@c8y/tutorial 1023.97.8 → 1024.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/cumulocity.config.ts +7 -7
  2. package/package.json +14 -12
  3. package/src/app/app.config.ts +2 -1
  4. package/src/dashboard/custom-dashboard/custom-dashboard.component.html +46 -29
  5. package/src/dashboard/custom-dashboard/custom-dashboard.component.ts +1 -0
  6. package/src/dashboard/responsive-layout-dashboard/responsive-layout-dashboard.component.html +99 -0
  7. package/src/dashboard/responsive-layout-dashboard/responsive-layout-dashboard.component.ts +26 -0
  8. package/src/dashboard/responsive-layout-dashboard/responsive-layout-dashboard.module.ts +24 -0
  9. package/src/grids/device-grid-example/device-grid-example-configuration-strategy.ts +2 -2
  10. package/src/grids/server-grid-example/server-grid-example.service.ts +1 -2
  11. package/src/grids/server-grid-example/void-configuration-strategy.ts +2 -2
  12. package/src/grids/tree-grid-example/server-tree-grid-example.service.ts +1 -2
  13. package/src/hooks/query-param-bottom-drawer/example-button.component.ts +12 -1
  14. package/src/hooks/query-param-bottom-drawer/example-component.ts +12 -5
  15. package/src/hooks/query-param-modal/example-button.component.ts +12 -1
  16. package/src/hooks/query-param-modal/example-component.ts +15 -4
  17. package/src/input-group/input-group-editable-example.component.ts +76 -0
  18. package/tsconfig.app.json +1 -0
  19. package/src/branding/branding.less +0 -78
  20. package/src/component-styles/leaner-style-sheets-example/leaner-style-sheets-example.component.less +0 -16
  21. package/src/component-styles/leaner-style-sheets-example/leaner-style-sheets-example.component.ts +0 -15
  22. package/src/component-styles/leaner-style-sheets-example/leaner-style-sheets-example.module.ts +0 -26
@@ -114,6 +114,13 @@ export default {
114
114
  description: 'An introduction to custom dashboards.',
115
115
  scope: 'self'
116
116
  },
