-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify working with columns #40
Changes from all commits
48ef0bb
355e4ab
4577a00
fa3da69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -227,6 +227,197 @@ export const preferences = { | |
}, | ||
}; | ||
|
||
/** | ||
* if a `requester` is not provided, | ||
* Get the columns for the table, considering any and all plugins that could modify columns. | ||
* | ||
* If you are an end-consumer of ember-headless-table, this is the function to use. | ||
* If you are a plugin-author, you'll want to pass your plugin class as the second parameter. | ||
* | ||
* For a given plugin, `requester`, determine what columns should be returned. | ||
* Since multiple plugins could be used in a table, there is an implicit hierarchy of | ||
* column modifications that can occur from each of those plugins. | ||
* | ||
* If a plugin defines other plugins as either *requirements* or *optional requirements*, | ||
* and that upstream plugin defines a `columns` property, then those columns will be returned here. | ||
* | ||
* This works recursively up the plugin tree up until a plugin has no requirements, and then | ||
* all columns from the table are returned. | ||
*/ | ||
function columnsFor<DataType = any>( | ||
table: Table<DataType>, | ||
requester?: Plugin<any> | undefined | ||
): Column<DataType>[] { | ||
assert(`First argument passed to columns.for must be an instance of Table`, table[TABLE_KEY]); | ||
|
||
let visibility = findPlugin(table.plugins, 'columnVisibility'); | ||
let reordering = findPlugin(table.plugins, 'columnOrder'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trying to work on #152 and stumbled across this after some digging... Is this how the column order plugin ends up getting the columns with visibility already applied? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, there is a graph of plugins determined at lookup time. You can choose:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get that the column order plugin needs to be aware of individual column's visibility values but I think it still needs to be able to loop over all columns... Looking at those things you can choose, after a bunch of clicking through the code I think the APIs are:
Does that seem right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ye! 🎉 |
||
|
||
// TODO: actually resolve the graph, rather than use the hardcoded feature names | ||
// atm, this only "happens" to work based on expectations of | ||
// of the currently implemented plugins' capabilities and implied hierarchy. | ||
|
||
if (requester) { | ||
assert( | ||
`[${requester.name}] requested columns from the table, but the plugin, ${requester.name}, ` + | ||
`is not used in this table`, | ||
table.plugins.some((plugin) => plugin instanceof (requester as Class<Plugin>)) | ||
); | ||
|
||
if (visibility && visibility.constructor === requester) { | ||
return table.columns.values(); | ||
} | ||
|
||
if (reordering && reordering.constructor === requester) { | ||
if (visibility) { | ||
assert( | ||
`<#${visibility.name}> defined a 'columns' property, but did not return valid data.`, | ||
visibility.columns && Array.isArray(visibility.columns) | ||
); | ||
|
||
return visibility.columns; | ||
} | ||
|
||
return table.columns.values(); | ||
} | ||
|
||
if (reordering) { | ||
assert( | ||
`<#${reordering.name}> defined a 'columns' property, but did not return valid data.`, | ||
reordering.columns && Array.isArray(reordering.columns) | ||
); | ||
|
||
return reordering.columns; | ||
} | ||
|
||
if (visibility) { | ||
assert( | ||
`<#${visibility.name}> defined a 'columns' property, but did not return valid data.`, | ||
visibility.columns && Array.isArray(visibility.columns) | ||
); | ||
|
||
return visibility.columns; | ||
} | ||
|
||
return table.columns.values(); | ||
} | ||
|
||
/** | ||
* This flow is the inverse of when we have a requester | ||
*/ | ||
|
||
if (reordering) { | ||
assert( | ||
`<#${reordering.name}> defined a 'columns' property, but did not return valid data.`, | ||
reordering.columns && Array.isArray(reordering.columns) | ||
); | ||
|
||
return reordering.columns; | ||
} | ||
|
||
if (visibility) { | ||
assert( | ||
`<#${visibility.name}> defined a 'columns' property, but did not return valid data.`, | ||
visibility.columns && Array.isArray(visibility.columns) | ||
); | ||
|
||
return visibility.columns; | ||
} | ||
|
||
return table.columns.values(); | ||
} | ||
|
||
export const columns = { | ||
for: columnsFor, | ||
|
||
/** | ||
* for a given current or reference column, return the column that | ||
* is immediately next, or to the right of that column. | ||
* | ||
* If a plugin class is provided, the hierarchy of column list modifications | ||
* will be respected. | ||
*/ | ||
next: <Data = unknown>( | ||
current: Column<Data>, | ||
requester?: Plugin<any> | ||
): Column<Data> | undefined => { | ||
let columns = requester ? columnsFor(current.table, requester) : columnsFor(current.table); | ||
|
||
let referenceIndex = columns.indexOf(current); | ||
|
||
assert( | ||
`index of reference column must be >= 0. column likely not a part of the table`, | ||
referenceIndex >= 0 | ||
); | ||
|
||
/** | ||
* There can be nothing after the last column | ||
*/ | ||
if (referenceIndex >= columns.length - 1) { | ||
return undefined; | ||
} | ||
|
||
return columns[referenceIndex + 1]; | ||
}, | ||
|
||
/** | ||
* for a given current or reference column, return the column that | ||
* is immediately previous, or to the left of that column. | ||
* | ||
* If a plugin class is provided, the hierarchy of column list modifications | ||
* will be respected. | ||
*/ | ||
previous: <Data = unknown>( | ||
current: Column<Data>, | ||
requester?: Plugin<any> | ||
): Column<Data> | undefined => { | ||
let columns = requester ? columnsFor(current.table, requester) : columnsFor(current.table); | ||
let referenceIndex = columns.indexOf(current); | ||
|
||
assert( | ||
`index of reference column must be >= 0. column likely not a part of the table`, | ||
referenceIndex >= 0 | ||
); | ||
|
||
/** | ||
* There can be nothing before the first column | ||
*/ | ||
if (referenceIndex === 0) { | ||
return undefined; | ||
} | ||
|
||
return columns[referenceIndex - 1]; | ||
}, | ||
/** | ||
* for a given current or reference column, return the columns that | ||
* should appear before, or to the left of that column. | ||
* | ||
* if a plugin class is provided, the hierarchy of column list modifications | ||
* will be respected. | ||
*/ | ||
before: <Data = unknown>(current: Column<Data>, requester?: Plugin<any>): Column<Data>[] => { | ||
let columns = requester ? columnsFor(current.table, requester) : columnsFor(current.table); | ||
|
||
let referenceIndex = columns.indexOf(current); | ||
|
||
return columns.slice(0, referenceIndex); | ||
}, | ||
/** | ||
* for a given current or reference column, return the columns that | ||
* should appear after, or to the right of that column. | ||
* | ||
* if a plugin class is provided, the hierarchy of column list modifications | ||
* will be respected. | ||
*/ | ||
after: <Data = unknown>(current: Column<Data>, requester?: Plugin<any>): Column<Data>[] => { | ||
let columns = requester ? columnsFor(current.table, requester) : columnsFor(current.table); | ||
|
||
let referenceIndex = columns.indexOf(current); | ||
|
||
return columns.slice(referenceIndex + 1); | ||
}, | ||
}; | ||
|
||
export const meta = { | ||
/** | ||
* @public | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the code that powers all the refactors in this PR.
It's also some of the messier code.
I wasn't really in a headspace to do recursive trees, so this is what works for now.
If we add another plugin that needs to tweak columns rendered, then we'll have to do this proper