@dragonworks/ngx-dashboard-widgets 20.0.5 → 20.0.6

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 (34) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +31 -42
  3. package/src/lib/arrow-widget/arrow-state-dialog.component.ts +187 -0
  4. package/src/lib/arrow-widget/arrow-widget.component.html +9 -0
  5. package/src/lib/arrow-widget/arrow-widget.component.scss +52 -0
  6. package/src/lib/arrow-widget/arrow-widget.component.ts +78 -0
  7. package/src/lib/arrow-widget/arrow-widget.metadata.ts +3 -0
  8. package/src/lib/clock-widget/analog-clock/analog-clock.component.html +66 -0
  9. package/src/lib/clock-widget/analog-clock/analog-clock.component.scss +103 -0
  10. package/src/lib/clock-widget/analog-clock/analog-clock.component.ts +120 -0
  11. package/src/lib/clock-widget/clock-state-dialog.component.ts +170 -0
  12. package/src/lib/clock-widget/clock-widget.component.html +16 -0
  13. package/src/lib/clock-widget/clock-widget.component.scss +160 -0
  14. package/src/lib/clock-widget/clock-widget.component.ts +87 -0
  15. package/src/lib/clock-widget/clock-widget.metadata.ts +42 -0
  16. package/src/lib/clock-widget/digital-clock/__tests__/digital-clock.component.spec.ts +276 -0
  17. package/src/lib/clock-widget/digital-clock/digital-clock.component.html +1 -0
  18. package/src/lib/clock-widget/digital-clock/digital-clock.component.scss +43 -0
  19. package/src/lib/clock-widget/digital-clock/digital-clock.component.ts +105 -0
  20. package/src/lib/directives/__tests__/responsive-text.directive.spec.ts +906 -0
  21. package/src/lib/directives/responsive-text.directive.ts +334 -0
  22. package/src/lib/label-widget/__tests__/label-widget.component.spec.ts +539 -0
  23. package/src/lib/label-widget/label-state-dialog.component.ts +385 -0
  24. package/src/lib/label-widget/label-widget.component.html +21 -0
  25. package/src/lib/label-widget/label-widget.component.scss +112 -0
  26. package/src/lib/label-widget/label-widget.component.ts +96 -0
  27. package/src/lib/label-widget/label-widget.metadata.ts +3 -0
  28. package/src/public-api.ts +7 -0
  29. package/tsconfig.lib.json +15 -0
  30. package/tsconfig.lib.prod.json +11 -0
  31. package/tsconfig.spec.json +14 -0
  32. package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs +0 -1428
  33. package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs.map +0 -1
  34. package/index.d.ts +0 -151
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/ngx-dashboard-widgets",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,42 +1,31 @@
1
- {
2
- "name": "@dragonworks/ngx-dashboard-widgets",
3
- "version": "20.0.5",
4
- "description": "Widget collection for ngx-dashboard with Material Design 3 compliance including arrow, label, clock widgets and responsive text directive",
5
- "peerDependencies": {
6
- "@angular/common": "^20.0.0",
7
- "@angular/core": "^20.0.0",
8
- "@dragonworks/ngx-dashboard": "^20.0.0"
9
- },
10
- "dependencies": {
11
- "tslib": "^2.3.0"
12
- },
13
- "sideEffects": false,
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/TobyBackstrom/ngx-dashboard.git"
17
- },
18
- "author": "Toby Backstrom",
19
- "license": "MIT",
20
- "bugs": {
21
- "url": "https://github.com/TobyBackstrom/ngx-dashboard/issues"
22
- },
23
- "homepage": "https://dragonworks.dev",
24
- "keywords": [
25
- "angular",
26
- "dashboard",
27
- "widgets",
28
- "material-design",
29
- "md3"
30
- ],
31
- "module": "fesm2022/dragonworks-ngx-dashboard-widgets.mjs",
32
- "typings": "index.d.ts",
33
- "exports": {
34
- "./package.json": {
35
- "default": "./package.json"
36
- },
37
- ".": {
38
- "types": "./index.d.ts",
39
- "default": "./fesm2022/dragonworks-ngx-dashboard-widgets.mjs"
40
- }
41
- }
42
- }
1
+ {
2
+ "name": "@dragonworks/ngx-dashboard-widgets",
3
+ "version": "20.0.6",
4
+ "description": "Widget collection for ngx-dashboard with Material Design 3 compliance including arrow, label, clock widgets and responsive text directive",
5
+ "peerDependencies": {
6
+ "@angular/common": "^20.0.0",
7
+ "@angular/core": "^20.0.0",
8
+ "@dragonworks/ngx-dashboard": "^20.0.0"
9
+ },
10
+ "dependencies": {
11
+ "tslib": "^2.3.0"
12
+ },
13
+ "sideEffects": false,
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/TobyBackstrom/ngx-dashboard.git"
17
+ },
18
+ "author": "Toby Backstrom",
19
+ "license": "MIT",
20
+ "bugs": {
21
+ "url": "https://github.com/TobyBackstrom/ngx-dashboard/issues"
22
+ },
23
+ "homepage": "https://dragonworks.dev",
24
+ "keywords": [
25
+ "angular",
26
+ "dashboard",
27
+ "widgets",
28
+ "material-design",
29
+ "md3"
30
+ ]
31
+ }
@@ -0,0 +1,187 @@
1
+ import { Component, inject, signal, computed } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import {
5
+ MAT_DIALOG_DATA,
6
+ MatDialogRef,
7
+ MatDialogModule,
8
+ } from '@angular/material/dialog';
9
+ import { MatButtonModule } from '@angular/material/button';
10
+ import { ArrowWidgetState } from './arrow-widget.component';
11
+ import { MatFormFieldModule } from '@angular/material/form-field';
12
+ import { MatSelectModule } from '@angular/material/select';
13
+ import { MatSliderModule } from '@angular/material/slider';
14
+ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
15
+
16
+ @Component({
17
+ selector: 'lib-arrow-state-dialog',
18
+ standalone: true,
19
+ imports: [
20
+ CommonModule,
21
+ FormsModule,
22
+ MatDialogModule,
23
+ MatButtonModule,
24
+ MatFormFieldModule,
25
+ MatSelectModule,
26
+ MatSliderModule,
27
+ MatSlideToggleModule,
28
+ ],
29
+ template: `
30
+ <h2 mat-dialog-title>Arrow Settings</h2>
31
+ <mat-dialog-content>
32
+ <!-- Direction Selection -->
33
+ <mat-form-field appearance="outline" class="direction-field">
34
+ <mat-label>Arrow Direction</mat-label>
35
+ <mat-select
36
+ [value]="direction()"
37
+ (selectionChange)="direction.set($any($event.value))"
38
+ >
39
+ <mat-option value="up">Up</mat-option>
40
+ <mat-option value="right">Right</mat-option>
41
+ <mat-option value="down">Down</mat-option>
42
+ <mat-option value="left">Left</mat-option>
43
+ </mat-select>
44
+ </mat-form-field>
45
+
46
+ <!-- Opacity Slider -->
47
+ <div class="slider-field">
48
+ <div class="field-label">Opacity: {{ formatOpacity(opacity()) }}%</div>
49
+ <mat-slider [min]="0.1" [max]="1" [step]="0.1">
50
+ <input matSliderThumb [(ngModel)]="opacity" />
51
+ </mat-slider>
52
+ </div>
53
+
54
+ <!-- Background Toggle -->
55
+ <div class="toggle-field">
56
+ <mat-slide-toggle
57
+ [checked]="hasBackground()"
58
+ (change)="onBackgroundToggle($event.checked)">
59
+ Background
60
+ </mat-slide-toggle>
61
+ <span class="toggle-hint">Adds a background behind the arrow</span>
62
+ </div>
63
+ </mat-dialog-content>
64
+
65
+ <mat-dialog-actions align="end">
66
+ <button mat-button (click)="onCancel()">Cancel</button>
67
+ <button mat-flat-button (click)="save()" [disabled]="!hasChanged()">
68
+ Save
69
+ </button>
70
+ </mat-dialog-actions>
71
+ `,
72
+ styles: [
73
+ `
74
+ mat-dialog-content {
75
+ display: block;
76
+ overflow-y: auto;
77
+ overflow-x: hidden;
78
+ }
79
+
80
+ mat-form-field {
81
+ width: 100%;
82
+ display: block;
83
+ margin-bottom: 1rem;
84
+ }
85
+
86
+ .direction-field {
87
+ margin-top: 1rem;
88
+ }
89
+
90
+ /* Opacity slider section */
91
+ .slider-field {
92
+ margin-bottom: 1.5rem;
93
+ margin-right: 1rem;
94
+ }
95
+
96
+ .field-label {
97
+ display: block;
98
+ margin-bottom: 0.5rem;
99
+ }
100
+
101
+ mat-slider {
102
+ width: 100%;
103
+ display: block;
104
+ }
105
+
106
+ /* Toggle section */
107
+ .toggle-field {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 0.75rem;
111
+ margin-bottom: 0.5rem;
112
+ }
113
+
114
+ .toggle-hint {
115
+ margin: 0;
116
+ }
117
+ `,
118
+ ],
119
+ })
120
+ export class ArrowStateDialogComponent {
121
+ private readonly data = inject<ArrowWidgetState>(MAT_DIALOG_DATA);
122
+ private readonly dialogRef = inject(MatDialogRef<ArrowStateDialogComponent>);
123
+
124
+ // State signals
125
+ readonly direction = signal<'left' | 'up' | 'right' | 'down'>(
126
+ this.data.direction
127
+ );
128
+ readonly opacity = signal<number>(this.data.opacity ?? 1);
129
+ readonly hasBackground = signal<boolean>(this.data.hasBackground ?? true);
130
+ readonly transparentBackground = signal<boolean>(!(this.data.hasBackground ?? true));
131
+
132
+ // Store original values for comparison
133
+ private readonly originalDirection = this.data.direction;
134
+ private readonly originalOpacity = this.data.opacity ?? 1;
135
+ private readonly originalHasBackground = this.data.hasBackground ?? true;
136
+
137
+ // Computed values
138
+ readonly rotation = computed(() => {
139
+ const rotationMap = {
140
+ up: 0,
141
+ right: 90,
142
+ down: 180,
143
+ left: 270,
144
+ };
145
+ return rotationMap[this.direction()];
146
+ });
147
+
148
+ readonly rotationTransform = computed(() => `rotate(${this.rotation()}deg)`);
149
+
150
+ readonly directionName = computed(() => {
151
+ const nameMap = {
152
+ up: 'Up',
153
+ right: 'Right',
154
+ down: 'Down',
155
+ left: 'Left',
156
+ };
157
+ return nameMap[this.direction()];
158
+ });
159
+
160
+ readonly hasChanged = computed(
161
+ () =>
162
+ this.direction() !== this.originalDirection ||
163
+ this.opacity() !== this.originalOpacity ||
164
+ this.hasBackground() !== this.originalHasBackground
165
+ );
166
+
167
+ formatOpacity(value: number): number {
168
+ return Math.round(value * 100);
169
+ }
170
+
171
+ onBackgroundToggle(hasBackground: boolean): void {
172
+ this.hasBackground.set(hasBackground);
173
+ this.transparentBackground.set(!hasBackground);
174
+ }
175
+
176
+ onCancel(): void {
177
+ this.dialogRef.close();
178
+ }
179
+
180
+ save(): void {
181
+ this.dialogRef.close({
182
+ direction: this.direction(),
183
+ opacity: this.opacity(),
184
+ hasBackground: this.hasBackground(),
185
+ } as ArrowWidgetState);
186
+ }
187
+ }
@@ -0,0 +1,9 @@
1
+ <!-- arrow-widget.component.html -->
2
+ <div class="svg-wrapper" [class.has-background]="state().hasBackground">
3
+ <div
4
+ class="svg-placeholder"
5
+ [innerHTML]="safeSvgIcon"
6
+ [style.transform]="'rotate(' + rotationAngle() + 'deg)'"
7
+ [style.opacity]="state().opacity"
8
+ ></div>
9
+ </div>
@@ -0,0 +1,52 @@
1
+ // arrow-widget.component.scss
2
+ :host {
3
+ display: block;
4
+ container-type: size;
5
+ width: 100%;
6
+ height: 100%;
7
+ overflow: hidden;
8
+ }
9
+
10
+ .svg-wrapper {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ height: 100%;
15
+ width: 100%;
16
+ box-sizing: border-box;
17
+ transition: background-color var(--mat-sys-motion-duration-medium2)
18
+ var(--mat-sys-motion-easing-standard);
19
+
20
+ // Add background color when enabled
21
+ &.has-background {
22
+ background-color: var(--mat-sys-surface-container-high);
23
+ border-radius: 4px;
24
+ }
25
+ }
26
+
27
+ .svg-placeholder {
28
+ width: min(80cqw, 80cqh);
29
+ aspect-ratio: 1 / 1;
30
+ opacity: 0.3;
31
+ transition: transform 0.3s ease-in-out, opacity 0.3s ease;
32
+ transform-origin: center center;
33
+
34
+ // SVG styling
35
+ ::ng-deep svg {
36
+ width: 100%;
37
+ height: 100%;
38
+ display: block;
39
+ fill: var(--mat-sys-on-surface-variant, #6c757d);
40
+ transition: fill 0.2s ease;
41
+ }
42
+
43
+ // Different color when parent has background
44
+ .has-background & ::ng-deep svg {
45
+ fill: var(--mat-sys-on-surface, #1f1f1f);
46
+ }
47
+ }
48
+
49
+ // Hover effect
50
+ .svg-wrapper:hover .svg-placeholder ::ng-deep svg {
51
+ fill: var(--mat-sys-primary, #6750a4);
52
+ }
@@ -0,0 +1,78 @@
1
+ // arrow-widget.component.ts
2
+ import { Component, inject, signal, computed } from '@angular/core';
3
+ import { Widget, WidgetMetadata } from '@dragonworks/ngx-dashboard';
4
+ import { DomSanitizer } from '@angular/platform-browser';
5
+ import { MatDialog } from '@angular/material/dialog';
6
+ import { svgIcon } from './arrow-widget.metadata';
7
+ import { ArrowStateDialogComponent } from './arrow-state-dialog.component';
8
+
9
+ export interface ArrowWidgetState {
10
+ direction: 'left' | 'up' | 'right' | 'down';
11
+ opacity?: number;
12
+ hasBackground?: boolean;
13
+ }
14
+
15
+ @Component({
16
+ selector: 'ngx-dashboard-arrow-widget',
17
+ imports: [],
18
+ templateUrl: './arrow-widget.component.html',
19
+ styleUrl: './arrow-widget.component.scss',
20
+ })
21
+ export class ArrowWidgetComponent implements Widget {
22
+ static metadata: WidgetMetadata = {
23
+ widgetTypeid: '@default/arrow-widget',
24
+ name: 'Arrow',
25
+ description: 'A generic arrow',
26
+ svgIcon,
27
+ };
28
+
29
+ readonly #sanitizer = inject(DomSanitizer);
30
+ readonly #dialog = inject(MatDialog);
31
+
32
+ readonly safeSvgIcon = this.#sanitizer.bypassSecurityTrustHtml(svgIcon);
33
+
34
+ readonly state = signal<ArrowWidgetState>({
35
+ direction: 'up',
36
+ opacity: 0.3,
37
+ hasBackground: true,
38
+ });
39
+ // Computed rotation
40
+ readonly rotationAngle = computed(() => {
41
+ const rotationMap = {
42
+ up: 0,
43
+ right: 90,
44
+ down: 180,
45
+ left: 270,
46
+ };
47
+ return rotationMap[this.state().direction];
48
+ });
49
+
50
+ dashboardSetState(state?: unknown): void {
51
+ if (state) {
52
+ this.state.update((current) => ({
53
+ ...current,
54
+ ...(state as ArrowWidgetState),
55
+ }));
56
+ }
57
+ }
58
+
59
+ dashboardGetState(): ArrowWidgetState {
60
+ return this.state();
61
+ }
62
+
63
+ dashboardEditState(): void {
64
+ const dialogRef = this.#dialog.open(ArrowStateDialogComponent, {
65
+ data: this.state(),
66
+ width: '400px',
67
+ maxWidth: '90vw',
68
+ disableClose: false,
69
+ autoFocus: false,
70
+ });
71
+
72
+ dialogRef.afterClosed().subscribe((result) => {
73
+ if (result) {
74
+ this.state.set(result);
75
+ }
76
+ });
77
+ }
78
+ }
@@ -0,0 +1,3 @@
1
+ // arrow-widget.metadata.ts
2
+ export const svgIcon =
3
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-120v-320H120l360-440 360 440H640v320H320Zm80-80h160v-320h111L480-754 289-520h111v320Zm80-320Z"/></svg>';
@@ -0,0 +1,66 @@
1
+ <div class="analog-clock-container">
2
+ <div class="aspect-ratio-box">
3
+ <svg
4
+ version="1.1"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ viewBox="0 0 800 800"
7
+ preserveAspectRatio="xMidYMid meet"
8
+ >
9
+ <!-- Optional face circle; uncomment if you want a visible outline by default -->
10
+ <!-- <circle cx="400" cy="400" r="400" fill="transparent" stroke="currentColor" stroke-width="2" /> -->
11
+
12
+ <use transform="matrix(-1,0,0,1,800,0)" href="#one-half" />
13
+ <g id="one-half">
14
+ <g id="one-fourth">
15
+ <!-- 12 / 3 / 6 / 9 heavy marks -->
16
+ <path d="m400 40v107" stroke-width="26.7" stroke="currentColor" />
17
+ <g id="one-twelfth">
18
+ <!-- 30° heavy marks -->
19
+ <path
20
+ d="m580 88.233-42.5 73.612"
21
+ stroke-width="26.7"
22
+ stroke="currentColor"
23
+ />
24
+ <g id="one-thirtieth">
25
+ <!-- minute/second ticks -->
26
+ <path
27
+ id="one-sixtieth"
28
+ d="m437.63 41.974-3.6585 34.808"
29
+ stroke-width="13.6"
30
+ stroke="currentColor"
31
+ />
32
+ <use transform="rotate(6 400 400)" href="#one-sixtieth" />
33
+ </g>
34
+ <use transform="rotate(12 400 400)" href="#one-thirtieth" />
35
+ </g>
36
+ <use transform="rotate(30 400 400)" href="#one-twelfth" />
37
+ <use transform="rotate(60 400 400)" href="#one-twelfth" />
38
+ </g>
39
+ <use transform="rotate(90 400 400)" href="#one-fourth" />
40
+ </g>
41
+
42
+ <!-- Hands -->
43
+ <path
44
+ class="clock-hour-hand"
45
+ id="anim-clock-hour-hand"
46
+ #hourHand
47
+ d="m 381.925,476 h 36.15 l 5e-4,-300.03008 L 400,156.25 381.9245,175.96992 Z"
48
+ transform="rotate(110.2650694444, 400, 400)"
49
+ />
50
+ <path
51
+ class="clock-minute-hand"
52
+ id="anim-clock-minute-hand"
53
+ #minuteHand
54
+ d="M 412.063,496.87456 H 387.937 L 385.249,65.68306 400,52.75 414.751,65.68306 Z"
55
+ transform="rotate(243.1808333333, 400, 400)"
56
+ />
57
+ <path
58
+ class="clock-second-hand"
59
+ id="anim-clock-second-hand"
60
+ #secondHand
61
+ d="M 397.317,63.51744 395.91962,168.4 C 374.575,170.5125 358.2,188.365 358.2,210 c 0,21.635 16.3,39 36.61214,41.47594 L 391.52847,498 h 16.94306 L 405.1868,251.47593 C 425.5,249 441.8,231.635 441.8,210 c 2e-5,-21.635 -16.375,-39.4875 -37.71971,-41.6 L 402.683,63.51744 400,60 Z M 400,190.534 c 10.888,0 19.466,8.866 19.466,19.466 0,10.6 -8.578,19.466 -19.466,19.466 -10.888,0 -19.466,-8.866 -19.466,-19.466 0,-10.6 8.578,-19.466 19.466,-19.466 z"
62
+ transform="rotate(190.85, 400, 400)"
63
+ />
64
+ </svg>
65
+ </div>
66
+ </div>
@@ -0,0 +1,103 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ height: 100%;
5
+ }
6
+
7
+ .analog-clock-container {
8
+ width: 100%;
9
+ height: 100%;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+
14
+ // Modern aspect ratio approach
15
+ .aspect-ratio-box {
16
+ position: relative;
17
+ width: 100%;
18
+ max-width: 100%;
19
+ max-height: 100%;
20
+ aspect-ratio: 1 / 1;
21
+
22
+ // Fallback for browsers without aspect-ratio support
23
+ @supports not (aspect-ratio: 1 / 1) {
24
+ &::before {
25
+ content: "";
26
+ display: block;
27
+ padding-bottom: 100%;
28
+ }
29
+
30
+ svg {
31
+ position: absolute;
32
+ top: 0;
33
+ left: 0;
34
+ width: 100%;
35
+ height: 100%;
36
+ }
37
+ }
38
+
39
+ svg {
40
+ display: block;
41
+ width: 100%;
42
+ height: 100%;
43
+
44
+ // Clock face styling
45
+ // circle {
46
+ // fill: transparent;
47
+ // stroke: var(--mat-sys-outline, #79747e);
48
+ // stroke-width: 2;
49
+ // }
50
+
51
+ // Hour markers and tick marks
52
+ path:not(.clock-hour-hand):not(.clock-minute-hand):not(
53
+ .clock-second-hand
54
+ ) {
55
+ stroke: var(--mat-sys-on-surface, #1d1b20);
56
+ }
57
+
58
+ // Clock hands
59
+ .clock-hour-hand {
60
+ fill: var(--mat-sys-on-surface, #1d1b20);
61
+ }
62
+
63
+ .clock-minute-hand {
64
+ fill: var(--mat-sys-on-surface, #1d1b20);
65
+ }
66
+
67
+ .clock-second-hand {
68
+ fill: var(--mat-sys-primary, #6750a4);
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ // Hide second hand when showSeconds is false
75
+ :host:not(.show-seconds) {
76
+ .clock-second-hand {
77
+ display: none;
78
+ }
79
+ }
80
+
81
+ // Background styling based on hasBackground setting
82
+ :host.has-background {
83
+ svg circle {
84
+ fill: var(--mat-sys-surface, #fffbfe);
85
+ }
86
+ }
87
+
88
+ // Hover effects
89
+ :host:hover {
90
+ opacity: 0.8;
91
+
92
+ svg {
93
+ .clock-hour-hand, .clock-minute-hand {
94
+ fill: var(--mat-sys-primary, #6750a4);
95
+ }
96
+ }
97
+ }
98
+
99
+ // Host class styling for consistency with digital clock
100
+ :host.clock-widget.analog {
101
+ container-type: size;
102
+ container-name: analog-clock;
103
+ }