npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@get-set/gs-table

v1.0.3

Published

Get-Set Table — a beautiful, feature-complete data table / grid for vanilla JS, jQuery, prototype and React.

Downloads

367

Readme

@get-set/gs-table

A dependency-free, fully accessible (WAI-ARIA grid) data table / grid available in four usage modes from one codebase:

  • Native / vanilla JS — a window.GSTable(selector | element, params) factory plus a window.GSTableConfigue instance registry.
  • jQuery$(selector).GSTable(params) (auto-registered by the bundle).
  • Prototypeelement.GSTable(params) on HTMLElement.prototype (auto-registered by the bundle).
  • React — a <GSTable /> forwardRef component with a typed imperative GSTableHandle and a React-only scoped-styles gsx prop.

All four surfaces drive one shared core (actions/, constants/, helpers/, types/), so behaviour is identical — every option works in every target (except the few tagged React-only below).

Features

  • Multi-column sorting — click cycles none → asc → desc → none; enable multiSort to stack sorts with visible priority badges. Custom sortComparator per column. Beautiful animated sort-arrow.
  • Per-column filters + global searchtext, select (options inferred from the data or supplied), and number filters with >, <, >=, <=, = operators. Optional debounce.
  • Pagination modes — client-side pagination (with a page-size selector), load-more, or infinite scroll.
  • Row selectionsingle (radio) or multiple (checkbox) with a header select-all, isRowSelectable predicate, and getSelected() / getSelectedRows().
  • Expandable rows — per-row detail renderer, singleExpand mode, animated chevron + reveal.
  • Sticky header + pinnable columns — pin a column to the start / end edge with pinned: 'start' | 'end' (friendly alias of the legacy sticky); pinned columns stay fixed while the rest scroll horizontally, with correct cumulative offsets (including the checkbox / expander lead columns), an edge shadow, and RTL support. Scrollable body with maxHeight.
  • Responsive horizontal scroll — a too-wide table scrolls horizontally inside its own box (overflow-x: auto, touch-momentum on iOS) instead of breaking the page layout; columns keep a readable min-width.
  • Resizable columns (drag the divider) and reorderable columns (drag & drop headers).
  • Variantsstriped / bordered / hover / compact (stackable), plus a zebra shorthand.
  • Loading skeleton rows, a polished empty state, and per-row / per-cell relayout animations (all respect prefers-reduced-motion).
  • Custom cell + header renderers, rowKey, onRowClick, density (comfortable / cozy / compact) and size (sm / md / lg).
  • Row context menu — an optional, self-contained right-click menu per row (rowContextMenu array or per-row builder, onRowContextMenu) with submenus, separators, danger + disabled items, full keyboard + ARIA support. No extra dependency.
  • Light / dark / auto theming + custom accentColor, RTL, per-part customClass overrides, ARIA grid roles + full keyboard support, and a React-only scoped-styles gsx prop.
  • Rich callbacksonSort, onFilter, onPageChange, onSelectionChange, onExpandChange, onRowClick, onColumnResize, onColumnReorder.

Installation

npm i @get-set/gs-table

Compatibility

| Target | Requirement | |---|---| | React (the <GSTable> component) | React 16.8+ (Hooks + forwardRef), and 17 / 18 / 19. React is an optional peer dependency — you only need it for the component. | | Native / vanilla (window.GSTable) | No framework. Any modern evergreen browser. The optional jQuery ($(...).GSTable(...)) and HTMLElement.prototype.GSTable(...) adapters are registered automatically by the bundle. | | TypeScript | First-class — type declarations (.d.ts) ship in the package. | | SSR / Next.js | Safe to import server-side. Render inside a Client Component ('use client') — the table is a browser widget. |

The peer range is ^16.8.0 || ^17.0 || ^18.0 || ^19.0, with react / react-dom marked optional so the native build has zero dependencies.

Project layout

GSTable.ts                  # native entry (webpack -> dist-js/bundle.js, window global)
components/GSTable.tsx       # React component (tsc -> dist/, npm entry)
components/styles/GSTableCSS.ts   # CSS-in-TS injected by the React component
actions/                    # shared engine: init/render/interactions/destroy/refresh
helpers/                    # data pipeline, columns, layout, icons, ui helpers
constants/                  # defaults + size/density scales
types/                      # Params, Ref, GSTableColumn, global.d.ts
styles/GSTable.css          # stylesheet for the native / jQuery / prototype builds
example.html                # runnable native showcase

