Full Code of vincjo/datatables for AI

main 755b8179c99d cached
640 files
3.9 MB
1.1M tokens
450 symbols
1 requests
Download .txt
Showing preview only (4,221K chars total). Download the full file or copy to clipboard to get everything.
Repository: vincjo/datatables
Branch: main
Commit: 755b8179c99d
Files: 640
Total size: 3.9 MB

Directory structure:
gitextract_z5jwp21r/

├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── datatables.code-workspace
├── ecosystem.config.cjs
├── mdsvex.config.js
├── package.json
├── src/
│   ├── app.d.ts
│   ├── app.html
│   ├── hooks.server.ts
│   ├── lib/
│   │   ├── index.ts
│   │   ├── legacy/
│   │   │   ├── index.ts
│   │   │   ├── local/
│   │   │   │   ├── Comparator.ts
│   │   │   │   ├── Context.ts
│   │   │   │   ├── DataHandler.ts
│   │   │   │   ├── Datatable.svelte
│   │   │   │   ├── Pagination.svelte
│   │   │   │   ├── RowCount.svelte
│   │   │   │   ├── RowsPerPage.svelte
│   │   │   │   ├── Search.svelte
│   │   │   │   ├── Th.svelte
│   │   │   │   ├── ThFilter.svelte
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── EventHandler.ts
│   │   │   │   │   ├── FilterHandler.ts
│   │   │   │   │   ├── PageHandler.ts
│   │   │   │   │   ├── SearchHandler.ts
│   │   │   │   │   ├── SelectHandler.ts
│   │   │   │   │   └── SortHandler.ts
│   │   │   │   ├── helpers/
│   │   │   │   │   ├── AdvancedFilterHelper.ts
│   │   │   │   │   ├── CalculationHelper.ts
│   │   │   │   │   └── FilterHelper.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── utils.ts
│   │   │   └── remote/
│   │   │       ├── Context.ts
│   │   │       ├── DataHandler.ts
│   │   │       ├── Datatable.svelte
│   │   │       ├── Pagination.svelte
│   │   │       ├── README.md
│   │   │       ├── RowCount.svelte
│   │   │       ├── RowsPerPage.svelte
│   │   │       ├── Search.svelte
│   │   │       ├── SelectedCount.svelte
│   │   │       ├── Th.svelte
│   │   │       ├── ThFilter.svelte
│   │   │       ├── handlers/
│   │   │       │   ├── EventHandler.ts
│   │   │       │   ├── FilterHandler.ts
│   │   │       │   ├── PageHandler.ts
│   │   │       │   ├── SearchHandler.ts
│   │   │       │   ├── SelectHandler.ts
│   │   │       │   ├── SortHandler.ts
│   │   │       │   └── TriggerHandler.ts
│   │   │       └── index.ts
│   │   ├── server/
│   │   │   └── index.ts
│   │   ├── src/
│   │   │   ├── client/
│   │   │   │   ├── AbstractTableHandler.svelte.ts
│   │   │   │   ├── TableHandler.svelte.ts
│   │   │   │   ├── builders/
│   │   │   │   │   ├── AdvancedFilterBuilder.svelte.ts
│   │   │   │   │   ├── CSVBuilder.svelte.ts
│   │   │   │   │   ├── CalculationBuilder.svelte.ts
│   │   │   │   │   ├── FilterBuilder.svelte.ts
│   │   │   │   │   ├── QueryBuilder.svelte.ts
│   │   │   │   │   ├── RecordFilterBuilder.svelte.ts
│   │   │   │   │   ├── SearchBuilder.svelte.ts
│   │   │   │   │   └── SortBuilder.svelte.ts
│   │   │   │   ├── core/
│   │   │   │   │   ├── check.ts
│   │   │   │   │   ├── entry.ts
│   │   │   │   │   ├── field.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── rows.ts
│   │   │   │   │   └── value.ts
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── FilterHandler.svelte.ts
│   │   │   │   │   ├── PageHandler.svelte.ts
│   │   │   │   │   ├── QueryHandler.svelte.ts
│   │   │   │   │   ├── SearchHandler.svelte.ts
│   │   │   │   │   ├── SelectHandler.svelte.ts
│   │   │   │   │   └── SortHandler.svelte.ts
│   │   │   │   └── index.ts
│   │   │   ├── server/
│   │   │   │   ├── AbstractTableHandler.svelte.ts
│   │   │   │   ├── TableHandler.svelte.ts
│   │   │   │   ├── builders/
│   │   │   │   │   ├── FilterBuilder.svelte.ts
│   │   │   │   │   ├── SearchBuilder.svelte.ts
│   │   │   │   │   └── SortBuilder.svelte.ts
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── FetchHandler.svelte.ts
│   │   │   │   │   ├── FilterHandler.svelte.ts
│   │   │   │   │   ├── PageHandler.svelte.ts
│   │   │   │   │   ├── SearchHandler.svelte.ts
│   │   │   │   │   ├── SelectHandler.svelte.ts
│   │   │   │   │   └── SortHandler.svelte.ts
│   │   │   │   └── index.ts
│   │   │   └── shared/
│   │   │       ├── Datatable.svelte
│   │   │       ├── EventDispatcher.ts
│   │   │       ├── Pagination.svelte
│   │   │       ├── RowCount.svelte
│   │   │       ├── RowsPerPage.svelte
│   │   │       ├── Search.svelte
│   │   │       ├── Th.svelte
│   │   │       ├── ThFilter.svelte
│   │   │       ├── ThSort.svelte
│   │   │       ├── builders/
│   │   │       │   ├── HighlightBuilder.svelte.ts
│   │   │       │   └── ViewBuilder.svelte.ts
│   │   │       ├── clsx/
│   │   │       │   ├── Datatable.svelte
│   │   │       │   ├── Pagination.svelte
│   │   │       │   └── ThSort.svelte
│   │   │       └── index.ts
│   │   └── style.css
│   ├── routes/
│   │   ├── +layout.svelte
│   │   ├── +page.svelte
│   │   ├── Description.svelte
│   │   ├── Header.svelte
│   │   ├── Header_Github.svelte
│   │   ├── Header_MobileNav.svelte
│   │   ├── Header_Mode.svelte
│   │   ├── Header_Theme.svelte
│   │   ├── Header_Version.svelte
│   │   ├── about/
│   │   │   ├── +layout.svelte
│   │   │   ├── +page.svelte
│   │   │   ├── Nav.svelte
│   │   │   ├── Nav_Mobile.svelte
│   │   │   └── [slug]/
│   │   │       ├── +page.server.ts
│   │   │       └── +page.svelte
│   │   ├── api/
│   │   │   └── [mode]/
│   │   │       ├── +layout.server.ts
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.svelte
│   │   │       ├── Nav.svelte
│   │   │       ├── Nav_Key.svelte
│   │   │       ├── Nav_Mobile.svelte
│   │   │       ├── [slug]/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Content.svelte
│   │   │       │   └── Content_Ext.svelte
│   │   │       ├── content.svx
│   │   │       ├── gen/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   └── Board.svelte
│   │   │       └── md/
│   │   │           ├── +layout.svelte
│   │   │           ├── +layout.ts
│   │   │           ├── +page.svelte
│   │   │           ├── Nav.svelte
│   │   │           ├── Nav_Key.svelte
│   │   │           ├── [slug]/
│   │   │           │   ├── +page.svelte
│   │   │           │   ├── +page.ts
│   │   │           │   ├── AST.svelte
│   │   │           │   ├── Content.svelte
│   │   │           │   └── Content_Ext.svelte
│   │   │           └── content.svx
│   │   ├── components/
│   │   │   ├── +layout.svelte
│   │   │   └── +page.svelte
│   │   ├── docs/
│   │   │   ├── client/
│   │   │   │   ├── +layout.svelte
│   │   │   │   ├── +page.server.ts
│   │   │   │   ├── add-on/
│   │   │   │   │   ├── csv-export/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── record-filter/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Main.svelte
│   │   │   │   │       ├── content.svx
│   │   │   │   │       └── data_cars.ts
│   │   │   │   ├── calculation/
│   │   │   │   │   ├── avg/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Advanced.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── bounds/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── data_cars.ts
│   │   │   │   │   ├── data_grocery.ts
│   │   │   │   │   ├── data_parcel.ts
│   │   │   │   │   ├── distinct/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── median/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Advanced.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   └── sum/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Advanced.svelte
│   │   │   │   │       ├── Basic.svelte
│   │   │   │   │       └── code.svx
│   │   │   │   ├── filters/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── check/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Comparators.svelte
│   │   │   │   │   │   ├── Comparators_Check.svelte
│   │   │   │   │   │   ├── content.svx
│   │   │   │   │   │   ├── content2.svx
│   │   │   │   │   │   └── data.ts
│   │   │   │   │   ├── criteria/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   ├── content.svx
│   │   │   │   │   │   └── data.ts
│   │   │   │   │   ├── highlight/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── nested/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Nested.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── getting-started/
│   │   │   │   │   ├── hello-world/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Client.svx
│   │   │   │   │   │   └── Main.svelte
│   │   │   │   │   ├── i18n/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── doc.svx
│   │   │   │   │   ├── intro/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── migration/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── overview/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── pagination/
│   │   │   │   │   ├── navigation/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Navigation.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── pages/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── CurrentPage.svelte
│   │   │   │   │   │   ├── Pages.svelte
│   │   │   │   │   │   ├── PagesWithEllipsis.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── row-count/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── rows-per-page/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── search/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── highlight/
│   │   │   │   │   │   ├── +page.server.ts
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── +page.server.ts
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── recursive/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   ├── Main_Search.svelte
│   │   │   │   │   │   ├── Main_Tree.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── regex/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── scope/
│   │   │   │   │       ├── +page.server.ts
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Example.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── select/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── all/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── row/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── scope/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── sort/
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── dates/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── nested/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Nested.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   └── view/
│   │   │   │       ├── freeze/
│   │   │   │       │   ├── +page.svelte
│   │   │   │       │   ├── Main.svelte
│   │   │   │       │   └── content.svx
│   │   │   │       ├── ordering/
│   │   │   │       │   ├── +page.svelte
│   │   │   │       │   └── content.svx
│   │   │   │       └── visible/
│   │   │   │           ├── +page.svelte
│   │   │   │           ├── Main.svelte
│   │   │   │           └── content.svx
│   │   │   └── server/
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.server.ts
│   │   │       ├── data/
│   │   │       │   ├── invalidate/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── is-loading/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   ├── api.ts
│   │   │       │   │   └── content.svx
│   │   │       │   └── load/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── filters/
│   │   │       │   └── input/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── getting-started/
│   │   │       │   ├── hello-world/
│   │   │       │   │   ├── +page.server.ts
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   ├── api.ts
│   │   │       │   │   └── content.svx
│   │   │       │   ├── i18n/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   └── doc.svx
│   │   │       │   ├── intro/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── migration/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   └── overview/
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       └── content.svx
│   │   │       ├── pagination/
│   │   │       │   ├── navigation/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Navigation.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── pages/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── CurrentPage.svelte
│   │   │       │   │   ├── Pages.svelte
│   │   │       │   │   ├── PagesWithEllipsis.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── row-count/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   └── rows-per-page/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── search/
│   │   │       │   └── input/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── select/
│   │   │       │   ├── +layout.server.ts
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── Main_OLD.svelte
│   │   │       │   ├── all/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── api.ts
│   │   │       │   └── row/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── sort/
│   │   │       │   └── button/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── tips/
│   │   │       │   └── sticky-header/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── doc.svx
│   │   │       └── view/
│   │   │           ├── +layout.server.ts
│   │   │           ├── api.ts
│   │   │           ├── freeze/
│   │   │           │   ├── +page.svelte
│   │   │           │   ├── Main.svelte
│   │   │           │   └── content.svx
│   │   │           ├── ordering/
│   │   │           │   ├── +page.svelte
│   │   │           │   └── content.svx
│   │   │           └── visible/
│   │   │               ├── +page.svelte
│   │   │               ├── Main.svelte
│   │   │               └── content.svx
│   │   ├── examples/
│   │   │   ├── client/
│   │   │   │   ├── +layout.svelte
│   │   │   │   ├── +page.server.ts
│   │   │   │   ├── +page.svelte
│   │   │   │   ├── Test.svelte
│   │   │   │   ├── column-ordering/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   └── SortButton.svelte
│   │   │   │   ├── crud/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Datatable.svelte
│   │   │   │   │   ├── Modal_Create.svelte
│   │   │   │   │   ├── Modal_Destroy.svelte
│   │   │   │   │   ├── Modal_Update.svelte
│   │   │   │   │   ├── api.svelte.ts
│   │   │   │   │   ├── doc.svx
│   │   │   │   │   └── intro.svx
│   │   │   │   ├── distinct/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   ├── RowCount.svelte
│   │   │   │   │   ├── Search.svelte
│   │   │   │   │   ├── code.svx
│   │   │   │   │   └── data.ts
│   │   │   │   ├── hello-world/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   └── page.server.ts
│   │   │   │   ├── nested-array/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   └── Main.svelte
│   │   │   │   ├── pokedex/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── MCF_Main.svelte
│   │   │   │   │   ├── MCF_PokemonStats.svelte
│   │   │   │   │   ├── MCF_Table.svelte
│   │   │   │   │   ├── MCF_TableFilter.svelte
│   │   │   │   │   └── data.ts
│   │   │   │   ├── shadcn/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Table.svelte
│   │   │   │   │   ├── Table_Footer.svelte
│   │   │   │   │   ├── Table_Footer_Pagination.svelte
│   │   │   │   │   ├── Table_Footer_RowSelection.svelte
│   │   │   │   │   ├── Table_Footer_RowsPerPage.svelte
│   │   │   │   │   ├── Table_Header.svelte
│   │   │   │   │   ├── Table_Header_ColumnVisibility.svelte
│   │   │   │   │   ├── Table_Header_Filter.svelte
│   │   │   │   │   ├── Table_Th.svelte
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── test/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Table.svelte
│   │   │   │   │   └── data2.ts
│   │   │   │   ├── test-calculation/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   └── data.ts
│   │   │   │   └── tree/
│   │   │   │       ├── +page.svelte
│   │   │   │       ├── Client.svx
│   │   │   │       ├── Main.svelte
│   │   │   │       ├── Main_Search.svelte
│   │   │   │       └── Main_Tree.svelte
│   │   │   └── server/
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.server.ts
│   │   │       ├── Features.svelte
│   │   │       ├── beer-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── hello-world/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── pokedex-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── PokemonStats.svelte
│   │   │       │   ├── PokemonTypes.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── ssr-user/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   └── params.svelte.ts
│   │   │       ├── todo-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       └── user-api/
│   │   │           ├── +page.server.ts
│   │   │           ├── +page.svelte
│   │   │           ├── Main.svelte
│   │   │           ├── api.ts
│   │   │           └── example.server.ts
│   │   └── export/
│   │       └── [mode]/
│   │           ├── gen/
│   │           │   ├── +page.server.ts
│   │           │   ├── +page.svelte
│   │           │   └── Board.svelte
│   │           └── md/
│   │               ├── +layout.svelte
│   │               ├── +layout.ts
│   │               ├── +page.svelte
│   │               ├── Nav.svelte
│   │               ├── Nav_Key.svelte
│   │               ├── [slug]/
│   │               │   ├── +page.svelte
│   │               │   ├── +page.ts
│   │               │   ├── AST.svelte
│   │               │   ├── Content.svelte
│   │               │   └── Content_Ext.svelte
│   │               └── content.svx
│   └── site/
│       ├── Banner.svelte
│       ├── Logo.svelte
│       ├── Site.svelte.ts
│       ├── components/
│       │   ├── Demo.svelte
│       │   ├── Demo_Code.svelte
│       │   ├── Demo_Code_Icon.svelte
│       │   ├── Demo_Code_Nav.svelte
│       │   ├── Demo_CopyButton.svelte
│       │   ├── Demo_Data.svelte
│       │   ├── Highlight.svelte
│       │   ├── Install.svelte
│       │   ├── Nav_Search.svelte
│       │   ├── docs/
│       │   │   ├── Layout.svelte
│       │   │   ├── Nav.svelte
│       │   │   └── Nav_Mobile.svelte
│       │   └── examples/
│       │       ├── Layout.svelte
│       │       ├── Nav.svelte
│       │       └── Nav_Mobile.svelte
│       ├── data/
│       │   ├── cars.ts
│       │   ├── data.11000.ts
│       │   ├── data.75.ts
│       │   ├── data.ts
│       │   ├── data_with_null.ts
│       │   ├── int-bool-string.ts
│       │   ├── pokedex.ts
│       │   └── tree.ts
│       ├── index.ts
│       └── utils/
│           └── viewport.ts
├── static/
│   ├── documents/
│   │   ├── client/
│   │   │   ├── methods.clearFilters.json
│   │   │   ├── methods.clearSearch.json
│   │   │   ├── methods.clearSelection.json
│   │   │   ├── methods.clearSort.json
│   │   │   ├── methods.createAdvancedFilter.json
│   │   │   ├── methods.createCSV.json
│   │   │   ├── methods.createCalculation.json
│   │   │   ├── methods.createFilter.json
│   │   │   ├── methods.createQuery.json
│   │   │   ├── methods.createRecordFilter.json
│   │   │   ├── methods.createSearch.json
│   │   │   ├── methods.createSort.json
│   │   │   ├── methods.createView.json
│   │   │   ├── methods.filter.json
│   │   │   ├── methods.getSelectedRows.json
│   │   │   ├── methods.getView.json
│   │   │   ├── methods.on.json
│   │   │   ├── methods.select.json
│   │   │   ├── methods.selectAll.json
│   │   │   ├── methods.setPage.json
│   │   │   ├── methods.setRows.json
│   │   │   ├── methods.setRowsPerPage.json
│   │   │   ├── nav.json
│   │   │   ├── properties.allRows.json
│   │   │   ├── properties.clientWidth.json
│   │   │   ├── properties.currentPage.json
│   │   │   ├── properties.element.json
│   │   │   ├── properties.filterCount.json
│   │   │   ├── properties.filters.json
│   │   │   ├── properties.i18n.json
│   │   │   ├── properties.isAllSelected.json
│   │   │   ├── properties.pageCount.json
│   │   │   ├── properties.pages.json
│   │   │   ├── properties.pagesWithEllipsis.json
│   │   │   ├── properties.queries.json
│   │   │   ├── properties.rowCount.json
│   │   │   ├── properties.rows.json
│   │   │   ├── properties.rowsPerPage.json
│   │   │   ├── properties.selected.json
│   │   │   ├── types.Check.json
│   │   │   ├── types.ColumnView.json
│   │   │   ├── types.Criterion.json
│   │   │   ├── types.Field.json
│   │   │   ├── types.Filter.json
│   │   │   ├── types.Internationalization.json
│   │   │   ├── types.Row.json
│   │   │   ├── types.SearchType.json
│   │   │   ├── types.Sort.json
│   │   │   ├── types.SortParams.json
│   │   │   ├── types.TableHandlerParams.json
│   │   │   ├── types.TableParams.json
│   │   │   └── types.ViewColumn.json
│   │   ├── markdown/
│   │   │   ├── client/
│   │   │   │   ├── methods.clearSort.md
│   │   │   │   ├── methods.createAdvancedFilter.md
│   │   │   │   ├── methods.createCSV.md
│   │   │   │   ├── methods.createCalculation.md
│   │   │   │   ├── methods.createRecordFilter.md
│   │   │   │   ├── methods.getSelectedRows.md
│   │   │   │   ├── methods.setRows.md
│   │   │   │   ├── methods.setRowsPerPage.md
│   │   │   │   ├── properties.allRows.md
│   │   │   │   ├── properties.filters.md
│   │   │   │   ├── types.Check.md
│   │   │   │   ├── types.Comparator.md
│   │   │   │   ├── types.Criterion.md
│   │   │   │   ├── types.Filter.md
│   │   │   │   ├── types.Internationalization.md
│   │   │   │   ├── types.Sort.md
│   │   │   │   ├── types.SortParams.md
│   │   │   │   └── types.TableParams.md
│   │   │   ├── methods.clearFilters.md
│   │   │   ├── methods.clearSearch.md
│   │   │   ├── methods.clearSelection.md
│   │   │   ├── methods.createFilter.md
│   │   │   ├── methods.createSearch.md
│   │   │   ├── methods.createSort.md
│   │   │   ├── methods.createView.md
│   │   │   ├── methods.getView.md
│   │   │   ├── methods.on.md
│   │   │   ├── methods.select.md
│   │   │   ├── methods.selectAll.md
│   │   │   ├── methods.setPage.md
│   │   │   ├── methods.setRowsPerPage.md
│   │   │   ├── properties.clientWidth.md
│   │   │   ├── properties.currentPage.md
│   │   │   ├── properties.element.md
│   │   │   ├── properties.filterCount.md
│   │   │   ├── properties.i18n.md
│   │   │   ├── properties.isAllSelected.md
│   │   │   ├── properties.pageCount.md
│   │   │   ├── properties.pages.md
│   │   │   ├── properties.pagesWithEllipsis.md
│   │   │   ├── properties.rowCount.md
│   │   │   ├── properties.rows.md
│   │   │   ├── properties.rowsPerPage.md
│   │   │   ├── properties.selected.md
│   │   │   ├── properties.sort.md
│   │   │   ├── server/
│   │   │   │   ├── methods.getState.md
│   │   │   │   ├── methods.invalidate.md
│   │   │   │   ├── methods.load.md
│   │   │   │   ├── properties.filters.md
│   │   │   │   ├── properties.isLoading.md
│   │   │   │   ├── properties.sort.md
│   │   │   │   ├── properties.totalRows.md
│   │   │   │   ├── types.Filter.md
│   │   │   │   ├── types.Sort.md
│   │   │   │   └── types.State.md
│   │   │   ├── types.ColumnView.md
│   │   │   ├── types.Field.md
│   │   │   ├── types.Internationalization.md
│   │   │   └── types.Row.md
│   │   └── server/
│   │       ├── methods.clearFilters.json
│   │       ├── methods.clearSearch.json
│   │       ├── methods.clearSelection.json
│   │       ├── methods.createFilter.json
│   │       ├── methods.createSearch.json
│   │       ├── methods.createSort.json
│   │       ├── methods.createView.json
│   │       ├── methods.filter.json
│   │       ├── methods.getState.json
│   │       ├── methods.getView.json
│   │       ├── methods.invalidate.json
│   │       ├── methods.load.json
│   │       ├── methods.on.json
│   │       ├── methods.select.json
│   │       ├── methods.selectAll.json
│   │       ├── methods.setPage.json
│   │       ├── methods.setRowsPerPage.json
│   │       ├── methods.setTotalRows.json
│   │       ├── nav.json
│   │       ├── properties.clientWidth.json
│   │       ├── properties.currentPage.json
│   │       ├── properties.debounce.json
│   │       ├── properties.element.json
│   │       ├── properties.events.json
│   │       ├── properties.filterCount.json
│   │       ├── properties.filters.json
│   │       ├── properties.i18n.json
│   │       ├── properties.isAllSelected.json
│   │       ├── properties.isLoading.json
│   │       ├── properties.pageCount.json
│   │       ├── properties.pages.json
│   │       ├── properties.pagesWithEllipsis.json
│   │       ├── properties.rowCount.json
│   │       ├── properties.rows.json
│   │       ├── properties.rowsPerPage.json
│   │       ├── properties.search.json
│   │       ├── properties.selectBy.json
│   │       ├── properties.selected.json
│   │       ├── properties.sort.json
│   │       ├── properties.totalRows.json
│   │       ├── types.ColumnView.json
│   │       ├── types.Field.json
│   │       ├── types.Filter.json
│   │       ├── types.Internationalization.json
│   │       ├── types.Row.json
│   │       ├── types.Sort.json
│   │       ├── types.State.json
│   │       ├── types.TableParams.json
│   │       └── types.ViewColumn.json
│   ├── fonts/
│   │   ├── Inter/
│   │   │   ├── OFL.txt
│   │   │   └── README.txt
│   │   └── Roboto/
│   │       └── LICENSE.txt
│   ├── global.css
│   ├── gros-theme.css
│   ├── gros.css
│   ├── prism-dark.css
│   └── prism-light.css
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/build
/.svelte-kit
/.vscode
/package
/build
.env
.env.*
/dist
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*


================================================
FILE: .npmrc
================================================
engine-strict=true


