vanilla-table 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/README.md +254 -20
- package/index.js +226 -10
- package/package.json +5 -1
- package/src/core.js +84 -0
- package/src/exports.js +109 -0
- package/src/filters.js +115 -0
- package/src/pagination.js +194 -0
- package/src/render.js +99 -0
- package/src/sort.js +180 -0
- package/src/table.css +385 -0
package/README.md
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
> Lightweight vanilla JavaScript table library with sorting, pagination, filtering, and export capabilities
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/vanilla-table)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## ✨ Planned Features
|
|
8
|
+
## ✨ Features
|
|
10
9
|
|
|
11
10
|
- **Zero Dependencies** - Pure vanilla JavaScript, no jQuery or other libraries required
|
|
12
|
-
- **Sorting** -
|
|
11
|
+
- **Sorting** - Column sorting with customizable sort functions
|
|
13
12
|
- **Pagination** - Client-side pagination with configurable page sizes
|
|
14
13
|
- **Filtering** - Column-based filtering with multiple filter types
|
|
15
14
|
- **Search** - Global search across all columns
|
|
16
|
-
- **Export** - Export to CSV
|
|
15
|
+
- **Export** - Export to CSV and JSON formats
|
|
17
16
|
- **Zebra Striping** - Alternating row colors for better readability
|
|
18
17
|
- **Responsive** - Mobile-friendly table design
|
|
19
|
-
- **Lightweight** - Minimal footprint
|
|
18
|
+
- **Lightweight** - Minimal footprint (~15KB minified)
|
|
19
|
+
- **Customizable** - Extensive configuration options and custom render functions
|
|
20
20
|
|
|
21
21
|
## 📦 Installation
|
|
22
22
|
|
|
@@ -24,30 +24,264 @@ This package is currently under active development. The full implementation is b
|
|
|
24
24
|
npm install vanilla-table
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Or use directly in the browser:
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<!-- Include CSS -->
|
|
31
|
+
<link rel="stylesheet" href="path/to/vanilla-table/src/table.css" />
|
|
32
|
+
|
|
33
|
+
<!-- Include all JavaScript modules -->
|
|
34
|
+
<script src="path/to/vanilla-table/src/core.js"></script>
|
|
35
|
+
<script src="path/to/vanilla-table/src/render.js"></script>
|
|
36
|
+
<script src="path/to/vanilla-table/src/sort.js"></script>
|
|
37
|
+
<script src="path/to/vanilla-table/src/filters.js"></script>
|
|
38
|
+
<script src="path/to/vanilla-table/src/pagination.js"></script>
|
|
39
|
+
<script src="path/to/vanilla-table/src/exports.js"></script>
|
|
40
|
+
<script src="path/to/vanilla-table/index.js"></script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 🚀 Quick Start
|
|
44
|
+
|
|
45
|
+
### Basic Usage
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<table id="my-table">
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th data-field="name">Name</th>
|
|
52
|
+
<th data-field="email">Email</th>
|
|
53
|
+
<th data-field="age">Age</th>
|
|
54
|
+
</tr>
|
|
55
|
+
</thead>
|
|
56
|
+
<tbody></tbody>
|
|
57
|
+
</table>
|
|
58
|
+
|
|
59
|
+
<script>
|
|
60
|
+
const data = [
|
|
61
|
+
{ name: "John Doe", email: "john@example.com", age: 30 },
|
|
62
|
+
{ name: "Jane Smith", email: "jane@example.com", age: 25 },
|
|
63
|
+
{ name: "Bob Johnson", email: "bob@example.com", age: 35 },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const table = new VanillaTable("#my-table", {
|
|
67
|
+
data: data,
|
|
68
|
+
sorting: true,
|
|
69
|
+
pagination: true,
|
|
70
|
+
perPage: 10,
|
|
71
|
+
});
|
|
72
|
+
</script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### With Pagination Controls
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<div id="table-info"></div>
|
|
79
|
+
<table id="my-table">
|
|
80
|
+
...
|
|
81
|
+
</table>
|
|
82
|
+
<div id="pagination"></div>
|
|
83
|
+
|
|
84
|
+
<script>
|
|
85
|
+
const table = new VanillaTable("#my-table", {
|
|
86
|
+
data: data,
|
|
87
|
+
pagination: true,
|
|
88
|
+
perPage: 10,
|
|
89
|
+
paginationInfo: "#table-info",
|
|
90
|
+
paginationControls: "#pagination",
|
|
91
|
+
});
|
|
92
|
+
</script>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### With Search
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<input type="text" id="search" placeholder="Search..." />
|
|
99
|
+
<table id="my-table">
|
|
100
|
+
...
|
|
101
|
+
</table>
|
|
102
|
+
|
|
103
|
+
<script>
|
|
104
|
+
const table = new VanillaTable("#my-table", {
|
|
105
|
+
data: data,
|
|
106
|
+
search: true,
|
|
107
|
+
searchInput: "#search",
|
|
108
|
+
});
|
|
109
|
+
</script>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 📖 Configuration Options
|
|
113
|
+
|
|
114
|
+
| Option | Type | Default | Description |
|
|
115
|
+
| -------------------- | -------------- | --------------------- | ---------------------------------------------------- |
|
|
116
|
+
| `data` | Array | `[]` | Array of row objects |
|
|
117
|
+
| `columns` | Array | `[]` | Column configuration (optional, can infer from HTML) |
|
|
118
|
+
| `pagination` | Boolean | `false` | Enable pagination |
|
|
119
|
+
| `perPage` | Number | `25` | Rows per page |
|
|
120
|
+
| `paginationInfo` | String/Element | `null` | Element for pagination info text |
|
|
121
|
+
| `paginationControls` | String/Element | `null` | Element for pagination buttons |
|
|
122
|
+
| `sorting` | Boolean | `true` | Enable sorting |
|
|
123
|
+
| `search` | Boolean | `false` | Enable search |
|
|
124
|
+
| `searchInput` | String/Element | `null` | Search input element |
|
|
125
|
+
| `filters` | Object | `{}` | Initial filters |
|
|
126
|
+
| `zebra` | Boolean | `true` | Enable zebra striping |
|
|
127
|
+
| `noResultsText` | String | `'No results found.'` | Text when no results |
|
|
128
|
+
|
|
129
|
+
## 🎨 Column Configuration
|
|
130
|
+
|
|
131
|
+
Columns can be configured for custom rendering and behavior:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const table = new VanillaTable("#my-table", {
|
|
135
|
+
data: data,
|
|
136
|
+
columns: [
|
|
137
|
+
{
|
|
138
|
+
data: "name",
|
|
139
|
+
title: "Full Name",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
data: "salary",
|
|
143
|
+
title: "Salary",
|
|
144
|
+
render: (value) => `$${value.toLocaleString()}`,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
data: "status",
|
|
148
|
+
title: "Status",
|
|
149
|
+
sortable: false, // Disable sorting for this column
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
data: "user.email", // Nested property access
|
|
153
|
+
title: "Email",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 🔧 API Methods
|
|
160
|
+
|
|
161
|
+
### Data Management
|
|
28
162
|
|
|
29
163
|
```javascript
|
|
30
|
-
|
|
164
|
+
// Load new data
|
|
165
|
+
table.loadData(newData);
|
|
166
|
+
|
|
167
|
+
// Get all data
|
|
168
|
+
const allData = table.getAllData();
|
|
169
|
+
|
|
170
|
+
// Get visible data (after filters/search)
|
|
171
|
+
const visibleData = table.getVisibleData();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Search & Filtering
|
|
31
175
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
176
|
+
```javascript
|
|
177
|
+
// Search
|
|
178
|
+
table.search("query");
|
|
179
|
+
|
|
180
|
+
// Apply filters
|
|
181
|
+
table.applyFilters({
|
|
182
|
+
department: "Engineering",
|
|
183
|
+
status: ["active", "pending"], // Array of allowed values
|
|
40
184
|
});
|
|
41
185
|
|
|
42
|
-
|
|
186
|
+
// Clear all filters and search
|
|
187
|
+
table.clearFilters();
|
|
43
188
|
```
|
|
44
189
|
|
|
45
|
-
|
|
190
|
+
### Pagination
|
|
46
191
|
|
|
47
|
-
|
|
192
|
+
```javascript
|
|
193
|
+
// Go to specific page
|
|
194
|
+
table.goToPage(3);
|
|
195
|
+
|
|
196
|
+
// Set page size
|
|
197
|
+
table.setPageSize(50);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Export
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
// Export to CSV
|
|
204
|
+
table.exportCSV("data.csv");
|
|
205
|
+
|
|
206
|
+
// Export to JSON
|
|
207
|
+
table.exportJSON("data.json");
|
|
208
|
+
|
|
209
|
+
// Get as string (without downloading)
|
|
210
|
+
const csvString = table.toCSV();
|
|
211
|
+
const jsonString = table.toJSON();
|
|
212
|
+
|
|
213
|
+
// Include filtered rows in export
|
|
214
|
+
table.exportCSV("all-data.csv", true);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Cleanup
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
// Destroy table instance
|
|
221
|
+
table.destroy();
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## 🎯 Advanced Examples
|
|
225
|
+
|
|
226
|
+
### Custom Render Functions
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
const table = new VanillaTable("#my-table", {
|
|
230
|
+
data: data,
|
|
231
|
+
columns: [
|
|
232
|
+
{
|
|
233
|
+
data: "avatar",
|
|
234
|
+
title: "Avatar",
|
|
235
|
+
render: (value, row) => {
|
|
236
|
+
return `<img src="${value}" alt="${row.name}" style="width: 40px; border-radius: 50%;">`;
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
data: "status",
|
|
241
|
+
title: "Status",
|
|
242
|
+
render: (value) => {
|
|
243
|
+
const color = value === "active" ? "green" : "red";
|
|
244
|
+
return `<span style="color: ${color}">${value}</span>`;
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Custom Filtering
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
// Filter with custom function
|
|
255
|
+
table.applyFilters({
|
|
256
|
+
age: (value) => value >= 18 && value <= 65,
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Multi-Column Sorting
|
|
261
|
+
|
|
262
|
+
Users can:
|
|
263
|
+
|
|
264
|
+
- **Click** a column header to sort by that column
|
|
265
|
+
- **Shift+Click** to add additional sort columns (priority indicated by numbers)
|
|
266
|
+
|
|
267
|
+
## 📝 Example
|
|
268
|
+
|
|
269
|
+
See `example.html` for a complete working demo with all features.
|
|
48
270
|
|
|
49
271
|
## 🤝 Contributing
|
|
50
272
|
|
|
273
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
274
|
+
|
|
275
|
+
## 📄 License
|
|
276
|
+
|
|
277
|
+
MIT © matejkadlec
|
|
278
|
+
|
|
279
|
+
## 🔗 Links
|
|
280
|
+
|
|
281
|
+
- [npm Package](https://www.npmjs.com/package/vanilla-table)
|
|
282
|
+
- [GitHub Repository](https://github.com/matejkadlec/vanilla-table)
|
|
283
|
+
- [Report Issues](https://github.com/matejkadlec/vanilla-table/issues)
|
|
284
|
+
|
|
51
285
|
Contributions, issues, and feature requests are welcome once the initial implementation is complete.
|
|
52
286
|
|
|
53
287
|
## 📧 Contact
|
package/index.js
CHANGED
|
@@ -1,20 +1,236 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vanilla
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 🚧 Full implementation coming soon
|
|
2
|
+
* VanillaTable - Lightweight vanilla JavaScript table library
|
|
3
|
+
* Features: sorting, pagination, filtering, search, and export capabilities
|
|
6
4
|
*/
|
|
7
5
|
|
|
6
|
+
// Load all modules (in browser, these will be included via script tags)
|
|
7
|
+
// In Node.js/bundler environments, you would use actual imports
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main VanillaTable class
|
|
11
|
+
*/
|
|
8
12
|
class VanillaTable {
|
|
13
|
+
/**
|
|
14
|
+
* Create a new VanillaTable instance
|
|
15
|
+
* @param {string|HTMLElement} selector - CSS selector or DOM element for the table
|
|
16
|
+
* @param {Object} options - Configuration options
|
|
17
|
+
*/
|
|
9
18
|
constructor(selector, options = {}) {
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
|
|
19
|
+
// Find the table element
|
|
20
|
+
this.tableElement = typeof selector === 'string'
|
|
21
|
+
? document.querySelector(selector)
|
|
22
|
+
: selector;
|
|
23
|
+
|
|
24
|
+
if (!this.tableElement) {
|
|
25
|
+
throw new Error('Table element not found');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Default options
|
|
29
|
+
this.options = {
|
|
30
|
+
data: [], // Array of row objects
|
|
31
|
+
columns: [], // Column configuration
|
|
32
|
+
pagination: false, // Enable pagination
|
|
33
|
+
perPage: 25, // Rows per page
|
|
34
|
+
paginationInfo: null, // Element or selector for pagination info
|
|
35
|
+
paginationControls: null, // Element or selector for pagination controls
|
|
36
|
+
sorting: true, // Enable sorting
|
|
37
|
+
search: false, // Enable search
|
|
38
|
+
searchInput: null, // Element or selector for search input
|
|
39
|
+
filters: {}, // Initial filters
|
|
40
|
+
zebra: true, // Enable zebra striping
|
|
41
|
+
noResultsText: 'No results found.',
|
|
42
|
+
...options
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Add vanilla-table class to the table
|
|
46
|
+
this.tableElement.classList.add('vanilla-table');
|
|
47
|
+
|
|
48
|
+
// Initialize core
|
|
49
|
+
this.core = new VanillaTableCore(this.tableElement, this.options);
|
|
50
|
+
|
|
51
|
+
// Initialize modules
|
|
52
|
+
this.renderer = new VanillaTableRenderer(this.core);
|
|
53
|
+
this.sorter = this.options.sorting ? new VanillaTableSorter(this.core, this.renderer) : null;
|
|
54
|
+
this.paginator = this.options.pagination ? new VanillaTablePaginator(this.core) : null;
|
|
55
|
+
this.filter = new VanillaTableFilter(this.core, this.renderer);
|
|
56
|
+
this.exporter = new VanillaTableExporter(this.core, this.renderer);
|
|
57
|
+
|
|
58
|
+
// Setup callbacks
|
|
59
|
+
if (this.sorter) {
|
|
60
|
+
this.core.onSort = () => {
|
|
61
|
+
if (this.paginator) this.paginator.update();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Initialize search if enabled
|
|
66
|
+
if (this.options.search && this.options.searchInput) {
|
|
67
|
+
this.initSearch();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Load initial data if provided
|
|
71
|
+
if (this.options.data && this.options.data.length > 0) {
|
|
72
|
+
this.loadData(this.options.data);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Load data into the table
|
|
78
|
+
* @param {Array} data - Array of row objects
|
|
79
|
+
*/
|
|
80
|
+
loadData(data) {
|
|
81
|
+
this.renderer.renderTable(data);
|
|
82
|
+
|
|
83
|
+
if (this.sorter) {
|
|
84
|
+
this.sorter.init();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.paginator) {
|
|
88
|
+
this.paginator.update();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Initialize search functionality
|
|
96
|
+
*/
|
|
97
|
+
initSearch() {
|
|
98
|
+
const searchInput = typeof this.options.searchInput === 'string'
|
|
99
|
+
? document.querySelector(this.options.searchInput)
|
|
100
|
+
: this.options.searchInput;
|
|
101
|
+
|
|
102
|
+
if (!searchInput) return;
|
|
103
|
+
|
|
104
|
+
searchInput.addEventListener('input', (e) => {
|
|
105
|
+
this.search(e.target.value);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Search the table
|
|
111
|
+
* @param {string} query - Search query
|
|
112
|
+
*/
|
|
113
|
+
search(query) {
|
|
114
|
+
this.filter.applySearch(query);
|
|
115
|
+
if (this.paginator) {
|
|
116
|
+
this.core.state.currentPage = 1; // Reset to first page
|
|
117
|
+
this.paginator.update();
|
|
118
|
+
}
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Apply filters to the table
|
|
124
|
+
* @param {Object} filters - Filter configuration
|
|
125
|
+
*/
|
|
126
|
+
applyFilters(filters) {
|
|
127
|
+
this.filter.applyFilters(filters);
|
|
128
|
+
if (this.paginator) {
|
|
129
|
+
this.core.state.currentPage = 1; // Reset to first page
|
|
130
|
+
this.paginator.update();
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
13
133
|
}
|
|
14
134
|
|
|
15
|
-
|
|
16
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Clear all filters and search
|
|
137
|
+
*/
|
|
138
|
+
clearFilters() {
|
|
139
|
+
this.filter.clearAll();
|
|
140
|
+
if (this.paginator) {
|
|
141
|
+
this.paginator.update();
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Go to a specific page
|
|
148
|
+
* @param {number} page - Page number
|
|
149
|
+
*/
|
|
150
|
+
goToPage(page) {
|
|
151
|
+
if (this.paginator) {
|
|
152
|
+
this.paginator.goToPage(page);
|
|
153
|
+
}
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Set page size
|
|
159
|
+
* @param {number} size - Number of rows per page
|
|
160
|
+
*/
|
|
161
|
+
setPageSize(size) {
|
|
162
|
+
this.core.state.perPage = size;
|
|
163
|
+
if (this.paginator) {
|
|
164
|
+
this.paginator.update();
|
|
165
|
+
}
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Export table data to CSV
|
|
171
|
+
* @param {string} filename - Output filename
|
|
172
|
+
* @param {boolean} includeFiltered - Include filtered rows
|
|
173
|
+
*/
|
|
174
|
+
exportCSV(filename = 'table.csv', includeFiltered = false) {
|
|
175
|
+
this.exporter.downloadCSV(filename, includeFiltered);
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Export table data to JSON
|
|
181
|
+
* @param {string} filename - Output filename
|
|
182
|
+
* @param {boolean} includeFiltered - Include filtered rows
|
|
183
|
+
*/
|
|
184
|
+
exportJSON(filename = 'table.json', includeFiltered = false) {
|
|
185
|
+
this.exporter.downloadJSON(filename, includeFiltered);
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get current table data as CSV string
|
|
191
|
+
* @param {boolean} includeFiltered - Include filtered rows
|
|
192
|
+
*/
|
|
193
|
+
toCSV(includeFiltered = false) {
|
|
194
|
+
return this.exporter.toCSV(includeFiltered);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get current table data as JSON string
|
|
199
|
+
* @param {boolean} includeFiltered - Include filtered rows
|
|
200
|
+
*/
|
|
201
|
+
toJSON(includeFiltered = false) {
|
|
202
|
+
return this.exporter.toJSON(includeFiltered);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get visible data
|
|
207
|
+
*/
|
|
208
|
+
getVisibleData() {
|
|
209
|
+
return this.exporter.getVisibleData();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get all data
|
|
214
|
+
*/
|
|
215
|
+
getAllData() {
|
|
216
|
+
return this.exporter.getAllData();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Destroy the table instance
|
|
221
|
+
*/
|
|
222
|
+
destroy() {
|
|
223
|
+
// Remove event listeners and clean up
|
|
224
|
+
this.tableElement.classList.remove('vanilla-table', 'vanilla-table-zebra', 'sorting-enabled');
|
|
225
|
+
// Note: In a full implementation, you'd want to track and remove all event listeners
|
|
17
226
|
}
|
|
18
227
|
}
|
|
19
228
|
|
|
20
|
-
module
|
|
229
|
+
// Export for different module systems
|
|
230
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
231
|
+
module.exports = VanillaTable;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (typeof window !== 'undefined') {
|
|
235
|
+
window.VanillaTable = VanillaTable;
|
|
236
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanilla-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lightweight vanilla JavaScript table library with sorting, pagination, filtering, and export capabilities",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"src/"
|
|
9
|
+
],
|
|
6
10
|
"scripts": {
|
|
7
11
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
12
|
},
|
package/src/core.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core utilities for VanillaTable
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class VanillaTableCore {
|
|
6
|
+
constructor(table, options = {}) {
|
|
7
|
+
this.table = table;
|
|
8
|
+
this.tbody = table.querySelector("tbody");
|
|
9
|
+
this.thead = table.querySelector("thead");
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.data = [];
|
|
12
|
+
this.rows = [];
|
|
13
|
+
this.state = {
|
|
14
|
+
currentPage: 1,
|
|
15
|
+
perPage: options.perPage || 25,
|
|
16
|
+
sortState: [],
|
|
17
|
+
filters: {},
|
|
18
|
+
searchQuery: "",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Add zebra striping class if enabled
|
|
22
|
+
if (options.zebra !== false) {
|
|
23
|
+
this.table.classList.add("vanilla-table-zebra");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Recompute zebra striping for currently visible rows
|
|
29
|
+
*/
|
|
30
|
+
recomputeZebra() {
|
|
31
|
+
if (this.options.zebra === false) return;
|
|
32
|
+
|
|
33
|
+
let visibleIndex = 0;
|
|
34
|
+
this.rows.forEach((row) => {
|
|
35
|
+
if (row.classList.contains("vanilla-table-no-results")) return;
|
|
36
|
+
if (row.style.display === "none") return;
|
|
37
|
+
|
|
38
|
+
row.classList.remove("odd", "even");
|
|
39
|
+
row.classList.add(visibleIndex % 2 === 0 ? "odd" : "even");
|
|
40
|
+
visibleIndex++;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Run a function with transitions temporarily disabled
|
|
46
|
+
*/
|
|
47
|
+
withTransitionSuspended(fn) {
|
|
48
|
+
this.table.classList.add("suspend-transitions");
|
|
49
|
+
try {
|
|
50
|
+
fn();
|
|
51
|
+
} finally {
|
|
52
|
+
requestAnimationFrame(() =>
|
|
53
|
+
this.table.classList.remove("suspend-transitions")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get visible rows (not hidden by filters/search)
|
|
60
|
+
*/
|
|
61
|
+
getVisibleRows() {
|
|
62
|
+
return this.rows.filter(
|
|
63
|
+
(row) =>
|
|
64
|
+
!row.classList.contains("vanilla-table-no-results") &&
|
|
65
|
+
row.style.display !== "none"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Update the no-results row visibility
|
|
71
|
+
*/
|
|
72
|
+
updateNoResults() {
|
|
73
|
+
const noResultsRow = this.tbody.querySelector(".vanilla-table-no-results");
|
|
74
|
+
if (!noResultsRow) return;
|
|
75
|
+
|
|
76
|
+
const hasVisibleRows = this.rows.some(
|
|
77
|
+
(row) =>
|
|
78
|
+
!row.classList.contains("vanilla-table-no-results") &&
|
|
79
|
+
row.style.display !== "none"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
noResultsRow.style.display = hasVisibleRows ? "none" : "";
|
|
83
|
+
}
|
|
84
|
+
}
|