react-hook-tanstack-table 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.org +29 -0
  3. package/dist/TableContext.cjs +6 -0
  4. package/dist/TableContext.d.cts +7 -0
  5. package/dist/TableContext.d.cts.map +1 -0
  6. package/dist/TableContext.d.ts +7 -0
  7. package/dist/TableContext.d.ts.map +1 -0
  8. package/dist/TableContext.js +8 -0
  9. package/dist/TableContext.js.map +1 -0
  10. package/dist/hasTableArg-Bu3FbcSo.js +8 -0
  11. package/dist/hasTableArg-Bu3FbcSo.js.map +1 -0
  12. package/dist/hasTableArg-dMtayKWH.cjs +11 -0
  13. package/dist/index.cjs +40 -0
  14. package/dist/index.d.cts +11 -0
  15. package/dist/index.d.ts +11 -0
  16. package/dist/index.js +11 -0
  17. package/dist/invariant-BgWq7zZS.js +12 -0
  18. package/dist/invariant-BgWq7zZS.js.map +1 -0
  19. package/dist/invariant-D8b385d_.cjs +21 -0
  20. package/dist/isShallowEqual-D6PQ041u.cjs +36 -0
  21. package/dist/isShallowEqual-Kynsoe7a.js +33 -0
  22. package/dist/isShallowEqual-Kynsoe7a.js.map +1 -0
  23. package/dist/runGetters-BfRTE7LZ.js +16 -0
  24. package/dist/runGetters-BfRTE7LZ.js.map +1 -0
  25. package/dist/runGetters-Q-UsPG2j.cjs +19 -0
  26. package/dist/types-DIWB6wdO.d.cts +11 -0
  27. package/dist/types-DIWB6wdO.d.cts.map +1 -0
  28. package/dist/types-DIWB6wdO.d.ts +11 -0
  29. package/dist/types-DIWB6wdO.d.ts.map +1 -0
  30. package/dist/useCell.cjs +53 -0
  31. package/dist/useCell.d.cts +17 -0
  32. package/dist/useCell.d.cts.map +1 -0
  33. package/dist/useCell.d.ts +17 -0
  34. package/dist/useCell.d.ts.map +1 -0
  35. package/dist/useCell.js +53 -0
  36. package/dist/useCell.js.map +1 -0
  37. package/dist/useColumn.cjs +54 -0
  38. package/dist/useColumn.d.cts +19 -0
  39. package/dist/useColumn.d.cts.map +1 -0
  40. package/dist/useColumn.d.ts +19 -0
  41. package/dist/useColumn.d.ts.map +1 -0
  42. package/dist/useColumn.js +54 -0
  43. package/dist/useColumn.js.map +1 -0
  44. package/dist/useHeader.cjs +48 -0
  45. package/dist/useHeader.d.cts +19 -0
  46. package/dist/useHeader.d.cts.map +1 -0
  47. package/dist/useHeader.d.ts +19 -0
  48. package/dist/useHeader.d.ts.map +1 -0
  49. package/dist/useHeader.js +48 -0
  50. package/dist/useHeader.js.map +1 -0
  51. package/dist/useReactTable-Y2z5JrT_.js +70 -0
  52. package/dist/useReactTable-Y2z5JrT_.js.map +1 -0
  53. package/dist/useReactTable-nxQRPiMU.cjs +95 -0
  54. package/dist/useReactTable.cjs +3 -0
  55. package/dist/useReactTable.d.cts +10 -0
  56. package/dist/useReactTable.d.cts.map +1 -0
  57. package/dist/useReactTable.d.ts +10 -0
  58. package/dist/useReactTable.d.ts.map +1 -0
  59. package/dist/useReactTable.js +2 -0
  60. package/dist/useRow.cjs +54 -0
  61. package/dist/useRow.d.cts +19 -0
  62. package/dist/useRow.d.cts.map +1 -0
  63. package/dist/useRow.d.ts +19 -0
  64. package/dist/useRow.d.ts.map +1 -0
  65. package/dist/useRow.js +54 -0
  66. package/dist/useRow.js.map +1 -0
  67. package/dist/useTable.cjs +44 -0
  68. package/dist/useTable.d.cts +11 -0
  69. package/dist/useTable.d.cts.map +1 -0
  70. package/dist/useTable.d.ts +11 -0
  71. package/dist/useTable.d.ts.map +1 -0
  72. package/dist/useTable.js +44 -0
  73. package/dist/useTable.js.map +1 -0
  74. package/dist/useTableWithSelector-Bdsf6Zxk.cjs +79 -0
  75. package/dist/useTableWithSelector-D3f689pF.js +70 -0
  76. package/dist/useTableWithSelector-D3f689pF.js.map +1 -0
  77. package/dist/useTableWithSelector.cjs +3 -0
  78. package/dist/useTableWithSelector.d.cts +8 -0
  79. package/dist/useTableWithSelector.d.cts.map +1 -0
  80. package/dist/useTableWithSelector.d.ts +8 -0
  81. package/dist/useTableWithSelector.d.ts.map +1 -0
  82. package/dist/useTableWithSelector.js +2 -0
  83. package/package.json +99 -0
  84. package/src/contexts/TableContext.ts +7 -0
  85. package/src/hooks/useCell.ts +156 -0
  86. package/src/hooks/useColumn.ts +168 -0
  87. package/src/hooks/useHeader.ts +151 -0
  88. package/src/hooks/useReactTable.ts +88 -0
  89. package/src/hooks/useRow.ts +132 -0
  90. package/src/hooks/useTable.ts +113 -0
  91. package/src/hooks/useTableWithSelector.ts +143 -0
  92. package/src/index.ts +13 -0
  93. package/src/lib/hasTableArg.ts +19 -0
  94. package/src/lib/identity.ts +1 -0
  95. package/src/lib/invariant.ts +12 -0
  96. package/src/lib/isShallowEqual.ts +77 -0
  97. package/src/lib/runGetters.ts +17 -0
  98. package/src/lib/tableRegistry.ts +3 -0
  99. package/src/lib/uncapitalize.ts +2 -0
  100. package/src/lib/useLayoutEffect.ts +3 -0
  101. package/src/lib/useShallowMemo.ts +12 -0
  102. package/src/types.ts +24 -0
