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,141 @@
|
|
|
1
|
+
/*SPINNER*/
|
|
2
|
+
$path: '../../../../assets/spinner/';
|
|
3
|
+
.spinner {
|
|
4
|
+
margin: 0 auto;
|
|
5
|
+
}
|
|
6
|
+
.spinnercontainer {
|
|
7
|
+
margin: auto;
|
|
8
|
+
margin-top: 20px;
|
|
9
|
+
height: 54px;
|
|
10
|
+
color: rgba(0, 0, 0, 0); /*do not change*/
|
|
11
|
+
text-align: center;
|
|
12
|
+
float: left;
|
|
13
|
+
width: 100%;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.inner {
|
|
17
|
+
width: 30px;
|
|
18
|
+
height: 50px;
|
|
19
|
+
display: inline-block;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.spinner1 {
|
|
23
|
+
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDEyLjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgNTE0NDgpICAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiBbCgk8IUVOVElUWSBuc19zdmcgImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTwhRU5USVRZIG5zX3hsaW5rICJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KXT4KPHN2ZyAgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9IiZuc19zdmc7IiB4bWxuczp4bGluaz0iJm5zX3hsaW5rOyIgd2lkdGg9IjE1LjY1NyIgaGVpZ2h0PSIxMy41NTkiCgkgdmlld0JveD0iMCAwIDE1LjY1NyAxMy41NTkiIG92ZXJmbG93PSJ2aXNpYmxlIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxNS42NTcgMTMuNTU5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBvbHlnb24gb3BhY2l0eT0iMC42IiBmaWxsPSIjYzFjNmUzIiBwb2ludHM9IjMuOTE0LDEzLjU1OSAwLDYuNzggMy45MTQsMCAxMS43NDMsMCAxNS42NTcsNi43OCAxMS43NDMsMTMuNTU5ICIvPgo8L3N2Zz4K);
|
|
24
|
+
background-repeat: no-repeat;
|
|
25
|
+
background-position: center;
|
|
26
|
+
background-size: 50px;
|
|
27
|
+
width: 50px;
|
|
28
|
+
height: 50px;
|
|
29
|
+
-webkit-animation: spin 2s infinite ease-in;
|
|
30
|
+
-moz-animation: spin 2s infinite ease-in;
|
|
31
|
+
-ms-animation: spin 2s infinite ease-in;
|
|
32
|
+
-o-animation: spin 2s infinite ease-in;
|
|
33
|
+
animation: spin 2s infinite ease-in;
|
|
34
|
+
-webkit-animation-delay: 0.1s;
|
|
35
|
+
-moz-animation-delay: 0.1s;
|
|
36
|
+
animation-delay: 0.1s;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.spinner2 {
|
|
40
|
+
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDEyLjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgNTE0NDgpICAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiBbCgk8IUVOVElUWSBuc19zdmcgImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTwhRU5USVRZIG5zX3hsaW5rICJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KXT4KPHN2ZyAgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9IiZuc19zdmc7IiB4bWxuczp4bGluaz0iJm5zX3hsaW5rOyIgd2lkdGg9IjE1LjY1NyIgaGVpZ2h0PSIxMy41NTkiCgkgdmlld0JveD0iMCAwIDE1LjY1NyAxMy41NTkiIG92ZXJmbG93PSJ2aXNpYmxlIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxNS42NTcgMTMuNTU5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBvbHlnb24gb3BhY2l0eT0iMC42IiBmaWxsPSIjN2Y5MWMzIiBwb2ludHM9IjMuOTE0LDEzLjU1OSAwLDYuNzggMy45MTQsMCAxMS43NDMsMCAxNS42NTcsNi43OCAxMS43NDMsMTMuNTU5ICIvPgo8L3N2Zz4K);
|
|
41
|
+
background-repeat: no-repeat;
|
|
42
|
+
background-position: center;
|
|
43
|
+
background-size: 50px;
|
|
44
|
+
width: 50px;
|
|
45
|
+
height: 50px;
|
|
46
|
+
-webkit-animation: spin 2s infinite ease-in;
|
|
47
|
+
-moz-animation: spin 2s infinite ease-in;
|
|
48
|
+
-ms-animation: spin 2s infinite ease-in;
|
|
49
|
+
-o-animation: spin 2s infinite ease-in;
|
|
50
|
+
animation: spin 2s infinite ease-in;
|
|
51
|
+
-webkit-animation-delay: 0.25s;
|
|
52
|
+
-moz-animation-delay: 0.25s;
|
|
53
|
+
animation-delay: 0.25s;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.spinner3 {
|
|
57
|
+
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDEyLjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgNTE0NDgpICAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiBbCgk8IUVOVElUWSBuc19zdmcgImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTwhRU5USVRZIG5zX3hsaW5rICJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KXT4KPHN2ZyAgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9IiZuc19zdmc7IiB4bWxuczp4bGluaz0iJm5zX3hsaW5rOyIgd2lkdGg9IjE1LjY1NyIgaGVpZ2h0PSIxMy41NTkiCgkgdmlld0JveD0iMCAwIDE1LjY1NyAxMy41NTkiIG92ZXJmbG93PSJ2aXNpYmxlIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxNS42NTcgMTMuNTU5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBvbHlnb24gb3BhY2l0eT0iMC42IiBmaWxsPSIjMzE2MmE3IiBwb2ludHM9IjMuOTE0LDEzLjU1OSAwLDYuNzggMy45MTQsMCAxMS43NDMsMCAxNS42NTcsNi43OCAxMS43NDMsMTMuNTU5ICIvPgo8L3N2Zz4K);
|
|
58
|
+
background-repeat: no-repeat;
|
|
59
|
+
background-position: center;
|
|
60
|
+
background-size: 50px;
|
|
61
|
+
width: 50px;
|
|
62
|
+
height: 50px;
|
|
63
|
+
-webkit-animation: spin 2s infinite ease-in;
|
|
64
|
+
-moz-animation: spin 2s infinite ease-in;
|
|
65
|
+
-ms-animation: spin 2s infinite ease-in;
|
|
66
|
+
-o-animation: spin 2s infinite ease-in;
|
|
67
|
+
animation: spin 2s infinite ease-in;
|
|
68
|
+
-webkit-animation-delay: 0.5s;
|
|
69
|
+
-moz-animation-delay: 0.5s;
|
|
70
|
+
animation-delay: 0.5s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@-webkit-keyframes spin {
|
|
74
|
+
0% {
|
|
75
|
+
transform: scale(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
50% {
|
|
79
|
+
transform: scale(0.5);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
100% {
|
|
83
|
+
transform: scale(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@-moz-keyframes spin {
|
|
88
|
+
0% {
|
|
89
|
+
transform: scale(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
50% {
|
|
93
|
+
transform: scale(0.5);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
100% {
|
|
97
|
+
transform: scale(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@-ms-keyframes spin {
|
|
102
|
+
0% {
|
|
103
|
+
transform: scale(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
50% {
|
|
107
|
+
transform: scale(0.5);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
100% {
|
|
111
|
+
transform: scale(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@-o-keyframes spin {
|
|
116
|
+
0% {
|
|
117
|
+
transform: scale(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
50% {
|
|
121
|
+
transform: scale(0.5);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
100% {
|
|
125
|
+
transform: scale(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@keyframes spin {
|
|
130
|
+
0% {
|
|
131
|
+
transform: scale(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
50% {
|
|
135
|
+
transform: scale(0.5) rotate(90deg);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
100% {
|
|
139
|
+
transform: scale(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Component, HostBinding, OnInit } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'es-spinner',
|
|
5
|
+
templateUrl: 'spinner.component.html',
|
|
6
|
+
styleUrls: ['spinner.component.scss'],
|
|
7
|
+
})
|
|
8
|
+
export class SpinnerComponent {
|
|
9
|
+
@HostBinding('attr.data-test') readonly dataTest = 'loading-spinner';
|
|
10
|
+
|
|
11
|
+
constructor() {}
|
|
12
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Translations
|
|
2
|
+
|
|
3
|
+
Depending on the application mode, translations are loaded locally or are provided by the backend.
|
|
4
|
+
|
|
5
|
+
- In **development mode**, translations are loaded from `assets/i18n/<language>.json`)
|
|
6
|
+
- In **production mode**, translations are provided by the backend.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
If possible, use the `translate` directive:
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<div translate [translateParams]="{value: 'world'}">HELLO</div>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
... or the `translate` pipe:
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<div>{{ 'HELLO' | translate:{value: 'world'} }}</div>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You can also translate strings programmatically:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { TranslateService } from "@ngx-translate/core";
|
|
26
|
+
import { TranslationsService } from '../translations/translations.service';
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
private translate: TranslateService,
|
|
30
|
+
private translations: TranslationsService,
|
|
31
|
+
) {
|
|
32
|
+
// This is the preferred way of programmatically using translations.
|
|
33
|
+
this.translate.get('HELLO', {value: 'world'}).subscribe((translatedString) => {
|
|
34
|
+
// Do stuff.
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Do this only if you have to use `translate.instant()`.
|
|
38
|
+
this.translations.waitForInit().subscribe(() => {
|
|
39
|
+
// You can safely use `this.translate.instant()` here.
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
See https://github.com/ngx-translate/core for more information.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx-translate/core';
|
|
2
|
+
|
|
3
|
+
export class FallbackTranslationHandler implements MissingTranslationHandler {
|
|
4
|
+
handle(params: MissingTranslationHandlerParams) {
|
|
5
|
+
return (params.interpolateParams as any)?.fallback ?? params.key;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { fakeAsync, tick } from '@angular/core/testing';
|
|
3
|
+
import { ConfigService } from 'ngx-edu-sharing-api';
|
|
4
|
+
import * as rxjs from 'rxjs';
|
|
5
|
+
import { Observable } from 'rxjs';
|
|
6
|
+
import { TRANSLATION_LIST, TranslationLoader } from './translation-loader';
|
|
7
|
+
import { TranslationSource } from './translation-source';
|
|
8
|
+
import { EduSharingUiConfiguration } from '../edu-sharing-ui-configuration';
|
|
9
|
+
|
|
10
|
+
class HttpClientStub {
|
|
11
|
+
get(url: string): Observable<any> {
|
|
12
|
+
return rxjs.of(null);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class EduSharingUiConfigurationStub {
|
|
16
|
+
production = false;
|
|
17
|
+
}
|
|
18
|
+
class ConfigStub {
|
|
19
|
+
observeTranslationOverrides(lang: string): Observable<any> {
|
|
20
|
+
return rxjs.of(null);
|
|
21
|
+
}
|
|
22
|
+
observeDefaultTranslations(lang: string): Observable<any> {
|
|
23
|
+
return rxjs.of(null);
|
|
24
|
+
}
|
|
25
|
+
setLocale(lang: string): void {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('TranslationLoader', () => {
|
|
29
|
+
let translationLoader: TranslationLoader;
|
|
30
|
+
let httpClient: HttpClientStub;
|
|
31
|
+
let config: ConfigStub;
|
|
32
|
+
let uiConfig: EduSharingUiConfigurationStub;
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
httpClient = new HttpClientStub();
|
|
36
|
+
config = new ConfigStub();
|
|
37
|
+
uiConfig = new EduSharingUiConfigurationStub();
|
|
38
|
+
translationLoader = TranslationLoader.create(
|
|
39
|
+
httpClient as HttpClient,
|
|
40
|
+
config as unknown as ConfigService,
|
|
41
|
+
uiConfig as unknown as EduSharingUiConfiguration,
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('getTranslation', () => {
|
|
46
|
+
function callGetTranslation(lang: string) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
fakeAsync(() => {
|
|
49
|
+
translationLoader
|
|
50
|
+
.getTranslation(lang)
|
|
51
|
+
.subscribe((result) => resolve(result), fail);
|
|
52
|
+
tick(100);
|
|
53
|
+
})();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
it('should run', async () => {
|
|
58
|
+
const result = await callGetTranslation('de');
|
|
59
|
+
expect(result).toEqual({});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Source = Local', () => {
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
(translationLoader as any).source = TranslationSource.Local;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should call nothing for lang=none', async () => {
|
|
68
|
+
const getSpy = spyOn(httpClient, 'get').and.callThrough();
|
|
69
|
+
const getConfigLanguageSpy = spyOn(
|
|
70
|
+
config,
|
|
71
|
+
'observeTranslationOverrides',
|
|
72
|
+
).and.callThrough();
|
|
73
|
+
const getLanguageDefaultsSpy = spyOn(
|
|
74
|
+
config,
|
|
75
|
+
'observeDefaultTranslations',
|
|
76
|
+
).and.callThrough();
|
|
77
|
+
await callGetTranslation('none');
|
|
78
|
+
expect(getSpy.calls.count()).toBe(0);
|
|
79
|
+
expect(getConfigLanguageSpy.calls.count()).toBe(0);
|
|
80
|
+
expect(getLanguageDefaultsSpy.calls.count()).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should call http get', async () => {
|
|
84
|
+
const getSpy = spyOn(httpClient, 'get').and.callThrough();
|
|
85
|
+
await callGetTranslation('de');
|
|
86
|
+
expect(getSpy.calls.count()).toBe(TRANSLATION_LIST.length);
|
|
87
|
+
expect(getSpy.calls.first().args).toEqual(['assets/i18n/common/de.json']);
|
|
88
|
+
expect(getSpy.calls.mostRecent().args).toEqual(['assets/i18n/override/de.json']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should call observeTranslationOverrides', async () => {
|
|
92
|
+
const getConfigLanguageSpy = spyOn(
|
|
93
|
+
config,
|
|
94
|
+
'observeTranslationOverrides',
|
|
95
|
+
).and.callThrough();
|
|
96
|
+
await callGetTranslation('de');
|
|
97
|
+
expect(getConfigLanguageSpy.calls.count()).toBe(1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should call setLocale with correct locale', async () => {
|
|
101
|
+
const setLocaleSpy = spyOn(config, 'setLocale').and.callThrough();
|
|
102
|
+
await callGetTranslation('de');
|
|
103
|
+
expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE']);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should not call observeDefaultTranslations', async () => {
|
|
107
|
+
const getLanguageDefaultsSpy = spyOn(
|
|
108
|
+
config,
|
|
109
|
+
'observeDefaultTranslations',
|
|
110
|
+
).and.callThrough();
|
|
111
|
+
await callGetTranslation('de');
|
|
112
|
+
expect(getLanguageDefaultsSpy.calls.count()).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should include translations via http', async () => {
|
|
116
|
+
httpClient.get = (url) => {
|
|
117
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
118
|
+
return rxjs.of({ foo: 'bar' });
|
|
119
|
+
}
|
|
120
|
+
return rxjs.of(null);
|
|
121
|
+
};
|
|
122
|
+
const result = await callGetTranslation('de');
|
|
123
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should merge translations via http', async () => {
|
|
127
|
+
httpClient.get = (url) => {
|
|
128
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
129
|
+
return rxjs.of({ foo: 'bar' });
|
|
130
|
+
} else if (url === 'assets/i18n/admin/de.json') {
|
|
131
|
+
return rxjs.of({ bar: 'baz' });
|
|
132
|
+
}
|
|
133
|
+
return rxjs.of(null);
|
|
134
|
+
};
|
|
135
|
+
const result = await callGetTranslation('de');
|
|
136
|
+
expect(result).toEqual({ foo: 'bar', bar: 'baz' });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should override nested translations via http', async () => {
|
|
140
|
+
httpClient.get = (url) => {
|
|
141
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
142
|
+
return rxjs.of({ prefix: { foo: 'bar' } });
|
|
143
|
+
} else if (url === 'assets/i18n/admin/de.json') {
|
|
144
|
+
return rxjs.of({ prefix: { bar: 'baz' } });
|
|
145
|
+
}
|
|
146
|
+
return rxjs.of(null);
|
|
147
|
+
};
|
|
148
|
+
const result = await callGetTranslation('de');
|
|
149
|
+
expect(result).toEqual({ prefix: { bar: 'baz' } });
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should include translations via observeTranslationOverrides', async () => {
|
|
153
|
+
config.observeTranslationOverrides = (lang) => {
|
|
154
|
+
return rxjs.of({ foo: 'bar' });
|
|
155
|
+
};
|
|
156
|
+
const result = await callGetTranslation('de');
|
|
157
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should override translations via observeTranslationOverrides', async () => {
|
|
161
|
+
httpClient.get = (url) => {
|
|
162
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
163
|
+
return rxjs.of({ foo: 'bar' });
|
|
164
|
+
}
|
|
165
|
+
return rxjs.of(null);
|
|
166
|
+
};
|
|
167
|
+
config.observeTranslationOverrides = (lang) => {
|
|
168
|
+
return rxjs.of({ foo: 'baz' });
|
|
169
|
+
};
|
|
170
|
+
const result = await callGetTranslation('de');
|
|
171
|
+
expect(result).toEqual({ foo: 'baz' });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should merge translations via observeTranslationOverrides', async () => {
|
|
175
|
+
httpClient.get = (url) => {
|
|
176
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
177
|
+
return rxjs.of({ foo: 'bar' });
|
|
178
|
+
}
|
|
179
|
+
return rxjs.of(null);
|
|
180
|
+
};
|
|
181
|
+
config.observeTranslationOverrides = (lang) => {
|
|
182
|
+
return rxjs.of({ bar: 'baz' });
|
|
183
|
+
};
|
|
184
|
+
const result = await callGetTranslation('de');
|
|
185
|
+
expect(result).toEqual({ foo: 'bar', bar: 'baz' });
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should override nested translations via observeTranslationOverrides', async () => {
|
|
189
|
+
httpClient.get = (url) => {
|
|
190
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
191
|
+
return rxjs.of({ prefix: { foo: 'bar' } });
|
|
192
|
+
}
|
|
193
|
+
return rxjs.of(null);
|
|
194
|
+
};
|
|
195
|
+
config.observeTranslationOverrides = (lang) => {
|
|
196
|
+
return rxjs.of({ prefix: { bar: 'baz' } });
|
|
197
|
+
};
|
|
198
|
+
const result = await callGetTranslation('de');
|
|
199
|
+
expect(result).toEqual({ prefix: { bar: 'baz' } });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should deep-merge translations via observeTranslationOverrides', async () => {
|
|
203
|
+
httpClient.get = (url) => {
|
|
204
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
205
|
+
return rxjs.of({ prefix: { foo: 'bar' } });
|
|
206
|
+
}
|
|
207
|
+
return rxjs.of(null);
|
|
208
|
+
};
|
|
209
|
+
config.observeTranslationOverrides = (lang) => {
|
|
210
|
+
return rxjs.of({ 'prefix.bar': 'baz' });
|
|
211
|
+
};
|
|
212
|
+
const result = await callGetTranslation('de');
|
|
213
|
+
expect(result).toEqual({ prefix: { foo: 'bar', bar: 'baz' } });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should deep-merge translations via observeTranslationOverrides (3 levels)', async () => {
|
|
217
|
+
httpClient.get = (url) => {
|
|
218
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
219
|
+
return rxjs.of({
|
|
220
|
+
l1: { l2: { l3: { foo: 'bar' } } },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return rxjs.of(null);
|
|
224
|
+
};
|
|
225
|
+
config.observeTranslationOverrides = (lang) => {
|
|
226
|
+
return rxjs.of({ 'l1.l2.l3.bar': 'baz' });
|
|
227
|
+
};
|
|
228
|
+
const result = await callGetTranslation('de');
|
|
229
|
+
expect(result).toEqual({
|
|
230
|
+
l1: { l2: { l3: { foo: 'bar', bar: 'baz' } } },
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should create missing levels via observeTranslationOverrides', async () => {
|
|
235
|
+
httpClient.get = (url) => {
|
|
236
|
+
if (url === 'assets/i18n/common/de.json') {
|
|
237
|
+
return rxjs.of({});
|
|
238
|
+
}
|
|
239
|
+
return rxjs.of(null);
|
|
240
|
+
};
|
|
241
|
+
config.observeTranslationOverrides = (lang) => {
|
|
242
|
+
return rxjs.of({ 'l1.l2.l3.bar': 'baz' });
|
|
243
|
+
};
|
|
244
|
+
const result = await callGetTranslation('de');
|
|
245
|
+
expect(result).toEqual({
|
|
246
|
+
l1: { l2: { l3: { bar: 'baz' } } },
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('Source = Repository', () => {
|
|
252
|
+
beforeEach(() => {
|
|
253
|
+
(translationLoader as any).source = TranslationSource.Repository;
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should call nothing for lang=none', async () => {
|
|
257
|
+
const getSpy = spyOn(httpClient, 'get').and.callThrough();
|
|
258
|
+
const getConfigLanguageSpy = spyOn(
|
|
259
|
+
config,
|
|
260
|
+
'observeTranslationOverrides',
|
|
261
|
+
).and.callThrough();
|
|
262
|
+
const getLanguageDefaultsSpy = spyOn(
|
|
263
|
+
config,
|
|
264
|
+
'observeDefaultTranslations',
|
|
265
|
+
).and.callThrough();
|
|
266
|
+
await callGetTranslation('none');
|
|
267
|
+
expect(getSpy.calls.count()).toBe(0);
|
|
268
|
+
expect(getConfigLanguageSpy.calls.count()).toBe(0);
|
|
269
|
+
expect(getLanguageDefaultsSpy.calls.count()).toBe(0);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should not call http get', async () => {
|
|
273
|
+
const getSpy = spyOn(httpClient, 'get').and.callThrough();
|
|
274
|
+
await callGetTranslation('de');
|
|
275
|
+
expect(getSpy.calls.count()).toBe(0);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should call observeTranslationOverrides', async () => {
|
|
279
|
+
const getConfigLanguageSpy = spyOn(
|
|
280
|
+
config,
|
|
281
|
+
'observeTranslationOverrides',
|
|
282
|
+
).and.callThrough();
|
|
283
|
+
await callGetTranslation('de');
|
|
284
|
+
expect(getConfigLanguageSpy.calls.count()).toBe(1);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should call observeDefaultTranslations', async () => {
|
|
288
|
+
const getLanguageDefaultsSpy = spyOn(
|
|
289
|
+
config,
|
|
290
|
+
'observeDefaultTranslations',
|
|
291
|
+
).and.callThrough();
|
|
292
|
+
await callGetTranslation('de');
|
|
293
|
+
expect(getLanguageDefaultsSpy.calls.count()).toBe(1);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should call setLocale with correct locale', async () => {
|
|
297
|
+
const setLocaleSpy = spyOn(config, 'setLocale').and.callThrough();
|
|
298
|
+
await callGetTranslation('de');
|
|
299
|
+
expect(setLocaleSpy.calls.mostRecent().args).toEqual(['de_DE']);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should include translations via observeDefaultTranslations', async () => {
|
|
303
|
+
config.observeDefaultTranslations = (lang) => {
|
|
304
|
+
return rxjs.of({ foo: 'bar' });
|
|
305
|
+
};
|
|
306
|
+
const result = await callGetTranslation('de');
|
|
307
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should include translations via observeTranslationOverrides', async () => {
|
|
311
|
+
config.observeTranslationOverrides = (lang) => {
|
|
312
|
+
return rxjs.of({ foo: 'bar' });
|
|
313
|
+
};
|
|
314
|
+
const result = await callGetTranslation('de');
|
|
315
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should merge translations via observeTranslationOverrides', async () => {
|
|
319
|
+
config.observeDefaultTranslations = (lang) => {
|
|
320
|
+
return rxjs.of({ foo: 'bar' });
|
|
321
|
+
};
|
|
322
|
+
config.observeTranslationOverrides = (lang) => {
|
|
323
|
+
return rxjs.of({ bar: 'baz' });
|
|
324
|
+
};
|
|
325
|
+
const result = await callGetTranslation('de');
|
|
326
|
+
expect(result).toEqual({ foo: 'bar', bar: 'baz' });
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should override nested translations via observeTranslationOverrides', async () => {
|
|
330
|
+
config.observeDefaultTranslations = (lang) => {
|
|
331
|
+
return rxjs.of({ prefix: { foo: 'bar' } });
|
|
332
|
+
};
|
|
333
|
+
config.observeTranslationOverrides = (lang) => {
|
|
334
|
+
return rxjs.of({ prefix: { bar: 'baz' } });
|
|
335
|
+
};
|
|
336
|
+
const result = await callGetTranslation('de');
|
|
337
|
+
expect(result).toEqual({ prefix: { bar: 'baz' } });
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should deep-merge translations via observeTranslationOverrides', async () => {
|
|
341
|
+
config.observeDefaultTranslations = (lang) => {
|
|
342
|
+
return rxjs.of({ prefix: { foo: 'bar' } });
|
|
343
|
+
};
|
|
344
|
+
config.observeTranslationOverrides = (lang) => {
|
|
345
|
+
return rxjs.of({ 'prefix.bar': 'baz' });
|
|
346
|
+
};
|
|
347
|
+
const result = await callGetTranslation('de');
|
|
348
|
+
expect(result).toEqual({ prefix: { foo: 'bar', bar: 'baz' } });
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|