@hug/ngx-layout 1.1.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/CHANGELOG.md +11 -0
- package/README.md +6 -0
- package/karma.conf.js +45 -0
- package/ng-package.json +15 -0
- package/package.json +46 -0
- package/src/_layout-theme.scss +136 -0
- package/src/index.ts +1 -0
- package/src/layout.component.html +60 -0
- package/src/layout.component.scss +251 -0
- package/src/layout.component.ts +155 -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
package/CHANGELOG.md
ADDED
package/README.md
ADDED
package/karma.conf.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Karma configuration file, see link for more information
|
|
2
|
+
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
3
|
+
|
|
4
|
+
module.exports = config => {
|
|
5
|
+
config.set({
|
|
6
|
+
basePath: '',
|
|
7
|
+
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
|
8
|
+
plugins: [
|
|
9
|
+
require('karma-jasmine'),
|
|
10
|
+
require('karma-chrome-launcher'),
|
|
11
|
+
require('karma-jasmine-html-reporter'),
|
|
12
|
+
require('karma-coverage'),
|
|
13
|
+
require('@angular-devkit/build-angular/plugins/karma')
|
|
14
|
+
],
|
|
15
|
+
client: {
|
|
16
|
+
jasmine: {
|
|
17
|
+
// you can add configuration options for Jasmine here
|
|
18
|
+
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
|
19
|
+
// for example, you can disable the random execution with `random: false`
|
|
20
|
+
// or set a specific seed with `seed: 4321`
|
|
21
|
+
},
|
|
22
|
+
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
|
23
|
+
},
|
|
24
|
+
jasmineHtmlReporter: {
|
|
25
|
+
suppressAll: true // removes the duplicated traces
|
|
26
|
+
},
|
|
27
|
+
coverageReporter: {
|
|
28
|
+
dir: require('path').join(__dirname, '../../coverage/layout'),
|
|
29
|
+
subdir: '.',
|
|
30
|
+
reporters: [
|
|
31
|
+
{ type: 'html' },
|
|
32
|
+
{ type: 'text-summary' }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
reporters: ['progress', 'kjhtml'],
|
|
36
|
+
port: 9876,
|
|
37
|
+
colors: true,
|
|
38
|
+
logLevel: config.LOG_INFO,
|
|
39
|
+
autoWatch: true,
|
|
40
|
+
browsers: ['Chrome'],
|
|
41
|
+
singleRun: false,
|
|
42
|
+
failOnEmptyTestSuite: false,
|
|
43
|
+
restartOnFileChange: true
|
|
44
|
+
});
|
|
45
|
+
};
|
package/ng-package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
|
3
|
+
"dest": "../../dist/layout",
|
|
4
|
+
"assets": [
|
|
5
|
+
"CHANGELOG.md",
|
|
6
|
+
{
|
|
7
|
+
"input": "src/",
|
|
8
|
+
"glob": "_layout-theme.scss",
|
|
9
|
+
"output": "."
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"lib": {
|
|
13
|
+
"entryFile": "src/index.ts"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hug/ngx-layout",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "HUG Angular - layout component",
|
|
5
|
+
"homepage": "https://github.com/dsi-hug/ngx-components",
|
|
6
|
+
"license": "GPL-3.0-only",
|
|
7
|
+
"author": "HUG - Hôpitaux Universitaires Genève",
|
|
8
|
+
"contributors": [
|
|
9
|
+
"badisi (https://github.com/badisi)",
|
|
10
|
+
"vapkse (https://github.com/vapkse)"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/dsi-hug/ngx-components.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"angular",
|
|
18
|
+
"material",
|
|
19
|
+
"material design",
|
|
20
|
+
"components"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"lint": "eslint . --fix",
|
|
25
|
+
"test": "ng test layout",
|
|
26
|
+
"test:ci": "ng test layout --watch=false --browsers=ChromeHeadless",
|
|
27
|
+
"build:ng": "ng build layout -c=production",
|
|
28
|
+
"build": "nx build:ng @hug/ngx-layout --verbose",
|
|
29
|
+
"release": "nx release -p=@hug/ngx-layout --yes --verbose",
|
|
30
|
+
"release:dry-run": "nx release -p=@hug/ngx-layout --verbose --dry-run"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@angular/common": ">= 14",
|
|
34
|
+
"@angular/core": ">= 14",
|
|
35
|
+
"@angular/cdk": ">= 14",
|
|
36
|
+
"@angular/material": ">= 14",
|
|
37
|
+
"@hug/ngx-core": "1.1.5",
|
|
38
|
+
"@hug/ngx-sidenav": "1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"tslib": "^2.6.3"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
@use '@angular/material'as mat;
|
|
2
|
+
|
|
3
|
+
@mixin layout-theme($theme) {
|
|
4
|
+
$primary: map-get($theme, primary);
|
|
5
|
+
$accent: map-get($theme, accent);
|
|
6
|
+
$background: map-get($theme, background);
|
|
7
|
+
$foreground: map-get($theme, foreground);
|
|
8
|
+
|
|
9
|
+
layout>.mat-toolbar.mat-primary {
|
|
10
|
+
background-color: mat.get-color-from-palette($primary, darker);
|
|
11
|
+
color: mat.get-color-from-palette($primary, darker-contrast);
|
|
12
|
+
|
|
13
|
+
.mat-icon-button:not([disabled]) .mat-icon {
|
|
14
|
+
color: mat.get-color-from-palette($primary, darker-contrast);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&:not(:first-of-type) {
|
|
18
|
+
background-color: mat.get-color-from-palette($primary);
|
|
19
|
+
color: mat.get-color-from-palette($primary, default-contrast);
|
|
20
|
+
|
|
21
|
+
.mat-icon-button:not([disabled]) .mat-icon {
|
|
22
|
+
color: mat.get-color-from-palette($primary, default-contrast);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
layout {
|
|
28
|
+
mat-drawer {
|
|
29
|
+
&.right {
|
|
30
|
+
[filters-title] {
|
|
31
|
+
color: mat.get-color-from-palette($foreground, secondary-text);
|
|
32
|
+
background-color: mat.get-color-from-palette($background, disabled-button-toggle);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
[filters-subtitle] {
|
|
36
|
+
color: mat.get-color-from-palette($foreground, secondary-text);
|
|
37
|
+
background-color: mat.get-color-from-palette($background, hover);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[filters-icon] {
|
|
41
|
+
color: mat.get-color-from-palette($foreground, text);
|
|
42
|
+
|
|
43
|
+
&:hover {
|
|
44
|
+
color: mat.get-color-from-palette($foreground, secondary-text);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[filters-chip-list] {
|
|
49
|
+
.mat-standard-chip {
|
|
50
|
+
background-color: mat.get-color-from-palette($accent);
|
|
51
|
+
color: mat.get-color-from-palette($background, background);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.mat-standard-chip {
|
|
55
|
+
&:not([disabled]) {
|
|
56
|
+
&:hover {
|
|
57
|
+
background-color: mat.get-color-from-palette($accent, 700);
|
|
58
|
+
color: mat.get-color-from-palette($accent, 600);
|
|
59
|
+
|
|
60
|
+
&:before {
|
|
61
|
+
color: mat.get-color-from-palette($background, background);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[filters-toggle-group] {
|
|
69
|
+
.mat-button-toggle {
|
|
70
|
+
color: mat.get-color-from-palette($foreground, secondary-text);
|
|
71
|
+
|
|
72
|
+
&.mat-button-toggle-checked {
|
|
73
|
+
background-color: mat.get-color-from-palette($accent);
|
|
74
|
+
color: mat.get-color-from-palette($background, background);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&:not(:has(button[disabled])) {
|
|
78
|
+
&:hover {
|
|
79
|
+
background-color: mat.get-color-from-palette($accent, 200);
|
|
80
|
+
color: mat.get-color-from-palette($background, background);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
mat-button-toggle-group {
|
|
89
|
+
&:not([filters-toggle-group]):not([color="accent"]):not([color="primary"]) {
|
|
90
|
+
mat-button-toggle {
|
|
91
|
+
button {
|
|
92
|
+
.mat-button-toggle-label-content {
|
|
93
|
+
color: mat.get-color-from-palette($foreground, secondary-text);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
mat-toolbar {
|
|
101
|
+
&.actions {
|
|
102
|
+
.info-boxes-container {
|
|
103
|
+
.info-box {
|
|
104
|
+
&.accent {
|
|
105
|
+
background-color: mat.get-color-from-palette($accent);
|
|
106
|
+
color: mat.get-color-from-palette($accent, default-contrast);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
&.primary {
|
|
110
|
+
background-color: mat.get-color-from-palette($primary, 800);
|
|
111
|
+
color: mat.get-color-from-palette($primary, default-contrast);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Custom scrollbar
|
|
119
|
+
::-webkit-scrollbar-track {
|
|
120
|
+
background-color: mat.get-color-from-palette($background, background);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
::-webkit-scrollbar-thumb {
|
|
124
|
+
background-color: mat.get-color-from-palette($foreground, hint-text);
|
|
125
|
+
border: 2px solid mat.get-color-from-palette($background, background);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
::-webkit-scrollbar-thumb:hover {
|
|
129
|
+
background-color: mat.get-color-from-palette($foreground, secondary-text);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.editor-toolbar-wrapper {
|
|
133
|
+
background-color: mat.get-color-from-palette($background, background);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './layout.component';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<ng-container *ngTemplateOutlet="filter"></ng-container>
|
|
2
|
+
|
|
3
|
+
<ng-template #content>
|
|
4
|
+
<div class="main-content">
|
|
5
|
+
<span class="primary-action-container" [class.bottom]="(mediaService.isHandset$ | async)" *ngIf="layoutPrimaryAction && !((layoutActions || layoutInfoBoxes) && ((mediaService.isHandset$ | async) === false))">
|
|
6
|
+
<ng-template [ngTemplateOutlet]="layoutPrimaryAction"></ng-template>
|
|
7
|
+
</span>
|
|
8
|
+
<ng-content></ng-content>
|
|
9
|
+
</div>
|
|
10
|
+
<ng-container *ngIf="(mediaService.isHandset$ | async) && actionsToolbar">
|
|
11
|
+
<ng-container *ngTemplateOutlet="actionsToolbar"></ng-container>
|
|
12
|
+
</ng-container>
|
|
13
|
+
</ng-template>
|
|
14
|
+
|
|
15
|
+
<ng-template #actionsToolbar>
|
|
16
|
+
<mat-toolbar id="actions-toolbar" class="actions" [color]="toolbarColor" [class.bottom]="(mediaService.isHandset$ | async)">
|
|
17
|
+
<span class="primary-action-container" *ngIf="layoutPrimaryAction && (layoutActions || layoutInfoBoxes) && ((mediaService.isHandset$ | async) === false)">
|
|
18
|
+
<ng-template [ngTemplateOutlet]="layoutPrimaryAction"></ng-template>
|
|
19
|
+
</span>
|
|
20
|
+
<ng-container *ngIf="layoutActions">
|
|
21
|
+
<ng-template [ngTemplateOutlet]="layoutActions"></ng-template>
|
|
22
|
+
</ng-container>
|
|
23
|
+
<div class="info-boxes-container" *ngIf="layoutInfoBoxes && (mediaService.isHandset$ | async) === false">
|
|
24
|
+
<ng-template [ngTemplateOutlet]="layoutInfoBoxes"></ng-template>
|
|
25
|
+
</div>
|
|
26
|
+
</mat-toolbar>
|
|
27
|
+
</ng-template>
|
|
28
|
+
|
|
29
|
+
<ng-template #filter>
|
|
30
|
+
<mat-toolbar id="toolbar" [color]="toolbarColor" *ngIf="layoutToolbar || layoutRight">
|
|
31
|
+
<button type="button" id="sidenav-button" mat-icon-button *ngIf="withSidenav && (mediaService.isHandset$ | async) && ((sidenavService.openChanged$ | async) === false)" (click)="sidenavService.toggle()">
|
|
32
|
+
<mat-icon>menu</mat-icon>
|
|
33
|
+
</button>
|
|
34
|
+
<button type="button" id="back-button" mat-icon-button *ngIf="withBackButton" (click)="this.backButtonClicked.emit($event)" [matTooltip]="backButtonLabel">
|
|
35
|
+
<mat-icon>arrow_back</mat-icon>
|
|
36
|
+
</button>
|
|
37
|
+
<div id="toolbar-content-container">
|
|
38
|
+
<ng-container *ngIf="layoutToolbar">
|
|
39
|
+
<ng-template [ngTemplateOutlet]="layoutToolbar"></ng-template>
|
|
40
|
+
</ng-container>
|
|
41
|
+
</div>
|
|
42
|
+
<button type="button" id="filter-button" mat-icon-button (click)="sideFilter.toggle()" *ngIf="sideFilter && (keepFilterButtonDisplayed || (mediaService.isHandset$ | async)) && layoutRight" matTooltip="Afficher/Masquer les filtres">
|
|
43
|
+
<mat-icon>tune</mat-icon>
|
|
44
|
+
</button>
|
|
45
|
+
<button type="button" id="close-button" mat-icon-button *ngIf="withCloseButton" (click)="this.closeButtonClicked.emit($event)" [matTooltip]="closeButtonLabel">
|
|
46
|
+
<mat-icon>close</mat-icon>
|
|
47
|
+
</button>
|
|
48
|
+
</mat-toolbar>
|
|
49
|
+
<ng-container *ngIf="(layoutActions || layoutInfoBoxes) && ((mediaService.isHandset$ | async) === false)">
|
|
50
|
+
<ng-container *ngTemplateOutlet="actionsToolbar"></ng-container>
|
|
51
|
+
</ng-container>
|
|
52
|
+
<mat-drawer-container *ngIf="layoutRight; else content" autosize="true">
|
|
53
|
+
<mat-drawer-content>
|
|
54
|
+
<ng-container *ngTemplateOutlet="content"></ng-container>
|
|
55
|
+
</mat-drawer-content>
|
|
56
|
+
<mat-drawer id="side-filter" #sideFilter (closed)="sideFilterClosed.emit()" (openedChange)="sideFilterOpened.emit()" class="right" position="end" [attr.role]="(mediaService.isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(mediaService.isHandset$ | async) ? 'over' : 'side'" [opened]="(mediaService.isHandset$ | async) === false">
|
|
57
|
+
<ng-template [ngTemplateOutlet]="layoutRight"></ng-template>
|
|
58
|
+
</mat-drawer>
|
|
59
|
+
</mat-drawer-container>
|
|
60
|
+
</ng-template>
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
layout {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
position: absolute;
|
|
5
|
+
top: 0;
|
|
6
|
+
bottom: 0;
|
|
7
|
+
left: 0;
|
|
8
|
+
right: 0;
|
|
9
|
+
|
|
10
|
+
&.no-left.no-right .main-content {
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
mat-sidenav-container {
|
|
15
|
+
position: absolute;
|
|
16
|
+
top: 0;
|
|
17
|
+
bottom: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
right: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
mat-sidenav-content,
|
|
23
|
+
mat-drawer-content {
|
|
24
|
+
display: flex !important;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mat-sidenav {
|
|
29
|
+
width: 200px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
mat-drawer {
|
|
33
|
+
width: 220px;
|
|
34
|
+
|
|
35
|
+
&.right {
|
|
36
|
+
padding: 0.3rem;
|
|
37
|
+
|
|
38
|
+
[filters-title] {
|
|
39
|
+
font-size: 1.2rem;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
flex: 1 1 auto;
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
padding: 0.6rem;
|
|
46
|
+
margin-bottom: 0.3rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[filters-subtitle] {
|
|
50
|
+
font-size: 1rem;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
flex: 1 1 auto;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
padding: 0.4rem 0.6rem;
|
|
57
|
+
margin-bottom: 0.3rem;
|
|
58
|
+
margin-top: 0.3rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
[icons-container] {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[filters-icon] {
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
transition: color 0.3s ease-in-out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[filters-chip-list] {
|
|
72
|
+
.mat-chip-list-wrapper {
|
|
73
|
+
margin: 0;
|
|
74
|
+
max-height: 20vh;
|
|
75
|
+
overflow-y: auto;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.mat-standard-chip {
|
|
79
|
+
position: relative;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.mat-standard-chip {
|
|
83
|
+
padding: 0.5rem;
|
|
84
|
+
margin: 0.1rem;
|
|
85
|
+
font-size: 0.75rem;
|
|
86
|
+
transition: 0.3s ease-in-out;
|
|
87
|
+
|
|
88
|
+
&:not([disabled]) {
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
|
|
91
|
+
&:hover {
|
|
92
|
+
&:before {
|
|
93
|
+
content: 'close';
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
|
|
98
|
+
position: absolute;
|
|
99
|
+
left: 0;
|
|
100
|
+
right: 0;
|
|
101
|
+
top: 0;
|
|
102
|
+
bottom: 0;
|
|
103
|
+
|
|
104
|
+
font-weight: bold;
|
|
105
|
+
font-size: 1.2rem;
|
|
106
|
+
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
|
|
109
|
+
transition: 0.3s ease-in-out;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
[filters-toggle-group] {
|
|
117
|
+
display: flex;
|
|
118
|
+
|
|
119
|
+
.mat-button-toggle {
|
|
120
|
+
transition: 0.3s ease-in-out;
|
|
121
|
+
|
|
122
|
+
mat-icon {
|
|
123
|
+
margin-right: 1rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.mat-button-toggle-label-content {
|
|
127
|
+
text-align: start;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.mat-button-toggle-focus-overlay {
|
|
131
|
+
height: 100%;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
mat-form-field {
|
|
137
|
+
width: 100%;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.mat-form-field-infix {
|
|
141
|
+
width: inherit;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&.mat-drawer-side {
|
|
145
|
+
box-shadow: -3px 0 5px -1px rgba(0, 0, 0, 0.2);
|
|
146
|
+
z-index: 10;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
mat-drawer-container {
|
|
152
|
+
flex: 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.mat-toolbar {
|
|
156
|
+
&:not(.actions) {
|
|
157
|
+
display: flex;
|
|
158
|
+
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2),
|
|
159
|
+
0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
|
|
160
|
+
z-index: 10;
|
|
161
|
+
|
|
162
|
+
#toolbar-content-container {
|
|
163
|
+
display: flex;
|
|
164
|
+
flex-grow: 1;
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
[toolbar-title] {
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
text-overflow: ellipsis;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
&>div {
|
|
174
|
+
display: flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
&#actions-toolbar {
|
|
180
|
+
flex: 0 0 auto;
|
|
181
|
+
height: 40px;
|
|
182
|
+
font-size: inherit;
|
|
183
|
+
padding-right: 0;
|
|
184
|
+
|
|
185
|
+
.info-boxes-container {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex: 1 1 auto;
|
|
188
|
+
justify-content: flex-end;
|
|
189
|
+
|
|
190
|
+
.info-box {
|
|
191
|
+
display: flex;
|
|
192
|
+
flex-grow: 0;
|
|
193
|
+
line-height: 40px;
|
|
194
|
+
padding-left: 2rem;
|
|
195
|
+
padding-right: 2rem;
|
|
196
|
+
box-shadow: -3px 0 5px -1px rgba(0, 0, 0, 0.2);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&.bottom {
|
|
201
|
+
&#actions-toolbar {
|
|
202
|
+
display: flex;
|
|
203
|
+
width: 100%;
|
|
204
|
+
justify-content: space-around;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.primary-action-container {
|
|
209
|
+
z-index: 20;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.main-content {
|
|
215
|
+
position: relative;
|
|
216
|
+
display: flex;
|
|
217
|
+
flex: 1 1 auto;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
|
|
220
|
+
.primary-action-container {
|
|
221
|
+
position: absolute;
|
|
222
|
+
z-index: 20;
|
|
223
|
+
opacity: 0.8;
|
|
224
|
+
transition: 0.3s ease-in-out;
|
|
225
|
+
|
|
226
|
+
&:hover {
|
|
227
|
+
opacity: 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
&.bottom {
|
|
231
|
+
right: 1rem;
|
|
232
|
+
bottom: 1rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
&:not(.bottom) {
|
|
236
|
+
top: 1rem;
|
|
237
|
+
left: 1rem;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
::-webkit-scrollbar {
|
|
243
|
+
width: 12px;
|
|
244
|
+
height: 12px;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
::-webkit-scrollbar-thumb {
|
|
248
|
+
transition: 0.3s ease-in-out;
|
|
249
|
+
border-radius: 6px;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { ChangeDetectionStrategy, Component, ContentChild, EventEmitter, HostBinding, Input, Output, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
4
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
5
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
+
import { MatDrawer, MatSidenavModule } from '@angular/material/sidenav';
|
|
7
|
+
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
8
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
9
|
+
import { MediaService } from '@hug/ngx-core';
|
|
10
|
+
import { SidenavService } from '@hug/ngx-sidenav';
|
|
11
|
+
|
|
12
|
+
@Component({
|
|
13
|
+
selector: 'layout',
|
|
14
|
+
templateUrl: './layout.component.html',
|
|
15
|
+
styleUrls: ['./layout.component.scss'],
|
|
16
|
+
encapsulation: ViewEncapsulation.None,
|
|
17
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
18
|
+
standalone: true,
|
|
19
|
+
imports: [
|
|
20
|
+
CommonModule,
|
|
21
|
+
MatButtonModule,
|
|
22
|
+
MatIconModule,
|
|
23
|
+
MatSidenavModule,
|
|
24
|
+
MatToolbarModule,
|
|
25
|
+
MatTooltipModule
|
|
26
|
+
]
|
|
27
|
+
})
|
|
28
|
+
export class LayoutComponent {
|
|
29
|
+
@Input() public toolbarColor = 'primary';
|
|
30
|
+
@Input() public editorToolbarId = 'editor-toolbar';
|
|
31
|
+
|
|
32
|
+
@Input() public closeButtonLabel = 'Fermer';
|
|
33
|
+
@Input() public backButtonLabel = 'Retour';
|
|
34
|
+
|
|
35
|
+
@Input() public layoutToolbarExternal?: TemplateRef<unknown>;
|
|
36
|
+
@Input() public layoutPrimaryActionExternal?: TemplateRef<unknown>;
|
|
37
|
+
@Input() public layoutActionsExternal?: TemplateRef<unknown>;
|
|
38
|
+
@Input() public layoutInfoBoxesExternal?: TemplateRef<unknown>;
|
|
39
|
+
@Input() public layoutRightExternal?: TemplateRef<unknown>;
|
|
40
|
+
|
|
41
|
+
@Output() public readonly closeButtonClicked = new EventEmitter<MouseEvent>();
|
|
42
|
+
@Output() public readonly backButtonClicked = new EventEmitter<MouseEvent>();
|
|
43
|
+
@Output() public readonly sideFilterClosed = new EventEmitter<void>();
|
|
44
|
+
@Output() public readonly sideFilterOpened = new EventEmitter<void>();
|
|
45
|
+
|
|
46
|
+
@ContentChild('layoutToolbar') protected layoutToolbarContent?: TemplateRef<unknown>;
|
|
47
|
+
@ContentChild('layoutPrimaryAction') protected layoutPrimaryActionContent?: TemplateRef<unknown>;
|
|
48
|
+
@ContentChild('layoutActions') protected layoutActionsContent?: TemplateRef<unknown>;
|
|
49
|
+
@ContentChild('layoutInfoBoxes') protected layoutInfoBoxesContent?: TemplateRef<unknown>;
|
|
50
|
+
@ContentChild('layoutRight') protected layoutRightContent?: TemplateRef<unknown>;
|
|
51
|
+
|
|
52
|
+
@HostBinding('class.no-right') protected noRight = false;
|
|
53
|
+
|
|
54
|
+
@ViewChild('sideFilter') protected sideFilter?: MatDrawer;
|
|
55
|
+
|
|
56
|
+
private _withEditorToolbar = true;
|
|
57
|
+
|
|
58
|
+
public get layoutToolbar(): TemplateRef<unknown> | undefined {
|
|
59
|
+
return this.layoutToolbarExternal ?? this.layoutToolbarContent;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public get layoutPrimaryAction(): TemplateRef<unknown> | undefined {
|
|
63
|
+
return this.layoutPrimaryActionExternal ?? this.layoutPrimaryActionContent;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get layoutActions(): TemplateRef<unknown> | undefined {
|
|
67
|
+
return this.layoutActionsExternal ?? this.layoutActionsContent;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public get layoutInfoBoxes(): TemplateRef<unknown> | undefined {
|
|
71
|
+
return this.layoutInfoBoxesExternal ?? this.layoutInfoBoxesContent;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public get layoutRight(): TemplateRef<unknown> | undefined {
|
|
75
|
+
const value = this.layoutRightExternal ?? this.layoutRightContent;
|
|
76
|
+
this.noRight = !value;
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Input()
|
|
81
|
+
public set withEditorToolbar(value: BooleanInput) {
|
|
82
|
+
this._withEditorToolbar = coerceBooleanProperty(value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public get withEditorToolbar(): BooleanInput {
|
|
86
|
+
return this._withEditorToolbar;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private _withSidenav = false;
|
|
90
|
+
|
|
91
|
+
@Input()
|
|
92
|
+
public set withSidenav(value: BooleanInput) {
|
|
93
|
+
this._withSidenav = coerceBooleanProperty(value);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public get withSidenav(): BooleanInput {
|
|
97
|
+
return this._withSidenav;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private _keepFilterButtonDisplayed = true;
|
|
101
|
+
@Input()
|
|
102
|
+
public set keepFilterButtonDisplayed(value: BooleanInput) {
|
|
103
|
+
this._keepFilterButtonDisplayed = coerceBooleanProperty(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public get keepFilterButtonDisplayed(): BooleanInput {
|
|
107
|
+
return this._keepFilterButtonDisplayed;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private _withCloseButton = false;
|
|
111
|
+
@Input()
|
|
112
|
+
public set withCloseButton(value: BooleanInput) {
|
|
113
|
+
this._withCloseButton = coerceBooleanProperty(value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public get withCloseButton(): BooleanInput {
|
|
117
|
+
return this._withCloseButton;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private _withBackButton = false;
|
|
121
|
+
|
|
122
|
+
@Input()
|
|
123
|
+
public set withBackButton(value: BooleanInput) {
|
|
124
|
+
this._withBackButton = coerceBooleanProperty(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public get withBackButton(): BooleanInput {
|
|
128
|
+
return this._withBackButton;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private _displayEditorToolbar = true;
|
|
132
|
+
|
|
133
|
+
@Input()
|
|
134
|
+
public set displayEditorToolbar(value: BooleanInput) {
|
|
135
|
+
this._displayEditorToolbar = coerceBooleanProperty(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public get displayEditorToolbar(): BooleanInput {
|
|
139
|
+
return this._displayEditorToolbar;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public constructor(
|
|
143
|
+
protected mediaService: MediaService,
|
|
144
|
+
protected sidenavService: SidenavService
|
|
145
|
+
) {
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public closeSideFilter(): void {
|
|
149
|
+
void this.sideFilter?.close();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public openSideFilter(): void {
|
|
153
|
+
void this.sideFilter?.open();
|
|
154
|
+
}
|
|
155
|
+
}
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
2
|
+
|
|
3
|
+
import 'zone.js';
|
|
4
|
+
import 'zone.js/testing';
|
|
5
|
+
|
|
6
|
+
import { getTestBed } from '@angular/core/testing';
|
|
7
|
+
import {
|
|
8
|
+
BrowserDynamicTestingModule,
|
|
9
|
+
platformBrowserDynamicTesting
|
|
10
|
+
} from '@angular/platform-browser-dynamic/testing';
|
|
11
|
+
|
|
12
|
+
declare const require: {
|
|
13
|
+
context: (path: string, deep?: boolean, filter?: RegExp) => {
|
|
14
|
+
<T>(id: string): T;
|
|
15
|
+
keys: () => string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// First, initialize the Angular testing environment.
|
|
20
|
+
getTestBed().initTestEnvironment(
|
|
21
|
+
BrowserDynamicTestingModule,
|
|
22
|
+
platformBrowserDynamicTesting()
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Then we find all the tests.
|
|
26
|
+
const context = require.context('./', true, /\.spec\.ts$/);
|
|
27
|
+
// And load the modules.
|
|
28
|
+
context.keys().forEach(context);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.base.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/lib",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"inlineSources": true,
|
|
9
|
+
"types": []
|
|
10
|
+
},
|
|
11
|
+
"exclude": [
|
|
12
|
+
"src/test.ts",
|
|
13
|
+
"**/*.spec.ts"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/spec",
|
|
6
|
+
"types": [
|
|
7
|
+
"jasmine"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src/test.ts"
|
|
12
|
+
],
|
|
13
|
+
"include": [
|
|
14
|
+
"**/*.spec.ts",
|
|
15
|
+
"**/*.d.ts"
|
|
16
|
+
]
|
|
17
|
+
}
|