VANILLA (window factory)

The factory accepts a selector string OR a DOM element — the same signature the jQuery + prototype adapters call internally.

<link rel="stylesheet" href="node_modules/@get-set/gs-table/styles/GSTable.css" />
<div id="people"></div>
<script src="node_modules/@get-set/gs-table/dist-js/bundle.js"></script>
<script>
  // (a) selector string
  const grid = window.GSTable('#people', {
    reference: 'people',
    rowKey: 'id',
    searchable: true,
    selectable: 'multiple',
    pageSize: 10,
    variant: ['striped', 'hover'],
    columns: [
      { key: 'id', label: '#', width: 60, align: 'right' },
      { key: 'name', sortable: true, filterable: true },
      { key: 'team', sortable: true, filterable: true, filterType: 'select' },
      { key: 'salary', align: 'right', sortable: true,
        render: ({ value }) => '$' + value.toLocaleString() }
    ],
    data: [
      { id: 1, name: 'Ada Lovelace', team: 'Platform', salary: 145000 },
      { id: 2, name: 'Alan Turing',  team: 'Data',     salary: 138000 }
    ],
    onSort: (sort) => console.log('sort', sort),
    onSelectionChange: (keys, rows) => console.log('selected', keys, rows)
  });

  // (b) a DOM element works too
  const el = document.getElementById('people2');
  window.GSTable(el, { rowKey: 'id', columns, data });
</script>

Instance registry

Every instance is registered under its reference (auto-generated when omitted):

window.GSTable('#people', { reference: 'people', columns, data });

const grid = window.GSTableConfigue.instance('people'); // -> the Ref
grid.sortBy('salary', 'desc');
grid.setPage(2);
console.log(grid.getSelectedRows());
grid.destroy();

JQUERY

Loaded automatically by the bundle when window.jQuery is present. It calls the factory once per matched element (passing the element, not the selector).

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="node_modules/@get-set/gs-table/dist-js/bundle.js"></script>
<script>
  $('#people').GSTable({ rowKey: 'id', columns, data, searchable: true });
</script>

PROTOTYPE

Also registered by the bundle — call it directly on any element:

document.getElementById('people').GSTable({ rowKey: 'id', columns, data });

REACT

'use client';
import React, { useRef } from 'react';
import GSTable, { GSTableHandle } from '@get-set/gs-table';

interface Person { id: number; name: string; team: string; salary: number; }

const data: Person[] = [
  { id: 1, name: 'Ada Lovelace', team: 'Platform', salary: 145000 },
  { id: 2, name: 'Alan Turing',  team: 'Data',     salary: 138000 }
];

export default function People() {
  const ref = useRef<GSTableHandle<Person>>(null);

  return (
    <>
      <button onClick={() => ref.current?.selectAll()}>Select all</button>
      <button onClick={() => ref.current?.sort('salary', 'desc')}>Sort by pay</button>

      <GSTable<Person>
        ref={ref}
        rowKey="id"
        data={data}
        searchable
        selectable="multiple"
        expandable
        variant={['striped', 'hover']}
        theme="auto"
        accentColor="#7c3aed"
        pageSize={10}
        columns={[
          { key: 'id', label: '#', width: 60, align: 'right' },
          { key: 'name', sortable: true, filterable: true },
          { key: 'team', sortable: true, filterable: true, filterType: 'select' },
          { key: 'salary', align: 'right', sortable: true,
            render: ({ value }) => <b>${Number(value).toLocaleString()}</b> }
        ]}
        expandedRender={(row) => <div>Full profile for {row.name}</div>}
        onSelectionChange={(keys, rows) => console.log(keys, rows)}
        // React-only: scoped inline styles (auto-removed on unmount)
        gsx={{
          '.gs-table-th': { textTransform: 'uppercase', letterSpacing: '0.04em' },
          '.gs-table-row:hover .gs-table-td': { background: '#faf5ff' }
        }}
      />
    </>
  );
}