================================================
FILE: CHANGELOG.md
================================================
# 2.8.0 - 2025-12-17
### Added
[client] Disable filter recursion [#187](https://github.com/vincjo/datatables/issues/187)

```ts
const filter = table.createFilter(field).isNotRecursive()
const advancedFilter = table.createAdvancedFilter(field).isNotRecursive()
```

# 2.7.0 - 2025-11-25
### Fixed
[server] optimize (reduce) the number of reloads by removing irrelevant triggering events

### Changed
Add an optional debounce parameter to the TableHandler. Default = 0
This adds delays before an invalidation is triggered.

# 2.6.6 - 2025-10-08
### Fixed
fix: `RecordFilter` search capabilities.


# 2.6.5 - 2025-10-08
### Fixed
fix: Server-Side pagination - Changing filters resets to page 1 [#179](https://github.com/vincjo/datatables/issues/179)


# 2.6.4 - 2025-09-17
### Fixed
fix: remove console.log in `Calculation.round()`

# 2.6.3 - 2025-09-07
### Fixed
fix: multiple criteria filter (AdvancedFilter) handles nullish values

# 2.6.2 - 2025-07-16
### Fixed 
fix: Broken package with $lib import
https://github.com/vincjo/datatables/issues/169

# 2.6.1 - 2025-07-14
### Fixed
fix: make sure `init()` arg can be undefined in any case.
### Changed
- upgrade Svelte to the latest 5.36.0

# 2.6.0 - 2025-07-14
### Added
feat: add `init()` method to initialize a value in "search", "filter" and "sort".


# 2.5.0 - 2025-02-06
### Added
feat: add the median calculation.
```ts
const median = table.createCalculation('field').median()
```

# 2.4.0 - 2025-01-25
### Added
feat: (experimental) add `queries` to handle filtering inside nested array of objects. 
```ts
table.createQuery('login_count')
    .from(['groups', 'users'])
    .where(check.isGreaterThan)
    .set(1000)

// will check if "user.login_count" is greater than 1000 in users in groups
```
### Fixed
- `selectAll`: remove duplicate keys [#157](https://github.com/vincjo/datatables/issues/157)



# 2.3.1 - 2025-01-21
### Changed
- refactor: core functions has been organized into modules `/client/core/{value, entry, rows, check, field}`
### Fixed
- fix: add "index.js" to make type exports compatible with ES Module

# 2.3.0 - 2025-01-15
### Added
feat: add `search.recursive()` to handle search in tree data structures ([DOC](https://vincjo.fr/datatables/docs/client/search/recursive)).
### Changed
- breaking: search is not recursive by default anymore [#152](https://github.com/vincjo/datatables/issues/152)
- upgrade Svelte to the latest 5.18.0


# 2.2.0 - 2024-12-05
### Added
feat: add `table.clearSort()` method [#150](https://github.com/vincjo/datatables/issues/150)


# 2.1.0 - 2024-11-26
### Fixed
- export type AdvancedFilterBuilder [#145](https://github.com/vincjo/datatables/issues/145)
- use random string instead of `crypto.randomUUID()` [PR #147](https://github.com/vincjo/datatables/pull/147)

# 2.0.5 - 2024-10-24
### Fixed
- fix: `state_unsafe_mutation` error. currentPage is mutated in setters instead: filter.set() / search.set(). [#128](https://github.com/vincjo/datatables/issues/128) [#138](https://github.com/vincjo/datatables/issues/138)



# 2.0.4 - 2024-10-22
### Fixed
- fix: (legacy) remove self closing tags


# 2.0.3 - 2024-10-22
### Changed
- docs: stay on the same page after switch between client-side and server-side navigation
### Fixed
- fix: improve perf in the data filtering function
- fix: prefer unknown type instead of enumerating primitives

# 2.0.2 - 2024-10-21
### Fixed
- fix: remove runes tag from npm publication

# 2.0.1 - 2024-10-21
### Fixed
- fix: `table.select(value: unknown)` select arg type is *unknown* instead of `T[keyof T]`


# 2.0.0 - 2024-10-21
- published major release

# 2.0.0-runes.46 - 2024-10-21
### Fixed
- fix: `EventDispatcher`

# 2.0.0-runes.45 - 2024-10-16
### Fixed
- fix: `clearSearch` maximum call stack exceeded

# 2.0.0-runes.44 - 2024-10-14
### Added
- export type SearchBuilder, CSVBuilder, FilterBuilder, CalculationBuilder...
- added RecordFilter class
### Changed
- feat: handle scrollTop status in setRows method
- fix: replace structureCloned with $state.snapshot()

# 2.0.0-runes.{40,41,42,43} - 2024-10-06
### Fixed
- back to `:global()` style: css import is not working as expected after packaging.

# 2.0.0-runes.39 - 2024-10-06
### Added
- added `TableHandlerInterface` to improve shared components type.
### Changed
- update dependencies: svelte-next.262
- breaking: `table.createCalcultaion().distinct()` parameter is now: `distinct({ sort: [field, direction] })` 

### Fixed
- remove dupplicate builder: `ColumnViewBuilders`
- remove dupplicate type definition: `Row`, `ColumnView`, `Internationalization`, `Field`
- use generic `T` type in `AbstractTableHandler` instead of `Row` (server-side pagination).
- distinct values has a default order in addition to sort param
- fix `$$Generic<Row>` type for server-side pagination
- fix `isAllSelected` when `selectScope` = 'all'


# 2.0.0-runes.38 - 2024-10-04
### Fixed
- CSS import in `<Datatable>` component.

# 2.0.0-runes.37 - 2024-09-29
### Added
- feat: make selectBy parameter type of `Field<Row>` so it can combine multiple fields or use a nested property as identifier
- docs: added a [migration guide](https://github.com/vincjo/datatables/blob/runes/MIGRATION.md)
### Changed
- update dependencies: svelte-next.260
### Fixed
- added type="button" in `<Pagination>` and `<RowCount/>` components, including the legacy part.

# 2.0.0-runes.{33,34,35,36} - 2024-09-16

### Added
- feat: add headless option for Datatable component
- feat: use @import instead of :global() in Datatable component
### Changed
- update dependencies: svelte-next.257

# 2.0.0-runes.32 - 2024-09-16

### Changed
- update dependencies: svelte-next.246


# 2.0.0-runes.31 - 2024-07-25

### Changed
- rename type `ViewColumn` to `ColumnView`
### Fixed
- improve types
- fix criterion value type

# 2.0.0-runes.30 - 2024-07-01

### Fixed
- client-side: refactor / improve types

# 2.0.0-runes.29 - 2024-06-30

### Fixed
- client-side: untrack sort and event dispatcher when rows are updated.


# 2.0.0-runes.28 - 2024-06-30

### Changed
- client-side *(breaking)*:  sort params for distinct values is now an object `{ field: 'value' | 'count', direction: 'asc' | 'desc' }` instead of an array
- client-side: use crypto UUID instead of js random string
- client-side: improve types

# 2.0.0-runes.27 - 2024-06-29

### Added
- client-side: regular expression search takes scope parameter into account

# 2.0.0-runes.26 - 2024-06-27

### Added
- server-side: added a `table.isLoading` state which is true while `invalidate()` method runs

# 2.0.0-runes.25 - 2024-06-26

### Fixed
- server-side: column filtering


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Contributions are welcome.

1. Fork the project
2. ⚠️ Create your feature branch `git checkout -b myfeature`
3. Commit your changes `git commit -m 'feat: my feature'`
4. Push to the branch `git push origin myfeature`
5. Open a Pull Request


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 vincjo

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<div align="center">
    <img align="center" src="./static/logo.svg" alt="logo" width="172"/>
    <p align="center">
        <h1 align="center" style="font-size:32px;margin:0;border:none;">svelte simple datatables</h1>
        <p style="color:#eee">A powerful toolkit for building datatable components.</p>
        <img src="https://img.shields.io/badge/svelte-runes-v2?color=%23c2185b" alt="any text"/>
        <img src="https://img.shields.io/github/license/vincjo/datatables?color=c2185b" alt="last commit"/>
    </p>
</div>



# Docs
Streamline your data workflow with a robust API providing advanced features while reducing code complexity.

:globe_with_meridians: **[vincjo.fr/datatables](https://vincjo.fr/datatables)**



# Install

```apache
npm i -D @vincjo/datatables
```


# Smooth transition from v1 to v2

In order to make the migration process a little easier, v1 is embed in “legacy” namespace so you will have the opportunity to upgrade your components progressively by simply modifying imports.

```diff
- @vincjo/datatables
+ @vincjo/datatables/legacy

- @vincjo/datatables/remote
+ @vincjo/datatables/legacy/remote
```
<br>


# Sample code

```svelte
<script lang="ts">
    import { TableHandler } from '@vincjo/datatables'
    import { someData } from './data'

    const table = new TableHandler(someData, { rowsPerPage: 50 })
</script>

<table>
    <thead>
        <tr>
            <th>First name</th>
            <th>Last name</th>
        </tr>
    </thead>
    <tbody>
        {#each table.rows as row}
            <tr>
                <td>{row.first_name}</td>
                <td>{row.last_name}</td>
            </tr>
        {/each}
    </tbody>
</table>
```





================================================
FILE: datatables.code-workspace
================================================
{
	"folders": [
		{ "path": "./" },
		{ "path": "../autodoc"}
	],
	"settings": {
		"restoreTerminals.terminals": [
			 {
				"profile": "Git Bash",
				"splitTerminals": [{
					"name": "API",
					"commands": ["cd ../autodoc && npm run dev"],
				}],
			}, {
				"profile": "Git Bash",
				"splitTerminals": [{
					"name": "DEV",
					"commands": ["npm run dev"],
				}],
			}, {
				"profile": "Git Bash",
				"splitTerminals": [{
					"name": "Git bash",
					"commands": ["cd ./"],
				}],
			}
		],
		"terminal.integrated.persistentSessionReviveProcess": "never"
	}
}

================================================
FILE: ecosystem.config.cjs
================================================
module.exports = {
    apps: [
        {
            name: 'datatables',
            script: './build/index.js',
            watch: false,
            max_restarts: 10,
            env: {
                NODE_ENV: 'production',
                PORT: 3010
            }
        }
    ],
    deploy: {
        production: {
            user: 'vincjo',
            host: ['vincjo.fr -p 625'],
            ref: 'origin/main',
            repo: 'git@github.com:vincjo/datatables.git',
            path: '/home/vincjo/www/datatables',
            'post-deploy':
                'npm install --force && npm run build && pm2 startOrRestart ecosystem.config.cjs --env production'
        }
    }
}


================================================
FILE: mdsvex.config.js
================================================
import { defineMDSveXConfig as defineConfig } from 'mdsvex'

const config = defineConfig({
    extensions: ['.svelte.md', '.md', '.svx'],

    smartypants: {
        dashes: 'oldschool'
    },

    remarkPlugins: [],
    rehypePlugins: []
})

export default config


================================================
FILE: package.json
================================================
{
    "name": "@vincjo/datatables",
    "version": "2.8.0",
    "keywords": [
        "svelte sveltejs table tables datatable datatables filter headless sort selection lazy-loading"
    ],
    "description": "A powerful toolkit for building datatable components",
    "repository": {
        "type": "git",
        "url": "git+https://github.com/vincjo/datatables.git"
    },
    "author": "vincjo",
    "contributors": [
        "bn-l",
        "jst-r"
    ],
    "license": "MIT",
    "bugs": {
        "url": "https://github.com/vincjo/datatables/issues"
    },
    "homepage": "https://vincjo.fr/datatables",
    "scripts": {
        "dev": "vite dev",
        "build": "vite build",
        "preview": "vite preview",
        "package": "svelte-kit sync && svelte-package",
        "publish:runes": "svelte-kit sync && svelte-package && npm publish",
        "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
        "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
        "format": "prettier --plugin-search-dir . --write .",
        "deploy": "pm2 deploy ecosystem.config.cjs production",
        "deploy:setup": "pm2 deploy ecosystem.config.cjs production setup"
    },
    "devDependencies": {
        "@rollup/plugin-replace": "^6.0.2",
        "@sveltejs/adapter-node": "^5.2.13",
        "@sveltejs/kit": "^2.23.0",
        "@sveltejs/package": "=2.3.7",
        "@sveltejs/vite-plugin-svelte": "^6.0.0",
        "@types/node": "^24.0.13",
        "dotenv": "^17.2.0",
        "gros": "^1.1.3",
        "mdsvex": "^0.12.6",
        "prism-svelte": "^0.5.0",
        "prismjs": "^1.30.0",
        "svelte": "5.36.0",
        "svelte-check": "^4.2.2",
        "svelte-dnd-action": "^0.9.63",
        "svelte-preprocess": "^6.0.3",
        "tslib": "^2.8.1",
        "typescript": "^5.8.3",
        "vite": "^7.0.4"
    },
    "peerDependencies": {
        "svelte": "^5.16.0"
    },
    "type": "module",
    "files": [
        "dist"
    ],
    "svelte": "./dist/index.js",
    "types": "./dist/index.d.ts",
    "exports": {
        ".": {
            "types": "./dist/index.d.ts",
            "css": "./dist/*.css",
            "svelte": "./dist/index.js",
            "default": "./dist/index.js"
        },
        "./server": {
            "types": "./dist/server/index.d.ts",
            "svelte": "./dist/server/index.js",
            "default": "./dist/server/index.js"
        },
        "./legacy": {
            "types": "./dist/legacy/index.d.ts",
            "svelte": "./dist/legacy/index.js",
            "default": "./dist/legacy/index.js"
        },
        "./legacy/remote": {
            "types": "./dist/legacy/remote/index.d.ts",
            "svelte": "./dist/legacy/remote/index.js",
            "default": "./dist/legacy/remote/index.js"
        }
    }
}


================================================
FILE: src/app.d.ts
================================================
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
    // interface Locals {}
    // interface PageData {}
    // interface Error {}
    // interface Platform {}
}


================================================
FILE: src/app.html
================================================
<!DOCTYPE html>
<html lang="en" data-theme="" data-mode="">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%sveltekit.assets%/favicon.png" />
        <link rel="stylesheet" href="%sveltekit.assets%/gros-theme.css" />
        <link rel="stylesheet" href="%sveltekit.assets%/gros.css" />
        <link rel="stylesheet" href="%sveltekit.assets%/global.css" />
        <link rel="stylesheet" href="%sveltekit.assets%/prism-dark.css" />
        <link rel="stylesheet" href="%sveltekit.assets%/prism-light.css" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>svelte simple datatables</title>
		<!-- <meta name="viewport" content="width=1024"> -->
        %sveltekit.head%
    </head>
    <body>
        <div>%sveltekit.body%</div>
    </body>
</html>


================================================
FILE: src/hooks.server.ts
================================================
import type { RequestEvent } from '@sveltejs/kit'


export const handle = async ({ event, resolve }) => {
    const theme = event.cookies.get('siteTheme') ?? 'light'
    const mode = getMode(event)
    return resolve(event, {
        transformPageChunk: ({ html }) => { 
            return html.replace(`data-theme=""`, `data-theme="${theme}"`)
                       .replace(`data-mode=""`, `data-mode="${mode}"`)
        }
    })
}

const getMode = (event: RequestEvent) => {
    if (event.url.pathname.includes('/server')) {
        return 'server'
    }
    else if (event.url.pathname.includes('/client')) {
        return 'client'
    }
    return event.cookies.get('siteMode') ?? 'client'
}

================================================
FILE: src/lib/index.ts
================================================

export {
    // class:
    TableHandler,
    RecordFilter,
    // components:
    Datatable,
    Search,
    RowsPerPage,
    Th,
    ThSort,
    ThFilter,
    Pagination,
    RowCount,
    // utils:
    check,
    // types:
    type Field,
    type Check,
    type Internationalization,
    type Row,
    type TableParams,
    type SortParams,
    type TableHandlerInterface,

    type AdvancedFilterBuilder,
    type CalculationBuilder,
    type CSVBuilder,
    type FilterBuilder,
    type RecordFilterBuilder,
    type SearchBuilder,
    type SortBuilder,

} from '$lib/src/client/index.js'


================================================
FILE: src/lib/legacy/index.ts
================================================
// Reexport your entry components here
import DataHandler  from './local/DataHandler'
import Datatable    from './local/Datatable.svelte'
import Th           from './local/Th.svelte'
import ThFilter     from './local/ThFilter.svelte'
import Pagination   from './local/Pagination.svelte'
import RowCount     from './local/RowCount.svelte'
import RowsPerPage  from './local/RowsPerPage.svelte'
import Search       from './local/Search.svelte'
import { check }    from './local/Comparator'
import type { Internationalization, Row, Field, Comparator, Filter, Sort, Selectable, Order, FilterBy, OrderBy  } from './local'

export { DataHandler, check, Datatable, Th, ThFilter, Pagination, RowCount, RowsPerPage, Search }

export type  {
    Internationalization,
    Row,
    Field,
    Comparator,
    Filter,
    Sort,

    // deprecated
    Selectable,
    Order,
    OrderBy,
    FilterBy,
}

================================================
FILE: src/lib/legacy/local/Comparator.ts
================================================
import type { Criterion } from '$lib/legacy/local'

export const check = {

    isLike: (entry: any, value: any) => {
        return stringify(entry).indexOf(stringify(value)) > -1
    },

    isNotLike: (entry: any, value: any) => {
        return stringify(entry).indexOf(stringify(value)) === -1
    },

    startsWith: (entry: any, value: any) => {
        return stringify(entry).startsWith(stringify(value))
    },

    endsWith: (entry: any, value: any) => {
        return stringify(entry).endsWith(stringify(value))
    },

    isEqualTo: (entry: any, value: any) => {
        return stringify(entry) === stringify(value)
    },

    isNotEqualTo: (entry: any, value: any) => {
        return stringify(entry) !== stringify(value)
    },

    isGreaterThan: (entry: number, value: number) => {
        if (isNull(entry)) return false
        return entry > value
    },

    isGreaterThanOrEqualTo: (entry: number, value: number) => {
        if (isNull(entry)) return false
        return entry >= value
    },

    isLessThan: (entry: number, value: number) => {
        if (isNull(entry)) return false
        return entry < value
    },

    isLessThanOrEqualTo: (entry: number, value: number) => {
        if (isNull(entry)) return false
        return entry <= value
    },

    isBetween: (entry: number, value: [min: number, max: number]) => {
        if (isNull(entry)) return false
        const [min, max] = value
        return entry >= min && entry <= max
    },

    isStrictlyBetween: (entry: number, value: [min: number, max: number]) => {
        if (isNull(entry)) return false
        const [min, max] = value
        return entry > min && entry < max
    },

    isTrue: (entry: boolean, _: any) => {
        return entry === true
    },

    isFalse: (entry: boolean, _: any) => {
        return entry === false
    },

    isNull: (entry: null, _: any) => {
        return entry === null || entry === undefined
    },

    isNotNull: (entry: any, _: any) => {
        return entry === null || entry === undefined ? false : true
    },

    whereIn: (entry: any, values: Criterion[] = []) => {
        if (isNull(entry)) return false
        if (values.length === 0) return false
        for(const { value, comparator } of values) {
            if (comparator(entry, value)) {
                return true
            }
        }
        return false
    },

    /**
     * @deprecated use "isLike" instead 
     * @since 1.12.7 2023-09-27
     */
    contains: (entry: any, value: any) => {
        return check.isLike(entry, value)
    },
}


/* utils */

function stringify(value: string | number | boolean = null) {
    return String(value)
        .toLowerCase()
        .normalize('NFD')
        .replace(/[\u0300-\u036f]/g, '')
}

function isNull(entry: any) {
    if (entry === null || entry === undefined) return true
}

================================================
FILE: src/lib/legacy/local/Context.ts
================================================
import { writable, derived, type Writable, type Readable } from 'svelte/store'
import type { Filter, Sort, Comparator, Criterion, Field } from '$lib/legacy/local'
import type { Params }  from '$lib/legacy/local/DataHandler'
import { isNull, parseField } from './utils'
import { check } from './Comparator'
import EventHandler from './handlers/EventHandler'


export default class Context<Row> 
{
    public event                : EventHandler
    public rowsPerPage          : Writable<number | null>
    public pageNumber           : Writable<number>
    public search               : Writable<{ value?: string, scope?: (keyof Row)[] }>
    public filters              : Writable<Filter<Row>[]>
    public filterCount          : Readable<number>
    public rawRows              : Writable<Row[]>
    public filteredRows         : Readable<Row[]>
    public pagedRows            : Readable<Row[]>
    public rowCount             : Readable<{ total: number, start: number, end: number }>
    public pages                : Readable<number[]>
    public pagesWithEllipsis    : Readable<number[]>
    public pageCount            : Readable<number>
    public sort                 : Writable<(Sort<Row>)>
    public selected             : Writable<(Row | Row[keyof Row])[]>
    public selectScope          : Writable<'all' | 'currentPage'>
    public isAllSelected        : Readable<boolean>

    constructor(data: Row[], params: Params) 
    {
        this.event               = new EventHandler()
        this.rowsPerPage         = writable(params.rowsPerPage)
        this.pageNumber          = writable(1)
        this.search              = writable({})
        this.filters             = writable([])
        this.filterCount         = this.createFilterCount()
        this.rawRows             = writable(data)
        this.filteredRows        = this.createFilteredRows()
        this.pagedRows           = this.createPagedRows()
        this.rowCount            = this.createRowCount()
        this.pages               = this.createPages()
        this.pagesWithEllipsis   = this.createPagesWithEllipsis()
        this.pageCount           = this.createPageCount()
        this.sort                = writable({})
        this.selected            = writable([])
        this.selectScope         = writable('all')
        this.isAllSelected       = this.createIsAllSelected()
    }

    private createFilterCount()
    {
        return derived(this.filters, ($filters) => $filters.length)
    }

    private createFilteredRows() 
    {
        return derived(
            [this.rawRows, this.search, this.filters],
            ([$rawRows, $search, $filters]) => {
                if ($search.value) {
                    $rawRows = $rawRows.filter((row) => {
                        const fields = $search.scope ?? Object.keys(row) as Field<Row>[]
                        const scope = fields.map((field: Field<Row>) => {
                            const { callback } = parseField(field)
                            return callback
                        })
                        return scope.some((callback) => {
                            return this.match(callback(row), $search.value)
                        })
                    })
                    this.pageNumber.set(1)
                    this.selected.set([])
                    this.event.trigger('change')
                }

                if ($filters.length > 0) {
                    $filters.forEach((filter) => {
                        return ($rawRows = $rawRows.filter((row) => {
                            const entry = filter.callback(row)
                            return this.match(entry, filter.value, filter.comparator)
                        }))
                    })
                    this.pageNumber.set(1)
                    this.selected.set([])
                    this.event.trigger('change')
                }
                return $rawRows
            }
        )
    }

    private match(entry: Row[keyof Row], value: string|number|boolean|symbol|number[]|Criterion[], compare: Comparator<Row> = null) 
    {
        if (isNull(value)) {
            return true
        }
        if (!entry && compare) {
            return compare(entry, value)
        }
        if (!entry) return check.isLike(entry, value)
        else if (typeof entry === 'object') {
            return Object.keys(entry).some((k) => {
                return this.match(entry[k], value, compare)
            })
        }
        if (!compare) return check.isLike(entry, value)
        return compare(entry, value)
    }

    private createPagedRows()
    {
        return derived(
            [this.filteredRows, this.rowsPerPage, this.pageNumber],
            ([$filteredRows, $rowsPerPage, $pageNumber]) => {
                if (!$rowsPerPage) {
                    return $filteredRows
                }
                return $filteredRows.slice(
                    ($pageNumber - 1) * $rowsPerPage,
                    $pageNumber * $rowsPerPage
                )
            }
        )
    }

    private createRowCount()
    {
        return derived(
            [this.filteredRows, this.pageNumber, this.rowsPerPage],
            ([$filteredRows, $pageNumber, $rowsPerPage]) => {
                const total = $filteredRows.length
                if (!$rowsPerPage) {
                    return { total: total, start: 1, end: total }
                }
                return {
                    total: total,
                    start: $pageNumber * $rowsPerPage - $rowsPerPage + 1,
                    end: Math.min($pageNumber * $rowsPerPage, $filteredRows.length)
                }
            }
        )
    }

    private createPages()
    {
        return derived([this.rowsPerPage, this.filteredRows], ([$rowsPerPage, $filteredRows]) => {
            if (!$rowsPerPage) {
                return [1]
            }
            const pages = Array.from(Array(Math.ceil($filteredRows.length / $rowsPerPage)))
            return pages.map((_, i) =>  i + 1 )
        })
    }

    private createPagesWithEllipsis()
    {
        return derived([this.pages, this.pageNumber], ([$pages, $pageNumber]) => {
            if ($pages.length <= 7) {
                return $pages
            }
            const ellipse = null
            const firstPage = 1
            const lastPage = $pages.length
            if ($pageNumber <= 4) {
                return [
                    ...$pages.slice(0, 5),
                    ellipse,
                    lastPage
                ]
            } else if ($pageNumber < $pages.length - 3) {
                return [
                    firstPage,
                    ellipse,
                    ...$pages.slice($pageNumber - 2, $pageNumber + 1),
                    ellipse,
                    lastPage
                ]
            } else {
                return [
                    firstPage,
                    ellipse,
                    ...$pages.slice($pages.length - 5,
                    $pages.length)
                ]
            }
        })
    }

    private createPageCount()
    {
        return derived(this.pages, ($pages) => {
            return $pages.length
        })
    }

    private createIsAllSelected()
    {
        return derived(
            [this.selected, this.pagedRows, this.filteredRows, this.selectScope],
            ([$selected, $pagedRows, $filteredRows, $selectScope]) => {
                const rowCount = $selectScope === 'currentPage' ? $pagedRows.length : $filteredRows.length
                if (rowCount === $selected.length && rowCount !== 0) {
                    return true
                }
                return false
            }
        )
    }
}


================================================
FILE: src/lib/legacy/local/DataHandler.ts
================================================
import Context                  from './Context'
import SortHandler              from './handlers/SortHandler'
import SelectHandler            from './handlers/SelectHandler'
import PageHandler              from './handlers/PageHandler'
import SearchHandler            from './handlers/SearchHandler'
import FilterHandler            from './handlers/FilterHandler'

import FilterHelper             from './helpers/FilterHelper'
import AdvancedFilterHelper     from './helpers/AdvancedFilterHelper'
import CalculationHelper        from './helpers/CalculationHelper'

import type { Readable, Writable } from 'svelte/store'
import type { Internationalization, Row, Field, Comparator } from '$lib/legacy/local'

export type Params = { rowsPerPage?: number, i18n?: Internationalization }

export default class DataHandler<T extends Row = any>
{
    private context         : Context<T>
    private sortHandler     : SortHandler<T>
    private selectHandler   : SelectHandler<T>
    private pageHandler     : PageHandler<T>
    private searchHandler   : SearchHandler<T>
    private filterHandler   : FilterHandler<T>
    public  i18n            : Internationalization

    constructor(data: T[] = [], params: Params = { rowsPerPage: null })
    {
        this.i18n           = this.translate(params.i18n)
        this.context        = new Context(data, params)
        this.sortHandler    = new SortHandler(this.context)
        this.selectHandler  = new SelectHandler(this.context)
        this.pageHandler    = new PageHandler(this.context)
        this.searchHandler  = new SearchHandler(this.context)
        this.filterHandler  = new FilterHandler(this.context)
    }

    public setRows(data: T[])
    {
        this.context.rawRows.set(data)
        this.context.event.trigger('change')
        this.applySort()
    }

    public getRows(): Readable<T[]>
    {
        return this.context.pagedRows
    }

    public getAllRows(): Readable<T[]>
    {
        return this.context.filteredRows
    }

    public getRowCount(): Readable<{ total: number, start: number, end: number }>
    {
        return this.context.rowCount
    }

    public getRowsPerPage(): Writable<number | null>
    {
        return this.context.rowsPerPage
    }

    public getPages(param = { ellipsis: false }): Readable<number[]>
    {
        if (param.ellipsis) {
            return this.context.pagesWithEllipsis
        }
        return this.context.pages
    }

    public getPageCount(): Readable<number>
    {
        return this.context.pageCount
    }

    public getPageNumber(): Readable<number>
    {
        return this.context.pageNumber
    }

    public setPage(value: number | 'previous' | 'next'): void
    {
        switch (value) {
            case 'previous' : return this.pageHandler.previous()
            case 'next'     : return this.pageHandler.next()
            default         : return this.pageHandler.goto(value as number)
        }
    }

    public search(value: string, scope: Field<T>[] = null)
    {
        this.searchHandler.set(value, scope)
    }

    public clearSearch()
    {
        this.searchHandler.clear()
    }

    public sort(orderBy: Field<T>, identifier?: string)
    {
        this.setPage(1)
        this.sortHandler.set(orderBy, identifier)
    }

    public sortAsc(orderBy: Field<T>, identifier?: string)
    {
        this.setPage(1)
        this.sortHandler.asc(orderBy, identifier)
    }

    public sortDesc(orderBy: Field<T>, identifier?: string)
    {
        this.setPage(1)
        this.sortHandler.desc(orderBy, identifier)
    }

    public getSort(): Writable<{ identifier?: string, direction?: 'asc' | 'desc' }>
    {
        return this.context.sort
    }

    public applySort( params: { orderBy: Field<T>, direction?: 'asc' | 'desc' } = null )
    {
        this.sortHandler.apply(params)
    }

    public defineSort(orderBy: Field<T>, direction?: 'asc' | 'desc')
    {
        this.sortHandler.define(orderBy, direction)
    }

    public clearSort()
    {
        this.sortHandler.clear()
    }

    public filter( value: string | number | null | undefined | boolean | number[], filterBy: Field<T>, comparator: Comparator<T> = null )
    {
        this.filterHandler.set(value, filterBy, comparator)
    }

    public getFilters()
    {
        return this.filterHandler.get()
    }

    public createFilter( filterBy: Field<T>, comparator?: Comparator<T> )
    {
        return new FilterHelper( this.filterHandler, filterBy, comparator )
    }

    public createAdvancedFilter(filterBy: Field<T>)
    {
        return new AdvancedFilterHelper(this.filterHandler, filterBy)
    }

    public getFilterCount(): Readable<number>
    {
        return this.context.filterCount
    }

    public clearFilters(): void
    {
        this.filterHandler.clear()
    }

    public select(value: T | T[keyof T])
    {
        this.selectHandler.set(value)
    }

    public getSelected()
    {
        return this.context.selected
    }

    public selectAll(params: { selectBy?: keyof T; scope?: 'all' | 'currentPage' } = {}): void
    {
        this.context.selectScope.set(params.scope === 'currentPage' ? 'currentPage' : 'all')
        this.selectHandler.all(params.selectBy ?? null)
    }

    public isAllSelected(): Readable<boolean>
    {
        return this.context.isAllSelected
    }

    public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: () => void)
    {
        this.context.event.add(event, callback)
    }

    public createCalculation(field: Field<T>, param: { precision: number } = null)
    {
        return new CalculationHelper(this.context, field, { precision: param?.precision ?? 2 })
    }

    public translate(i18n: Internationalization): Internationalization
    {
        return {
            ...{
                search: 'Search...',
                show: 'Show',
                entries: 'entries',
                filter: 'Filter',
                rowCount: 'Showing {start} to {end} of {total} entries',
                noRows: 'No entries found',
                previous: 'Previous',
                next: 'Next'
            },
            ...i18n
        }
    }




    /**
     * @deprecated use setRows() instead
     * @since v0.9.99 2023-01-16
     */
    public update(data: any[]): void
    {
        console.log(
            '%c%s',
            'color:#e65100;background:#fff3e0;font-size:12px;border-radius:4px;padding:4px;text-align:center;',
            `DataHandler.update(data) method is deprecated. Please use DataHandler.setRows(data) instead`
        )
        this.context.rawRows.set(data)
    }

    /**
     * @deprecated use applySort() instead
     * @since v1.11.0 2023-07-11
     */
    public applySorting( params: { orderBy: Field<T>, direction?: 'asc' | 'desc' } = null )
    {
        this.applySort(params)
    }

    /**
     * @deprecated use getSort() instead
     * @since v1.11.0 2023-07-11
     */
    public getSorted()
    {
        return this.getSort()
    }


    public getTriggerChange(): Writable<number>
    {
        return this.context.event.triggerChange
    }
}


================================================
FILE: src/lib/legacy/local/Datatable.svelte
================================================
<script lang="ts">
    import { type DataHandler, type Row, Search, RowsPerPage, RowCount, Pagination } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>

    export let search       = true
    export let rowsPerPage  = true
    export let rowCount     = true
    export let pagination   = true

    let element: HTMLElement
    let clientWidth = 1000

    const height = (search || rowsPerPage ? 48 : 8) + (rowCount || pagination ? 48 : 8)

    handler.on('change', () => {
        if (element) element.scrollTop = 0
    })

</script>

<section bind:clientWidth class={$$props.class ?? ''}>
    <header class:container={search || rowsPerPage}>
        {#if search}
            <Search {handler} />
        {/if}
        {#if rowsPerPage}
            <RowsPerPage {handler} small={clientWidth < 600} />
        {/if}
    </header>

    <article bind:this={element} style="height:calc(100% - {height}px)">
        <slot />
    </article>

    <footer class:container={rowCount || pagination}>
        {#if rowCount}
            <RowCount {handler} small={clientWidth < 600} />
        {/if}
        {#if pagination}
            <Pagination {handler} small={clientWidth < 600} />
        {/if}
    </footer>
</section>

<style>
    section {
        height: 100%;
    }

    section :global(table) {
        border-collapse: separate;
        border-spacing: 0;
        width: 100%;
    }

    section :global(thead) {
        position: sticky;
        inset-block-start: 0;
        z-index: 1;
    }

    header,
    footer {
        min-height: 8px;
        padding: 0 16px;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    header.container,
    footer.container {
        height: 48px;
    }
    footer {
        border-top: 1px solid #e0e0e0;
    }

    article {
        position: relative;
        /* height:calc(100% - 96px); */
        overflow: auto;
        scrollbar-width: thin;
    }

    article::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }
    article::-webkit-scrollbar-track {
        background: #f5f5f5;
    }
    article::-webkit-scrollbar-thumb {
        background: #c2c2c2;
    }
    article::-webkit-scrollbar-thumb:hover {
        background: #9e9e9e;
    }
</style>


================================================
FILE: src/lib/legacy/local/Pagination.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false

    const pageNumber = handler.getPageNumber()
    const pageCount = handler.getPageCount()
    const pages = handler.getPages({ ellipsis: true })
</script>

<section class={$$props.class ?? ''}>
    {#if small}
        <button type="button"
            class="small"
            class:disabled={$pageNumber === 1}
            on:click={() => handler.setPage(1)}
        >
            &#10092;&#10092;
        </button>
        <button type="button"
            class:disabled={$pageNumber === 1}
            on:click={() => handler.setPage('previous')}
        >
            &#10094;
        </button>
        <button type="button"
            class:disabled={$pageNumber === $pageCount}
            on:click={() => handler.setPage('next')}
        >
            &#10095;
        </button>
        <button type="button"
            class="small"
            class:disabled={$pageNumber === $pageCount}
            on:click={() => handler.setPage($pageCount)}
        >
            &#10093;&#10093;
        </button>
    {:else}
        <button type="button"
            class:disabled={$pageNumber === 1}
            on:click={() => handler.setPage('previous')}
        >
            {@html handler.i18n.previous}
        </button>
        {#each $pages as page}
            <button type="button"
                class:active={$pageNumber === page}
                class:ellipse={page === null}
                on:click={() => handler.setPage(page)}
            >
                {page ?? '...'}
            </button>
        {/each}
        <button type="button"
            class:disabled={$pageNumber === $pageCount}
            on:click={() => handler.setPage('next')}
        >
            {@html handler.i18n.next}
        </button>
    {/if}
</section>

<style>
    section {
        display: flex;
    }
    button {
        background: inherit;
        height: 32px;
        width: 32px;
        color: #616161;
        cursor: pointer;
        font-size: 13px;
        margin: 0;
        padding: 0;
        transition: all, 0.2s;
        line-height: 32px;
        border: 1px solid #e0e0e0;
        border-right: none;
        border-radius: 0;
        outline: none;
    }
    button:first-child {
        border-radius: 4px 0 0 4px;
    }
    button:last-child {
        border-right: 1px solid #e0e0e0;
        border-radius: 0 4px 4px 0;
    }

    button:first-child:not(.small),
    button:last-child:not(.small) {
        min-width: 72px;
    }

    button:not(.active):hover {
        background: #eee;
    }
    button.ellipse:hover {
        background: inherit;
        cursor: default;
    }
    button.active {
        background: #eee;
        font-weight: bold;
        cursor: default;
    }
    button.disabled:hover {
        background: inherit;
        cursor: default;
    }
</style>


================================================
FILE: src/lib/legacy/local/RowCount.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false
    const rowCount = handler.getRowCount()
</script>

<aside class={$$props.class ?? ''}>
    {#if small}
        {#if $rowCount.total > 0}
            <b>{$rowCount.start}</b>-
            <b>{$rowCount.end}</b>/
            <b>{$rowCount.total}</b>
        {:else}
            {handler.i18n.noRows}
        {/if}
    {:else if $rowCount.total > 0}
        {@html handler.i18n.rowCount
            .replace('{start}', `<b>${$rowCount.start}</b>`)
            .replace('{end}', `<b>${$rowCount.end}</b>`)
            .replace('{total}', `<b>${$rowCount.total}</b>`)}
    {:else}
        {handler.i18n.noRows}
    {/if}
</aside>

<style>
    aside {
        color: #616161;
        line-height: 32px;
        font-size: 14px;
    }
</style>


================================================
FILE: src/lib/legacy/local/RowsPerPage.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false

    const rowsPerPage = handler.getRowsPerPage()

    const options = [5, 10, 20, 50, 100]
</script>

<aside class={$$props.class ?? ''}>
    {#if !small}
        <span>{handler.i18n.show}</span>
    {/if}
    <select bind:value={$rowsPerPage} on:change={() => handler.setPage(1)}>
        {#each options as option}
            <option value={option}>
                {option}
            </option>
        {/each}
    </select>
    {#if !small}
        <span>{handler.i18n.entries}</span>
    {/if}
</aside>

<style>
    aside {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        height: 32px;
        color: #757575;
    }
    select {
        margin: 0 4px;
    }
</style>


================================================
FILE: src/lib/legacy/local/Search.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    let value = ''

    handler.on('clearSearch', () => value = '')
</script>

<input
    class={$$props.class ?? ''}
    bind:value
    placeholder={handler.i18n.search}
    spellcheck="false"
    on:input={() => handler.search(value)}
/>

<style>
    input {
        border: 1px solid #e0e0e0;
        border-radius: 4px;
        outline: none;
        padding: 0 8px;
        line-height: 24px;
        margin: 0;
        height: 24px;
        background: transparent;
        width: 40%;
        max-width: 176px;
        min-width: 96px;
        transition: all, 0.1s;
    }
    input:focus {
        border: 1px solid #bdbdbd;
    }
    input::placeholder {
        color: #9e9e9e;
        line-height: 24px;
    }
</style>


================================================
FILE: src/lib/legacy/local/Th.svelte
================================================
<script lang="ts">
    import type { DataHandler, Field, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let orderBy: Field<T>
    export let identifier = orderBy?.toString()
    export let align: 'left' | 'right' | 'center' = 'left'
    export let rowSpan: number = 1

    const sort = handler.getSort()
</script>

<th
    on:click={() => handler.sort(orderBy, identifier)}
    class:sortable={orderBy}
    class:active={$sort.identifier === identifier}
    class={$$props.class ?? ''}
    rowspan={rowSpan}
>
    <div
        class="flex"
        style:justify-content={align === 'left' ? 'flex-start' : align === 'right' ? 'flex-end' : 'center'}
    >
        <strong>
            <slot />
        </strong>
        <span class:asc={$sort.direction === 'asc'} class:desc={$sort.direction === 'desc'}></span>
    </div>
</th>

<style>
    th {
        background: inherit;
        padding: 8px 20px;
        white-space: nowrap;
        font-size: 13px;
        user-select: none;
        border-bottom: 1px solid #e0e0e0;
    }
    th.sortable {
        cursor: pointer;
    }
    th strong {
        white-space: pre-wrap;
        font-size: 13.5px;
        line-height: 16px;
    }
    th.sortable div.flex {
        padding: 0;
        display: flex;
        align-items: center;
        height: 100%;
    }
    th.sortable span {
        padding-left: 8px;
    }
    th.sortable span:before,
    th.sortable span:after {
        border: 4px solid transparent;
        content: '';
        display: block;
        height: 0;
        width: 0;
    }
    th.sortable span:before {
        border-bottom-color: #e0e0e0;
        margin-top: 2px;
    }
    th.sortable span:after {
        border-top-color: #e0e0e0;
        margin-top: 2px;
    }
    th.active.sortable span.asc:before {
        border-bottom-color: #9e9e9e;
    }
    th.active.sortable span.desc:after {
        border-top-color: #9e9e9e;
    }
    th:not(.sortable) span {
        visibility: hidden;
    }
</style>


================================================
FILE: src/lib/legacy/local/ThFilter.svelte
================================================
<script lang="ts">
    import type { DataHandler, Field, Comparator, Row } from '$lib/legacy/local'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let filterBy: Field<T>
    export let align: 'left' | 'right' | 'center' = 'left'
    export let comparator: Comparator<T> = null

    let value: string = ''

    handler.on('clearFilters', () => value = '')
</script>

<th class={$$props.class ?? ''}>
    <input
        style:text-align={align}
        type="text"
        placeholder={handler.i18n.filter}
        spellcheck="false"
        bind:value
        on:input={() => handler.filter(value, filterBy, comparator)}
    />
</th>

<style>
    th {
        border-bottom: 1px solid #e0e0e0;
    }
    input {
        width: 100%;
        height: 24px;
        border: none;
        text-align: left;
        padding: 0 20px;
        background: inherit;
        outline: none;
        border-radius: 0;
        font-size: 14px;
        font-family: Arial, Helvetica, sans-serif;
    }
    input::placeholder {
        color: #bdbdbd;
        font-style: italic;
        font-size: 13px;
    }
    input:focus {
        outline: none;
        border: none;
    }
</style>


================================================
FILE: src/lib/legacy/local/handlers/EventHandler.ts
================================================
import { writable } from 'svelte/store'


export default class EventHandler
{
    private events = {
        change      : [] as (() => void)[],
        clearFilters: [] as (() => void)[],
        clearSearch : [] as (() => void)[]
    }
    public triggerChange = writable(0)     // legacy


    public add(event: keyof EventHandler['events'], callback: () => void)
    {
        this.events[event].push(callback)
    }

    public trigger(event: keyof EventHandler['events'])
    {
        for (const callback of this.events[event]) {
            callback()
        }
        /* legacy: support for triggerChange store */
        if (event === 'change') {
            this.triggerChange.update((store) => { return store + 1 })
        }
    }
}

================================================
FILE: src/lib/legacy/local/handlers/FilterHandler.ts
================================================
import type { Filter, Field, Comparator, EventHandler, Criterion } from '$lib/legacy/local'
import { isNotNull } from '../utils'
import type Context from '$lib/legacy/local/Context'
import { type Writable, type Readable, derived } from 'svelte/store'
import { parseField } from '$lib/legacy/local/utils'
// import { check } from '$lib/legacy/local/Comparator'

type Value = string | number | null | undefined | boolean | number[] | Criterion[]
// type Collection<Row> = {
//     value: unknown
//     filterBy: Field<Row>
//     set: (value: unknown, comparator: Comparator<Row>) => void,
//     clear: () => void
// }


export default class FilterHandler<Row>
{
    protected filters: Writable<Filter<Row>[]>
    protected event: EventHandler
    private collection: Readable<{ value: unknown, filterBy: Field<Row>, check: string }[]>

    constructor(context: Context<Row>)
    {
        this.filters = context.filters
        this.event   = context.event
    }

    public set(value: Value, filterBy: Field<Row>, comparator: Comparator<Row> = null, name?: string )
    {
        const { callback, identifier, key } = parseField(filterBy, name)
        const filter = { value, identifier, callback, comparator, key }
        this.filters.update((store) => {
            store = store.filter((item) => item.identifier !== identifier)
            if (isNotNull(value)) {
                store.push(filter)
            }
            return store
        })
    }

    public clear()
    {
        this.filters.set([])
        this.event.trigger('change')
        this.event.trigger('clearFilters')
    }

    public get()
    {
        if (this.collection) {
            return this.collection
        }
        this.collection = this.createCollection()
        return this.collection
    }

    private createCollection()
    {
        return derived(this.filters, ($filters) => {
            return $filters.map( ({ value, callback, key, comparator }) => {
                const filterBy = key as Field<Row> ?? callback
                return { 
                    value, 
                    filterBy,
                    check: comparator ? comparator.name : 'isLike'
                    // set: (value: Value, comparator: Comparator<Row> = check.isLike) => {
                    //     this.set(value, filterBy, comparator)
                    // },
                    // clear: () => {
                    //     this.set(undefined, filterBy)
                    // }
                }
            })
        })
    }

}


================================================
FILE: src/lib/legacy/local/handlers/PageHandler.ts
================================================
import type Context from '$lib/legacy/local/Context'
import { type Writable, type Readable, get } from 'svelte/store'
import type { EventHandler } from '$lib/legacy/local'

export default class PageHandler<Row> 
{
    private pageNumber  : Writable<number>
    private rowCount    : Readable<{ total: number, start: number, end: number }>
    private rowsPerPage : Writable<number | null>
    private event       : EventHandler

    constructor(context: Context<Row>) 
    {
        this.pageNumber     = context.pageNumber
        this.rowCount       = context.rowCount
        this.rowsPerPage    = context.rowsPerPage
        this.event          = context.event
    }

    public goto(number: number)
    {
        this.pageNumber.update((store) => {
            const rowsPerPage = get(this.rowsPerPage)
            if (rowsPerPage) {
                const total = get(this.rowCount).total
                if (number >= 1 && number <= Math.ceil(total / rowsPerPage)) {
                    store = number
                    this.event.trigger('change')
                }
            }
            return store
        })
    }

    public previous()
    {
        const number = get(this.pageNumber) - 1
        this.goto(number)
    }

    public next()
    {
        const number = get(this.pageNumber) + 1
        this.goto(number)
    }
}


================================================
FILE: src/lib/legacy/local/handlers/SearchHandler.ts
================================================
import type Context from '$lib/legacy/local/Context'
import type { Writable } from 'svelte/store'
import type { EventHandler, Field } from '$lib/legacy/local'

export default class SearchHandler<Row> 
{
    private search  : Writable<{ value?: string, scope?: Field<Row>[] }>
    private event   : EventHandler

    constructor(context: Context<Row>) 
    {
        this.search = context.search
        this.event  = context.event
    }

    public set(value: string, scope: Field<Row>[] = null)
    {

        this.search.update((store) => {
            store = {
                value: value ?? '',
                scope: scope ?? null,
            }
            return store
        })
    }

    public clear()
    {
        this.search.set({ value: null, scope: null })
        this.event.trigger('change')
        this.event.trigger('clearSearch')
    }
}


================================================
FILE: src/lib/legacy/local/handlers/SelectHandler.ts
================================================
import type Context from '$lib/legacy/local/Context'
import { type Writable, type Readable, get } from 'svelte/store'
import type EventHandler from './EventHandler'



export default class SelectHandler<Row> 
{
    private filteredRows    : Readable<Row[]>
    private pagedRows       : Readable<Row[]>
    private selected        : Writable<(Row | Row[keyof Row])[]>
    private scope           : Writable<'currentPage' | 'all'>
    private isAllSelected   : Readable<boolean>
    private event           : EventHandler

    constructor(context: Context<Row>) 
    {
        this.filteredRows   = context.filteredRows
        this.pagedRows      = context.pagedRows
        this.selected       = context.selected
        this.scope          = context.selectScope
        this.isAllSelected  = context.isAllSelected
        this.event          = context.event
    }

    public set(value: Row[keyof Row] | Row) 
    {
        const selected = get(this.selected)
        if (selected.includes(value)) {
            this.selected.set(selected.filter((item) => item !== value))
        } else {
            this.selected.set([value, ...selected])
        }
    }

    public all(selectBy: keyof Row = null) 
    {
        const isAllSelected = get(this.isAllSelected)
        if (isAllSelected) {
            return this.clear()
        }
        const scope = get(this.scope)
        const rows = scope === 'currentPage' ? get(this.pagedRows) : get(this.filteredRows)

        if (scope === 'currentPage') {
            this.event.add('change', () => this.clear())
        }

        if (selectBy) {
            this.selected.set( rows.map((row) => row[selectBy]) )
        } else {
            this.selected.set(rows)
        }
    }

    public clear() 
    {
        this.selected.set([])
    }
}


================================================
FILE: src/lib/legacy/local/handlers/SortHandler.ts
================================================
import type Context from '$lib/legacy/local/Context'
import type { Sort, Field, EventHandler } from '$lib/legacy/local'
import { type Writable, get } from 'svelte/store'
import { parseField } from '$lib/legacy/local/utils'

export default class SortHandler<Row> 
{
    private rawRows     : Writable<Row[]>
    private event       : EventHandler
    private sort        : Writable<(Sort<Row>)>
    private backup      : Sort<Row>[]

    constructor(context: Context<Row>) 
    {
        this.rawRows    = context.rawRows
        this.event      = context.event
        this.sort       = context.sort
        this.backup     = []
    }

    public set(orderBy: Field<Row> = null, uid?: string)
    {
        if (!orderBy) return
        const sort = get(this.sort)
        const { identifier } = parseField(orderBy, uid)
        if (sort.identifier !== identifier) {
            this.sort.update((store) => (store.direction = null))
        }
        if (sort.direction === null || sort.direction === 'desc') {
            this.asc(orderBy, uid)
        } 
        else if (sort.direction === 'asc') {
            this.desc(orderBy, uid)
        }
    }

    public asc(orderBy: Field<Row>, uid?: string)
    {
        if (!orderBy) return
        const { identifier, callback, key } = parseField(orderBy, uid)
        this.sort.set({ identifier, callback, direction: 'asc', key })
        this.rawRows.update((store) => {
            store.sort((x, y) => {
                const [a, b] = [callback(x), callback(y)]
                if (a === b) return 0
                if (a === null) return 1
                if (b === null) return -1
                if (typeof a === 'boolean') return a === false ? 1 : -1
                if (typeof a === 'string') return a.localeCompare(b as string)
                if (typeof a === 'number') return a - (b as number)
                if (typeof a === 'object') return JSON.stringify(a).localeCompare(JSON.stringify(b))
                else return String(a).localeCompare(String(b))
            })
            return store
        })
        this.log({ identifier, callback, direction: 'asc' })
        this.event.trigger('change')
    }

    public desc(orderBy: Field<Row>, uid?: string)
    {
        if (!orderBy) return
        const { identifier, callback, key } = parseField(orderBy, uid)
        this.sort.set({ identifier, callback, direction: 'desc', key })
        this.rawRows.update((store) => {
            store.sort((x, y) => {
                const [a, b] = [callback(x), callback(y)]
                if (a === b) return 0
                if (a === null) return 1
                if (b === null) return -1
                if (typeof b === 'boolean') return b === false ? 1 : -1
                if (typeof b === 'string') return b.localeCompare(a as string)
                if (typeof b === 'number') return b - (a as number)
                if (typeof b === 'object') return JSON.stringify(b).localeCompare(JSON.stringify(a))
                else return String(b).localeCompare(String(a))
            })
            return store
        })
        this.log({ identifier, callback, direction: 'desc' })
        this.event.trigger('change')
    }

    public apply(params: { orderBy: Field<Row>, direction?: 'asc' | 'desc' } = null) 
    {
        if (params) {
            switch (params.direction) {
                case 'asc' : return this.asc(params.orderBy)
                case 'desc': return this.desc(params.orderBy)
                default    : return this.set(params.orderBy)
            }
        }
        else {
            this.restore()
        }
    }

    public clear()
    {
        this.backup = []
        this.sort.set({})
    }

    public define(orderBy: Field<Row>, direction: 'asc' | 'desc' = 'asc')
    {
        if (!orderBy) return
        const { identifier, callback, key } = parseField(orderBy)
        this.sort.set({ identifier, callback, direction, key })
    }

    private restore()
    {
        for (const sort of this.backup) {
            const { key, callback, direction } = sort
            const param = key as Field<Row> ?? callback
            this[direction](param)
        }
    }

    private log(sort: Sort<Row>)
    {
        this.backup = this.backup.filter(item => item.identifier !== sort.identifier )
        if (this.backup.length >= 3) {
            const [_, slot2, slot3] = this.backup
            this.backup = [slot2, slot3, sort]
        }
        else {
            this.backup = [...this.backup, sort]
        }
    }
}


================================================
FILE: src/lib/legacy/local/helpers/AdvancedFilterHelper.ts
================================================
import type { Field, Comparator, Criterion } from '$lib/legacy/local'
import type FilterHandler from '$lib/legacy/local/handlers/FilterHandler'
import { type Writable, writable } from 'svelte/store'
import { check } from '$lib/legacy/local/Comparator'

type Value = string | number | [min: number, max: number]

export default class AdvancedFilterHandler<Row>
{
    private filterHandler   : FilterHandler<Row>
    private criteria        : Criterion[]
    private filterBy        : Field<Row>
    private selected        : Writable<Value[]>

    constructor(filterHandler: FilterHandler<Row>, filterBy: Field<Row>)
    {
        this.filterHandler  = filterHandler
        this.filterBy       = filterBy
        this.criteria       = []
        this.selected       = writable([])
    }

    public set(value: Value, comparator: Comparator<any> = check.isLike)
    {
        if (this.criteria.find(criterion => criterion.value === value)) {
            this.criteria = this.criteria.filter(criterion => criterion.value !== value)
        }
        else {
            this.criteria = [ { value, comparator }, ...this.criteria ]
        }
        if (this.criteria.length === 0) {
            return this.clear()
        }
        this.filterHandler.set(this.criteria, this.filterBy, check.whereIn)
        this.selected.set(this.criteria.map(criterion => criterion.value))
    }

    public getSelected()
    {
        return this.selected
    }

    public clear()
    {
        this.criteria = []
        this.selected.set([])
        this.filterHandler.set(undefined, this.filterBy, check.whereIn)
    }
}


================================================
FILE: src/lib/legacy/local/helpers/CalculationHelper.ts
================================================
import type { Field } from '$lib/legacy/local'
import type Context from '$lib/legacy/local/Context'
import { type Writable, type Readable, get, derived } from 'svelte/store'
import { parseField } from '$lib/legacy/local/utils' 

export default class CalcultationHandler<Row>
{
    private rawRows: Writable<Row[]>
    private filteredRows: Readable<Row[]>
    private callback: (row: Row) => Row[keyof Row]
    private precision: number

    constructor(context: Context<Row>, field: Field<Row>, param: { precision: number })
    {
        this.rawRows        = context.rawRows
        this.filteredRows   = context.filteredRows
        this.callback       = parseField(field).callback
        this.precision      = param.precision
    }

    public distinct(callback: (values: any[]) => any[] = null)
    {
        const rawRows = get(this.rawRows)
        const values = rawRows.map(row => this.callback(row))
 
        const array = callback ? callback(values) : values

        const result = array.reduce((acc, curr) => {
            acc[curr] = (acc[curr] ?? 0) + 1
            return acc
        }, {})
        return Object.entries(result).map(([value, count]) => ({ value, count }))
    }

    public avg(callback: (values: number[]) => any[] = null)
    {
        return derived(this.filteredRows, $filteredRows => {
            if ($filteredRows.length === 0) return 0
            const values = $filteredRows.map(row => this.callback(row)).filter(Boolean) as number[]
            const array = callback ? callback(values) : values
            return this.round(array.reduce((acc, curr) => acc + curr, 0) / array.length)
        })
    }

    public sum(callback: (values: number[]) => any[] = null)
    {
        return derived(this.filteredRows, $filteredRows => {
            const values = $filteredRows.map(row => this.callback(row)) as number[]
            const array = callback ? callback(values) : values
            return this.round(array.reduce((acc, curr) => acc + curr, 0))
        })
    }

    public bounds(callback: (values: number[]) => any[] = null): [min: number, max: number]
    {
        const rawRows = get(this.rawRows)
        const values = rawRows.map(row => this.callback(row))
 
        const numbers = callback ? callback(values as number[]) : values

        return [
            Math.min(...numbers.filter(Boolean)),
            Math.max(...numbers.filter(Boolean))
        ]
    }

    public setPrecision(value: number)
    {
        this.precision = value
    }

    private round(value: number)
    {
        if (this.precision === 0) {
            return Math.round(value)
        }
        const denominator = Math.pow(10, this.precision)
        return Math.round( (value + Number.EPSILON) * denominator ) / denominator
    }
}

================================================
FILE: src/lib/legacy/local/helpers/FilterHelper.ts
================================================
import type { Field, Comparator } from '$lib/legacy/local'
import { check } from '$lib/legacy/local/Comparator'
import type FilterHandler from '../handlers/FilterHandler'

type Value = string | number | boolean

export default class FilterHelper<Row>
{
    private filterHandler   : FilterHandler<Row>
    private filterBy        : Field<Row>
    private uid             : string
    private comparator      : Comparator<Row>
    private callback        : () => void

    constructor(filterHandler: FilterHandler<Row>, filterBy: Field<Row>, comparator?: Comparator<Row>)
    {
        this.filterHandler  = filterHandler
        this.filterBy       = filterBy
        this.uid            = 'f_' + (Math.random()).toString(28).substring(2)
        this.comparator     = comparator ?? check.isLike
        this.callback       = () => null
    }

    public set(value: Value, comparator?: Comparator<any>)
    {
        if (comparator) {
            this.comparator = comparator
        }
        this.filterHandler.set(value, this.filterBy, this.comparator, this.uid)
    }

    public clear()
    {
        this.callback()
        this.filterHandler.set(undefined, this.filterBy)
    }

    public on(event: 'clear', callback: () => void)
    {
        this.callback = callback
    }
}


================================================
FILE: src/lib/legacy/local/index.ts
================================================
// Reexport your entry components here
import DataHandler  from './DataHandler'
import Datatable    from './Datatable.svelte'
import Th           from './Th.svelte'
import ThFilter     from './ThFilter.svelte'
import Pagination   from './Pagination.svelte'
import RowCount     from './RowCount.svelte'
import RowsPerPage  from './RowsPerPage.svelte'
import Search       from './Search.svelte'
import { check }    from './Comparator'

export { DataHandler, check, Datatable, Th, ThFilter, Pagination, RowCount, RowsPerPage, Search }

export type { default as EventHandler } from './handlers/EventHandler'

export type Internationalization = {
    search?: string
    show?: string
    entries?: string
    filter?: string
    rowCount?: string
    noRows?: string
    previous?: string
    next?: string
}

export type Row = { [key: string]: any  }
export type Field<Row> = keyof Row | ((row: Row) => Row[keyof Row])


export type Comparator<Row> = (entry: Row[keyof Row], value: any) => boolean

export type Criterion = { value: string | number | [min: number, max: number], comparator: Comparator<Row> }

export type Filter<Row> = {
    callback: (row: Row) => Row[keyof Row]
    identifier: string
    value?: string | number | boolean | symbol | Criterion[] | number[]
    comparator?: Comparator<Row>
    key?: string
}

export type Sort<Row> = {
    callback?: (row: Row) => Row[keyof Row]
    identifier?: string
    direction?: 'asc' | 'desc'
    key?: string
}




/**
 * @deprecated use (Row[keyof Row] | Row) instead
 * 
 * import type { Row } from '@vincjo/datatables'
 */
export type Selectable<Row> = Row[keyof Row] | Row

/**
 * @deprecated use type Field<Row> instead
 * 
 * import type { Field } from '@vincjo/datatables'
 */
export type FilterBy<Row> = Field<Row>

/**
 * @deprecated use type Field<Row> instead
 * 
 * import type { Field } from '@vincjo/datatables'
 */
export type OrderBy<Row>  = Field<Row>

/**
 * @deprecated use type Sort<Row> instead
 * 
 * import type { Sort } from '@vincjo/datatables'
 */
export type Order<Row>  = Sort<Row>

================================================
FILE: src/lib/legacy/local/utils.ts
================================================
import type { Row, Field } from '$lib/legacy/local'

export const isNull = (value: any) => {
    if (value === null || value === undefined || value === '') return true
    return false
}
export const isNotNull = (value: any) => { return !isNull(value) }

export const parseField = (field: Field<any>, uid?: string) => {
    const identifier = uid ?? field.toString()
    if (typeof field === 'string') {
        return {
            callback: (row: Row) => row[field],
            identifier,
            key: field as string
        }
    } else if (typeof field === 'function') {
        return {
            callback: field,
            identifier,
            key: undefined
        }
    }
    throw new Error(`Invalid field argument: ${String(field)}`)
}



================================================
FILE: src/lib/legacy/remote/Context.ts
================================================
import { type Writable, writable, get, derived, type Readable } from 'svelte/store'
import type { State, Sort, Filter } from '$lib/legacy/remote'
import type { Params }  from './DataHandler'
import EventHandler from './handlers/EventHandler'

export default class Context<Row>
{
    public totalRows            : Writable<number | undefined>
    public rowsPerPage          : Writable<number>
    public pageNumber           : Writable<number>
    public event                : EventHandler
    public search               : Writable<string>
    public filters              : Writable<Filter<Row>[]>
    public rows                 : Writable<Row[]>
    public rowCount             : Readable<{ total: number, start: number, end: number }>
    public pages                : Readable<number[]>
    public pagesWithEllipsis    : Readable<number[]>
    public pageCount            : Readable<number>
    public sort                 : Writable<Sort<Row>>
    public selected             : Writable<(Row | Row[keyof Row])[]>
    public isAllSelected        : Readable<boolean>
    public selectedCount        : Readable<{ count: number, total: number }>
    public selectBy             : keyof Row | undefined


    constructor(data: Row[], params: Params)
    {
        this.totalRows          = writable(params.totalRows)
        this.rowsPerPage        = writable(params.rowsPerPage)
        this.pageNumber         = writable(1)
        this.event              = new EventHandler()
        this.search             = writable('')
        this.filters            = writable([])
        this.rows               = writable(data)
        this.rowCount           = this.createRowCount()
        this.pages              = this.createPages()
        this.pagesWithEllipsis  = this.createPagesWithEllipsis()
        this.pageCount          = this.createPageCount()
        this.sort               = writable(undefined)
        this.selected           = writable([])
        this.isAllSelected      = this.createIsAllSelected()
        this.selectedCount      = this.createSelectedCount()
        this.selectBy           = params.selectBy as keyof Row ?? undefined
    }

    public getState(): State
    {
        const pageNumber    = get(this.pageNumber)
        const rowsPerPage   = get(this.rowsPerPage)
        const sort          = get(this.sort)
        const filters       = get(this.filters)
        return {
            pageNumber,
            rowsPerPage,
            offset: rowsPerPage * (pageNumber - 1),
            search: get(this.search),
            sorted: sort ?? undefined as any, // deprecated
            sort: sort ?? undefined as any,
            filters: filters.length > 0 ? filters : undefined as any,
            setTotalRows: (value: number) => this.totalRows.set(value)
        }
    }

    private createPages()
    {
        return derived([this.rowsPerPage, this.totalRows], ([$rowsPerPage, $totalRows]) => {
            if (!$rowsPerPage || !$totalRows) {
                return undefined
            }
            const pages = Array.from(Array(Math.ceil($totalRows / $rowsPerPage)))
            return pages.map((_, i) => {
                return i + 1
            })
        })
    }

    private createPagesWithEllipsis()
    {
        return derived([this.pages, this.pageNumber], ([$pages, $pageNumber]) => {
            if (!$pages) {
                return undefined
            }
            if ($pages.length <= 7) {
                return $pages
            }
            const ellipse = null
            const firstPage = 1
            const lastPage = $pages.length
            if ($pageNumber <= 4) {
                return [
                    ...$pages.slice(0, 5),
                    ellipse,
                    lastPage
                ]
            } else if ($pageNumber < $pages.length - 3) {
                return [
                    firstPage,
                    ellipse,
                    ...$pages.slice($pageNumber - 2, $pageNumber + 1),
                    ellipse,
                    lastPage
                ]
            } else {
                return [
                    firstPage,
                    ellipse,
                    ...$pages.slice($pages.length - 5,
                    $pages.length)
                ]
            }
        })
    }

    private createPageCount()
    {
        return derived(this.pages, ($pages) => {
            if (!$pages) return undefined
            return $pages.length
        })
    }

    private createRowCount()
    {
        return derived(
            [this.totalRows, this.pageNumber, this.rowsPerPage],
            ([$totalRows, $pageNumber, $rowsPerPage]) => {

                if (!$rowsPerPage || !$totalRows) {
                    return undefined
                }
                return {
                    total: $totalRows,
                    start: $pageNumber * $rowsPerPage - $rowsPerPage + 1,
                    end: Math.min($pageNumber * $rowsPerPage, $totalRows)
                }
            }
        )
    }

    private createIsAllSelected()
    {
        return derived([this.selected, this.rows], ([$selected, $rows]) => {
            if ($rows.length === 0) {
                return false
            }
            if (this.selectBy) {
                const ids = $rows.map(row => row[this.selectBy])
                return ids.every(id => $selected.includes(id))
            }
            return $rows.every(row => $selected.includes(row as Row))
        })
    }

    private createSelectedCount()
    {
        return derived(
            [this.selected, this.totalRows],
            ([$selected, $totalRows]) => {
                return {
                    count: $selected.length,
                    total: $totalRows
                }
            }
        )
    }
}

================================================
FILE: src/lib/legacy/remote/DataHandler.ts
================================================
import Context          from './Context'
import TriggerHandler   from './handlers/TriggerHandler'
import SortHandler      from './handlers/SortHandler'
import SelectHandler    from './handlers/SelectHandler'
import PageHandler      from './handlers/PageHandler'
import SearchHandler    from './handlers/SearchHandler'
import FilterHandler    from './handlers/FilterHandler'

import type { Writable, Readable } from 'svelte/store'
import type { Internationalization, Row, State, Sort } from '$lib/legacy/remote'

export type Params = {
    rowsPerPage     ?: number,
    totalRows       ?: number,
    selectBy        ?: keyof Row,
    i18n            ?: Internationalization
}

export default class DataHandler<T extends Row = any>
{
    private context         : Context<T>
    private triggerHandler  : TriggerHandler<T>
    private sortHandler     : SortHandler<T>
    private selectHandler   : SelectHandler<T>
    private pageHandler     : PageHandler<T>
    private searchHandler   : SearchHandler<T>
    private filterHandler   : FilterHandler<T>
    public i18n             : Internationalization

    constructor(data: T[] = [], params: Params = { rowsPerPage: 5 })
    {
        this.i18n           = this.translate(params.i18n)
        this.context        = new Context(data, params)
        this.triggerHandler = new TriggerHandler(this.context)
        this.sortHandler    = new SortHandler(this.context)
        this.selectHandler  = new SelectHandler(this.context)
        this.pageHandler    = new PageHandler(this.context)
        this.searchHandler  = new SearchHandler(this.context)
        this.filterHandler  = new FilterHandler(this.context)
    }

    public onChange(callback: (state: State) => Promise<T[]>)
    {
        this.triggerHandler.set(callback)
    }

    public invalidate()
    {
        this.triggerHandler.invalidate()
    }

    public setRows(data: T[])
    {
        this.context.rows.set(data)
    }

    public setTotalRows(value: number)
    {
        this.context.totalRows.set(value)
    }

    public getRows(): Writable<T[]>
    {
        return this.context.rows
    }

    public select(value: T[keyof T] | T)
    {
        this.selectHandler.set(value)
    }

    public getSelected()
    {
        return this.context.selected
    }

    public selectAll(): void
    {
        this.selectHandler.all()
    }

    public isAllSelected(): Readable<boolean>
    {
        return this.context.isAllSelected
    }

    public getSelectedCount(): Readable<{ count: number, total: number }>
    {
        return this.context.selectedCount
    }

    public clearSelection()
    {
        this.selectHandler.clear()
    }

    public getRowsPerPage(): Writable<number | null>
    {
        return this.context.rowsPerPage
    }

    public sort(orderBy: keyof T)
    {
        this.setPage(1)
        this.sortHandler.set(orderBy)
    }

    public applySort( params: { orderBy:  keyof T, direction?: 'asc' | 'desc' } = null )
    {
        this.sortHandler.apply(params)
    }

    public sortAsc(orderBy: keyof T)
    {
        this.setPage(1)
        this.sortHandler.asc(orderBy)
    }

    public sortDesc(orderBy: keyof T)
    {
        this.setPage(1)
        this.sortHandler.desc(orderBy)
    }

    public getSort(): Writable<Sort<T>>
    {
        return this.context.sort
    }

    public search(value: string): void
    {
        this.setPage(1)
        this.context.search.set(value)
    }

    public clearSearch()
    {
        this.searchHandler.remove()
    }

    public filter(value: string | number, filterBy: keyof T)
    {
        this.setPage(1)
        return this.filterHandler.set(value, filterBy)
    }

    public clearFilters(): void
    {
        this.filterHandler.remove()
    }

    public getPages(params = { ellipsis: false }): Readable<number[]>
    {
        if (params.ellipsis) {
            return this.context.pagesWithEllipsis
        }
        return this.context.pages
    }

    public getPageCount(): Readable<number>
    {
        return this.context.pageCount
    }

    public getPageNumber(): Writable<number>
    {
        return this.context.pageNumber
    }

    public setPage(value: number | 'previous' | 'next'): void
    {
        switch (value) {
            case 'previous' : return this.pageHandler.previous()
            case 'next'     : return this.pageHandler.next()
            default         : return this.pageHandler.goto(value as number)
        }
    }

    public getRowCount(): Readable<{ total: number, start: number, end: number }>
    {
        return this.context.rowCount
    }

    public on(event: 'change', callback: () => void)
    {
        this.context.event.add(event, callback)
    }

    public translate(i18n: Internationalization): Internationalization
    {
        return {
            ...{
                search: 'Search...',
                show: 'Show',
                entries: 'entries',
                filter: 'Filter',
                rowCount: 'Showing {start} to {end} of {total} entries',
                noRows: 'No entries found',
                previous: 'Previous',
                next: 'Next',
                selectedCount: '{count} of {total} row(s).'
            },
            ...i18n
        }
    }



    /**
     *
     * @depracted use on('change', callback) instead
     */
    public getTriggerChange(): Writable<number>
    {
        return this.context.event.triggerChange
    }

    /**
     *
     * @deprecated use applySort() instead
     */
    public applySorting( params: { orderBy:  keyof T, direction?: 'asc' | 'desc' } = null )
    {
        this.applySort(params)
    }


    /**
     *
     * @deprecated use getSort() instead
     */
    public getSorted()
    {
        return this.getSort()
    }
}

================================================
FILE: src/lib/legacy/remote/Datatable.svelte
================================================
<script lang="ts">
    import { type DataHandler, type Row, Search, RowsPerPage, RowCount, SelectedCount, Pagination } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>

    export let search         = true
    export let rowsPerPage    = true
    export let rowCount       = true
    export let selectedCount  = false
    export let pagination     = true

    let element: HTMLElement
    let clientWidth = 1000

    const height = (search || rowsPerPage ? 48 : 8) + (rowCount || selectedCount || pagination ? 48 : 8)

    handler.on('change', () => {
        if (element) element.scrollTop = 0
    })
</script>

<section bind:clientWidth class={$$props.class ?? ''}>
    <header class:container={search || rowsPerPage}>
        {#if search}
            <Search {handler} />
        {:else}
            <div></div>
        {/if}
        {#if rowsPerPage}
            <RowsPerPage {handler} small={clientWidth < 600} />
        {/if}
    </header>

    <article bind:this={element} style="height:calc(100% - {height}px)">
        <slot />
    </article>

    <footer class:container={rowCount || pagination}>
        {#if selectedCount}
            <SelectedCount {handler}/>
        {:else if rowCount}
            <RowCount {handler} small={clientWidth < 600} />
        {/if}
        {#if pagination}
            <Pagination {handler} small={clientWidth < 600} />
        {/if}
    </footer>
</section>

<style>
    section {
        height: 100%;
    }

    section :global(table) {
        border-collapse: separate;
        border-spacing: 0;
        width: 100%;
    }

    section :global(thead) {
        position: sticky;
        inset-block-start: 0;
        z-index: 1;
    }

    header,
    footer {
        min-height: 8px;
        padding: 0 16px;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    header.container,
    footer.container {
        height: 48px;
    }
    footer {
        border-top: 1px solid #e0e0e0;
    }

    article {
        position: relative;
        /* height:calc(100% - 96px); */
        overflow: auto;
        scrollbar-width: thin;
    }

    article::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }
    article::-webkit-scrollbar-track {
        background: #f5f5f5;
    }
    article::-webkit-scrollbar-thumb {
        background: #c2c2c2;
    }
    article::-webkit-scrollbar-thumb:hover {
        background: #9e9e9e;
    }
</style>


================================================
FILE: src/lib/legacy/remote/Pagination.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false

    const pageNumber = handler.getPageNumber()
    const pageCount = handler.getPageCount()
    const pages = handler.getPages({ ellipsis: true })

    const setPage = (value: 'previous' | 'next' | number) => {
        handler.setPage(value)
        handler.invalidate()
    }
</script>

<section class={$$props.class ?? ''}>
    {#if $pages === undefined}
        <button type="button" 
            class="small"
            class:disabled={$pageNumber === 1}
            on:click={() => setPage('previous')}
        >
            &#10094;
        </button>
        <button type="button" class="page">page <b>{$pageNumber}</b></button>
        <button type="button" 
            class="small"
            on:click={() => setPage('next')}
        >
            &#10095;
        </button>
    {:else}
        {#if small}
            <button type="button"
                class="small"
                class:disabled={$pageNumber === 1}
                on:click={() => setPage(1)}
            >
                &#10092;&#10092;
            </button>
            <button type="button"
                class:disabled={$pageNumber === 1}
                on:click={() => setPage('previous')}
            >
                &#10094;
            </button>
            <button type="button"
                class:disabled={$pageNumber === $pageCount}
                on:click={() => setPage('next')}
            >
                &#10095;
            </button>
            <button type="button"
                class="small"
                class:disabled={$pageNumber === $pageCount}
                on:click={() => setPage($pageCount)}
            >
                &#10093;&#10093;
            </button>
        {:else}
            <button type="button"
                class:disabled={$pageNumber === 1}
                on:click={() => setPage('previous')}
            >
                {@html handler.i18n.previous}
            </button>
            {#each $pages as page}
                <button type="button"
                    class:active={$pageNumber === page}
                    class:ellipse={page === null}
                    on:click={() => setPage(page)}
                >
                    {page ?? '...'}
                </button>
            {/each}
            <button type="button"
                class:disabled={$pageNumber === $pageCount}
                on:click={() => setPage('next')}
            >
                {@html handler.i18n.next}
            </button>
        {/if}
    {/if}
</section>

<style>
    section {
        display: flex;
    }
    button {
        background: inherit;
        height: 32px;
        width: 32px;
        color: #616161;
        cursor: pointer;
        font-size: 13px;
        margin: 0;
        padding: 0;
        transition: all, 0.2s;
        line-height: 32px;
        border: 1px solid #e0e0e0;
        border-right: none;
        border-radius: 0;
        outline: none;
    }
    button:first-child {
        border-radius: 4px 0 0 4px;
    }
    button:last-child {
        border-right: 1px solid #e0e0e0;
        border-radius: 0 4px 4px 0;
    }

    button:first-child:not(.small),
    button:last-child:not(.small) {
        min-width: 72px;
    }

    button:not(.active):hover {
        background: #eee;
    }
    button.disabled:hover {
        background: inherit;
        cursor: default;
    }
    button.page {
        width: 72px;
        background: #fafafa;
    }
    button.page:hover {
        background: #fafafa;
        cursor: default;
    }
    button.ellipse:hover {
        background: inherit;
        cursor: default;
    }
    button.active {
        background: #eee;
        font-weight: bold;
        cursor: default;
    }
    button.disabled {
        color: #bdbdbd;
    }
    button.disabled:hover {
        background: inherit;
        cursor: default;
    }
</style>


================================================
FILE: src/lib/legacy/remote/README.md
================================================
<div align="center">
    <img align="center" src="../../../static/logo-remote.svg" alt="logo" width="172"/>
    <p align="center">
        <h1 align="center" style="font-size:32px;margin:0;border:none;">svelte simple datatables</h1>
        <p style="color:#eee">For server-side pagination</p>
        <img src="https://img.shields.io/npm/v/@vincjo/datatables?color=%23006990" alt="npm version"/>
        <img src="https://img.shields.io/github/license/vincjo/datatables?color=006990" alt="last commit"/>
    </p>
</div>


**&rarr; [Home](https://vincjo.fr/datatables/remote/home)**

**&rarr; [Examples](https://vincjo.fr/datatables/remote/examples)**

================================================
FILE: src/lib/legacy/remote/RowCount.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false
    const rowCount = handler.getRowCount()
</script>

{#if $rowCount === undefined}
    <div></div>
{:else}
    <aside class={$$props.class ?? ''}>
        {#if small}
            {#if $rowCount.total > 0}
                <b>{$rowCount.start}</b>-
                <b>{$rowCount.end}</b>/
                <b>{$rowCount.total}</b>
            {:else}
                {handler.i18n.noRows}
            {/if}
        {:else if $rowCount.total > 0}
            {@html handler.i18n.rowCount
                .replace('{start}', `<b>${$rowCount.start}</b>`)
                .replace('{end}',   `<b>${$rowCount.end}</b>`)
                .replace('{total}', `<b>${$rowCount.total}</b>`)}
        {:else}
            {handler.i18n.noRows}
        {/if}
    </aside>
{/if}


<style>
    aside {
        color: #616161;
        line-height: 32px;
        font-size: 14px;
    }
</style>


================================================
FILE: src/lib/legacy/remote/RowsPerPage.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let small = false
    export let options = [5, 10, 20]

    const rowsPerPage = handler.getRowsPerPage()

    const setRowsPerPage = () => {
        handler.setPage(1)
        handler.invalidate()
    }
</script>

<aside class={$$props.class ?? ''}>
    {#if !small}
        <span>{handler.i18n.show}</span>
    {/if}
    <select bind:value={$rowsPerPage} on:change={setRowsPerPage}>
        {#each options as option}
            <option value={option}>
                {option}
            </option>
        {/each}
    </select>
    {#if !small}
        <span>{handler.i18n.entries}</span>
    {/if}
</aside>

<style>
    aside {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        height: 32px;
        color: #757575;
    }
    select {
        margin: 0 4px;
    }
</style>


================================================
FILE: src/lib/legacy/remote/Search.svelte
================================================
<script lang="ts">
	import type { DataHandler, Row } from '$lib/legacy/remote'

	type T = $$Generic<Row>

	export let handler: DataHandler<T>

	let value = ''
	let timeout: any

	const search = () => {
		handler.search(value)
		clearTimeout(timeout)
		timeout = setTimeout( () => {
            handler.invalidate()
		}, 400)
	}
</script>

<input
	class={$$props.class ?? ''}
	bind:value
	placeholder={handler.i18n.search}
	spellcheck="false"
	on:input={search}
/>

<style>
	input {
		border: 1px solid #e0e0e0;
		border-radius: 4px;
		outline: none;
		padding: 0 8px;
		line-height: 24px;
		margin: 0;
		height: 24px;
		background: transparent;
		width: 40%;
		max-width: 176px;
		min-width: 96px;
		transition: all, 0.1s;
	}
	input:focus {
		border: 1px solid #bdbdbd;
	}
	input::placeholder {
		color: #9e9e9e;
		line-height: 24px;
	}
</style>


================================================
FILE: src/lib/legacy/remote/SelectedCount.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    const selectedCount = handler.getSelectedCount()
</script>


<aside class={$$props.class ?? ''}>

    <b>{$selectedCount.count}</b> 
    {#if $selectedCount.total}
        of <b>{$selectedCount.total}</b>
    {/if}
    row(s) selected.
    {#if $selectedCount.count > 0}
        <button type="button" on:click={() => handler.clearSelection()}>❌ Clear</button>
    {/if}
</aside>


<style>
    aside {
        color: #616161;
        line-height: 32px;
        font-size: 14px;
    }
    button {
        background: #fafafa;
        padding: 2px 4px;
        border-radius: 2px;
        cursor: pointer;
        border: 1px solid #e0e0e0;
    }
</style>


================================================
FILE: src/lib/legacy/remote/Th.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let orderBy: keyof T
    export let align: 'left' | 'right' | 'center' = 'left'
    export let rowSpan: number = 1

    const sort = handler.getSort()
    const update = () => {
        handler.sort(orderBy)
        handler.invalidate()
    }
</script>

<th
    on:click={update}
    class:sortable={orderBy}
    class:active={$sort?.orderBy === orderBy}
    class={$$props.class ?? ''}
    rowspan={rowSpan}
>
    <div
        class="flex"
        style:justify-content={align === 'left' ? 'flex-start' : align === 'right' ? 'flex-end' : 'center'}
    >
        <strong>
            <slot />
        </strong>
        <span class:asc={$sort?.direction === 'asc'} class:desc={$sort?.direction === 'desc'}></span>
    </div>
</th>

<style>
    th {
        background: inherit;
        padding: 8px 20px;
        white-space: nowrap;
        font-size: 13px;
        user-select: none;
        border-bottom: 1px solid #e0e0e0;
    }
    th.sortable {
        cursor: pointer;
    }
    th strong {
        white-space: pre-wrap;
        font-size: 13.5px;
        line-height: 16px;
    }
    th.sortable div.flex {
        padding: 0;
        display: flex;
        align-items: center;
        height: 100%;
    }
    th.sortable span {
        padding-left: 8px;
    }
    th.sortable span:before,
    th.sortable span:after {
        border: 4px solid transparent;
        content: '';
        display: block;
        height: 0;
        width: 0;
    }
    th.sortable span:before {
        border-bottom-color: #e0e0e0;
        margin-top: 2px;
    }
    th.sortable span:after {
        border-top-color: #e0e0e0;
        margin-top: 2px;
    }
    th.active.sortable span.asc:before {
        border-bottom-color: #9e9e9e;
    }
    th.active.sortable span.desc:after {
        border-top-color: #9e9e9e;
    }
    th:not(.sortable) span {
        visibility: hidden;
    }
</style>


================================================
FILE: src/lib/legacy/remote/ThFilter.svelte
================================================
<script lang="ts">
    import type { DataHandler, Row } from '$lib/legacy/remote'

    type T = $$Generic<Row>

    export let handler: DataHandler<T>
    export let filterBy: keyof T
    export let align: 'left' | 'right' | 'center' = 'left'

    let value: string = ''
	let timeout: any 

    const filter = () => {
        handler.filter(value, filterBy)
		clearTimeout(timeout)
		timeout = setTimeout( () => {
            handler.invalidate()
		}, 400)
	}
</script>

<th class={$$props.class ?? ''}>
    <input
        style:text-align={align}
        type="text"
        placeholder={handler.i18n.filter}
        spellcheck="false"
        bind:value
        on:input={filter}
    />
</th>

<style>
    th {
        border-bottom: 1px solid #e0e0e0;
    }
    input {
        width: 100%;
        height: 24px;
        border: none;
        text-align: left;
        padding: 0 20px;
        background: inherit;
        outline: none;
        border-radius: 0;
        font-size: 14px;
        font-family: Arial, Helvetica, sans-serif;
    }
    input::placeholder {
        color: #bdbdbd;
        font-style: italic;
        font-size: 13px;
    }
    input:focus {
        outline: none;
        border: none;
    }
</style>


================================================
FILE: src/lib/legacy/remote/handlers/EventHandler.ts
================================================
import { writable } from 'svelte/store'


export default class EventHandler
{
    private events = {
        change      : [] as (() => void)[],
        clearFilters: [] as (() => void)[],
        clearSearch : [] as (() => void)[]
    }
    public triggerChange = writable(0)     // legacy


    public add(event: keyof EventHandler['events'], callback: () => void)
    {
        this.events[event].push(callback)
    }

    public trigger(event: keyof EventHandler['events'])
    {
        for (const callback of this.events[event]) {
            callback()
        }
        /* legacy: support for triggerChange store */
        if (event === 'change') {
            this.triggerChange.update((store) => { return store + 1 })
        }
    }
}

================================================
FILE: src/lib/legacy/remote/handlers/FilterHandler.ts
================================================
import type { Filter } from '$lib/legacy/remote'
import type Context from '$lib/legacy/remote/Context'
import type { Writable } from 'svelte/store'

export default class FilterHandler<Row>
{
    public filters: Writable<Filter<Row>[]>

    constructor(context: Context<Row>) 
    {
        this.filters = context.filters
    }

    public set(value: string | number, filterBy: keyof Row )
    {
        const filter = { filterBy, value }
        this.filters.update((store) => {
            store = store.filter((item) => {
                return (item.filterBy !== filterBy) && item.value
            })
            if (value) {
                store.push(filter)
            }
            return store
        })
    }

    public remove()
    {
        this.filters.set([])
    }
}


================================================
FILE: src/lib/legacy/remote/handlers/PageHandler.ts
================================================
import type Context from '$lib/legacy/remote/Context'
import { type Writable, type Readable, get } from 'svelte/store'
import type EventHandler from './EventHandler'

export default class PageHandler<Row>
{
    public totalRows        : Writable<number>
    public pageNumber       : Writable<number>
    public rowCount         : Readable<{ total: number, start: number, end: number }>
    public rowsPerPage      : Writable<number | null>
    public event            : EventHandler
    public pages            : Readable<number[]>
    public selected         : Writable<(Row | Row[keyof Row])[]>

    constructor(context: Context<Row>)
    {
        this.totalRows          = context.totalRows
        this.pageNumber         = context.pageNumber
        this.rowCount           = context.rowCount
        this.rowsPerPage        = context.rowsPerPage
        this.event              = context.event
        this.pages              = context.pages
        this.selected           = context.selected
    }

    public get()
    {
        return this.pages
    }

    public goto(number: number)
    {
        const rowsPerPage = get(this.rowsPerPage)
        const totalRows = get(this.totalRows)

        this.pageNumber.update((store) => {
            if (rowsPerPage && totalRows) {
                if (number >= 1 && number <= Math.ceil(totalRows / rowsPerPage)) {
                    store = number
                    this.event.trigger('change')
                }
                return store
            }
            else {
                if (number >= 1) {
                    store = number
                    this.event.trigger('change')
                }
                return store
            }
        })
    }

    public previous()
    {
        const number = get(this.pageNumber) - 1
        this.goto(number)
    }

    public next()
    {
        const number = get(this.pageNumber) + 1
        this.goto(number)
    }
}


================================================
FILE: src/lib/legacy/remote/handlers/SearchHandler.ts
================================================
import type Context from '$lib/legacy/remote/Context'
import type { Writable } from 'svelte/store'

export default class SearchHandler<Row> 
{
    private search: Writable<string>

    constructor(context: Context<Row>) {
        this.search = context.search
    }

    public set(value: string)
    {
        this.search.set(value ?? null)
    }

    public remove()
    {
        this.search.set(null)
    }
}


================================================
FILE: src/lib/legacy/remote/handlers/SelectHandler.ts
================================================
import type Context from '$lib/legacy/remote/Context'
import { type Writable, type Readable, get } from 'svelte/store'



export default class SelectHandler<Row>
{
    private rows            : Readable<Row[]>
    private selected        : Writable<(Row | Row[keyof Row])[]>
    private isAllSelected   : Readable<boolean>
    private selectBy        : keyof Row | undefined

    constructor(context: Context<Row>)
    {
        this.rows               = context.rows
        this.selected           = context.selected
        this.isAllSelected      = context.isAllSelected
        this.selectBy           = context.selectBy
    }

    public set(value: Row | Row[keyof Row])
    {
        const selected = get(this.selected)
        if (selected.includes(value)) {
            this.selected.set(selected.filter((item) => item !== value))
        }
        else {
            this.selected.set([value, ...selected])
        }
    }

    public all()
    {
        const rows = get(this.rows)
        const isAllSelected = get(this.isAllSelected)
        this.selected.update( store => {
            if (this.selectBy) {
                return store = store.filter(item => !rows.map((row) => row[this.selectBy]).includes(item as Row[keyof Row]))
            }
            return store = store.filter(item => !rows.includes(item as Row))
        })
        if (!isAllSelected) {
            this.selected.update( store => {
                if (this.selectBy) {
                    store = [...rows.map((row) => row[this.selectBy]), ...store]
                }
                else {
                    store = [...rows, ...store]
                }
                return store
            })
        }
    }

    public clear()
    {
        this.selected.set([])
    }
}

================================================
FILE: src/lib/legacy/remote/handlers/SortHandler.ts
================================================
import type Context from '$lib/legacy/remote/Context'
import type { Order } from '$lib/legacy/remote'
import { type Writable, get } from 'svelte/store'
import type EventHandler from './EventHandler'

export default class SortHandler<Row>
{
    private event           : EventHandler
    private hasMultipleSort : boolean
    private sort            : Writable<Order<Row>>

    constructor(context: Context<Row>)
    {
        this.event              = context.event
        this.hasMultipleSort    = false
        this.sort               = context.sort
    }

    public set(orderBy: keyof Row = null)
    {
        if (!orderBy) return
        const sort = get(this.sort)

        if(!sort || sort.orderBy !== orderBy) {
            this.asc(orderBy)
        }
        else if (sort.direction === 'asc') {
            this.desc(sort.orderBy)
        }
        else if (sort.direction === 'desc') {
            this.asc(orderBy)
        } 
    }

    public asc(orderBy: keyof Row)
    {
        if (!orderBy) return
        this.sort.set({ orderBy, direction: 'asc' })
        this.event.trigger('change')
    }

    public desc(orderBy: keyof Row)
    {
        if (!orderBy) return
        this.sort.set({ orderBy, direction: 'desc' })
        this.event.trigger('change')
    }

    public apply(params: { orderBy: keyof Row, direction?: 'asc' | 'desc' } = null)
    {
        if (params) {
            switch (params.direction) {
                case 'asc' : return this.asc(params.orderBy)
                case 'desc': return this.desc(params.orderBy)
                default    : return this.set(params.orderBy)
            }
        }
        const sort = get(this.sort)
        if (sort) {
            return this.apply({ orderBy: sort.orderBy, direction: sort.direction })
        }
        return
    }


    // public set(orderBy: keyof Row, direction: 'asc' | 'desc')
    // {
    //     const sort = { orderBy, direction }
    //     if (this.hasMultipleSort === false) {
    //         this.sort.set([ sort ])
    //         return
    //     }
    //     this.sort.update((store) => {
    //         store = store.filter((item) => {
    //             return (item.orderBy !== orderBy) && item.direction
    //         })
    //         if (orderBy) {
    //             store.push(sort)
    //         }
    //         return store
    //     })
    // }

    // public sort(orderBy: keyof Row = null)
    // {
    //     if (!orderBy) return
    //     const sort = get(this.sort)
    //     const exists = sort.find(sort => sort.orderBy === orderBy)

    //     if(!exists) {
    //         this.sortAsc(orderBy)
    //     }
    //     else if (exists.direction === 'asc') {
    //         this.sortDesc(exists.orderBy)
    //     }
    //     else if (exists.direction === 'desc') {
    //         this.sortAsc(orderBy)
    //     } 
    // }

    // public sortAsc(orderBy: keyof Row)
    // {
    //     if (!orderBy) return
    //     this.set( orderBy, 'asc' )
    //     this.triggerChange.update((store) => { return store + 1 })
    // }

    // public sortDesc(orderBy: keyof Row)
    // {
    //     if (!orderBy) return
    //     this.set( orderBy, 'desc' )
    //     this.triggerChange.update((store) => { return store + 1 })
    // }

    // public applySorting(params: { orderBy: keyof Row, direction?: 'asc' | 'desc' } = null)
    // {
    //     if (params) {
    //         switch (params.direction) {
    //             case 'asc' : return this.sortAsc(params.orderBy)
    //             case 'desc': return this.sortDesc(params.orderBy)
    //             default    : return this.sort(params.orderBy)
    //         }
    //     }
    //     const sort = get(this.sort)
    //     if (sort.length > 0) {
    //         for (const order of sort) {
    //             return this.applySorting({ orderBy: order.orderBy, direction: order.direction })
    //         }
    //     }
    //     return
    // }
}


================================================
FILE: src/lib/legacy/remote/handlers/TriggerHandler.ts
================================================
import type { State } from '$lib/legacy/remote'
import type Context from '$lib/legacy/remote/Context'


export default class TriggerHandler<Row>
{
    private context: Context<Row>
    private reload: (state: State) => Promise<Row[]>

    constructor(context: Context<Row>)
    {
        this.context = context
    }

    public set(callback: (state: State) => Promise<Row[]>)
    {
        this.reload = callback
    }

    public async invalidate()
    {
        if (!this.reload) return
        const state = this.context.getState()
        const data = await this.reload(state)
        if (data) {
            this.context.rows.set(data)
        }
    }
}



================================================
FILE: src/lib/legacy/remote/index.ts
================================================
// Reexport your entry components here
import DataHandler      from './DataHandler'
import Datatable        from './Datatable.svelte'
import Search           from './Search.svelte'
import RowsPerPage      from './RowsPerPage.svelte'
import Th               from './Th.svelte'
import ThFilter         from './ThFilter.svelte'
import RowCount         from './RowCount.svelte'
import SelectedCount    from './SelectedCount.svelte'
import Pagination       from './Pagination.svelte'

export { DataHandler, Datatable, Search, RowsPerPage, Th, ThFilter, RowCount, SelectedCount, Pagination }

export type Internationalization = {
    search          ?: string
    show            ?: string
    entries         ?: string
    filter          ?: string
    rowCount        ?: string
    noRows          ?: string
    previous        ?: string
    next            ?: string
    selectedCount   ?: string
}

export type Row = { [key: string]: unknown }

export type Filter<Row> = {
    filterBy: keyof Row
    value?: string | number | boolean
}

export type Sort<Row> = {
    orderBy?: keyof Row
    direction?: 'asc' | 'desc'
}


export type State = {
    pageNumber: number,
    rowsPerPage: number,
    offset: number,
    search: string | undefined,
    sort: Sort<Row> | undefined
    filters: Filter<Row>[] | undefined
    setTotalRows: (value: number) => void
    /**
     * @deprecated use 'sort' instead
     */
    sorted: Sort<Row> | undefined
}


/**
 * @deprecated use (Row[keyof Row] | Row) instead
 * @since v1.13.0 2023-11-14
 *
 * import type { Row } from '@vincjo/datatables'
 */
export type Selectable<Row> = Row[keyof Row] | Row


/**
 * @deprecated use type Sort<Row> instead
 * @since v1.13.0 2023-11-14
 *
 * import type { Sort } from '@vincjo/datatables'
 */
export type Order<Row>  = Sort<Row>

================================================
FILE: src/lib/server/index.ts
================================================
export {
    // class:
    TableHandler,
    // components:
    Datatable,
    Search,
    RowsPerPage,
    Th,
    ThSort,
    ThFilter,
    Pagination,
    RowCount,
    // types:
    type Row,
    type State,
    type Filter,
    type Sort,
    type Internationalization,
    type TableHandlerInterface
}  from '$lib/src/server/index.js'




================================================
FILE: src/lib/src/client/AbstractTableHandler.svelte.ts
================================================
import type { Field, TableParams } from '$lib/src/client'
import { EventDispatcher }  from '$lib/src/shared'
import { type Search, type Filter, type Query, type Sort, data, parse } from './core'


export default abstract class AbstractTableHandler<Row>
{
    protected selectBy         ?: Field<Row>
    protected selectScope       = $state<'all' | 'currentPage'>('currentPage')
    protected highlight         : boolean
    protected event             = new EventDispatcher()
    protected rawRows           = $state.raw<Row[]>([])
    protected search            = $state<(Search<Row>)>({ value: null })
    protected sort              = $state<(Sort<Row>)>({})

    public filters              = $state<(Filter<Row>)[]>([])
    public queries              = $state<(Query<Row>)[]>([])
    public rowsPerPage          = $state<number>(10)
    public currentPage          = $state<number>(1)
    public element              = $state<HTMLElement>(undefined)
    public clientWidth          = $state<number>(1000)
    public filterCount          = $derived<number>(this.filters.length)
    public allRows              = $derived<readonly $state.Snapshot<Row>[]>(this.createAllRows())
    public rows                 = $derived<readonly $state.Snapshot<Row>[]>(this.createRows())
    public rowCount             = $derived<{total: number, start: number, end: number, selected: number}>(this.createRowCount())
    public pages                = $derived<readonly number[]>(this.createPages())
    public pageCount            = $derived<number>(this.pages.length)
    public pagesWithEllipsis    = $derived<readonly number[]>(this.createPagesWithEllipsis())
    public selected             = $state<unknown[]>([])
    public isAllSelected        = $derived<boolean>(this.createIsAllSelected())

    constructor(data: Row[], params: TableParams<Row>)
    {
        this.rawRows        = data
        this.rowsPerPage    = params.rowsPerPage ?? null
        this.highlight      = params.highlight ?? false
        this.selectBy       = params.selectBy
    }

    private createAllRows()
    {
        let allRows = $state.snapshot(this.rawRows)
        if (this.search.value) {
            allRows = data.search(allRows, this.search, this.highlight)
            this.event.dispatch('change')
        }
        else if (this.filters.length > 0) {
            for (const filter of this.filters) {
                allRows = data.filter(allRows, filter, this.highlight)
            }
            this.event.dispatch('change')
        }
        else if (this.queries.length > 0) {
            for (const query of this.queries) {
                allRows = data.query(allRows, query)
            }
            this.event.dispatch('change')
        }
        return allRows
    }

    private createRows()
    {
        if (!this.rowsPerPage) return this.allRows
        return this.allRows.slice(
            (this.currentPage - 1) * this.rowsPerPage,
            this.currentPage * this.rowsPerPage
        )
    }

    private createRowCount()
    {
        const total = this.allRows.length
        if (!this.rowsPerPage) {
            return { total: total, start: 1, end: total, selected: this.selected.length }
        }
        return {
            total: total,
            start: this.currentPage * this.rowsPerPage - this.rowsPerPage + 1,
            end: Math.min(this.currentPage * this.rowsPerPage, total),
            selected: this.selected.length
        }
    }

    private createPages()
    {
        if (!this.rowsPerPage) {
            return [1]
        }
        const pages = Array.from(Array(Math.ceil(this.allRows.length / this.rowsPerPage)))
        return pages.map((_, i) =>  i + 1 )
    }

    private createPagesWithEllipsis()
    {
        if (this.pageCount <= 7) {
            return this.pages
        }
        const ellipse = null
        const firstPage = 1
        const lastPage = this.pageCount
        if (this.currentPage <= 4) {
            return [
                ...this.pages.slice(0, 5),
                ellipse,
                lastPage
            ]
        } else if (this.currentPage < lastPage - 3) {
            return [
                firstPage,
                ellipse,
                ...this.pages.slice(this.currentPage - 2, this.currentPage + 1),
                ellipse,
                lastPage
            ]
        } else {
            return [
                firstPage,
                ellipse,
                ...this.pages.slice(lastPage - 5, lastPage)
            ]
        }
    }

    private createIsAllSelected()
    {
        if (this.rowCount.total === 0 || !this.selectBy) {
            return false
        }
        const { callback } = parse(this.selectBy)
        if (this.selectScope === 'all') {
            const identifiers = this.allRows.map(callback)
            return identifiers.every(id => this.selected.includes(id))
        }
        const identifiers = this.rows.map(callback)
        return identifiers.every(id => this.selected.includes(id))
    }
}


================================================
FILE: src/lib/src/client/TableHandler.svelte.ts
================================================
import { untrack }              from 'svelte'
import AbstractTableHandler     from './AbstractTableHandler.svelte'

import SortHandler              from './handlers/SortHandler.svelte'
import FilterHandler            from './handlers/FilterHandler.svelte'
import QueryHandler             from './handlers/QueryHandler.svelte'
import SelectHandler            from './handlers/SelectHandler.svelte'
import PageHandler              from './handlers/PageHandler.svelte'
import SearchHandler            from './handlers/SearchHandler.svelte'

import type { Internationalization, Row, Field, Check, TableParams, ColumnView, TableHandlerInterface } from '$lib/src/client'

import ViewBuilder              from '../shared/builders/ViewBuilder.svelte'
import SearchBuilder            from './builders/SearchBuilder.svelte'
import FilterBuilder            from './builders/FilterBuilder.svelte'
import QueryBuilder             from './builders/QueryBuilder.svelte'
import AdvancedFilterBuilder    from './builders/AdvancedFilterBuilder.svelte'
import CalculationBuilder       from './builders/CalculationBuilder.svelte'
import SortBuilder              from './builders/SortBuilder.svelte'
import CSVBuilder               from './builders/CSVBuilder.svelte'
import RecordFilterBuilder      from './builders/RecordFilterBuilder.svelte'


export default class TableHandler<T extends Row = any> extends AbstractTableHandler<T> implements TableHandlerInterface<T>
{
    public  i18n            : Internationalization
    private view            : ViewBuilder<T>
    private sortHandler     : SortHandler<T>
    private filterHandler   : FilterHandler<T>
    private queryHandler    : QueryHandler<T>
    private selectHandler   : SelectHandler<T>
    private pageHandler     : PageHandler<T>
    private searchHandler   : SearchHandler<T>

    constructor(data: T[] = [], params: TableParams<T> = { rowsPerPage: null })
    {
        super(data, params)
        this.translate(params.i18n)
        this.sortHandler    = new SortHandler(this)
        this.filterHandler  = new FilterHandler(this)
        this.queryHandler   = new QueryHandler(this)
        this.selectHandler  = new SelectHandler(this)
        this.pageHandler    = new PageHandler(this)
        this.searchHandler  = new SearchHandler(this)
    }

    public setRows(data: T[]): void
    {
        const scrollTop = this.element?.scrollTop ?? 0
        this.rawRows = data
        untrack(() => {
            this.event.dispatch('change')
            this.sortHandler.restore()
            if (this.element) {
                setTimeout(() => this.element.scrollTop = scrollTop, 2)
            }
        })
    }

    public setRowsPerPage(value: number): void
    {
        this.rowsPerPage = value
        this.setPage(1)
    }

    public setPage(value: number | 'previous' | 'next' | 'last'): void
    {
        switch (value) {
            case 'previous' : return this.pageHandler.previous()
            case 'next'     : return this.pageHandler.next()
            case 'last'     : return this.pageHandler.last()
            default         : return this.pageHandler.goto(value as number)
        }
    }

    public createSearch(scope?: Field<T>[]): SearchBuilder<T>
    {
        return new SearchBuilder(this.searchHandler, scope)
    }

    public clearSearch(): void
    {
        this.searchHandler.clear()
        this.event.dispatch('clearSearch')
        this.setPage(1)
    }

    public createRecordFilter(records?: Row[]): RecordFilterBuilder
    {
        return new RecordFilterBuilder(records)
    }

    public createSort(field: Field<T>, params?: { locales: Intl.LocalesArgument, options: Intl.CollatorOptions}): SortBuilder<T>
    {
        return new SortBuilder(this.sortHandler, field, params)
    }

    public clearSort()
    {
        this.sortHandler.clear()
    }

    public clearFilters(): void
    {
        this.filters = []
        this.event.dispatch('clearFilters')
        this.setPage(1)
    }

    public createAdvancedFilter(field: Field<T>, check?: Check): AdvancedFilterBuilder<T>
    {
        return new AdvancedFilterBuilder(this.filterHandler, field, check)
    }

    public createFilter(field: Field<T>, check?: Check): FilterBuilder<T>
    {
        return new FilterBuilder(this.filterHandler, field, check)
    }

    public createQuery(): QueryBuilder<T>
    {
        return new QueryBuilder(this.queryHandler)
    }

    public select(value: unknown): void
    {
        this.selectHandler.set(value)
    }

    public selectAll(params: { scope?: 'all' | 'currentPage' } = {}): void
    {
        this.selectScope = (params.scope === 'all') ? 'all' : 'currentPage'
        this.selectHandler.all(this.selectScope)
    }

    public getSelectedRows(): T[]
    {
        return this.selectHandler.getRows()
    }

    public clearSelection(): void
    {
        this.selectHandler.clear()
    }

    public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: () => void)
    {
        this.event.add(event, callback)
    }

    public createCalculation(field: Field<T>): CalculationBuilder<T>
    {
        return new CalculationBuilder(this, field)
    }

    public createCSV(): CSVBuilder<T>
    {
        return new CSVBuilder(this)
    }

    public createView(columns: ColumnView[]): ViewBuilder<T>
    {
        this.view = new ViewBuilder(this, columns)
        return this.view
    }

    public getView(): ViewBuilder<T>
    {
        return this.view
    }

    private translate(i18n: Internationalization): void
    {
        this.i18n = {
            ...{
                search: 'Search...',
                show: 'Show',
                entries: 'entries',
                filter: 'Filter',
                rowCount: 'Showing {start} to {end} of {total} entries',
                noRows: 'No entries found',
                previous: 'Previous',
                next: 'Next'
            },
            ...i18n
        }
    }
}


================================================
FILE: src/lib/src/client/builders/AdvancedFilterBuilder.svelte.ts
================================================
import type { Field, Check, Criterion } from '$lib/src/client'
import type FilterHandler               from '$lib/src/client/handlers/FilterHandler.svelte'
import { check as comparator }          from '$lib/src/client/core'

export default class AdvancedFilterBuilder<Row>
{
    public  criteria        = $state<unknown[]>([])
    private id              = Math.random().toString(36).substring(2, 15)
    private filterHandler   : FilterHandler<Row>
    private collection      : Criterion[]
    private field           : Field<Row>
    private check           : Check
    private isRecursive     = true

    constructor(filterHandler: FilterHandler<Row>, field: Field<Row>, check?: Check)
    {
        this.filterHandler  = filterHandler
        this.field          = field
        this.collection     = []
        this.check          = check ?? comparator.isEqualTo
        this.cleanup()
    }

    public set(value: unknown, check?: Check): void
    {
        if (this.collection.find(criterion => criterion.value === value)) {
            this.collection = this.collection.filter(criterion => criterion.value !== value)
        }
        else {
            this.collection = [ { value, check: check ?? this.check }, ...this.collection ]
        }
        if (this.collection.length === 0) {
            return this.clear()
        }
        this.filterHandler.set(this.collection, this.field, comparator.whereIn, this.id, this.isRecursive)
        this.criteria = this.collection.map(criterion => criterion.value)
    }

    public isNotRecursive()
    {
        this.isRecursive = false
        return this
    }

    public clear(): void
    {
        this.collection = []
        this.criteria = []
        this.filterHandler.unset(this.id)
    }

    private cleanup()
    {
        this.filterHandler['table'].on('clearFilters', () => this.clear())
    }
}


================================================
FILE: src/lib/src/client/builders/CSVBuilder.svelte.ts
================================================
import type { TableHandler } from '$lib/src/client'

export default class CSVBuilder<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public download(filename: string)
    {
        const csv = this.get()
        const element = document.createElement('a')
        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(csv))
        element.setAttribute('download', filename)

        element.style.display = 'none'
        document.body.appendChild(element)

        element.click()
        document.body.removeChild(element)
    }

    public get(): string
    {
        const rows = this.getRows()
        rows.unshift(this.getHeader().join(','))
        return rows.join('\r\n')
    }

    private getRows()
    {
        return this.table.allRows.map(row => {
            const entries = Object.entries(row).map(([_, value]) => {
                if (value === null) return ''
                if (typeof value === 'number') {
                    return value
                }
                return `"${value}"`
            })
           return entries.join(',')
       })
    }

    private getHeader()
    {
        const [row] = this.table.allRows
        return Object.entries(row).map(([key, _]) => {
            return `"${key}"`
        })
    }
}


================================================
FILE: src/lib/src/client/builders/CalculationBuilder.svelte.ts
================================================
import type { Field, TableHandler } from '$lib/src/client'
import { sort, parse }              from '$lib/src/client/core'

type Sort = [key: 'value' | 'count', direction: 'asc' | 'desc']

export default class CalcultationBuilder<Row>
{
    private callback    : (row: $state.Snapshot<Row>) => string | number
    private precision   : number
    private table       : TableHandler<Row>

    constructor(table: TableHandler<Row>, field: Field<Row>)
    {
        this.table      = table
        this.callback   = parse(field).callback as (row: $state.Snapshot<Row>) => string | number
    }

    public distinct(param?: { sort: Sort }): { value: string, count: number }[]
    {
        const values = this.table.allRows.map(row => this.callback(row))

        const aggregate: { [key: string ]: number } = values.reduce((acc, curr) => {
            if (Array.isArray(curr)) {
                for (const item of curr) {
                    acc[item] = (acc[item] ?? 0) + 1
                }
                return acc
            }
            acc[curr] = (acc[curr] ?? 0) + 1
            return acc
        }, {})
        const result = Object.entries(aggregate).map(([value, count]) => ({ value, count }))
        if (param?.sort) {
            const [field, direction] = param.sort
            if (field === 'count') {
                result.sort((x, y) => sort.asc(x.value, y.value))
            }
            result.sort((a, b) => sort[direction](a[field], b[field]))
        }
        return result
    }

    public avg(param?: { precision: number }): number
    {
        this.precision  = param?.precision ?? 2
        if (this.table.allRows.length === 0) return 0
        const values = this.table.allRows.map(row => this.callback(row)).filter(Boolean) as number[]
        return this.round(values.reduce((acc, curr) => acc + curr, 0) / values.length)
    }

    public sum(param?: { precision: number }): number
    {
        this.precision  = param?.precision ?? 2
        const values = this.table.allRows.map(row => this.callback(row)) as number[]
        return this.round(values.reduce((acc, curr) => acc + curr, 0))
    }

    public median(param?: { precision: number }): number
    {
        this.precision  = param?.precision ?? 2
        const values = [...this.table.allRows.map(row => this.callback(row))]
            .sort((a: number, b: number) => a - b)
            .filter(Boolean) as number[]
        if (values.length === 0) return null
        const half = Math.floor(values.length / 2)
        return (values.length % 2
            ? values[half]
            : this.round((values[half - 1] + values[half]) / 2)
        )
    }

    public bounds(): number[]
    {
        const values = this.table.allRows.map(row => this.callback(row))

        const numbers = values.filter(Boolean) as number[]
        if (numbers.length === 0) return [null, null]
        return [
            Math.min(...numbers),
            Math.max(...numbers)
        ]
    }

    private round(value: number)
    {
        return Number(value.toFixed(this.precision))
    }
}

================================================
FILE: src/lib/src/client/builders/FilterBuilder.svelte.ts
================================================
import type { Field, Check }    from '$lib/src/client'
import type FilterHandler       from '../handlers/FilterHandler.svelte'
import { check as comparator }  from '$lib/src/client/core'
import type { FilterInterface } from '$lib/src/shared'

export default class FilterBuilder<Row> implements FilterInterface
{
    public  value           = $state<unknown>('')
    private id              = Math.random().toString(36).substring(2, 15)
    private filterHandler   : FilterHandler<Row>
    private field           : Field<Row>
    private check           : Check
    private isRecursive     = true

    constructor(filterHandler: FilterHandler<Row>, field: Field<Row>, check?: Check)
    {
        this.filterHandler  = filterHandler
        this.field          = field
        this.check          = check ?? comparator.isLike
        this.cleanup()
    }

    public set(check?: Check)
    {
        this.filterHandler.set(this.value, this.field, check ?? this.check, this.id, this.isRecursive)
    }

    public init(value?: unknown)
    {
        this.value = value
        this.set()
        return this
    }

    public isNotRecursive()
    {
        this.isRecursive = false
        return this
    }

    public clear()
    {
        this.value = ''
        this.filterHandler.unset(this.id)
    }

    private cleanup()
    {
        this.filterHandler['table'].on('clearFilters', () => this.clear())
    }
}


================================================
FILE: src/lib/src/client/builders/QueryBuilder.svelte.ts
================================================
import type { Check }    from '$lib/src/client'
import type QueryHandler        from '../handlers/QueryHandler.svelte'

export default class QueryBuilder<Row>
{
    public  value           = $state<unknown>('')
    private id              = Math.random().toString(36).substring(2, 15)
    private queryHandler    : QueryHandler<Row>
    private path            : string[] = []
    private check           : Check

    constructor(queryHandler: QueryHandler<Row>)
    {
        this.queryHandler   = queryHandler
        this.cleanup()
    }

    public from(path: string[])
    {
        this.path = path
        return this
    }

    public where(filter: (row: any, value?: unknown) => boolean)
    {
        this.check = filter
        return this
    }

 

    public set(value?: unknown)
    {
        if (value) this.value = value
        this.queryHandler.set(this.path, this.value, this.check, this.id)
    }

    public clear()
    {
        this.value = ''
        this.queryHandler.unset(this.id)
    }

    private cleanup()
    {
        this.queryHandler['table'].on('clearFilters', () => this.clear())
    }
}


================================================
FILE: src/lib/src/client/builders/RecordFilterBuilder.svelte.ts
================================================
import { match, check, isNotNull } from '$lib/src/client/core'
import type { Row }         from '$lib/src/client'

export default class RecordFilterBuilder
{
    public  value       = $state<string>('')
    public  records     = $derived<readonly Row[]>(this.createRecords())
    private rawRecords  = $state.raw<Row[]>([])
    private filter      = $state<string>('')

    constructor(records: Row[])
    {
        this.rawRecords = records
    }

    public set()
    {
        this.filter = this.value
    }

    private createRecords(): readonly Row[]
    {
        if (isNotNull(this.filter)) {
            return this.rawRecords.filter(record => match(record, this.filter, { check: check.isLike }))
        }
        return this.rawRecords
    }
}

================================================
FILE: src/lib/src/client/builders/SearchBuilder.svelte.ts
================================================
import { type Field }       from '$lib/src/client'
import type SearchHandler   from '../handlers/SearchHandler.svelte'
import type { SearchInterface } from '$lib/src/shared'

export default class SearchBuilder<Row> implements SearchInterface
{
    public  value           = $state<string>('')
    private scope           : Field<Row>[]
    private searchHandler   : SearchHandler<Row>

    constructor(searchHandler: SearchHandler<Row>, scope?: Field<Row>[])
    {
        this.searchHandler  = searchHandler
        this.scope          = scope
        this.cleanup()
    }

    public set()
    {
        this.searchHandler.set(this.value, this.scope)
    }

    public init(value?: string)
    {
        this.value = value
        this.set()
        return this
    }

    public recursive()
    {
        this.searchHandler.recursive(this.value, this.scope)
    }

    public regex()
    {
        this.searchHandler.regex(this.value, this.scope)
    }

    public clear()
    {
        this.value = ''
        this.searchHandler.clear()
    }

    private cleanup()
    {
        this.searchHandler['table'].on('clearSearch', () => this.clear())
    }
}


================================================
FILE: src/lib/src/client/builders/SortBuilder.svelte.ts
================================================
import type { Field, SortParams }   from '$lib/src/client'
import type SortHandler             from '../handlers/SortHandler.svelte'
import type { SortInterface }       from '$lib/src/shared'


export default class SortBuilder<Row> implements SortInterface
{
    public  direction       = $derived<'asc' | 'desc'>(this.createDirection())
    public  isActive        = $derived<boolean>(this.createIsActive())
    private id              = Math.random().toString(36).substring(2, 15)
    private sortHandler     : SortHandler<Row>
    private field           : Field<Row>
    private params          : SortParams

    constructor(sortHandler: SortHandler<Row>, field: Field<Row>, params: SortParams)
    {
        this.sortHandler    = sortHandler
        this.field          = field
        this.params         = params ?? {}
    }

    public set()
    {
        this.sortHandler.set(this.field, this.id, this.params)
    }

    public init(direction?: 'asc' | 'desc')
    {
        if (!direction) return this
        this[direction]()
        return this
    }

    public asc()
    {
        this.sortHandler.asc(this.field, this.id, this.params)
    }

    public desc()
    {
        this.sortHandler.desc(this.field, this.id, this.params)
    }

    public clear()
    {
        this.sortHandler.clear()
    }

    private createIsActive()
    {
        if (this.id === this.sortHandler['table']['sort']?.id) {
            return true
        }
        return false
    }

    private createDirection()
    {
        if (this.isActive === false) return null
        return this.sortHandler['table']['sort']?.direction
    }
}


================================================
FILE: src/lib/src/client/core/check.ts
================================================
import { isNull, stringify, isNotNull } from './value'
import type { Criterion, Check } from '$lib/src/client'


export const check: { [name: string]: Check } = {
    isLike:                 (entry: unknown, value: unknown)        => stringify(entry).includes(stringify(value)),
    isNotLike:              (entry: unknown, value: unknown)        => stringify(entry).includes(stringify(value)) === false,
    startsWith:             (entry: unknown, value: unknown)        => stringify(entry).startsWith(stringify(value)),
    endsWith:               (entry: unknown, value: unknown)        => stringify(entry).endsWith(stringify(value)),
    isEqualTo:              (entry: unknown, value: unknown)        => stringify(entry) === stringify(value),
    isNotEqualTo:           (entry: unknown, value: unknown)        => stringify(entry) !== stringify(value),
    isGreaterThan:          (entry: number, value: number)          => isNull(entry) ? false : (entry > value),
    isGreaterThanOrEqualTo: (entry: number, value: number)          => isNull(entry) ? false : (entry >= value),
    isLessThan:             (entry: number, value: number)          => isNull(entry) ? false : (entry < value),
    isLessThanOrEqualTo:    (entry: number, value: number)          => isNull(entry) ? false : (entry <= value),
    isBetween:              (entry: number, [min, max]: number[])   => isNull(entry) ? false : (entry >= min && entry <= max),
    isStrictlyBetween:      (entry: number, [min, max]: number[])   => isNull(entry) ? false : (entry > min && entry < max),
    isTrue:                 (entry: unknown, _: unknown)            => entry === true,
    isFalse:                (entry: unknown, _: unknown)            => entry === false,
    isNull:                 (entry: unknown, _: unknown)            => isNull(entry),
    isNotNull:              (entry: unknown, _: unknown)            => isNotNull(entry),

    // multiple criteria
    whereIn: (entry: unknown, criteria: Criterion[] = []) => {
        if (criteria.length === 0) return false
        for(const { value, check } of criteria) {
            if (value?.['key']) {
                return checkByKey(entry, value as any, check)
            }
            else if (check(entry, value)) {
                return true
            }
        }
        return false
    },


    // regexp
    match: (entry: unknown, pattern: string) => {
        const match = pattern.match(/^([\/~@;%#'])(.*?)\1([gimsuy]*)$/)
        const regex = match ? new RegExp(match[2], match[3]
              .split('')
              .filter((char, pos, flagArr) => flagArr.indexOf(char) === pos)
              .join('')
          ) 
          : new RegExp(pattern)
        return stringify(entry).match(regex) ? true : false
    },
}

const checkByKey = (entry: unknown, param: { key: string, value: unknown }, check: Check) => {
    if (!param?.key) return false
    const { key, value } = param
    if (Array.isArray(entry) === false && typeof entry === 'object') {
        const keys = Object.keys(entry)
        if (keys.includes(key) && check(entry[key], value)) {
			return true
		}
    }
    return false
}

================================================
FILE: src/lib/src/client/core/entry.ts
================================================
import { isNull, isObject, isObjectArray } from './value'
import { check } from './check'
import type { Criterion, Check } from '$lib/src/client'

type Params = {
    isRecursive?: boolean,
    check?: Check,
    highlight?: boolean,
}


export const match = (entry: unknown, value: unknown | Criterion[], params: Params): boolean => {
    params.check = params.check ?? check.isLike
    if (isNull(value)) {
        return true
    }
    else if (isObjectArray(entry)) {
        // if (isObject(entry) && entry.is_map === true) return true
        return Object.keys(entry).some(k => match(entry[k], value, params))
    }
    return params.check(entry, value)
}

export const sift = (entry: unknown, value: unknown, params: Params) => {
    if (Array.isArray(entry)) {
        entry = entry.filter((item: unknown) => {
            // to disable deletion while filtering, check is worth true anyway
            const check = params.isRecursive === true ? match(item, value, params) : true
            if (typeof item === 'object' && check === true) {
                for (const k of Object.keys(item)) {
                    item[k] = sift(item[k], value, params)
                }
            }
            return check
        })
    }
    if (params.highlight && (typeof entry === 'string' || typeof entry === 'number') && typeof value === 'string' && match(entry, value, params)) {
        return emphasize(entry, value)
    }
    return entry
}

const emphasize = (entry: string | number, value: string) => {
    const search = value
        .replace(/a/g, '[aàâáä]')
        .replace(/e/g, '[eèêéë]')
        .replace(/i/g, '[iìîíï]')
        .replace(/o/g, '[oòôо́ö]')
        .replace(/u/g, '[uùûúü]')
        .replace(/y/g, '[yỳŷýÿ]')
    const exp = new RegExp(`${search}`, 'gi')
    return String(entry).replace(exp, `<u class="highlight">$&</u>`)
}

export const deepEmphasize = <Row>(entry: Row, value: string, callback: (entry: Row) => unknown) => {
    const path = callback.toString()
        .split('=>')[1]
        .replace(/\(\)/g, '')
        .replace(/\?/g, '')
        .split('.').splice(1).join('.')
        .trim()
    if (path.indexOf(' ') > -1) return entry
    return deepSet(entry, path, value)
}

const deepSet = <Row>(entry: Row, path: string, value: string) => {
    const initial = entry
    const keys = path.replace(/\[/g, '.[').split(".")
    try {
        for (let i = 0; i < keys.length; i++) {
            let current = keys[i]
            let next = keys[i + 1]
            if (current.includes('[')) {
                current = String(parseInt(current.substring(1, current.length - 1)))
            }
            if (next && next.includes('[')) {
                next = String(parseInt(next.substring(1, next.length - 1)))
            }
            if (next !== undefined) {
                entry[current] = entry[current] ? entry[current] : (isNaN(Number(next)) ? {} : [])
            } else {
                entry[current] = emphasize(entry[current] as string | number, value)
            }
            entry = entry[current] as Row
        }
        return initial
    } catch(_) {
        return initial
    }
}

================================================
FILE: src/lib/src/client/core/field.ts
================================================
import type { Field } from '$lib/src/client'

export const parse = <Row>(field: Field<Row>, id?: string) => {
    if (typeof field === 'string') {
        return {
            callback: (row: $state.Snapshot<Row>) => row[(field as keyof $state.Snapshot<Row>)],
            id: id,
            key: field,
        }
    } else if (typeof field === 'function') {
        return {
            callback: field as (row: $state.Snapshot<Row>) => unknown,
            id: id,
            key: undefined,
        }
    }
    throw new Error(`Invalid field argument: ${String(field)}`)
}

================================================
FILE: src/lib/src/client/core/index.ts
================================================
import type { Field, Check } from '$lib/src/client'


export { isNull, isNotNull, stringify } from './value'
export { match, sift, deepEmphasize }   from './entry'
export { check }                        from './check'
export { data, sort }                   from './rows'
export { parse }                        from './field'

export type Search<Row> = {
    value: string
    scope?: Field<Row>[]
    isRecursive?: boolean
    check?: Check
}

export type Filter<Row> = {
    callback: (row:  $state.Snapshot<Row>) => unknown
    id: string
    value?: unknown
    isRecursive?: boolean
    check?: Check
    key?: string
}

export type Query<Row> = {
    path: string[]
    id: string
    value?: unknown
    check?: Check
}

export type Sort<Row> = {
    callback?: (row:  $state.Snapshot<Row>) => unknown
    id?: string
    direction?: 'asc' | 'desc'
    key?: string,
}




================================================
FILE: src/lib/src/client/core/rows.ts
================================================
import type { Field } from '$lib/src/client'
import { type Search, type Filter, type Query } from '$lib/src/client/core'

import { match, sift, deepEmphasize }   from './entry'
import { isNull }                       from './value'
import { parse }                        from './field'


export const data = {
    search: <Row>(allRows: $state.Snapshot<Row[]>, { scope, isRecursive, value, check }: Search<Row>, highlight: boolean = false) => {
        return allRows.filter(row => {
            const keys = scope ?? Object.keys(row) as Field<Row>[]
            const fields = keys.map(field => parse(field))
            for(const { key, callback } of fields) {
                if (key) {
                    row[key] = sift(row[key], value, {
                        highlight: highlight,
                        isRecursive: isRecursive === true
                    })
                }
                else if (highlight) {
                    row = deepEmphasize(row, value, callback)
                }
            }
            return fields.some(({ callback }) => {
                return match(callback(row), value, { check })
            })
        })
    },
    filter: <Row>(allRows: $state.Snapshot<Row[]>, { callback, isRecursive, value, check, key }: Filter<Row>,  highlight: boolean = false) => {
        return allRows.filter((row) => {
            const checked = match(callback(row), value, { check })
            if (key) {
                row[key] = sift(row[key], value, {
                    highlight: highlight, 
                    check: check,
                    isRecursive: isRecursive === false ? false : true
                })
            }
            else if (highlight && checked && value && typeof value === 'string') {
                row = deepEmphasize(row, value, callback)
            }
            return checked
        })
    },
    query: <Row>(allRows: $state.Snapshot<Row[]>, { path, value, check }: Query<Row>) => {
        return allRows.filter(row => {
            if (path.length === 0) {
                return check(row, value)
            }
            let obj = row
            let i = 1
            let verify = false
            function recursive(i) {

            }
            // ['groups', 'users']
            // row[groups].map(group => group.users.map(user) => {

            // })

            // row.groups.filter(group => group.users.filter(user => ))
            for (const prop of path) {
                if (i === 1 && i < path.length) {
                    obj = obj[prop]
                    for (const subobj of obj[prop]) {

                    }
                }
                if (i === path.length) {
                    console.log(prop, obj)
                    obj[prop] = data.query(obj[prop], { path: [], value, check } as Query<Row>)
                    if (obj[prop].length > 0) {
                        verify = true
                    }
                }
                obj = obj[prop]
                i++
            }
            return verify
        })
        // return allRows.map(row => {
        //     let obj = row
        //     const [root, ...props] = path
        //     let i = 1
        //     for (const prop of path) {
        //         if (obj[prop]) {
        //             if (obj[root]) {
        //                 obj[root] = data.query(obj[root], { path, value, check } as Query<Row>) 
        //             }
        //             if (i < props.length) {
        //                 obj[prop] = data.query(obj[prop], { path, value, check } as Query<Row>)
        //             }
        //             else {
        //                 obj[prop] = obj[prop].filter((item: any) => check(item))
        //             }
        //         }
        //         i++
        //     }
        //     return row
        // }).filter(Boolean)
    }
}

export const sort = {
    asc: (a: unknown, b: unknown, locales?: Intl.LocalesArgument, options?: Intl.CollatorOptions) => {
        if (a === b)                        return 0
        else if (isNull(a))                 return 1
        else if (isNull(b))                 return -1
        else if (typeof a === 'boolean')    return a === false ? 1 : -1
        else if (typeof a === 'string')     return a.localeCompare(b as string, locales, options)
        else if (typeof a === 'number')     return a - (b as number)
        else if (typeof a === 'object')     return JSON.stringify(a).localeCompare(JSON.stringify(b), locales, options)
        return String(a).localeCompare(String(b), locales, options)
    },
    desc: (a: unknown, b: unknown, locales?: Intl.LocalesArgument, options?: Intl.CollatorOptions) => {
        if (a === b)                        return 0
        else if (isNull(a))                 return 1
        else if (isNull(b))                 return -1
        else if (typeof b === 'boolean')    return b === false ? 1 : -1
        else if (typeof b === 'string')     return b.localeCompare(a as string, locales, options)
        else if (typeof b === 'number')     return b - (a as number)
        else if (typeof b === 'object')     return JSON.stringify(b).localeCompare(JSON.stringify(a), locales, options)
        else return String(b).localeCompare(String(a), locales, options)
    }
}

================================================
FILE: src/lib/src/client/core/value.ts
================================================


export const isNull = (value: unknown) => {
    if (value === null || value === undefined || value === '') return true
    return false
}
export const isNotNull = (value: unknown) => { return !isNull(value) }

export const stringify = (value: unknown = null) => {
    return String(value)
        .toLowerCase()
        .normalize('NFD')
        .replace(/[\u0300-\u036f]/g, '')
}

export const isObject = (value: unknown) => {
    if (typeof value !== 'object')  return false
    else if (value === null)        return false
    else if (Array.isArray(value))  return false
    return true
}

export const isObjectArray = (value: unknown) => {
    if (typeof value !== 'object')  return false
    else if (value === null)        return false
    // test
    else if (Array.isArray(value)) {
        if (isNotNull(value[0]) && typeof value[0] !== 'object') return false
    }
    return true
}

================================================
FILE: src/lib/src/client/handlers/FilterHandler.svelte.ts
================================================
import type { Field, Check, TableHandler }  from '$lib/src/client'
import { isNotNull, parse }                 from '$lib/src/client/core'


export default class FilterHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(value: unknown, field: Field<Row>, check: Check = null, id: string, isRecursive = true)
    {
        this.table.setPage(1)

        const { callback, key } = parse<Row>(field, id)
        const filter = { value, id, callback, check, key, isRecursive }

        this.table.filters = this.table.filters.filter(filter => filter.id !== id)
        if (isNotNull(value)) {
            this.table.filters.push(filter)
        }
    }

    public unset(id: string)
    {
        this.table.setPage(1)
        this.table.filters = this.table.filters.filter(filter => filter.id !== id)
    }
}


================================================
FILE: src/lib/src/client/handlers/PageHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/client'

export default class PageHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public goto(page: number)
    {
        if (this.table.rowsPerPage) {
            if (page >= 1 && page <= this.table.pageCount) {
                this.table.currentPage = page
                this.table['event'].dispatch('change')
            }
        }
    }

    public previous()
    {
        this.goto(this.table.currentPage - 1)
    }

    public next()
    {
        this.goto(this.table.currentPage + 1)
    }

    public last()
    {
        this.goto(this.table.pageCount)
    }
}


================================================
FILE: src/lib/src/client/handlers/QueryHandler.svelte.ts
================================================
import type { Field, Check, TableHandler }  from '$lib/src/client'
import { isNotNull } from '$lib/src/client/core'


export default class QueryHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(path: string[], value: unknown, check: Check, id: string)
    {
        this.table.setPage(1)
        this.table.queries = this.table.queries.filter(query => query.id !== id)
        this.table.queries.push({ path, value, check, id })
    }

    public unset(id: string)
    {
        this.table.setPage(1)
        this.table.queries = this.table.queries.filter(query => query.id !== id)
    }
}


================================================
FILE: src/lib/src/client/handlers/SearchHandler.svelte.ts
================================================
import { type TableHandler, type Field, type Criterion, check } from '$lib/src/client'

export default class SearchHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(value: string, scope?: Field<Row>[])
    {
        this.table.setPage(1)
        this.table['search'] = { value: value, scope: scope }
    }

    public recursive(value: string, scope?: Field<Row>[])
    {
        this.table.setPage(1)
        this.table['search'] = { value: value, scope: scope, isRecursive: true }
    }

    public regex(pattern: string, scope?: Field<Row>[])
    {
        this.table.setPage(1)
        this.table['search'] = { value: pattern, scope: scope, check: check.match }
    }

    public clear()
    {
        this.table.setPage(1)
        this.table['search'] = { value: '' }
    }
}


================================================
FILE: src/lib/src/client/handlers/SelectHandler.svelte.ts
================================================
import type { TableHandler }    from '$lib/src/client'
import { parse }                from '$lib/src/client/core'

export default class SelectHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(value: unknown)
    {
        if (this.table.selected.includes(value)) {
            this.table.selected = this.table.selected.filter((item) => item !== value)
        } else {
            this.table.selected = [value, ...this.table.selected]
        }
    }

    public all(scope: 'all' | 'currentPage')
    {
        const rows = (scope === 'currentPage') ? this.table.rows : this.table.allRows
        const { callback } = parse(this.table['selectBy'])
        const selection = rows.map(callback)
        if (scope === 'currentPage') {
            if (this.table.isAllSelected) {
                this.table.selected = this.table.selected.filter(item => selection.includes(item) === false)
            }
            else {
                this.table.selected = [...new Set([...selection, ...this.table.selected])]
            }
        }
        else {
            this.table.isAllSelected ? this.clear() : this.table.selected = selection
        }
    }

    public clear()
    {
        this.table.selected = []
    }

    public getRows()
    {
        const { callback } = parse(this.table['selectBy'])
        return this.table['rawRows'].filter(row => {
            return this.table.selected.includes(callback(row as $state.Snapshot<Row>))
        })
    }
}

================================================
FILE: src/lib/src/client/handlers/SortHandler.svelte.ts
================================================
import type { Field, TableHandler, SortParams } from '$lib/src/client'
import { type Sort, parse, sort } from '$lib/src/client/core'

export default class SortHandler<Row> 
{
    private backup  : Sort<Row>[]
    private table   : TableHandler<Row>

    constructor(table: TableHandler<Row>) 
    {
        this.table  = table
        this.backup = []
    }

    public set(field: Field<Row>, id: string, params: SortParams = {})
    {
        if (this.table['sort'].id !== id) {
            this.table['sort'].direction = null
        }
        if (this.table['sort'].direction === null || this.table['sort'].direction === 'desc') {
            this.asc(field, id, params)
        }
        else if (this.table['sort'].direction === 'asc') {
            this.desc(field, id, params)
        }
    }

    public asc(field: Field<Row>, id: string, { locales, options }: SortParams = {})
    {
        if (!field) return
        const { callback, key } = parse(field, id)
        this.table['sort'] = { id, callback, direction: 'asc', key }
        this.table['rawRows'] = [...this.table['rawRows']].sort((x, y) => {
            const [a, b] = [callback(x as $state.Snapshot<Row>), callback(y as $state.Snapshot<Row>)]
            return sort.asc(a, b, locales, options)
        })
        this.save({ id, callback, direction: 'asc' })
        this.table.setPage(1)
    }

    public desc(field: Field<Row>, id: string, { locales, options }: SortParams = {})
    {
        if (!field) return
        const { callback, key } = parse(field, id)
        this.table['sort'] = { id, callback, direction: 'desc', key }
        this.table['rawRows'] = [...this.table['rawRows']].sort((x, y) => {
            const [a, b] = [callback(x as $state.Snapshot<Row>), callback(y as $state.Snapshot<Row>)]
            return sort.desc(a, b, locales, options)
        })
        this.save({ id, callback, direction: 'desc' })
        this.table.setPage(1)
    }

    public clear()
    {
        this.backup = []
        this.table['sort'] = {}
    }

    public restore()
    {
        for (const { key, callback, direction, id } of this.backup) {
            const field = (key ?? callback) as Field<Row>
            this[direction](field, id)
        }
    }

    private save(sort: Sort<Row>)
    {
        this.backup = this.backup.filter(item => item.id !== sort.id )
        if (this.backup.length >= 3) {
            const [_, slot2, slot3] = this.backup
            this.backup = [slot2, slot3, sort]
        }
        else {
            this.backup = [...this.backup, sort]
        }
    }
}


================================================
FILE: src/lib/src/client/index.ts
================================================
export { default as TableHandler } from './TableHandler.svelte'
export { check } from './core'

import type { Internationalization, Field }         from '$lib/src/shared'
export type { default as AdvancedFilterBuilder }    from './builders/AdvancedFilterBuilder.svelte'
export type { default as CalculationBuilder }       from './builders/CalculationBuilder.svelte'
export type { default as CSVBuilder }               from './builders/CSVBuilder.svelte'
export type { default as FilterBuilder }            from './builders/FilterBuilder.svelte'
export type { default as RecordFilterBuilder }      from './builders/RecordFilterBuilder.svelte'
export type { default as SearchBuilder }            from './builders/SearchBuilder.svelte'
export type { default as SortBuilder }              from './builders/SortBuilder.svelte'
export { default as RecordFilter }                  from './builders/RecordFilterBuilder.svelte'

export {
    Datatable,
    Search,
    RowsPerPage,
    Th,
    ThSort,
    ThFilter,
    Pagination,
    RowCount,
    type Row,
    type Field,
    type Internationalization,
    type ColumnView,
    type TableHandlerInterface,
} from '$lib/src/shared'

export type SortParams = {
    locales?: Intl.LocalesArgument,
    options?: Intl.CollatorOptions
}

export type Check = (entry: unknown, value: unknown) => boolean

export type TableParams<Row> = {
    rowsPerPage?: number,
    selectBy?: Field<Row>,
    highlight?: boolean,
    i18n?: Internationalization,
}

export type Criterion = {
    value: unknown,
    check: Check
}

================================================
FILE: src/lib/src/server/AbstractTableHandler.svelte.ts
================================================
import type { State, Sort, Filter, TableParams } from '$lib/src/server'
import { EventDispatcher } from '$lib/src/shared'

export default class AbstractTableHandler<Row>
{
    protected selectBy         ?: keyof Row
    protected event             = new EventDispatcher()
    protected search            = $state<string>('')
    protected sort              = $state<(Sort<Row>)>({})
    
    public debounce             : number
    public totalRows            = $state<number>(undefined)
    public isLoading            = $state<boolean>(false)
    public rowsPerPage          = $state<number>(10)
    public currentPage          = $state<number>(1)
    public filters              = $state<(Filter<Row>)[]>([])
    public filterCount          = $derived<number>(this.filters.length)
    public rows                 = $state<Row[]>([])
    public rowCount             = $derived<{total: number, start: number, end: number, selected: number}>(this.createRowCount())
    public pages                = $derived<number[]>(this.createPages())
    public pageCount            = $derived<number>(this.createPageCount())
    public pagesWithEllipsis    = $derived<number[]>(this.createPagesWithEllipsis())
    public selected             = $state<(Row[keyof Row])[]>([])
    public isAllSelected        = $derived<boolean>(this.createIsAllSelected())
    public element              = $state<HTMLElement>(undefined)
    public clientWidth          = $state<number>(1000)

    constructor(data: Row[], params: TableParams<Row>)
    {
        this.rows               = data
        this.selectBy           = params.selectBy as keyof Row ?? undefined
        this.totalRows          = params.totalRows
        this.rowsPerPage        = params.rowsPerPage ?? 10
        this.debounce           = params.debounce ?? 0
    }

    public getState(): State<Row>
    {
        return {
            currentPage: this.currentPage,
            rowsPerPage: this.rowsPerPage,
            offset: this.rowsPerPage * (this.currentPage - 1),
            search: this.search,
            sort: this.sort.field ? this.sort : undefined,
            filters: this.filters.length > 0 ? this.filters : undefined,
            setTotalRows: (value: number) => this.totalRows = value
        }
    }

    private createPages()
    {
        if (!this.rowsPerPage || !this.totalRows) {
            return undefined
        }
        const pages = Array.from(Array(Math.ceil(this.totalRows / this.rowsPerPage)))
        return pages.map((_, i) => i + 1)
    }

    private createPageCount()
    {
        if (!this.pages) return undefined
        return this.pages.length
    }

    private createPagesWithEllipsis()
    {
        if (!this.pages) {
            return undefined
        }
        if (this.pageCount <= 7) {
            return this.pages
        }
        const ellipse = null
        const firstPage = 1
        const lastPage = this.pageCount
        if (this.currentPage <= 4) {
            return [
                ...this.pages.slice(0, 5),
                ellipse,
                lastPage
            ]
        } else if (this.currentPage < this.pageCount - 3) {
            return [
                firstPage,
                ellipse,
                ...this.pages.slice(this.currentPage - 2, this.currentPage + 1),
                ellipse,
                lastPage
            ]
        } else {
            return [
                firstPage,
                ellipse,
                ...this.pages.slice(this.pageCount - 5, this.pageCount)
            ]
        }
    }

    private createRowCount()
    {
        if (!this.rowsPerPage || !this.totalRows) {
            return {
                total: undefined,
                start: undefined,
                end: undefined,
                selected: this.selected.length
            }
        }
        return {
            total: this.totalRows,
            start: this.currentPage * this.rowsPerPage - this.rowsPerPage + 1,
            end: Math.min(this.currentPage * this.rowsPerPage, this.totalRows),
            selected: this.selected.length
        }
    }

    private createIsAllSelected()
    {
        if (this.rows.length === 0) {
            return false
        }
        const ids = this.rows.map(row => row[this.selectBy])
        return ids.every(id => this.selected.includes(id))
    }
}

================================================
FILE: src/lib/src/server/TableHandler.svelte.ts
================================================
import AbstractTableHandler from './AbstractTableHandler.svelte'

import FetchHandler         from './handlers/FetchHandler.svelte'
import SortHandler          from './handlers/SortHandler.svelte'
import SelectHandler        from './handlers/SelectHandler.svelte'
import PageHandler          from './handlers/PageHandler.svelte'
import SearchHandler        from './handlers/SearchHandler.svelte'
import FilterHandler        from './handlers/FilterHandler.svelte'

import ViewBuilder          from '../shared/builders/ViewBuilder.svelte'
import SearchBuilder        from './builders/SearchBuilder.svelte'
import SortBuilder          from './builders/SortBuilder.svelte'
import FilterBuilder        from './builders/FilterBuilder.svelte'

import type { Internationalization, Row, Field, State, ColumnView, TableParams } from '$lib/src/server'
import type { TableHandlerInterface } from '$lib/src/shared'



export default class TableHandler<T extends Row = any> extends AbstractTableHandler<T> implements TableHandlerInterface<T>
{
    private fetchHandler    : FetchHandler<T>
    private sortHandler     : SortHandler<T>
    private selectHandler   : SelectHandler<T>
    private pageHandler     : PageHandler<T>
    private searchHandler   : SearchHandler<T>
    private filterHandler   : FilterHandler<T>
    private view            : ViewBuilder<T>
    public  i18n            : Internationalization

    constructor(data: T[] = [], params: TableParams<T> = { rowsPerPage: 5 })
    {
        super(data, params)
        this.i18n           = this.translate(params.i18n)
        this.fetchHandler   = new FetchHandler(this)
        this.sortHandler    = new SortHandler(this)
        this.selectHandler  = new SelectHandler(this)
        this.pageHandler    = new PageHandler(this)
        this.searchHandler  = new SearchHandler(this)
        this.filterHandler  = new FilterHandler(this)
    }

    public load(callback: (state: State<T>) => Promise<T[]>): void
    {
        this.fetchHandler.set(callback)
    }

    public invalidate(): void
    {
        this.fetchHandler.invalidate()
    }

    public setRowsPerPage(value: number)
    {
        this.rowsPerPage = value
        this.setPage(1)
    }

    public setPage(value: number | 'previous' | 'next' | 'last'): void
    {
        switch (value) {
            case 'previous' : return this.pageHandler.previous()
            case 'next'     : return this.pageHandler.next()
            case 'last'     : return this.pageHandler.goto(this.pageCount)
            default         : return this.pageHandler.goto(value as number)
        }
    }

    public clearSearch(): void
    {
        this.searchHandler.clear()
    }

    public createSearch(): SearchBuilder<T>
    {
        return new SearchBuilder(this)
    }

    public createSort(field: Field<T>): SortBuilder<T>
    {
        if (typeof field === 'function') {
            throw new Error(`Invalid field argument: ${String(field)}. Function type arguments are not allowed in server-side mode`)
        }
        return new SortBuilder(this.sortHandler, field)
    }

    public clearFilters(): void
    {
        this.filterHandler.clear()
        this.invalidate()
    }

    public createFilter(field: Field<T>): FilterBuilder<T>
    {
        if (typeof field === 'function') {
            throw new Error(`Invalid field argument: ${String(field)}. Function type arguments are not allowed in server-side mode`)
        }
        return new FilterBuilder(this.filterHandler, field)
    }

    public select(value: T[keyof T])
    {
        this.selectHandler.set(value)
    }

    public selectAll(): void
    {
        this.selectHandler.all()
    }

    public clearSelection(): void
    {
        this.selectHandler.clear()
    }

    public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: () => void): void
    {
        this.event.add(event, callback)
    }

    public createView(columns: ColumnView[]): ViewBuilder<T>
    {
        this.view = new ViewBuilder(this, columns)
        return this.view
    }

    public getView(): ViewBuilder<T>
    {
        return this.view
    }

    private translate(i18n: Internationalization): Internationalization
    {
        return {
            ...{
                search: 'Search...',
                show: 'Show',
                entries: 'entries',
                filter: 'Filter',
                rowCount: 'Showing {start} to {end} of {total} entries',
                noRows: 'No entries found',
                previous: 'Previous',
                next: 'Next',
                selectedCount: '{count} of {total} row(s).'
            },
            ...i18n
        }
    }

}

================================================
FILE: src/lib/src/server/builders/FilterBuilder.svelte.ts
================================================
import type FilterHandler from '../handlers/FilterHandler.svelte'

export default class FilterBuilder<Row>
{
    public  value           = $state<string>('')
    private timeout         = undefined
    private filterHandler   : FilterHandler<Row>
    private field           : keyof Row

    constructor(filterHandler: FilterHandler<Row>, field: keyof Row)
    {
        this.filterHandler  = filterHandler
        this.field          = field
        this.cleanup()
    }

    public set()
    {
        this.filterHandler.set(this.value, this.field)
		clearTimeout(this.timeout)
		this.timeout = setTimeout( () => {
            this.filterHandler['table'].setPage(1)
		}, 400)
    }

    public init(value?: string)
    {
        if (!value) return this
        this.value = value
        this.filterHandler.set(this.value, this.field)
        return this
    }

    public clear()
    {
        this.value = ''
        this.filterHandler.unset(this.field)
    }

    private cleanup()
    {
        this.filterHandler['table'].on('clearFilters', () => this.clear())
    }
}


================================================
FILE: src/lib/src/server/builders/SearchBuilder.svelte.ts
================================================
import type TableHandler from '../TableHandler.svelte'
import type { SearchInterface } from '$lib/src/shared'

export default class SearchBuilder<Row> implements SearchInterface
{
    public  value           = $state<string>('')
    private timeout         = undefined
    private table           : TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
        this.cleanup()
    }

    public set()
    {
        this.table['search'] = this.value
        clearTimeout(this.timeout)
        this.timeout = setTimeout( () => {
            this.table.setPage(1)
        }, 400)
    }

    public init(value?: string)
    {
        if (!value) return this
        this.value = value
        this.table['search'] = value
        return this
    }

    public clear()
    {
        this.value = ''
        this.table['search'] = ''
        // this.table.invalidate()
        this.table.setPage(1)
    }

    private cleanup()
    {
        this.table.on('clearSearch', () => this.clear())
    }
}


================================================
FILE: src/lib/src/server/builders/SortBuilder.svelte.ts
================================================
import type SortHandler         from '../handlers/SortHandler.svelte'
import type { SortInterface }   from '$lib/src/shared'

export default class SortBuilder<Row> implements SortInterface
{
    private sortHandler : SortHandler<Row>
    private field       : keyof Row
    public  isActive    = $derived<boolean>(this.createIsActive())
    public  direction   = $derived<'asc' | 'desc'>(this.createDirection())

    constructor(sortHandler: SortHandler<Row>, field: keyof Row)
    {
        this.sortHandler = sortHandler
        this.field       = field
    }

    public set()
    {
        this.sortHandler.set(this.field)
    }

    public init(direction?: 'asc' | 'desc')
    {
        if (!direction) return this
        this[direction]()
        return this
    }

    public asc()
    {
        this.sortHandler.asc(this.field)
    }

    public desc()
    {
        this.sortHandler.desc(this.field)
    }

    private createIsActive()
    {
        if (this.field === this.sortHandler['table']['sort']?.field) {
            return true
        }
        return false
    }

    private createDirection()
    {
        if (this.isActive === false) return null
        return this.sortHandler['table']['sort']?.direction
    }
}


================================================
FILE: src/lib/src/server/handlers/FetchHandler.svelte.ts
================================================
import type { State, TableHandler } from '$lib/src/server'


export default class FetchHandler<Row>
{
    private table: TableHandler<Row>
    private reload: (state: State) => Promise<Row[]>
    private timeout: NodeJS.Timeout

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(callback: (state: State) => Promise<Row[]>)
    {
        this.reload = callback
    }

    public async invalidate()
    {
        if (!this.reload) return
        clearTimeout(this.timeout)
        this.timeout = setTimeout(() => this.trigger(), this.table.debounce)
    }

    private async trigger()
    {
        this.table.isLoading = true
        const state = this.table.getState()
        const data = await this.reload(state)
        this.table.isLoading = false
        if (data) {
            this.table.rows = data
        }
    }
}



================================================
FILE: src/lib/src/server/handlers/FilterHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/server'

export default class FilterHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>) 
    {
        this.table = table
    }

    public set(value: string | number, field: keyof Row)
    {
        this.table.filters = this.table.filters.filter(filter =>  filter.field !== field && filter.value)
        if (value) {
            this.table.filters.push({ value, field })
        }
    }

    public unset(field: keyof Row)
    {
        this.table.filters = this.table.filters.filter(filter => filter.field !== field)
    }

    public clear()
    {
        this.table.filters = []
        this.table['event'].dispatch('clearFilters')
    }
}


================================================
FILE: src/lib/src/server/handlers/PageHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/server'

export default class PageHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public goto(number: number)
    {
        if (this.table.rowsPerPage && this.table.totalRows) {
            if (number >= 1 && number <= this.table.pageCount) {
                this.table.currentPage = number
                this.table['event'].dispatch('change')
                this.table.invalidate()
            }
        }
        else {
            if (number >= 1) {
                this.table.currentPage = number
                this.table['event'].dispatch('change')
                this.table.invalidate()
            }
        }
    }

    public previous()
    {
        this.goto(this.table.currentPage - 1)
    }

    public next()
    {
        this.goto(this.table.currentPage + 1)
    }
}


================================================
FILE: src/lib/src/server/handlers/SearchHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/server'

export default class SearchHandler<Row> 
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>) {
        this.table = table
    }

    public clear()
    {
        this.table['event'].dispatch('clearSearch')
    }
}


================================================
FILE: src/lib/src/server/handlers/SelectHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/server'

export default class SelectHandler<Row>
{
    private table: TableHandler<Row>

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(value: Row[keyof Row])
    {
        if (this.table.selected.includes(value)) {
            this.table.selected = this.table.selected.filter((item) => item !== value)
        }
        else {
            this.table.selected = [value, ...this.table.selected]
        }
    }

    public all()
    {
        const selection = this.table.rows.map((row) => row[this.table['selectBy']])
        if (this.table.isAllSelected) {
            this.table.selected = this.table.selected.filter(item => selection.includes(item) === false)
        }
        else {
            this.table.selected = [...new Set([...selection, ...this.table.selected])]
        }
    }

    public clear()
    {
        this.table.selected = []
    }
}

================================================
FILE: src/lib/src/server/handlers/SortHandler.svelte.ts
================================================
import type { TableHandler } from '$lib/src/server'

export default class SortHandler<Row>
{
    private table: TableHandler

    constructor(table: TableHandler<Row>)
    {
        this.table = table
    }

    public set(field: keyof Row)
    {
        const sort = this.table['sort']

        if(!sort || sort.field !== field) {
            this.asc(field)
        }
        else if (sort.direction === 'asc') {
            this.desc(field)
        }
        else if (sort.direction === 'desc') {
            this.asc(field)
        }
    }

    public asc(field: keyof Row)
    {
        this.table['sort'] = { field, direction: 'asc' }
        this.table.setPage(1)
    }

    public desc(field: keyof Row)
    {
        this.table['sort'] = { field, direction: 'desc' }
        this.table.setPage(1)
    }
}


================================================
FILE: src/lib/src/server/index.ts
================================================
export { default as TableHandler }  from './TableHandler.svelte'
import type { Row, Internationalization } from '$lib/src/shared/index.js'
export {
    Datatable,
    Search,
    RowsPerPage,
    Th,
    ThSort,
    ThFilter,
    Pagination,
    RowCount,
    type Row,
    type Field,
    type ColumnView,
    type Internationalization,
    type TableHandlerInterface
} from '$lib/src/shared'

export type State<T extends Row = any> = {
    currentPage : number,
    rowsPerPage : number,
    offset      : number,
    search     ?: string,
    sort       ?: Sort<T>
    filters    ?: Filter<T>[]
    setTotalRows: (value: number) => void
}

export type TableParams<Row> = {
    rowsPerPage     ?: number,
    totalRows       ?: number,
    selectBy        ?: keyof Row,
    debounce        ?: number
    i18n            ?: Internationalization
}

export type Filter<Row> = {
    field: keyof Row
    value?: unknown
}

export type Sort<Row> = {
    field?: keyof Row
    direction?: 'asc' | 'desc'
}

================================================
FILE: src/lib/src/shared/Datatable.svelte
================================================
<script lang="ts">
    import type { Snippet } from 'svelte'
    import { type TableHandlerInterface, Search, RowsPerPage, RowCount, Pagination } from '$lib/src/shared'
    type T = $$Generic<Row>
    interface Props {
        table    : TableHandlerInterface<T>
        children : Snippet
        basic   ?: boolean
        headless?: boolean
        header  ?: Snippet
        footer  ?: Snippet
    }
    let { table, children, basic = false, headless = false, header = undefined, footer = undefined }: Props = $props()

    table.on('change', () => table.element ? table.element.scrollTop = 0 : '')
</script>

<section bind:clientWidth={table.clientWidth} class:svelte-simple-datatable={!headless}>

    <header>
        {#if header}
            {@render header()}
        {:else if basic === true}
            <Search {table}/>
            <RowsPerPage {table}/>
        {/if}
    </header>

    <article bind:this={table.element} class="thin-scrollbar">
        {@render children()}
    </article>

    <footer class:divider={basic}>
        {#if footer}
            {@render footer()}
        {:else if basic === true}
            <RowCount {table}/>
            <Pagination {table}/>
        {/if}
    </footer>

</section>

<style>
    section {
        height: 100%;
        display: flex;
        flex-direction: column;
        background: inherit;
        border-radius: inherit;
    }
    header, footer {
        padding: 0;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        width: 100%;
    }
    article {
        position: relative;
        height: 100%;
        overflow: auto;
        background: inherit;
    }
    article :global(.hidden) {
        display: none;
    }
    article::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }
    article :global(table) {
        border-collapse: separate;
        border-spacing: 0;
        width: 100%;
        background: inherit;
    }
    article :global(table thead) {
        position: sticky;
        inset-block-start: 0;
        background: inherit;
        z-index: 1;
    }
    article :global(thead tr) {
        background: inherit;
    }
    article :global(thead tr th) {
        background: inherit;
    }




    /* optional global style */
    .svelte-simple-datatable :global(thead tr:first-child th) {
        padding: 8px 20px;
        background: inherit;
    }
    .svelte-simple-datatable :global(tbody) {
        background: inherit;
    }
    .svelte-simple-datatable :global(tbody tr) {
        transition: background, 0.2s;
        background: inherit;
    }
    .svelte-simple-datatable :global(tbody tr:hover) {
        background: var(--grey-lighten-3, #fafafa);
    }
    .svelte-simple-datatable :global(tbody td) {
        padding: 4px 20px;
        border-right: 1px solid var(--grey-lighten, #eee);
        border-bottom: 1px solid var(--grey-lighten, #eee);
        background: inherit;
    }
    .svelte-simple-datatable :global(tbody td:last-child) {
        border-right: none;
    }
    .svelte-simple-datatable :global(u.highlight) {
        text-decoration: none;
        background: rgba(251, 192, 45, 0.6);
        border-radius: 2px;
    }
    .svelte-simple-datatable :global(footer.divider) {
        border-top: 1px solid var(--grey, #e0e0e0);
    }
</style>


================================================
FILE: src/lib/src/shared/EventDispatcher.ts
================================================

export default class EventDispatcher
{
    private listeners = {
        change      : [] as (() => void)[],
        clearFilters: [] as (() => void)[],
        clearSearch : [] as (() => void)[]
    }
    private queue: Set<string> = new Set()

    public add(event: keyof EventDispatcher['listeners'], callback: () => void)
    {
        this.listeners[event].push(callback)
    }

    public async dispatch(event: keyof EventDispatcher['listeners'])
    {
        this.queue.add(event)
        await new Promise((resolve) => setTimeout(resolve, 40))
        if (this.queue.size > 0) {
            this.run()
        }
        this.queue.clear()
    }

    private run()
    {
        for (const event of this.queue) {
            for (const callback of this.listeners[event]) {
                callback()
            }
        }
        // console.log(this.queue)
    }
}

================================================
FILE: src/lib/src/shared/Pagination.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface } from '$lib/src/shared'
    type T = $$Generic<Row>
    let { table }: { table: TableHandlerInterface<T> } = $props()
</script>

<section>
    {#if table.pages === undefined}
        {@render nopage()}
    {:else if table.clientWidth < 600}
        {@render small()}
    {:else}
        {@render ellipsis()}
    {/if}
</section>



{#snippet nopage()}
    <button type="button" class="small" class:disabled={table.currentPage === 1} onclick={() => table.setPage('previous')}>
        &#10094;
    </button>
    <button type="button" class="page">Page <b>{table.currentPage}</b></button>
    <button type="button" class="small" onclick={() => table.setPage('next')}>
        &#10095;
    </button>
{/snippet}



{#snippet small()}
    <button type="button" class="small" class:disabled={table.currentPage === 1} onclick={() => table.setPage(1)}>
        &#10092;&#10092;
    </button>
    <button type="button" class:disabled={table.currentPage === 1} onclick={() => table.setPage('previous')}>
        &#10094;
    </button>
    <button type="button" class:disabled={table.currentPage === table.pageCount} onclick={() => table.setPage('next')}>
        &#10095;
    </button>
    <button type="button" class="small" class:disabled={table.currentPage === table.pageCount} onclick={() => table.setPage('last')}>
        &#10093;&#10093;
    </button>
{/snippet}



{#snippet ellipsis()}
    <button type="button" class:disabled={table.currentPage === 1} onclick={() => table.setPage('previous')}>
        {@html table.i18n.previous}
    </button>
    {#each table.pagesWithEllipsis as page}
        <button type="button"
            class="bg-darken-active"
            class:active={table.currentPage === page}
            class:ellipse={page === null}
            onclick={() => table.setPage(page)}
        >
            {page ?? '...'}
        </button>
    {/each}
    <button type="button" class:disabled={table.currentPage === table.pageCount} onclick={() => table.setPage('next')}>
        {@html table.i18n.next}
    </button>
{/snippet}


<style>
    section {
        display: flex;
        margin: 8px 16px;
    }
    button {
        background: inherit;
        height: 32px;
        width: 32px;
        color: var(--font-grey, #9e9e9e);
        cursor: pointer;
        font-size: 13px;
        margin: 0;
        padding: 0;
        transition: all, 0.2s;
        line-height: 32px;
        border: 1px solid var(--grey, #e0e0e0);
        border-right: none;
        border-radius: 0;
        outline: none;
    }
    button:first-child {
        border-radius: 4px 0 0 4px;
    }
    button:last-child {
        border-right: 1px solid var(--grey, #e0e0e0);
        border-radius: 0 4px 4px 0;
    }

    button:first-child:not(.small),
    button:last-child:not(.small) {
        min-width: 72px;
    }

    button:not(.active):hover {
        background: var(--grey-lighten, #eee);
    }
    button.ellipse:hover {
        background: inherit;
        cursor: default;
    }
    button.active {
        background: var(--grey-lighten, #eee);
        font-weight: bold;
        color: var(--font, #424242);
        cursor: default;
    }
    button.disabled:hover {
        background: inherit;
        cursor: default;
    }
    button.page {
        width: 80px;
    }
</style>


================================================
FILE: src/lib/src/shared/RowCount.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface } from '$lib/src/shared'

    type T = $$Generic<Row>
    let { table, selection = false }: { table: TableHandlerInterface<T>, selection?: boolean } = $props()

    const { start, end, total, selected } = $derived(table.rowCount)
</script>

<aside>
    {#if selection}
        {@render selectedRows()}
    {:else if table.clientWidth < 640}
        {@render small()}
    {:else}
        {@render rowCount()}
    {/if}
</aside>



{#snippet selectedRows()}
    <b>{selected}</b>
    {#if total}
        of <b>{total}</b>
    {/if}
    row(s) selected.
    {#if selected > 0}
        <button type="button" onclick={() => table.clearSelection()}>❌ Clear</button>
    {/if}
{/snippet}



{#snippet small()}
    {#if total > 0}
        <b>{start}</b>-
        <b>{end}</b>/
        <b>{total}</b>
    {:else}
        {table.i18n.noRows}
    {/if}
{/snippet}



{#snippet rowCount()}
    {#if total > 0}
        {@html table.i18n.rowCount
            .replace('{start}', `<b>${start}</b>`)
            .replace('{end}', `<b>${end}</b>`)
            .replace('{total}', `<b>${total}</b>`)}
    {:else}
        {table.i18n.noRows}
    {/if}
{/snippet}



<style>
    aside {
        margin: 8px 16px;
        color: var(--font-grey, #757575);
        line-height: 32px;
        font-size: 14px;
    }
</style>


================================================
FILE: src/lib/src/shared/RowsPerPage.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface } from '$lib/src/shared'

    type T = $$Generic<Row>
    let { table, options = [5, 10, 20, 50, 100] }: { table: TableHandlerInterface<T>, options?: number[] } = $props()
</script>

<aside>
    {#if table.clientWidth > 600}
        <span>{table.i18n.show}</span>
    {/if}
    <select bind:value={table.rowsPerPage} onchange={() => table.setPage(1)}>
        {#each options as option}
            <option value={option}>
                {option}
            </option>
        {/each}
    </select>
    {#if table.clientWidth > 600}
        <span>{table.i18n.entries}</span>
    {/if}
</aside>

<style>
    aside {
        margin: 8px 16px;
        display: flex;
        justify-content: flex-start;
        align-items: center;
        height: 32px;
        color: #757575;
    }
    select {
        margin: 0 4px;
        background: var(--bg, #fff);
        color: var(--font-grey, #757575);
        border-radius: 4px;
        border: 1px solid var(--grey, #9e9e9e);
        padding: 2px 4px;
    }
    select:focus {
        outline: 2px solid var(--grey, #e0e0e0);
    }
</style>


================================================
FILE: src/lib/src/shared/Search.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface } from '$lib/src/shared'
    type T = $$Generic<Row>
    let { table, value }: { table: TableHandlerInterface<T>, value?: string } = $props()
    const search = table.createSearch().init(value)
</script>

<input
    bind:value={search.value}
    oninput={() => search.set()}
    placeholder={table.i18n.search}
    spellcheck="false"
/>

<style>
    input {
        margin: 8px 16px;
        border: 1px solid var(--grey, #e0e0e0);
        color: var(--font-grey, #424242);
        border-radius: 4px;
        outline: none;
        padding: 0 8px;
        line-height: 24px;
        height: 24px;
        background: transparent;
        width: 40%;
        max-width: 176px;
        min-width: 96px;
        transition: all, 0.1s;
    }
    input:focus {
        border: 1px solid var(--grey, #e0e0e0);
        outline: 2px solid var(--grey, #e0e0e0);
    }
    input::placeholder {
        color: #9e9e9e;
        line-height: 24px;
    }
</style>


================================================
FILE: src/lib/src/shared/Th.svelte
================================================
<script lang="ts">
    import type { Snippet } from 'svelte'

    let { children }: { children?: Snippet } = $props()
</script>

<th>
    <strong>
        {@render children?.()}
    </strong>
</th>

<style>
    th {
        background: inherit;
        padding: 8px 20px;
        white-space: nowrap;
        font-size: 13px;
        user-select: none;
        text-align: left;
        border-bottom: 1px solid var(--grey, #e0e0e0);
    }
    th strong {
        white-space: pre-wrap;
        font-weight: bold;
        font-size: 13.5px;
        line-height: 16px;
    }
</style>


================================================
FILE: src/lib/src/shared/ThFilter.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface, Field } from '$lib/src/shared'
    import type { Check } from '$lib/src/client'

    type T = $$Generic<Row>
    type Props = {
        table  : TableHandlerInterface<T>,
        field  : Field<T>,
        value ?: unknown,
        check ?: Check
    }
    let { table, field, value, check }: Props = $props()

    const filter = table.createFilter(field, check).init(value)
</script>

<th>
    <input
        type="text"
        placeholder={table.i18n.filter}
        spellcheck="false"
        bind:value={filter.value}
        oninput={() => filter.set()}
    />
</th>

<style>
    th {
        border-bottom: 1px solid var(--grey, #e0e0e0);
    }
    input {
        width: 100%;
        height: 24px;
        border: none;
        text-align: left;
        padding: 0 20px;
        background: inherit;
        outline: none;
        border-radius: 0;
        font-size: 14px;
        color: var(--font-grey, #757575);
        font-family: Arial, Helvetica, sans-serif;
    }
    input::placeholder {
        color: var(--grey, #bdbdbd);
        font-size: 13px;
    }
    input:focus {
        outline: none;
        border: none;
    }
</style>


================================================
FILE: src/lib/src/shared/ThSort.svelte
================================================
<script lang="ts">
    import type { TableHandlerInterface, Field } from '$lib/src/shared'
    import type { Snippet } from 'svelte'

    type T = $$Generic<Row>
    type Props = {
        table   : TableHandlerInterface<T>,
        field   : Field<T>,
        direction?: 'asc' | 'desc',
        children: Snippet
    }
    let { table, field, direction, children }: Props = $props()

    const sort = table.createSort(field).init(direction)
</script>

<th onclick={() => sort.set()} class:active={sort.isActive}>
    <div class="flex">
        <strong>
            {@render children()}
        </strong>
        <span
            class:asc={sort.direction === 'asc'}
            class:desc={sort.direction === 'desc'}>
        </span>
    </div>
</th>

<style>
    th {
        background: inherit;
        padding: 8px 20px;
        white-space: nowrap;
        font-size: 13px;
        user-select: none;
        border-bottom: 1px solid var(--grey, #e0e0e0);
        cursor: pointer;
    }
    th strong {
        white-space: pre-wrap;
        font-size: 13.5px;
        line-height: 16px;
        text-align: left;
    }
    div.flex {
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        height: 100%;
    }
    span {
        padding-left: 8px;
    }
    span:before,
    span:after {
        border: 4px solid transparent;
        content: '';
        display: block;
        height: 0;
        width: 0;
    }
    span:before {
        border-bottom-color: var(--grey, #e0e0e0);
        margin-top: 2px;
    }
    span:after {
        border-top-color: var(--grey, #e0e0e0);
        margin-top: 2px;
    }
    th.active span.asc:before {
        border-bottom-color: var(--font-grey, #9e9e9e);
    }
    th.active span.desc:after {
        border-top-color: var(--font-grey, #9e9e9e);
    }
</style>


================================================
FILE: src/lib/src/shared/builders/HighlightBuilder.svelte.ts
================================================
import type { TableHandlerInterface } from '$lib/src/shared'
import { stringify } from '$lib/src/client/core'


export default class HighlightBuilder<Row>
{
    public  selector    : string
    private table       : TableHandlerInterface<Row>
    private interval    : NodeJS.Timeout
    private keywords    = $derived(this.createKeywords())

    constructor(table: TableHandlerInterface<Row>, selector: string = 'tbody')
    {
        this.table      = table
        this.selector   = selector
        this.interval   = setInterval(() => this.createHighlight(), 200)
    }

    private createHighlight()
    {
        if (!this.table?.element) {
            return
        }
        clearInterval(this.interval)
        const node = this.table.element.querySelector(this.selector)
        this.table.on('change', () => { 
            // this.reset(node)
            this.emphasize(node)
        })
    }

    private createKeywords(): string[]
    {
        return [
            this.table['search'].value ?? null,
            // ...this.table.filters.map(filter => filter.value)
        ].filter(Boolean)
    }

    private emphasize(node: Node)
    {
        if (this.keywords.length === 0) return
        if (node.nodeType === Node.ELEMENT_NODE) {
            for (const child of node.childNodes) {
                if (child.nodeName !== 'EM') {
                    this.emphasize(child)
                }
            }
        }
        else if (node.nodeTyp
Download .txt
gitextract_z5jwp21r/

├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── datatables.code-workspace
├── ecosystem.config.cjs
├── mdsvex.config.js
├── package.json
├── src/
│   ├── app.d.ts
│   ├── app.html
│   ├── hooks.server.ts
│   ├── lib/
│   │   ├── index.ts
│   │   ├── legacy/
│   │   │   ├── index.ts
│   │   │   ├── local/
│   │   │   │   ├── Comparator.ts
│   │   │   │   ├── Context.ts
│   │   │   │   ├── DataHandler.ts
│   │   │   │   ├── Datatable.svelte
│   │   │   │   ├── Pagination.svelte
│   │   │   │   ├── RowCount.svelte
│   │   │   │   ├── RowsPerPage.svelte
│   │   │   │   ├── Search.svelte
│   │   │   │   ├── Th.svelte
│   │   │   │   ├── ThFilter.svelte
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── EventHandler.ts
│   │   │   │   │   ├── FilterHandler.ts
│   │   │   │   │   ├── PageHandler.ts
│   │   │   │   │   ├── SearchHandler.ts
│   │   │   │   │   ├── SelectHandler.ts
│   │   │   │   │   └── SortHandler.ts
│   │   │   │   ├── helpers/
│   │   │   │   │   ├── AdvancedFilterHelper.ts
│   │   │   │   │   ├── CalculationHelper.ts
│   │   │   │   │   └── FilterHelper.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── utils.ts
│   │   │   └── remote/
│   │   │       ├── Context.ts
│   │   │       ├── DataHandler.ts
│   │   │       ├── Datatable.svelte
│   │   │       ├── Pagination.svelte
│   │   │       ├── README.md
│   │   │       ├── RowCount.svelte
│   │   │       ├── RowsPerPage.svelte
│   │   │       ├── Search.svelte
│   │   │       ├── SelectedCount.svelte
│   │   │       ├── Th.svelte
│   │   │       ├── ThFilter.svelte
│   │   │       ├── handlers/
│   │   │       │   ├── EventHandler.ts
│   │   │       │   ├── FilterHandler.ts
│   │   │       │   ├── PageHandler.ts
│   │   │       │   ├── SearchHandler.ts
│   │   │       │   ├── SelectHandler.ts
│   │   │       │   ├── SortHandler.ts
│   │   │       │   └── TriggerHandler.ts
│   │   │       └── index.ts
│   │   ├── server/
│   │   │   └── index.ts
│   │   ├── src/
│   │   │   ├── client/
│   │   │   │   ├── AbstractTableHandler.svelte.ts
│   │   │   │   ├── TableHandler.svelte.ts
│   │   │   │   ├── builders/
│   │   │   │   │   ├── AdvancedFilterBuilder.svelte.ts
│   │   │   │   │   ├── CSVBuilder.svelte.ts
│   │   │   │   │   ├── CalculationBuilder.svelte.ts
│   │   │   │   │   ├── FilterBuilder.svelte.ts
│   │   │   │   │   ├── QueryBuilder.svelte.ts
│   │   │   │   │   ├── RecordFilterBuilder.svelte.ts
│   │   │   │   │   ├── SearchBuilder.svelte.ts
│   │   │   │   │   └── SortBuilder.svelte.ts
│   │   │   │   ├── core/
│   │   │   │   │   ├── check.ts
│   │   │   │   │   ├── entry.ts
│   │   │   │   │   ├── field.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── rows.ts
│   │   │   │   │   └── value.ts
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── FilterHandler.svelte.ts
│   │   │   │   │   ├── PageHandler.svelte.ts
│   │   │   │   │   ├── QueryHandler.svelte.ts
│   │   │   │   │   ├── SearchHandler.svelte.ts
│   │   │   │   │   ├── SelectHandler.svelte.ts
│   │   │   │   │   └── SortHandler.svelte.ts
│   │   │   │   └── index.ts
│   │   │   ├── server/
│   │   │   │   ├── AbstractTableHandler.svelte.ts
│   │   │   │   ├── TableHandler.svelte.ts
│   │   │   │   ├── builders/
│   │   │   │   │   ├── FilterBuilder.svelte.ts
│   │   │   │   │   ├── SearchBuilder.svelte.ts
│   │   │   │   │   └── SortBuilder.svelte.ts
│   │   │   │   ├── handlers/
│   │   │   │   │   ├── FetchHandler.svelte.ts
│   │   │   │   │   ├── FilterHandler.svelte.ts
│   │   │   │   │   ├── PageHandler.svelte.ts
│   │   │   │   │   ├── SearchHandler.svelte.ts
│   │   │   │   │   ├── SelectHandler.svelte.ts
│   │   │   │   │   └── SortHandler.svelte.ts
│   │   │   │   └── index.ts
│   │   │   └── shared/
│   │   │       ├── Datatable.svelte
│   │   │       ├── EventDispatcher.ts
│   │   │       ├── Pagination.svelte
│   │   │       ├── RowCount.svelte
│   │   │       ├── RowsPerPage.svelte
│   │   │       ├── Search.svelte
│   │   │       ├── Th.svelte
│   │   │       ├── ThFilter.svelte
│   │   │       ├── ThSort.svelte
│   │   │       ├── builders/
│   │   │       │   ├── HighlightBuilder.svelte.ts
│   │   │       │   └── ViewBuilder.svelte.ts
│   │   │       ├── clsx/
│   │   │       │   ├── Datatable.svelte
│   │   │       │   ├── Pagination.svelte
│   │   │       │   └── ThSort.svelte
│   │   │       └── index.ts
│   │   └── style.css
│   ├── routes/
│   │   ├── +layout.svelte
│   │   ├── +page.svelte
│   │   ├── Description.svelte
│   │   ├── Header.svelte
│   │   ├── Header_Github.svelte
│   │   ├── Header_MobileNav.svelte
│   │   ├── Header_Mode.svelte
│   │   ├── Header_Theme.svelte
│   │   ├── Header_Version.svelte
│   │   ├── about/
│   │   │   ├── +layout.svelte
│   │   │   ├── +page.svelte
│   │   │   ├── Nav.svelte
│   │   │   ├── Nav_Mobile.svelte
│   │   │   └── [slug]/
│   │   │       ├── +page.server.ts
│   │   │       └── +page.svelte
│   │   ├── api/
│   │   │   └── [mode]/
│   │   │       ├── +layout.server.ts
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.svelte
│   │   │       ├── Nav.svelte
│   │   │       ├── Nav_Key.svelte
│   │   │       ├── Nav_Mobile.svelte
│   │   │       ├── [slug]/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Content.svelte
│   │   │       │   └── Content_Ext.svelte
│   │   │       ├── content.svx
│   │   │       ├── gen/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   └── Board.svelte
│   │   │       └── md/
│   │   │           ├── +layout.svelte
│   │   │           ├── +layout.ts
│   │   │           ├── +page.svelte
│   │   │           ├── Nav.svelte
│   │   │           ├── Nav_Key.svelte
│   │   │           ├── [slug]/
│   │   │           │   ├── +page.svelte
│   │   │           │   ├── +page.ts
│   │   │           │   ├── AST.svelte
│   │   │           │   ├── Content.svelte
│   │   │           │   └── Content_Ext.svelte
│   │   │           └── content.svx
│   │   ├── components/
│   │   │   ├── +layout.svelte
│   │   │   └── +page.svelte
│   │   ├── docs/
│   │   │   ├── client/
│   │   │   │   ├── +layout.svelte
│   │   │   │   ├── +page.server.ts
│   │   │   │   ├── add-on/
│   │   │   │   │   ├── csv-export/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── record-filter/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Main.svelte
│   │   │   │   │       ├── content.svx
│   │   │   │   │       └── data_cars.ts
│   │   │   │   ├── calculation/
│   │   │   │   │   ├── avg/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Advanced.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── bounds/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── data_cars.ts
│   │   │   │   │   ├── data_grocery.ts
│   │   │   │   │   ├── data_parcel.ts
│   │   │   │   │   ├── distinct/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   ├── median/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Advanced.svelte
│   │   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   │   └── code.svx
│   │   │   │   │   └── sum/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Advanced.svelte
│   │   │   │   │       ├── Basic.svelte
│   │   │   │   │       └── code.svx
│   │   │   │   ├── filters/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── check/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Comparators.svelte
│   │   │   │   │   │   ├── Comparators_Check.svelte
│   │   │   │   │   │   ├── content.svx
│   │   │   │   │   │   ├── content2.svx
│   │   │   │   │   │   └── data.ts
│   │   │   │   │   ├── criteria/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   ├── content.svx
│   │   │   │   │   │   └── data.ts
│   │   │   │   │   ├── highlight/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── nested/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Nested.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── getting-started/
│   │   │   │   │   ├── hello-world/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Client.svx
│   │   │   │   │   │   └── Main.svelte
│   │   │   │   │   ├── i18n/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── doc.svx
│   │   │   │   │   ├── intro/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── migration/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── overview/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── pagination/
│   │   │   │   │   ├── navigation/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Navigation.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── pages/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── CurrentPage.svelte
│   │   │   │   │   │   ├── Pages.svelte
│   │   │   │   │   │   ├── PagesWithEllipsis.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── row-count/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── rows-per-page/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── search/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── highlight/
│   │   │   │   │   │   ├── +page.server.ts
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── +page.server.ts
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── recursive/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   ├── Main_Search.svelte
│   │   │   │   │   │   ├── Main_Tree.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── regex/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── scope/
│   │   │   │   │       ├── +page.server.ts
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Example.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── select/
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   ├── all/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── row/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── scope/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   ├── sort/
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Main.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   ├── dates/
│   │   │   │   │   │   ├── +page.svelte
│   │   │   │   │   │   ├── Example.svelte
│   │   │   │   │   │   └── content.svx
│   │   │   │   │   └── nested/
│   │   │   │   │       ├── +page.svelte
│   │   │   │   │       ├── Nested.svelte
│   │   │   │   │       └── content.svx
│   │   │   │   └── view/
│   │   │   │       ├── freeze/
│   │   │   │       │   ├── +page.svelte
│   │   │   │       │   ├── Main.svelte
│   │   │   │       │   └── content.svx
│   │   │   │       ├── ordering/
│   │   │   │       │   ├── +page.svelte
│   │   │   │       │   └── content.svx
│   │   │   │       └── visible/
│   │   │   │           ├── +page.svelte
│   │   │   │           ├── Main.svelte
│   │   │   │           └── content.svx
│   │   │   └── server/
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.server.ts
│   │   │       ├── data/
│   │   │       │   ├── invalidate/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── is-loading/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   ├── api.ts
│   │   │       │   │   └── content.svx
│   │   │       │   └── load/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── filters/
│   │   │       │   └── input/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── getting-started/
│   │   │       │   ├── hello-world/
│   │   │       │   │   ├── +page.server.ts
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   ├── api.ts
│   │   │       │   │   └── content.svx
│   │   │       │   ├── i18n/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Main.svelte
│   │   │       │   │   └── doc.svx
│   │   │       │   ├── intro/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── migration/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   └── overview/
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       └── content.svx
│   │   │       ├── pagination/
│   │   │       │   ├── navigation/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── Navigation.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── pages/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   ├── CurrentPage.svelte
│   │   │       │   │   ├── Pages.svelte
│   │   │       │   │   ├── PagesWithEllipsis.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── row-count/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   └── rows-per-page/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── search/
│   │   │       │   └── input/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── select/
│   │   │       │   ├── +layout.server.ts
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── Main_OLD.svelte
│   │   │       │   ├── all/
│   │   │       │   │   ├── +page.svelte
│   │   │       │   │   └── content.svx
│   │   │       │   ├── api.ts
│   │   │       │   └── row/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── content.svx
│   │   │       ├── sort/
│   │   │       │   └── button/
│   │   │       │       ├── +page.server.ts
│   │   │       │       ├── +page.svelte
│   │   │       │       ├── Main.svelte
│   │   │       │       ├── api.ts
│   │   │       │       └── content.svx
│   │   │       ├── tips/
│   │   │       │   └── sticky-header/
│   │   │       │       ├── +page.svelte
│   │   │       │       └── doc.svx
│   │   │       └── view/
│   │   │           ├── +layout.server.ts
│   │   │           ├── api.ts
│   │   │           ├── freeze/
│   │   │           │   ├── +page.svelte
│   │   │           │   ├── Main.svelte
│   │   │           │   └── content.svx
│   │   │           ├── ordering/
│   │   │           │   ├── +page.svelte
│   │   │           │   └── content.svx
│   │   │           └── visible/
│   │   │               ├── +page.svelte
│   │   │               ├── Main.svelte
│   │   │               └── content.svx
│   │   ├── examples/
│   │   │   ├── client/
│   │   │   │   ├── +layout.svelte
│   │   │   │   ├── +page.server.ts
│   │   │   │   ├── +page.svelte
│   │   │   │   ├── Test.svelte
│   │   │   │   ├── column-ordering/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   └── SortButton.svelte
│   │   │   │   ├── crud/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Datatable.svelte
│   │   │   │   │   ├── Modal_Create.svelte
│   │   │   │   │   ├── Modal_Destroy.svelte
│   │   │   │   │   ├── Modal_Update.svelte
│   │   │   │   │   ├── api.svelte.ts
│   │   │   │   │   ├── doc.svx
│   │   │   │   │   └── intro.svx
│   │   │   │   ├── distinct/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Basic.svelte
│   │   │   │   │   ├── RowCount.svelte
│   │   │   │   │   ├── Search.svelte
│   │   │   │   │   ├── code.svx
│   │   │   │   │   └── data.ts
│   │   │   │   ├── hello-world/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   ├── Main.svelte
│   │   │   │   │   └── page.server.ts
│   │   │   │   ├── nested-array/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Client.svx
│   │   │   │   │   └── Main.svelte
│   │   │   │   ├── pokedex/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── MCF_Main.svelte
│   │   │   │   │   ├── MCF_PokemonStats.svelte
│   │   │   │   │   ├── MCF_Table.svelte
│   │   │   │   │   ├── MCF_TableFilter.svelte
│   │   │   │   │   └── data.ts
│   │   │   │   ├── shadcn/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Table.svelte
│   │   │   │   │   ├── Table_Footer.svelte
│   │   │   │   │   ├── Table_Footer_Pagination.svelte
│   │   │   │   │   ├── Table_Footer_RowSelection.svelte
│   │   │   │   │   ├── Table_Footer_RowsPerPage.svelte
│   │   │   │   │   ├── Table_Header.svelte
│   │   │   │   │   ├── Table_Header_ColumnVisibility.svelte
│   │   │   │   │   ├── Table_Header_Filter.svelte
│   │   │   │   │   ├── Table_Th.svelte
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── test/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   ├── Table.svelte
│   │   │   │   │   └── data2.ts
│   │   │   │   ├── test-calculation/
│   │   │   │   │   ├── +page.svelte
│   │   │   │   │   └── data.ts
│   │   │   │   └── tree/
│   │   │   │       ├── +page.svelte
│   │   │   │       ├── Client.svx
│   │   │   │       ├── Main.svelte
│   │   │   │       ├── Main_Search.svelte
│   │   │   │       └── Main_Tree.svelte
│   │   │   └── server/
│   │   │       ├── +layout.svelte
│   │   │       ├── +page.server.ts
│   │   │       ├── Features.svelte
│   │   │       ├── beer-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── hello-world/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── pokedex-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── PokemonStats.svelte
│   │   │       │   ├── PokemonTypes.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       ├── ssr-user/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   └── params.svelte.ts
│   │   │       ├── todo-api/
│   │   │       │   ├── +page.server.ts
│   │   │       │   ├── +page.svelte
│   │   │       │   ├── Main.svelte
│   │   │       │   ├── api.ts
│   │   │       │   └── example.server.ts
│   │   │       └── user-api/
│   │   │           ├── +page.server.ts
│   │   │           ├── +page.svelte
│   │   │           ├── Main.svelte
│   │   │           ├── api.ts
│   │   │           └── example.server.ts
│   │   └── export/
│   │       └── [mode]/
│   │           ├── gen/
│   │           │   ├── +page.server.ts
│   │           │   ├── +page.svelte
│   │           │   └── Board.svelte
│   │           └── md/
│   │               ├── +layout.svelte
│   │               ├── +layout.ts
│   │               ├── +page.svelte
│   │               ├── Nav.svelte
│   │               ├── Nav_Key.svelte
│   │               ├── [slug]/
│   │               │   ├── +page.svelte
│   │               │   ├── +page.ts
│   │               │   ├── AST.svelte
│   │               │   ├── Content.svelte
│   │               │   └── Content_Ext.svelte
│   │               └── content.svx
│   └── site/
│       ├── Banner.svelte
│       ├── Logo.svelte
│       ├── Site.svelte.ts
│       ├── components/
│       │   ├── Demo.svelte
│       │   ├── Demo_Code.svelte
│       │   ├── Demo_Code_Icon.svelte
│       │   ├── Demo_Code_Nav.svelte
│       │   ├── Demo_CopyButton.svelte
│       │   ├── Demo_Data.svelte
│       │   ├── Highlight.svelte
│       │   ├── Install.svelte
│       │   ├── Nav_Search.svelte
│       │   ├── docs/
│       │   │   ├── Layout.svelte
│       │   │   ├── Nav.svelte
│       │   │   └── Nav_Mobile.svelte
│       │   └── examples/
│       │       ├── Layout.svelte
│       │       ├── Nav.svelte
│       │       └── Nav_Mobile.svelte
│       ├── data/
│       │   ├── cars.ts
│       │   ├── data.11000.ts
│       │   ├── data.75.ts
│       │   ├── data.ts
│       │   ├── data_with_null.ts
│       │   ├── int-bool-string.ts
│       │   ├── pokedex.ts
│       │   └── tree.ts
│       ├── index.ts
│       └── utils/
│           └── viewport.ts
├── static/
│   ├── documents/
│   │   ├── client/
│   │   │   ├── methods.clearFilters.json
│   │   │   ├── methods.clearSearch.json
│   │   │   ├── methods.clearSelection.json
│   │   │   ├── methods.clearSort.json
│   │   │   ├── methods.createAdvancedFilter.json
│   │   │   ├── methods.createCSV.json
│   │   │   ├── methods.createCalculation.json
│   │   │   ├── methods.createFilter.json
│   │   │   ├── methods.createQuery.json
│   │   │   ├── methods.createRecordFilter.json
│   │   │   ├── methods.createSearch.json
│   │   │   ├── methods.createSort.json
│   │   │   ├── methods.createView.json
│   │   │   ├── methods.filter.json
│   │   │   ├── methods.getSelectedRows.json
│   │   │   ├── methods.getView.json
│   │   │   ├── methods.on.json
│   │   │   ├── methods.select.json
│   │   │   ├── methods.selectAll.json
│   │   │   ├── methods.setPage.json
│   │   │   ├── methods.setRows.json
│   │   │   ├── methods.setRowsPerPage.json
│   │   │   ├── nav.json
│   │   │   ├── properties.allRows.json
│   │   │   ├── properties.clientWidth.json
│   │   │   ├── properties.currentPage.json
│   │   │   ├── properties.element.json
│   │   │   ├── properties.filterCount.json
│   │   │   ├── properties.filters.json
│   │   │   ├── properties.i18n.json
│   │   │   ├── properties.isAllSelected.json
│   │   │   ├── properties.pageCount.json
│   │   │   ├── properties.pages.json
│   │   │   ├── properties.pagesWithEllipsis.json
│   │   │   ├── properties.queries.json
│   │   │   ├── properties.rowCount.json
│   │   │   ├── properties.rows.json
│   │   │   ├── properties.rowsPerPage.json
│   │   │   ├── properties.selected.json
│   │   │   ├── types.Check.json
│   │   │   ├── types.ColumnView.json
│   │   │   ├── types.Criterion.json
│   │   │   ├── types.Field.json
│   │   │   ├── types.Filter.json
│   │   │   ├── types.Internationalization.json
│   │   │   ├── types.Row.json
│   │   │   ├── types.SearchType.json
│   │   │   ├── types.Sort.json
│   │   │   ├── types.SortParams.json
│   │   │   ├── types.TableHandlerParams.json
│   │   │   ├── types.TableParams.json
│   │   │   └── types.ViewColumn.json
│   │   ├── markdown/
│   │   │   ├── client/
│   │   │   │   ├── methods.clearSort.md
│   │   │   │   ├── methods.createAdvancedFilter.md
│   │   │   │   ├── methods.createCSV.md
│   │   │   │   ├── methods.createCalculation.md
│   │   │   │   ├── methods.createRecordFilter.md
│   │   │   │   ├── methods.getSelectedRows.md
│   │   │   │   ├── methods.setRows.md
│   │   │   │   ├── methods.setRowsPerPage.md
│   │   │   │   ├── properties.allRows.md
│   │   │   │   ├── properties.filters.md
│   │   │   │   ├── types.Check.md
│   │   │   │   ├── types.Comparator.md
│   │   │   │   ├── types.Criterion.md
│   │   │   │   ├── types.Filter.md
│   │   │   │   ├── types.Internationalization.md
│   │   │   │   ├── types.Sort.md
│   │   │   │   ├── types.SortParams.md
│   │   │   │   └── types.TableParams.md
│   │   │   ├── methods.clearFilters.md
│   │   │   ├── methods.clearSearch.md
│   │   │   ├── methods.clearSelection.md
│   │   │   ├── methods.createFilter.md
│   │   │   ├── methods.createSearch.md
│   │   │   ├── methods.createSort.md
│   │   │   ├── methods.createView.md
│   │   │   ├── methods.getView.md
│   │   │   ├── methods.on.md
│   │   │   ├── methods.select.md
│   │   │   ├── methods.selectAll.md
│   │   │   ├── methods.setPage.md
│   │   │   ├── methods.setRowsPerPage.md
│   │   │   ├── properties.clientWidth.md
│   │   │   ├── properties.currentPage.md
│   │   │   ├── properties.element.md
│   │   │   ├── properties.filterCount.md
│   │   │   ├── properties.i18n.md
│   │   │   ├── properties.isAllSelected.md
│   │   │   ├── properties.pageCount.md
│   │   │   ├── properties.pages.md
│   │   │   ├── properties.pagesWithEllipsis.md
│   │   │   ├── properties.rowCount.md
│   │   │   ├── properties.rows.md
│   │   │   ├── properties.rowsPerPage.md
│   │   │   ├── properties.selected.md
│   │   │   ├── properties.sort.md
│   │   │   ├── server/
│   │   │   │   ├── methods.getState.md
│   │   │   │   ├── methods.invalidate.md
│   │   │   │   ├── methods.load.md
│   │   │   │   ├── properties.filters.md
│   │   │   │   ├── properties.isLoading.md
│   │   │   │   ├── properties.sort.md
│   │   │   │   ├── properties.totalRows.md
│   │   │   │   ├── types.Filter.md
│   │   │   │   ├── types.Sort.md
│   │   │   │   └── types.State.md
│   │   │   ├── types.ColumnView.md
│   │   │   ├── types.Field.md
│   │   │   ├── types.Internationalization.md
│   │   │   └── types.Row.md
│   │   └── server/
│   │       ├── methods.clearFilters.json
│   │       ├── methods.clearSearch.json
│   │       ├── methods.clearSelection.json
│   │       ├── methods.createFilter.json
│   │       ├── methods.createSearch.json
│   │       ├── methods.createSort.json
│   │       ├── methods.createView.json
│   │       ├── methods.filter.json
│   │       ├── methods.getState.json
│   │       ├── methods.getView.json
│   │       ├── methods.invalidate.json
│   │       ├── methods.load.json
│   │       ├── methods.on.json
│   │       ├── methods.select.json
│   │       ├── methods.selectAll.json
│   │       ├── methods.setPage.json
│   │       ├── methods.setRowsPerPage.json
│   │       ├── methods.setTotalRows.json
│   │       ├── nav.json
│   │       ├── properties.clientWidth.json
│   │       ├── properties.currentPage.json
│   │       ├── properties.debounce.json
│   │       ├── properties.element.json
│   │       ├── properties.events.json
│   │       ├── properties.filterCount.json
│   │       ├── properties.filters.json
│   │       ├── properties.i18n.json
│   │       ├── properties.isAllSelected.json
│   │       ├── properties.isLoading.json
│   │       ├── properties.pageCount.json
│   │       ├── properties.pages.json
│   │       ├── properties.pagesWithEllipsis.json
│   │       ├── properties.rowCount.json
│   │       ├── properties.rows.json
│   │       ├── properties.rowsPerPage.json
│   │       ├── properties.search.json
│   │       ├── properties.selectBy.json
│   │       ├── properties.selected.json
│   │       ├── properties.sort.json
│   │       ├── properties.totalRows.json
│   │       ├── types.ColumnView.json
│   │       ├── types.Field.json
│   │       ├── types.Filter.json
│   │       ├── types.Internationalization.json
│   │       ├── types.Row.json
│   │       ├── types.Sort.json
│   │       ├── types.State.json
│   │       ├── types.TableParams.json
│   │       └── types.ViewColumn.json
│   ├── fonts/
│   │   ├── Inter/
│   │   │   ├── OFL.txt
│   │   │   └── README.txt
│   │   └── Roboto/
│   │       └── LICENSE.txt
│   ├── global.css
│   ├── gros-theme.css
│   ├── gros.css
│   ├── prism-dark.css
│   └── prism-light.css
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (450 symbols across 66 files)

FILE: src/lib/legacy/local/Comparator.ts
  function stringify (line 100) | function stringify(value: string | number | boolean = null) {
  function isNull (line 107) | function isNull(entry: any) {

FILE: src/lib/legacy/local/Context.ts
  class Context (line 9) | class Context<Row>
    method constructor (line 29) | constructor(data: Row[], params: Params)
    method createFilterCount (line 50) | private createFilterCount()
    method createFilteredRows (line 55) | private createFilteredRows()
    method match (line 92) | private match(entry: Row[keyof Row], value: string|number|boolean|symb...
    method createPagedRows (line 110) | private createPagedRows()
    method createRowCount (line 126) | private createRowCount()
    method createPages (line 144) | private createPages()
    method createPagesWithEllipsis (line 155) | private createPagesWithEllipsis()
    method createPageCount (line 189) | private createPageCount()
    method createIsAllSelected (line 196) | private createIsAllSelected()

FILE: src/lib/legacy/local/DataHandler.ts
  type Params (line 15) | type Params = { rowsPerPage?: number, i18n?: Internationalization }
  class DataHandler (line 17) | class DataHandler<T extends Row = any>
    method constructor (line 27) | constructor(data: T[] = [], params: Params = { rowsPerPage: null })
    method setRows (line 38) | public setRows(data: T[])
    method getRows (line 45) | public getRows(): Readable<T[]>
    method getAllRows (line 50) | public getAllRows(): Readable<T[]>
    method getRowCount (line 55) | public getRowCount(): Readable<{ total: number, start: number, end: nu...
    method getRowsPerPage (line 60) | public getRowsPerPage(): Writable<number | null>
    method getPages (line 65) | public getPages(param = { ellipsis: false }): Readable<number[]>
    method getPageCount (line 73) | public getPageCount(): Readable<number>
    method getPageNumber (line 78) | public getPageNumber(): Readable<number>
    method setPage (line 83) | public setPage(value: number | 'previous' | 'next'): void
    method search (line 92) | public search(value: string, scope: Field<T>[] = null)
    method clearSearch (line 97) | public clearSearch()
    method sort (line 102) | public sort(orderBy: Field<T>, identifier?: string)
    method sortAsc (line 108) | public sortAsc(orderBy: Field<T>, identifier?: string)
    method sortDesc (line 114) | public sortDesc(orderBy: Field<T>, identifier?: string)
    method getSort (line 120) | public getSort(): Writable<{ identifier?: string, direction?: 'asc' | ...
    method applySort (line 125) | public applySort( params: { orderBy: Field<T>, direction?: 'asc' | 'de...
    method defineSort (line 130) | public defineSort(orderBy: Field<T>, direction?: 'asc' | 'desc')
    method clearSort (line 135) | public clearSort()
    method filter (line 140) | public filter( value: string | number | null | undefined | boolean | n...
    method getFilters (line 145) | public getFilters()
    method createFilter (line 150) | public createFilter( filterBy: Field<T>, comparator?: Comparator<T> )
    method createAdvancedFilter (line 155) | public createAdvancedFilter(filterBy: Field<T>)
    method getFilterCount (line 160) | public getFilterCount(): Readable<number>
    method clearFilters (line 165) | public clearFilters(): void
    method select (line 170) | public select(value: T | T[keyof T])
    method getSelected (line 175) | public getSelected()
    method selectAll (line 180) | public selectAll(params: { selectBy?: keyof T; scope?: 'all' | 'curren...
    method isAllSelected (line 186) | public isAllSelected(): Readable<boolean>
    method on (line 191) | public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: ...
    method createCalculation (line 196) | public createCalculation(field: Field<T>, param: { precision: number }...
    method translate (line 201) | public translate(i18n: Internationalization): Internationalization
    method update (line 225) | public update(data: any[]): void
    method applySorting (line 239) | public applySorting( params: { orderBy: Field<T>, direction?: 'asc' | ...
    method getSorted (line 248) | public getSorted()
    method getTriggerChange (line 254) | public getTriggerChange(): Writable<number>

FILE: src/lib/legacy/local/handlers/EventHandler.ts
  class EventHandler (line 4) | class EventHandler
    method add (line 14) | public add(event: keyof EventHandler['events'], callback: () => void)
    method trigger (line 19) | public trigger(event: keyof EventHandler['events'])

FILE: src/lib/legacy/local/handlers/FilterHandler.ts
  type Value (line 8) | type Value = string | number | null | undefined | boolean | number[] | C...
  class FilterHandler (line 17) | class FilterHandler<Row>
    method constructor (line 23) | constructor(context: Context<Row>)
    method set (line 29) | public set(value: Value, filterBy: Field<Row>, comparator: Comparator<...
    method clear (line 42) | public clear()
    method get (line 49) | public get()
    method createCollection (line 58) | private createCollection()

FILE: src/lib/legacy/local/handlers/PageHandler.ts
  class PageHandler (line 5) | class PageHandler<Row>
    method constructor (line 12) | constructor(context: Context<Row>)
    method goto (line 20) | public goto(number: number)
    method previous (line 35) | public previous()
    method next (line 41) | public next()

FILE: src/lib/legacy/local/handlers/SearchHandler.ts
  class SearchHandler (line 5) | class SearchHandler<Row>
    method constructor (line 10) | constructor(context: Context<Row>)
    method set (line 16) | public set(value: string, scope: Field<Row>[] = null)
    method clear (line 28) | public clear()

FILE: src/lib/legacy/local/handlers/SelectHandler.ts
  class SelectHandler (line 7) | class SelectHandler<Row>
    method constructor (line 16) | constructor(context: Context<Row>)
    method set (line 26) | public set(value: Row[keyof Row] | Row)
    method all (line 36) | public all(selectBy: keyof Row = null)
    method clear (line 56) | public clear()

FILE: src/lib/legacy/local/handlers/SortHandler.ts
  class SortHandler (line 6) | class SortHandler<Row>
    method constructor (line 13) | constructor(context: Context<Row>)
    method set (line 21) | public set(orderBy: Field<Row> = null, uid?: string)
    method asc (line 37) | public asc(orderBy: Field<Row>, uid?: string)
    method desc (line 60) | public desc(orderBy: Field<Row>, uid?: string)
    method apply (line 83) | public apply(params: { orderBy: Field<Row>, direction?: 'asc' | 'desc'...
    method clear (line 97) | public clear()
    method define (line 103) | public define(orderBy: Field<Row>, direction: 'asc' | 'desc' = 'asc')
    method restore (line 110) | private restore()
    method log (line 119) | private log(sort: Sort<Row>)

FILE: src/lib/legacy/local/helpers/AdvancedFilterHelper.ts
  type Value (line 6) | type Value = string | number | [min: number, max: number]
  class AdvancedFilterHandler (line 8) | class AdvancedFilterHandler<Row>
    method constructor (line 15) | constructor(filterHandler: FilterHandler<Row>, filterBy: Field<Row>)
    method set (line 23) | public set(value: Value, comparator: Comparator<any> = check.isLike)
    method getSelected (line 38) | public getSelected()
    method clear (line 43) | public clear()

FILE: src/lib/legacy/local/helpers/CalculationHelper.ts
  class CalcultationHandler (line 6) | class CalcultationHandler<Row>
    method constructor (line 13) | constructor(context: Context<Row>, field: Field<Row>, param: { precisi...
    method distinct (line 21) | public distinct(callback: (values: any[]) => any[] = null)
    method avg (line 35) | public avg(callback: (values: number[]) => any[] = null)
    method sum (line 45) | public sum(callback: (values: number[]) => any[] = null)
    method bounds (line 54) | public bounds(callback: (values: number[]) => any[] = null): [min: num...
    method setPrecision (line 67) | public setPrecision(value: number)
    method round (line 72) | private round(value: number)

FILE: src/lib/legacy/local/helpers/FilterHelper.ts
  type Value (line 5) | type Value = string | number | boolean
  class FilterHelper (line 7) | class FilterHelper<Row>
    method constructor (line 15) | constructor(filterHandler: FilterHandler<Row>, filterBy: Field<Row>, c...
    method set (line 24) | public set(value: Value, comparator?: Comparator<any>)
    method clear (line 32) | public clear()
    method on (line 38) | public on(event: 'clear', callback: () => void)

FILE: src/lib/legacy/local/index.ts
  type Internationalization (line 16) | type Internationalization = {
  type Row (line 27) | type Row = { [key: string]: any  }
  type Field (line 28) | type Field<Row> = keyof Row | ((row: Row) => Row[keyof Row])
  type Comparator (line 31) | type Comparator<Row> = (entry: Row[keyof Row], value: any) => boolean
  type Criterion (line 33) | type Criterion = { value: string | number | [min: number, max: number], ...
  type Filter (line 35) | type Filter<Row> = {
  type Sort (line 43) | type Sort<Row> = {
  type Selectable (line 58) | type Selectable<Row> = Row[keyof Row] | Row
  type FilterBy (line 65) | type FilterBy<Row> = Field<Row>
  type OrderBy (line 72) | type OrderBy<Row>  = Field<Row>
  type Order (line 79) | type Order<Row>  = Sort<Row>

FILE: src/lib/legacy/remote/Context.ts
  class Context (line 6) | class Context<Row>
    method constructor (line 26) | constructor(data: Row[], params: Params)
    method getState (line 46) | public getState(): State
    method createPages (line 64) | private createPages()
    method createPagesWithEllipsis (line 77) | private createPagesWithEllipsis()
    method createPageCount (line 114) | private createPageCount()
    method createRowCount (line 122) | private createRowCount()
    method createIsAllSelected (line 140) | private createIsAllSelected()
    method createSelectedCount (line 154) | private createSelectedCount()

FILE: src/lib/legacy/remote/DataHandler.ts
  type Params (line 12) | type Params = {
  class DataHandler (line 19) | class DataHandler<T extends Row = any>
    method constructor (line 30) | constructor(data: T[] = [], params: Params = { rowsPerPage: 5 })
    method onChange (line 42) | public onChange(callback: (state: State) => Promise<T[]>)
    method invalidate (line 47) | public invalidate()
    method setRows (line 52) | public setRows(data: T[])
    method setTotalRows (line 57) | public setTotalRows(value: number)
    method getRows (line 62) | public getRows(): Writable<T[]>
    method select (line 67) | public select(value: T[keyof T] | T)
    method getSelected (line 72) | public getSelected()
    method selectAll (line 77) | public selectAll(): void
    method isAllSelected (line 82) | public isAllSelected(): Readable<boolean>
    method getSelectedCount (line 87) | public getSelectedCount(): Readable<{ count: number, total: number }>
    method clearSelection (line 92) | public clearSelection()
    method getRowsPerPage (line 97) | public getRowsPerPage(): Writable<number | null>
    method sort (line 102) | public sort(orderBy: keyof T)
    method applySort (line 108) | public applySort( params: { orderBy:  keyof T, direction?: 'asc' | 'de...
    method sortAsc (line 113) | public sortAsc(orderBy: keyof T)
    method sortDesc (line 119) | public sortDesc(orderBy: keyof T)
    method getSort (line 125) | public getSort(): Writable<Sort<T>>
    method search (line 130) | public search(value: string): void
    method clearSearch (line 136) | public clearSearch()
    method filter (line 141) | public filter(value: string | number, filterBy: keyof T)
    method clearFilters (line 147) | public clearFilters(): void
    method getPages (line 152) | public getPages(params = { ellipsis: false }): Readable<number[]>
    method getPageCount (line 160) | public getPageCount(): Readable<number>
    method getPageNumber (line 165) | public getPageNumber(): Writable<number>
    method setPage (line 170) | public setPage(value: number | 'previous' | 'next'): void
    method getRowCount (line 179) | public getRowCount(): Readable<{ total: number, start: number, end: nu...
    method on (line 184) | public on(event: 'change', callback: () => void)
    method translate (line 189) | public translate(i18n: Internationalization): Internationalization
    method getTriggerChange (line 213) | public getTriggerChange(): Writable<number>
    method applySorting (line 222) | public applySorting( params: { orderBy:  keyof T, direction?: 'asc' | ...
    method getSorted (line 232) | public getSorted()

FILE: src/lib/legacy/remote/handlers/EventHandler.ts
  class EventHandler (line 4) | class EventHandler
    method add (line 14) | public add(event: keyof EventHandler['events'], callback: () => void)
    method trigger (line 19) | public trigger(event: keyof EventHandler['events'])

FILE: src/lib/legacy/remote/handlers/FilterHandler.ts
  class FilterHandler (line 5) | class FilterHandler<Row>
    method constructor (line 9) | constructor(context: Context<Row>)
    method set (line 14) | public set(value: string | number, filterBy: keyof Row )
    method remove (line 28) | public remove()

FILE: src/lib/legacy/remote/handlers/PageHandler.ts
  class PageHandler (line 5) | class PageHandler<Row>
    method constructor (line 15) | constructor(context: Context<Row>)
    method get (line 26) | public get()
    method goto (line 31) | public goto(number: number)
    method previous (line 54) | public previous()
    method next (line 60) | public next()

FILE: src/lib/legacy/remote/handlers/SearchHandler.ts
  class SearchHandler (line 4) | class SearchHandler<Row>
    method constructor (line 8) | constructor(context: Context<Row>) {
    method set (line 12) | public set(value: string)
    method remove (line 17) | public remove()

FILE: src/lib/legacy/remote/handlers/SelectHandler.ts
  class SelectHandler (line 6) | class SelectHandler<Row>
    method constructor (line 13) | constructor(context: Context<Row>)
    method set (line 21) | public set(value: Row | Row[keyof Row])
    method all (line 32) | public all()
    method clear (line 55) | public clear()

FILE: src/lib/legacy/remote/handlers/SortHandler.ts
  class SortHandler (line 6) | class SortHandler<Row>
    method constructor (line 12) | constructor(context: Context<Row>)
    method set (line 19) | public set(orderBy: keyof Row = null)
    method asc (line 35) | public asc(orderBy: keyof Row)
    method desc (line 42) | public desc(orderBy: keyof Row)
    method apply (line 49) | public apply(params: { orderBy: keyof Row, direction?: 'asc' | 'desc' ...

FILE: src/lib/legacy/remote/handlers/TriggerHandler.ts
  class TriggerHandler (line 5) | class TriggerHandler<Row>
    method constructor (line 10) | constructor(context: Context<Row>)
    method set (line 15) | public set(callback: (state: State) => Promise<Row[]>)
    method invalidate (line 20) | public async invalidate()

FILE: src/lib/legacy/remote/index.ts
  type Internationalization (line 14) | type Internationalization = {
  type Row (line 26) | type Row = { [key: string]: unknown }
  type Filter (line 28) | type Filter<Row> = {
  type Sort (line 33) | type Sort<Row> = {
  type State (line 39) | type State = {
  type Selectable (line 60) | type Selectable<Row> = Row[keyof Row] | Row
  type Order (line 69) | type Order<Row>  = Sort<Row>

FILE: src/lib/src/client/AbstractTableHandler.svelte.ts
  method constructor (line 32) | constructor(data: Row[], params: TableParams<Row>)
  method createAllRows (line 40) | private createAllRows()
  method createRows (line 62) | private createRows()
  method createRowCount (line 71) | private createRowCount()
  method createPages (line 85) | private createPages()
  method createPagesWithEllipsis (line 94) | private createPagesWithEllipsis()
  method createIsAllSelected (line 125) | private createIsAllSelected()

FILE: src/lib/src/client/TableHandler.svelte.ts
  class TableHandler (line 24) | class TableHandler<T extends Row = any> extends AbstractTableHandler<T> ...
    method constructor (line 35) | constructor(data: T[] = [], params: TableParams<T> = { rowsPerPage: nu...
    method setRows (line 47) | public setRows(data: T[]): void
    method setRowsPerPage (line 60) | public setRowsPerPage(value: number): void
    method setPage (line 66) | public setPage(value: number | 'previous' | 'next' | 'last'): void
    method createSearch (line 76) | public createSearch(scope?: Field<T>[]): SearchBuilder<T>
    method clearSearch (line 81) | public clearSearch(): void
    method createRecordFilter (line 88) | public createRecordFilter(records?: Row[]): RecordFilterBuilder
    method createSort (line 93) | public createSort(field: Field<T>, params?: { locales: Intl.LocalesArg...
    method clearSort (line 98) | public clearSort()
    method clearFilters (line 103) | public clearFilters(): void
    method createAdvancedFilter (line 110) | public createAdvancedFilter(field: Field<T>, check?: Check): AdvancedF...
    method createFilter (line 115) | public createFilter(field: Field<T>, check?: Check): FilterBuilder<T>
    method createQuery (line 120) | public createQuery(): QueryBuilder<T>
    method select (line 125) | public select(value: unknown): void
    method selectAll (line 130) | public selectAll(params: { scope?: 'all' | 'currentPage' } = {}): void
    method getSelectedRows (line 136) | public getSelectedRows(): T[]
    method clearSelection (line 141) | public clearSelection(): void
    method on (line 146) | public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: ...
    method createCalculation (line 151) | public createCalculation(field: Field<T>): CalculationBuilder<T>
    method createCSV (line 156) | public createCSV(): CSVBuilder<T>
    method createView (line 161) | public createView(columns: ColumnView[]): ViewBuilder<T>
    method getView (line 167) | public getView(): ViewBuilder<T>
    method translate (line 172) | private translate(i18n: Internationalization): void

FILE: src/lib/src/client/builders/AdvancedFilterBuilder.svelte.ts
  class AdvancedFilterBuilder (line 5) | class AdvancedFilterBuilder<Row>
    method constructor (line 15) | constructor(filterHandler: FilterHandler<Row>, field: Field<Row>, chec...
    method set (line 24) | public set(value: unknown, check?: Check): void
    method isNotRecursive (line 39) | public isNotRecursive()
    method clear (line 45) | public clear(): void
    method cleanup (line 52) | private cleanup()

FILE: src/lib/src/client/builders/CSVBuilder.svelte.ts
  class CSVBuilder (line 3) | class CSVBuilder<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method download (line 12) | public download(filename: string)
    method get (line 26) | public get(): string
    method getRows (line 33) | private getRows()
    method getHeader (line 47) | private getHeader()

FILE: src/lib/src/client/builders/CalculationBuilder.svelte.ts
  type Sort (line 4) | type Sort = [key: 'value' | 'count', direction: 'asc' | 'desc']
  class CalcultationBuilder (line 6) | class CalcultationBuilder<Row>
    method constructor (line 12) | constructor(table: TableHandler<Row>, field: Field<Row>)
    method distinct (line 18) | public distinct(param?: { sort: Sort }): { value: string, count: numbe...
    method avg (line 43) | public avg(param?: { precision: number }): number
    method sum (line 51) | public sum(param?: { precision: number }): number
    method median (line 58) | public median(param?: { precision: number }): number
    method bounds (line 72) | public bounds(): number[]
    method round (line 84) | private round(value: number)

FILE: src/lib/src/client/builders/FilterBuilder.svelte.ts
  class FilterBuilder (line 6) | class FilterBuilder<Row> implements FilterInterface
    method constructor (line 15) | constructor(filterHandler: FilterHandler<Row>, field: Field<Row>, chec...
    method set (line 23) | public set(check?: Check)
    method init (line 28) | public init(value?: unknown)
    method isNotRecursive (line 35) | public isNotRecursive()
    method clear (line 41) | public clear()
    method cleanup (line 47) | private cleanup()

FILE: src/lib/src/client/builders/QueryBuilder.svelte.ts
  class QueryBuilder (line 4) | class QueryBuilder<Row>
    method constructor (line 12) | constructor(queryHandler: QueryHandler<Row>)
    method from (line 18) | public from(path: string[])
    method where (line 24) | public where(filter: (row: any, value?: unknown) => boolean)
    method set (line 32) | public set(value?: unknown)
    method clear (line 38) | public clear()
    method cleanup (line 44) | private cleanup()

FILE: src/lib/src/client/builders/RecordFilterBuilder.svelte.ts
  class RecordFilterBuilder (line 4) | class RecordFilterBuilder
    method constructor (line 11) | constructor(records: Row[])
    method set (line 16) | public set()
    method createRecords (line 21) | private createRecords(): readonly Row[]

FILE: src/lib/src/client/builders/SearchBuilder.svelte.ts
  class SearchBuilder (line 5) | class SearchBuilder<Row> implements SearchInterface
    method constructor (line 11) | constructor(searchHandler: SearchHandler<Row>, scope?: Field<Row>[])
    method set (line 18) | public set()
    method init (line 23) | public init(value?: string)
    method recursive (line 30) | public recursive()
    method regex (line 35) | public regex()
    method clear (line 40) | public clear()
    method cleanup (line 46) | private cleanup()

FILE: src/lib/src/client/builders/SortBuilder.svelte.ts
  class SortBuilder (line 6) | class SortBuilder<Row> implements SortInterface
    method constructor (line 15) | constructor(sortHandler: SortHandler<Row>, field: Field<Row>, params: ...
    method set (line 22) | public set()
    method init (line 27) | public init(direction?: 'asc' | 'desc')
    method asc (line 34) | public asc()
    method desc (line 39) | public desc()
    method clear (line 44) | public clear()
    method createIsActive (line 49) | private createIsActive()
    method createDirection (line 57) | private createDirection()

FILE: src/lib/src/client/core/entry.ts
  type Params (line 5) | type Params = {

FILE: src/lib/src/client/core/index.ts
  type Search (line 10) | type Search<Row> = {
  type Filter (line 17) | type Filter<Row> = {
  type Query (line 26) | type Query<Row> = {
  type Sort (line 33) | type Sort<Row> = {

FILE: src/lib/src/client/core/rows.ts
  function recursive (line 54) | function recursive(i) {

FILE: src/lib/src/client/handlers/FilterHandler.svelte.ts
  class FilterHandler (line 5) | class FilterHandler<Row>
    method constructor (line 9) | constructor(table: TableHandler<Row>)
    method set (line 14) | public set(value: unknown, field: Field<Row>, check: Check = null, id:...
    method unset (line 27) | public unset(id: string)

FILE: src/lib/src/client/handlers/PageHandler.svelte.ts
  class PageHandler (line 3) | class PageHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method goto (line 12) | public goto(page: number)
    method previous (line 22) | public previous()
    method next (line 27) | public next()
    method last (line 32) | public last()

FILE: src/lib/src/client/handlers/QueryHandler.svelte.ts
  class QueryHandler (line 5) | class QueryHandler<Row>
    method constructor (line 9) | constructor(table: TableHandler<Row>)
    method set (line 14) | public set(path: string[], value: unknown, check: Check, id: string)
    method unset (line 21) | public unset(id: string)

FILE: src/lib/src/client/handlers/SearchHandler.svelte.ts
  class SearchHandler (line 3) | class SearchHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method set (line 12) | public set(value: string, scope?: Field<Row>[])
    method recursive (line 18) | public recursive(value: string, scope?: Field<Row>[])
    method regex (line 24) | public regex(pattern: string, scope?: Field<Row>[])
    method clear (line 30) | public clear()

FILE: src/lib/src/client/handlers/SelectHandler.svelte.ts
  class SelectHandler (line 4) | class SelectHandler<Row>
    method constructor (line 8) | constructor(table: TableHandler<Row>)
    method set (line 13) | public set(value: unknown)
    method all (line 22) | public all(scope: 'all' | 'currentPage')
    method clear (line 40) | public clear()
    method getRows (line 45) | public getRows()

FILE: src/lib/src/client/handlers/SortHandler.svelte.ts
  class SortHandler (line 4) | class SortHandler<Row>
    method constructor (line 9) | constructor(table: TableHandler<Row>)
    method set (line 15) | public set(field: Field<Row>, id: string, params: SortParams = {})
    method asc (line 28) | public asc(field: Field<Row>, id: string, { locales, options }: SortPa...
    method desc (line 41) | public desc(field: Field<Row>, id: string, { locales, options }: SortP...
    method clear (line 54) | public clear()
    method restore (line 60) | public restore()
    method save (line 68) | private save(sort: Sort<Row>)

FILE: src/lib/src/client/index.ts
  type SortParams (line 30) | type SortParams = {
  type Check (line 35) | type Check = (entry: unknown, value: unknown) => boolean
  type TableParams (line 37) | type TableParams<Row> = {
  type Criterion (line 44) | type Criterion = {

FILE: src/lib/src/server/AbstractTableHandler.svelte.ts
  class AbstractTableHandler (line 4) | class AbstractTableHandler<Row>
    method constructor (line 28) | constructor(data: Row[], params: TableParams<Row>)
    method getState (line 37) | public getState(): State<Row>
    method createPages (line 50) | private createPages()
    method createPageCount (line 59) | private createPageCount()
    method createPagesWithEllipsis (line 65) | private createPagesWithEllipsis()
    method createRowCount (line 99) | private createRowCount()
    method createIsAllSelected (line 117) | private createIsAllSelected()

FILE: src/lib/src/server/TableHandler.svelte.ts
  class TableHandler (line 20) | class TableHandler<T extends Row = any> extends AbstractTableHandler<T> ...
    method constructor (line 31) | constructor(data: T[] = [], params: TableParams<T> = { rowsPerPage: 5 })
    method load (line 43) | public load(callback: (state: State<T>) => Promise<T[]>): void
    method invalidate (line 48) | public invalidate(): void
    method setRowsPerPage (line 53) | public setRowsPerPage(value: number)
    method setPage (line 59) | public setPage(value: number | 'previous' | 'next' | 'last'): void
    method clearSearch (line 69) | public clearSearch(): void
    method createSearch (line 74) | public createSearch(): SearchBuilder<T>
    method createSort (line 79) | public createSort(field: Field<T>): SortBuilder<T>
    method clearFilters (line 87) | public clearFilters(): void
    method createFilter (line 93) | public createFilter(field: Field<T>): FilterBuilder<T>
    method select (line 101) | public select(value: T[keyof T])
    method selectAll (line 106) | public selectAll(): void
    method clearSelection (line 111) | public clearSelection(): void
    method on (line 116) | public on(event: 'change' | 'clearFilters' | 'clearSearch', callback: ...
    method createView (line 121) | public createView(columns: ColumnView[]): ViewBuilder<T>
    method getView (line 127) | public getView(): ViewBuilder<T>
    method translate (line 132) | private translate(i18n: Internationalization): Internationalization

FILE: src/lib/src/server/builders/FilterBuilder.svelte.ts
  class FilterBuilder (line 3) | class FilterBuilder<Row>
    method constructor (line 10) | constructor(filterHandler: FilterHandler<Row>, field: keyof Row)
    method set (line 17) | public set()
    method init (line 26) | public init(value?: string)
    method clear (line 34) | public clear()
    method cleanup (line 40) | private cleanup()

FILE: src/lib/src/server/builders/SearchBuilder.svelte.ts
  class SearchBuilder (line 4) | class SearchBuilder<Row> implements SearchInterface
    method constructor (line 10) | constructor(table: TableHandler<Row>)
    method set (line 16) | public set()
    method init (line 25) | public init(value?: string)
    method clear (line 33) | public clear()
    method cleanup (line 41) | private cleanup()

FILE: src/lib/src/server/builders/SortBuilder.svelte.ts
  class SortBuilder (line 4) | class SortBuilder<Row> implements SortInterface
    method constructor (line 11) | constructor(sortHandler: SortHandler<Row>, field: keyof Row)
    method set (line 17) | public set()
    method init (line 22) | public init(direction?: 'asc' | 'desc')
    method asc (line 29) | public asc()
    method desc (line 34) | public desc()
    method createIsActive (line 39) | private createIsActive()
    method createDirection (line 47) | private createDirection()

FILE: src/lib/src/server/handlers/FetchHandler.svelte.ts
  class FetchHandler (line 4) | class FetchHandler<Row>
    method constructor (line 10) | constructor(table: TableHandler<Row>)
    method set (line 15) | public set(callback: (state: State) => Promise<Row[]>)
    method invalidate (line 20) | public async invalidate()
    method trigger (line 27) | private async trigger()

FILE: src/lib/src/server/handlers/FilterHandler.svelte.ts
  class FilterHandler (line 3) | class FilterHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method set (line 12) | public set(value: string | number, field: keyof Row)
    method unset (line 20) | public unset(field: keyof Row)
    method clear (line 25) | public clear()

FILE: src/lib/src/server/handlers/PageHandler.svelte.ts
  class PageHandler (line 3) | class PageHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method goto (line 12) | public goto(number: number)
    method previous (line 30) | public previous()
    method next (line 35) | public next()

FILE: src/lib/src/server/handlers/SearchHandler.svelte.ts
  class SearchHandler (line 3) | class SearchHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>) {
    method clear (line 11) | public clear()

FILE: src/lib/src/server/handlers/SelectHandler.svelte.ts
  class SelectHandler (line 3) | class SelectHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method set (line 12) | public set(value: Row[keyof Row])
    method all (line 22) | public all()
    method clear (line 33) | public clear()

FILE: src/lib/src/server/handlers/SortHandler.svelte.ts
  class SortHandler (line 3) | class SortHandler<Row>
    method constructor (line 7) | constructor(table: TableHandler<Row>)
    method set (line 12) | public set(field: keyof Row)
    method asc (line 27) | public asc(field: keyof Row)
    method desc (line 33) | public desc(field: keyof Row)

FILE: src/lib/src/server/index.ts
  type State (line 19) | type State<T extends Row = any> = {
  type TableParams (line 29) | type TableParams<Row> = {
  type Filter (line 37) | type Filter<Row> = {
  type Sort (line 42) | type Sort<Row> = {

FILE: src/lib/src/shared/EventDispatcher.ts
  class EventDispatcher (line 2) | class EventDispatcher
    method add (line 11) | public add(event: keyof EventDispatcher['listeners'], callback: () => ...
    method dispatch (line 16) | public async dispatch(event: keyof EventDispatcher['listeners'])
    method run (line 26) | private run()

FILE: src/lib/src/shared/builders/HighlightBuilder.svelte.ts
  class HighlightBuilder (line 5) | class HighlightBuilder<Row>
    method constructor (line 12) | constructor(table: TableHandlerInterface<Row>, selector: string = 'tbo...
    method createHighlight (line 19) | private createHighlight()
    method createKeywords (line 32) | private createKeywords(): string[]
    method emphasize (line 40) | private emphasize(node: Node)

FILE: src/lib/src/shared/builders/ViewBuilder.svelte.ts
  class ViewBuilder (line 3) | class ViewBuilder<Row>
    method constructor (line 10) | constructor(table: TableHandlerInterface<Row>, columns: ColumnView[])
    method toggle (line 17) | public toggle(name: string)
    method createColumns (line 26) | private createColumns(columns: ColumnView[])
    method preset (line 57) | private preset()
    method freeze (line 72) | private freeze(index: number, left = 0)
    method setPosition (line 85) | public setPosition(current: number, destination: number)

FILE: src/lib/src/shared/index.ts
  type Row (line 12) | type Row = { [key: string | number | symbol]: any }
  type Field (line 14) | type Field<Row> = keyof Row | ((row: Row) => unknown)
  type TableHandlerInterface (line 16) | interface TableHandlerInterface<Row> {
  type Internationalization (line 34) | type Internationalization = {
  type ColumnView (line 45) | type ColumnView = {
  type SearchInterface (line 53) | interface SearchInterface {
  type FilterInterface (line 59) | interface FilterInterface {
  type SortInterface (line 65) | interface SortInterface {

FILE: src/routes/examples/client/crud/api.svelte.ts
  class API (line 3) | class API
    method create (line 7) | public create(user)
    method destroy (line 13) | public destroy(user)
    method update (line 18) | public update(user)

FILE: src/routes/examples/server/hello-world/api.ts
  type Row (line 3) | type Row = { id: string, name: string, email: string, body: string }

FILE: src/routes/examples/server/pokedex-api/api.ts
  type PokemonStat (line 2) | type PokemonStat = {

FILE: src/routes/examples/server/ssr-user/params.svelte.ts
  class SSRFilters (line 5) | class SSRFilters {
    method constructor (line 8) | constructor() {
    method get (line 12) | get(name: string) {
    method update (line 16) | update(filters: Record<string, string>) {
    method fromState (line 31) | fromState(s: State) {
    method clear (line 60) | clear(...params: string[]) {
    method isFiltered (line 69) | isFiltered(...params: string[]) {

FILE: src/site/Site.svelte.ts
  class Site (line 5) | class Site
    method setMode (line 10) | public async setMode(value: 'client' | 'server' | string)
    method getMode (line 18) | public getMode()

FILE: src/site/index.ts
  type Code (line 32) | type Code = {

FILE: src/site/utils/viewport.ts
  function ensureIntersectionObserver (line 3) | function ensureIntersectionObserver() {
  function viewport (line 19) | function viewport(element) {
Condensed preview — 640 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,311K chars).
[
  {
    "path": ".gitignore",
    "chars": 154,
    "preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/.vscode\n/package\n/build\n.env\n.env.*\n/dist\n!.env.example\nvite.config.js.times"
  },
  {
    "path": ".npmrc",
    "chars": 19,
    "preview": "engine-strict=true\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6597,
    "preview": "# 2.8.0 - 2025-12-17\n### Added\n[client] Disable filter recursion [#187](https://github.com/vincjo/datatables/issues/187)"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 255,
    "preview": "# Contributing\nContributions are welcome.\n\n1. Fork the project\n2. ⚠️ Create your feature branch `git checkout -b myfeatu"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2020 vincjo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "README.md",
    "chars": 1692,
    "preview": "<div align=\"center\">\n    <img align=\"center\" src=\"./static/logo.svg\" alt=\"logo\" width=\"172\"/>\n    <p align=\"center\">\n   "
  },
  {
    "path": "datatables.code-workspace",
    "chars": 573,
    "preview": "{\n\t\"folders\": [\n\t\t{ \"path\": \"./\" },\n\t\t{ \"path\": \"../autodoc\"}\n\t],\n\t\"settings\": {\n\t\t\"restoreTerminals.terminals\": [\n\t\t\t {"
  },
  {
    "path": "ecosystem.config.cjs",
    "chars": 689,
    "preview": "module.exports = {\n    apps: [\n        {\n            name: 'datatables',\n            script: './build/index.js',\n       "
  },
  {
    "path": "mdsvex.config.js",
    "chars": 265,
    "preview": "import { defineMDSveXConfig as defineConfig } from 'mdsvex'\n\nconst config = defineConfig({\n    extensions: ['.svelte.md'"
  },
  {
    "path": "package.json",
    "chars": 2848,
    "preview": "{\n    \"name\": \"@vincjo/datatables\",\n    \"version\": \"2.8.0\",\n    \"keywords\": [\n        \"svelte sveltejs table tables data"
  },
  {
    "path": "src/app.d.ts",
    "chars": 304,
    "preview": "/// <reference types=\"@sveltejs/kit\" />\n\n// See https://kit.svelte.dev/docs/types#app\n// for information about these int"
  },
  {
    "path": "src/app.html",
    "chars": 822,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\" data-mode=\"\">\n    <head>\n        <meta charset=\"utf-8\" />\n        <link re"
  },
  {
    "path": "src/hooks.server.ts",
    "chars": 698,
    "preview": "import type { RequestEvent } from '@sveltejs/kit'\n\n\nexport const handle = async ({ event, resolve }) => {\n    const them"
  },
  {
    "path": "src/lib/index.ts",
    "chars": 596,
    "preview": "\nexport {\n    // class:\n    TableHandler,\n    RecordFilter,\n    // components:\n    Datatable,\n    Search,\n    RowsPerPag"
  },
  {
    "path": "src/lib/legacy/index.ts",
    "chars": 889,
    "preview": "// Reexport your entry components here\nimport DataHandler  from './local/DataHandler'\nimport Datatable    from './local/"
  },
  {
    "path": "src/lib/legacy/local/Comparator.ts",
    "chars": 2857,
    "preview": "import type { Criterion } from '$lib/legacy/local'\n\nexport const check = {\n\n    isLike: (entry: any, value: any) => {\n  "
  },
  {
    "path": "src/lib/legacy/local/Context.ts",
    "chars": 7743,
    "preview": "import { writable, derived, type Writable, type Readable } from 'svelte/store'\nimport type { Filter, Sort, Comparator, C"
  },
  {
    "path": "src/lib/legacy/local/DataHandler.ts",
    "chars": 7109,
    "preview": "import Context                  from './Context'\nimport SortHandler              from './handlers/SortHandler'\nimport Se"
  },
  {
    "path": "src/lib/legacy/local/Datatable.svelte",
    "chars": 2310,
    "preview": "<script lang=\"ts\">\n    import { type DataHandler, type Row, Search, RowsPerPage, RowCount, Pagination } from '$lib/legac"
  },
  {
    "path": "src/lib/legacy/local/Pagination.svelte",
    "chars": 2998,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/local'\n\n    type T = $$Generic<Row>\n\n    expor"
  },
  {
    "path": "src/lib/legacy/local/RowCount.svelte",
    "chars": 919,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/local'\n\n    type T = $$Generic<Row>\n\n    expor"
  },
  {
    "path": "src/lib/legacy/local/RowsPerPage.svelte",
    "chars": 902,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/local'\n\n    type T = $$Generic<Row>\n\n    expor"
  },
  {
    "path": "src/lib/legacy/local/Search.svelte",
    "chars": 880,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/local'\n\n    type T = $$Generic<Row>\n\n    expor"
  },
  {
    "path": "src/lib/legacy/local/Th.svelte",
    "chars": 2051,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Field, Row } from '$lib/legacy/local'\n\n    type T = $$Generic<Row>\n\n  "
  },
  {
    "path": "src/lib/legacy/local/ThFilter.svelte",
    "chars": 1206,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Field, Comparator, Row } from '$lib/legacy/local'\n\n    type T = $$Gene"
  },
  {
    "path": "src/lib/legacy/local/handlers/EventHandler.ts",
    "chars": 746,
    "preview": "import { writable } from 'svelte/store'\n\n\nexport default class EventHandler\n{\n    private events = {\n        change     "
  },
  {
    "path": "src/lib/legacy/local/handlers/FilterHandler.ts",
    "chars": 2528,
    "preview": "import type { Filter, Field, Comparator, EventHandler, Criterion } from '$lib/legacy/local'\nimport { isNotNull } from '."
  },
  {
    "path": "src/lib/legacy/local/handlers/PageHandler.ts",
    "chars": 1347,
    "preview": "import type Context from '$lib/legacy/local/Context'\nimport { type Writable, type Readable, get } from 'svelte/store'\nim"
  },
  {
    "path": "src/lib/legacy/local/handlers/SearchHandler.ts",
    "chars": 862,
    "preview": "import type Context from '$lib/legacy/local/Context'\nimport type { Writable } from 'svelte/store'\nimport type { EventHan"
  },
  {
    "path": "src/lib/legacy/local/handlers/SelectHandler.ts",
    "chars": 1798,
    "preview": "import type Context from '$lib/legacy/local/Context'\nimport { type Writable, type Readable, get } from 'svelte/store'\nim"
  },
  {
    "path": "src/lib/legacy/local/handlers/SortHandler.ts",
    "chars": 4535,
    "preview": "import type Context from '$lib/legacy/local/Context'\nimport type { Sort, Field, EventHandler } from '$lib/legacy/local'\n"
  },
  {
    "path": "src/lib/legacy/local/helpers/AdvancedFilterHelper.ts",
    "chars": 1608,
    "preview": "import type { Field, Comparator, Criterion } from '$lib/legacy/local'\nimport type FilterHandler from '$lib/legacy/local/"
  },
  {
    "path": "src/lib/legacy/local/helpers/CalculationHelper.ts",
    "chars": 2779,
    "preview": "import type { Field } from '$lib/legacy/local'\nimport type Context from '$lib/legacy/local/Context'\nimport { type Writab"
  },
  {
    "path": "src/lib/legacy/local/helpers/FilterHelper.ts",
    "chars": 1285,
    "preview": "import type { Field, Comparator } from '$lib/legacy/local'\nimport { check } from '$lib/legacy/local/Comparator'\nimport t"
  },
  {
    "path": "src/lib/legacy/local/index.ts",
    "chars": 2067,
    "preview": "// Reexport your entry components here\nimport DataHandler  from './DataHandler'\nimport Datatable    from './Datatable.sv"
  },
  {
    "path": "src/lib/legacy/local/utils.ts",
    "chars": 762,
    "preview": "import type { Row, Field } from '$lib/legacy/local'\n\nexport const isNull = (value: any) => {\n    if (value === null || v"
  },
  {
    "path": "src/lib/legacy/remote/Context.ts",
    "chars": 5822,
    "preview": "import { type Writable, writable, get, derived, type Readable } from 'svelte/store'\nimport type { State, Sort, Filter } "
  },
  {
    "path": "src/lib/legacy/remote/DataHandler.ts",
    "chars": 5783,
    "preview": "import Context          from './Context'\nimport TriggerHandler   from './handlers/TriggerHandler'\nimport SortHandler    "
  },
  {
    "path": "src/lib/legacy/remote/Datatable.svelte",
    "chars": 2500,
    "preview": "<script lang=\"ts\">\n    import { type DataHandler, type Row, Search, RowsPerPage, RowCount, SelectedCount, Pagination } f"
  },
  {
    "path": "src/lib/legacy/remote/Pagination.svelte",
    "chars": 4055,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/README.md",
    "chars": 651,
    "preview": "<div align=\"center\">\n    <img align=\"center\" src=\"../../../static/logo-remote.svg\" alt=\"logo\" width=\"172\"/>\n    <p align"
  },
  {
    "path": "src/lib/legacy/remote/RowCount.svelte",
    "chars": 1055,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/RowsPerPage.svelte",
    "chars": 986,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/Search.svelte",
    "chars": 846,
    "preview": "<script lang=\"ts\">\n\timport type { DataHandler, Row } from '$lib/legacy/remote'\n\n\ttype T = $$Generic<Row>\n\n\texport let ha"
  },
  {
    "path": "src/lib/legacy/remote/SelectedCount.svelte",
    "chars": 809,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/Th.svelte",
    "chars": 2052,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/ThFilter.svelte",
    "chars": 1235,
    "preview": "<script lang=\"ts\">\n    import type { DataHandler, Row } from '$lib/legacy/remote'\n\n    type T = $$Generic<Row>\n\n    expo"
  },
  {
    "path": "src/lib/legacy/remote/handlers/EventHandler.ts",
    "chars": 746,
    "preview": "import { writable } from 'svelte/store'\n\n\nexport default class EventHandler\n{\n    private events = {\n        change     "
  },
  {
    "path": "src/lib/legacy/remote/handlers/FilterHandler.ts",
    "chars": 785,
    "preview": "import type { Filter } from '$lib/legacy/remote'\nimport type Context from '$lib/legacy/remote/Context'\nimport type { Wri"
  },
  {
    "path": "src/lib/legacy/remote/handlers/PageHandler.ts",
    "chars": 1947,
    "preview": "import type Context from '$lib/legacy/remote/Context'\nimport { type Writable, type Readable, get } from 'svelte/store'\ni"
  },
  {
    "path": "src/lib/legacy/remote/handlers/SearchHandler.ts",
    "chars": 412,
    "preview": "import type Context from '$lib/legacy/remote/Context'\nimport type { Writable } from 'svelte/store'\n\nexport default class"
  },
  {
    "path": "src/lib/legacy/remote/handlers/SelectHandler.ts",
    "chars": 1771,
    "preview": "import type Context from '$lib/legacy/remote/Context'\nimport { type Writable, type Readable, get } from 'svelte/store'\n\n"
  },
  {
    "path": "src/lib/legacy/remote/handlers/SortHandler.ts",
    "chars": 3947,
    "preview": "import type Context from '$lib/legacy/remote/Context'\nimport type { Order } from '$lib/legacy/remote'\nimport { type Writ"
  },
  {
    "path": "src/lib/legacy/remote/handlers/TriggerHandler.ts",
    "chars": 661,
    "preview": "import type { State } from '$lib/legacy/remote'\nimport type Context from '$lib/legacy/remote/Context'\n\n\nexport default c"
  },
  {
    "path": "src/lib/legacy/remote/index.ts",
    "chars": 1808,
    "preview": "// Reexport your entry components here\nimport DataHandler      from './DataHandler'\nimport Datatable        from './Data"
  },
  {
    "path": "src/lib/server/index.ts",
    "chars": 343,
    "preview": "export {\n    // class:\n    TableHandler,\n    // components:\n    Datatable,\n    Search,\n    RowsPerPage,\n    Th,\n    ThSo"
  },
  {
    "path": "src/lib/src/client/AbstractTableHandler.svelte.ts",
    "chars": 5033,
    "preview": "import type { Field, TableParams } from '$lib/src/client'\nimport { EventDispatcher }  from '$lib/src/shared'\nimport { ty"
  },
  {
    "path": "src/lib/src/client/TableHandler.svelte.ts",
    "chars": 5964,
    "preview": "import { untrack }              from 'svelte'\nimport AbstractTableHandler     from './AbstractTableHandler.svelte'\n\nimpo"
  },
  {
    "path": "src/lib/src/client/builders/AdvancedFilterBuilder.svelte.ts",
    "chars": 1868,
    "preview": "import type { Field, Check, Criterion } from '$lib/src/client'\nimport type FilterHandler               from '$lib/src/cl"
  },
  {
    "path": "src/lib/src/client/builders/CSVBuilder.svelte.ts",
    "chars": 1371,
    "preview": "import type { TableHandler } from '$lib/src/client'\n\nexport default class CSVBuilder<Row>\n{\n    private table: TableHand"
  },
  {
    "path": "src/lib/src/client/builders/CalculationBuilder.svelte.ts",
    "chars": 3081,
    "preview": "import type { Field, TableHandler } from '$lib/src/client'\nimport { sort, parse }              from '$lib/src/client/cor"
  },
  {
    "path": "src/lib/src/client/builders/FilterBuilder.svelte.ts",
    "chars": 1417,
    "preview": "import type { Field, Check }    from '$lib/src/client'\nimport type FilterHandler       from '../handlers/FilterHandler.s"
  },
  {
    "path": "src/lib/src/client/builders/QueryBuilder.svelte.ts",
    "chars": 1125,
    "preview": "import type { Check }    from '$lib/src/client'\nimport type QueryHandler        from '../handlers/QueryHandler.svelte'\n\n"
  },
  {
    "path": "src/lib/src/client/builders/RecordFilterBuilder.svelte.ts",
    "chars": 753,
    "preview": "import { match, check, isNotNull } from '$lib/src/client/core'\nimport type { Row }         from '$lib/src/client'\n\nexpor"
  },
  {
    "path": "src/lib/src/client/builders/SearchBuilder.svelte.ts",
    "chars": 1158,
    "preview": "import { type Field }       from '$lib/src/client'\nimport type SearchHandler   from '../handlers/SearchHandler.svelte'\ni"
  },
  {
    "path": "src/lib/src/client/builders/SortBuilder.svelte.ts",
    "chars": 1633,
    "preview": "import type { Field, SortParams }   from '$lib/src/client'\nimport type SortHandler             from '../handlers/SortHan"
  },
  {
    "path": "src/lib/src/client/core/check.ts",
    "chars": 3149,
    "preview": "import { isNull, stringify, isNotNull } from './value'\nimport type { Criterion, Check } from '$lib/src/client'\n\n\nexport "
  },
  {
    "path": "src/lib/src/client/core/entry.ts",
    "chars": 3155,
    "preview": "import { isNull, isObject, isObjectArray } from './value'\nimport { check } from './check'\nimport type { Criterion, Check"
  },
  {
    "path": "src/lib/src/client/core/field.ts",
    "chars": 578,
    "preview": "import type { Field } from '$lib/src/client'\n\nexport const parse = <Row>(field: Field<Row>, id?: string) => {\n    if (ty"
  },
  {
    "path": "src/lib/src/client/core/index.ts",
    "chars": 880,
    "preview": "import type { Field, Check } from '$lib/src/client'\n\n\nexport { isNull, isNotNull, stringify } from './value'\nexport { ma"
  },
  {
    "path": "src/lib/src/client/core/rows.ts",
    "chars": 5289,
    "preview": "import type { Field } from '$lib/src/client'\nimport { type Search, type Filter, type Query } from '$lib/src/client/core'"
  },
  {
    "path": "src/lib/src/client/core/value.ts",
    "chars": 895,
    "preview": "\n\nexport const isNull = (value: unknown) => {\n    if (value === null || value === undefined || value === '') return true"
  },
  {
    "path": "src/lib/src/client/handlers/FilterHandler.svelte.ts",
    "chars": 904,
    "preview": "import type { Field, Check, TableHandler }  from '$lib/src/client'\nimport { isNotNull, parse }                 from '$li"
  },
  {
    "path": "src/lib/src/client/handlers/PageHandler.svelte.ts",
    "chars": 710,
    "preview": "import type { TableHandler } from '$lib/src/client'\n\nexport default class PageHandler<Row>\n{\n    private table: TableHan"
  },
  {
    "path": "src/lib/src/client/handlers/QueryHandler.svelte.ts",
    "chars": 690,
    "preview": "import type { Field, Check, TableHandler }  from '$lib/src/client'\nimport { isNotNull } from '$lib/src/client/core'\n\n\nex"
  },
  {
    "path": "src/lib/src/client/handlers/SearchHandler.svelte.ts",
    "chars": 880,
    "preview": "import { type TableHandler, type Field, type Criterion, check } from '$lib/src/client'\n\nexport default class SearchHandl"
  },
  {
    "path": "src/lib/src/client/handlers/SelectHandler.svelte.ts",
    "chars": 1561,
    "preview": "import type { TableHandler }    from '$lib/src/client'\nimport { parse }                from '$lib/src/client/core'\n\nexpo"
  },
  {
    "path": "src/lib/src/client/handlers/SortHandler.svelte.ts",
    "chars": 2584,
    "preview": "import type { Field, TableHandler, SortParams } from '$lib/src/client'\nimport { type Sort, parse, sort } from '$lib/src/"
  },
  {
    "path": "src/lib/src/client/index.ts",
    "chars": 1554,
    "preview": "export { default as TableHandler } from './TableHandler.svelte'\nexport { check } from './core'\n\nimport type { Internatio"
  },
  {
    "path": "src/lib/src/server/AbstractTableHandler.svelte.ts",
    "chars": 4353,
    "preview": "import type { State, Sort, Filter, TableParams } from '$lib/src/server'\nimport { EventDispatcher } from '$lib/src/shared"
  },
  {
    "path": "src/lib/src/server/TableHandler.svelte.ts",
    "chars": 4679,
    "preview": "import AbstractTableHandler from './AbstractTableHandler.svelte'\n\nimport FetchHandler         from './handlers/FetchHand"
  },
  {
    "path": "src/lib/src/server/builders/FilterBuilder.svelte.ts",
    "chars": 1076,
    "preview": "import type FilterHandler from '../handlers/FilterHandler.svelte'\n\nexport default class FilterBuilder<Row>\n{\n    public "
  },
  {
    "path": "src/lib/src/server/builders/SearchBuilder.svelte.ts",
    "chars": 1032,
    "preview": "import type TableHandler from '../TableHandler.svelte'\nimport type { SearchInterface } from '$lib/src/shared'\n\nexport de"
  },
  {
    "path": "src/lib/src/server/builders/SortBuilder.svelte.ts",
    "chars": 1238,
    "preview": "import type SortHandler         from '../handlers/SortHandler.svelte'\nimport type { SortInterface }   from '$lib/src/sha"
  },
  {
    "path": "src/lib/src/server/handlers/FetchHandler.svelte.ts",
    "chars": 873,
    "preview": "import type { State, TableHandler } from '$lib/src/server'\n\n\nexport default class FetchHandler<Row>\n{\n    private table:"
  },
  {
    "path": "src/lib/src/server/handlers/FilterHandler.svelte.ts",
    "chars": 732,
    "preview": "import type { TableHandler } from '$lib/src/server'\n\nexport default class FilterHandler<Row>\n{\n    private table: TableH"
  },
  {
    "path": "src/lib/src/server/handlers/PageHandler.svelte.ts",
    "chars": 924,
    "preview": "import type { TableHandler } from '$lib/src/server'\n\nexport default class PageHandler<Row>\n{\n    private table: TableHan"
  },
  {
    "path": "src/lib/src/server/handlers/SearchHandler.svelte.ts",
    "chars": 297,
    "preview": "import type { TableHandler } from '$lib/src/server'\n\nexport default class SearchHandler<Row> \n{\n    private table: Table"
  },
  {
    "path": "src/lib/src/server/handlers/SelectHandler.svelte.ts",
    "chars": 951,
    "preview": "import type { TableHandler } from '$lib/src/server'\n\nexport default class SelectHandler<Row>\n{\n    private table: TableH"
  },
  {
    "path": "src/lib/src/server/handlers/SortHandler.svelte.ts",
    "chars": 814,
    "preview": "import type { TableHandler } from '$lib/src/server'\n\nexport default class SortHandler<Row>\n{\n    private table: TableHan"
  },
  {
    "path": "src/lib/src/server/index.ts",
    "chars": 1001,
    "preview": "export { default as TableHandler }  from './TableHandler.svelte'\nimport type { Row, Internationalization } from '$lib/sr"
  },
  {
    "path": "src/lib/src/shared/Datatable.svelte",
    "chars": 3367,
    "preview": "<script lang=\"ts\">\n    import type { Snippet } from 'svelte'\n    import { type TableHandlerInterface, Search, RowsPerPag"
  },
  {
    "path": "src/lib/src/shared/EventDispatcher.ts",
    "chars": 875,
    "preview": "\nexport default class EventDispatcher\n{\n    private listeners = {\n        change      : [] as (() => void)[],\n        cl"
  },
  {
    "path": "src/lib/src/shared/Pagination.svelte",
    "chars": 3357,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface } from '$lib/src/shared'\n    type T = $$Generic<Row>\n    let "
  },
  {
    "path": "src/lib/src/shared/RowCount.svelte",
    "chars": 1361,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface } from '$lib/src/shared'\n\n    type T = $$Generic<Row>\n    let"
  },
  {
    "path": "src/lib/src/shared/RowsPerPage.svelte",
    "chars": 1147,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface } from '$lib/src/shared'\n\n    type T = $$Generic<Row>\n    let"
  },
  {
    "path": "src/lib/src/shared/Search.svelte",
    "chars": 1010,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface } from '$lib/src/shared'\n    type T = $$Generic<Row>\n    let "
  },
  {
    "path": "src/lib/src/shared/Th.svelte",
    "chars": 583,
    "preview": "<script lang=\"ts\">\n    import type { Snippet } from 'svelte'\n\n    let { children }: { children?: Snippet } = $props()\n</"
  },
  {
    "path": "src/lib/src/shared/ThFilter.svelte",
    "chars": 1213,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface, Field } from '$lib/src/shared'\n    import type { Check } fro"
  },
  {
    "path": "src/lib/src/shared/ThSort.svelte",
    "chars": 1875,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface, Field } from '$lib/src/shared'\n    import type { Snippet } f"
  },
  {
    "path": "src/lib/src/shared/builders/HighlightBuilder.svelte.ts",
    "chars": 2067,
    "preview": "import type { TableHandlerInterface } from '$lib/src/shared'\nimport { stringify } from '$lib/src/client/core'\n\n\nexport d"
  },
  {
    "path": "src/lib/src/shared/builders/ViewBuilder.svelte.ts",
    "chars": 3425,
    "preview": "import type { TableHandlerInterface, ColumnView } from '$lib/src/shared'\n\nexport default class ViewBuilder<Row>\n{\n    pu"
  },
  {
    "path": "src/lib/src/shared/clsx/Datatable.svelte",
    "chars": 3379,
    "preview": "<script lang=\"ts\">\n    import type { Snippet } from 'svelte'\n    import { type TableHandlerInterface, Search, RowsPerPag"
  },
  {
    "path": "src/lib/src/shared/clsx/Pagination.svelte",
    "chars": 3408,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface } from '$lib/src/shared'\n    type T = $$Generic<Row>\n    let "
  },
  {
    "path": "src/lib/src/shared/clsx/ThSort.svelte",
    "chars": 1810,
    "preview": "<script lang=\"ts\">\n    import type { TableHandlerInterface, Field } from '$lib/src/shared'\n    import type { Snippet } f"
  },
  {
    "path": "src/lib/src/shared/index.ts",
    "chars": 2349,
    "preview": "export { default as Datatable       }   from './Datatable.svelte'\nexport { default as Pagination      }   from './Pagina"
  },
  {
    "path": "src/lib/style.css",
    "chars": 1234,
    "preview": ".svelte-simple-datatable table {\n    border-collapse: separate;\n    border-spacing: 0;\n    width: 100%;\n    background: "
  },
  {
    "path": "src/routes/+layout.svelte",
    "chars": 1064,
    "preview": "<script>\n    import Header               from './Header.svelte'\n    // import { ModalContainer }   from 'gros/modal'\n   "
  },
  {
    "path": "src/routes/+page.svelte",
    "chars": 4977,
    "preview": "<script lang=\"ts\">\n    import { site }     from '$site'\n    import { path }     from 'gros/page'\n    import Description "
  },
  {
    "path": "src/routes/Description.svelte",
    "chars": 1539,
    "preview": "<script>\n    const checked = `<svg width=\"28px\" height=\"28px\" viewBox=\"0 0 24 24\"><path fill=\"#4caf50\" d=\"M5 21q-.825 0-"
  },
  {
    "path": "src/routes/Header.svelte",
    "chars": 4524,
    "preview": "<script lang=\"ts\">\n    import { site, Logo } from '$site'\n    import { path }     from 'gros/page'\n    import Github    "
  },
  {
    "path": "src/routes/Header_Github.svelte",
    "chars": 2513,
    "preview": "<script>\n    let { isMobile = false } = $props()\n</script>\n\n{#if isMobile}\n    <a href=\"https://github.com/vincjo/datata"
  },
  {
    "path": "src/routes/Header_MobileNav.svelte",
    "chars": 2871,
    "preview": "<script lang=\"ts\">\n    import { slide }    from 'svelte/transition'\n    import { path }     from 'gros/page'\n    import "
  },
  {
    "path": "src/routes/Header_Mode.svelte",
    "chars": 5019,
    "preview": "<script lang=\"ts\">\n    import { Tooltip }  from 'gros/tooltip'\n    import { site }     from '$site'\n    import { path } "
  },
  {
    "path": "src/routes/Header_Theme.svelte",
    "chars": 5821,
    "preview": "<script lang=\"ts\">\n    import { theme } from 'gros/theme'\n    let { isMobile = false } = $props()\n</script>\n\n{#if isMobi"
  },
  {
    "path": "src/routes/Header_Version.svelte",
    "chars": 1363,
    "preview": "<script lang=\"ts\">\n    import { Dropdown } from 'gros/dropdown'\n</script>\n\n\n<Dropdown position=\"bottom-start\">\n    <asid"
  },
  {
    "path": "src/routes/about/+layout.svelte",
    "chars": 4396,
    "preview": "<script lang=\"ts\">\n    import Nav from './Nav.svelte'\n    import type { Snippet } from 'svelte'\n    import MobileNav fro"
  },
  {
    "path": "src/routes/about/+page.svelte",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/routes/about/Nav.svelte",
    "chars": 1939,
    "preview": "<script lang=\"ts\">\n    import { path } from 'gros/page'\n    type Props = { nav: { title: string, page: string, icon: str"
  },
  {
    "path": "src/routes/about/Nav_Mobile.svelte",
    "chars": 3529,
    "preview": "<script lang=\"ts\">\n    import { path }         from 'gros/page'\n    import { clickOutside } from 'gros/action'\n    impor"
  },
  {
    "path": "src/routes/about/[slug]/+page.server.ts",
    "chars": 598,
    "preview": "import { readFileSync } from 'fs'\n\nexport const load = async ({ params }) => {\n    const slug = params.slug\n    try {\n  "
  },
  {
    "path": "src/routes/about/[slug]/+page.svelte",
    "chars": 415,
    "preview": "<script lang=\"ts\">\n    let { data } = $props()\n</script>\n\n<section class=\"about\">\n    {@html data.content}\n</section>\n\n\n"
  },
  {
    "path": "src/routes/api/[mode]/+layout.server.ts",
    "chars": 553,
    "preview": "import { internalFilter } from '$site'\n\nexport const load = async ({ params }) => {\n    const response = await fetch(`BA"
  },
  {
    "path": "src/routes/api/[mode]/+layout.svelte",
    "chars": 1338,
    "preview": "<script lang=\"ts\">\n    import Nav from './Nav.svelte'\n    import MobibleNav from './Nav_Mobile.svelte'\n    import { path"
  },
  {
    "path": "src/routes/api/[mode]/+page.svelte",
    "chars": 106,
    "preview": "<script>\n    import Content from './content.svx'\n</script>\n\n<section class=\"md\">\n    <Content/>\n</section>"
  },
  {
    "path": "src/routes/api/[mode]/Nav.svelte",
    "chars": 1325,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import Key from './Nav_Key.svelte'\n    import "
  },
  {
    "path": "src/routes/api/[mode]/Nav_Key.svelte",
    "chars": 2733,
    "preview": "<script lang=\"ts\">\n    import { site }     from '$site'\n    import { path }     from 'gros/page'\n    import { page }    "
  },
  {
    "path": "src/routes/api/[mode]/Nav_Mobile.svelte",
    "chars": 2512,
    "preview": "<script lang=\"ts\">\n    import { clickOutside } from 'gros/action'\n    import { fly, fade }    from 'svelte/transition'\n "
  },
  {
    "path": "src/routes/api/[mode]/[slug]/+page.server.ts",
    "chars": 727,
    "preview": "\n\nexport const load = async ({ params }) => {\n\n    const mode = params.mode\n    const [key, name] = params.slug.split('~"
  },
  {
    "path": "src/routes/api/[mode]/[slug]/+page.svelte",
    "chars": 846,
    "preview": "<script lang=\"ts\">\n    import { page } from '$app/stores'\n    import Content  from './Content.svelte'\n\n    let { data } "
  },
  {
    "path": "src/routes/api/[mode]/[slug]/Content.svelte",
    "chars": 1088,
    "preview": "<script lang=\"ts\">\n    import Highlight from '$site/components/Highlight.svelte'\n    import Ext       from './Content_Ex"
  },
  {
    "path": "src/routes/api/[mode]/[slug]/Content_Ext.svelte",
    "chars": 1492,
    "preview": "<script lang=\"ts\">\n    import Prism from 'prismjs'\n    import 'prism-svelte'\n    import 'prismjs/components/prism-typesc"
  },
  {
    "path": "src/routes/api/[mode]/content.svx",
    "chars": 1268,
    "preview": "<script>\n    import { site } from '$site'\n</script>\n\n\n<span class=\"index\" style:color=\"var(--primary)\">{site.mode === 'c"
  },
  {
    "path": "src/routes/api/[mode]/gen/+page.server.ts",
    "chars": 526,
    "preview": "import { dev } from '$app/environment'\n\nexport const load = async (url) => {\n    if (!dev) {\n        return {\n          "
  },
  {
    "path": "src/routes/api/[mode]/gen/+page.svelte",
    "chars": 516,
    "preview": "<script>\n    import Board from './Board.svelte'\n    let { data } = $props()\n</script>\n\n<section class=\"flex\">\n    <h1>{d"
  },
  {
    "path": "src/routes/api/[mode]/gen/Board.svelte",
    "chars": 845,
    "preview": "<script>\n    import { loading } from 'gros/loading'\n\n\n    const generate = async (mode) => {\n        loading.start('Gene"
  },
  {
    "path": "src/routes/api/[mode]/md/+layout.svelte",
    "chars": 1399,
    "preview": "<script lang=\"ts\">\n    import Nav from './Nav.svelte'\n    import { path } from 'gros/page'\n    import { dev } from '$app"
  },
  {
    "path": "src/routes/api/[mode]/md/+layout.ts",
    "chars": 567,
    "preview": "import { internalFilter } from '$site'\nimport { dev } from '$app/environment'\n\n\nexport const load = async ({ params }) ="
  },
  {
    "path": "src/routes/api/[mode]/md/+page.svelte",
    "chars": 107,
    "preview": "\n<script>\n    import Content from './content.svx'\n</script>\n\n<section class=\"md\">\n    <Content/>\n</section>"
  },
  {
    "path": "src/routes/api/[mode]/md/Nav.svelte",
    "chars": 1547,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import Key from './Nav_Key.svelte'\n    let { n"
  },
  {
    "path": "src/routes/api/[mode]/md/Nav_Key.svelte",
    "chars": 2517,
    "preview": "<script lang=\"ts\">\n    import { site } from '$site'\n    import { path } from 'gros/page'\n    import { page } from '$app/"
  },
  {
    "path": "src/routes/api/[mode]/md/[slug]/+page.svelte",
    "chars": 882,
    "preview": "<script lang=\"ts\">\n    import { page } from '$app/stores'\n    import Content  from './Content.svelte'\n    import AST fro"
  },
  {
    "path": "src/routes/api/[mode]/md/[slug]/+page.ts",
    "chars": 285,
    "preview": "\n\nexport const load = async ({ params }) => {\n\n    const mode = params.mode\n    const [key, name] = params.slug.split('~"
  },
  {
    "path": "src/routes/api/[mode]/md/[slug]/AST.svelte",
    "chars": 698,
    "preview": "<script lang=\"ts\">\n    import { page } from '$app/stores'\n\n    let ast = $state(undefined)\n\n    const load = async () =>"
  },
  {
    "path": "src/routes/api/[mode]/md/[slug]/Content.svelte",
    "chars": 340,
    "preview": "<script lang=\"ts\">\n    let { data }: { data: any, key: string } = $props()\n</script>\n\n\n<section>\n    <h1>{data.name}</h1"
  },
  {
    "path": "src/routes/api/[mode]/md/[slug]/Content_Ext.svelte",
    "chars": 1412,
    "preview": "<script lang=\"ts\">\n    import Prism from 'prismjs'\n    import 'prism-svelte'\n    import 'prismjs/components/prism-typesc"
  },
  {
    "path": "src/routes/api/[mode]/md/content.svx",
    "chars": 291,
    "preview": "<script>\n    import { site } from '$site'\n</script>\n\n\n{#if site.mode === 'client'}\n    <span style:color=\"var(--font)\">C"
  },
  {
    "path": "src/routes/components/+layout.svelte",
    "chars": 3396,
    "preview": "<script>\n    import Layout from '$site/components/docs/Layout.svelte'\n    let { children } = $props()\n    const root = `"
  },
  {
    "path": "src/routes/components/+page.svelte",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/routes/docs/client/+layout.svelte",
    "chars": 12836,
    "preview": "<script>\n    import Layout from '$site/components/docs/Layout.svelte'\n    let { children } = $props()\n    const root = `"
  },
  {
    "path": "src/routes/docs/client/+page.server.ts",
    "chars": 170,
    "preview": "import { redirect } from '@sveltejs/kit'\nimport { path } from 'gros/page'\n\nexport const load = () => {\n    redirect(307,"
  },
  {
    "path": "src/routes/docs/client/add-on/csv-export/+page.svelte",
    "chars": 115,
    "preview": "<script>\n    import Content from './content.svx'\n    import Main from './Main.svelte'\n</script>\n\n<Content/>\n<Main/>"
  },
  {
    "path": "src/routes/docs/client/add-on/csv-export/Main.svelte",
    "chars": 1757,
    "preview": "<script lang=\"ts\">\n    import { TableHandler, Datatable, ThSort, Search, RowCount, Pagination } from '$lib/src/client'\n "
  },
  {
    "path": "src/routes/docs/client/add-on/csv-export/content.svx",
    "chars": 148,
    "preview": "\n\n# CSV export\n\n```ts\nconst csv = table.createCSV()\n```\n\n```svelte\n<button onclick={() => csv.download('filename.csv')}>"
  },
  {
    "path": "src/routes/docs/client/add-on/record-filter/+page.svelte",
    "chars": 116,
    "preview": "<script>\n    import Content from './content.svx'\n    import Main from './Main.svelte'\n</script>\n\n<Content/>\n\n<Main/>"
  },
  {
    "path": "src/routes/docs/client/add-on/record-filter/Main.svelte",
    "chars": 2798,
    "preview": "<script lang=\"ts\">\n    import { TableHandler, Datatable, ThSort } from '$lib/src/client'\n    import { data } from './dat"
  },
  {
    "path": "src/routes/docs/client/add-on/record-filter/content.svx",
    "chars": 275,
    "preview": "\n# Record filter\n\n```ts\nconst filter = table.createRecordFilter(distinct)\n```\n\n```svelte\n<input type=\"text\" bind:value={"
  },
  {
    "path": "src/routes/docs/client/add-on/record-filter/data_cars.ts",
    "chars": 5276,
    "preview": "export const data = [{\"id\":1,\"make\":\"GMC\",\"model\":\"Yukon\",\"model_year\":1999,\"color\":\"#2a06a7\",\"price\":73958},\n{\"id\":2,\"m"
  },
  {
    "path": "src/routes/docs/client/calculation/avg/+page.svelte",
    "chars": 109,
    "preview": "<script>\n    import Code from './code.svx'\n</script>\n\n<section>\n    <h1>Average</h1>\n    <Code />\n</section>\n"
  },
  {
    "path": "src/routes/docs/client/calculation/avg/Advanced.svelte",
    "chars": 2475,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_parcel'\n\n    con"
  },
  {
    "path": "src/routes/docs/client/calculation/avg/Basic.svelte",
    "chars": 2303,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_cars'\n\n    const"
  },
  {
    "path": "src/routes/docs/client/calculation/avg/code.svx",
    "chars": 606,
    "preview": "<script>\n    import Basic from './Basic.svelte'\n    import Advanced from './Advanced.svelte'\n</script>\n\n### Basic usage\n"
  },
  {
    "path": "src/routes/docs/client/calculation/bounds/+page.svelte",
    "chars": 118,
    "preview": "<script>\n    import Code from './code.svx'\n</script>\n\n<section>\n    <h1>Bounds [min, max]</h1>\n    <Code />\n</section>"
  },
  {
    "path": "src/routes/docs/client/calculation/bounds/Basic.svelte",
    "chars": 2770,
    "preview": "<script lang=\"ts\">\n    import { TableHandler, Datatable, ThSort } from '$lib/src/client'\n    import { data } from '../da"
  },
  {
    "path": "src/routes/docs/client/calculation/bounds/code.svx",
    "chars": 264,
    "preview": "<script>\n    import Basic from './Basic.svelte'\n</script>\n\n### Basic usage\n```ts\nconst [min, max] = $derived(table.creat"
  },
  {
    "path": "src/routes/docs/client/calculation/data_cars.ts",
    "chars": 5276,
    "preview": "export const data = [{\"id\":1,\"make\":\"GMC\",\"model\":\"Yukon\",\"model_year\":1999,\"color\":\"#2a06a7\",\"price\":73958},\n{\"id\":2,\"m"
  },
  {
    "path": "src/routes/docs/client/calculation/data_grocery.ts",
    "chars": 2950,
    "preview": "export const data = [{\"id\":1,\"product\":\"Coffee Cup 16oz Foam\",\"price\":58.64},\n{\"id\":2,\"product\":\"Turnip - Wax\",\"price\":4"
  },
  {
    "path": "src/routes/docs/client/calculation/data_parcel.ts",
    "chars": 3734,
    "preview": "export const data = [{\"id\":1,\"address\":\"53 Mariners Cove Place\",\"width\":35.2579,\"length\":97.6174},\n{\"id\":2,\"address\":\"81"
  },
  {
    "path": "src/routes/docs/client/calculation/distinct/+page.svelte",
    "chars": 116,
    "preview": "<script>\n    import Code from './code.svx'\n</script>\n\n<section>\n    <h1>Distinct values</h1>\n    <Code />\n</section>"
  },
  {
    "path": "src/routes/docs/client/calculation/distinct/Basic.svelte",
    "chars": 2919,
    "preview": "<script lang=\"ts\">\n    import { TableHandler, Datatable, ThSort } from '$lib/src/client'\n    import { data } from '../da"
  },
  {
    "path": "src/routes/docs/client/calculation/distinct/code.svx",
    "chars": 729,
    "preview": "<script>\n    import Basic from './Basic.svelte'\n</script>\n\n### Basic usage\n\n```ts\nconst distinct = $derived.by(() => {\n "
  },
  {
    "path": "src/routes/docs/client/calculation/median/+page.svelte",
    "chars": 107,
    "preview": "<script>\n    import Code from './code.svx'\n</script>\n\n<section>\n    <h1>Median</h1>\n    <Code />\n</section>"
  },
  {
    "path": "src/routes/docs/client/calculation/median/Advanced.svelte",
    "chars": 2462,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_parcel'\n\n\n    co"
  },
  {
    "path": "src/routes/docs/client/calculation/median/Basic.svelte",
    "chars": 2167,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_grocery'\n\n    co"
  },
  {
    "path": "src/routes/docs/client/calculation/median/code.svx",
    "chars": 621,
    "preview": "<script>\n    import Basic from './Basic.svelte'\n    import Advanced from './Advanced.svelte'\n</script>\n\n### Basic usage\n"
  },
  {
    "path": "src/routes/docs/client/calculation/sum/+page.svelte",
    "chars": 104,
    "preview": "<script>\n    import Code from './code.svx'\n</script>\n\n<section>\n    <h1>Sum</h1>\n    <Code />\n</section>"
  },
  {
    "path": "src/routes/docs/client/calculation/sum/Advanced.svelte",
    "chars": 2452,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_parcel'\n\n\n    co"
  },
  {
    "path": "src/routes/docs/client/calculation/sum/Basic.svelte",
    "chars": 2157,
    "preview": "<script lang=\"ts\">\n    import { TableHandler } from '$lib/src/client'\n    import { data } from '../data_grocery'\n\n    co"
  },
  {
    "path": "src/routes/docs/client/calculation/sum/code.svx",
    "chars": 601,
    "preview": "<script>\n    import Basic from './Basic.svelte'\n    import Advanced from './Advanced.svelte'\n</script>\n\n### Basic usage\n"
  },
  {
    "path": "src/routes/docs/client/filters/Main.svelte",
    "chars": 1480,
    "preview": "<script>\n    import data from '$site/data/data'\n    import { TableHandler, Datatable, Th, Pagination } from '$lib/src/cl"
  },
  {
    "path": "src/routes/docs/client/filters/check/+page.svelte",
    "chars": 241,
    "preview": "<script>\n    import Content from './content.svx'\n    import Content2 from './content2.svx'\n    import Main from '../Main"
  },
  {
    "path": "src/routes/docs/client/filters/check/Comparators.svelte",
    "chars": 2173,
    "preview": "<script lang=\"ts\">\n    import Check from './Comparators_Check.svelte'\n    const comparators = [\n        { name: 'isLike'"
  },
  {
    "path": "src/routes/docs/client/filters/check/Comparators_Check.svelte",
    "chars": 2981,
    "preview": "<script>\n    import { TableHandler, check } from '$lib/src/client'\n    import { Range } from 'gros/form'\n    import { da"
  },
  {
    "path": "src/routes/docs/client/filters/check/content.svx",
    "chars": 298,
    "preview": "# Check function\n\nAdditionally, you can specify a check function to match with values.\n\n<!-- This function reduces boile"
  },
  {
    "path": "src/routes/docs/client/filters/check/content2.svx",
    "chars": 349,
    "preview": "<script>\n    import Comparators from './Comparators.svelte'\n</script>\n\n\n## Available check functions:\n\n```ts\nimport { ch"
  },
  {
    "path": "src/routes/docs/client/filters/check/data.ts",
    "chars": 1722,
    "preview": "export const data = {\n    string: [\n        { value: 'Aude' },\n        { value: 'Théodule' },\n        { value: null },\n "
  },
  {
    "path": "src/routes/docs/client/filters/criteria/+page.svelte",
    "chars": 73,
    "preview": "<script>\n    import Content from './content.svx'\n</script>\n\n\n<Content/>\n\n"
  },
  {
    "path": "src/routes/docs/client/filters/criteria/Main.svelte",
    "chars": 3187,
    "preview": "\n<script lang=\"ts\">\n    import { TableHandler, check } from '$lib/src/client'\n    import { data } from './data'\n\n    con"
  },
  {
    "path": "src/routes/docs/client/filters/criteria/content.svx",
    "chars": 457,
    "preview": "<script>\n    import Main from './Main.svelte'\n</script>\n\n\n# Multiple criteria filter\n\n```ts\nconst filter = table.createA"
  },
  {
    "path": "src/routes/docs/client/filters/criteria/data.ts",
    "chars": 2612,
    "preview": "export const data = [{\"id\":1,\"task\":\"nulla suspendisse potenti cras in purus eu magna vulputate luctus cum sociis natoqu"
  },
  {
    "path": "src/routes/docs/client/filters/highlight/+page.svelte",
    "chars": 132,
    "preview": "<script>\n    import Content from './content.svx'\n    import Main from '../Main.svelte'\n</script>\n\n\n<Content/>\n\n<Main isH"
  },
  {
    "path": "src/routes/docs/client/filters/highlight/content.svx",
    "chars": 369,
    "preview": "\n# Highlight results\n\nTo highlight results, define \"highlight\" at `true` in `TableHandler` params:\n\n```ts\nconst table = "
  },
  {
    "path": "src/routes/docs/client/filters/input/+page.svelte",
    "chars": 117,
    "preview": "<script>\n    import Content from './content.svx'\n    import Main from '../Main.svelte'\n</script>\n\n<Content/>\n\n<Main/>"
  },
  {
    "path": "src/routes/docs/client/filters/input/content.svx",
    "chars": 522,
    "preview": "# Filter input \n\n```ts\nconst filter = table.createFilter('last_name')\n```\n\n```svelte\n<input type=\"text\" bind:value={filt"
  },
  {
    "path": "src/routes/docs/client/filters/nested/+page.svelte",
    "chars": 123,
    "preview": "<script>\n    import Content from './content.svx'\n    import Nested from './Nested.svelte'\n</script>\n\n\n<Content/>\n\n<Neste"
  },
  {
    "path": "src/routes/docs/client/filters/nested/Nested.svelte",
    "chars": 2189,
    "preview": "<script>\n    import { TableHandler, Datatable, Th } from '$lib/src/client'\n\n    const data = [\n        { user: { name: '"
  },
  {
    "path": "src/routes/docs/client/filters/nested/content.svx",
    "chars": 274,
    "preview": "# Nested prop \n\nYou may have nested properties that you need to filter specifically.\n\n`field` can be set as a callback w"
  },
  {
    "path": "src/routes/docs/client/getting-started/hello-world/+page.svelte",
    "chars": 102,
    "preview": "<script>\n    import Client from './Client.svx'\n    let { data } = $props()\n</script>\n\n<Client {data}/>"
  },
  {
    "path": "src/routes/docs/client/getting-started/hello-world/Client.svx",
    "chars": 1733,
    "preview": "<script>\n    import Main from './Main.svelte'\n</script>\n\n# Hello World\n\nHere is a preview of what the default datatable "
  },
  {
    "path": "src/routes/docs/client/getting-started/hello-world/Main.svelte",
    "chars": 1254,
    "preview": "<script lang=\"ts\">\n    import { TableHandler, Datatable, ThSort, ThFilter } from '$lib/src/client'\n    import myData fro"
  }
]

// ... and 440 more files (download for full content)

About this extraction

This page contains the full source code of the vincjo/datatables GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 640 files (3.9 MB), approximately 1.1M tokens, and a symbol index with 450 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!