@tanstack/table-core 8.0.0-alpha.67 → 8.0.0-alpha.70

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 (140) hide show
  1. package/build/cjs/aggregationFns.js +25 -37
  2. package/build/cjs/aggregationFns.js.map +1 -1
  3. package/build/cjs/core.js +167 -46
  4. package/build/cjs/core.js.map +1 -1
  5. package/build/cjs/createTable.js +24 -40
  6. package/build/cjs/createTable.js.map +1 -1
  7. package/build/cjs/features/Cells.js +28 -34
  8. package/build/cjs/features/Cells.js.map +1 -1
  9. package/build/cjs/features/ColumnSizing.js +102 -116
  10. package/build/cjs/features/ColumnSizing.js.map +1 -1
  11. package/build/cjs/features/Columns.js +52 -79
  12. package/build/cjs/features/Columns.js.map +1 -1
  13. package/build/cjs/features/Expanding.js +49 -58
  14. package/build/cjs/features/Expanding.js.map +1 -1
  15. package/build/cjs/features/Filters.js +98 -193
  16. package/build/cjs/features/Filters.js.map +1 -1
  17. package/build/cjs/features/Grouping.js +53 -71
  18. package/build/cjs/features/Grouping.js.map +1 -1
  19. package/build/cjs/features/Headers.js +109 -165
  20. package/build/cjs/features/Headers.js.map +1 -1
  21. package/build/cjs/features/Ordering.js +31 -43
  22. package/build/cjs/features/Ordering.js.map +1 -1
  23. package/build/cjs/features/Pagination.js +79 -82
  24. package/build/cjs/features/Pagination.js.map +1 -1
  25. package/build/cjs/features/Pinning.js +71 -125
  26. package/build/cjs/features/Pinning.js.map +1 -1
  27. package/build/cjs/features/RowSelection.js +93 -112
  28. package/build/cjs/features/RowSelection.js.map +1 -1
  29. package/build/cjs/features/Rows.js +32 -22
  30. package/build/cjs/features/Rows.js.map +1 -1
  31. package/build/cjs/features/Sorting.js +54 -73
  32. package/build/cjs/features/Sorting.js.map +1 -1
  33. package/build/cjs/features/Visibility.js +47 -86
  34. package/build/cjs/features/Visibility.js.map +1 -1
  35. package/build/cjs/filterFns.js +48 -72
  36. package/build/cjs/filterFns.js.map +1 -1
  37. package/build/cjs/index.js +13 -13
  38. package/build/cjs/sortingFns.js +21 -21
  39. package/build/cjs/sortingFns.js.map +1 -1
  40. package/build/cjs/utils/filterRowsUtils.js +49 -55
  41. package/build/cjs/utils/filterRowsUtils.js.map +1 -1
  42. package/build/cjs/utils/getCoreRowModel.js +98 -0
  43. package/build/cjs/utils/getCoreRowModel.js.map +1 -0
  44. package/build/cjs/utils/getExpandedRowModel.js +17 -21
  45. package/build/cjs/utils/getExpandedRowModel.js.map +1 -1
  46. package/build/cjs/utils/getFacetedMinMaxValues.js +48 -0
  47. package/build/cjs/utils/getFacetedMinMaxValues.js.map +1 -0
  48. package/build/cjs/utils/getFacetedRowModel.js +50 -0
  49. package/build/cjs/utils/getFacetedRowModel.js.map +1 -0
  50. package/build/cjs/utils/getFacetedUniqueValues.js +48 -0
  51. package/build/cjs/utils/getFacetedUniqueValues.js.map +1 -0
  52. package/build/cjs/utils/getFilteredRowModel.js +130 -0
  53. package/build/cjs/utils/getFilteredRowModel.js.map +1 -0
  54. package/build/cjs/utils/getGroupedRowModel.js +126 -136
  55. package/build/cjs/utils/getGroupedRowModel.js.map +1 -1
  56. package/build/cjs/utils/getPaginationRowModel.js +39 -39
  57. package/build/cjs/utils/getPaginationRowModel.js.map +1 -1
  58. package/build/cjs/utils/getSortedRowModel.js +112 -0
  59. package/build/cjs/utils/getSortedRowModel.js.map +1 -0
  60. package/build/cjs/utils.js +36 -207
  61. package/build/cjs/utils.js.map +1 -1
  62. package/build/esm/index.js +1778 -2347
  63. package/build/esm/index.js.map +1 -1
  64. package/build/stats-html.html +1 -1
  65. package/build/stats-react.json +421 -481
  66. package/build/types/core.d.ts +3 -5
  67. package/build/types/features/Cells.d.ts +1 -1
  68. package/build/types/features/ColumnSizing.d.ts +2 -2
  69. package/build/types/features/Columns.d.ts +1 -1
  70. package/build/types/features/Expanding.d.ts +1 -1
  71. package/build/types/features/Filters.d.ts +12 -7
  72. package/build/types/features/Grouping.d.ts +5 -4
  73. package/build/types/features/Ordering.d.ts +1 -1
  74. package/build/types/features/Pagination.d.ts +4 -4
  75. package/build/types/features/Pinning.d.ts +1 -1
  76. package/build/types/features/RowSelection.d.ts +1 -1
  77. package/build/types/features/Rows.d.ts +4 -3
  78. package/build/types/features/Sorting.d.ts +2 -1
  79. package/build/types/features/Visibility.d.ts +1 -0
  80. package/build/types/index.d.ts +6 -4
  81. package/build/types/types.d.ts +3 -13
  82. package/build/types/utils/filterRowsUtils.d.ts +3 -3
  83. package/build/types/utils/getCoreRowModel.d.ts +2 -0
  84. package/build/types/utils/getFacetedMinMaxValues.d.ts +2 -0
  85. package/build/types/utils/getFacetedRowModel.d.ts +2 -0
  86. package/build/types/utils/getFacetedUniqueValues.d.ts +2 -0
  87. package/build/types/utils/{getCoreRowModelSync.d.ts → getFilteredRowModel.d.ts} +1 -1
  88. package/build/types/utils/getGroupedRowModel.d.ts +1 -1
  89. package/build/types/utils/getSortedRowModel.d.ts +2 -0
  90. package/build/types/utils.d.ts +1 -17
  91. package/build/umd/index.development.js +1779 -2350
  92. package/build/umd/index.development.js.map +1 -1
  93. package/build/umd/index.production.js +1 -1
  94. package/build/umd/index.production.js.map +1 -1
  95. package/package.json +1 -1
  96. package/src/core.ts +197 -45
  97. package/src/features/Cells.ts +7 -11
  98. package/src/features/ColumnSizing.ts +27 -19
  99. package/src/features/Columns.ts +4 -8
  100. package/src/features/Expanding.ts +5 -3
  101. package/src/features/Filters.ts +105 -162
  102. package/src/features/Grouping.ts +21 -23
  103. package/src/features/Ordering.ts +5 -3
  104. package/src/features/Pagination.ts +32 -19
  105. package/src/features/Pinning.ts +13 -7
  106. package/src/features/RowSelection.ts +5 -3
  107. package/src/features/Rows.ts +21 -6
  108. package/src/features/Sorting.ts +14 -12
  109. package/src/features/Visibility.ts +7 -0
  110. package/src/filterFns.ts +9 -9
  111. package/src/index.ts +12 -4
  112. package/src/sortingFns.ts +11 -11
  113. package/src/types.ts +3 -16
  114. package/src/utils/filterRowsUtils.ts +47 -68
  115. package/src/utils/{getCoreRowModelSync.ts → getCoreRowModel.ts} +2 -8
  116. package/src/utils/getFacetedMinMaxValues.ts +37 -0
  117. package/src/utils/getFacetedRowModel.ts +50 -0
  118. package/src/utils/getFacetedUniqueValues.ts +37 -0
  119. package/src/utils/{getFilteredRowModelSync.ts → getFilteredRowModel.ts} +29 -20
  120. package/src/utils/getGroupedRowModel.ts +52 -61
  121. package/src/utils/{getSortedRowModelSync.ts → getSortedRowModel.ts} +3 -3
  122. package/src/utils.ts +28 -236
  123. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -112
  124. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  125. package/build/cjs/utils/getCoreRowModelAsync.js +0 -125
  126. package/build/cjs/utils/getCoreRowModelAsync.js.map +0 -1
  127. package/build/cjs/utils/getCoreRowModelSync.js +0 -102
  128. package/build/cjs/utils/getCoreRowModelSync.js.map +0 -1
  129. package/build/cjs/utils/getFilteredRowModelSync.js +0 -125
  130. package/build/cjs/utils/getFilteredRowModelSync.js.map +0 -1
  131. package/build/cjs/utils/getSortedRowModelSync.js +0 -118
  132. package/build/cjs/utils/getSortedRowModelSync.js.map +0 -1
  133. package/build/types/utils/getCoreRowModelAsync.d.ts +0 -4
  134. package/build/types/utils/getFilteredRowModelAsync.d.ts +0 -4
  135. package/build/types/utils/getFilteredRowModelSync.d.ts +0 -2
  136. package/build/types/utils/getSortedRowModelAsync.d.ts +0 -13
  137. package/build/types/utils/getSortedRowModelSync.d.ts +0 -2
  138. package/src/utils/getCoreRowModelAsync.ts +0 -104
  139. package/src/utils/getFilteredRowModelAsync.ts +0 -157
  140. package/src/utils/getSortedRowModelAsync.ts +0 -200
