Skip to content

Commit

Permalink
tree: use dot decoration on folders
Browse files Browse the repository at this point in the history
The following commit updates the rendering of tail decorations for
composite tree nodes (nodes that contain children nodes like folders).

The following updates the rendering of tail decorations for composite
tree nodes (nodes that contain children nodes like folders).  The
updates include :
- rendering a generic icon decoration (dot) for composite nodes with children with decoration data.
- updates to the rendering logic to only render the decoration data with the highest priority so no duplicate generic icons are present.
  • Loading branch information
OmarSdt-EC committed May 12, 2021
1 parent 3e5ce53 commit 01e816d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 34 deletions.
95 changes: 61 additions & 34 deletions packages/core/src/browser/tree/tree-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -729,54 +729,81 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
* @param node the tree node.
* @param icon the icon.
*/
protected decorateIcon(node: TreeNode, icon: React.ReactNode | null): React.ReactNode {
// eslint-disable-next-line no-null/no-null
if (icon === null) {
// eslint-disable-next-line no-null/no-null
return null;
protected decorateIcon(node: TreeNode, icon: React.ReactNode): React.ReactNode {
// icon can be null or undefined
if (!icon) {
return;
}

const overlayIcons: React.ReactNode[] = [];
new Map(this.getDecorationData(node, 'iconOverlay').reverse().filter(notEmpty)
.map(overlay => [overlay.position, overlay] as [TreeDecoration.IconOverlayPosition, TreeDecoration.IconOverlay | TreeDecoration.IconClassOverlay]))
.forEach((overlay, position) => {
const iconClasses = [TreeDecoration.Styles.DECORATOR_SIZE_CLASS, TreeDecoration.IconOverlayPosition.getStyle(position)];
const style = (color?: string) => color === undefined ? {} : { color };
if (overlay.background) {
overlayIcons.push(<span key={node.id + 'bg'} className={this.getIconClass(overlay.background.shape, iconClasses)} style={style(overlay.background.color)}>
</span>);
}
const overlayIcon = (overlay as TreeDecoration.IconOverlay).icon || (overlay as TreeDecoration.IconClassOverlay).iconClass;
overlayIcons.push(<span key={node.id} className={this.getIconClass(overlayIcon, iconClasses)} style={style(overlay.color)}></span>);
});

// if multiple overlays have the same overlay.position attribute, we'll de-duplicate those and only process the first one from the decoration array
const positionToOverlay = new Map<TreeDecoration.IconOverlayPosition, TreeDecoration.IconOverlay | TreeDecoration.IconClassOverlay>(
this.getDecorationData(node, 'iconOverlay').reverse().filter(notEmpty).map(overlay => [/* key */ overlay.position, /* value */ overlay])
);
for (const overlay of positionToOverlay.values()) {
const iconClasses = [TreeDecoration.Styles.DECORATOR_SIZE_CLASS, TreeDecoration.IconOverlayPosition.getStyle(overlay.position)];
const style = (color?: string) => color === undefined ? {} : { color };
if (overlay.background) {
overlayIcons.push(<span key={node.id + 'bg'} className={this.getIconClass(overlay.background.shape, iconClasses)} style={style(overlay.background.color)}></span>);
}
const overlayIcon = (overlay as TreeDecoration.IconOverlay).icon || (overlay as TreeDecoration.IconClassOverlay).iconClass;
overlayIcons.push(<span key={node.id} className={this.getIconClass(overlayIcon, iconClasses)} style={style(overlay.color)}></span>);
}
if (overlayIcons.length > 0) {
return <div className={TreeDecoration.Styles.ICON_WRAPPER_CLASS}>{icon}{overlayIcons}</div>;
}

return icon;
}

/**
* Get the generic icon decoration.
* @returns the class icon used for generic decorations.
*/
protected getGenericClassIconDecoration(): string[] {
return ['fa', 'fa-circle'];
}

/**
* Render the tree node tail decorations.
* @param node the tree node.
* @param props the node properties.
*/
protected renderTailDecorations(node: TreeNode, props: NodeProps): React.ReactNode {
return <React.Fragment>
{this.getDecorationData(node, 'tailDecorations').filter(notEmpty).reduce((acc, current) => acc.concat(current), []).map((decoration, index) => {
const { tooltip } = decoration;
const { data, fontData } = decoration as TreeDecoration.TailDecoration;
const color = (decoration as TreeDecoration.TailDecorationIcon).color;
const icon = (decoration as TreeDecoration.TailDecorationIcon).icon || (decoration as TreeDecoration.TailDecorationIconClass).iconClass;
const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS].join(' ');
const style = fontData ? this.applyFontStyles({}, fontData) : color ? { color } : undefined;
const content = data ? data : icon ? <span key={node.id + 'icon' + index} className={this.getIconClass(icon)}></span> : '';
return <div key={node.id + className + index} className={className} style={style} title={tooltip}>
{content}
</div>;
})}
</React.Fragment>;
const decorationData = this.getDecorationData(node, 'tailDecorations').filter(notEmpty).reduce((acc, current) => acc.concat(current), []);

if (decorationData.length === 0) {
return;
}

if (CompositeTreeNode.is(node)) {
// If the node is a composite, we just want to use the decorationData with the highest priority (last element).
const decoration = decorationData.pop()!;
const { tooltip } = decoration as TreeDecoration.TailDecoration;
const { fontData } = decoration as TreeDecoration.TailDecoration;
const color = (decoration as TreeDecoration.TailDecorationIcon).color;
const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS].join(' ');
const style = fontData ? this.applyFontStyles({}, fontData) : color ? { color } : undefined;
const icon = this.getGenericClassIconDecoration();
const content = <span className={this.getIconClass(icon, [TreeDecoration.Styles.DECORATOR_SIZE_CLASS])}></span>;
return <div className={className} style={style} title={tooltip}>
{content}
</div>;
} else {
return <React.Fragment>
{decorationData.map((decoration, index) => {
// If the node is not a composite, we apply the map method on the array of DecorationData.
const { tooltip } = decoration;
const { data, fontData } = decoration as TreeDecoration.TailDecoration;
const color = (decoration as TreeDecoration.TailDecorationIcon).color;
const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS].join(' ');
const style = fontData ? this.applyFontStyles({}, fontData) : color ? { color } : undefined;
const icon = (decoration as TreeDecoration.TailDecorationIcon).icon || (decoration as TreeDecoration.TailDecorationIconClass).iconClass;
const content = data ? data : icon ? <span key={node.id + 'icon' + index} className={this.getIconClass(icon)}></span> : '';
return <div key={node.id + className + index} className={className} style={style} title={tooltip}>
{content}
</div>;
})}
</React.Fragment>;
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/markers/src/browser/problem/problem-decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ export class ProblemDecorator implements TreeDecorator {
fontData: {
color,
},
tailDecorations: [
{
data: 'allo',
color
}
],
iconOverlay: {
position,
icon,
Expand Down

0 comments on commit 01e816d

Please sign in to comment.