GithubHelp home page GithubHelp logo

bryanmylee / svelte-headless-table Goto Github PK

View Code? Open in Web Editor NEW
422.0 7.0 25.0 2.22 MB

Unopinionated and extensible data tables for Svelte

Home Page: https://svelte-headless-table.bryanmylee.com/

JavaScript 0.58% HTML 0.15% Svelte 6.58% TypeScript 92.69%
datatable filtering headless hiding plugin sorting svelte sveltejs table expanding

svelte-headless-table's People

Contributors

blerrgh avatar bryanmylee avatar lolcabanon avatar manuel-dasilva avatar risalfajar avatar ryanylee avatar thomas725 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

svelte-headless-table's Issues

setting allPageRowsSelected to "false" clears entire selection, not just the current page

Writing true to the allPageRowsSelected store adds only the current page's IDs to the selectedDataIds store, as expected.

But setting it to false clears all selected rows, not only the ones on the current page.

I would expect the behavior to be consistent where setting true and false add/remove current page IDs only.

Relevant code in addSelectedRows.ts

		const setAllPageRowsSelected = ($allPageRowsSelected: boolean) => {
			if ($allPageRowsSelected) {
				const $pageRows = get(tableState.pageRows);
				const pageDataIds = $pageRows
					.map((row) => (row.isData() ? row.dataId : null))
					.filter(nonNull);
				selectedDataIds.addAll(pageDataIds);
			} else {
				selectedDataIds.clear();
			}
		};

Cell Event Dispatcher Isn't Reactive

I have this code which worked perfectly except the Edit button

const repository = new UserRepository()
const data: Readable<User[]> = readable([], function start(set: Subscriber<User[]>) {
    return repository.listenAll(users => set(users ?? []))
})
const table = createTable(data)
const columns = table.createColumns([
    table.column({
        header: 'Username',
        accessor: 'username'
    }),
    table.column({
        id: 'actions',
        header: 'Actions',
        accessor: (item) => item,
        cell: ({value}, state) => createRender(TableActions)
            .on('edit', () => console.log(value.username))
    })
])

When a user click on edit button inside TableActions component, it'll trigger the edit event. The problem is when data changes, the username that gets printed on the console is not changed, it always printed the old value unless I refresh the page.

Workaround

    cell: (cell, state) => createRender(TableActions)
        .on('edit', () => console.log(get(state.data)[cell.row.id].username))

Add example to conditionally add styling to rows, columns & cells

Really enjoying working with svelte-headless-table so far! It would be great if we could add an example on how to conditionally apply styles to individual rows/columns/cells. E.g. append a style to the first column and all rows under <tbody>

From the example I can see this gets applied by:

      <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
        <tr {...rowAttrs}>

Can we include an example of how you'd pass values into rowAttrs from table.createColumns?
Thanks

Conditional row styling how?

So I'm preprocessing my data, and adding in a invalid: (predicate result) key on my object, I'd like to style the "invalid" rows to have a red background color, I tried:

<Subscribe
  attrs={row.attrs()}
  let:attrs
  props={row.props()}
  let:props
>
  <tr
    {...attrs}
    class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
    class:invalid={props.} // intellisense won't show my my original object here
  >

but props won't let me access the "invalid" key, same with attrs

Why use "header" and "Render" in column definition at all ?

First of all - I am probably doing this the wrong way, so please enlighten me.

I would like to have custom header cell, so I guess I have three ways to do it

