Introduce support for categorizing plugins

Summary:
This PR introduces the possibility to group plugins in categories.
The category can be determined by setting the `category` field in `package.json`.
Categories are sorted alphabetically.
Categories are shown below all uncategorized items.
Within categories, items are sorted as before: by last recently usage.

Design wise, the category name might now look more prominent than the app name, this is to be addressed in a follow up PR.

Reviewed By: jknoxville

Differential Revision: D18169459

fbshipit-source-id: 77deb0f27a0462a0d449944ddc262396160687a2
This commit is contained in:
Michel Weststrate
2019-10-28 06:20:47 -07:00
committed by Facebook Github Bot
parent b073c90e24
commit 494ffd26b3
4 changed files with 78 additions and 37 deletions

View File

@@ -52,6 +52,7 @@ you can also specify a title to show in the Flipper sidebar and an icon to displ
"keywords": ["flipper-plugin"], "keywords": ["flipper-plugin"],
"icon": "apps", "icon": "apps",
"title": "Sea Mammals", "title": "Sea Mammals",
"category": "Example Plugin",
"dependencies": { "dependencies": {
"flipper": "latest" "flipper": "latest"
} }

View File

@@ -118,6 +118,12 @@ const PluginName = styled(Text)(
}), }),
); );
const CategoryName = styled(PluginName)({
color: colors.macOSSidebarSectionTitle,
textTransform: 'uppercase',
fontSize: '0.9em',
});
const Plugins = styled(FlexColumn)({ const Plugins = styled(FlexColumn)({
flexGrow: 1, flexGrow: 1,
overflow: 'auto', overflow: 'auto',
@@ -386,34 +392,45 @@ class MainSidebar extends PureComponent<Props, State> {
return ( return (
<React.Fragment key={client.id}> <React.Fragment key={client.id}>
<SidebarHeader>{client.query.app}</SidebarHeader> <SidebarHeader>{client.query.app}</SidebarHeader>
{plugins {groupPluginsByCategory(
.sort((a: typeof FlipperPlugin, b: typeof FlipperPlugin) => plugins
client.byClientLRU(plugins.length, a, b), .sort(
) (a: typeof FlipperPlugin, b: typeof FlipperPlugin) =>
.slice( client.byClientLRU(plugins.length, a, b),
0, )
client.showAllPlugins .slice(
? client.plugins.length 0,
: minShowPluginsCount, client.showAllPlugins
) ? client.plugins.length
.map((plugin: typeof FlipperPlugin) => ( : minShowPluginsCount,
<PluginSidebarListItem ),
key={plugin.id} ).map(([category, plugins]) => (
isActive={ <>
plugin.id === selectedPlugin && {category && (
selectedApp === client.id <ListItem>
} <CategoryName>{category}</CategoryName>
onClick={() => </ListItem>
selectPlugin({ )}
selectedPlugin: plugin.id, {plugins.map(plugin => (
selectedApp: client.id, <PluginSidebarListItem
deepLinkPayload: null, key={plugin.id}
}) isActive={
} plugin.id === selectedPlugin &&
plugin={plugin} selectedApp === client.id
app={client.query.app} }
/> onClick={() =>
))} selectPlugin({
selectedPlugin: plugin.id,
selectedApp: client.id,
deepLinkPayload: null,
})
}
plugin={plugin}
app={client.query.app}
/>
))}
</>
))}
{plugins.length > minShowPluginsCount && ( {plugins.length > minShowPluginsCount && (
<PluginShowMoreOrLess <PluginShowMoreOrLess
onClick={() => onClick={() =>
@@ -452,6 +469,27 @@ class MainSidebar extends PureComponent<Props, State> {
} }
} }
type PluginsByCategory = [string, (typeof FlipperPlugin)[]][];
function groupPluginsByCategory(
plugins: (typeof FlipperPlugin)[],
): PluginsByCategory {
// Pre condition: plugins are already sorted globally
const byCategory: {[cat: string]: (typeof FlipperPlugin)[]} = {};
const res: PluginsByCategory = [];
plugins.forEach(plugin => {
const category = plugin.category || '';
(byCategory[category] || (byCategory[category] = [])).push(plugin);
});
// Sort categories
Object.keys(byCategory)
.sort()
.forEach(category => {
res.push([category, byCategory[category]]);
});
return res;
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>( export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
({ ({
application: {windowIsFocused}, application: {windowIsFocused},

View File

@@ -77,6 +77,7 @@ export abstract class FlipperBasePlugin<
> extends Component<Props<PersistedState>, State> { > extends Component<Props<PersistedState>, State> {
abstract ['constructor']: any; abstract ['constructor']: any;
static title: string | null = null; static title: string | null = null;
static category: string | null = null;
static id: string = ''; static id: string = '';
static icon: string | null = null; static icon: string | null = null;
static gatekeeper: string | null = null; static gatekeeper: string | null = null;

View File

@@ -1,12 +1,13 @@
{ {
"name": "sea-mammals", "name": "sea-mammals",
"version": "1.0.0", "version": "1.0.0",
"main": "index.tsx", "main": "index.tsx",
"license": "MIT", "license": "MIT",
"keywords": ["flipper-plugin"], "keywords": ["flipper-plugin"],
"icon": "apps", "icon": "apps",
"title": "Sea Mammals", "title": "Sea Mammals",
"bugs": { "category": "Example Plugin",
"email": "realpassy@fb.com" "bugs": {
} "email": "realpassy@fb.com"
} }
}