The CSS is injected automatically by the component (once per document) — no stylesheet import is required in the React build. The styles are also available as a string at @get-set/gs-table/dist/components/styles/GSTableCSS if you want to inline them yourself.

GSTableHandle (React ref)

| Method | Description | |---|---| | sort(key, dir?) | Toggle / set a column's sort. dir omitted cycles asc → desc → none. | | setSort(sort) | Replace the full sort list ({ key, dir }[]). | | getSort() | The active sort list. | | filter(key, value) | Set a single column filter (empty string clears it). | | setFilters(filters) | Replace all per-column filters at once. | | getFilters() | The active filters ({ [key]: value }). | | setSearch(term) | Set the global search term. | | setPage(page) | Go to a 1-based page (clamped to range). | | setPageSize(size) | Change page size (resets to page 1). | | getPage() | The current 1-based page. | | getPageCount() | Total pages for the current filtered set. | | getSelected() | The selected row keys. | | getSelectedRows() | The selected row objects. | | selectAll() | Select every (selectable) row in the current filtered set. | | clearSelection() | Clear the selection. | | toggleRow(key) | Toggle a single row's selection by key. | | toggleExpand(key) | Toggle a single row's expansion by key. | | getRows() | The filtered + sorted rows (all pages). | | refresh() | Force a re-render from current state. |

Native instance methods (window.GSTable(...) return value / GSTableConfigue.instance)

The native Ref mirrors the React handle and adds a few native-only helpers:

| Method | Description | |---|---| | sortBy(key, dir?) | Toggle / set a column's sort (cycles when dir omitted). | | setSort(sort) / getSort() | Replace / read the sort list. | | filter(key, value) / setFilters(f) / getFilters() | Column filters. | | setSearch(term) | Global search. | | setPage(page) / setPageSize(size) / getPage() / getPageCount() | Pagination. | | getSelected() / getSelectedRows() | Selection keys / objects. | | selectAll() / clearSelection() / toggleRow(key) | Selection control. | | toggleExpand(key) | Expand / collapse a row. | | setData(rows) | Native-only — replace the row data and re-render. | | getRows() | Filtered + sorted rows (all pages). | | setLoading(on) | Native-only — toggle the skeleton loading state. | | refresh() | Re-render from current state (call after mutating ref.params). | | destroy() | Tear down, restore the container, and unregister. |


Props / options

Every option below is accepted by all four targets unless tagged. Numbers supplied to size options are treated as px.

Data & columns

| Option | Type | Default | Description | |---|---|---|---| | columns | GSTableColumn[] | [] | Column definitions (see below). | | data | T[] | [] | Row data. | | rowKey | string \| (row, i) => string \| number | row index | Stable key per row. | | reference | string | auto GUID | Registry key. |

GSTableColumn

| Field | Type | Description | |---|---|---| | key | string | Required. Unique key; also the property read from each row. | | label | string | Header text. Defaults to a humanized key. | | width / minWidth / maxWidth | number \| string | Column sizing. | | align | 'left' \| 'center' \| 'right' | Cell + header alignment. Default left. | | sortable | boolean | Participate in sorting. Default true. | | filterable | boolean | Show a per-column filter box. Default false. | | filterType | 'text' \| 'select' \| 'number' \| 'none' | Filter control kind. Default text. | | filterOptions | Array<string \| {label,value}> | Explicit select options (else inferred). | | pinned | boolean \| 'start' \| 'end' | Pin (horizontal-sticky) the column to the start or end edge while scrolling horizontally. true pins to the start. Default unpinned. Friendly alias of sticky; pinned wins if both are set. Set it in the column definition to pin by default. | | pinnable | boolean | Optional: let the user toggle this column's pin state at runtime. Default false; the declarative pinned works without it. | | sticky | boolean \| 'start' \| 'end' | Legacy alias of pinned — pin the column while scrolling horizontally. | | resizable | boolean | Allow drag-resize of this column. | | hidden | boolean | Keep in data but do not render. | | accessor | (row) => unknown | Derive the cell value. | | render | (ctx) => ReactNode \| string | Custom cell renderer (React node or HTML string). | | headerRender | (col) => ReactNode \| string | Custom header renderer. | | sortComparator | (a, b, rowA, rowB) => number | Custom sort comparator. | | className / headerClassName | string | Extra classes on cells / header. |

