simple-datatables
Advanced tools
Comparing version
@@ -20,3 +20,2 @@ import { cellType, DataTableConfiguration, DataTableOptions, inputCellType, elementNodeType, renderOptions, TableDataType } from "./types"; | ||
initialized: boolean; | ||
_input: HTMLInputElement; | ||
_label: HTMLElement; | ||
@@ -42,3 +41,6 @@ lastPage: number; | ||
_searchData: number[]; | ||
_searchQuery: string; | ||
_searchQueries: { | ||
term: string; | ||
columns: (number[] | undefined); | ||
}[]; | ||
_tableAttributes: { | ||
@@ -96,6 +98,13 @@ [key: string]: string; | ||
/** | ||
* Perform a search of the data set | ||
* Perform a simple search of the data set | ||
*/ | ||
search(query: string): boolean; | ||
search(term: string, columns?: (number[] | undefined)): boolean; | ||
/** | ||
* Perform a search of the data set seraching for up to multiple strings in various columns | ||
*/ | ||
multiSearch(queries: { | ||
term: string; | ||
columns: (number[] | undefined); | ||
}[]): boolean; | ||
/** | ||
* Change page | ||
@@ -102,0 +111,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
export declare const layoutTemplate: (options: any) => string; | ||
export declare const layoutTemplate: (options: any, dom: any) => string; |
@@ -77,6 +77,9 @@ interface elementNodeType { | ||
/** | ||
* default: '{select} entries per page' | ||
* default: 'Search within table' | ||
* Sets the title of the search input. | ||
*/ | ||
searchTitle: string; | ||
/** | ||
* default: 'entries per page' | ||
* Sets the per-page dropdown's label | ||
* | ||
* {select} - the per-page dropdown (required) | ||
*/ | ||
@@ -217,3 +220,4 @@ perPage: string; | ||
placeholder: "Search...", | ||
perPage: "{select} entries per page", | ||
searchTitle: "Search within table", | ||
perPage: "entries per page", | ||
noRows: "No entries to found", | ||
@@ -225,3 +229,3 @@ info: "Showing {start} to {end} of {rows} entries", | ||
*/ | ||
template: (DataTableConfiguration: any) => string; | ||
template: (DataTableConfiguration: any, HTMLTableElement: any) => string; | ||
/** | ||
@@ -228,0 +232,0 @@ * Allows for custom arranging of the DOM elements in the top and bottom containers. There are for 4 variables you can utilize: |
@@ -79,6 +79,9 @@ import { DiffDOM } from 'diff-dom'; | ||
/** | ||
* default: '{select} entries per page' | ||
* default: 'Search within table' | ||
* Sets the title of the search input. | ||
*/ | ||
searchTitle: string; | ||
/** | ||
* default: 'entries per page' | ||
* Sets the per-page dropdown's label | ||
* | ||
* {select} - the per-page dropdown (required) | ||
*/ | ||
@@ -219,3 +222,4 @@ perPage: string; | ||
placeholder: "Search...", | ||
perPage: "{select} entries per page", | ||
searchTitle: "Search within table", | ||
perPage: "entries per page", | ||
noRows: "No entries to found", | ||
@@ -227,3 +231,3 @@ info: "Showing {start} to {end} of {rows} entries", | ||
*/ | ||
template: (DataTableConfiguration: any) => string; | ||
template: (DataTableConfiguration: any, HTMLTableElement: any) => string; | ||
/** | ||
@@ -467,3 +471,2 @@ * Allows for custom arranging of the DOM elements in the top and bottom containers. There are for 4 variables you can utilize: | ||
initialized: boolean; | ||
_input: HTMLInputElement; | ||
_label: HTMLElement; | ||
@@ -489,3 +492,6 @@ lastPage: number; | ||
_searchData: number[]; | ||
_searchQuery: string; | ||
_searchQueries: { | ||
term: string; | ||
columns: (number[] | undefined); | ||
}[]; | ||
_tableAttributes: { | ||
@@ -543,6 +549,13 @@ [key: string]: string; | ||
/** | ||
* Perform a search of the data set | ||
* Perform a simple search of the data set | ||
*/ | ||
search(query: string): boolean; | ||
search(term: string, columns?: (number[] | undefined)): boolean; | ||
/** | ||
* Perform a search of the data set seraching for up to multiple strings in various columns | ||
*/ | ||
multiSearch(queries: { | ||
term: string; | ||
columns: (number[] | undefined); | ||
}[]): boolean; | ||
/** | ||
* Change page | ||
@@ -549,0 +562,0 @@ */ |
@@ -115,2 +115,3 @@ #### Type: `array` | ||
select: 6, | ||
type: 'string', | ||
render: function(data, td, rowIndex, cellIndex) { | ||
@@ -117,0 +118,0 @@ return `${data}<button type='button' data-row='${rowIndex}'>Select</button>`; |
@@ -20,2 +20,3 @@ #### Getting Started | ||
* [datatable.search](Events#datatablesearch) | ||
* [datatable.multisearch](Events#datatablesearch) | ||
* [datatable.selectrow](Events#datatableselectrow) | ||
@@ -82,1 +83,3 @@ | ||
* [print()](print()) | ||
* [search()](search()) | ||
* [multiSearch()](multiSearch()) |
@@ -10,3 +10,4 @@ ### `labels` | ||
placeholder: "Search...", | ||
perPage: "{select} entries per page", | ||
searchTitle: "Search within table", | ||
perPage: "entries per page", | ||
noRows: "No entries to found", | ||
@@ -35,5 +36,10 @@ info: "Showing {start} to {end} of {rows} entries", | ||
<tr> | ||
<td><code>searchTitle</code></td> | ||
<td>Sets the title of the search input</td> | ||
<td>None</td> | ||
</tr> | ||
<tr> | ||
<td><code>perPage</code></td> | ||
<td>Sets the per-page dropdown's label</td> | ||
<td><code>{select}</code> - the per-page dropdown (<code class="danger">required</code>)</td> | ||
<td>None</td> | ||
</tr> | ||
@@ -64,3 +70,4 @@ <tr> | ||
placeholder: "Search employees...", | ||
perPage: "Show {select} employees per page", | ||
searchTitle: "Search within employees", | ||
perPage: "employees per page", | ||
noRows: "No employees to display", | ||
@@ -67,0 +74,0 @@ info: "Showing {start} to {end} of {rows} employees (Page {page} of {pages} pages)", |
@@ -7,3 +7,3 @@ ### `template` | ||
```javascript | ||
(options) => `<div class='${options.classes.top}'> | ||
(options, dom) => `<div class='${options.classes.top}'> | ||
${ | ||
@@ -21,3 +21,3 @@ options.paging && options.perPageSelect ? | ||
`<div class='${options.classes.search}'> | ||
<input class='${options.classes.input}' placeholder='${options.labels.placeholder}' type='text'> | ||
<input class='${options.classes.input}' placeholder='${options.labels.placeholder}' type='search' title='${options.labels.searchTitle}'${dom.id ? ` aria-controls="${dom.id}"` : ""}> | ||
</div>` : | ||
@@ -24,0 +24,0 @@ "" |
### Upgrading | ||
## From 7.0.x to 7.1.x | ||
* The [search()](search()) methods allows to specify which columns are to be searched. And the [multiSearch()](multiSearch()) method allows specifying multiple simultaneous searches. | ||
## From 6.0.x to 7.0.x | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "simple-datatables", | ||
"version": "7.0.3", | ||
"version": "7.1.0", | ||
"description": "A lightweight, dependency-free JavaScript HTML table plugin.", | ||
@@ -18,6 +18,6 @@ "main": "dist/index.js", | ||
"build_js_umd": "browserify dist/index.js --standalone simpleDatatables -o dist/umd/simple-datatables.js", | ||
"build_css": "cp -R src/css/* dist/", | ||
"build_demos": "npm run build_demos_js && cp -R src/css docs/demos/dist/", | ||
"build_css": "shx cp -R src/css/* dist/", | ||
"build_demos": "npm run build_demos_js && shx cp -R src/css docs/demos/dist/", | ||
"build_demos_js": "rollup -c rollup.demos.config.mjs", | ||
"postbuild_demos": "cp -r dist/umd/simple-datatables.js docs/demos/dist/umd.js", | ||
"postbuild_demos": "shx cp -r dist/umd/simple-datatables.js docs/demos/dist/umd.js", | ||
"prepare": "npm run build" | ||
@@ -72,2 +72,3 @@ }, | ||
"selenium-webdriver": "^4.7.1", | ||
"shx": "^0.3.4", | ||
"tslib": "^2.4.1", | ||
@@ -78,4 +79,4 @@ "typescript": "^4.9.4" | ||
"dayjs": "^1.11.7", | ||
"diff-dom": "5.0.2" | ||
"diff-dom": "5.0.3" | ||
} | ||
} |
@@ -302,4 +302,4 @@ import {readDataCell, readHeaderCell} from "./read_data" | ||
dir} | ||
if (this.dt._searchQuery) { | ||
this.dt.search(this.dt._searchQuery) | ||
if (this.dt._searchQueries.length) { | ||
this.dt.multiSearch(this.dt._searchQueries) | ||
this.dt.emit("datatable.sort", index, dir) | ||
@@ -306,0 +306,0 @@ } else if (!init) { |
@@ -59,2 +59,3 @@ import {DataTableConfiguration} from "./types" | ||
placeholder: "Search...", // The search input placeholder | ||
searchTitle: "Search within table", // The search input title | ||
perPage: "entries per page", // per-page dropdown label | ||
@@ -61,0 +62,0 @@ noRows: "No entries found", // Message shown when there are no records to show |
@@ -53,4 +53,2 @@ import { | ||
_input: HTMLInputElement | ||
_label: HTMLElement | ||
@@ -80,3 +78,3 @@ | ||
_searchQuery: string | ||
_searchQueries: {term: string, columns: (number[] | undefined)}[] | ||
@@ -137,3 +135,3 @@ _tableAttributes: { [key: string]: string} | ||
this._dd = new DiffDOM() | ||
this._dd = new DiffDOM({valueDiffing: false}) | ||
@@ -147,2 +145,3 @@ this.initialized = false | ||
this.hasRows = false | ||
this._searchQueries = [] | ||
@@ -188,3 +187,3 @@ this.init() | ||
this.wrapperDOM.innerHTML = this.options.template(this.options) | ||
this.wrapperDOM.innerHTML = this.options.template(this.options, this.dom) | ||
@@ -280,3 +279,3 @@ const selector = this.wrapperDOM.querySelector(`select.${this.options.classes.selector}`) | ||
this.data.headings, | ||
(this.options.paging || this._searchQuery) && this._currentPage && this.pages.length && !renderOptions.noPaging ? | ||
(this.options.paging || this._searchQueries.length) && this._currentPage && this.pages.length && !renderOptions.noPaging ? | ||
this.pages[this._currentPage - 1] : | ||
@@ -300,3 +299,2 @@ this.data.data.map((row, index) => ({ | ||
} | ||
const diff = this._dd.diff(this._virtualDOM, newVirtualDOM) | ||
@@ -338,3 +336,3 @@ this._dd.apply(this.dom, diff) | ||
f = f + 1 | ||
items = this._searchQuery ? this._searchData.length : this.data.data.length | ||
items = this._searchQueries.length ? this._searchData.length : this.data.data.length | ||
} | ||
@@ -486,6 +484,25 @@ | ||
if (this.options.searchable) { | ||
this._input = this.wrapperDOM.querySelector(`.${this.options.classes.input}`) | ||
if (this._input) { | ||
this._input.addEventListener("keyup", () => this.search(this._input.value), false) | ||
} | ||
this.wrapperDOM.addEventListener("keyup", (event: KeyboardEvent) => { | ||
const target = event.target | ||
if (!(target instanceof HTMLInputElement) || !target.matches(`.${this.options.classes.input}`)) { | ||
return | ||
} | ||
event.preventDefault() | ||
const searches : {term: string, columns: (number[] | undefined)}[] = (Array.from(this.wrapperDOM.querySelectorAll(`.${this.options.classes.input}`)) as HTMLInputElement[]).filter( | ||
el => el.value.length | ||
).map( | ||
el => el.dataset.columns ? | ||
{term: el.value, | ||
columns: (JSON.parse(el.dataset.columns) as number[])} : | ||
{term: el.value, | ||
columns: undefined} | ||
) | ||
if (searches.length === 1) { | ||
const search = searches[0] | ||
this.search(search.term, search.columns) | ||
} else { | ||
this.multiSearch(searches) | ||
} | ||
}) | ||
} | ||
@@ -654,3 +671,3 @@ | ||
if (this._searchQuery) { | ||
if (this._searchQueries.length) { | ||
rows = [] | ||
@@ -704,24 +721,49 @@ | ||
/** | ||
* Perform a search of the data set | ||
* Perform a simple search of the data set | ||
*/ | ||
search(query: string) { | ||
search(term: string, columns: (number[] | undefined ) = undefined) { | ||
if (!term.length) { | ||
this._currentPage = 1 | ||
this._searchQueries = [] | ||
this._searchData = [] | ||
this.update() | ||
this.emit("datatable.search", "", []) | ||
this.wrapperDOM.classList.remove("search-results") | ||
return false | ||
} | ||
this.multiSearch([ | ||
{term, | ||
columns: columns ? columns : undefined} | ||
]) | ||
this.emit("datatable.search", term, this._searchData) | ||
} | ||
/** | ||
* Perform a search of the data set seraching for up to multiple strings in various columns | ||
*/ | ||
multiSearch(queries : {term: string, columns: (number[] | undefined)}[]) { | ||
if (!this.hasRows) return false | ||
this._currentPage = 1 | ||
this._searchQuery = query | ||
this._searchQueries = queries | ||
this._searchData = [] | ||
if (!query.length) { | ||
queries = queries.filter(query => query.term.length) | ||
if (!queries.length) { | ||
this.update() | ||
this.emit("datatable.search", query, this._searchData) | ||
this.emit("datatable.multisearch", queries, this._searchData) | ||
this.wrapperDOM.classList.remove("search-results") | ||
return false | ||
} | ||
const queryWords : (string | false)[]= this.columns.settings.map( | ||
column => { | ||
if (column.hidden || !column.searchable) { | ||
const queryWords = queries.map(query => this.columns.settings.map( | ||
(column, index) => { | ||
if (column.hidden || !column.searchable || (query.columns && !query.columns.includes(index))) { | ||
return false | ||
} | ||
let columnQuery = query | ||
let columnQuery = query.term | ||
const sensitivity = column.sensitivity || this.options.sensitivity | ||
@@ -741,29 +783,34 @@ if (["base", "accent"].includes(sensitivity)) { | ||
) | ||
) | ||
this.data.data.forEach((row: cellType[], idx: number) => { | ||
for (let i=0; i<row.length; i++) { | ||
const query = queryWords[i] | ||
if (query) { | ||
const cell = row[i] | ||
let content = (cell.text || String(cell.data)).trim() | ||
if (content.length) { | ||
const column = this.columns.settings[i] | ||
const sensitivity = column.sensitivity || this.options.sensitivity | ||
if (["base", "accent"].includes(sensitivity)) { | ||
content = content.toLowerCase() | ||
} | ||
if (["base", "case"].includes(sensitivity)) { | ||
content = content.normalize("NFD").replace(/\p{Diacritic}/gu, "") | ||
} | ||
const ignorePunctuation = column.ignorePunctuation || this.options.ignorePunctuation | ||
if (ignorePunctuation) { | ||
content = content.replace(/[.,/#!$%^&*;:{}=-_`~()]/g, "") | ||
} | ||
if (query.split(" ").find(queryWord => content.includes(queryWord))) { | ||
this._searchData.push(idx) | ||
break | ||
} | ||
const searchRow = row.map((cell, i) => { | ||
let content = (cell.text || String(cell.data)).trim() | ||
if (content.length) { | ||
const column = this.columns.settings[i] | ||
const sensitivity = column.sensitivity || this.options.sensitivity | ||
if (["base", "accent"].includes(sensitivity)) { | ||
content = content.toLowerCase() | ||
} | ||
if (["base", "case"].includes(sensitivity)) { | ||
content = content.normalize("NFD").replace(/\p{Diacritic}/gu, "") | ||
} | ||
const ignorePunctuation = column.ignorePunctuation || this.options.ignorePunctuation | ||
if (ignorePunctuation) { | ||
content = content.replace(/[.,/#!$%^&*;:{}=-_`~()]/g, "") | ||
} | ||
} | ||
return content | ||
}) | ||
if ( | ||
queryWords.every( | ||
queries => queries.find( | ||
(query, index) => query ? | ||
query.split(" ").find(queryWord => searchRow[index].includes(queryWord)) : | ||
false | ||
) | ||
) | ||
) { | ||
this._searchData.push(idx) | ||
} | ||
}) | ||
@@ -780,3 +827,3 @@ | ||
this.emit("datatable.search", query, this._searchData) | ||
this.emit("datatable.multisearch", queries, this._searchData) | ||
} | ||
@@ -855,4 +902,8 @@ | ||
if (this.options.searchable) { | ||
this._input.value = "" | ||
this._searchQuery = "" | ||
(Array.from(this.wrapperDOM.querySelectorAll(`.${this.options.classes.input}`)) as HTMLInputElement[]).forEach( | ||
el => { | ||
el.value = "" | ||
} | ||
) | ||
this._searchQueries = [] | ||
} | ||
@@ -924,32 +975,42 @@ this._currentPage = 1 | ||
let newVirtualDOM = structuredClone(this._virtualDOM) | ||
let newVirtualDOM : elementNodeType = { | ||
nodeName: "TABLE", | ||
attributes: { | ||
class: this.options.classes.table | ||
}, | ||
childNodes: [ | ||
{ | ||
nodeName: "THEAD", | ||
childNodes: [ | ||
headingsToVirtualHeaderRowDOM( | ||
this.data.headings, this.columns.settings, this.columns._state, this.options, {}) | ||
] | ||
}, | ||
{ | ||
nodeName: "TBODY", | ||
childNodes: [ | ||
{ | ||
nodeName: "TR", | ||
childNodes: [ | ||
{ | ||
nodeName: "TD", | ||
attributes: { | ||
class: this.options.classes.empty, | ||
colspan: String(colspan) | ||
}, | ||
childNodes: [ | ||
{ | ||
nodeName: "#text", | ||
data: message | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
} | ||
let tbody : elementNodeType = newVirtualDOM.childNodes?.find((node: elementNodeType) => node.nodeName === "TBODY") as elementNodeType | ||
if (!tbody) { | ||
tbody = {nodeName: "TBODY"} | ||
newVirtualDOM.childNodes = [tbody] | ||
] | ||
} | ||
tbody.childNodes = [ | ||
{ | ||
nodeName: "TR", | ||
childNodes: [ | ||
{ | ||
nodeName: "TD", | ||
attributes: { | ||
class: this.options.classes.empty, | ||
colspan: String(colspan) | ||
}, | ||
childNodes: [ | ||
{ | ||
nodeName: "#text", | ||
data: message | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
if (this.options.tableRender) { | ||
@@ -956,0 +1017,0 @@ const renderedTableVirtualDOM : (elementNodeType | void) = this.options.tableRender(this.data, newVirtualDOM, "message") |
// Template for custom layouts | ||
export const layoutTemplate = options => `<div class='${options.classes.top}'> | ||
export const layoutTemplate = (options, dom) => `<div class='${options.classes.top}'> | ||
${ | ||
@@ -15,3 +15,3 @@ options.paging && options.perPageSelect ? | ||
`<div class='${options.classes.search}'> | ||
<input class='${options.classes.input}' placeholder='${options.labels.placeholder}' type='text'> | ||
<input class='${options.classes.input}' placeholder='${options.labels.placeholder}' type='search' title='${options.labels.searchTitle}'${dom.id ? ` aria-controls="${dom.id}"` : ""}> | ||
</div>` : | ||
@@ -18,0 +18,0 @@ "" |
@@ -92,6 +92,9 @@ // Same definitions as in diff-dom | ||
/** | ||
* default: '{select} entries per page' | ||
* default: 'Search within table' | ||
* Sets the title of the search input. | ||
*/ | ||
searchTitle: string; | ||
/** | ||
* default: 'entries per page' | ||
* Sets the per-page dropdown's label | ||
* | ||
* {select} - the per-page dropdown (required) | ||
*/ | ||
@@ -239,3 +242,4 @@ perPage: string; | ||
placeholder: "Search...", | ||
perPage: "{select} entries per page", | ||
searchTitle: "Search within table", | ||
perPage: "entries per page", | ||
noRows: "No entries to found", | ||
@@ -247,3 +251,3 @@ info: "Showing {start} to {end} of {rows} entries", | ||
*/ | ||
template: (DataTableConfiguration) => string; | ||
template: (DataTableConfiguration, HTMLTableElement) => string; | ||
/** | ||
@@ -250,0 +254,0 @@ * Allows for custom arranging of the DOM elements in the top and bottom containers. There are for 4 variables you can utilize: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
3182748
-11.07%28
3.7%163
-2.4%13864
-7.5%+ Added
- Removed
Updated