@@ -0,0 +1,37 @@
1
+ import { TableInstance, TableGenerics } from '../types'
2
+ import { memo } from '../utils'
3
+
4
+ export function getFacetedUniqueValues<TGenerics extends TableGenerics>(): (
5
+ instance: TableInstance<TGenerics>,
6
+ columnId: string
7
+ ) => () => Map<any, number> {
8
+ return (instance, columnId) =>
9
+ memo(
10
+ () => [instance.getColumn(columnId).getFacetedRowModel()],
11
+ facetedRowModel => {
12
+ let facetedUniqueValues = new Map<any, number>()
13
+
14
+ for (let i = 0; i < facetedRowModel.flatRows.length; i++) {
15
+ const value = facetedRowModel.flatRows[i]?.getValue(columnId)
16
+
17
+ if (facetedUniqueValues.has(value)) {
18
+ facetedUniqueValues.set(
19
+ value,
20
+ (facetedUniqueValues.get(value) ?? 0) + 1
21
+ )
22
+ } else {
23
+ facetedUniqueValues.set(value, 1)
24
+ }
25
+ }
26
+
27
+ return facetedUniqueValues
28
+ },
29
+ {
30
+ key:
31
+ process.env.NODE_ENV === 'development' &&
32
+ 'getFacetedUniqueValues_' + columnId,
33
+ debug: () => instance.options.debugAll ?? instance.options.debugTable,
34
+ onChange: () => {},
35
+ }
36
+ )
37
+ }
@@ -1,13 +1,9 @@
1
- import {
2
- ColumnFilter,
3
- FilterFn,
4
- ResolvedColumnFilter,
5
- } from '../features/Filters'
1
+ import { ResolvedColumnFilter } from '../features/Filters'
6
2
  import { TableInstance, RowModel, TableGenerics, Row } from '../types'
