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,155 @@
1
+ import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
2
+ import {
3
+ DragDataLegacy,
4
+ dragNodesTransferType,
5
+ DropActionLegacy,
6
+ readDraggedNodes,
7
+ } from './drag-nodes/drag-nodes';
8
+
9
+ /**
10
+ * Handle dragging and dropping of nodes onto an element.
11
+ *
12
+ * Use in combination with `NodesDragSourceDirective`.
13
+ *
14
+ * When an element that sets `NodesDragSourceDirective` is dragged onto an
15
+ * element that sets this directive, drag events are passed as output events.
16
+ * Some of these output events provide data about the dragged nodes. Passed
17
+ * events are filtered to not trigger unnecessarily.
18
+ */
19
+ @Directive({
20
+ selector: '[esLegacyNodesDropTarget]',
21
+ })
22
+ export class NodesDropTargetLegacyDirective {
23
+ /**
24
+ * The last node that some (other) node(s) were dragged over.
25
+ *
26
+ * One of two mechanisms to determine if we entered the element, i.e., if
27
+ * the cursor -- while dragging node(s) -- is moved from outside onto this
28
+ * node.
29
+ *
30
+ * If the cursor was over another node before, we know it just entered this
31
+ * one.
32
+ *
33
+ * This fails when the cursor is moved from this node to a place other than
34
+ * a node and back again.
35
+ */
36
+ private static last: NodesDropTargetLegacyDirective;
37
+
38
+ /**
39
+ * A function to determine whether this element is a valid drop target for
40
+ * the dragged nodes.
41
+ *
42
+ * Output events are *only* fired, when this function evaluates to `true`.
43
+ */
44
+ @Input('esLegacyNodesDropTarget') canDrop: boolean | ((dragData: DragDataLegacy) => boolean);
45
+ @Input() nodesDragAllowedActions: DropActionLegacy[] = ['move', 'copy'];
46
+
47
+ @Output() nodesDragEnter = new EventEmitter<DragEvent>();
48
+ @Output() nodesDragLeave = new EventEmitter<DragEvent>();
49
+ /**
50
+ * Indicates whether the cursor is hovering over the element, holding one or more nodes.
51
+ */
52
+ @Output() nodesHoveringChange = new EventEmitter<boolean>();
53
+ /**
54
+ * Triggered when one or more nodes are dropped onto the element.
55
+ */
56
+ @Output() nodesDrop = new EventEmitter<DragDataLegacy>();
57
+
58
+ /**
59
+ * Difference of enter- and leave events.
60
+ *
61
+ * One of two mechanisms to determine if we entered the element, i.e., if
62
+ * the cursor -- while dragging node(s) -- is moved from outside onto this
63
+ * node.
64
+ *
65
+ * If we enter this node a second time without having left it once, we
66
+ * probably just stayed on it.
67
+ *
68
+ * This fails when a leave event fails to fire, e.g., when the DOM element
69
+ * was repositioned while the curser was dragged over it.
70
+ */
71
+ private enterCount = 0;
72
+ private canDropCurrent: boolean;
73
+ private dropAction: DropActionLegacy;
74
+
75
+ constructor() {}
76
+
77
+ @HostListener('dragenter', ['$event']) onDragEnter(event: DragEvent) {
78
+ if (!event.dataTransfer.types.includes(dragNodesTransferType)) {
79
+ return;
80
+ }
81
+ if (this.enterCount === 0 || NodesDropTargetLegacyDirective.last !== this) {
82
+ NodesDropTargetLegacyDirective.last = this;
83
+ this.enterCount = 0;
84
+ this.canDropCurrent = this.getCanDrop(event);
85
+ if (this.canDropCurrent) {
86
+ this.nodesHoveringChange.emit(true);
87
+ this.nodesDragEnter.emit(event);
88
+ }
89
+ }
90
+ if (this.canDropCurrent) {
91
+ event.preventDefault();
92
+ }
93
+ this.enterCount++;
94
+ }
95
+
96
+ @HostListener('dragleave', ['$event']) onDragLeave(event: DragEvent) {
97
+ if (!event.dataTransfer.types.includes(dragNodesTransferType)) {
98
+ return;
99
+ }
100
+ this.enterCount--;
101
+ if (this.canDropCurrent) {
102
+ if (this.enterCount === 0) {
103
+ this.nodesDragLeave.emit(event);
104
+ this.nodesHoveringChange.emit(false);
105
+ }
106
+ }
107
+ }
108
+
109
+ @HostListener('dragover', ['$event']) onDragOver(event: DragEvent) {
110
+ if (!event.dataTransfer.types.includes(dragNodesTransferType)) {
111
+ return;
112
+ }
113
+ if (this.canDropCurrent) {
114
+ event.preventDefault();
115
+ this.dropAction = this.getDropAction(event);
116
+ event.dataTransfer.dropEffect = this.dropAction;
117
+ }
118
+ }
119
+
120
+ @HostListener('drop', ['$event']) onDrop(event: DragEvent) {
121
+ if (!event.dataTransfer.types.includes(dragNodesTransferType)) {
122
+ return;
123
+ }
124
+ if (this.canDropCurrent) {
125
+ event.preventDefault();
126
+ const nodes = readDraggedNodes();
127
+ this.nodesDrop.emit({
128
+ event,
129
+ nodes,
130
+ dropAction: this.getDropAction(event),
131
+ });
132
+ this.nodesHoveringChange.emit(false);
133
+ }
134
+ this.enterCount = 0;
135
+ NodesDropTargetLegacyDirective.last = null;
136
+ }
137
+
138
+ private getCanDrop(event: DragEvent): boolean {
139
+ if (typeof this.canDrop === 'function') {
140
+ const nodes = readDraggedNodes();
141
+ return this.canDrop({ event, nodes, dropAction: this.getDropAction(event) });
142
+ }
143
+ return this.canDrop;
144
+ }
145
+
146
+ private getDropAction(event: DragEvent): DropActionLegacy {
147
+ if (this.nodesDragAllowedActions.includes('copy') && event.ctrlKey) {
148
+ return 'copy';
149
+ } else if (this.nodesDragAllowedActions.includes('link') && event.altKey) {
150
+ return 'link';
151
+ } else {
152
+ return 'move';
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,32 @@
1
+ <mat-menu
2
+ #dropdown="matMenu"
3
+ class="mat-dropdown-menu"
4
+ [class]="menuClass"
5
+ backdropClass="mat-dropdown-menu"
6
+ [xPosition]="position === 'right' ? 'after' : 'before'"
7
+ >
8
+ <!-- MatMenu has role="menu", so the only meaningful role of descendants is "menuitem" -->
9
+ <ul role="none">
10
+ <ng-container *ngFor="let option of _options; let i = index">
11
+ <li *ngIf="option.isEnabled || showDisabled" role="none">
12
+ <button
13
+ mat-menu-item
14
+ class="mat-menu-item collection-item-{{
15
+ option.name | replaceChars : { search: '.', replace: '-' }
16
+ }}"
17
+ matTooltip="{{ option | optionTooltip }}"
18
+ matTooltipPosition="right"
19
+ matTooltipTouchGestures="off"
20
+ [class.mat-menu-item-separate]="option.isSeparate || isNewGroup(i)"
21
+ [class.mat-menu-item-selected]="option.isSelected"
22
+ [disabled]="!option.isEnabled"
23
+ (click)="click(option)"
24
+ attr.data-test="menu-item-{{ option.name }}"
25
+ >
26
+ <i [esIcon]="option.icon"></i> {{ option.name | translate }}
27
+ </button>
28
+ </li>
29
+ </ng-container>
30
+ </ul>
31
+ </mat-menu>
32
+ <div #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu" class="display-none"></div>
@@ -0,0 +1,67 @@
1
+ @import '../../../assets/scss/mixins';
2
+
3
+ // We cannot use `:host` here, since the `mat-menu` elements are rearranged
4
+ // outside of the original container. Hence we rely on the `.mat-dropdown-menu`
5
+ // selector to not bleed into other components.
6
+ ::ng-deep .mat-dropdown-menu {
7
+ .mat-mdc-menu-content {
8
+ min-width: 200px;
9
+ > ul > li > button {
10
+ &:not(:disabled) > span {
11
+ color: var(--primary);
12
+ }
13
+ display: flex;
14
+ align-items: center;
15
+ > i {
16
+ width: 35px;
17
+ }
18
+ }
19
+ &:not(:empty) {
20
+ padding: 0 !important;
21
+ }
22
+ }
23
+
24
+ ul {
25
+ margin: 0;
26
+ list-style: none;
27
+ padding-left: 0;
28
+ li {
29
+ .mat-mdc-menu-item {
30
+ .mat-mdc-menu-item-text {
31
+ display: flex;
32
+ align-items: center;
33
+ i {
34
+ margin-right: 10px;
35
+ }
36
+ }
37
+ &:hover:not([disabled]),
38
+ &.cdk-focused {
39
+ color: var(--itemSelectedTextColor);
40
+ background-color: var(--listItemSelectedBackground);
41
+ }
42
+ &.cdk-keyboard-focused {
43
+ @include setGlobalKeyboardFocus('border');
44
+ }
45
+ }
46
+
47
+ .mat-menu-item-separate {
48
+ border-top: 1px solid #ccc;
49
+ }
50
+
51
+ // Mobile menus at bottom
52
+ @media screen and (max-width: (calc(var(--mobileWidth) - var(--mobileStage) * 1))) {
53
+ &.cdk-overlay-backdrop {
54
+ background: rgba(0, 0, 0, 0.8);
55
+ }
56
+ &.mat-menu-panel {
57
+ position: fixed;
58
+ bottom: 0;
59
+ left: 0;
60
+ max-width: 100%;
61
+ width: 100%;
62
+ border-radius: 0;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,71 @@
1
+ import { Component, Input, ViewChild } from '@angular/core';
2
+ import { MatMenu, MatMenuContent, MatMenuTrigger } from '@angular/material/menu';
3
+ import { OptionItem } from '../types/option-item';
4
+ import { Helper } from '../util/helper';
5
+ import { UIService } from '../services/ui.service';
6
+
7
+ /**
8
+ * The dropdown is one base component of the action bar (showing more actions),
9
+ * but can also be used standalone.
10
+ */
11
+ @Component({
12
+ selector: 'es-dropdown',
13
+ templateUrl: 'dropdown.component.html',
14
+ styleUrls: ['dropdown.component.scss'],
15
+ })
16
+ export class DropdownComponent {
17
+ @ViewChild('dropdown', { static: true }) menu: MatMenu;
18
+ @ViewChild('menuTrigger') menuTrigger: MatMenuTrigger;
19
+
20
+ @Input() position: 'left' | 'right' = 'left';
21
+
22
+ @Input() set options(options: OptionItem[]) {
23
+ this._options = this.ui.filterValidOptions(Helper.deepCopyArray(options));
24
+ }
25
+
26
+ /**
27
+ * The object that should be returned via the option's callback.
28
+ *
29
+ * Can be null
30
+ */
31
+ @Input() callbackObject: any;
32
+
33
+ /**
34
+ * Should disabled ("greyed out") options be shown or hidden?
35
+ */
36
+ @Input() showDisabled = true;
37
+
38
+ /**
39
+ * An additional class to add to the `mat-menu` instance.
40
+ *
41
+ * This is needed to customize the menu styling since the menu contents are
42
+ * taken out of the host container by angular.
43
+ */
44
+ @Input() menuClass: string;
45
+
46
+ _options: OptionItem[];
47
+
48
+ constructor(private ui: UIService) {}
49
+
50
+ click(option: OptionItem) {
51
+ if (!option.isEnabled) {
52
+ return;
53
+ }
54
+ setTimeout(() => option.callback(this.callbackObject));
55
+ }
56
+
57
+ isNewGroup(i: number) {
58
+ if (i > 0) {
59
+ return this._options[i].group !== this._options[i - 1].group;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ /** Whether there are any enabled options so we can open the menu. */
65
+ canShowDropdown(): boolean {
66
+ // We can only open the dropdown menu, when there is at least one enabled option. Even when
67
+ // there are options with `showDisabled: true`, showing a menu with no selectable option
68
+ // causes a11y issues.
69
+ return this._options?.some((o) => o.isEnabled);
70
+ }
71
+ }
@@ -0,0 +1,47 @@
1
+ import { Injectable, InjectionToken, Provider } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
+ import { AccessibilitySettings } from './types/accessibillity';
4
+ import { KeyboardShortcut } from './types/keyboard-shortcuts';
5
+
6
+ export const EDU_SHARING_UI_CONFIG = new InjectionToken<EduSharingUiConfigurationParams>(
7
+ 'EDU_SHARING_API_CONFIG',
8
+ );
9
+
10
+ @Injectable({
11
+ providedIn: 'root',
12
+ })
13
+ export class EduSharingUiConfiguration {
14
+ production: boolean;
15
+ static create(params: EduSharingUiConfigurationParams = {}): EduSharingUiConfiguration {
16
+ return { ...new EduSharingUiConfiguration(), ...params };
17
+ }
18
+ }
19
+
20
+ export type EduSharingUiConfigurationParams = Partial<EduSharingUiConfiguration>;
21
+
22
+ export function getConfigProvider(params?: EduSharingUiConfigurationParams): Provider[] {
23
+ return [
24
+ // Provide the params given to `forRoot()`. These can be overridden by the application by
25
+ // providing `EDU_SHARING_API_CONFIG` itself.
26
+ {
27
+ provide: EDU_SHARING_UI_CONFIG,
28
+ useValue: params,
29
+ },
30
+ // Inject `configuration` as both, `ApiConfiguration` and `EduSharingApiConfiguration`, to pass
31
+ // `rootUrl` on to `ApiModule` while also adding our custom configuration.
32
+ {
33
+ provide: EduSharingUiConfiguration,
34
+ deps: [EDU_SHARING_UI_CONFIG],
35
+ // deps: [[new Optional(), EDU_SHARING_API_CONFIG]],
36
+ useFactory: (configParams: EduSharingUiConfigurationParams) =>
37
+ EduSharingUiConfiguration.create(configParams),
38
+ },
39
+ {
40
+ provide: EduSharingUiConfiguration,
41
+ deps: [EDU_SHARING_UI_CONFIG],
42
+ // deps: [[new Optional(), EDU_SHARING_API_CONFIG]],
43
+ useFactory: (configParams: EduSharingUiConfigurationParams) =>
44
+ EduSharingUiConfiguration.create(configParams),
45
+ },
46
+ ];
47
+ }
@@ -0,0 +1,49 @@
1
+ import { ModuleWithProviders, NgModule } from '@angular/core';
2
+ import { MatTooltipModule } from '@angular/material/tooltip';
3
+ import { TranslateModule } from '@ngx-translate/core';
4
+ import { CommonModule } from '@angular/common';
5
+ import { EduSharingUiConfigurationParams, getConfigProvider } from './edu-sharing-ui-configuration';
6
+ import { ListItemsModule } from './list-items/list-items.module';
7
+ import { NodeEntriesModule } from './node-entries/node-entries.module';
8
+ import { EduSharingUiCommonModule } from './common/edu-sharing-ui-common.module';
9
+ import { NodeImageSizePipe } from './pipes/node-image-size.pipe';
10
+ import { FormatDatePipe } from './pipes/format-date.pipe';
11
+ import { ListItemLabelPipe } from './node-entries/list-item-label.pipe';
12
+ import { SortDropdownComponent } from './sort-dropdown/sort-dropdown.component';
13
+
14
+ @NgModule({
15
+ declarations: [],
16
+ imports: [
17
+ CommonModule,
18
+ TranslateModule,
19
+ EduSharingUiCommonModule,
20
+ ListItemsModule,
21
+ NodeEntriesModule,
22
+ // Loading the TranslationsModule here causes errors for lazy-loaded pages like the search
23
+ // page. For usage outside the context of edu-sharing, we probably need to import the
24
+ // TranslationsModule at the main module that packages or uses this library.
25
+ ],
26
+ exports: [
27
+ CommonModule,
28
+ MatTooltipModule,
29
+ TranslateModule,
30
+ EduSharingUiCommonModule,
31
+ ListItemsModule,
32
+ NodeEntriesModule,
33
+ NodeImageSizePipe,
34
+ FormatDatePipe,
35
+ ListItemsModule,
36
+ ListItemLabelPipe,
37
+ SortDropdownComponent,
38
+ ],
39
+ })
40
+ export class EduSharingUiModule {
41
+ public static forRoot(
42
+ config: EduSharingUiConfigurationParams,
43
+ ): ModuleWithProviders<EduSharingUiModule> {
44
+ return {
45
+ ngModule: EduSharingUiModule,
46
+ providers: [getConfigProvider(config)],
47
+ };
48
+ }
49
+ }
@@ -0,0 +1,30 @@
1
+ import { ListCollectionInfoComponent } from './list-collection-info/list-collection-info.component';
2
+ import { ListCountsComponent } from './list-counts/list-counts.component';
3
+ import { ListNodeLicenseComponent } from './list-node-license/list-node-license.component';
4
+ import { ListNodeReplicationSourceComponent } from './list-node-replication-source/list-node-replication-source.component';
5
+ import { ListNodeWorkflowComponent } from './list-node-workflow/list-node-workflow.component';
6
+ import { ListTextComponent } from './list-text/list-text.component';
7
+ import { ListWidgetClass } from './list-widget';
8
+
9
+ export enum ListWidgetType {
10
+ CollectionInfo = 'CollectionInfo',
11
+ Text = 'Text',
12
+ Custom = 'Custom',
13
+ NodeLicense = 'NodeLicense',
14
+ NodeReplicationSource = 'NodeReplicationSource',
15
+ NodeWorkflow = 'NodeWorkflow',
16
+ NodeCounts = 'NodeCounts',
17
+ }
18
+ export const AVAILABLE_LIST_WIDGETS: {
19
+ [widgetType in ListWidgetType]: ListWidgetClass;
20
+ } = {
21
+ CollectionInfo: ListCollectionInfoComponent,
22
+ NodeLicense: ListNodeLicenseComponent,
23
+ NodeReplicationSource: ListNodeReplicationSourceComponent,
24
+ NodeWorkflow: ListNodeWorkflowComponent,
25
+ NodeCounts: ListCountsComponent,
26
+ Custom: null,
27
+
28
+ // use the widgets with wildcards as last ones
29
+ Text: ListTextComponent,
30
+ };
@@ -0,0 +1,17 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+ import { DurationFormat } from '../util/duration-helper';
3
+ import { DurationHelper } from '../util/duration-helper';
4
+
5
+ @Pipe({
6
+ name: 'formatDuration',
7
+ })
8
+ export class FormatDurationPipe implements PipeTransform {
9
+ transform(
10
+ value: string,
11
+ args = {
12
+ format: DurationFormat.Hms,
13
+ },
14
+ ): string {
15
+ return DurationHelper.getDurationFormatted(value, args.format);
16
+ }
17
+ }
@@ -0,0 +1,52 @@
1
+ <ng-container [ngSwitch]="widgetType">
2
+ <ng-container *ngSwitchCase="'Custom'">
3
+ <ng-container
4
+ *ngTemplateOutlet="
5
+ customTemplate;
6
+ context: {
7
+ item: this.item,
8
+ node: this.node,
9
+ provideLabel: this.provideLabel,
10
+ forceText: this.forceText
11
+ }
12
+ "
13
+ >
14
+ </ng-container>
15
+ </ng-container>
16
+ <es-list-collection-info
17
+ *ngSwitchCase="'CollectionInfo'"
18
+ [item]="item"
19
+ [node]="node"
20
+ [provideLabel]="provideLabel"
21
+ ></es-list-collection-info>
22
+ <es-list-text
23
+ *ngSwitchCase="'Text'"
24
+ [item]="item"
25
+ [node]="node"
26
+ [provideLabel]="provideLabel"
27
+ ></es-list-text>
28
+ <es-list-counts
29
+ *ngSwitchCase="'NodeCounts'"
30
+ [item]="item"
31
+ [node]="node"
32
+ [provideLabel]="provideLabel"
33
+ ></es-list-counts>
34
+ <es-list-node-license
35
+ *ngSwitchCase="'NodeLicense'"
36
+ [item]="item"
37
+ [node]="node"
38
+ [provideLabel]="provideLabel"
39
+ ></es-list-node-license>
40
+ <es-list-node-replication-source
41
+ *ngSwitchCase="'NodeReplicationSource'"
42
+ [item]="item"
43
+ [node]="node"
44
+ [provideLabel]="provideLabel"
45
+ ></es-list-node-replication-source>
46
+ <es-list-node-workflow
47
+ *ngSwitchCase="'NodeWorkflow'"
48
+ [item]="item"
49
+ [node]="node"
50
+ [provideLabel]="provideLabel"
51
+ ></es-list-node-workflow>
52
+ </ng-container>
@@ -0,0 +1,44 @@
1
+ import { Component, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core';
2
+ import { AVAILABLE_LIST_WIDGETS, ListWidgetType } from '../available-widgets';
3
+ import { ListWidget } from '../list-widget';
4
+ import { NodeEntriesGlobalService } from '../../node-entries/node-entries-global.service';
5
+ import { Node } from 'ngx-edu-sharing-api';
6
+
7
+ @Component({
8
+ selector: 'es-list-base',
9
+ templateUrl: './list-base.component.html',
10
+ })
11
+ export class ListBaseComponent extends ListWidget implements OnChanges {
12
+ /**
13
+ * use text only widgets (for table)
14
+ */
15
+ @Input() forceText = false;
16
+ widgetType: ListWidgetType;
17
+ customTemplate: TemplateRef<unknown>;
18
+ constructor(private nodeEntriesGlobalService: NodeEntriesGlobalService) {
19
+ super();
20
+ }
21
+
22
+ ngOnChanges(changes: SimpleChanges): void {
23
+ if (this.node && this.item) {
24
+ this.customTemplate = this.nodeEntriesGlobalService.getCustomFieldTemplate(
25
+ this.item,
26
+ this.node as Node,
27
+ );
28
+ if (this.customTemplate) {
29
+ this.widgetType = ListWidgetType.Custom;
30
+ } else if (this.forceText) {
31
+ this.widgetType = ListWidgetType.Text;
32
+ } else {
33
+ this.widgetType = Object.entries(AVAILABLE_LIST_WIDGETS).find(
34
+ ([id, w]) =>
35
+ w?.supportedItems?.filter(
36
+ (i) =>
37
+ i.type === this.item.type &&
38
+ (i.name === this.item.name || i.name === '*'),
39
+ ).length > 0,
40
+ )?.[0] as ListWidgetType;
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,48 @@
1
+ <ng-container [ngSwitch]="item.name">
2
+ <ng-container *ngSwitchCase="'info'">
3
+ <ng-container *ngIf="indicatorIcons$ | async; else textRepresentation">
4
+ <i
5
+ esIcon="layers"
6
+ [altText]="'COLLECTION.INFO_REFERENCES_MULTI' | translate"
7
+ [matTooltip]="'COLLECTION.INFO_REFERENCES_MULTI' | translate"
8
+ ></i>
9
+ {{ $any(node).collection?.childCollectionsCount }}
10
+ <i
11
+ esIcon="insert_drive_file"
12
+ [altText]="'collections_content' | translate"
13
+ [matTooltip]="'collections_content' | translate"
14
+ ></i>
15
+ {{ $any(node).collection?.childReferencesCount }}
16
+ </ng-container>
17
+ <ng-template #textRepresentation>
18
+ <div class="rows">
19
+ <p>
20
+ {{ 'COLLECTION.INFO_REFERENCES_MULTI' | translate }}:
21
+ {{ $any(node).collection?.childCollectionsCount }}
22
+ </p>
23
+ <p>
24
+ {{ 'collections_content' | translate }}:
25
+ {{ $any(node).collection?.childReferencesCount }}
26
+ </p>
27
+ </div>
28
+ </ng-template>
29
+ </ng-container>
30
+ <ng-container *ngSwitchCase="'scope'">
31
+ <i
32
+ *ngIf="indicatorIcons$ | async; else textRepresentation"
33
+ class="collectionScope"
34
+ [esIcon]="nodeHelper.getCollectionScopeInfo($any(node)).icon"
35
+ [matTooltip]="
36
+ 'COLLECTION.SCOPE.' + nodeHelper.getCollectionScopeInfo($any(node)).scopeName | translate
37
+ "
38
+ [altText]="
39
+ 'COLLECTION.SCOPE.' + nodeHelper.getCollectionScopeInfo($any(node)).scopeName | translate
40
+ "
41
+ ></i>
42
+ <ng-template #textRepresentation>
43
+ {{
44
+ 'COLLECTION.SCOPE.' + nodeHelper.getCollectionScopeInfo($any(node)).scopeName | translate
45
+ }}
46
+ </ng-template>
47
+ </ng-container>
48
+ </ng-container>
@@ -0,0 +1,8 @@
1
+ .rows {
2
+ display: flex;
3
+ flex-direction: column;
4
+ text-align: start;
5
+ p {
6
+ margin: 0;
7
+ }
8
+ }
@@ -0,0 +1,24 @@
1
+ import { Component } from '@angular/core';
2
+ import { ListItem } from '../../types/list-item';
3
+ import { ListWidget } from '../list-widget';
4
+ import { NodeHelperService } from '../../services/node-helper.service';
5
+ import { AccessibilityService } from '../../services/accessibility.service';
6
+
7
+ @Component({
8
+ selector: 'es-list-collection-info',
9
+ templateUrl: './list-collection-info.component.html',
10
+ styleUrls: ['./list-collection-info.component.scss'],
11
+ })
12
+ export class ListCollectionInfoComponent extends ListWidget {
13
+ static supportedItems = [
14
+ new ListItem('COLLECTION', 'info'),
15
+ new ListItem('COLLECTION', 'scope'),
16
+ ];
17
+
18
+ readonly indicatorIcons$;
19
+
20
+ constructor(private accessibility: AccessibilityService, public nodeHelper: NodeHelperService) {
21
+ super();
22
+ this.indicatorIcons$ = this.accessibility.observe('indicatorIcons');
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ {{ getCount() }}
@@ -0,0 +1,3 @@
1
+ :host {
2
+ white-space: pre-line;
3
+ }