The cell render context is { value, row, rowIndex, column, rowKey }.

Sorting

| Option | Type | Default | Description | |---|---|---|---| | sortable | boolean | true | Enable sorting globally. | | multiSort | boolean | false | Stack sorts across columns (priority badges). | | sort | GSTableSort[] | — | Controlled sort state ({ key, dir }[]). | | defaultSort | GSTableSort[] | — | Uncontrolled initial sort. |

Filtering & search

| Option | Type | Default | Description | |---|---|---|---| | filterable | boolean | true | Show the filter row when any column is filterable. | | filters | Record<string,string> | — | Controlled per-column filters. | | defaultFilters | Record<string,string> | — | (React) uncontrolled initial filters. | | searchable | boolean | false | Show the global search box. | | search | string | — | Controlled search term. | | defaultSearch | string | — | (React) uncontrolled initial term. | | searchPlaceholder | string | 'Search…' | Search input placeholder. | | filterDebounce | number | 0 | Debounce (ms) for filter/search input. |

Pagination

| Option | Type | Default | Description | |---|---|---|---| | pagination | 'pagination' \| 'load-more' \| 'infinite' \| false | 'pagination' | Paging mode. false shows all rows. | | pageSize | number | 10 | Rows per page. | | pageSizeOptions | number[] | [10, 25, 50, 100] | Page-size selector options. | | showPageSizeSelector | boolean | true | Show the page-size selector. | | page | number | 1 | Controlled 1-based page. | | defaultPage | number | 1 | (React) uncontrolled initial page. | | loadMoreText | string | 'Load more' | Load-more button label. |

Selection

| Option | Type | Default | Description | |---|---|---|---| | selectable | false \| 'single' \| 'multiple' | false | Row selection mode. | | showSelectAll | boolean | true | Header select-all checkbox (multiple mode). | | selected | Array<string \| number> | — | Controlled selected keys. | | defaultSelected | Array<string \| number> | — | (React) uncontrolled initial selection. | | isRowSelectable | (row, i) => boolean | — | Mark rows as unselectable. | | selectionActions | GSTableSelectionAction[] | — | Bulk actions shown in the selection bar while rows are selected. | | selectionBarLabel | (count) => string | `${n} selected` | Custom count label for the selection bar. |

Selection bar (bulk actions)

When selectable is on and selectionActions is a non-empty array, selecting one or more rows shows a selection bar above the table: the selected count, one button per action, and a clear (×) button.

Each GSTableSelectionAction:

| Field | Type | Description | |---|---|---| | id | string | Optional stable id. | | label | string | Button label. | | icon | string (native HTML) / ReactNode (React) | Leading icon. | | danger | boolean | Destructive (red) styling. | | disabled | boolean \| (keys, rows) => boolean | Disable statically or per selection. | | onClick | (keys, rows, ctx) => void | Receives the selected keys + rows; ctx.clear() clears the selection. |

<GSTable
  columns={columns}
  data={data}
  rowKey="id"
  selectable="multiple"
  selectionActions={[
    { label: 'Export', icon: '⬇', onClick: (keys, rows) => exportRows(rows) },
    { label: 'Merge', disabled: (keys) => keys.length < 2, onClick: merge },
    { label: 'Delete', danger: true, onClick: (keys, _rows, ctx) => { removeAll(keys); ctx.clear(); } }
  ]}
/>

Vanilla works the same way — pass selectionActions in the params object.

Expandable rows

| Option | Type | Default | Description | |---|---|---|---| | expandable | boolean | false | Enable expandable detail rows. | | expandedRender | (row, i) => ReactNode \| string | — | Detail renderer. | | expanded | Array<string \| number> | — | Controlled expanded keys. | | defaultExpanded | Array<string \| number> | — | (React) uncontrolled initial expanded. | | singleExpand | boolean | false | Only one row expanded at a time. |

Sticky · resizable · reorderable

