Skip to content

Commit

Permalink
[ACA-2064] support custom icons for extensions (#864)
Browse files Browse the repository at this point in the history
* icon component, custom svg

* split components, fix modules

* simplify code

* universal icon component

* support custom icon registration

* update docs

* test fixes
  • Loading branch information
DenysVuika authored Dec 7, 2018
1 parent ec3eeb7 commit 99a8192
Show file tree
Hide file tree
Showing 30 changed files with 497 additions and 221 deletions.
1 change: 1 addition & 0 deletions docs/extending/extensibility-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ You can create plugins that change, toggle, or extend the following areas:
- buttons
- "More actions" buttons
- Content metadata presets (used on `Properties` tab)
- Custom icons

Extensions can also:

Expand Down
42 changes: 42 additions & 0 deletions docs/extending/icons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Custom Icons

You can register and use custom `.svg` icons with toolbars, context menus, etc.

The icons are declared in the `features.icons` section, for example:

```json
{
"features": {
"icons": [
{
"id": "adf:join_library",
"value": "./assets/images/join-library.svg"
},
{
"id": "adf:move_file",
"value": "./assets/images/adf-move-file-24px.svg"
}
]
}
}
```

The `id` value must conform to the format `[namespace]:[name]`,
similar to that of the [Material Icon](https://material.angular.io/components/icon/api) component.
The icon file path should be relative to the deployed application root (or `index.html` file);

After that, you can use the icon id with other elements, for example:

```json
{
"id": "app.toolbar.move",
"order": 500,
"title": "APP.ACTIONS.MOVE",
"icon": "adf:move_file",
"actions": {
"click": "MOVE_NODES"
}
}
```

It is also possible to override the icon value or disable the entry from within external extensions.
65 changes: 33 additions & 32 deletions docs/toc.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
- [Home](/)
- [Documentation](/#documentation)
- [How to contribute](/#how-to-contribute)
- [Documentation](/#documentation)
- [How to contribute](/#how-to-contribute)
- [App features](/features/)
- [User interface layout](/features/user-interface-layout)
- [Header](/features/header)
- [Side navigation](/features/side-navigation)
- [Document List Layout](/features/document-list-layout)
- [File Viewer](/features/file-viewer)
- [Info Drawer](/features/info-drawer)
- [Version Manager](/features/version-manager)
- [Search results](/features/search-results)
- [User interface layout](/features/user-interface-layout)
- [Header](/features/header)
- [Side navigation](/features/side-navigation)
- [Document List Layout](/features/document-list-layout)
- [File Viewer](/features/file-viewer)
- [Info Drawer](/features/info-drawer)
- [Version Manager](/features/version-manager)
- [Search results](/features/search-results)
- [Getting started](/getting-started/)
- [Prerequisites](/getting-started/prerequisites)
- [Building from source](/getting-started/building-from-source)
- [Internationalization (i18n)](/getting-started/internationalization)
- [CORS](/getting-started/cors)
- [Configuration](/getting-started/configuration)
- [Navigation](/getting-started/navigation)
- [Docker](/getting-started/docker)
- [Prerequisites](/getting-started/prerequisites)
- [Building from source](/getting-started/building-from-source)
- [Internationalization (i18n)](/getting-started/internationalization)
- [CORS](/getting-started/cors)
- [Configuration](/getting-started/configuration)
- [Navigation](/getting-started/navigation)
- [Docker](/getting-started/docker)
- [Extending](/extending/)
- [Extensibility features](/extending/extensibility-features)
- [Extension format](/extending/extension-format)
- [Routes](/extending/routes)
- [Components](/extending/components)
- [Actions](/extending/actions)
- [Application actions](/extending/application-actions)
- [Rules](/extending/rules)
- [Application features](/extending/application-features)
- [Registration](/extending/registration)
- [Creating custom evaluators](/extending/creating-custom-evaluators)
- [Tutorials](/extending/tutorials)
- [Redistributable libraries](/extending/redistributable-libraries)
- [Extensibility features](/extending/extensibility-features)
- [Extension format](/extending/extension-format)
- [Routes](/extending/routes)
- [Components](/extending/components)
- [Actions](/extending/actions)
- [Application actions](/extending/application-actions)
- [Rules](/extending/rules)
- [Application features](/extending/application-features)
- [Custom icons](/extending/icons)
- [Registration](/extending/registration)
- [Creating custom evaluators](/extending/creating-custom-evaluators)
- [Tutorials](/extending/tutorials)
- [Redistributable libraries](/extending/redistributable-libraries)
- [Tutorials](/tutorials/)
- [Introduction to extending ACA](/tutorials/introduction-to-extending)
- [Custom route with parameters](/tutorials/custom-route-with-parameters)
- [Dialog actions](/tutorials/dialog-actions)
- [Introduction to extending ACA](/tutorials/introduction-to-extending)
- [Custom route with parameters](/tutorials/custom-route-with-parameters)
- [Dialog actions](/tutorials/dialog-actions)
- [Get help](/help)
24 changes: 24 additions & 0 deletions extension.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,24 @@
"type": "boolean"
}
}
},
"iconRef": {
"type": "object",
"required": ["id", "value"],
"properties": {
"id": {
"description": "Unique identifier. Must be in the format '[namespace]:[name]'.",
"type": "string"
},
"value": {
"description": "Icon path relative to the application root.",
"type": "string"
},
"disabled": {
"description": "Toggles the disabled state",
"type": "boolean"
}
}
}
},

Expand Down Expand Up @@ -628,6 +646,12 @@
"description": "Application-specific features and extensions",
"type": "object",
"properties": {
"icons": {
"description": "Custom icons",
"type": "array",
"items": { "$ref": "#/definitions/iconRef" },
"minItems": 1
},
"header": {
"description": "Application header extensions",
"type": "array",
Expand Down
17 changes: 2 additions & 15 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ import { AppSearchResultsModule } from './components/search/search-results.modul
import { AppLoginModule } from './components/login/login.module';
import { AppHeaderModule } from './components/header/header.module';
import { environment } from '../environments/environment';
import { LibraryMembershipDirective } from './directives/library-membership.directive';
import { ToggleJoinLibraryComponent } from './components/toolbar/toggle-join-library/toggle-join-library.component';
import { LibraryFavoriteDirective } from './directives/library-favorite.directive';
import { ToggleFavoriteLibraryComponent } from './components/toolbar/toggle-favorite-library/toggle-favorite-library.component';
import { AppDataService } from './services/data.service';

@NgModule({
Expand Down Expand Up @@ -118,11 +114,7 @@ import { AppDataService } from './services/data.service';
LibrariesComponent,
FavoriteLibrariesComponent,
NodeVersionsDialogComponent,
LibraryDialogComponent,
LibraryMembershipDirective,
ToggleJoinLibraryComponent,
LibraryFavoriteDirective,
ToggleFavoriteLibraryComponent
LibraryDialogComponent
],
providers: [
{ provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy },
Expand All @@ -137,12 +129,7 @@ import { AppDataService } from './services/data.service';
}
}
],
entryComponents: [
LibraryDialogComponent,
NodeVersionsDialogComponent,
ToggleJoinLibraryComponent,
ToggleFavoriteLibraryComponent
],
entryComponents: [LibraryDialogComponent, NodeVersionsDialogComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
10 changes: 7 additions & 3 deletions src/app/components/common/common.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ import { LibraryStatusColumnComponent } from './library-status-column/library-st
import { LibraryRoleColumnComponent } from './library-role-column/library-role-column.component';
import { TrashcanNameColumnComponent } from './trashcan-name-column/trashcan-name-column.component';
import { DynamicColumnComponent } from './dynamic-column/dynamic-column.component';
import { IconComponent } from './icon/icon.component';
import { MatIconModule } from '@angular/material';

@NgModule({
imports: [CommonModule, CoreModule.forChild()],
imports: [CommonModule, CoreModule.forChild(), MatIconModule],
declarations: [
GenericErrorComponent,
LocationLinkComponent,
Expand All @@ -45,7 +47,8 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen
LibraryStatusColumnComponent,
LibraryRoleColumnComponent,
TrashcanNameColumnComponent,
DynamicColumnComponent
DynamicColumnComponent,
IconComponent
],
exports: [
GenericErrorComponent,
Expand All @@ -55,7 +58,8 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen
LibraryStatusColumnComponent,
LibraryRoleColumnComponent,
TrashcanNameColumnComponent,
DynamicColumnComponent
DynamicColumnComponent,
IconComponent
],
entryComponents: [
LocationLinkComponent,
Expand Down
7 changes: 7 additions & 0 deletions src/app/components/common/icon/icon.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ng-container *ngIf="isCustom; else: default">
<mat-icon [svgIcon]="value"></mat-icon>
</ng-container>

<ng-template #default>
<mat-icon>{{ value }}</mat-icon>
</ng-template>
4 changes: 4 additions & 0 deletions src/app/components/common/icon/icon.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.adf-icon {
display: inline-flex;
vertical-align: middle;
}
58 changes: 58 additions & 0 deletions src/app/components/common/icon/icon.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/

import {
Component,
Input,
ViewEncapsulation,
ChangeDetectionStrategy
} from '@angular/core';

@Component({
selector: 'adf-icon',
templateUrl: './icon.component.html',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'adf-icon' },
styleUrls: ['./icon.component.scss']
})
export class IconComponent {
private _value = '';
private _isCustom = false;

get value(): string {
return this._value;
}

@Input()
set value(value: string) {
this._value = value || 'settings';
this._isCustom = this._value.includes(':');
}

get isCustom(): boolean {
return this._isCustom;
}
}
63 changes: 31 additions & 32 deletions src/app/components/context-menu/context-menu-item.component.html
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
<div class="aca-context-menu">
<ng-container [ngSwitch]="actionRef.type">
<ng-container [ngSwitch]="actionRef.type">
<ng-container *ngSwitchCase="'menu'">
<button mat-menu-item [id]="actionRef.id" [matMenuTriggerFor]="childMenu">
<adf-icon [value]="actionRef.icon"></adf-icon>
<span>{{ actionRef.title | translate }}</span>
</button>

<ng-container *ngSwitchCase="'menu'">
<button
mat-menu-item
[id]="actionRef.id"
[matMenuTriggerFor]="childMenu">
<mat-icon color="primary">{{ actionRef.icon }}</mat-icon>
<span>{{ actionRef.title | translate }}</span>
</button>

<mat-menu #childMenu="matMenu">
<ng-container *ngFor="let child of actionRef.children; trackBy: trackById">
<app-context-menu-item [actionRef]="child"></app-context-menu-item>
</ng-container>
</mat-menu>
</ng-container>

<ng-container *ngSwitchCase="'separator'">
<mat-divider></mat-divider>
<mat-menu #childMenu="matMenu">
<ng-container
*ngFor="let child of actionRef.children; trackBy: trackById"
>
<app-context-menu-item [actionRef]="child"></app-context-menu-item>
</ng-container>
</mat-menu>
</ng-container>

<ng-container *ngSwitchCase="'custom'">
<adf-dynamic-component [id]="actionRef.component"></adf-dynamic-component>
</ng-container>
<ng-container *ngSwitchCase="'separator'">
<mat-divider></mat-divider>
</ng-container>

<ng-container *ngSwitchCase="'custom'">
<adf-dynamic-component [id]="actionRef.component"></adf-dynamic-component>
</ng-container>

<ng-container *ngSwitchDefault>
<button mat-menu-item
color="primary"
[id]="actionRef.id"
(click)="runAction()">
<mat-icon color="primary">{{ actionRef.icon }}</mat-icon>
<span>{{ actionRef.title | translate }}</span>
</button>
</ng-container>
<ng-container *ngSwitchDefault>
<button
mat-menu-item
color="primary"
[id]="actionRef.id"
(click)="runAction()"
>
<adf-icon [value]="actionRef.icon"></adf-icon>
<span>{{ actionRef.title | translate }}</span>
</button>
</ng-container>
</div>
</ng-container>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ describe('ContextMenuComponent', () => {
fixture.detectChanges();

const buttonElement = fixture.nativeElement.querySelector('button');
expect(buttonElement.innerText.trim()).toBe(contextItem.title);
expect(buttonElement.querySelector('span').innerText.trim()).toBe(
contextItem.title
);
});

it('should not run action when entry has no click attribute defined', () => {
Expand Down
Loading

0 comments on commit 99a8192

Please sign in to comment.