@lukfel/ng-scaffold 20.0.10
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/.eslintrc.json +37 -0
- package/README.md +382 -0
- package/ng-package.json +8 -0
- package/package.json +28 -0
- package/src/lib/components/bottom-bar/bottom-bar.component.html +29 -0
- package/src/lib/components/bottom-bar/bottom-bar.component.scss +33 -0
- package/src/lib/components/bottom-bar/bottom-bar.component.spec.ts +24 -0
- package/src/lib/components/bottom-bar/bottom-bar.component.ts +31 -0
- package/src/lib/components/content-title-card/content-title-card.component.html +25 -0
- package/src/lib/components/content-title-card/content-title-card.component.scss +17 -0
- package/src/lib/components/content-title-card/content-title-card.component.spec.ts +108 -0
- package/src/lib/components/content-title-card/content-title-card.component.ts +24 -0
- package/src/lib/components/drawer/drawer.component.html +33 -0
- package/src/lib/components/drawer/drawer.component.scss +10 -0
- package/src/lib/components/drawer/drawer.component.spec.ts +52 -0
- package/src/lib/components/drawer/drawer.component.ts +30 -0
- package/src/lib/components/floating-button/floating-button.component.html +32 -0
- package/src/lib/components/floating-button/floating-button.component.scss +20 -0
- package/src/lib/components/floating-button/floating-button.component.spec.ts +84 -0
- package/src/lib/components/floating-button/floating-button.component.ts +57 -0
- package/src/lib/components/footer/footer.component.html +38 -0
- package/src/lib/components/footer/footer.component.scss +39 -0
- package/src/lib/components/footer/footer.component.spec.ts +118 -0
- package/src/lib/components/footer/footer.component.ts +14 -0
- package/src/lib/components/header/header.component.html +170 -0
- package/src/lib/components/header/header.component.scss +102 -0
- package/src/lib/components/header/header.component.spec.ts +134 -0
- package/src/lib/components/header/header.component.ts +53 -0
- package/src/lib/components/loading-overlay/loading-overlay.component.html +3 -0
- package/src/lib/components/loading-overlay/loading-overlay.component.scss +16 -0
- package/src/lib/components/loading-overlay/loading-overlay.component.spec.ts +24 -0
- package/src/lib/components/loading-overlay/loading-overlay.component.ts +10 -0
- package/src/lib/components/navbar/navbar.component.html +43 -0
- package/src/lib/components/navbar/navbar.component.scss +71 -0
- package/src/lib/components/navbar/navbar.component.spec.ts +43 -0
- package/src/lib/components/navbar/navbar.component.ts +35 -0
- package/src/lib/components/scaffold/scaffold.component.html +74 -0
- package/src/lib/components/scaffold/scaffold.component.scss +48 -0
- package/src/lib/components/scaffold/scaffold.component.spec.ts +119 -0
- package/src/lib/components/scaffold/scaffold.component.ts +191 -0
- package/src/lib/interceptors/loading.interceptor.ts +51 -0
- package/src/lib/models/bottom-bar-config.model.ts +8 -0
- package/src/lib/models/confirm-dialog-config.model.ts +6 -0
- package/src/lib/models/content-title-card-config.model.ts +6 -0
- package/src/lib/models/drawer-config.model.ts +6 -0
- package/src/lib/models/floating-button-config.model.ts +13 -0
- package/src/lib/models/footer-config.model.ts +10 -0
- package/src/lib/models/header-config.model.ts +26 -0
- package/src/lib/models/index.ts +15 -0
- package/src/lib/models/library-config.model.ts +4 -0
- package/src/lib/models/menu-button.model.ts +10 -0
- package/src/lib/models/navbar-config.model.ts +8 -0
- package/src/lib/models/navigation-link.model.ts +6 -0
- package/src/lib/models/placeholder-config.model.ts +7 -0
- package/src/lib/models/scaffold-config.model.ts +21 -0
- package/src/lib/models/seo-config.model.ts +6 -0
- package/src/lib/scaffold.module.ts +54 -0
- package/src/lib/services/breakpoint.service.spec.ts +15 -0
- package/src/lib/services/breakpoint.service.ts +16 -0
- package/src/lib/services/dialog.service.spec.ts +18 -0
- package/src/lib/services/dialog.service.ts +58 -0
- package/src/lib/services/index.ts +9 -0
- package/src/lib/services/local-storage.service.spec.ts +15 -0
- package/src/lib/services/local-storage.service.ts +125 -0
- package/src/lib/services/logger.service.spec.ts +15 -0
- package/src/lib/services/logger.service.ts +46 -0
- package/src/lib/services/router.service.spec.ts +15 -0
- package/src/lib/services/router.service.ts +91 -0
- package/src/lib/services/scaffold.service.spec.ts +15 -0
- package/src/lib/services/scaffold.service.ts +77 -0
- package/src/lib/services/seo.service.spec.ts +15 -0
- package/src/lib/services/seo.service.ts +75 -0
- package/src/lib/services/snackbar.service.spec.ts +18 -0
- package/src/lib/services/snackbar.service.ts +38 -0
- package/src/lib/services/theme.service.spec.ts +20 -0
- package/src/lib/services/theme.service.ts +71 -0
- package/src/lib/shared/components/dialogs/confirm-dialog/confirm-dialog.component.html +24 -0
- package/src/lib/shared/components/dialogs/confirm-dialog/confirm-dialog.component.scss +0 -0
- package/src/lib/shared/components/dialogs/confirm-dialog/confirm-dialog.component.spec.ts +85 -0
- package/src/lib/shared/components/dialogs/confirm-dialog/confirm-dialog.component.ts +14 -0
- package/src/lib/shared/components/file-upload/file-upload.component.html +21 -0
- package/src/lib/shared/components/file-upload/file-upload.component.scss +5 -0
- package/src/lib/shared/components/file-upload/file-upload.component.spec.ts +25 -0
- package/src/lib/shared/components/file-upload/file-upload.component.ts +43 -0
- package/src/lib/shared/components/icon/icon.component.html +17 -0
- package/src/lib/shared/components/icon/icon.component.scss +9 -0
- package/src/lib/shared/components/icon/icon.component.spec.ts +22 -0
- package/src/lib/shared/components/icon/icon.component.ts +17 -0
- package/src/lib/shared/components/input/input.component.html +38 -0
- package/src/lib/shared/components/input/input.component.scss +31 -0
- package/src/lib/shared/components/input/input.component.spec.ts +62 -0
- package/src/lib/shared/components/input/input.component.ts +72 -0
- package/src/lib/shared/components/placeholder/placeholder.component.html +21 -0
- package/src/lib/shared/components/placeholder/placeholder.component.scss +30 -0
- package/src/lib/shared/components/placeholder/placeholder.component.spec.ts +24 -0
- package/src/lib/shared/components/placeholder/placeholder.component.ts +16 -0
- package/src/lib/shared/modules/material.module.ts +77 -0
- package/src/lib/shared/shared.module.ts +18 -0
- package/src/public-api.ts +16 -0
- package/styles/_classes.scss +34 -0
- package/styles/_theme.scss +97 -0
- package/styles/_variables.scss +50 -0
- package/styles/fonts/icons/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 +0 -0
- package/styles/fonts/icons/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUce.woff2 +0 -0
- package/styles/fonts/material-icons.scss +48 -0
- package/styles/fonts/roboto/roboto-v30-latin-100.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-100.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-100italic.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-100italic.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-300.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-300.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-300italic.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-300italic.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-500.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-500.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-500italic.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-500italic.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-700.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-700.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-700italic.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-700italic.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-italic.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-italic.woff2 +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-regular.woff +0 -0
- package/styles/fonts/roboto/roboto-v30-latin-regular.woff2 +0 -0
- package/styles/fonts/roboto-font.scss +109 -0
- package/styles/style.scss +54 -0
- package/tsconfig.lib.json +14 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +14 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../.eslintrc.json",
|
|
3
|
+
"ignorePatterns": [
|
|
4
|
+
"!**/*"
|
|
5
|
+
],
|
|
6
|
+
"overrides": [
|
|
7
|
+
{
|
|
8
|
+
"files": [
|
|
9
|
+
"*.ts"
|
|
10
|
+
],
|
|
11
|
+
"rules": {
|
|
12
|
+
"@angular-eslint/directive-selector": [
|
|
13
|
+
"error",
|
|
14
|
+
{
|
|
15
|
+
"type": "attribute",
|
|
16
|
+
"prefix": "lf",
|
|
17
|
+
"style": "camelCase"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"@angular-eslint/component-selector": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
"type": "element",
|
|
24
|
+
"prefix": "lf",
|
|
25
|
+
"style": "kebab-case"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"files": [
|
|
32
|
+
"*.html"
|
|
33
|
+
],
|
|
34
|
+
"rules": {}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
## Introduction
|
|
3
|
+
|
|
4
|
+
This Angular library provides a foundational scaffold for modern web and mobile applications. It includes essential UI elements such as a header, sidebar, drawer, footer, floating button, and built-in services for theme switching, snackbar notifications, dialog management, and breakpoint detection. Simply wrap your `router-outlet` with the `lf-scaffold` element and configure the `ScaffoldConfig` within the `ScaffoldService`.
|
|
5
|
+
|
|
6
|
+
- **NPM**: [@lukfel/ng-scaffold](https://www.npmjs.com/package/@lukfel/ng-scaffold)
|
|
7
|
+
- **Demo**: [lukfel.github.io/ng-scaffold](https://lukfel.github.io/ng-scaffold)
|
|
8
|
+
- **Examples**: [Create a Tournament](https://www.create-a-tournament.com), [What a Waste](https://www.what-a-waste.at), [Uglygotchi](https://www.uglygotchi.at)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
Install the package using npm:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install @lukfel/ng-scaffold
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Module
|
|
24
|
+
Import the `ScaffoldModule` into your `app.module.ts` file.
|
|
25
|
+
|
|
26
|
+
* **Note:** (Optional) The library includes a built-in logging service called `Logger`, which logs library deugging events when a `ScaffoldLibraryConfig` is provided and `debugging` is set to `true`. Logging is automatically disabled in production mode when `prodution` is set to `true`.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { ScaffoldModule } from '@lukfel/ng-scaffold';
|
|
30
|
+
import { isDevMode } from '@angular/core';
|
|
31
|
+
|
|
32
|
+
@NgModule({
|
|
33
|
+
...
|
|
34
|
+
imports: [
|
|
35
|
+
ScaffoldModule.forRoot({ production: !isDevMode(), debugging: isDevMode() }), // Omit .forRoot(...) if logging is not required
|
|
36
|
+
]
|
|
37
|
+
})
|
|
38
|
+
export class AppModule { }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Styling
|
|
45
|
+
Import the styles in your `styles.scss` and apply a default theme.
|
|
46
|
+
|
|
47
|
+
* **Note:** The library’s styles include Material icons and Roboto font styles.
|
|
48
|
+
|
|
49
|
+
```scss
|
|
50
|
+
@use "@lukfel/ng-scaffold/styles" as lf;
|
|
51
|
+
@include lf.scaffold-theme(); // include a default theme
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Custom Themes (Optional)
|
|
55
|
+
To customize the default theme, define a new theme map specifying `primary`, `accent`, and `warn` colors using Material palettes. Enabling the `dark` option applies a dark theme. Pass your custom theme to `lf.scaffold-theme($my-theme)`.
|
|
56
|
+
|
|
57
|
+
```scss
|
|
58
|
+
@use "@lukfel/ng-scaffold/styles" as lf;
|
|
59
|
+
@use '@angular/material' as mat;
|
|
60
|
+
|
|
61
|
+
$my-theme: (
|
|
62
|
+
primary: mat.define-palette(mat.$pink-palette),
|
|
63
|
+
accent: mat.define-palette(mat.$blue-palette),
|
|
64
|
+
warn: mat.define-palette(mat.$red-palette),
|
|
65
|
+
dark: false
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
@include lf.scaffold-theme($my-theme);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Multiple Themes (Optional)
|
|
72
|
+
To switch between multiple themes dynamically, define additional themes using `lf.scaffold-colors($theme, 'theme-class')`, then apply the class to the `<body class="theme-class">` tag.
|
|
73
|
+
|
|
74
|
+
* **Note:** The `ThemeService` allows dynamic theme switching.
|
|
75
|
+
|
|
76
|
+
```scss
|
|
77
|
+
@use "@lukfel/ng-scaffold/styles" as lf;
|
|
78
|
+
@use '@angular/material' as mat;
|
|
79
|
+
|
|
80
|
+
$my-theme: (
|
|
81
|
+
primary: mat.define-palette(mat.$pink-palette),
|
|
82
|
+
accent: mat.define-palette(mat.$blue-palette),
|
|
83
|
+
warn: mat.define-palette(mat.$red-palette),
|
|
84
|
+
dark: false
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
$my-theme2: (
|
|
88
|
+
primary: mat.define-palette(mat.$purple-palette),
|
|
89
|
+
accent: mat.define-palette(mat.$amber-palette),
|
|
90
|
+
dark: false
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
@include lf.scaffold-theme($my-theme); // Set the primary theme with lf.scaffold-theme(...)
|
|
94
|
+
@include lf.scaffold-colors($my-theme2, 'my-theme2'); // Set additional themes with lf.scaffold-colors(...)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Custom Typography (Optional)
|
|
98
|
+
To change the default typography from Roboto, pass an additional parameter ``font-family`` in the theme map.
|
|
99
|
+
|
|
100
|
+
* **Note:** Don't forget to also import and set the font-family in the styles.
|
|
101
|
+
|
|
102
|
+
```scss
|
|
103
|
+
@use "@lukfel/ng-scaffold/styles" as lf;
|
|
104
|
+
@use '@angular/material' as mat;
|
|
105
|
+
|
|
106
|
+
$my-theme: (
|
|
107
|
+
primary: mat.define-palette(mat.$pink-palette),
|
|
108
|
+
accent: mat.define-palette(mat.$blue-palette),
|
|
109
|
+
warn: mat.define-palette(mat.$red-palette),
|
|
110
|
+
dark: false,
|
|
111
|
+
font-family: 'Comic Sans'
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
@include lf.scaffold-theme($my-theme);
|
|
115
|
+
|
|
116
|
+
body {
|
|
117
|
+
font-family: "Comic Sans MS" !important;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## Template
|
|
125
|
+
Wrap your application’s content inside the `lf-scaffold` component in `app.component.html`.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<lf-scaffold>
|
|
129
|
+
<!-- (Optional) drawer content shows inside the left drawer if enabled -->
|
|
130
|
+
<ng-container drawerContent></ng-container>
|
|
131
|
+
<router-outlet></router-outlet>
|
|
132
|
+
</lf-scaffold>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
Import the `ScaffoldService` in `app.component.ts` to manage the `ScaffoldConfig` settings.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { ScaffoldService } from '@lukfel/ng-scaffold';
|
|
143
|
+
|
|
144
|
+
export class AppComponent {
|
|
145
|
+
constructor(private scaffoldService: ScaffoldService) {}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Update Configuration
|
|
150
|
+
Define the `ScaffoldConfig` in `app.component.ts` and update the `scaffoldConfig` property in `ScaffoldService`.
|
|
151
|
+
|
|
152
|
+
* **Notes:**
|
|
153
|
+
* If a sub-configuration (e.g., `headerConfig`) is missing or does not have `enable: true`, the corresponding UI element will not be displayed.
|
|
154
|
+
* Refer to the demo project for full configuration details.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { ScaffoldService, ScaffoldConfig } from '@lukfel/ng-scaffold';
|
|
158
|
+
|
|
159
|
+
export class AppComponent {
|
|
160
|
+
|
|
161
|
+
public scaffoldConfig: ScaffoldConfig = {
|
|
162
|
+
scrollPositionRestoration: true,
|
|
163
|
+
headerConfig: { enable: true, title: 'Scaffold', subtitle: 'by Lukas Felbinger' },
|
|
164
|
+
navbarConfig: { enable: true },
|
|
165
|
+
footerConfig: { enable: true, copyright: '© Lukas Felbinger 2023' },
|
|
166
|
+
floatingButtonConfig: { enable: true }
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
constructor(private scaffoldService: ScaffoldService) {
|
|
170
|
+
this.scaffoldService.scaffoldConfig = this.scaffoldConfig;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## Events
|
|
179
|
+
There are two ways to listen to scaffold user events (button clicks, input changes, ...):
|
|
180
|
+
|
|
181
|
+
### Option 1 (Recommended) – Subscribe to Observables
|
|
182
|
+
Subscribe to the event Observables and listen to changes
|
|
183
|
+
```ts
|
|
184
|
+
constructor(private scaffoldService: ScaffoldService, private router: Router) {
|
|
185
|
+
// Listen to click events (header menu and navbar buttons - click)
|
|
186
|
+
this.scaffoldService.buttonClickEventValue$.subscribe((id: string) => {
|
|
187
|
+
this.router.navigate([id]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Listen to header input change events (header input field - change)
|
|
191
|
+
this.scaffoldService.headerInputChangeValue$.subscribe((value: string) => {
|
|
192
|
+
...
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Option 2 – Use Output Events
|
|
198
|
+
Specify the needed output events and call custom methods
|
|
199
|
+
```html
|
|
200
|
+
<lf-scaffold
|
|
201
|
+
(headerButtonClickEvent)="headerButtonClickEvent($event)"
|
|
202
|
+
(headerInputSubmitEvent)="headerInputSubmitEvent($event)"
|
|
203
|
+
(headerInputChangeEvent)="headerInputChangeEvent($event)"
|
|
204
|
+
(navbarButtonClickEvent)="navbarButtonClickEvent($event)">
|
|
205
|
+
<router-outlet></router-outlet>
|
|
206
|
+
</lf-scaffold>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
// Listen to header click events (header menu buttons - click)
|
|
211
|
+
public headerButtonClickEvent(id: string): void {
|
|
212
|
+
this.router.navigate([id]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Listen to header input submit events (header input field - submit)
|
|
216
|
+
public headerInputSubmitEvent(value: string): void {
|
|
217
|
+
...
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Listen to header input change events (header input field - change)
|
|
221
|
+
public headerInputChangeEvent(value: string): void {
|
|
222
|
+
...
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Listen to navbar click events (navbar menu buttons - click)
|
|
226
|
+
public navbarButtonClickEvent(id: string): void {
|
|
227
|
+
this.router.navigate([id]);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
## Additional Services
|
|
235
|
+
This library includes several utility services:
|
|
236
|
+
|
|
237
|
+
- **`Logger`** – Development-only logging
|
|
238
|
+
- **`SnackbarService`** – Display snackbar notifications
|
|
239
|
+
- **`DialogService`** – Display custom dialogs
|
|
240
|
+
- **`BreakpointService`** – Detect screen breakpoints
|
|
241
|
+
- **`ThemeService`** – Manage themes dynamically
|
|
242
|
+
- **`RouterService`** – Track route changes and retreive route history
|
|
243
|
+
- **`SeoService`** – Manage meta tags
|
|
244
|
+
- **`LocalStorageService`** – Handle local storage
|
|
245
|
+
|
|
246
|
+
### Logger
|
|
247
|
+
Logs internal library information if `debugging` is `true` and hides application logs during production if `production` is `true`.
|
|
248
|
+
* **Note:** `ScaffoldLibraryConfig` must be set during initialization ``ScaffoldModule.forRoot( { production: !isDevMode(), debugging: isDevMode() } )``
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { Logger } from '@lukfel/ng-scaffold';
|
|
252
|
+
|
|
253
|
+
export class AppComponent {
|
|
254
|
+
|
|
255
|
+
constructor(private logger: Logger) {}
|
|
256
|
+
|
|
257
|
+
// Generic api call with logging
|
|
258
|
+
public apiCallWithLogging(): void {
|
|
259
|
+
this.apiService.apiCall().then(result => {
|
|
260
|
+
this.logger.log(result);
|
|
261
|
+
}).catch(error => {
|
|
262
|
+
this.logger.error(error);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### SnackbarService
|
|
269
|
+
Provides basic methods to display simple snackbar notifications with or without actions.
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { SnackbarService } from '@lukfel/ng-scaffold';
|
|
273
|
+
|
|
274
|
+
export class AppComponent {
|
|
275
|
+
|
|
276
|
+
constructor(private snackbarService: SnackbarService) {}
|
|
277
|
+
|
|
278
|
+
// Generic api call with snackbar response
|
|
279
|
+
public apiCallWithSnackbarResponse(): void {
|
|
280
|
+
this.apiService.apiCall().then(result => {
|
|
281
|
+
this.snackbarService.openSnackbar('Call was successful');
|
|
282
|
+
}).catch(error => {
|
|
283
|
+
this.snackbarService.openSnackbar('Call was not successful');
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### DialogService
|
|
290
|
+
Includes a basic confirmation dialog thar returns a `Promise`. Use the method `openCustomDialog` to pass your own dialog template and config.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { DialogService } from '@lukfel/ng-scaffold';
|
|
294
|
+
|
|
295
|
+
export class AppComponent {
|
|
296
|
+
|
|
297
|
+
constructor(private dialogService: DialogService) {}
|
|
298
|
+
|
|
299
|
+
// Generic api call with a subsequent confirmation dialog
|
|
300
|
+
public apiCallWithDialogConfirmation(): void {
|
|
301
|
+
this.dialogService.openConfirmDialog('Do you really want to make this api call?').then(response => {
|
|
302
|
+
// If the user confirmed the dialog, go through with the api call
|
|
303
|
+
if(response === true) {
|
|
304
|
+
this.apiService.apiCall().then(result => {
|
|
305
|
+
...
|
|
306
|
+
}).catch(error => {
|
|
307
|
+
...
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### BreakpointService
|
|
316
|
+
Allows you to subscribe to breakpoint changes.
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import { BreakpointService } from '@lukfel/ng-scaffold';
|
|
320
|
+
|
|
321
|
+
export class AppComponent {
|
|
322
|
+
|
|
323
|
+
constructor(private breakpointService: BreakpointService) {
|
|
324
|
+
this.breakpointService.breakpoint$.subscribe((result: BreakpointState) => {
|
|
325
|
+
// Check which breakpoint is active
|
|
326
|
+
if (result.breakpoints[Breakpoints.XSmall]) {
|
|
327
|
+
...
|
|
328
|
+
} else if (result.breakpoints[Breakpoints.Small]) {
|
|
329
|
+
...
|
|
330
|
+
} else if (result.breakpoints[Breakpoints.Medium]) {
|
|
331
|
+
...
|
|
332
|
+
} else if (result.breakpoints[Breakpoints.Large]) {
|
|
333
|
+
...
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### ThemeService
|
|
341
|
+
Dynamically change between your defined themes.
|
|
342
|
+
|
|
343
|
+
* **Note:** The theme must be defined and included in your styles [see multiple themes](#multiple-themes-optional)
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { ThemeService } from '@lukfel/ng-scaffold';
|
|
347
|
+
|
|
348
|
+
export class AppComponent {
|
|
349
|
+
|
|
350
|
+
constructor(private themeService: ThemeService) {
|
|
351
|
+
this.themeService.setTheme('my-theme2', true); // the second parameter allows to persists the theme in the LocalStorage (using the built in LocalStorageService)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
## Interceptors
|
|
360
|
+
Intercept HTTP Calls and automatically show a loading spinner.
|
|
361
|
+
|
|
362
|
+
* **Note:** The loading spinner can also be manually shown by udpating the value for `scaffoldConfig.loading` in the `ScaffoldService`
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
import { ScaffoldModule } from '@lukfel/ng-scaffold';
|
|
366
|
+
import { isDevMode } from '@angular/core';
|
|
367
|
+
|
|
368
|
+
@NgModule({
|
|
369
|
+
...
|
|
370
|
+
imports: [
|
|
371
|
+
ScaffoldModule.forRoot({ production: !isDevMode(), debugging: isDevMode() }), // Omit .forRoot(...) if logging is not required
|
|
372
|
+
],
|
|
373
|
+
providers: [
|
|
374
|
+
{
|
|
375
|
+
provide: HTTP_INTERCEPTORS,
|
|
376
|
+
useClass: LoadingInterceptor,
|
|
377
|
+
multi: true
|
|
378
|
+
}
|
|
379
|
+
]
|
|
380
|
+
})
|
|
381
|
+
export class AppModule { }
|
|
382
|
+
```
|
package/ng-package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lukfel/ng-scaffold",
|
|
3
|
+
"version": "20.0.10",
|
|
4
|
+
"description": "This Angular library provides a basic UI scaffold and services for modern web and mobile apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Lukas Felbinger",
|
|
8
|
+
"url": "https://www.lukasfelbinger.at"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/lukfel/ng-scaffold"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@angular/common": "^20.0.5",
|
|
16
|
+
"@angular/core": "^20.0.5",
|
|
17
|
+
"@angular/material": "^20.0.5"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"tslib": "^2.3.0"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
"./styles": {
|
|
24
|
+
"sass": "./styles/style.scss"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@if (bottomBarConfig && bottomBarConfig.enable) {
|
|
2
|
+
<div
|
|
3
|
+
class="lf-bottom-bar px-4"
|
|
4
|
+
[ngClass]="bottomBarConfig.class"
|
|
5
|
+
[class.lf-bottom-bar-show-navbar]="!isMobile && navbarEnabled"
|
|
6
|
+
[class.lf-bottom-bar-show-navbar-mobile]="isMobile && navbarEnabled">
|
|
7
|
+
<button mat-icon-button (click)="closeClicked()">
|
|
8
|
+
<mat-icon>close</mat-icon>
|
|
9
|
+
</button>
|
|
10
|
+
@if (bottomBarConfig.message) {
|
|
11
|
+
<span class="ml-2">{{ bottomBarConfig.message }}</span>
|
|
12
|
+
}
|
|
13
|
+
@if (bottomBarConfig.actions) {
|
|
14
|
+
<div class="lf-bottom-bar-actions ml-4">
|
|
15
|
+
@for (action of bottomBarConfig.actions; track action) {
|
|
16
|
+
<button
|
|
17
|
+
mat-button
|
|
18
|
+
color="accent"
|
|
19
|
+
class="lf-bottom-bar-action ml-2"
|
|
20
|
+
[ngClass]="action.class"
|
|
21
|
+
(click)="actionClicked(action.id)"
|
|
22
|
+
[matTooltip]="action.tooltip!">
|
|
23
|
+
{{ action.label }}
|
|
24
|
+
</button>
|
|
25
|
+
}
|
|
26
|
+
</div>
|
|
27
|
+
}
|
|
28
|
+
</div>
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@use '../../../../styles/variables' as *;
|
|
2
|
+
|
|
3
|
+
.lf-bottom-bar {
|
|
4
|
+
transition: bottom $transition;
|
|
5
|
+
transition: left $transition;
|
|
6
|
+
z-index: 101;
|
|
7
|
+
position: fixed;
|
|
8
|
+
left: 0;
|
|
9
|
+
bottom: 0;
|
|
10
|
+
right: 0;
|
|
11
|
+
height: $bottom-bar-height;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
background-color: var(--color-accent);
|
|
16
|
+
color: white;
|
|
17
|
+
|
|
18
|
+
&.lf-bottom-bar-show-navbar-mobile {
|
|
19
|
+
bottom: calc($navbar-height-mobile + (2 * $navbar-padding)) !important;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&.lf-bottom-bar-show-navbar {
|
|
23
|
+
left: calc($navbar-width + (2 * $navbar-padding)) !important;;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.lf-bottom-bar-actions {
|
|
27
|
+
.lf-bottom-bar-action {
|
|
28
|
+
color: var(--color-accent) !important;
|
|
29
|
+
background-color: white !important;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { SharedModule } from '../../shared/shared.module';
|
|
3
|
+
import { BottomBarComponent } from './bottom-bar.component';
|
|
4
|
+
|
|
5
|
+
describe('BottomBarComponent', () => {
|
|
6
|
+
let component: BottomBarComponent;
|
|
7
|
+
let fixture: ComponentFixture<BottomBarComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
declarations: [BottomBarComponent],
|
|
12
|
+
imports: [SharedModule]
|
|
13
|
+
})
|
|
14
|
+
.compileComponents();
|
|
15
|
+
|
|
16
|
+
fixture = TestBed.createComponent(BottomBarComponent);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
fixture.detectChanges();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should create', () => {
|
|
22
|
+
expect(component).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
import { BottomBarConfig } from '../../models';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'lf-bottom-bar',
|
|
6
|
+
templateUrl: './bottom-bar.component.html',
|
|
7
|
+
styleUrls: ['./bottom-bar.component.scss'],
|
|
8
|
+
standalone: false
|
|
9
|
+
})
|
|
10
|
+
export class BottomBarComponent {
|
|
11
|
+
|
|
12
|
+
@Input() public bottomBarConfig: BottomBarConfig | null = null;
|
|
13
|
+
@Input() public isMobile: boolean = false;
|
|
14
|
+
@Input() public navbarEnabled: boolean = false;
|
|
15
|
+
|
|
16
|
+
@Output() public bottomBarCloseClickEvent = new EventEmitter<string>();
|
|
17
|
+
@Output() public bottomBarButtonClickEvent = new EventEmitter<string>();
|
|
18
|
+
|
|
19
|
+
public actionClicked(id?: string): void {
|
|
20
|
+
if (!id) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.bottomBarButtonClickEvent.emit(id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public closeClicked(): void {
|
|
28
|
+
this.bottomBarCloseClickEvent.emit('bottom-bar_close');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@if (contentTitleCardConfig && contentTitleCardConfig.enable) {
|
|
2
|
+
<mat-card
|
|
3
|
+
class="lf-content-title-card mat-elevation-z2 px-4"
|
|
4
|
+
[ngClass]="contentTitleCardConfig.class">
|
|
5
|
+
<!-- back button -->
|
|
6
|
+
@if (contentTitleCardConfig.showBackButton && routeHistory.length > 1) {
|
|
7
|
+
<button mat-icon-button color="accent" (click)="backButtonClicked()">
|
|
8
|
+
<mat-icon>arrow_back_ios</mat-icon>
|
|
9
|
+
</button>
|
|
10
|
+
}
|
|
11
|
+
<!-- spacer -->
|
|
12
|
+
<div style="flex: 1 1 auto"></div>
|
|
13
|
+
<!-- label -->
|
|
14
|
+
<span class="lf-content-title-card-label">
|
|
15
|
+
{{ contentTitleCardConfig.label || "" }}
|
|
16
|
+
</span>
|
|
17
|
+
<!-- spacer -->
|
|
18
|
+
<div style="flex: 1 1 auto"></div>
|
|
19
|
+
<!-- empty button spacer -->
|
|
20
|
+
<div
|
|
21
|
+
[style.width]="
|
|
22
|
+
contentTitleCardConfig.showBackButton && routeHistory.length > 1 ? '48px' : '0px'
|
|
23
|
+
"></div>
|
|
24
|
+
</mat-card>
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@use '../../../../styles/variables' as *;
|
|
2
|
+
|
|
3
|
+
.lf-content-title-card {
|
|
4
|
+
z-index: $content-title-card-z-index;
|
|
5
|
+
height: $content-title-card-height;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-flow: row nowrap;
|
|
8
|
+
align-items: center;
|
|
9
|
+
border-radius: 0;
|
|
10
|
+
|
|
11
|
+
.lf-content-title-card-label {
|
|
12
|
+
font-size: $content-title-card-labe-font-size;
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
text-overflow: ellipsis;
|
|
16
|
+
}
|
|
17
|
+
}
|