{#each headerRow.cells as cell (cell.id)}
    <Subscribe attrs={cell.attrs()} props={cell.props()} let:attrs let:props>
        <div {...attrs}>
             // render my custom component like this
            <Render of={cell.render()}/>

            // or  just use plain html
            <span>{cell.label}</span>

            // or svelte component
            <MyCustomHeader {cell}/>
        </div>
    </Subscribe>
{/each}

Looking at the docs, you are recommending to use createRender tho. Why is Render recommended ? If we want to enable the sorting plugin, we are adding plain html anyway, right ?

{#each headerRow.cells as cell (cell.id)}
    <Subscribe attrs={cell.attrs()} props={cell.props()} let:attrs let:props>
        <div {...attrs}>
            <Render of={cell.render()}/>

            // from the example
            {#if props.sort.order === 'asc'}
                ⬇️
            {:else if props.sort.order === 'desc'}
                ⬆️
             {/if}
        </div>
    </Subscribe>
{/each}

If I want to have that sorting "hidden" in the header cell component, I need to do this kind of thing, since header does not provide the specific HeaderCell but the whole table state.

table.column({
    header: (state) => {
        return createRender(TableHeaderCell, {
            label: "Popis",
            sortToggle: () => state.pluginStates.sort.sortKeys.toggleId("description", {}),
            sortOrder: get(state.pluginStates.sort.sortKeys).find(key => key.id === "description")?.order
        })
    },
    accessor: "description",
}),

I feel like I am doing something horribly wrong. Would you mind explaining please ?

addResizedColumns plugin breaks if data store referenced multiple times in script functions without addHiddenColumns plugin

The ability to resize columns breaks if you subscribe to the main data store more than once in separate functions and don't also implement the addHiddenColumns plugin.

Problem

Here is an example in a REPL:
https://svelte.dev/repl/71eb58bfe8754460926531cf211405da?version=3.49.0

Details:
The REPL above is a clone of the 'Simple Column Resizing' REPL. The only difference is it adds two functions.

function read1() {
    console.log($data[0])
}
	
function read2() {
    console.log($data[1])
}

Just adding those functions completely breaks the resizing of columns. The functions don't even have to be called. I haven't yet figured out what causes them to break the resizing; though, I assume it has to do with how Svelte Headless Table manages the state of the resizing columns.

Workaround

By adding and implementing the addHiddenColumns plugin, the columns will resume resizing as expected.

Here is an example in a REPL:
https://svelte.dev/repl/9c2868beb9ef4c0897ccdcab0dbfcda4?version=3.49.0

This is the code that has to be added:

import { addHiddenColumns, addResizedColumns } from 'svelte-headless-table/plugins';
const table = createTable(data, {
    hideColumns: addHiddenColumns(),
    resize: addResizedColumns(),
});
const {
    flatColumns,
    headerRows,
    pageRows,
    tableAttrs,
    tableBodyAttrs,
    visibleColumns,
    pluginStates,
} = table.createViewModel(columns);
	
const ids = flatColumns.map((c) => c.id);
	
const { hiddenColumnIds } = pluginStates.hideColumns;
let hideForId = Object.fromEntries(ids.map((id) => [id, false]));
$: $hiddenColumnIds = Object.entries(hideForId)
    .filter(([, hide]) => hide)
    .map(([id]) => id);

Possible implementation of a virtual list ?

Hi,
I am aware that we already have pagination plugin in place which works great, but would it be possible to add an "infinite virtual list" pagination plugin ?

The idea would be to limit the table height and specify (or auto-calculate if reasonable) how many items will be shown/rendered and the rendered offset would be managed by scroll, eg. infinite virtual scroll.

Maybe this would be possible to "plug-in" ? https://github.com/Skayo/svelte-tiny-virtual-list

reactive header?

Hi there!

I'm trying to use this component in an app of mine.

It's using svelte-i18n, and for a vanilla table I put stuff like: <th>{$_('name')}</th> into the svelte template section for my table headers. This automatically changes when the language is switched.

Now to get started with your library, I've copied the code from your quick-start sample into a new Svelte component, and added those imports:

import { _ } from "svelte-i18n";
import {createTable, Subscribe, Render} from 'svelte-headless-table';
import { readable } from "svelte/store";

(it would be nice if you could copy the ones everybody needs into your sample, so future users don't need to search for them themselves, see pull request)

Anyway, now in the column definition, I replaced header: 'name' with header: $_('name') - which works, but isn't reactive, so doesn't change when the language changes.

How can I go about doing this in a way that preserves / propagates the reactivity?

Cannot read properties of undefined (reading 'map')

I'm using Supabase to query data to load into a table but I keep getting this error: Cannot read properties of undefined (reading 'map')

Data Model

[
  {
    "name": "Partner",
    "permissions": [

      {
        "can_add_members": false,
        "can_delete_members": false,
        "can_edit_members": false,
        "can_create_roles": false,
        "can_delete_roles": false,
        "can_edit_roles": false,
        "can_assign_roles": false,
        "can_create_projects": false,
        "can_delete_projects": false,
        "can_edit_projects": false,
        "can_publish_projects": false,
        "can_view_projects": false,
        "can_assign_members_to_projects": false
      }
    ]
  },
  {
    "name": "Associate Partner",
    "permissions": [
      {
        "can_add_members": false,
        "can_delete_members": false,
        "can_edit_members": false,
        "can_create_roles": false,
        "can_delete_roles": false,
        "can_edit_roles": false,
        "can_assign_roles": false,
        "can_create_projects": false,
        "can_delete_projects": false,
        "can_edit_projects": false,
        "can_publish_projects": false,
        "can_view_projects": false,
        "can_assign_members_to_projects": false
      }
    ]
  },
  {
]

Code

 const roleArray = writable([])
             onMount(() => {
             getPermissions()    
             });
   // TABLE CODE
    const table = createTable(roleArray);
    const columns = table.createColumns([
    table.column({
    header: 'Name',
    accessor: 'name',
     }),
 
    ]);

    const {
    headerRows,
    rows,
    tableAttrs,
    tableBodyAttrs,
     } = table.createViewModel(columns);


    // GET PERMISSIONS FROM SUPABSE
        async function getPermissions(){
        const { data: firm, error } = await supabaseClient
        .from('firms')
        .select('role_permissions')
        .eq('id',$userOrgID)
        
        if (firm){
           // console.log(JSON.stringify(firm, null, 2))
            const [fetchedUser] = firm;
            //console.log(fetchedUser.role_permissions)
            roleArray.set(fetchedUser.role_permissions)
            console.log($roleArray)
            
        } else{
            console.log("Error fetching permissions", error)
        }
    }

Improvement to the filter

Hi!
I'm using this library to perform research on big tables and I figure out that the filter doesn't filter pieces of worlds. Like if I have a value like "Computer science" and I try to filter "science" I will not see that row, I can only see it if I search "computer".
I think that can be useful to change this or allow the option to choose between this way to do the filter and the one that I proposed.

Expose drag event

Would it be possible to expose an event after resizing a table header? The use case would be for storing $columnWidths to localStorage, so the client does not need to resize their table columns every time. Currently I am calling the function with on:mouseup from the thead, but this is unreliable ie if the user drags the mouse up away from the table while resizing.
Thanks for the great library.

`addRowSelect` plugin

Currently, row ids are generated based on position in the original data.

To support better row selection and other row-id related plugins, we should allow developers to define their own id accessors.

`addRowLabel` plugin

Allow developers to annotate BodyRows with a custom component that spans the entire row.

It should receive the row's original item as a prop to produce a RenderConfig.

[Question/Request] Integration with SvelteGantt

Awesome tool!
Trying to integrate it with the table module used in 'svelte-gantt' (https://github.com/ANovokmet/svelte-gantt/tree/master/src/modules/table). My point of struggle is that headless table doesn't seem to provide an object that reflects the 'displayed state' of the data in the table (sort -> with sorted items, grouping -> with switched children/parents). Any ideas how to derive that?

Example for sorting (e.g. item 0 in table is item 3 in object):
grafik

Remove `useX` conventions

Linked comment.

Issues

Currently, the creation of the view model is done with useTable and all plugins are prefixed with useX. This is primary influenced by React Table, but this might lead beginners to confuse the functions with Svelte actions and the use directive.

useTable

useTable should be renamed. Some ideas considered are:

  • createTableViewModel – this is the most descriptive but might be a little wordy.
  • createViewModel – this is too generic and does not indicate any relevance to Svelte Headless Table.
  • table.createViewModel – alternatively, we could re-export the function as a property on table.
const { headerRows, rows } = table.createViewModel(columns)

Plugins

Currently, the plugins are prefixed with use to indicate that the functions are meant to be used on the table. Some alternatives considered are:

  • removing the use prefix entirely – this would be the most succinct, but we lose out on the standard prefix to identify the plugins.

We also run into an issue of confusing property and function names. The keys for plugin names should describe what they are e.g. sort, filter, group, while the plugin functions should describe what they do. By removing the prefix, we potentially end up with names such as tableFilter: tableFilter(). While this is clear to most JS/TS users, the similarities to property shorthand notation may be confusing to beginners.

const table = createTable(data, {
  sort: sortBy(),
  tableFilter: tableFilter(),
  filter: columnFilters(),
});
  • applyX – it might be an extra two characters but this could achieve most of what we want without adding any confusion with the use directive.
const table = createTable(data, {
  sort: applySortBy(),
  tableFilter: applyTableFilter(),
  filter: applyColumnFilters(),
});

[Question] Reccomended way of doing virtualisation?

Hi, really enjoying this project, thanks for developing and maintaining it!

Is there any reccomended way of virtualising the tables generated? I know this is not really a concern of the library, but maybe it could become one?

Allow multiple group-on features for columns

Hi,

I'd like to request the following feature(s):

  • Possibility for several grouping options per column
  • Possibility for grouping base on child layer props (e.g. item.children.age instead of item.age / 'value'), All parents of grouped layers should be ignored in that case.

Illustrating Picture (edited):
image

Illustrating REPL (Basis for the picture):
https://svelte.dev/repl/320d8a80589a4c34950e335ae377f60f?version=3.50.1

Reason:
I want the table to describe the 'natural structure of a product' (consisting of different parts as children or even grand children).
However I want to be 'able to change the score' quickly to only look at child layers (e.g. part level).

I hope you can understand what I mean (:

`useExpanded` plugin

Allow data items to specify sub-items and render them as subrows.

The column model will also need to be extended to allow for DisplayColumns that do not represent any data property, but represents additional UI for row expansion state etc.

`addResizedColumns` breaks with `addGridLayout`

Moving forward, when providing more layout options, it is not always guaranteed that data cells will adopt their width from their column header cells.

To provide more flexibility, addResizedColumns should provide an option to set the width of all elements explicitly.

Table event system or plugin

Right now it's painful to work with cell events.

I have a situation, where my table row last column is an "action" column with bunch of buttons (eg. edit, delete) and I would like to listen to the dispatched event from the cell right at the table component or even outside of it to process it and modify the table data.

The Render component now blocks custom events and using solution in #45 seems kind of dirty.

I need to dynamically listen to my unknown list of actions (row A can have one action, row B three and row C won't have any)

I think some simple table event system would be great - dispatch event which would automaticaly include the current cell and my custom data.

Example at my custom cell

 <script>
    export let dispatchTableEvent // provided by this library, equivalent of `createEventDispatcher()` output
    
    const handleMyEvent = () => {
      dispatchTableEvent("my_event", {
        my_custom_payload: "world",
      })
    }
</script>


<button on:click|preventDefault={handleMyEvent}>
  HELLO
</button>

Example at table definition

const { events } = table.createViewModel(columns)

// or events.on()
events.addListener("my_event", (e) =>{
  e.detail.cell // dispatched DataCell object
  e.detail.my_custom_payload // any other payload I defined
})

// or make it universal
events.any((e) =>{
  // handle any dynamic event type
})

I am aware that I can make this system myself as a separate thing or perhaps as a plugin (I am not sure how would I go around adding custom props to each cell - the event dispatcher), but it would be nice to have it build in.

Would that be considered as a "bloat" that user can do himself and you would rarther have cleaner/simpler package ?

Plugins break in dev mode on SvelteKit

Ok so, no matter what I try, sortBy just won't work, I copied the simple example, but when I use the same example on my local machine, sorting just won't work, any ideas? I'm not getting any errors whatsoever, that's why I can't diagnose the issue

package.json:

"devDependencies": {
    "@babel/core": "^7.14.0",
    "@babel/preset-env": "^7.14.0",
    "@hurtigruten/svelte-table": "^2.0.3",
    "@iconify/svelte": "^2.2.1",
    "@sveltejs/adapter-static": "^1.0.0-next.26",
    "@sveltejs/kit": "next",
    "@testing-library/jest-dom": "^5.14.0",
    "@testing-library/svelte": "^3.0.0",
    "@types/jest": "^27.0.0",
    "@types/testing-library__jest-dom": "^5.14.0",
    "autoprefixer": "10.4.5",
    "babel-jest": "^27.0.0",
    "cross-env": "^7.0.3",
    "date-picker-svelte": "^2.0.0",
    "flowbite": "^1.4.5",
    "flowbite-svelte": "^0.17.3",
    "jest": "^27.0.0",
    "jspdf": "^2.5.1",
    "n2words": "^1.11.1",
    "postcss": "^8.4.12",
    "postcss-load-config": "^3.1.4",
    "svelte": "^3.44.0",
    "svelte-check": "^2.2.6",
    "svelte-headless-table": "^0.10.2",
    "svelte-jester": "^2.0.1",
    "svelte-preprocess": "^4.9.4",
    "svelte-typeahead": "^4.2.2",
    "tailwindcss": "^3.0.23",
    "ts-jest": "^27.0.0",
    "tslib": "^2.3.1",
    "typescript": "^4.6.2",
    "wretch": "^1.7.9",
    "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.9/xlsx-0.18.9.tgz"
  },

svelte.config.js

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://github.com/sveltejs/svelte-preprocess
    // for more information about preprocessors
    preprocess: preprocess({ postcss: true }),

    kit: {
        adapter: adapter(),

        // Apply the proxy setup
        vite: () => ({
            server,
            optimizeDeps: {
                include: ['fuzzy'],
            },
        }),
    },
};

export default config;

Originally posted by @Gildedter in #28 (comment)

Plugin 'addDataExport' - incorrect export of parent/ child hierarchy

Hi :)

I noticed that the plugin does not seem to output parent/ child hierarchy correctly when choosing type 'object':
image

Expected behaviour in this case would be the that there are 2 top level objects instead of 6, when expanded.
Example RPL

In case the error is on my end.. please give me a hint.
Your awesome work is very much appreciated!

Add a "select all" option for `addSelectedRows` plugin

This is probably my favourite Svelte libraries, it's so useful and versatile! Thank you so much for making this.

I do have one small gripe, which is that there isn't a "select all" option for the addSelectedRows plugin. This can be hacked in like so:

// on init
let tableColumns = [];
    let selectAll = writable(false);
    if (selectable) {
        tableColumns.push(table.display({
			id: 'selected',
			header: () => createRender(Checkbox, { // renders the "select all"
                isSelected: selectAll
			}),
			cell: ({ row }, { pluginStates }) => {
                const { isSelected } = pluginStates.select.getRowState(row);
                return createRender(Checkbox, {
                    isSelected
				});
			},
			plugins: {
                sort: {
                    disable: true
				}
			}
		}));
	}
// ... continue initialising columns

and then subscribing to the newly created selectAll to cascade the changes to all other checkboxes:

const { selectedDataIds } = pluginStates.select;

    if (selectable) {
        selectAll.subscribe(val => {
            if (!val) selectedDataIds.clear();
            else {
                let rows = $pageRows;
                let newSelected = {};
                for (const row of rows) {
                    newSelected[row.id] = true;
                }

                $selectedDataIds = { ...newSelected };
            }
        });
	}

This works fine, but could run in to issues when trying to do things the other way around - ie. trying to select the "select all" option when all rows are selected. You could probably do something like:

selectedDataIds.subscribe(val => {
    if (val.length !== $pageRows.length) return;

    let newValue = true;
    for (const rowVal of val) {
        if (!rowVal) {
            newValue = false;
            break; // exit early
        }
    }

    $selectAll = newValue;
});

But you may run in to issues w/ constantly cascading changes between the 2 subscribe functions.

It'd be nice if there was a sane default for a select all option, especially if it were performant - I'm not too worried about the performance of the library, but for my use case actually these headless tables take up the bulk of the application so I do get slightly wary about having to implement some of these things.

Thanks again for the library, however, it's saved me countless hours over the last month (and will continue to do so)!

createRender and event dispatcher

Not sure how to read an event while using createRender. I have a dropdown menu which I could render like below

<UserActionsDropdown on:message={handleMessage}/> 

I want to dispatch an event from UserActionsDropdown and be able to handle in the table below. How would be possible?

table.column({
	header: '',
	id: 'selected',
	accessor: (item) => item,
	cell: ({ value }) => {
		return createRender(UserActionsDropdown, {
			userId: value.id
		});
	}
})

`addEditable` plugin

Ideally, it should expose update and set methods on the view model extensions for BodyCells to allow for easier updates.

It should also provide a callback / event listener for handling edits with more control.

It should also provide methods on the view model extensions to confirm / cancel edits.

Multi-editing can be a configuration option.

Server-side managed state control

We could provide plugin state to the developer without transforming the rows.

Plugin state can then be used to signal the server for changes to data.

Discussion on how to implement editable table

I just tried and fall in love with this library, thanks a lot.

I want suggestion or to discuss on how to implement editable-table better.

This is my REPL implementation, which feel like a hacky way.

  1. store (Writable) is in its own file and is imported by Table and Input component
  2. Input has 3 props: value, rowId and columnId
  3. cell use createRender to render Input and pass all props to Input component
  4. Input component then access store with $store and set value with $store[rowId][columnId] = value

Edit:
Current problem

  • No type definition for row and column to use in Input component
  • I'm not sure about accessing store value with $store[rowId][columnId] is the best way or should there be an alternative way.

Uncaught (in promise) Error: A column id or string accessor is required

Hi, I have just added your package and tried to create a very basic table as listed in the docs and I am getting error when my accessor is an function. Am I missing something ?

Uncaught (in promise) Error: A column id or string accessor is required
const data = writable([
        {
            id: "t1",
            description: "dsajk hskf alfgb akl asklfal fba",
            supplier: {
                id: "sup1",
                name: "supplier test",
            },
            amount: "2",
            unitOfMeasure: "ks",
            attachment: {
                id: "attachment1",
                name: "aaa aaaa aa aaaaa aaa.jpg",
            },
        },
    ])

    const table = createTable(data)

    const columns = table.createColumns([
        table.column({
            header: "Popis",
            accessor: "description",
        }),
        table.column({
            header: "Dodavatel",
            accessor: (item) => item.supplier.name, // <-- it fails here
        }),
        table.column({
            header: "Množství",
            accessor: "amount",
        }),
        table.column({
            header: "Měrná jednotka",
            accessor: "unitOfMeasure",
        }),
    ])

table undefined error.

Svelte kit Version: 1.0.1
Svelte version: 3.55.0

I copied the quick start code into a component and included that in a +page.svelte file.

I get the error cannot read properties of undefined (reading 'createTable'). When I followed the guide I altered it to suit my needs and that errored with "tableAttrs is not defined"

Is this a version issue? Or vite optimizing the module?

Thanks.

Clickable row?

Is any plugin to add click event to each row with its corresponding data as argument?

Plugin 'addGroupBy' + 'addExpandedRows' - Missing Children / Expand option if grouped by column - Issue

Hi,

I noticed the following issue when combining the two mentioned plugins on nested data:

  • Children / Expand option is missing for rows that are grouped by column, although items originally have children when not grouped

Example picture:
image

Example REPL:
https://svelte.dev/repl/320d8a80589a4c34950e335ae377f60f?version=3.50.1

In case that I overlooked something please give me a hint, thank you! (:

`usePagination` affects row count

usePagination paginates the table by transforming rows and producing a resulting row set that represents the page. However, the resulting row set should not represent the number of rows in the table.

This poses an issue when previous plugins or column definitions subscribe to TableState#rows. For example, a header render config might subscribe to TableState#rows to display the number of items in the table via TableState#rows.length. We expect TableState#rows.length to decrease if a filtering plugin is used i.e. useColumnFilters or useFilters. However, usePagination should not have an effect on TableState#rows.length.

Potential solutions

Expose page pluginState on usePagination

React Table takes the approach of adding a new page property on the table instance which does not affect rows, and directs users to use page to display the page instead. This allows TableState#rows to remain unaffected. We could recreate this by exposing a page store on usePagination's pluginState.

However, the plugin architecture at the moment does not allow for the table's full plugin definitions or types to be passed into individual plugins. This is related to a more general issue where plugins are processed in sequence; a plugin cannot access the state of plugins defined after it. As a result, we lose out on type definitions on any BodyRow stores accessed from pluginState.

Another issue is the loss of plugin state, prop sets, and attributes on table components accessed from pluginState. Plugin state and prop sets are only injected into the final transformed headers and rows to reduce the number of subscribers to each plugin's state. If we allow table components to be defined in pluginState, we would have to forgo this optimization. However, more work needs to be done to determine if this optimization is unnecessary.

One more issue with the current architecture is how pre-X rows are currently implemented. All pre-X rows are empty when initialized, and only set if the original rows property is accessed; pre-X rows are only set when deriveRows is called.

Differentiate between deriveRows and derivePage

We already define intrinsic structural properties for table components, and plan on adding more (e.g. children for row aggregation and expansion). It isn't entirely out of the picture to define derivePage as an intrinsic function on useTable much like deriveRows and deriveFlatColumns are. This would also be re-used when useTokenPagination is implemented.

Although this reduces the flexibility of the plugin system, it may be an acceptable compromise. Mandating more structure for the plugin system should reduce the complexity of the mental model and reduce any unwanted side effects.

Row animations

Hello,
I would like to add animations to my rows so that for example the sorting is nicely animated.

Right now I would need to remove <Subscribe> component hosting the table row tr

<tbody {...$tableBodyAttrs}>
    {#each $rows as row (row.id)}
//  <Subscribe rowAttrs={row.attrs()} let:rowAttrs> 
        <tr animate:flip={{delay: 0, duration: 200, easing: quintInOut}} class="group text-sm" {...row.attrs()}>
            {#each row.cells as cell (cell.id)}
                <Subscribe attrs={cell.attrs()} let:attrs>
                    <td class="group-hover:bg-neutral-100 px-4 first:pl-8 last:pr-4 py-2.5" {...attrs}>
                        <Render of={cell.render()}/>
                    </td>
                </Subscribe>
            {/each}
        </tr>
//   </Subscribe>
    {/each}
</tbody>

Is it even possible to achieve animations with the Subscribe component wrapping our tr row ?

On the other hand what exactly are we losing (and how critical it is) when we remove the Subscribe component ?

Add a "high level filter" plugin

One of the things this library is missing (but could probably add reasonably easily - he says...) is the ability to add what I will call "high level" table filters.

In short, these would be moreso "descriptive" filters that don't align with a specific column or existing filter. As an example, a high level filter may be:

name: High Value
filter: row => row.value > 700 && row.status !== 'refunded'

The idea is that you could apply many high level filters, acting as either an AND or an OR. These could be toggled at any time dynamically changing the available rows for export, viewing etc.

API examples

// sidenote: could either have an extra "name" field, or use "key" as the "name" AND the key
createTable(data, {
    highLevelFilters: addHighLevelFilters({
        method: 'AND', //AND, OR
        filters: [
            {
                key: 'high-value',
                fn: row => row.value > 700 && row.status !== 'refunded'
            },
            {
                key: 'low-value',
                fn: row => row.value < 700 && row.status !== 'refunded'
            },
            {
                 key: 'recent',
                 fn: row => row.date >= new Date((new Date().getDate()) - 7) //not _technically_ correct but just as an example
            }
    ])
});

// ... getting filters
const { appliedFilters, filterMethod, availableFilters } = pluginStates.highLevelFilters;

// ... toggle filter
appliedFilters.toggle('high-value');

// ... add filter
appliedFilters.add('high-value');

// ... remove filter
appliedFilters.remove('high-value');

// ... set filter method
$filterMethod = 'OR';

// ... render available filters
{#each $availableFilters as filter }
    <button on:click={() => appliedFilters.toggle(filter.key)}>toggle {filter.key}</button>
{/each}

Not sure how you would feel about something like this, but at least for my use case it'd be super helpful.

I can probably contribute this myself but I'm really not that great with TypeScript so might need someone to jump in and type-ify it for me.

Setup issues with pnpm(?)

I had some issues using the headless-table in a current SvelteKit project using PNPM.

I got it running after installing

  • svelte-render
  • svelte-subscribe

But there was no error that pointed to that solution.

Additionally I have a problem with typings. Importing the package in a svelte component with the lang="ts" attribute says
Could not find a declaration file for module 'svelte-headless-table'

Any ideas how to fix that? Thank you!

renderJSON fn for Table

Hi, this library is awesome, thanks for putting it together and sharing it!

Is there a shortcut approach to "exporting" the current Table state as a JSON collection? With all sorting and filters applied, and col state (hidden), derived cols, and col names as keys, etc?

If not, then any advice on how best to hit the API to construct a custom renderJSON() for the Table? Or just brute force it? ;-)

Scientific apps I build tend to require dumping table state as csv files, which I typically do from JSON collections created from stores and applied app state.

Many thanks!

-Larry

`addResizeColumns` plugin

This plugin should expose view model extensions to allow for easy drag-to-resize controls to resize columns.

It should also provide column options to size by px or %.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.