| Option | Type | Default | Description | |---|---|---|---| | stickyHeader | boolean | true | Pin the header while the body scrolls. When on, the per-column filter row is pinned right below the header (its offset is measured from the real header height, so it never overlaps the first data rows). | | maxHeight | number \| string | — | Body height before it scrolls. | | resizableColumns | boolean | false | Enable drag-resize on all columns. | | reorderableColumns | boolean | false | Enable drag-and-drop header reordering. |

Pinning columns. Pin individual columns with the per-column pinned option (see GSTableColumn). The table always sits in a horizontally-scrollable viewport (overflow-x: auto), so pinned columns stay fixed at the start / end edge while the rest scroll under them. Pinned offsets are cumulative and include the selection-checkbox / expander lead columns. Pinning respects RTL (start = right) and works in both the native and React targets.

Appearance

| Option | Type | Default | Description | |---|---|---|---| | variant | GSTableVariant \| GSTableVariant[] | — | Visual variants (stack). | | zebra | boolean | false | Shorthand for the striped variant. | | density | 'comfortable' \| 'cozy' \| 'compact' | 'comfortable' | Row + control density. | | size | 'sm' \| 'md' \| 'lg' | 'md' | Font / control scale. | | theme | 'light' \| 'dark' \| 'auto' | 'auto' | Visual theme (auto follows the OS). | | accentColor | string | #2563eb | Accent (CSS var --gst-accent). | | rtl | boolean | false | Right-to-left layout. | | caption | string | — | Table caption (visible + screen-reader). | | customClass | GSTableCustomClass | — | Per-part class overrides. | | className | string | — | Extra class on the root. |

State: loading / empty

| Option | Type | Default | Description | |---|---|---|---| | loading | boolean | false | Show skeleton loading rows. | | skeletonRows | number | pageSize | Number of skeleton rows. | | emptyText | string | 'No records to display' | Empty-state message. | | emptyRender | ReactNode \| string | — | Custom empty-state content. |

React-only

| Option | Type | Description | |---|---|---| | gsx | NestedCSS | Scoped inline styles injected under [data-key], removed on unmount. |

Callbacks (honored in every target)

| Callback | Signature | |---|---| | onSort | (sort: GSTableSort[]) => void | | onFilter | (filters, search) => void | | onPageChange | (info: { page, pageSize, totalRows, totalPages }) => void | | onSelectionChange | (keys, rows) => void | | onExpandChange | (expanded: Array<string \| number>) => void | | onRowClick | (row, index, event) => void | | onColumnResize | (key, width) => void | | onColumnReorder | (order: string[]) => void | | onRowContextMenu | (row, index, event) => void |


Row context menu

GSTable ships an optional, self-contained per-row context menu. It has no dependency on any other @get-set package — the menu (positioning, keyboard, a11y, animation) is built in. Enable it by supplying rowContextMenu (a static item list or a per-row builder) and/or onRowContextMenu.

Behaviour (identical in the native and React targets):

  • Right-clicking a data row (not the header / filter row) fires onRowContextMenu(row, index, event).
  • If rowContextMenu yields a non-empty array, the native browser menu is suppressed (preventDefault) and the built-in menu opens at the cursor, with viewport flip (opens up / left near the edges) and shift-into-view. One level of children submenus is supported (flips left on overflow).
  • The menu dismisses on outside pointer-down, Escape, scroll, or window blur. Clicking an item runs its onClick(row, index, { close }) then closes.
  • Keyboard: / move the highlight (skipping disabled + separators, with wrap), Home/End jump to the first / last item, Enter/Space activate, / open / close a submenu (inverted in RTL), Escape closes. Focus moves into the menu on open and is restored to the previously focused element on close.
  • a11y: role="menu" on the container, role="menuitem" on items, role="separator" on dividers, aria-disabled on disabled items.
  • Honors the table theme tokens (accentColor, theme, rtl, size, density) and prefers-reduced-motion for the entrance animation.

GSTableMenuItem schema