@@ -0,0 +1,113 @@
1
+ import { useCallback } from "react"
2
+
3
+ import type {
4
+ RequiredKeys,
5
+ RowData,
6
+ Table,
7
+ TableOptionsResolved,
8
+ } from "@tanstack/table-core"
9
+
10
+ import { identity } from "../lib/identity"
11
+ import { runGetters } from "../lib/runGetters"
12
+ import { isShallowEqual } from "../lib/isShallowEqual"
13
+
14
+ import type { IsEqual, RunGetters } from "../types"
15
+
16
+ import { useTableWithSelector } from "./useTableWithSelector"
17
+
18
+ export interface TableValues<TData extends RowData> extends RunGetters<
19
+ Table<TData>
20
+ > {}
21
+
22
+ const tableValuesCache = new WeakMap<
23
+ RequiredKeys<TableOptionsResolved<any>, "state">,
24
+ TableValues<any>
25
+ >()
26
+
27
+ const tableHandlersCache = new WeakMap<
28
+ Table<any>,
29
+ {
30
+ toggleAllColumnsVisibilityHandler: ReturnType<
31
+ Table<any>["getToggleAllColumnsVisibilityHandler"]
32
+ >
33
+ toggleAllPageRowsSelectedHandler: ReturnType<
34
+ Table<any>["getToggleAllPageRowsSelectedHandler"]
35
+ >
36
+ toggleAllRowsExpandedHandler: ReturnType<
37
+ Table<any>["getToggleAllRowsExpandedHandler"]
38
+ >
39
+ toggleAllRowsSelectedHandler: ReturnType<
40
+ Table<any>["getToggleAllRowsSelectedHandler"]
41
+ >
42
+ }
43
+ >()
44
+
45
+ const getTableValues = <TData extends RowData>(
46
+ table: Table<TData>,
47
+ ): TableValues<TData> => {
48
+ let cached = tableValuesCache.get(table.options)
49
+ if (!cached) {
50
+ let cachedHandlers = tableHandlersCache.get(table)
51
+ if (!cachedHandlers) {
52
+ cachedHandlers = {
53
+ toggleAllColumnsVisibilityHandler:
54
+ table.getToggleAllColumnsVisibilityHandler(),
55
+ toggleAllPageRowsSelectedHandler:
56
+ table.getToggleAllPageRowsSelectedHandler(),
57
+ toggleAllRowsExpandedHandler: table.getToggleAllRowsExpandedHandler(),
58
+ toggleAllRowsSelectedHandler: table.getToggleAllRowsSelectedHandler(),
59
+ }
60
+ tableHandlersCache.set(table, cachedHandlers)
61
+ }
62
+
63
+ const {
64
+ toggleAllColumnsVisibilityHandler,
65
+ toggleAllPageRowsSelectedHandler,
66
+ toggleAllRowsExpandedHandler,
67
+ toggleAllRowsSelectedHandler,
68
+ } = cachedHandlers
69
+
70
+ const { getColumn, getRow, ...rest } = table
71
+
72
+ cached = {
73
+ ...runGetters(rest),
74
+ getColumn,
75
+ getRow,
76
+ toggleAllColumnsVisibilityHandler,
77
+ toggleAllPageRowsSelectedHandler,
78
+ toggleAllRowsExpandedHandler,
79
+ toggleAllRowsSelectedHandler,
80
+ }
81
+ tableValuesCache.set(table.options, cached)
82
+ }
83
+ return cached
84
+ }
85
+
86
+ type Selector<TData extends RowData, Selection> = (
87
+ tableValues: TableValues<TData>,
88
+ ) => Selection
89
+
90
+ export const useTable = <TData extends RowData, Selection = TableValues<TData>>(
91
+ table?: Table<TData> | undefined,
92
+ selector: Selector<TData, Selection> = identity as never,
93
+ isEqual: IsEqual<NoInfer<Selection>> = isShallowEqual,
94
+ ): Selection => {
95
+ const getSelection = useCallback(
96
+ (table: Table<TData>) => selector(getTableValues(table)),
97
+ [selector],
98
+ )
99
+
100
+ return useTableWithSelector(table, getSelection, isEqual)
101
+ }
102
+
103
+ const tableHook = useTable
104
+
105
+ const tableHookʹ =
106
+ <TData extends RowData>(table?: Table<TData> | undefined) =>
107
+ <Selection = TableValues<TData>>(
108
+ selector: Selector<TData, Selection> = identity as never,
109
+ isEqual: IsEqual<NoInfer<Selection>> = isShallowEqual,
110
+ ): Selection =>
111
+ tableHook(table, selector, isEqual)
112
+
113
+ export { tableHookʹ as useTableʹ }
@@ -0,0 +1,143 @@
1
+ import {
2
+ use,
3
+ useCallback,
4
+ useInsertionEffect,
5
+ useMemo,
6
+ useRef,
7
+ useSyncExternalStore,
8
+ } from "react"
9
+
10
+ import type {
11
+ RequiredKeys,
12
+ RowData,
13
+ Table,
14
+ TableOptionsResolved,
15
+ } from "@tanstack/table-core"
16
+
17
+ import { TableContext } from "../contexts/TableContext"
18
+
19
+ import { identity } from "../lib/identity"
20
+ import { tableRegistry } from "../lib/tableRegistry"
21
+ import { invariant } from "../lib/invariant"
22
+
23
+ import type { IsEqual } from "../types"
24
+
25
+ export const useTableWithSelector = <
26
+ TData extends RowData,
27
+ Selection = Table<TData>,
28
+ >(
29
+ table?: Table<TData> | undefined,
30
+ selector: (table: Table<TData>) => Selection = identity as never,
31
+ isEqual?: IsEqual<NoInfer<Selection>> | undefined,
32
+ ): Selection => {
33
+ "use no memo"
34
+
35
+ const resolvedTable = table ?? use<Table<TData> | null>(TableContext)
36
+
37
+ invariant(
38
+ resolvedTable,
39
+ "`useTableWithSelector` must be used as a descendent of `TableContext` or provided with a `Table` as an argument!",
40
+ )
41
+
42
+ const listeners = useMemo(
43
+ () => (tableRegistry.get(resolvedTable) ?? {}).listeners,
44
+ [resolvedTable],
45
+ )
46
+
47
+ invariant(
48
+ listeners,
49
+ "The provided/found `Table` instance cannot be subscribed to. Was it created by the correct `useReactTable` hook?",
50
+ )
51
+
52
+ const subscribe = useCallback(
53
+ (onTableChange: () => void) => {
54
+ listeners.add(onTableChange)
55
+ return () => {
56
+ listeners.delete(onTableChange)
57
+ }
58
+ },
59
+ [listeners],
60
+ )
61
+
62
+ // Use this to track the rendered snapshot.
63
+ const instRef = useRef<
64
+ | {
65
+ hasValue: false
66
+ value: null
67
+ }
68
+ | { hasValue: true; value: Selection }
69
+ >({
70
+ hasValue: false,
71
+ value: null,
72
+ })
73
+
74
+ // eslint-disable-next-line react-hooks/immutability
75
+ const getSnapshot = useMemo(() => {
76
+ let hasMemo = false,
77
+ memoizedTableOptions: RequiredKeys<TableOptionsResolved<TData>, "state">,
78
+ memoizedSelection: Selection
79
+
80
+ return () => {
81
+ const nextTableOptions = resolvedTable.options
82
+ if (!hasMemo) {
83
+ // The first time the hook is called, there is no memoized result.
84
+ // eslint-disable-next-line react-hooks/immutability -- should be fine 😅 this is how it is in the upstream, too
85
+ hasMemo = true
86
+ memoizedTableOptions = nextTableOptions
87
+ const nextSelection = selector(resolvedTable)
88
+
89
+ if (isEqual !== undefined) {
90
+ // Even if the selector has changed, the currently rendered selection
91
+ // may be equal to the new selection. We should attempt to reuse the
92
+ // current value if possible, to preserve downstream memoizations.
93
+ if (instRef.current.hasValue) {
94
+ const currentSelection = instRef.current.value
95
+ if (isEqual(currentSelection, nextSelection)) {
96
+ memoizedSelection = currentSelection
97
+ return currentSelection
98
+ }
99
+ }
100
+ }
101
+
102
+ memoizedSelection = nextSelection
103
+ return nextSelection
104
+ }
105
+
106
+ const prevTableOptions = memoizedTableOptions
107
+ // eslint-disable-next-line react-hooks/memo-dependencies
108
+ const prevSelection = memoizedSelection
109
+
110
+ if (Object.is(prevTableOptions, nextTableOptions)) {
111
+ // The snapshot is the same as last time. Reuse the previous selection.
112
+ return prevSelection
113
+ }
114
+
115
+ // The snapshot has changed, so we need to compute a new selection.
116
+ const nextSelection = selector(resolvedTable)
117
+
118
+ // If a custom isEqual function is provided, use that to check if the data
119
+ // has changed. If it hasn't, return the previous selection. That signals
120
+ // to React that the selections are conceptually equal, and we can bail
121
+ // out of rendering.
122
+ if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) {
123
+ // The snapshot still has changed, so make sure to update to not keep
124
+ // old references alive
125
+ memoizedTableOptions = nextTableOptions
126
+ return prevSelection
127
+ }
128
+
129
+ memoizedTableOptions = nextTableOptions
130
+ memoizedSelection = nextSelection
131
+ return nextSelection
132
+ }
133
+ }, [selector, resolvedTable, isEqual])
134
+
135
+ const value = useSyncExternalStore(subscribe, getSnapshot)
136
+
137
+ useInsertionEffect(() => {
138
+ instRef.current.hasValue = true
139
+ instRef.current.value = value
140
+ }, [value])
141
+
142
+ return value
143
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export * from '@tanstack/table-core'
2
+ export * from '@tanstack/react-table'
3
+
4
+ export { TableContext } from "./contexts/TableContext"
5
+
6
+ export { useReactTable } from "./hooks/useReactTable"
7
+
8
+ export { useTableWithSelector } from './hooks/useTableWithSelector'
9
+ export { useTable, useTableʹ } from "./hooks/useTable"
10
+ export { useHeader, useHeaderʹ } from "./hooks/useHeader"
11
+ export { useColumn, useColumnʹ } from "./hooks/useColumn"
12
+ export { useRow, useRowʹ } from "./hooks/useRow"
13
+ export { useCell, useCellʹ } from "./hooks/useCell"
@@ -0,0 +1,19 @@
1
+ import type {
2
+ RowData,
3
+ Table,
4
+ } from "@tanstack/table-core"
5
+
6
+ import { tableRegistry } from "./tableRegistry"
7
+
8
+ const isTable = <TData extends RowData>(
9
+ x: unknown | Table<TData>,
10
+ ): x is Table<TData> =>
11
+ typeof x === "object" && x !== null && tableRegistry.has(x as Table<TData>)
12
+
13
+ export const hasTableArg = <
14
+ TData extends RowData,
15
+ Rest extends [{}, ...unknown[]],
16
+ >(
17
+ args: [Table<TData> | undefined, ...Rest] | Rest,
18
+ ): args is [Table<TData> | undefined, ...Rest] =>
19
+ isTable(args[0]) || args[0] === undefined
@@ -0,0 +1 @@
1
+ export const identity = <T>(x: T, ..._: any[]): T => x
@@ -0,0 +1,12 @@
1
+ export function invariant(
2
+ condition: any,
3
+ message?: string | (() => string) | undefined
4
+ ): asserts condition {
5
+ if (condition) return
6
+ throw new Error(
7
+ [
8
+ 'Invariant failed',
9
+ typeof message === 'function' ? message() : message
10
+ ].filter(Boolean).join(': ')
11
+ );
12
+ }
@@ -0,0 +1,77 @@
1
+ const isMapShallowEqual = (
2
+ a: ReadonlyMap<unknown, unknown>,
3
+ b: ReadonlyMap<unknown, unknown>,
4
+ ): boolean => {
5
+ if (a.size !== b.size) {
6
+ return false;
7
+ }
8
+
9
+ for (const [key, value] of a) {
10
+ const valueB = b.get(key);
11
+ if (value !== valueB || !Object.is(value, valueB)) {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ const isSetShallowEqual = (
20
+ a: ReadonlySet<unknown>,
21
+ b: ReadonlySet<unknown>,
22
+ ): boolean => {
23
+ if (a.size !== b.size) {
24
+ return false;
25
+ }
26
+
27
+ for (const value of a) {
28
+ if (!b.has(value)) {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ export const isShallowEqual = <T> (a: T, b: T): boolean => {
37
+ if (a === b || Object.is(a, b)) {
38
+ return true;
39
+ }
40
+
41
+ if (
42
+ typeof a !== "object" ||
43
+ a === null ||
44
+ typeof b !== "object" ||
45
+ b === null
46
+ ) {
47
+ return false;
48
+ }
49
+
50
+ if (a instanceof Map && b instanceof Map) {
51
+ return isMapShallowEqual(a, b);
52
+ }
53
+
54
+ if (a instanceof Set && b instanceof Set) {
55
+ return isSetShallowEqual(a, b);
56
+ }
57
+
58
+ const keys = Object.keys(a);
59
+ if (keys.length !== Object.keys(b).length) {
60
+ return false;
61
+ }
62
+
63
+ for (const key of keys) {
64
+ if (!Object.hasOwn(b, key)) {
65
+ return false;
66
+ }
67
+
68
+ const { [key as keyof T]: valueA } = a;
69
+ const { [key as keyof T]: valueB } = b;
70
+
71
+ if (valueA !== valueB || !Object.is(valueA, valueB)) {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ return true;
77
+ }
@@ -0,0 +1,17 @@
1
+ import type { RunGetters } from "../types"
2
+ import { uncapitalize } from "./uncapitalize"
3
+
4
+ export const runGetters = <T extends object>(x: T): RunGetters<T> =>
5
+ Object.fromEntries(
6
+ Object.entries(x).map(([key, value]) => {
7
+ if (typeof value === "function" && key.startsWith("get")) {
8
+ try {
9
+ return [uncapitalize(key.replace(/^get/, "")), value()]
10
+ } catch {
11
+ return [key, value]
12
+ }
13
+ }
14
+
15
+ return [key, value]
16
+ }),
17
+ ) as RunGetters<T>
@@ -0,0 +1,3 @@
1
+ import type { Table } from "@tanstack/table-core"
2
+
3
+ export const tableRegistry: WeakMap<Table<any>, { listeners: Set<() => void> }> = new WeakMap()
@@ -0,0 +1,2 @@
1
+ export const uncapitalize = (str: string): string =>
2
+ str.slice(0, 1).toLowerCase() + str.slice(1)
@@ -0,0 +1,3 @@
1
+ import * as React from 'react'
2
+
3
+ export const useLayoutEffect: (effect: React.EffectCallback, deps?: React.DependencyList) => void = typeof document !== 'undefined' ? React.useLayoutEffect : () => {}
@@ -0,0 +1,12 @@
1
+ import { useState } from "react"
2
+ import { isShallowEqual } from "./isShallowEqual"
3
+
4
+ export const useShallowMemo = <T>(next: T): T => {
5
+ const [current, setCurrent] = useState(next)
6
+
7
+ if (!isShallowEqual(current, next)) {
8
+ setCurrent(next)
9
+ }
10
+
11
+ return current
12
+ }
package/src/types.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { Simplify } from 'type-fest'
2
+
3
+ export type IsEqual<Selection> = (prev: Selection, next: Selection) => boolean
4
+
5
+ type GetterKeys<T> = {
6
+ [k in keyof T]-?: k extends `get${string}` ?
7
+ T[k] extends () => unknown ?
8
+ k
9
+ : never
10
+ : never
11
+ }[keyof T]
12
+
13
+ type GottenKey<k extends string> =
14
+ k extends `get${infer K}` ? Uncapitalize<K> : never
15
+
16
+ type GetterReturn<T> = T extends () => unknown ? ReturnType<T> : never
17
+
18
+ export type RunGetters<T> = Simplify<
19
+ {
20
+ [k in GetterKeys<T> as GottenKey<k>]: GetterReturn<T[k]>
21
+ } & {
22
+ [k in Exclude<keyof T, GetterKeys<T>>]: T[k]
23
+ }
24
+ >