ngx-edu-sharing-ui 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/.browserslistrc +16 -0
  2. package/.eslintrc.json +44 -0
  3. package/README.md +40 -0
  4. package/assets/scss/mixins.scss +95 -0
  5. package/assets/scss/variables.scss +33 -0
  6. package/karma.conf.js +42 -0
  7. package/ng-package.json +10 -0
  8. package/package.json +19 -0
  9. package/src/lib/actionbar/actionbar.component.html +59 -0
  10. package/src/lib/actionbar/actionbar.component.scss +123 -0
  11. package/src/lib/actionbar/actionbar.component.ts +174 -0
  12. package/src/lib/common/edu-sharing-ui-common.module.ts +80 -0
  13. package/src/lib/directives/border-box-observer.directive.ts +75 -0
  14. package/src/lib/directives/check-text-overflow.directive.ts +61 -0
  15. package/src/lib/directives/drag-nodes/drag-nodes.ts +32 -0
  16. package/src/lib/directives/drag-nodes/nodes-drag-source.directive.ts +79 -0
  17. package/src/lib/directives/drag-nodes/nodes-drag.directive.ts +43 -0
  18. package/src/lib/directives/drag-nodes/nodes-drop-target.directive.ts +116 -0
  19. package/src/lib/directives/focus-state.directive.ts +34 -0
  20. package/src/lib/directives/icon.directive.ts +142 -0
  21. package/src/lib/directives/nodes-drop-target-legacy.directive.ts +155 -0
  22. package/src/lib/dropdown/dropdown.component.html +32 -0
  23. package/src/lib/dropdown/dropdown.component.scss +67 -0
  24. package/src/lib/dropdown/dropdown.component.ts +71 -0
  25. package/src/lib/edu-sharing-ui-configuration.ts +47 -0
  26. package/src/lib/edu-sharing-ui.module.ts +49 -0
  27. package/src/lib/list-items/available-widgets.ts +30 -0
  28. package/src/lib/list-items/format-duration.pipe.ts +17 -0
  29. package/src/lib/list-items/list-base/list-base.component.html +52 -0
  30. package/src/lib/list-items/list-base/list-base.component.ts +44 -0
  31. package/src/lib/list-items/list-collection-info/list-collection-info.component.html +48 -0
  32. package/src/lib/list-items/list-collection-info/list-collection-info.component.scss +8 -0
  33. package/src/lib/list-items/list-collection-info/list-collection-info.component.ts +24 -0
  34. package/src/lib/list-items/list-counts/list-counts.component.html +1 -0
  35. package/src/lib/list-items/list-counts/list-counts.component.scss +3 -0
  36. package/src/lib/list-items/list-counts/list-counts.component.ts +59 -0
  37. package/src/lib/list-items/list-items.module.ts +33 -0
  38. package/src/lib/list-items/list-node-license/list-node-license.component.html +8 -0
  39. package/src/lib/list-items/list-node-license/list-node-license.component.ts +47 -0
  40. package/src/lib/list-items/list-node-replication-source/list-node-replication-source.component.html +11 -0
  41. package/src/lib/list-items/list-node-replication-source/list-node-replication-source.component.ts +60 -0
  42. package/src/lib/list-items/list-node-workflow/list-node-workflow.component.html +3 -0
  43. package/src/lib/list-items/list-node-workflow/list-node-workflow.component.ts +21 -0
  44. package/src/lib/list-items/list-text/list-text.component.html +176 -0
  45. package/src/lib/list-items/list-text/list-text.component.scss +3 -0
  46. package/src/lib/list-items/list-text/list-text.component.ts +107 -0
  47. package/src/lib/list-items/list-widget.ts +52 -0
  48. package/src/lib/list-items/node-row/node-row.component.html +31 -0
  49. package/src/lib/list-items/node-row/node-row.component.scss +50 -0
  50. package/src/lib/list-items/node-row/node-row.component.ts +16 -0
  51. package/src/lib/list-items/node-source.pipe.ts +48 -0
  52. package/src/lib/node-entries/combined-data-source.ts +51 -0
  53. package/src/lib/node-entries/custom-templates-data-source.ts +6 -0
  54. package/src/lib/node-entries/drag-preview/drag-preview.component.html +6 -0
  55. package/src/lib/node-entries/drag-preview/drag-preview.component.scss +35 -0
  56. package/src/lib/node-entries/drag-preview/drag-preview.component.ts +15 -0
  57. package/src/lib/node-entries/entries-model.ts +120 -0
  58. package/src/lib/node-entries/items-cap.ts +54 -0
  59. package/src/lib/node-entries/list-item-label.pipe.ts +28 -0
  60. package/src/lib/node-entries/mixins.scss +23 -0
  61. package/src/lib/node-entries/node-cache.spec.ts +199 -0
  62. package/src/lib/node-entries/node-cache.ts +81 -0
  63. package/src/lib/node-entries/node-data-source-remote.ts +33 -0
  64. package/src/lib/node-entries/node-data-source.ts +148 -0
  65. package/src/lib/node-entries/node-entries-card/node-entries-card.component.html +167 -0
  66. package/src/lib/node-entries/node-entries-card/node-entries-card.component.scss +28 -0
  67. package/src/lib/node-entries/node-entries-card/node-entries-card.component.ts +132 -0
  68. package/src/lib/node-entries/node-entries-card/node-entries-card.main.scss +261 -0
  69. package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.html +205 -0
  70. package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.scss +181 -0
  71. package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.ts +361 -0
  72. package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.html +100 -0
  73. package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.scss +46 -0
  74. package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.ts +40 -0
  75. package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.html +23 -0
  76. package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.scss +58 -0
  77. package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.ts +16 -0
  78. package/src/lib/node-entries/node-entries-global.service.ts +79 -0
  79. package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.html +25 -0
  80. package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.scss +32 -0
  81. package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.ts +31 -0
  82. package/src/lib/node-entries/node-entries-table/node-entries-table.component.html +270 -0
  83. package/src/lib/node-entries/node-entries-table/node-entries-table.component.scss +169 -0
  84. package/src/lib/node-entries/node-entries-table/node-entries-table.component.ts +333 -0
  85. package/src/lib/node-entries/node-entries-templates.service.ts +31 -0
  86. package/src/lib/node-entries/node-entries-wrapper.component.ts +363 -0
  87. package/src/lib/node-entries/node-entries.component.html +33 -0
  88. package/src/lib/node-entries/node-entries.component.scss +13 -0
  89. package/src/lib/node-entries/node-entries.component.ts +151 -0
  90. package/src/lib/node-entries/node-entries.module.ts +93 -0
  91. package/src/lib/node-entries/node-rating/node-rating.component.html +53 -0
  92. package/src/lib/node-entries/node-rating/node-rating.component.scss +31 -0
  93. package/src/lib/node-entries/node-rating/node-rating.component.ts +105 -0
  94. package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.html +39 -0
  95. package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.scss +44 -0
  96. package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.ts +43 -0
  97. package/src/lib/node-entries/node-type-badge/node-type-badge.component.html +31 -0
  98. package/src/lib/node-entries/node-type-badge/node-type-badge.component.scss +5 -0
  99. package/src/lib/node-entries/node-type-badge/node-type-badge.component.ts +36 -0
  100. package/src/lib/node-entries/option-button/option-button.component.ts +42 -0
  101. package/src/lib/node-entries/preview-image/preview-image.component.html +19 -0
  102. package/src/lib/node-entries/preview-image/preview-image.component.scss +31 -0
  103. package/src/lib/node-entries/preview-image/preview-image.component.ts +47 -0
  104. package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.html +27 -0
  105. package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.scss +9 -0
  106. package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.ts +26 -0
  107. package/src/lib/node-url/node-url.component.html +66 -0
  108. package/src/lib/node-url/node-url.component.scss +32 -0
  109. package/src/lib/node-url/node-url.component.ts +136 -0
  110. package/src/lib/pipes/file-size.pipe.ts +24 -0
  111. package/src/lib/pipes/format-date.pipe.ts +39 -0
  112. package/src/lib/pipes/node-icon.pipe.ts +11 -0
  113. package/src/lib/pipes/node-image-size.pipe.ts +18 -0
  114. package/src/lib/pipes/node-image.pipe.ts +71 -0
  115. package/src/lib/pipes/node-person-name.pipe.ts +41 -0
  116. package/src/lib/pipes/node-title.pipe.ts +12 -0
  117. package/src/lib/pipes/option-tooltip.pipe.ts +32 -0
  118. package/src/lib/pipes/replace-chars.pipe.ts +21 -0
  119. package/src/lib/pipes/vcard-name.pipe.ts +11 -0
  120. package/src/lib/services/abstract/app.service.ts +4 -0
  121. package/src/lib/services/abstract/keyboard-shortcuts.service.ts +10 -0
  122. package/src/lib/services/abstract/options-helper.service.ts +29 -0
  123. package/src/lib/services/abstract/toast.service.ts +5 -0
  124. package/src/lib/services/accessibility.service.ts +101 -0
  125. package/src/lib/services/local-events.service.ts +29 -0
  126. package/src/lib/services/node-entries.service.ts +172 -0
  127. package/src/lib/services/node-helper.service.ts +239 -0
  128. package/src/lib/services/nodes-drag-drop.service.ts +165 -0
  129. package/src/lib/services/options-helper-data.service.ts +186 -0
  130. package/src/lib/services/repo-url.service.ts +46 -0
  131. package/src/lib/services/temporary-storage.service.ts +58 -0
  132. package/src/lib/services/ui.service.ts +182 -0
  133. package/src/lib/sort-dropdown/sort-dropdown.component.html +22 -0
  134. package/src/lib/sort-dropdown/sort-dropdown.component.scss +47 -0
  135. package/src/lib/sort-dropdown/sort-dropdown.component.ts +42 -0
  136. package/src/lib/spinner/spinner.component.html +14 -0
  137. package/src/lib/spinner/spinner.component.scss +141 -0
  138. package/src/lib/spinner/spinner.component.ts +12 -0
  139. package/src/lib/translations/README.md +44 -0
  140. package/src/lib/translations/fallback-translation-handler.ts +7 -0
  141. package/src/lib/translations/languages.ts +6 -0
  142. package/src/lib/translations/translation-loader.spec.ts +352 -0
  143. package/src/lib/translations/translation-loader.ts +189 -0
  144. package/src/lib/translations/translation-source.ts +9 -0
  145. package/src/lib/translations/translations.module.ts +49 -0
  146. package/src/lib/translations/translations.service.spec.ts +152 -0
  147. package/src/lib/translations/translations.service.ts +188 -0
  148. package/src/lib/types/accessibillity.ts +15 -0
  149. package/src/lib/types/api-models.ts +4 -0
  150. package/src/lib/types/drag-drop.ts +22 -0
  151. package/src/lib/types/keyboard-shortcuts.ts +29 -0
  152. package/src/lib/types/list-item.ts +67 -0
  153. package/src/lib/types/option-item.ts +247 -0
  154. package/src/lib/types/workflow.ts +35 -0
  155. package/src/lib/util/DateHelper.spec.ts +112 -0
  156. package/src/lib/util/DateHelper.ts +197 -0
  157. package/src/lib/util/VCard.ts +277 -0
  158. package/src/lib/util/color-helper.ts +125 -0
  159. package/src/lib/util/duration-helper.spec.ts +35 -0
  160. package/src/lib/util/duration-helper.ts +98 -0
  161. package/src/lib/util/functions.ts +15 -0
  162. package/src/lib/util/helper.ts +60 -0
  163. package/src/lib/util/isNumeric.ts +13 -0
  164. package/src/lib/util/rest-helper.ts +28 -0
  165. package/src/lib/util/ui-animation.ts +154 -0
  166. package/src/lib/util/ui-constants.ts +20 -0
  167. package/src/module.ts +76 -0
  168. package/src/test.ts +28 -0
  169. package/tsconfig.lib.json +15 -0
  170. package/tsconfig.lib.prod.json +10 -0
  171. package/tsconfig.spec.json +17 -0