| Field | Type | Description | |---|---|---| | id | string | Optional stable id. | | label | string | Visible label. | | icon | HTML string (native) / ReactNode (React) | Leading icon. | | shortcut | string | Trailing shortcut hint (non-interactive). | | disabled | boolean | Shown but not interactive. | | danger | boolean | Render in the destructive color. | | separator | boolean | Render as a divider (also '-' shorthand). | | onClick | (row, index, { close }) => void | Activation handler. | | children | GSTableMenuItem[] | One nested submenu level. |

rowContextMenu is either an array of items (a plain '-' string is a separator shorthand) or a builder (row, index) => items | false | null | undefined. Returning false / null / undefined / [] suppresses the menu for that row (but onRowContextMenu still fires).

Vanilla example

window.GSTable('#grid', {
  columns,
  data,
  rowKey: 'id',
  onRowContextMenu: (row, index) => console.log('right-clicked', row, index),
  rowContextMenu: (row) => [
    { label: 'Edit', icon: '✏️', shortcut: 'E', onClick: (r) => edit(r) },
    {
      label: 'Move to',
      children: [
        { label: 'Inbox', onClick: (r) => moveTo(r, 'inbox') },
        { label: 'Archive', onClick: (r) => moveTo(r, 'archive') }
      ]
    },
    '-',
    {
      label: 'Delete',
      danger: true,
      disabled: row.locked === true,
      onClick: (r, i, { close }) => {
        remove(r);
        close();
      }
    }
  ]
});

React example

<GSTable
  columns={columns}
  data={data}
  rowKey="id"
  onRowContextMenu={(row, index) => console.log(row, index)}
  rowContextMenu={(row) => [
    { label: 'Edit', icon: <EditIcon />, shortcut: 'E', onClick: (r) => edit(r) },
    { label: 'Duplicate', onClick: (r) => duplicate(r) },
    '-',
    {
      label: 'Delete',
      danger: true,
      onClick: (r, i, { close }) => {
        remove(r);
        close();
      }
    }
  ]}
/>

Variant catalog

Variants are stackable — pass an array (variant={['striped', 'hover', 'bordered']}).

| Variant | Effect | |---|---| | striped | Zebra row backgrounds (same as zebra: true). | | bordered | Vertical borders between every cell. | | hover | Row highlight on hover. | | compact | Tighter padding (same as density: 'compact'). |

Density & size

| density | Row padding | |---|---| | comfortable | Roomy (default). | | cozy | Medium. | | compact | Tight. |

| size | Font scale | |---|---| | sm | 0.8125rem | | md | 0.9375rem (default) | | lg | 1.0625rem |

Filter types

| filterType | Behaviour | |---|---| | text | Case-insensitive substring match (default). | | select | Exact match against a dropdown (options inferred or via filterOptions). | | number | Numeric with >, <, >=, <=, = operators (e.g. >=100). | | none | No filter control. |

customClass parts

root, toolbar, search, table, thead, headerRow, headerCell, filterRow, body, row, cell, footer, pagination, empty, loading.

Accessibility & keyboard

  • The table uses role="grid"; sortable headers expose aria-sort and are focusable (tabindex="0").
  • Enter / Space on a focused header toggles its sort.
  • Selection controls carry aria-labels; selected rows set aria-selected.
  • Expand toggles set aria-expanded.
  • All animations honor prefers-reduced-motion.

Changelog

Unreleased

  • Pinnable columns. New per-column pinned (boolean | 'start' | 'end', a friendly alias of sticky) and optional pinnable runtime-toggle flag. Pinned columns are horizontal-sticky with correct cumulative offsets (counting the selection-checkbox / expander lead columns), an edge shadow, and RTL support — in both native and React.
  • Responsive horizontal scroll. The table viewport now always scrolls horizontally (overflow-x: auto, -webkit-overflow-scrolling: touch) with a readable min-width, so a wide table no longer breaks the page layout. This viewport is the containing block for pinned columns.
  • Fix: filter row no longer overlaps the top data rows. With a sticky header, the sticky per-column filter row's top is measured from the real header-row height (--gst-header-h) instead of a fixed guess, so the body starts cleanly beneath it.
  • Fix: unselectable rows are no longer painted as selected. A row for which isRowSelectable returns false now gets a muted gs-table-row-unselectable style and can never carry the selected-row highlight.

License

ISC © Get-Set