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.
- package/.browserslistrc +16 -0
- package/.eslintrc.json +44 -0
- package/README.md +40 -0
- package/assets/scss/mixins.scss +95 -0
- package/assets/scss/variables.scss +33 -0
- package/karma.conf.js +42 -0
- package/ng-package.json +10 -0
- package/package.json +19 -0
- package/src/lib/actionbar/actionbar.component.html +59 -0
- package/src/lib/actionbar/actionbar.component.scss +123 -0
- package/src/lib/actionbar/actionbar.component.ts +174 -0
- package/src/lib/common/edu-sharing-ui-common.module.ts +80 -0
- package/src/lib/directives/border-box-observer.directive.ts +75 -0
- package/src/lib/directives/check-text-overflow.directive.ts +61 -0
- package/src/lib/directives/drag-nodes/drag-nodes.ts +32 -0
- package/src/lib/directives/drag-nodes/nodes-drag-source.directive.ts +79 -0
- package/src/lib/directives/drag-nodes/nodes-drag.directive.ts +43 -0
- package/src/lib/directives/drag-nodes/nodes-drop-target.directive.ts +116 -0
- package/src/lib/directives/focus-state.directive.ts +34 -0
- package/src/lib/directives/icon.directive.ts +142 -0
- package/src/lib/directives/nodes-drop-target-legacy.directive.ts +155 -0
- package/src/lib/dropdown/dropdown.component.html +32 -0
- package/src/lib/dropdown/dropdown.component.scss +67 -0
- package/src/lib/dropdown/dropdown.component.ts +71 -0
- package/src/lib/edu-sharing-ui-configuration.ts +47 -0
- package/src/lib/edu-sharing-ui.module.ts +49 -0
- package/src/lib/list-items/available-widgets.ts +30 -0
- package/src/lib/list-items/format-duration.pipe.ts +17 -0
- package/src/lib/list-items/list-base/list-base.component.html +52 -0
- package/src/lib/list-items/list-base/list-base.component.ts +44 -0
- package/src/lib/list-items/list-collection-info/list-collection-info.component.html +48 -0
- package/src/lib/list-items/list-collection-info/list-collection-info.component.scss +8 -0
- package/src/lib/list-items/list-collection-info/list-collection-info.component.ts +24 -0
- package/src/lib/list-items/list-counts/list-counts.component.html +1 -0
- package/src/lib/list-items/list-counts/list-counts.component.scss +3 -0
- package/src/lib/list-items/list-counts/list-counts.component.ts +59 -0
- package/src/lib/list-items/list-items.module.ts +33 -0
- package/src/lib/list-items/list-node-license/list-node-license.component.html +8 -0
- package/src/lib/list-items/list-node-license/list-node-license.component.ts +47 -0
- package/src/lib/list-items/list-node-replication-source/list-node-replication-source.component.html +11 -0
- package/src/lib/list-items/list-node-replication-source/list-node-replication-source.component.ts +60 -0
- package/src/lib/list-items/list-node-workflow/list-node-workflow.component.html +3 -0
- package/src/lib/list-items/list-node-workflow/list-node-workflow.component.ts +21 -0
- package/src/lib/list-items/list-text/list-text.component.html +176 -0
- package/src/lib/list-items/list-text/list-text.component.scss +3 -0
- package/src/lib/list-items/list-text/list-text.component.ts +107 -0
- package/src/lib/list-items/list-widget.ts +52 -0
- package/src/lib/list-items/node-row/node-row.component.html +31 -0
- package/src/lib/list-items/node-row/node-row.component.scss +50 -0
- package/src/lib/list-items/node-row/node-row.component.ts +16 -0
- package/src/lib/list-items/node-source.pipe.ts +48 -0
- package/src/lib/node-entries/combined-data-source.ts +51 -0
- package/src/lib/node-entries/custom-templates-data-source.ts +6 -0
- package/src/lib/node-entries/drag-preview/drag-preview.component.html +6 -0
- package/src/lib/node-entries/drag-preview/drag-preview.component.scss +35 -0
- package/src/lib/node-entries/drag-preview/drag-preview.component.ts +15 -0
- package/src/lib/node-entries/entries-model.ts +120 -0
- package/src/lib/node-entries/items-cap.ts +54 -0
- package/src/lib/node-entries/list-item-label.pipe.ts +28 -0
- package/src/lib/node-entries/mixins.scss +23 -0
- package/src/lib/node-entries/node-cache.spec.ts +199 -0
- package/src/lib/node-entries/node-cache.ts +81 -0
- package/src/lib/node-entries/node-data-source-remote.ts +33 -0
- package/src/lib/node-entries/node-data-source.ts +148 -0
- package/src/lib/node-entries/node-entries-card/node-entries-card.component.html +167 -0
- package/src/lib/node-entries/node-entries-card/node-entries-card.component.scss +28 -0
- package/src/lib/node-entries/node-entries-card/node-entries-card.component.ts +132 -0
- package/src/lib/node-entries/node-entries-card/node-entries-card.main.scss +261 -0
- package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.html +205 -0
- package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.scss +181 -0
- package/src/lib/node-entries/node-entries-card-grid/node-entries-card-grid.component.ts +361 -0
- package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.html +100 -0
- package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.scss +46 -0
- package/src/lib/node-entries/node-entries-card-small/node-entries-card-small.component.ts +40 -0
- package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.html +23 -0
- package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.scss +58 -0
- package/src/lib/node-entries/node-entries-global-options/node-entries-global-options.component.ts +16 -0
- package/src/lib/node-entries/node-entries-global.service.ts +79 -0
- package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.html +25 -0
- package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.scss +32 -0
- package/src/lib/node-entries/node-entries-table/column-chooser/column-chooser.component.ts +31 -0
- package/src/lib/node-entries/node-entries-table/node-entries-table.component.html +270 -0
- package/src/lib/node-entries/node-entries-table/node-entries-table.component.scss +169 -0
- package/src/lib/node-entries/node-entries-table/node-entries-table.component.ts +333 -0
- package/src/lib/node-entries/node-entries-templates.service.ts +31 -0
- package/src/lib/node-entries/node-entries-wrapper.component.ts +363 -0
- package/src/lib/node-entries/node-entries.component.html +33 -0
- package/src/lib/node-entries/node-entries.component.scss +13 -0
- package/src/lib/node-entries/node-entries.component.ts +151 -0
- package/src/lib/node-entries/node-entries.module.ts +93 -0
- package/src/lib/node-entries/node-rating/node-rating.component.html +53 -0
- package/src/lib/node-entries/node-rating/node-rating.component.scss +31 -0
- package/src/lib/node-entries/node-rating/node-rating.component.ts +105 -0
- package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.html +39 -0
- package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.scss +44 -0
- package/src/lib/node-entries/node-stats-badges/node-stats-badges.component.ts +43 -0
- package/src/lib/node-entries/node-type-badge/node-type-badge.component.html +31 -0
- package/src/lib/node-entries/node-type-badge/node-type-badge.component.scss +5 -0
- package/src/lib/node-entries/node-type-badge/node-type-badge.component.ts +36 -0
- package/src/lib/node-entries/option-button/option-button.component.ts +42 -0
- package/src/lib/node-entries/preview-image/preview-image.component.html +19 -0
- package/src/lib/node-entries/preview-image/preview-image.component.scss +31 -0
- package/src/lib/node-entries/preview-image/preview-image.component.ts +47 -0
- package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.html +27 -0
- package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.scss +9 -0
- package/src/lib/node-entries/sort-select-panel/sort-select-panel.component.ts +26 -0
- package/src/lib/node-url/node-url.component.html +66 -0
- package/src/lib/node-url/node-url.component.scss +32 -0
- package/src/lib/node-url/node-url.component.ts +136 -0
- package/src/lib/pipes/file-size.pipe.ts +24 -0
- package/src/lib/pipes/format-date.pipe.ts +39 -0
- package/src/lib/pipes/node-icon.pipe.ts +11 -0
- package/src/lib/pipes/node-image-size.pipe.ts +18 -0
- package/src/lib/pipes/node-image.pipe.ts +71 -0
- package/src/lib/pipes/node-person-name.pipe.ts +41 -0
- package/src/lib/pipes/node-title.pipe.ts +12 -0
- package/src/lib/pipes/option-tooltip.pipe.ts +32 -0
- package/src/lib/pipes/replace-chars.pipe.ts +21 -0
- package/src/lib/pipes/vcard-name.pipe.ts +11 -0
- package/src/lib/services/abstract/app.service.ts +4 -0
- package/src/lib/services/abstract/keyboard-shortcuts.service.ts +10 -0
- package/src/lib/services/abstract/options-helper.service.ts +29 -0
- package/src/lib/services/abstract/toast.service.ts +5 -0
- package/src/lib/services/accessibility.service.ts +101 -0
- package/src/lib/services/local-events.service.ts +29 -0
- package/src/lib/services/node-entries.service.ts +172 -0
- package/src/lib/services/node-helper.service.ts +239 -0
- package/src/lib/services/nodes-drag-drop.service.ts +165 -0
- package/src/lib/services/options-helper-data.service.ts +186 -0
- package/src/lib/services/repo-url.service.ts +46 -0
- package/src/lib/services/temporary-storage.service.ts +58 -0
- package/src/lib/services/ui.service.ts +182 -0
- package/src/lib/sort-dropdown/sort-dropdown.component.html +22 -0
- package/src/lib/sort-dropdown/sort-dropdown.component.scss +47 -0
- package/src/lib/sort-dropdown/sort-dropdown.component.ts +42 -0
- package/src/lib/spinner/spinner.component.html +14 -0
- package/src/lib/spinner/spinner.component.scss +141 -0
- package/src/lib/spinner/spinner.component.ts +12 -0
- package/src/lib/translations/README.md +44 -0
- package/src/lib/translations/fallback-translation-handler.ts +7 -0
- package/src/lib/translations/languages.ts +6 -0
- package/src/lib/translations/translation-loader.spec.ts +352 -0
- package/src/lib/translations/translation-loader.ts +189 -0
- package/src/lib/translations/translation-source.ts +9 -0
- package/src/lib/translations/translations.module.ts +49 -0
- package/src/lib/translations/translations.service.spec.ts +152 -0
- package/src/lib/translations/translations.service.ts +188 -0
- package/src/lib/types/accessibillity.ts +15 -0
- package/src/lib/types/api-models.ts +4 -0
- package/src/lib/types/drag-drop.ts +22 -0
- package/src/lib/types/keyboard-shortcuts.ts +29 -0
- package/src/lib/types/list-item.ts +67 -0
- package/src/lib/types/option-item.ts +247 -0
- package/src/lib/types/workflow.ts +35 -0
- package/src/lib/util/DateHelper.spec.ts +112 -0
- package/src/lib/util/DateHelper.ts +197 -0
- package/src/lib/util/VCard.ts +277 -0
- package/src/lib/util/color-helper.ts +125 -0
- package/src/lib/util/duration-helper.spec.ts +35 -0
- package/src/lib/util/duration-helper.ts +98 -0
- package/src/lib/util/functions.ts +15 -0
- package/src/lib/util/helper.ts +60 -0
- package/src/lib/util/isNumeric.ts +13 -0
- package/src/lib/util/rest-helper.ts +28 -0
- package/src/lib/util/ui-animation.ts +154 -0
- package/src/lib/util/ui-constants.ts +20 -0
- package/src/module.ts +76 -0
- package/src/test.ts +28 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +10 -0
- 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,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() }}
|