@@ -0,0 +1,59 @@
1
+ import { Component } from '@angular/core';
2
+ import { ListWidget } from '../list-widget';
3
+ import { ListItem } from '../../types/list-item';
4
+ import { StatisticsGroup } from 'ngx-edu-sharing-api';
5
+
6
+ @Component({
7
+ selector: 'es-list-counts',
8
+ templateUrl: './list-counts.component.html',
9
+ styleUrls: ['./list-counts.component.scss'],
10
+ })
11
+ export class ListCountsComponent extends ListWidget {
12
+ static supportedItems = [
13
+ new ListItem('NODE', 'counts.OVERALL'),
14
+ new ListItem('NODE', 'counts.VIEW_MATERIAL'),
15
+ new ListItem('NODE', 'counts.VIEW_MATERIAL_EMBEDDED'),
16
+ new ListItem('NODE', 'counts.VIEW_MATERIAL_PLAY_MEDIA'),
17
+ new ListItem('NODE', 'counts.DOWNLOAD_MATERIAL'),
18
+ ];
19
+
20
+ static getCountSingle(node: StatisticsGroup, id: string, group: number = null) {
21
+ const counts = (node as any).counts;
22
+ if (id === 'OVERALL') {
23
+ return Object.keys(counts.counts)
24
+ .map((c) => (group ? group : counts.counts[c]))
25
+ .reduce((a, b) => a + b);
26
+ }
27
+ return (group ? group : counts.counts[id]) || 0;
28
+ }
29
+ static getCount(node: StatisticsGroup, id: string) {
30
+ const counts = (node as any).counts;
31
+ let result = this.getCountSingle(node, id);
32
+ if (Object.keys(counts.groups)?.length > 0) {
33
+ const i1 = id;
34
+ if (counts.groups[i1]) {
35
+ const i2 = Object.keys(counts.groups[i1])[0];
36
+ if (counts.groups?.[i1]?.[i2]) {
37
+ result = Object.keys(counts.groups?.[i1]?.[i2])
38
+ .map(
39
+ (group) =>
40
+ (group || '-') +
41
+ ': ' +
42
+ this.getCountSingle(node, id, counts.groups?.[i1]?.[i2][group]),
43
+ )
44
+ .join('\n')
45
+ .trim();
46
+ }
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+
52
+ getId() {
53
+ return this.item.name.split('.')[1];
54
+ }
55
+
56
+ getCount() {
57
+ return ListCountsComponent.getCount(this.node as StatisticsGroup, this.getId());
58
+ }
59
+ }
@@ -0,0 +1,33 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { FormatDurationPipe } from './format-duration.pipe';
3
+ import { ListBaseComponent } from './list-base/list-base.component';
4
+ import { ListCollectionInfoComponent } from './list-collection-info/list-collection-info.component';
5
+ import { ListCountsComponent } from './list-counts/list-counts.component';
6
+ import { ListNodeLicenseComponent } from './list-node-license/list-node-license.component';
7
+ import { ListNodeReplicationSourceComponent } from './list-node-replication-source/list-node-replication-source.component';
8
+ import { ListNodeWorkflowComponent } from './list-node-workflow/list-node-workflow.component';
9
+ import { ListTextComponent } from './list-text/list-text.component';
10
+ import { NodeRowComponent } from './node-row/node-row.component';
11
+ import { NodeSourcePipe } from './node-source.pipe';
12
+ import { CommonModule } from '@angular/common';
13
+ import { EduSharingUiCommonModule } from '../common/edu-sharing-ui-common.module';
14
+ import { MatTooltipModule } from '@angular/material/tooltip';
15
+ import { TranslateModule } from '@ngx-translate/core';
16
+
17
+ @NgModule({
18
+ declarations: [
19
+ ListBaseComponent,
20
+ ListCollectionInfoComponent,
21
+ ListNodeLicenseComponent,
22
+ ListNodeReplicationSourceComponent,
23
+ ListNodeWorkflowComponent,
24
+ ListTextComponent,
25
+ ListCountsComponent,
26
+ NodeSourcePipe,
27
+ FormatDurationPipe,
28
+ NodeRowComponent,
29
+ ],
30
+ imports: [CommonModule, EduSharingUiCommonModule, MatTooltipModule, TranslateModule],
31
+ exports: [ListBaseComponent, ListTextComponent, FormatDurationPipe, NodeRowComponent],
32
+ })
33
+ export class ListItemsModule {}
@@ -0,0 +1,8 @@
1
+ <ng-container *ngIf="licenseIcon$ | async as licenseIcon">
2
+ <span *ngIf="indicatorIcons$ | async; else textRepresentation" [matTooltip]="tooltip$ | async"
3
+ ><img [alt]="tooltip$ | async" [src]="licenseIcon"
4
+ /></span>
5
+ <ng-template #textRepresentation>
6
+ <span>{{ tooltip$ | async }}</span>
7
+ </ng-template>
8
+ </ng-container>
@@ -0,0 +1,47 @@
1
+ import { Component } from '@angular/core';
2
+ import { ListWidget } from '../list-widget';
3
+ import { TranslateService } from '@ngx-translate/core';
4
+ import * as rxjs from 'rxjs';
5
+ import { map, switchMap } from 'rxjs/operators';
6
+ import { ListItem } from '../../types/list-item';
7
+ import { NodeHelperService } from '../../services/node-helper.service';
8
+ import { Node, RestConstants } from 'ngx-edu-sharing-api';
9
+ import { AccessibilityService } from '../../services/accessibility.service';
10
+
11
+ @Component({
12
+ selector: 'es-list-node-license',
13
+ templateUrl: './list-node-license.component.html',
14
+ })
15
+ export class ListNodeLicenseComponent extends ListWidget {
16
+ static supportedItems = [new ListItem('NODE', RestConstants.CCM_PROP_LICENSE)];
17
+
18
+ readonly licenseName$ = this.nodeSubject.pipe(
19
+ map((node) => this.nodeHelper.getLicenseName(node as Node)),
20
+ );
21
+
22
+ readonly licenseIcon$ = this.nodeSubject.pipe(
23
+ switchMap((node) => this.nodeHelper.getLicenseIcon(node as Node)),
24
+ );
25
+
26
+ readonly tooltip$ = rxjs.combineLatest([this.licenseName$, this.provideLabelSubject]).pipe(
27
+ switchMap(([licenseName, provideLabel]) => {
28
+ if (provideLabel) {
29
+ return this.translate
30
+ .get('NODE.ccm:commonlicense_key')
31
+ .pipe(map((commonLicenseKey) => `${commonLicenseKey}: ${licenseName}`));
32
+ } else {
33
+ return rxjs.of(licenseName);
34
+ }
35
+ }),
36
+ );
37
+
38
+ readonly indicatorIcons$ = this.accessibility.observe('indicatorIcons');
39
+
40
+ constructor(
41
+ private accessibility: AccessibilityService,
42
+ private nodeHelper: NodeHelperService,
43
+ private translate: TranslateService,
44
+ ) {
45
+ super();
46
+ }
47
+ }
@@ -0,0 +1,11 @@
1
+ <span *ngIf="indicatorIcons$ | async; else textRepresentation" [matTooltip]="tooltip$ | async">
2
+ <img
3
+ [src]="replicationSource$ | async | appNodeSource : { mode: 'url' }"
4
+ (error)="$any($event.target).style.display = 'none'"
5
+ [alt]="tooltip$ | async"
6
+ />
7
+ </span>
8
+
9
+ <ng-template #textRepresentation>
10
+ <span>{{ tooltip$ | async }}</span>
11
+ </ng-template>
@@ -0,0 +1,60 @@
1
+ import { Component } from '@angular/core';
2
+ import { TranslateService } from '@ngx-translate/core';
3
+ import * as rxjs from 'rxjs';
4
+ import { map, switchMap } from 'rxjs/operators';
5
+ import { ListWidget } from '../list-widget';
6
+ import { NodeSourcePipe } from '../node-source.pipe';
7
+ import { AccessibilityService } from '../../services/accessibility.service';
8
+ import { NetworkService, Node, RestConstants } from 'ngx-edu-sharing-api';
9
+ import { ListItem } from '../../types/list-item';
10
+
11
+ @Component({
12
+ selector: 'es-list-node-replication-source',
13
+ templateUrl: './list-node-replication-source.component.html',
14
+ providers: [NodeSourcePipe],
15
+ })
16
+ export class ListNodeReplicationSourceComponent extends ListWidget {
17
+ static supportedItems = [new ListItem('NODE', RestConstants.CCM_PROP_REPLICATIONSOURCE)];
18
+
19
+ readonly replicationSource$ = this.nodeSubject.pipe(
20
+ map((node) => (node as Node).properties['ccm:replicationsource']?.[0]),
21
+ );
22
+
23
+ readonly text$ = this.replicationSource$.pipe(
24
+ // Wait for repositories to be available to `restNetwork` since `nodeSource.transform` fails
25
+ // if it isn't.
26
+ switchMap((replicationSource) =>
27
+ this.networkService.getRepositories().pipe(map(() => replicationSource)),
28
+ ),
29
+ switchMap((replicationSource) =>
30
+ this.translate.get(
31
+ 'REPOSITORIES.' + this.nodeSource.transform(replicationSource, { mode: 'escaped' }),
32
+ { fallback: this.nodeSource.transform(replicationSource, { mode: 'text' }) },
33
+ ),
34
+ ),
35
+ );
36
+
37
+ readonly tooltip$ = rxjs.combineLatest([this.text$, this.provideLabelSubject]).pipe(
38
+ switchMap(([text, provideLabel]) => {
39
+ if (provideLabel) {
40
+ return this.translate
41
+ .get('NODE.ccm:replicationsource')
42
+ .pipe(map((replicationSource) => `${replicationSource}: ${text}`));
43
+ } else {
44
+ return rxjs.of(text);
45
+ }
46
+ }),
47
+ );
48
+
49
+ readonly indicatorIcons$ = this.accessibility.observe('indicatorIcons');
50
+
51
+ constructor(
52
+ private accessibility: AccessibilityService,
53
+
54
+ private nodeSource: NodeSourcePipe,
55
+ private translate: TranslateService,
56
+ private networkService: NetworkService,
57
+ ) {
58
+ super();
59
+ }
60
+ }
@@ -0,0 +1,3 @@
1
+ <div class="workflowStatus" [style.background-color]="getWorkflowStatus().color">
2
+ {{ 'WORKFLOW.' + getWorkflowStatus().id | translate }}
3
+ </div>
@@ -0,0 +1,21 @@
1
+ import { Component } from '@angular/core';
2
+ import { ListWidget } from '../list-widget';
3
+ import { ListItem } from '../../types/list-item';
4
+ import { NodeHelperService } from '../../services/node-helper.service';
5
+ import { Node, RestConstants } from 'ngx-edu-sharing-api';
6
+
7
+ @Component({
8
+ selector: 'es-list-node-workflow',
9
+ templateUrl: './list-node-workflow.component.html',
10
+ })
11
+ export class ListNodeWorkflowComponent extends ListWidget {
12
+ static supportedItems = [new ListItem('NODE', RestConstants.CCM_PROP_WF_STATUS)];
13
+
14
+ constructor(private nodeHelper: NodeHelperService) {
15
+ super();
16
+ }
17
+
18
+ getWorkflowStatus() {
19
+ return this.nodeHelper.getWorkflowStatus(this.node as Node).current;
20
+ }
21
+ }
@@ -0,0 +1,176 @@
1
+ <label *ngIf="item.config?.showLabel">
2
+ {{ getI18n(item) | translate }}
3
+ </label>
4
+ <ng-container *ngIf="item.type === 'ORG' || item.type === 'GROUP'">
5
+ <ng-container [ngSwitch]="item.name">
6
+ <span *ngSwitchCase="'displayName'" [class.type-danger]="isDangerousGroup()">
7
+ {{ $any(getNode()).profile.displayName }}
8
+ </span>
9
+ <span *ngSwitchCase="'groupType'">
10
+ {{ 'PERMISSIONS.GROUP_TYPE.' + $any(getNode()).profile.groupType | translate }}
11
+ </span>
12
+ <span *ngSwitchDefault>
13
+ {{ $any(getNode())[item.name] }}
14
+ </span>
15
+ </ng-container>
16
+ </ng-container>
17
+ <ng-container *ngIf="item.type === 'USER'">
18
+ <span *ngIf="isUserProfileAttribute(item.name)">
19
+ {{ $any(getNode()).profile[item.name] }}
20
+ </span>
21
+ <span *ngIf="item.name === 'status'">
22
+ {{ 'PERMISSIONS.USER_STATUS.' + $any(getNode()).status.status | translate }}
23
+ </span>
24
+ <span *ngIf="!isUserProfileAttribute(item.name) && item.name !== 'status'">
25
+ {{ $any(getNode())[item.name] }}
26
+ </span>
27
+ </ng-container>
28
+ <ng-container
29
+ [ngSwitch]="item.name"
30
+ *ngIf="item.type === 'NODE' || item.type === 'NODE_PROPOSAL' || item.type === 'COLLECTION'"
31
+ >
32
+ <span *ngSwitchCase="'name'">
33
+ {{ $any(getNode()).name }}
34
+ </span>
35
+ <span
36
+ *ngSwitchCase="'title'"
37
+ [matTooltip]="provideLabel ? $any(getNode()).title || $any(getNode()).name : null"
38
+ >
39
+ {{ $any(getNode()).title || $any(getNode()).name }}
40
+ </span>
41
+ <span
42
+ *ngSwitchCase="'cm:title'"
43
+ [matTooltip]="provideLabel ? $any(getNode()).title || $any(getNode()).name : null"
44
+ >
45
+ {{ $any(getNode()).title || $any(getNode()).name }}
46
+ </span>
47
+ <span
48
+ *ngSwitchCase="'cclom:title'"
49
+ [matTooltip]="provideLabel ? $any(getNode()).title || $any(getNode()).name : null"
50
+ >
51
+ {{ $any(getNode()).title || $any(getNode()).name }}
52
+ </span>
53
+ <span
54
+ *ngSwitchCase="'mediatype'"
55
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
56
+ >
57
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
58
+ {{ 'MEDIATYPE.' + $any(getNode()).mediatype | translate }}
59
+ </span>
60
+ <span *ngSwitchCase="'size'" [matTooltip]="provideLabel ? (getI18n(item) | translate) : null">
61
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
62
+ {{ $any(getNode()).size | formatSize }}
63
+ </span>
64
+ <span
65
+ *ngSwitchCase="'dimensions'"
66
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
67
+ >
68
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
69
+ {{ $any(getNode()) | NodeImageSize }}
70
+ </span>
71
+ <span
72
+ *ngSwitchCase="'ccm:wf_status'"
73
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
74
+ >
75
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
76
+ {{ 'WORKFLOW.' + getWorkflowStatus().id | translate }}
77
+ </span>
78
+ <span
79
+ *ngSwitchCase="'cm:creator'"
80
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
81
+ >
82
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
83
+ {{ $any(getNode()).createdBy | nodePersonName }}
84
+ </span>
85
+ <span
86
+ *ngSwitchCase="'cm:modifier'"
87
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
88
+ >
89
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
90
+ >{{ 'NODE.cm:modifier' | translate }}:</span
91
+ >
92
+ {{ $any(getNode()).modifiedBy | nodePersonName }}
93
+ </span>
94
+ <span
95
+ *ngSwitchCase="'ccm:replicationsource'"
96
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
97
+ >
98
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
99
+ >{{ 'NODE.ccm:replicationsource' | translate }}:</span
100
+ >
101
+ {{ $any(getNode()).properties['ccm:replicationsource'] | appNodeSource : { mode: 'text' } }}
102
+ </span>
103
+ <span
104
+ *ngSwitchCase="'ccm:commonlicense_key'"
105
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
106
+ >
107
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
108
+ >{{ 'NODE.ccm:commonlicense_key' | translate }}:</span
109
+ >
110
+ <ng-container *ngIf="$any(getNode()).properties['ccm:commonlicense_key'] as license">
111
+ {{
112
+ license[0] === 'CUSTOM'
113
+ ? $any(getNode()).properties['cclom:rights_description']
114
+ : ('LICENSE.NAMES.' + $any(getNode()).properties['ccm:commonlicense_key']
115
+ | translate : { fallback: $any(getNode()).properties['ccm:commonlicense_key'] })
116
+ }}
117
+ </ng-container>
118
+ <ng-container *ngIf="!$any(getNode()).properties['ccm:commonlicense_key']">
119
+ {{ 'LICENSE.NAMES.NONE' | translate }}
120
+ </ng-container>
121
+ </span>
122
+ <span
123
+ *ngSwitchCase="'ccm:educationaltypicalagerange'"
124
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
125
+ >
126
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
127
+ >{{ 'NODE.ccm:educationaltypicalagerange' | translate }}:</span
128
+ >
129
+ <ng-container
130
+ *ngIf="
131
+ $any(getNode()).properties['ccm:educationaltypicalagerange_from'] ||
132
+ $any(getNode()).properties['ccm:educationaltypicalagerange_to']
133
+ "
134
+ >
135
+ {{ $any(getNode()).properties['ccm:educationaltypicalagerange_from'] }} -
136
+ {{ $any(getNode()).properties['ccm:educationaltypicalagerange_to'] }}
137
+ </ng-container>
138
+ </span>
139
+ <span
140
+ *ngSwitchCase="'cclom:duration'"
141
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
142
+ >
143
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
144
+ >{{ 'NODE.cclom:duration' | translate }}:</span
145
+ >
146
+ {{ $any(getNode()).properties['cclom:duration']?.[0] | formatDuration }}
147
+ </span>
148
+ <span
149
+ *ngSwitchCase="'ccm:collection_proposal_status'"
150
+ [matTooltip]="provideLabel ? (getI18n(item) | translate) : null"
151
+ >
152
+ <span *ngIf="provideLabel" class="cdk-visually-hidden"
153
+ >{{ 'NODE.ccm:collection_proposal_status' | translate }}:</span
154
+ >
155
+ <!-- use node instead of getNode() to access the raw proposal data! -->
156
+ {{ ('PROPOSAL_STATUS.' + $any(node).properties['ccm:collection_proposal_status']?.[0]) | translate:{fallback: ''} }}
157
+ </span>
158
+ <span *ngSwitchDefault [matTooltip]="provideLabel ? (getI18n(item) | translate) : null">
159
+ <span *ngIf="provideLabel" class="cdk-visually-hidden">{{ getI18n(item) | translate }}:</span>
160
+ <ng-container
161
+ *ngIf="DATE_FIELDS.indexOf(item.name) === -1 && VCARD_FIELDS.indexOf(item.name) === -1"
162
+ >
163
+ {{ displayName$ | async }}
164
+ </ng-container>
165
+ <ng-container *ngIf="DATE_FIELDS.includes(item.name)">
166
+ {{ $any(getNode()).properties[item.name]?.[0] | formatDate: { async: true } | async }}
167
+ </ng-container>
168
+ <ng-container *ngIf="VCARD_FIELDS.includes(item.name)">
169
+ {{
170
+ $any(getNode()).properties[item.name]
171
+ ? ($any(getNode()).properties[item.name][0] | vcardName)
172
+ : ''
173
+ }}
174
+ </ng-container>
175
+ </span>
176
+ </ng-container>
@@ -0,0 +1,3 @@
1
+ .type-danger {
2
+ color: var(--warning);
3
+ }
@@ -0,0 +1,107 @@
1
+ import { ChangeDetectorRef, Component, OnInit, SimpleChanges } from '@angular/core';
2
+ import { MdsService, Node, Organization, RestConstants, ProposalNode } from 'ngx-edu-sharing-api';
3
+ import { BehaviorSubject, merge } from 'rxjs';
4
+ import { switchMap } from 'rxjs/operators';
5
+ import { ListItem } from '../../types/list-item';
6
+ import { ListWidget } from '../list-widget';
7
+ import { NodeHelperService } from '../../services/node-helper.service';
8
+ import * as Constants from 'ngx-edu-sharing-api';
9
+
10
+ @Component({
11
+ selector: 'es-list-text',
12
+ templateUrl: './list-text.component.html',
13
+ styleUrls: ['./list-text.component.scss'],
14
+ })
15
+ export class ListTextComponent extends ListWidget implements OnInit {
16
+ static supportedItems = [
17
+ new ListItem('NODE', '*'),
18
+ new ListItem('NODE_PROPOSAL', '*'),
19
+ new ListItem('COLLECTION', '*'),
20
+ new ListItem('ORG', '*'),
21
+ new ListItem('GROUP', '*'),
22
+ new ListItem('USER', '*'),
23
+ ];
24
+ readonly DATE_FIELDS = RestConstants.DATE_FIELDS;
25
+ readonly VCARD_FIELDS = RestConstants.getAllVCardFields();
26
+ displayName$ = new BehaviorSubject<string>(null);
27
+
28
+ constructor(
29
+ private nodeHelper: NodeHelperService,
30
+ private mds: MdsService,
31
+ private changeDetectorRef: ChangeDetectorRef,
32
+ ) {
33
+ super();
34
+ }
35
+
36
+ async ngOnChanges(changes: SimpleChanges) {}
37
+
38
+ async ngOnInit() {
39
+ merge([this.nodeSubject, this.itemSubject])
40
+ .pipe(switchMap(() => this.updateDisplayname()))
41
+ .subscribe((displayName) => {});
42
+ }
43
+ getNode() {
44
+ if (this.item.type === 'NODE_PROPOSAL') {
45
+ return (this.node as ProposalNode).proposal || this.node;
46
+ } else if ((this.node as Node).type === RestConstants.CCM_TYPE_COLLECTION_PROPOSAL) {
47
+ return (this.node as Node).relations?.Original ?? this.node;
48
+ }
49
+ return this.node;
50
+ }
51
+
52
+ isUserProfileAttribute(attribute: string) {
53
+ return (
54
+ [
55
+ RestConstants.AUTHORITY_FIRSTNAME,
56
+ RestConstants.AUTHORITY_LASTNAME,
57
+ RestConstants.AUTHORITY_EMAIL,
58
+ ].indexOf(attribute) !== -1
59
+ );
60
+ }
61
+ getWorkflowStatus() {
62
+ return this.nodeHelper.getWorkflowStatus(this.node as Node).current;
63
+ }
64
+
65
+ getI18n(item: ListItem) {
66
+ return (item.type === 'NODE_PROPOSAL' ? 'NODE_PROPOSAL' : 'NODE') + '.' + item.name;
67
+ }
68
+
69
+ isDangerousGroup() {
70
+ return (
71
+ (this.node as Organization).authorityName ===
72
+ RestConstants.GROUP_ALFRESCO_ADMINISTRATORS
73
+ );
74
+ }
75
+
76
+ private async updateDisplayname() {
77
+ const node = this.getNode() as Node;
78
+ if (!node.properties) {
79
+ this.displayName$.next('');
80
+ return;
81
+ }
82
+ this.displayName$.next(
83
+ node.properties[this.item.name + '_DISPLAYNAME']?.length > 0
84
+ ? node.properties[this.item.name + '_DISPLAYNAME'].join(', ')
85
+ : node.properties[this.item.name]?.join(', '),
86
+ );
87
+
88
+ const mds = await this.mds
89
+ .getMetadataSet({
90
+ repository: node.ref?.repo,
91
+ metadataSet: node.metadataset || Constants.DEFAULT,
92
+ })
93
+ .toPromise();
94
+ // @TODO
95
+ /*
96
+ const widget = MdsHelper.getWidget(this.item.name, null, mds.widgets);
97
+ if (widget?.values) {
98
+ const i18n = node.properties[this.item.name]
99
+ ?.map((prop) => widget.values.filter((v) => v.id === prop)?.[0]?.caption)
100
+ .filter((cap) => !!cap);
101
+ if (i18n) {
102
+ this.displayName$.next(i18n.join(', '));
103
+ }
104
+ }
105
+ */
106
+ }
107
+ }
@@ -0,0 +1,52 @@
1
+ import { Input, Type, Directive } from '@angular/core';
2
+ import { Person } from 'ngx-edu-sharing-api';
3
+ import { BehaviorSubject } from 'rxjs';
4
+ import { Node, ProposalNode, Group, Statistics } from 'ngx-edu-sharing-api';
5
+ import { ListItem } from '../types/list-item';
6
+
7
+ @Directive()
8
+ export class ListWidget {
9
+ @Input()
10
+ get node(): Node | ProposalNode | Group | Person | Statistics {
11
+ return this.nodeSubject.value;
12
+ }
13
+ set node(value: Node | ProposalNode | Group | Person | Statistics) {
14
+ this.nodeSubject.next(value);
15
+ }
16
+ protected readonly nodeSubject = new BehaviorSubject<
17
+ Node | ProposalNode | Group | Person | Statistics
18
+ >(null); // node (or group/user)
19
+
20
+ @Input()
21
+ get item(): ListItem {
22
+ return this.itemSubject.value;
23
+ }
24
+ set item(value: ListItem) {
25
+ this.itemSubject.next(value);
26
+ }
27
+ protected readonly itemSubject = new BehaviorSubject<ListItem>(null);
28
+
29
+ /**
30
+ * Provide a label for non-obvious fields that describes the field the given value belongs to.
31
+ *
32
+ * The label is included in a tooltip and made available for a11y technologies.
33
+ *
34
+ * Useful when the value is displayed without context.
35
+ *
36
+ * Other tooltips might be added even with this input set to `false`.
37
+ */
38
+ @Input()
39
+ get provideLabel() {
40
+ return this.provideLabelSubject.value;
41
+ }
42
+ set provideLabel(value) {
43
+ this.provideLabelSubject.next(value);
44
+ }
45
+ protected readonly provideLabelSubject = new BehaviorSubject(false);
46
+
47
+ constructor() {}
48
+ }
49
+
50
+ export type ListWidgetClass = {
51
+ supportedItems: ListItem[];
52
+ } & Type<ListWidget>;
@@ -0,0 +1,31 @@
1
+ <div class="node-row" *ngIf="node">
2
+ <div class="icon-bg">
3
+ <img
4
+ [src]="node | esNodeIcon | async"
5
+ [alt]="
6
+ node.mediatype
7
+ ? ('NODE.mediatype' | translate) + ': ' + ('MEDIATYPE.' + node.mediatype | translate)
8
+ : ''
9
+ "
10
+ [matTooltip]="
11
+ node.mediatype
12
+ ? ('NODE.mediatype' | translate) + ': ' + ('MEDIATYPE.' + node.mediatype | translate)
13
+ : ''
14
+ "
15
+ />
16
+ </div>
17
+ <div class="node-data">
18
+ <div class="node-data-primary">
19
+ <es-list-text [node]="node" [item]="columns[0]"> </es-list-text>
20
+ </div>
21
+ <div class="node-data-secondary">
22
+ <es-list-text
23
+ *ngFor="let column of columns.slice(1)"
24
+ [node]="node"
25
+ [item]="column"
26
+ [provideLabel]="true"
27
+ ></es-list-text>
28
+ <ng-container *ngTemplateOutlet="customMetadataRef; context: { node: node }"></ng-container>
29
+ </div>
30
+ </div>
31
+ </div>
@@ -0,0 +1,50 @@
1
+ @import '../../../../assets/scss/mixins';
2
+
3
+ .node-row {
4
+ width: 100%;
5
+ display: grid;
6
+ grid-template-columns: var(--tableIconSize) auto;
7
+ grid-gap: 10px;
8
+ align-items: center;
9
+ padding: 10px;
10
+ white-space: normal;
11
+ .node-data {
12
+ flex-grow: 1;
13
+ display: flex;
14
+ flex-direction: column;
15
+
16
+ .node-data-primary {
17
+ font-weight: bold;
18
+ padding-bottom: 5px;
19
+ @include limitLineCount(1, 1.25);
20
+ }
21
+
22
+ .node-data-secondary {
23
+ color: var(--textLight);
24
+ display: flex;
25
+ justify-content: space-between;
26
+ }
27
+ }
28
+
29
+ .icon-bg {
30
+ background-color: rgb(var(--palette-foreground-text-dark));
31
+ border-radius: 50%;
32
+ width: var(--tableIconSize);
33
+ height: var(--tableIconSize) !important;
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ @include materialShadowBottom();
38
+ }
39
+
40
+ .icon-bg img,
41
+ .icon-bg i {
42
+ width: 50%;
43
+ z-index: 0;
44
+ }
45
+
46
+ .icon-bg i {
47
+ color: rgb(var(--palette-foreground-text));
48
+ font-size: 20px;
49
+ }
50
+ }