forked from plotly/dash
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue 347- Improve TS type inference & simplify caching logic (plotly…
- Loading branch information
1 parent
0d79937
commit cfb5ab0
Showing
9 changed files
with
231 additions
and
188 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as R from 'ramda'; | ||
|
||
export type CacheKeyFragment = string | number | boolean; | ||
|
||
export function getCache<TKey extends CacheKeyFragment[]>(cache: Map<CacheKeyFragment, any>, ...key: TKey) { | ||
const cacheKeys = key.slice(0, -1); | ||
|
||
return R.reduce((c, fragment) => { | ||
return c.get(fragment) || c.set(fragment, new Map()).get(fragment); | ||
}, cache, cacheKeys) as Map<CacheKeyFragment, any>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { memoizeOne } from 'core/memoizer'; | ||
import { CacheKeyFragment, getCache } from '.'; | ||
|
||
export default <TKey extends CacheKeyFragment[]>() => { | ||
return <TEntryFn extends (...a: any[]) => any>(fn: TEntryFn) => { | ||
const cache = new Map<CacheKeyFragment, any>(); | ||
|
||
function get(...key: TKey) { | ||
const lastKey = key.slice(-1)[0]; | ||
|
||
const nestedCache = getCache(cache, ...key); | ||
|
||
return ( | ||
nestedCache.get(lastKey) || | ||
nestedCache.set(lastKey, memoizeOne(fn)).get(lastKey) | ||
); | ||
} | ||
|
||
return { get }; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { CacheKeyFragment, getCache } from '.'; | ||
|
||
export default <TKey extends CacheKeyFragment[]>() => | ||
<TEntry>(fn: (...a: TKey) => TEntry) => { | ||
const cache = new Map<CacheKeyFragment, any>(); | ||
|
||
function get(...key: TKey) { | ||
const lastKey = key.slice(-1)[0]; | ||
|
||
const nestedCache = getCache(cache, ...key); | ||
|
||
return nestedCache.has(lastKey) ? | ||
nestedCache.get(lastKey) : | ||
nestedCache.set(lastKey, fn(...key)).get(lastKey); | ||
} | ||
|
||
return { get }; | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 104 additions & 70 deletions
174
packages/dash-table/src/dash-table/derived/cell/dropdowns.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,128 @@ | ||
import * as R from 'ramda'; | ||
|
||
import { memoizeOneFactory } from 'core/memoizer'; | ||
import { memoizeOne } from 'core/memoizer'; | ||
import memoizerCache from 'core/cache/memoizer'; | ||
import SyntaxTree from 'core/syntax-tree'; | ||
|
||
import { | ||
IConditionalDropdown | ||
} from 'dash-table/components/CellDropdown/types'; | ||
|
||
import { | ||
Data, | ||
Datum, | ||
VisibleColumns, | ||
ColumnId, | ||
Indices, | ||
IColumnDropdown, | ||
IConditionalColumnDropdown, | ||
IDropdownProperties, | ||
DropdownValues | ||
DropdownValues, | ||
IBaseVisibleColumn, | ||
IVisibleColumn | ||
} from 'dash-table/components/Table/props'; | ||
import SyntaxTree from 'core/syntax-tree'; | ||
import memoizerCache from 'core/memoizerCache'; | ||
import { IConditionalDropdown } from 'dash-table/components/CellDropdown/types'; | ||
|
||
const mapData = R.addIndex<Datum, (DropdownValues | undefined)[]>(R.map); | ||
|
||
const getDropdown = ( | ||
astCache: (key: [ColumnId, number], query: string) => SyntaxTree, | ||
conditionalDropdowns: IConditionalDropdown[], | ||
datum: Datum, | ||
property: ColumnId, | ||
staticDropdown: DropdownValues | undefined | ||
): DropdownValues | undefined => { | ||
const dropdowns = [ | ||
...(staticDropdown ? [staticDropdown] : []), | ||
...R.map( | ||
([cd]) => cd.dropdown, | ||
R.filter( | ||
([cd, i]) => astCache([property, i], cd.condition).evaluate(datum), | ||
R.addIndex<IConditionalDropdown, [IConditionalDropdown, number]>(R.map)( | ||
(cd, i) => [cd, i], | ||
conditionalDropdowns | ||
)) | ||
) | ||
]; | ||
export default () => new Dropdowns().get; | ||
|
||
return dropdowns.length ? dropdowns.slice(-1)[0] : undefined; | ||
}; | ||
class Dropdowns { | ||
/** | ||
* Return the dropdown for each cell in the table. | ||
*/ | ||
get = memoizeOne(( | ||
columns: VisibleColumns, | ||
data: Data, | ||
indices: Indices, | ||
columnConditionalDropdown: any, | ||
columnStaticDropdown: any, | ||
dropdown_properties: any | ||
) => mapData((datum, rowIndex) => R.map(column => { | ||
const applicable = this.applicable.get(column.id, rowIndex)( | ||
column, | ||
indices[rowIndex], | ||
columnConditionalDropdown, | ||
columnStaticDropdown, | ||
dropdown_properties | ||
); | ||
|
||
const getter = ( | ||
astCache: (key: [ColumnId, number], query: string) => SyntaxTree, | ||
columns: VisibleColumns, | ||
data: Data, | ||
indices: Indices, | ||
columnConditionalDropdown: IConditionalColumnDropdown[], | ||
columnStaticDropdown: IColumnDropdown[], | ||
dropdown_properties: IDropdownProperties | ||
): (DropdownValues | undefined)[][] => mapData((datum, rowIndex) => R.map(column => { | ||
const realIndex = indices[rowIndex]; | ||
return this.dropdown.get(column.id, rowIndex)( | ||
applicable, | ||
column, | ||
datum | ||
); | ||
}, columns), data)); | ||
|
||
let legacyDropdown = ( | ||
( | ||
dropdown_properties && | ||
dropdown_properties[column.id] && | ||
/** | ||
* Returns the list of applicable dropdowns for a cell. | ||
*/ | ||
private readonly applicable = memoizerCache<[ColumnId, number]>()(( | ||
column: IBaseVisibleColumn, | ||
realIndex: number, | ||
columnConditionalDropdown: any, | ||
columnStaticDropdown: any, | ||
dropdown_properties: any | ||
): [any, any] => { | ||
let legacyDropdown = ( | ||
( | ||
dropdown_properties[column.id].length > realIndex ? | ||
dropdown_properties[column.id][realIndex] : | ||
null | ||
) | ||
) || column | ||
).options; | ||
dropdown_properties && | ||
dropdown_properties[column.id] && | ||
( | ||
dropdown_properties[column.id].length > realIndex ? | ||
dropdown_properties[column.id][realIndex] : | ||
null | ||
) | ||
) || column | ||
).options; | ||
|
||
const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); | ||
const base = columnStaticDropdown.find((ss: any) => ss.id === column.id); | ||
const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); | ||
const base = columnStaticDropdown.find((ss: any) => ss.id === column.id); | ||
|
||
const conditionalDropdowns = (conditional && conditional.dropdowns) || []; | ||
const staticDropdown = legacyDropdown || (base && base.dropdown); | ||
return [ | ||
legacyDropdown || (base && base.dropdown), | ||
(conditional && conditional.dropdowns) || [] | ||
]; | ||
}); | ||
|
||
return getDropdown(astCache, conditionalDropdowns, datum, column.id, staticDropdown); | ||
}, columns), data); | ||
/** | ||
* Returns the highest priority dropdown from the | ||
* applicable dropdowns. | ||
*/ | ||
private readonly dropdown = memoizerCache<[ColumnId, number]>()(( | ||
applicableDropdowns: [any, any], | ||
column: IVisibleColumn, | ||
datum: Datum | ||
) => { | ||
const [staticDropdown, conditionalDropdowns] = applicableDropdowns; | ||
|
||
const getterFactory = memoizeOneFactory(getter); | ||
const matches = [ | ||
...(staticDropdown ? [staticDropdown] : []), | ||
...R.map( | ||
([cd]) => cd.dropdown, | ||
R.filter( | ||
([cd, i]) => this.evaluation.get(column.id, i)( | ||
this.ast.get(column.id, i)(cd.condition), | ||
datum | ||
), | ||
R.addIndex<IConditionalDropdown, [IConditionalDropdown, number]>(R.map)( | ||
(cd, i) => [cd, i], | ||
conditionalDropdowns | ||
)) | ||
) | ||
]; | ||
|
||
const decoratedGetter = (_id: string): (( | ||
columns: VisibleColumns, | ||
data: Data, | ||
indices: Indices, | ||
columnConditionalDropdown: any, | ||
columnStaticDropdown: any, | ||
dropdown_properties: any | ||
) => (DropdownValues | undefined)[][]) => { | ||
const astCache = memoizerCache<[ColumnId, number], [string], SyntaxTree>( | ||
(query: string) => new SyntaxTree(query) | ||
); | ||
return matches.length ? matches.slice(-1)[0] : undefined; | ||
}); | ||
|
||
return getterFactory().bind(undefined, astCache); | ||
}; | ||
/** | ||
* Get the query's AST. | ||
*/ | ||
private readonly ast = memoizerCache<[ColumnId, number]>()(( | ||
query: string | ||
) => new SyntaxTree(query)); | ||
|
||
export default memoizeOneFactory(decoratedGetter); | ||
/** | ||
* Evaluate if the query matches the cell's data. | ||
*/ | ||
private readonly evaluation = memoizerCache<[ColumnId, number]>()(( | ||
ast: SyntaxTree, | ||
datum: Datum | ||
) => ast.evaluate(datum)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.