117
+ {
118
+ name: 'Responsive layout dashboard module',
119
+ module: 'ResponsiveLayoutDashboardModule',
120
+ path: './src/dashboard/responsive-layout-dashboard/responsive-layout-dashboard.module.ts',
121
+ description: 'Demonstrates the viewport-filling responsive layout mode of c8y-dashboard.',
122
+ scope: 'self'
123
+ },
117
124
  {
118
125
  name: 'Dashboard module with example of dynamic component usage',
119
126
  module: 'WidgetGuideDashboardModule',
@@ -177,13 +184,6 @@ export default {
177
184
  description: 'A sample for component styles using CSS.',
178
185
  scope: 'self'
179
186
  },
180
- {
181
- name: 'LESS Component styles Example',
182
- module: 'LeanerStyleSheetsExampleModule',
183
- path: './src/component-styles/leaner-style-sheets-example/leaner-style-sheets-example.module.ts',
184
- description: 'A sample for component styles using LESS.',
185
- scope: 'self'
186
- },
187
187
  {
188
188
  name: 'SCSS Component styles Example',
189
189
  module: 'SyntacticallyAwesomeStyleSheetsExampleModule',
package/package.json CHANGED
@@ -1,25 +1,27 @@
1
1
  {
2
2
  "name": "@c8y/tutorial",
3
- "version": "1023.97.8",
3
+ "version": "1024.0.0",
4
4
  "description": "This package is used to scaffold a tutorial for Cumulocity IoT Web SDK which explains a lot of concepts.",
5
5
  "dependencies": {
6
- "@angular/animations": "^20.3.24",
7
- "@angular/cdk": "^20.2.14",
8
- "@c8y/bootstrap": "1023.97.8",
9
- "@c8y/client": "1023.97.8",
10
- "@c8y/ngx-components": "1023.97.8",
11
- "@c8y/style": "1023.97.8",
6
+ "@angular/animations": "^21.2.0",
7
+ "@angular/cdk": "^21.2.0",
8
+ "@c8y/bootstrap": "1024.0.0",
9
+ "@c8y/client": "1024.0.0",
10
+ "@c8y/ngx-components": "1024.0.0",
11
+ "@c8y/style": "1024.0.0",
12
+ "gridstack": "^12.4.2",
12
13
  "leaflet": "1.9.4",
13
14
  "monaco-editor": "~0.53.0",
14
- "ngx-bootstrap": "20.0.2",
15
- "rxjs": "7.8.2"
15
+ "ngx-bootstrap": "21.0.1",
16
+ "rxjs": "7.8.2",
17
+ "zone.js": "~0.15.1"
16
18
  },
17
19
  "devDependencies": {
18
- "@c8y/devkit": "1023.97.8",
19
- "@c8y/options": "1023.97.8"
20
+ "@c8y/devkit": "1024.0.0",
21
+ "@c8y/options": "1024.0.0"
20
22
  },
21
23
  "peerDependencies": {
22
- "@angular/common": ">=20 <21"
24
+ "@angular/common": ">=21 <22"
23
25
  },
24
26
  "author": "Cumulocity GmbH",
25
27
  "license": "Apache-2.0"
@@ -1,4 +1,4 @@
1
- import { ApplicationConfig, importProvidersFrom } from '@angular/core';
1
+ import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
2
2
  import { provideAnimations } from '@angular/platform-browser/animations';
3
3
  import { CoreModule, RouterModule, VersionModule } from '@c8y/ngx-components';
4
4
  import { provideHelloSample } from '../hello';
@@ -30,6 +30,7 @@ import { configureWidgetProviders } from '@c8y/ngx-components/widgets/widget-pro
30
30
 
31
31
  export const appConfig: ApplicationConfig = {
32
32
  providers: [
33
+ provideZoneChangeDetection(),
33
34
  provideAnimations(),
34
35
  importProvidersFrom(RouterModule.forRoot()),
35
36
  importProvidersFrom(CoreModule.forRoot()),
@@ -11,6 +11,17 @@
11
11
  {{ 'Add random' }}
12
12
  </button>
13
13
  </c8y-action-bar-item>
14
+ <c8y-action-bar-item [placement]="'right'">
15
+ <button
16
+ class="btn btn-link"
17
+ title="{{ 'Toggle layout' }}"
18
+ type="button"
19
+ (click)="layout = layout === 'grid' ? 'custom' : 'grid'"
20
+ >
21
+ <i [c8yIcon]="layout === 'grid' ? 'cogs' : 'th'"></i>
22
+ {{ layout === 'grid' ? 'Switch to custom layout' : 'Switch to grid layout' }}
23
+ </button>
24
+ </c8y-action-bar-item>
14
25
  <c8y-action-bar-item [placement]="'right'">
15
26
  <button
16
27
  class="btn btn-link"
@@ -24,33 +35,37 @@
24
35
 
25
36
  <c8y-help src="/docs/cockpit/working-with-dashboards/#working-with-dashboards"></c8y-help>
26
37
 
27
- <c8y-dashboard (dashboardChange)="dashboardChange($event)">
38
+ <c8y-dashboard
39
+ [layout]="layout"
40
+ (dashboardChange)="dashboardChange($event)"
41
+ >
28
42
  <!-- Simple widgets represented by widgets array property from CustomDashboardComponent class-->
29
- <c8y-dashboard-child
30
- [class]="{
31
- 'panel-title-hidden': true
32
- }"
33
- title="dynamic"
34
- [isFrozen]="isFrozen"
35
- *ngFor="let widget of widgets"
36
- [data]="widget"
37
- [width]="widget.width"
38
- [height]="widget.height"
39
- #current
40
- >
41
- <div class="card-block">
42
- Simple widget with content only
43
- <br />
44
- x: {{ current.x }}
45
- <br />
46
- y: {{ current.y }}
47
- <br />
48
- width: {{ current.width }}
49
- <br />
50
- height: {{ current.height }}
51
- <br />
52
- </div>
53
- </c8y-dashboard-child>
43
+ @for (widget of widgets; track widget) {
44
+ <c8y-dashboard-child
45
+ [class]="{
46
+ 'panel-title-hidden': true
47
+ }"
48
+ title="dynamic"
49
+ [isFrozen]="isFrozen"
50
+ [data]="widget"
51
+ [width]="widget.width"
52
+ [height]="widget.height"
53
+ #current
54
+ >
55
+ <div class="card-block">
56
+ Simple widget with content only
57
+ <br />
58
+ x: {{ current.x }}
59
+ <br />
60
+ y: {{ current.y }}
61
+ <br />
62
+ width: {{ current.width }}
63
+ <br />
64
+ height: {{ current.height }}
65
+ <br />
66
+ </div>
67
+ </c8y-dashboard-child>
68
+ }
54
69
 
55
70
  <!-- Dashboard child with title and widget classes applied-->
56
71
  <c8y-dashboard-child
@@ -129,9 +144,11 @@
129
144
  [width]="8"
130
145
  [height]="2"
131
146
  >
132
- <c8y-dashboard-child-title *ngIf="showTitle">
133
- <span>Widget title</span>
134
- </c8y-dashboard-child-title>
147
+ @if (showTitle) {
148
+ <c8y-dashboard-child-title>
149
+ <span>Widget title</span>
150
+ </c8y-dashboard-child-title>
151
+ }
135
152
  <c8y-dashboard-child-action>
136
153
  <a
137
154
  href=""
@@ -20,6 +20,7 @@ export class CustomDashboardComponent implements AfterViewInit, OnDestroy {
20
20
  isFrozen = false;
21
21
  showTitle = true;
22
22
  editComponent = false;
23
+ layout: 'grid' | 'custom' = 'grid';
23
24
 
24
25
  @ViewChild('configForm', { static: false })
25
26
  configForm: NgForm;
@@ -0,0 +1,99 @@
1
+ <c8y-title>Responsive layout dashboard</c8y-title>
2
+
3
+ <c8y-action-bar-item [placement]="'right'">
4
+ <button
5
+ class="btn btn-link"
6
+ title="{{ isFrozen ? 'Enable edit mode' : 'Disable edit mode' }}"
7
+ type="button"
8
+ (click)="toggleEdit()"
9
+ >
10
+ <i [c8yIcon]="isFrozen ? 'unlock' : 'lock'"></i>
11
+ {{ isFrozen ? 'Enable edit mode' : 'Disable edit mode' }}
12
+ </button>
13
+ </c8y-action-bar-item>
14
+
15
+ <!--
16
+ layout="custom" — enables the configurable layout mode.
17
+ alignViewHeight="true" — each row scales so all rows fill the viewport height.
18
+ [rows]="4" — the grid has 4 rows; each row is 25% of the viewport height.
19
+ [columns]="12" — 12-column grid; a widget with width=6 spans 50% of the width.
20
+
21
+ Enable edit mode above to see the size-preset picker on each widget.
22
+ -->
23
+ <c8y-dashboard
24
+ class="fit-h"
25
+ layout="custom"
26
+ [columns]="columns"
27
+ [rows]="rows"
28
+ [alignViewHeight]="true"
29
+ >
30
+ <!-- Row 1 — full-width banner -->
31
+ <c8y-dashboard-child
32
+ [class]="{ 'panel-title-hidden': true }"
33
+ [isFrozen]="isFrozen"
34
+ [x]="0"
35
+ [y]="0"
36
+ [width]="12"
37
+ [height]="1"
38
+ >
39
+ <div class="card-block d-flex a-i-center j-c-between">
40
+ <span>Overview — 100% width &times; 25% viewport height</span>
41
+ </div>
42
+ </c8y-dashboard-child>
43
+
44
+ <!-- Row 2 — two equal columns -->
45
+ <c8y-dashboard-child
46
+ [class]="{ 'panel-title-hidden': true }"
47
+ [isFrozen]="isFrozen"
48
+ [x]="0"
49
+ [y]="1"
50
+ [width]="6"
51
+ [height]="1"
52
+ >
53
+ <div class="card-block d-flex a-i-center j-c-center">50% width &times; 25% height</div>
54
+ </c8y-dashboard-child>
55
+
56
+ <c8y-dashboard-child
57
+ [class]="{ 'panel-title-hidden': true }"
58
+ [isFrozen]="isFrozen"
59
+ [x]="6"
60
+ [y]="1"
61
+ [width]="6"
62
+ [height]="1"
63
+ >
64
+ <div class="card-block d-flex a-i-center j-c-center">50% width &times; 25% height</div>
65
+ </c8y-dashboard-child>
66
+
67
+ <!-- Rows 3–4 — a wide chart alongside a narrow sidebar -->
68
+ <c8y-dashboard-child
69
+ [class]="{ 'panel-title-border': true }"
70
+ [isFrozen]="isFrozen"
71
+ [x]="0"
72
+ [y]="2"
73
+ [width]="8"
74
+ [height]="2"
75
+ >
76
+ <c8y-dashboard-child-title>
77
+ <span>Chart — 67% wide &times; 50% height</span>
78
+ </c8y-dashboard-child-title>
79
+ <div class="card-block">
80
+ Width: 8&nbsp;of&nbsp;12 columns = 67% &nbsp;|&nbsp; Height: 2&nbsp;of&nbsp;4 rows = 50%
81
+ </div>
82
+ </c8y-dashboard-child>
83
+
84
+ <c8y-dashboard-child
85
+ [class]="{ 'panel-title-border': true }"
86
+ [isFrozen]="isFrozen"
87
+ [x]="8"
88
+ [y]="2"
89
+ [width]="4"
90
+ [height]="2"
91
+ >
92
+ <c8y-dashboard-child-title>
93
+ <span>Sidebar — 33% wide &times; 50% height</span>
94
+ </c8y-dashboard-child-title>
95
+ <div class="card-block">
96
+ Width: 4&nbsp;of&nbsp;12 columns = 33% &nbsp;|&nbsp; Height: 2&nbsp;of&nbsp;4 rows = 50%
97
+ </div>
98
+ </c8y-dashboard-child>
99
+ </c8y-dashboard>
@@ -0,0 +1,26 @@
1
+ import { Component } from '@angular/core';
2
+ import { CoreModule } from '@c8y/ngx-components';
3
+ import { CommonModule } from '@angular/common';
4
+
5
+ /**
6
+ * Demonstrates the "custom" layout mode of c8y-dashboard with alignViewHeight enabled.
7
+ *
8
+ * With rows=4 and alignViewHeight=true each row automatically scales to exactly
9
+ * one quarter of the visible viewport height, so the grid always fills the screen
10
+ * regardless of window size.
11
+ */
12
+ @Component({
13
+ selector: 'tut-responsive-layout-dashboard',
14
+ templateUrl: './responsive-layout-dashboard.component.html',
15
+ standalone: true,
16
+ imports: [CommonModule, CoreModule]
17
+ })
18
+ export class ResponsiveLayoutDashboardComponent {
19
+ isFrozen = true;
20
+ readonly rows = 4;
21
+ readonly columns = 12;
22
+
23
+ toggleEdit() {
24
+ this.isFrozen = !this.isFrozen;
25
+ }
26
+ }
@@ -0,0 +1,24 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { NavigatorNode, hookNavigator, hookRoute } from '@c8y/ngx-components';
3
+
4
+ @NgModule({
5
+ providers: [
6
+ hookRoute({
7
+ path: 'dashboards/responsive-layout',
8
+ loadComponent: () =>
9
+ import('./responsive-layout-dashboard.component').then(
10
+ m => m.ResponsiveLayoutDashboardComponent
11
+ )
12
+ }),
13
+ hookNavigator(
14
+ new NavigatorNode({
15
+ label: 'Responsive layout dashboard',
16
+ path: '/dashboards/responsive-layout',
17
+ icon: 'cogs',
18
+ priority: 2,
19
+ parent: 'Dashboards'
20
+ })
21
+ )
22
+ ]
23
+ })
24
+ export class ResponsiveLayoutDashboardModule {}
@@ -34,11 +34,11 @@ export class DeviceGridExampleConfigurationStrategy extends AbstractConfiguratio
34
34
  : null;
35
35
  }
36
36
 
37
- saveConfig$(config: GridConfig, ctx?: GridConfigContext): Observable<GridConfig> {
37
+ saveConfig$(config: GridConfig, ctx?: GridConfigContext): Observable<void> {
38
38
  const context = this.retrieveContext(ctx);
39
39
  if (context?.key) {
40
40
  localStorage.setItem(context.key, JSON.stringify(config));
41
41
  }
42
- return of(config);
42
+ return of(undefined);
43
43
  }
44
44
  }
@@ -1,5 +1,4 @@
1
1
  import { Injectable } from '@angular/core';
2
- import { transform } from 'lodash-es';
3
2
 
4
3
  import { IManagedObject, InventoryService, QueriesUtil } from '@c8y/client';
5
4
  import {
@@ -10,7 +9,7 @@ import {
10
9
  Pagination
11
10
  } from '@c8y/ngx-components';
12
11
 
13
- import { assign, get, identity } from 'lodash-es';
12
+ import { assign, get, identity, transform } from 'lodash-es';
14
13
  import { LastUpdatedDataGridColumn } from './last-updated-data-grid-column/last-updated.data-grid-column';
15
14
  import { TypeDataGridColumn } from './type-data-grid-column/type.data-grid-column';
16
15
 
@@ -31,7 +31,7 @@ export class VoidConfigurationStrategy extends AbstractConfigurationStrategy {
31
31
  return of(null);
32
32
  }
33
33
 
34
- saveConfig$(): Observable<GridConfig> {
35
- return of(null);
34
+ saveConfig$(): Observable<void> {
35
+ return of(undefined);
36
36
  }
37
37
  }
@@ -1,5 +1,4 @@
1
1
  import { Injectable } from '@angular/core';
2
- import { transform } from 'lodash-es';
3
2
 
4
3
  import { IManagedObject, InventoryService, QueriesUtil } from '@c8y/client';
5
4
  import {
@@ -10,7 +9,7 @@ import {
10
9
  Pagination
11
10
  } from '@c8y/ngx-components';
12
11
 
13
- import { assign, get, identity } from 'lodash-es';
12
+ import { assign, get, identity, transform } from 'lodash-es';
14
13
 
15
14
  /** Model for custom type filtering form. */
16
15
  export interface TypeFilteringModel {
@@ -1,9 +1,20 @@
1
1
  import { Component } from '@angular/core';
2
2
  import { Router, ActivatedRoute } from '@angular/router';
3
+ import { CoreModule } from '@c8y/ngx-components';
3
4
 
4
5
  @Component({
5
6
  selector: 'app-example-button',
6
- template: ` <button (click)="addQueryParam()">Add Query Param</button> `
7
+ standalone: true,
8
+ imports: [CoreModule],
9
+ template: `
10
+ <c8y-title>Query Param Bottom Drawer</c8y-title>
11
+ <div class="card">
12
+ <div class="card-block">
13
+ <p>Click the button to add a query parameter. The bottom drawer will open automatically.</p>
14
+ <button class="btn btn-primary" (click)="addQueryParam()">Add query param</button>
15
+ </div>
16
+ </div>
17
+ `
7
18
  })
8
19
  export class ExampleButtonComponent {
9
20
  constructor(
@@ -3,12 +3,19 @@ import { ActivatedRoute, Router } from '@angular/router';
3
3
 
4
4
  @Component({
5
5
  selector: 'app-example-component',
6
- template: `<p>This component was rendered via <code>hookQueryParamBottomDrawer</code></p>
6
+ template: `
7
+ <div class="p-24">
8
+ <h4 class="m-b-16">Query Param Bottom Drawer</h4>
9
+ <p>This drawer was opened via <code>hookQueryParamBottomDrawer</code>.</p>
10
+ <p class="text-muted">
11
+ The URL contains <code>?showExampleComponent=true</code>. Closing this drawer will remove
12
+ the query parameter.
13
+ </p>
14
+ </div>
7
15
  <div class="text-center card-footer p-24 separator">
8
- <button class="btn btn-default" title="{{ 'Close' }}" type="button" (click)="close()">
9
- {{ 'Close' }}
10
- </button>
11
- </div>`
16
+ <button class="btn btn-default" type="button" (click)="close()">Close</button>
17
+ </div>
18
+ `
12
19
  })
13
20
  export class ExampleComponent {
14
21
  shouldClose: Promise<boolean>;
@@ -1,9 +1,20 @@
1
1
  import { Component } from '@angular/core';
2
2
  import { Router, ActivatedRoute } from '@angular/router';
3
+ import { CoreModule } from '@c8y/ngx-components';
3
4
 
4
5
  @Component({
5
6
  selector: 'app-example-button',
6
- template: ` <button (click)="addQueryParam()">Add Query Param</button> `
7
+ standalone: true,
8
+ imports: [CoreModule],
9
+ template: `
10
+ <c8y-title>Query Param Modal</c8y-title>
11
+ <div class="card">
12
+ <div class="card-block">
13
+ <p>Click the button to add a query parameter. The modal will open automatically.</p>
14
+ <button class="btn btn-primary" (click)="addQueryParam()">Add query param</button>
15
+ </div>
16
+ </div>
17
+ `
7
18
  })
8
19
  export class ExampleButtonComponent {
9
20
  constructor(
@@ -3,10 +3,21 @@ import { BsModalRef } from 'ngx-bootstrap/modal';
3
3
 
4
4
  @Component({
5
5
  selector: 'app-example-component',
6
- template: `<p>This component was rendered via <code>hookQueryParamModal</code></p>
7
- <button class="btn btn-default" title="{{ 'Close' }}" type="button" (click)="cancel()">
8
- {{ 'Close' }}
9
- </button>`
6
+ template: `
7
+ <div class="modal-header">
8
+ <h4 class="modal-title">Query Param Modal</h4>
9
+ </div>
10
+ <div class="modal-body">
11
+ <p>This modal was opened via <code>hookQueryParamModal</code>.</p>
12
+ <p class="text-muted">
13
+ The URL contains <code>?showExampleModalComponent=true</code>. Closing this modal will
14
+ remove the query parameter.
15
+ </p>
16
+ </div>
17
+ <div class="modal-footer">
18
+ <button class="btn btn-default" type="button" (click)="cancel()">Close</button>
19
+ </div>
20
+ `
10
21
  })
11
22
  export class ExampleComponent {
12
23
  private readonly bsModalRef = inject(BsModalRef);
@@ -199,6 +199,73 @@ import { Observable, map, timer } from 'rxjs';
199
199
  ></c8y-input-group-editable>
200
200
  </div>
201
201
  </div>
202
+
203
+ <hr />
204
+
205
+ <!-- MULTILINE -->
206
+ <h4 class="text-medium m-b-24">Multiline</h4>
207
+ <div class="row m-b-24">
208
+ <div class="col-sm-4">
209
+ <p class="text-label-small">Auto-grow (no max-height)</p>
210
+ <c8y-input-group-editable
211
+ [multiline]="true"
212
+ ariaLabel="Notes"
213
+ [(ngModel)]="multilineModel"
214
+ ></c8y-input-group-editable>
215
+ <p class="text-muted m-t-4">
216
+ <small>Use <kbd>Ctrl+Enter</kbd> to save, <kbd>Escape</kbd> to cancel.</small>
217
+ </p>
218
+ </div>
219
+ <div class="col-sm-4">
220
+ <p class="text-label-small">With maxHeight="150px"</p>
221
+ <c8y-input-group-editable
222
+ [multiline]="true"
223
+ maxHeight="150px"
224
+ ariaLabel="Description with max height"
225
+ [(ngModel)]="multilineMaxHeightModel"
226
+ ></c8y-input-group-editable>
227
+ <p class="text-muted m-t-4">
228
+ <small>Scrolls once content exceeds 150 px.</small>
229
+ </p>
230
+ </div>
231
+ </div>
232
+
233
+ <hr />
234
+
235
+ <!-- FORM-OWNED SAVE (noSaveButton) -->
236
+ <h4 class="text-medium m-b-24">Form-owned save</h4>
237
+ <div class="row m-b-24">
238
+ <div class="col-sm-4">
239
+ <p class="text-label-small">Single-line ([noSaveButton]="true")</p>
240
+ <c8y-input-group-editable
241
+ placeholder="e.g. My app"
242
+ [noSaveButton]="true"
243
+ formGroupClass="m-b-0"
244
+ ariaLabel="Application name"
245
+ [(ngModel)]="noSaveButtonSingle"
246
+ ></c8y-input-group-editable>
247
+ <small class="text-muted">Value: {{ noSaveButtonSingle }}</small>
248
+ <p class="text-muted m-t-4">
249
+ <small
250
+ >Buttons hidden. Value updates on every keystroke. <kbd>Escape</kbd> reverts.</small
251
+ >
252
+ </p>
253
+ </div>
254
+ <div class="col-sm-4">
255
+ <p class="text-label-small">Multiline ([noSaveButton]="true")</p>
256
+ <c8y-input-group-editable
257
+ placeholder="e.g. Describe this item"
258
+ [multiline]="true"
259
+ [noSaveButton]="true"
260
+ formGroupClass="m-b-0"
261
+ ariaLabel="Description"
262
+ [(ngModel)]="noSaveButtonMulti"
263
+ ></c8y-input-group-editable>
264
+ <p class="text-muted m-t-4">
265
+ <small>Use <kbd>Escape</kbd> to revert.</small>
266
+ </p>
267
+ </div>
268
+ </div>
202
269
  </div>
203
270
  `,
204
271
  standalone: true,
@@ -238,6 +305,15 @@ export class InputGroupEditableExampleComponent {
238
305
  // Disabled state
239
306
  disabledModel = 'Read-only value';
240
307
 
308
+ // Multiline
309
+ multilineModel = 'Edit this multiline value';
310
+ multilineMaxHeightModel =
311
+ 'Edit this value\nAdd more lines to see scrolling kick in once the height cap is reached';
312
+
313
+ // Form-owned save (noSaveButton)
314
+ noSaveButtonSingle = 'My Application';
315
+ noSaveButtonMulti = 'Edit me — no Save button.\nEscape to revert.';
316
+
241
317
  // Async validation — simulates a server-side uniqueness check
242
318
  readonly asyncFormControl = new FormControl('available', { nonNullable: true });
243
319
 
package/tsconfig.app.json CHANGED
@@ -16,6 +16,7 @@
16
16
  "../ngx-components/**/*.ts"
17
17
  ],
18
18
  "exclude": [
19
+ "src/**/*.spec.ts",
19
20
  "../ngx-components/dist",
20
21
  "../ngx-components/**/*.spec.ts",
21
22
  ]
@@ -1,78 +0,0 @@
1
- @import '~@c8y/style/extend.less';
2
-
3
- // // to hide the Cumulocity IoT logo on top of navigator
4
- // // set @extending to true
5
- @extending: false;
6
-
7
- // Replace and uncomment each variable as you need them
8
-
9
- /* COLORS */
10
- // @brand-color: #53cd61; // main color
11
- // @brand-primary: @brand-color;
12
- // @brand-complementary: #a8b3b5;
13
- // @brand-primary-light: lighten(@brand-primary, 20%);
14
- // // status colors
15
- // @status-success: #5cb85c;
16
- // @status-info: @brand-color;
17
- // @status-warning: #f0ad4e;
18
- // @status-danger: #d9534f;
19
- // @danger: #d90000;
20
- // @warning: #fdc000;
21
- // @status-warning-dark: #ff8000;
22
- // @success: #5cb85c;
23
- // grays
24
- // @text-color: #444;
25
- // @gray-30: #3b4748;
26
- // @gray-40: #49595b;
27
- // @gray-40: #6d7a7c;
28
- // // @gray: #8a9596;
29
- // @gray-80: #cacece;
30
- // @gray-90: #f8f8f8;
31
- // @gray-100: #fcfcfc;
32
- // @text-muted: @gray-50;
33
-
34
- // @body-background-color: #f8f8f8; // page background color - always use a light background
35
-
36
- /* HEADER */
37
- // @headerColor: white;
38
- // @header-text-color: @gray-40;
39
- // @header-text-color-hover: @brand-primary;
40
- // @header-color-active: darken(@gray-40, 15%);
41
-
42
- /* NAVIGATOR */
43
- // @navColor: @gray-10;
44
- // @navColorHeader: @gray-10;
45
- // @navigator-header-color: white;
46
- // @navigator-text-color: @gray-100;
47
- // @navigator-separator-color: fade(white, 5%);
48
- // @navigator-font-family: @headings-font-family;
49
- // @navigator-font-size: 13px;
50
- // @navigator-color-active: white;
51
- // @navigator-active-bg: @brand-primary;
52
- // when set adds a vertical gradient in the navigator background
53
- // @grad-top: "";
54
- // @grad-bottom: "";
55
-
56
- /* TYPOGRAPHY */
57
- // @font-family-sans-serif: "Lato",Arial, Verdana, sans-serif;
58
- // @font-family-base: @font-family-sans-serif;
59
- // @headings-font-family: "Roboto",Arial, Verdana, sans-serif;
60
-
61
- /* BUTTONS */
62
- // @component-border-radius-base-base: 2px;
63
- // @component-border-radius-base-large: @component-border-radius-base-base;
64
- // @component-border-radius-base-small: @component-border-radius-base-base;
65
- // @btn-shadow: none;
66
-
67
- /* COMPONENTS */
68
- // @spinner-color: lighten(@brand-primary, 30%);
69
- // @link-color: #337ab7;
70
- // @link-color-hover: darken(@link-color, 15%);
71
- // @form-control-border-color-focus: #66afe9;
72
-
73
- // @body-background-pattern: "";
74
- // @darker-header: @gray-30;
75
- // @appswitcher-background: none;
76
- // @component-background-hover: fade(black, 1.5%);
77
- // @header-app-name: @header-text-color;
78
- // @image-path: 'img/';
@@ -1,16 +0,0 @@
1
- @background-color: #e6f7ff;
2
- @border-color: #91d5ff;
3
-
4
- .less-example {
5
- background-color: @background-color;
6
- padding: 15px;
7
- border: 1px solid @border-color;
8
-
9
- h5 {
10
- color: darken(@border-color, 30%);
11
- }
12
-
13
- p {
14
- color: darken(@background-color, 50%);
15
- }
16
- }
@@ -1,15 +0,0 @@
1
- import { Component } from '@angular/core';
2
- import { HeaderModule } from '@c8y/ngx-components';
3
-
4
- @Component({
5
- selector: 'tut-leaner-style-sheets-example',
6
- template: `<c8y-title>LESS component styles</c8y-title>
7
- <div class="less-example">
8
- <h5>LESS Styles</h5>
9
- <p>This is styled with LESS</p>
10
- </div>`,
11
- styleUrls: ['./leaner-style-sheets-example.component.less'],
12
- standalone: true,
13
- imports: [HeaderModule]
14
- })
15
- export class LeanerStyleSheetsExampleComponent {}
@@ -1,26 +0,0 @@
1
- import { NgModule } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { NavigatorNode, hookNavigator, hookRoute } from '@c8y/ngx-components';
4
-
5
- @NgModule({
6
- imports: [CommonModule],
7
- providers: [
8
- hookRoute({
9
- path: 'component-styles/less',
10
- loadComponent: () =>
11
- import('./leaner-style-sheets-example.component').then(
12
- m => m.LeanerStyleSheetsExampleComponent
13
- )
14
- }),
15
- hookNavigator(
16
- new NavigatorNode({
17
- path: '/component-styles/less',
18
- label: 'LESS',
19
- icon: 'c8y-css',
20
- priority: 79,
21
- parent: 'Component styles'
22
- })
23
- )
24
- ]
25
- })
26
- export class LeanerStyleSheetsExampleModule {}