n8n/packages/editor-ui/src/components/NodeCreator/helpers.ts

176 lines
5.2 KiB
TypeScript
Raw Normal View History

:sparkles: Change the UI of the Nodes Panel (#1855) * Add codex search properties to node types * implement basic styles * update header designs * update node list designs * add trigger icon * refactor node creator list * implement categories and subcategories * fix up spacing * add arrows * implement navigatable list * implement more of feature * implement navigation * add transitions * fix lint issues * fix overlay * :zap: Get and add codex categories * fix up design * update borders * implement no-matches view * fix preview bug * add color to search * clean up borders * add comma * Revert "Merge branch 'add-codex-data' of github.com:n8n-io/n8n into PROD-819-nodes-panel-redesign" 38b7d7ead19ab069f3f00a1ae6b6267eee55122a * use new impl * remove empty categories * update scrolling, hide start node * make scrollable * remove text while subcategory panel is open * fix up spacing * fix lint issues * update descriptions * update path * update images * fix tags manager * give min height to image * gst * update clear color * update font size * fix firefox spacing * close on click outside * add external link icon * update iterator key * add client side caching for images * update caching header * ⚡️ Add properties to codex for nodes panel (#1854) * :zap: Get and add codex categories * :zap: Add parens to evaluation + destructuring * :fire: Remove non-existing class reference * :zap: Add alias to codex property * move constants * :hammer: Rename CodexCategories to CodexData * :pencil2: Update getCodex documentation * refactor and move * refactor no results view * more refactoring * refactor subcategory panel * more refactoring * update text * update no results view * add miscellaneous to end of list * address design feedback * reimplement node search * fix up clear * update placeholder color * impl transition * focus on tab * update spacing * fix transition bug on start * fix up x * fix position * build * safari fix * remove input changes * css bleed issue with image * update css value * clean up * simplify impl * rename again * rename again * rename all * fix hover bug * remove keep alive * delete icon * update interface type * refactor components * update scss to module * clean up impl * clean up colors as vars * fix indentation * clean up scss * clean up scss * clean up scss * clean up scss * Clean up files * update logic to be more efficient * fix search bug * update type * remove unused * clean up js * update scrollable, border impl, transition * fix simicolon * build * update search * address max's comments * change icon border radius * change margin * update icon size * update icon size * update slide transition out * add comma * remove full * update trigger icon size * fix image size * address design feedback * update external link icons * address codacy issues * support custom nodes without codex file * address jan's feedback * address Ben's comments * add subcategory index * open/close categories with arrow keys * add lint comment * Address latest comments * :zap: Minor changes Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Mutasem <mutdmour@gmail.com> Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
2021-06-17 22:58:26 -07:00
import { CORE_NODES_CATEGORY, CUSTOM_NODES_CATEGORY, SUBCATEGORY_DESCRIPTIONS, UNCATEGORIZED_CATEGORY, UNCATEGORIZED_SUBCATEGORY, REGULAR_NODE_FILTER, TRIGGER_NODE_FILTER, ALL_NODE_FILTER } from '@/constants';
import { INodeCreateElement, ICategoriesWithNodes, INodeItemProps } from '@/Interface';
import { INodeTypeDescription } from 'n8n-workflow';
export const getCategoriesWithNodes = (nodeTypes: INodeTypeDescription[]): ICategoriesWithNodes => {
return nodeTypes.reduce(
(accu: ICategoriesWithNodes, nodeType: INodeTypeDescription) => {
if (!nodeType.codex || !nodeType.codex.categories) {
accu[UNCATEGORIZED_CATEGORY][UNCATEGORIZED_SUBCATEGORY].nodes.push({
type: 'node',
category: UNCATEGORIZED_CATEGORY,
key: `${UNCATEGORIZED_CATEGORY}_${nodeType.name}`,
properties: {
subcategory: UNCATEGORIZED_SUBCATEGORY,
nodeType,
},
includedByTrigger: nodeType.group.includes('trigger'),
includedByRegular: !nodeType.group.includes('trigger'),
});
return accu;
}
nodeType.codex.categories.forEach((_category: string) => {
const category = _category.trim();
const subcategory =
nodeType.codex &&
nodeType.codex.subcategories &&
nodeType.codex.subcategories[category]
? nodeType.codex.subcategories[category][0]
: UNCATEGORIZED_SUBCATEGORY;
if (!accu[category]) {
accu[category] = {};
}
if (!accu[category][subcategory]) {
accu[category][subcategory] = {
triggerCount: 0,
regularCount: 0,
nodes: [],
};
}
const isTrigger = nodeType.group.includes('trigger');
if (isTrigger) {
accu[category][subcategory].triggerCount++;
}
if (!isTrigger) {
accu[category][subcategory].regularCount++;
}
accu[category][subcategory].nodes.push({
type: 'node',
key: `${category}_${nodeType.name}`,
category,
properties: {
nodeType,
subcategory,
},
includedByTrigger: isTrigger,
includedByRegular: !isTrigger,
});
});
return accu;
},
{
[UNCATEGORIZED_CATEGORY]: {
[UNCATEGORIZED_SUBCATEGORY]: {
triggerCount: 0,
regularCount: 0,
nodes: [],
},
},
},
);
};
const getCategories = (categoriesWithNodes: ICategoriesWithNodes): string[] => {
const categories = Object.keys(categoriesWithNodes);
const sorted = categories.filter(
(category: string) =>
category !== CORE_NODES_CATEGORY && category !== CUSTOM_NODES_CATEGORY && category !== UNCATEGORIZED_CATEGORY,
);
sorted.sort();
return [CORE_NODES_CATEGORY, CUSTOM_NODES_CATEGORY, ...sorted, UNCATEGORIZED_CATEGORY];
};
export const getCategorizedList = (categoriesWithNodes: ICategoriesWithNodes): INodeCreateElement[] => {
const categories = getCategories(categoriesWithNodes);
return categories.reduce(
(accu: INodeCreateElement[], category: string) => {
if (!categoriesWithNodes[category]) {
return accu;
}
const categoryEl: INodeCreateElement = {
type: 'category',
key: category,
category,
properties: {
expanded: false,
},
};
const subcategories = Object.keys(categoriesWithNodes[category]);
if (subcategories.length === 1) {
const subcategory = categoriesWithNodes[category][
subcategories[0]
];
if (subcategory.triggerCount > 0) {
categoryEl.includedByTrigger = subcategory.triggerCount > 0;
}
if (subcategory.regularCount > 0) {
categoryEl.includedByRegular = subcategory.regularCount > 0;
}
return [...accu, categoryEl, ...subcategory.nodes];
}
subcategories.sort();
const subcategorized = subcategories.reduce(
(accu: INodeCreateElement[], subcategory: string) => {
const subcategoryEl: INodeCreateElement = {
type: 'subcategory',
key: `${category}_${subcategory}`,
category,
properties: {
subcategory,
description: SUBCATEGORY_DESCRIPTIONS[category][subcategory],
},
includedByTrigger: categoriesWithNodes[category][subcategory].triggerCount > 0,
includedByRegular: categoriesWithNodes[category][subcategory].regularCount > 0,
};
if (subcategoryEl.includedByTrigger) {
categoryEl.includedByTrigger = true;
}
if (subcategoryEl.includedByRegular) {
categoryEl.includedByRegular = true;
}
accu.push(subcategoryEl);
return accu;
},
[],
);
return [...accu, categoryEl, ...subcategorized];
},
[],
);
};
export const matchesSelectType = (el: INodeCreateElement, selectedType: string) => {
if (selectedType === REGULAR_NODE_FILTER && el.includedByRegular) {
return true;
}
if (selectedType === TRIGGER_NODE_FILTER && el.includedByTrigger) {
return true;
}
return selectedType === ALL_NODE_FILTER;
};
const matchesAlias = (nodeType: INodeTypeDescription, filter: string): boolean => {
if (!nodeType.codex || !nodeType.codex.alias) {
return false;
}
return nodeType.codex.alias.reduce((accu: boolean, alias: string) => {
return accu || alias.toLowerCase().indexOf(filter) > -1;
}, false);
};
export const matchesNodeType = (el: INodeCreateElement, filter: string) => {
const nodeType = (el.properties as INodeItemProps).nodeType;
return nodeType.displayName.toLowerCase().indexOf(filter) !== -1 || matchesAlias(nodeType, filter);
};