@dragonworks/ngx-dashboard-widgets 20.0.4 → 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.
- package/ng-package.json +7 -0
- package/package.json +31 -42
- package/src/lib/arrow-widget/arrow-state-dialog.component.ts +187 -0
- package/src/lib/arrow-widget/arrow-widget.component.html +9 -0
- package/src/lib/arrow-widget/arrow-widget.component.scss +52 -0
- package/src/lib/arrow-widget/arrow-widget.component.ts +78 -0
- package/src/lib/arrow-widget/arrow-widget.metadata.ts +3 -0
- package/src/lib/clock-widget/analog-clock/analog-clock.component.html +66 -0
- package/src/lib/clock-widget/analog-clock/analog-clock.component.scss +103 -0
- package/src/lib/clock-widget/analog-clock/analog-clock.component.ts +120 -0
- package/src/lib/clock-widget/clock-state-dialog.component.ts +170 -0
- package/src/lib/clock-widget/clock-widget.component.html +16 -0
- package/src/lib/clock-widget/clock-widget.component.scss +160 -0
- package/src/lib/clock-widget/clock-widget.component.ts +87 -0
- package/src/lib/clock-widget/clock-widget.metadata.ts +42 -0
- package/src/lib/clock-widget/digital-clock/__tests__/digital-clock.component.spec.ts +276 -0
- package/src/lib/clock-widget/digital-clock/digital-clock.component.html +1 -0
- package/src/lib/clock-widget/digital-clock/digital-clock.component.scss +43 -0
- package/src/lib/clock-widget/digital-clock/digital-clock.component.ts +105 -0
- package/src/lib/directives/__tests__/responsive-text.directive.spec.ts +906 -0
- package/src/lib/directives/responsive-text.directive.ts +334 -0
- package/src/lib/label-widget/__tests__/label-widget.component.spec.ts +539 -0
- package/src/lib/label-widget/label-state-dialog.component.ts +385 -0
- package/src/lib/label-widget/label-widget.component.html +21 -0
- package/src/lib/label-widget/label-widget.component.scss +112 -0
- package/src/lib/label-widget/label-widget.component.ts +96 -0
- package/src/lib/label-widget/label-widget.metadata.ts +3 -0
- package/src/public-api.ts +7 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +14 -0
- package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs +0 -1255
- package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs.map +0 -1
- package/index.d.ts +0 -147
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,42 +1,31 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@dragonworks/ngx-dashboard-widgets",
|
|
3
|
-
"version": "20.0.
|
|
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://
|
|
24
|
-
"keywords": [
|
|
25
|
-
"angular",
|
|
26
|
-
"dashboard",
|
|
27
|
-
"widgets",
|
|
28
|
-
"material-design",
|
|
29
|
-
"md3"
|
|
30
|
-
]
|
|
31
|
-
|
|
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,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
|
+
}
|