7
3
  import { memo } from '../utils'
8
4
  import { filterRows } from './filterRowsUtils'
9
5
 
10
- export function getFilteredRowModelSync<TGenerics extends TableGenerics>(): (
6
+ export function getFilteredRowModel<TGenerics extends TableGenerics>(): (
11
7
  instance: TableInstance<TGenerics>
12
8
  ) => () => RowModel<TGenerics> {
13
9
  return instance =>
@@ -83,34 +79,38 @@ export function getFilteredRowModelSync<TGenerics extends TableGenerics>(): (
83
79
  })
84
80
  }
85
81
 
82
+ let currentColumnFilter
83
+ let currentGlobalFilter
84
+
86
85
  // Flag the prefiltered row model with each filter state
87
86
  for (let j = 0; j < rowModel.flatRows.length; j++) {
88
- const row = rowModel.flatRows[j]
87
+ const row = rowModel.flatRows[j]!
89
88
 
90
89
  row.columnFilterMap = {}
91
90
 
92
91
  if (resolvedColumnFilters.length) {
93
92
  for (let i = 0; i < resolvedColumnFilters.length; i++) {
94
- const columnFilter = resolvedColumnFilters[i]
93
+ currentColumnFilter = resolvedColumnFilters[i]!
95
94
 
96
95
  // Tag the row with the column filter state
97
- row.columnFilterMap[columnFilter.id] = columnFilter.filterFn(
98
- row,
99
- columnFilter.id,
100
- columnFilter.resolvedValue
101
- )
96
+ row.columnFilterMap[currentColumnFilter.id] =
97
+ currentColumnFilter.filterFn(
98
+ row,
99
+ currentColumnFilter.id,
100
+ currentColumnFilter.resolvedValue
101
+ )
102
102
  }
103
103
  }
104
104
 
105
105
  if (resolvedGlobalFilters.length) {
106
106
  for (let i = 0; i < resolvedGlobalFilters.length; i++) {
107
- const globalFilter = resolvedGlobalFilters[i]
107
+ currentGlobalFilter = resolvedGlobalFilters[i]!
108
108
  // Tag the row with the first truthy global filter state
109
109
  if (
110
- globalFilter.filterFn(
110
+ currentGlobalFilter.filterFn(
111
111
  row,
112
- globalFilter.id,
113
- globalFilter.resolvedValue
112
+ currentGlobalFilter.id,
113
+ currentGlobalFilter.resolvedValue
114
114
  )
115
115
  ) {
116
116
  row.columnFilterMap.__global__ = true
@@ -124,12 +124,21 @@ export function getFilteredRowModelSync<TGenerics extends TableGenerics>(): (
124
124
  }
125
125
  }
126
126
 
127
+ const filterRowsImpl = (row: Row<TGenerics>) => {
128
+ // Horizontally filter rows through each column
129
+ for (let i = 0; i < filterableIds.length; i++) {
130
+ if (row.columnFilterMap[filterableIds[i]!] === false) {
131
+ return false
132
+ }
133
+ }
134
+ return true
135
+ }
136
+
127
137
  // Filter final rows using all of the active filters
128
- return filterRows(rowModel.rows, filterableIds, instance)
138
+ return filterRows(rowModel.rows, filterRowsImpl, instance)
129
139
  },
130
140
  {
131
- key:
132
- process.env.NODE_ENV === 'development' && 'getFilteredRowModelSync',
141
+ key: process.env.NODE_ENV === 'development' && 'getFilteredRowModel',
133
142
  debug: () => instance.options.debugAll ?? instance.options.debugTable,
134
143
  onChange: () => {
135
144
  instance.queue(() => {
@@ -1,7 +1,8 @@
1
+ import { CoreRow } from '../features/Rows'
1
2
  import { TableInstance, Row, RowModel, TableGenerics } from '../types'
2
3
  import { flattenBy, memo } from '../utils'
3
4
 
4
- export function getGroupedRowModelSync<TGenerics extends TableGenerics>(): (
5
+ export function getGroupedRowModel<TGenerics extends TableGenerics>(): (
5
6
  instance: TableInstance<TGenerics>
6
7
  ) => () => RowModel<TGenerics> {
7
8
  return instance =>
@@ -17,56 +18,6 @@ export function getGroupedRowModelSync<TGenerics extends TableGenerics>(): (
17
18
  instance.getColumn(columnId)
18
19
  )
19
20
 
20
- // Find the columns that can or are aggregating
21
- // Uses each column to aggregate rows into a single value
22
- const aggregateRowsToValues = (
23
- leafRows: Row<TGenerics>[],
24
- groupedRows: Row<TGenerics>[],
25
- depth: number
26
- ) => {
27
- const values: Record<string, unknown> = {}
28
-
29
- instance.getAllLeafColumns().forEach(column => {
30
- // Don't aggregate columns that are in the grouping
31
- if (existingGrouping.includes(column.id)) {
32
- values[column.id] = groupedRows[0]
33
- ? groupedRows[0].values[column.id]
34
- : null
35
- return
36
- }
37
-
38
- // Aggregate the values
39
- const aggregateFn = instance.getColumnAggregationFn(column.id)
40
-
41
- if (aggregateFn) {
42
- values[column.id] = aggregateFn(
43
- () =>
44
- leafRows.map(row => {
45
- let columnValue = row.values[column.id]
46
-
47
- if (!depth && column.aggregateValue) {
48
- columnValue = column.aggregateValue(columnValue)
49
- }
50
-
51
- return columnValue
52
- }),
53
- () => groupedRows.map(row => row.values[column.id])
54
- )
55
- } else if (column.aggregationFn) {
56
- console.info({ column })
57
- throw new Error(
58
- process.env.NODE_ENV !== 'production'
59
- ? `Table: Invalid column.aggregateType option for column listed above`
60
- : ''
61
- )
62
- } else {
63
- values[column.id] = null
64
- }
65
- })
66
-
67
- return values
68
- }
69
-
70
21
  const groupedFlatRows: Row<TGenerics>[] = []
71
22
  const groupedRowsById: Record<string, Row<TGenerics>> = {}
72
23
  // const onlyGroupedFlatRows: Row[] = [];
@@ -104,21 +55,61 @@ export function getGroupedRowModelSync<TGenerics extends TableGenerics>(): (
104
55
  ? flattenBy(groupedRows, row => row.subRows)
105
56
  : groupedRows
106
57
 
107
- const values = aggregateRowsToValues(leafRows, groupedRows, depth)
108
-
109
- const row = instance.createRow(
110
- id,
111
- undefined,
112
- index,
113
- depth,
114
- values
115
- )
58
+ const row = instance.createRow(id, undefined, index, depth)
116
59
 
117
60
  Object.assign(row, {
118
61
  groupingColumnId: columnId,
119
62
  groupingValue,
120
63
  subRows,
121
64
  leafRows,
65
+ getValue: (columnId: string) => {
66
+ // Don't aggregate columns that are in the grouping
67
+ if (existingGrouping.includes(columnId)) {
68
+ if (row.valuesCache.hasOwnProperty(columnId)) {
69
+ return row.valuesCache[columnId]
70
+ }
71
+
72
+ if (groupedRows[0]) {
73
+ row.valuesCache[columnId] =
74
+ groupedRows[0].getValue(columnId) ?? undefined
75
+ }
76
+
77
+ return row.valuesCache[columnId]
78
+ }
79
+
80
+ if (row.groupingValuesCache.hasOwnProperty(columnId)) {
81
+ return row.groupingValuesCache[columnId]
82
+ }
83
+
84
+ // Aggregate the values
85
+ const column = instance.getColumn(columnId)
86
+ const aggregateFn = column.getColumnAggregationFn()
87
+
88
+ if (aggregateFn) {
89
+ row.groupingValuesCache[columnId] = aggregateFn(
90
+ () =>
91
+ leafRows.map(row => {
92
+ let columnValue = row.getValue(columnId)
93
+
94
+ if (!depth && column.aggregateValue) {
95
+ columnValue = column.aggregateValue(columnValue)
96
+ }
97
+
98
+ return columnValue
99
+ }),
100
+ () => groupedRows.map(row => row.getValue(columnId))
101
+ )
102
+
103
+ return row.groupingValuesCache[columnId]
104
+ } else if (column.aggregationFn) {
105
+ console.info({ column })
106
+ throw new Error(
107
+ process.env.NODE_ENV !== 'production'
108
+ ? `Table: Invalid column.aggregateType option for column listed above`
109
+ : ''
110
+ )
111
+ }
112
+ },
122
113
  })
123
114
 
124
115
  subRows.forEach(subRow => {
@@ -180,7 +171,7 @@ function groupBy<TGenerics extends TableGenerics>(
180
171
  const groupMap = new Map<any, Row<TGenerics>[]>()
181
172
 
182
173
  return rows.reduce((map, row) => {
183
- const resKey = `${row.values[columnId]}`
174
+ const resKey = `${row.getValue(columnId)}`
184
175
  const previous = map.get(resKey)
185
176
  if (!previous) {
186
177
  map.set(resKey, [row])
@@ -2,7 +2,7 @@ import { TableInstance, Row, RowModel, TableGenerics } from '../types'
2
2
  import { SortingFn } from '../features/Sorting'
3
3
  import { memo } from '../utils'
4
4
 
5
- export function getSortedRowModelSync<TGenerics extends TableGenerics>(): (
5
+ export function getSortedRowModel<TGenerics extends TableGenerics>(): (
6
6
  instance: TableInstance<TGenerics>
7
7
  ) => () => RowModel<TGenerics> {
8
8
  return instance =>
@@ -53,8 +53,8 @@ export function getSortedRowModelSync<TGenerics extends TableGenerics>(): (
53
53
  const isDesc = sortEntry?.desc ?? false
54
54
 
55
55
  if (columnInfo.sortUndefined) {
56
- const aValue = rowA.values[sortEntry.id]
57
- const bValue = rowB.values[sortEntry.id]
56
+ const aValue = rowA.getValue(sortEntry.id)
57
+ const bValue = rowB.getValue(sortEntry.id)
58
58
 
59
59
  const aUndefined = typeof aValue === 'undefined'
60
60
  const bUndefined = typeof bValue === 'undefined'
package/src/utils.ts CHANGED
@@ -21,35 +21,6 @@ export function noop() {
21
21
  //
22
22
  }
23
23
 
24
- export function getBatchGroups<T>(arr: T[], count: number) {
25
- const groups: { start: number; end: number; items: T[] }[] = [
26
- {
27
- start: 0,
28
- end: count - 1,
29
- items: [],
30
- },
31
- ]
32
-
33
- let groupIndex = 0
34
- let group = groups[groupIndex]
35
-
36
- for (let i = 0; i < arr.length; i++) {
37
- if (i > group.end) {
38
- groupIndex++
39
- groups[groupIndex] = {
40
- start: i,
41
- end: i + count - 1,
42
- items: [],
43
- }
44
- }
45
- group = groups[groupIndex]
46
- const item = arr[i]
47
- group.items.push(item)
48
- }
49
-
50
- return groups
51
- }
52
-
53
24
  export function makeStateUpdater(key: keyof TableState, instance: unknown) {
54
25
  return (updater: Updater<any>) => {
55
26
  ;(instance as any).setState(<TTableState>(old: TTableState) => {
@@ -94,7 +65,7 @@ export function memo<TDeps extends readonly any[], TResult>(
94
65
  opts: {
95
66
  key: any
96
67
  debug?: () => any
97
- onChange?: (result: TResult, previousResult?: TResult) => void
68
+ onChange?: (result: TResult) => void
98
69
  }
99
70
  ): () => TResult {
100
71
  let deps: any[] = []
@@ -110,225 +81,46 @@ export function memo<TDeps extends readonly any[], TResult>(
110
81
  newDeps.length !== deps.length ||
111
82
  newDeps.some((dep: any, index: number) => deps[index] !== dep)
112
83
 
113
- if (depsChanged) {
114
- let oldResult = result
115
- let resultTime: number
116
- if (opts.key && opts.debug) resultTime = Date.now()
117
- result = fn(...newDeps)
118
- deps = newDeps
119
- opts?.onChange?.(result, oldResult)
84
+ if (!depsChanged) {
85
+ return result!
86
+ }
87
+
88
+ deps = newDeps
89
+
90
+ let resultTime: number
91
+ if (opts.key && opts.debug) resultTime = Date.now()
92
+
93
+ result = fn(...newDeps)
94
+ opts?.onChange?.(result)
120
95
 
121
- if (opts.key && opts.debug) {
122
- if (opts?.debug()) {
123
- const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100
124
- const resultEndTime =
125
- Math.round((Date.now() - resultTime!) * 100) / 100
126
- const resultFpsPercentage = resultEndTime / 16
96
+ if (opts.key && opts.debug) {
97
+ if (opts?.debug()) {
98
+ const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100
99
+ const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100
100
+ const resultFpsPercentage = resultEndTime / 16
127
101
 
128
- const pad = (str: number | string, num: number) => {
129
- str = String(str)
130
- while (str.length < num) {
131
- str = ' ' + str
132
- }
133
- return str
102
+ const pad = (str: number | string, num: number) => {
103
+ str = String(str)
104
+ while (str.length < num) {
105
+ str = ' ' + str
134
106
  }
107
+ return str
108
+ }
135
109
 
136
- console.info(
137
- `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
138
- `
110
+ console.info(
111
+ `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
112
+ `
139
113
  font-size: .6rem;
140
114
  font-weight: bold;
141
115
  color: hsl(${Math.max(
142
116
  0,
143
117
  Math.min(120 - 120 * resultFpsPercentage, 120)
144
118
  )}deg 100% 31%);`,
145
- opts?.key,
146
- {
147
- length: `${deps.length} -> ${newDeps.length}`,
148
- ...newDeps
149
- .map((_, index) => {
150
- if (deps[index] !== newDeps[index]) {
151
- return [index, deps[index], newDeps[index]]
152
- }
153
-
154
- return false
155
- })
156
- .filter(Boolean)
157
- .reduce(
158
- (accu, [a, b]: any) => ({
159
- ...accu,
160
- [a]: b,
161
- }),
162
- {}
163
- ),
164
- }
165
- )
166
- }
167
- }
168
-
169
- oldResult = undefined
170
- }
171
-
172
- return result!
173
- }
174
- }
175
-
176
- type WorkFn = () => void
177
-
178
- export function incrementalMemo<TDeps extends readonly any[], TResult>(
179
- getDeps: () => [...TDeps],
180
- getInitialValue: (...args: NoInfer<[...TDeps]>) => TResult,
181
- schedule: (
182
- ...args: NoInfer<[...TDeps]>
183
- ) => (resultRef: {
184
- current: TResult
185
- }) => (scheduler: (workFn: WorkFn) => void) => void,
186
- opts: {
187
- key: any
188
- onProgress: (
189
- progress: number,
190
- nextResult: TResult,
191
- result?: TResult
192
- ) => void
193
- onChange: (result: TResult, previousResult?: TResult) => void
194
- initialSync?: boolean
195
- timeout?: number
196
- debug?: () => any
197
- }
198
- ): () => TResult {
199
- let oldDeps: any[]
200
- let deps: any[]
201
- let result: TResult | undefined
202
- let nextResultRef: { current: TResult }
203
- let tasks: WorkFn[] = []
204
- let totalTaskCount = 0
205
- let resultStartTime: number
206
- let batchIndex = 0
207
- let progress = 0
208
- let working = false
209
- let callback: ReturnType<typeof requestIdleCallback>
210
-
211
- const timeout = opts.timeout ?? 100
212
-
213
- const onProgress = (latestResult: TResult, previousResult?: TResult) => {
214
- progress = 1 - tasks.length / totalTaskCount
215
- opts.onProgress(progress, latestResult, previousResult)
216
- }
217
-
218
- return () => {
219
- const scheduleFn = (workFn: WorkFn) => {
220
- totalTaskCount++
221
- tasks.push(workFn)
222
- }
223
-
224
- const newDeps = getDeps()
225
-
226
- let first = false
227
-
228
- if (!deps) {
229
- first = true
230
- deps = []
231
- result = getInitialValue(...newDeps)
232
- }
233
-
234
- const depsChanged =
235
- newDeps.length !== deps.length ||
236
- newDeps.some((dep: any, index: number) => deps[index] !== dep)
237
-
238
- if (depsChanged) {
239
- cancelIdleCallback(callback)
240
- oldDeps = deps
241
- deps = newDeps
242
- totalTaskCount = 0
243
- tasks = []
244
- nextResultRef = { current: getInitialValue(...newDeps) }
245
- resultStartTime = Date.now()
246
- schedule(...newDeps)(nextResultRef)(scheduleFn)
247
- }
248
-
249
- const commitResult = () => {
250
- cancelIdleCallback(callback)
251
-
252
- if (opts.key && opts.debug) {
253
- if (opts?.debug()) {
254
- const resultEndTime =
255
- Math.round((Date.now() - resultStartTime!) * 100) / 100
256
- const resultFpsPercentage = resultEndTime / batchIndex / 16
257
-
258
- console.info(
259
- `%c⏱ ${resultEndTime} ms / ${batchIndex} tasks = ${Math.round(
260
- resultEndTime / batchIndex
261
- )} ms/task`,
262
- `
263
- font-size: .6rem;
264
- font-weight: bold;
265
- color: hsl(${Math.max(
266
- 0,
267
- Math.min(120 - 120 * resultFpsPercentage, 120)
268
- )}deg 100% 31%);`,
269
- opts?.key,
270
- {
271
- length: `${oldDeps.length} -> ${deps.length}`,
272
- ...deps
273
- .map((_, index) => {
274
- if (oldDeps[index] !== deps[index]) {
275
- return [index, oldDeps[index], deps[index]]
276
- }
277
-
278
- return false
279
- })
280
- .filter(Boolean)
281
- .reduce(
282
- (accu, [a, b]: any) => ({
283
- ...accu,
284
- [a]: b,
285
- }),
286
- {}
287
- ),
288
- }
289
- )
290
- }
291
- }
292
-
293
- working = false
294
- let previousResult = result
295
- result = nextResultRef.current
296
- onProgress(result, previousResult)
297
- opts.onChange(result, previousResult)
298
- }
299
-
300
- if (opts.initialSync && first) {
301
- batchIndex = 1
302
- first = false
303
- for (let i = 0; i < tasks.length; i++) {
304
- tasks[i]()
305
- }
306
- tasks = []
307
- commitResult()
308
- } else if (!working && tasks.length) {
309
- cancelIdleCallback(callback)
310
- working = true
311
- batchIndex = 0
312
-
313
- const workLoop = (deadline: any) => {
314
- while (tasks.length && deadline.timeRemaining() > 0) {
315
- tasks.shift()!()
316
- }
317
-
318
- if (!tasks.length) {
319
- commitResult()
320
- } else {
321
- ++batchIndex
322
- onProgress(nextResultRef.current, result)
323
- callback = requestIdleCallback(workLoop, { timeout })
324
- }
119
+ opts?.key
120
+ )
325
121
  }
326
-
327
- callback = requestIdleCallback(workLoop, { timeout })
328
122
  }
329
123
 
330
124
  return result!
331
125
  }
332
126
  }
333
-
334
- // opts?.onChange?.(result, oldResult)