@dragonworks/ngx-dashboard-widgets 20.0.6 → 20.1.1

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/fesm2022/dragonworks-ngx-dashboard-widgets.mjs +2251 -0
  2. package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs.map +1 -0
  3. package/index.d.ts +532 -0
  4. package/package.json +42 -31
  5. package/ng-package.json +0 -7
  6. package/src/lib/arrow-widget/arrow-state-dialog.component.ts +0 -187
  7. package/src/lib/arrow-widget/arrow-widget.component.html +0 -9
  8. package/src/lib/arrow-widget/arrow-widget.component.scss +0 -52
  9. package/src/lib/arrow-widget/arrow-widget.component.ts +0 -78
  10. package/src/lib/arrow-widget/arrow-widget.metadata.ts +0 -3
  11. package/src/lib/clock-widget/analog-clock/analog-clock.component.html +0 -66
  12. package/src/lib/clock-widget/analog-clock/analog-clock.component.scss +0 -103
  13. package/src/lib/clock-widget/analog-clock/analog-clock.component.ts +0 -120
  14. package/src/lib/clock-widget/clock-state-dialog.component.ts +0 -170
  15. package/src/lib/clock-widget/clock-widget.component.html +0 -16
  16. package/src/lib/clock-widget/clock-widget.component.scss +0 -160
  17. package/src/lib/clock-widget/clock-widget.component.ts +0 -87
  18. package/src/lib/clock-widget/clock-widget.metadata.ts +0 -42
  19. package/src/lib/clock-widget/digital-clock/__tests__/digital-clock.component.spec.ts +0 -276
  20. package/src/lib/clock-widget/digital-clock/digital-clock.component.html +0 -1
  21. package/src/lib/clock-widget/digital-clock/digital-clock.component.scss +0 -43
  22. package/src/lib/clock-widget/digital-clock/digital-clock.component.ts +0 -105
  23. package/src/lib/directives/__tests__/responsive-text.directive.spec.ts +0 -906
  24. package/src/lib/directives/responsive-text.directive.ts +0 -334
  25. package/src/lib/label-widget/__tests__/label-widget.component.spec.ts +0 -539
  26. package/src/lib/label-widget/label-state-dialog.component.ts +0 -385
  27. package/src/lib/label-widget/label-widget.component.html +0 -21
  28. package/src/lib/label-widget/label-widget.component.scss +0 -112
  29. package/src/lib/label-widget/label-widget.component.ts +0 -96
  30. package/src/lib/label-widget/label-widget.metadata.ts +0 -3
  31. package/src/public-api.ts +0 -7
  32. package/tsconfig.lib.json +0 -15
  33. package/tsconfig.lib.prod.json +0 -11
  34. package/tsconfig.spec.json +0 -14
@@ -1,334 +0,0 @@
1
- import {
2
- Directive,
3
- ElementRef,
4
- AfterViewInit,
5
- OnDestroy,
6
- inject,
7
- DestroyRef,
8
- numberAttribute,
9
- booleanAttribute,
10
- input,
11
- } from '@angular/core';
12
- import { NgZone, PLATFORM_ID } from '@angular/core';
13
- import { isPlatformBrowser } from '@angular/common';
14
-
15
- /**
16
- * Directive that automatically adjusts font size to fit text within its parent container.
17
- * Uses canvas-based measurement for performance and DOM verification for accuracy.
18
- *
19
- * @example
20
- * <div class="container">
21
- * <span responsiveText [minFontSize]="12" [maxFontSize]="72">Dynamic text here</span>
22
- * </div>
23
- */
24
- @Directive({
25
- selector: '[responsiveText]',
26
- standalone: true,
27
- host: {
28
- '[style.display]': '"block"',
29
- '[style.width]': '"100%"',
30
- '[style.white-space]': '"nowrap"',
31
- '[style.overflow]': '"visible"',
32
- },
33
- })
34
- export class ResponsiveTextDirective implements AfterViewInit, OnDestroy {
35
- /* ───────────────────────── Inputs with transforms ─────────────── */
36
- /** Minimum font-size in pixels (accessibility floor) */
37
- minFontSize = input(8, { transform: numberAttribute });
38
-
39
- /** Maximum font-size in pixels (layout ceiling) */
40
- maxFontSize = input(512, { transform: numberAttribute });
41
-
42
- /**
43
- * Line-height: pass a multiplier (e.g. 1.1) or absolute px value.
44
- * For single-line text a multiplier < 10 is treated as unitless.
45
- */
46
- lineHeight = input(1.1, { transform: numberAttribute });
47
-
48
- /** Whether to observe text mutations after first render */
49
- observeMutations = input(true, { transform: booleanAttribute });
50
-
51
- /** Debounce delay in ms for resize/mutation callbacks */
52
- debounceMs = input(16, { transform: numberAttribute });
53
-
54
- /* ───────────────────────── Private state ───────────────────────── */
55
- private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
56
- private readonly zone = inject(NgZone);
57
- private readonly platformId = inject(PLATFORM_ID);
58
- private readonly destroyRef = inject(DestroyRef);
59
-
60
- // Canvas context - lazy initialization
61
- private _ctx?: CanvasRenderingContext2D;
62
- private get ctx(): CanvasRenderingContext2D {
63
- if (!this._ctx) {
64
- const canvas = document.createElement('canvas');
65
- this._ctx = canvas.getContext('2d', {
66
- willReadFrequently: true,
67
- alpha: false,
68
- })!;
69
- }
70
- return this._ctx;
71
- }
72
-
73
- private ro?: ResizeObserver;
74
- private mo?: MutationObserver;
75
- private fitTimeout?: number;
76
-
77
- // Cache for performance
78
- private lastText = '';
79
- private lastMaxW = 0;
80
- private lastMaxH = 0;
81
- private lastFontSize = 0;
82
-
83
- /* ───────────────────────── Lifecycle ──────────────────────────── */
84
- ngAfterViewInit() {
85
- if (!isPlatformBrowser(this.platformId)) return;
86
-
87
- // Set initial styles
88
- const span = this.el.nativeElement;
89
- span.style.transition = 'font-size 0.1s ease-out';
90
-
91
- // All observer callbacks run outside Angular's zone
92
- this.zone.runOutsideAngular(() => {
93
- this.fit();
94
- this.observeResize();
95
- if (this.observeMutations()) {
96
- this.observeText();
97
- }
98
- });
99
- }
100
-
101
- ngOnDestroy() {
102
- this.cleanup();
103
- }
104
-
105
- /* ───────────────────── Core fitting logic ───────────────────── */
106
- /**
107
- * Debounced fit handler to prevent excessive recalculations
108
- */
109
- private requestFit = () => {
110
- if (this.fitTimeout) {
111
- cancelAnimationFrame(this.fitTimeout);
112
- }
113
-
114
- this.fitTimeout = requestAnimationFrame(() => {
115
- this.fit();
116
- });
117
- };
118
-
119
- /**
120
- * Recalculate & apply the ideal font-size
121
- */
122
- private fit = () => {
123
- const span = this.el.nativeElement;
124
- const parent = span.parentElement;
125
-
126
- if (!parent) return;
127
-
128
- const text = span.textContent?.trim() || '';
129
- if (!text) {
130
- span.style.fontSize = `${this.minFontSize()}px`;
131
- return;
132
- }
133
-
134
- const { maxW, maxH } = this.getAvailableSpace(parent);
135
-
136
- // Check cache to avoid redundant calculations
137
- if (
138
- text === this.lastText &&
139
- maxW === this.lastMaxW &&
140
- maxH === this.lastMaxH &&
141
- this.lastFontSize > 0
142
- ) {
143
- return;
144
- }
145
-
146
- // Calculate with conservative buffer for sub-pixel accuracy
147
- const ideal = this.calcFit(text, maxW * 0.98, maxH * 0.98);
148
-
149
- span.style.fontSize = `${ideal}px`;
150
-
151
- // DOM verification pass
152
- this.verifyFit(span, maxW, maxH, ideal);
153
-
154
- // Update cache
155
- this.lastText = text;
156
- this.lastMaxW = maxW;
157
- this.lastMaxH = maxH;
158
- this.lastFontSize = parseFloat(span.style.fontSize);
159
- };
160
-
161
- /**
162
- * Calculate available space accounting for padding and borders
163
- */
164
- private getAvailableSpace(parent: HTMLElement): {
165
- maxW: number;
166
- maxH: number;
167
- } {
168
- const cs = getComputedStyle(parent);
169
- const maxW =
170
- parent.clientWidth -
171
- parseFloat(cs.paddingLeft) -
172
- parseFloat(cs.paddingRight);
173
- const maxH =
174
- parent.clientHeight -
175
- parseFloat(cs.paddingTop) -
176
- parseFloat(cs.paddingBottom);
177
-
178
- return { maxW: Math.max(0, maxW), maxH: Math.max(0, maxH) };
179
- }
180
-
181
- /**
182
- * DOM-based verification to handle sub-pixel discrepancies
183
- */
184
- private verifyFit(
185
- span: HTMLElement,
186
- maxW: number,
187
- maxH: number,
188
- ideal: number
189
- ) {
190
- // Simple synchronous verification
191
- if (span.scrollWidth > maxW || span.scrollHeight > maxH) {
192
- let safe = ideal;
193
- let iterations = 0;
194
- const maxIterations = 10;
195
-
196
- while (
197
- iterations < maxIterations &&
198
- safe > this.minFontSize() &&
199
- (span.scrollWidth > maxW || span.scrollHeight > maxH)
200
- ) {
201
- safe -= 0.25;
202
- span.style.fontSize = `${safe}px`;
203
- iterations++;
204
- }
205
-
206
- // Update cache with verified size
207
- this.lastFontSize = safe;
208
- }
209
- }
210
-
211
- /* ───────────────────── Binary search algorithm ────────────────── */
212
- /**
213
- * Binary search for optimal font size using canvas measurements
214
- */
215
- private calcFit(
216
- text: string,
217
- maxW: number,
218
- maxH: number,
219
- precision = 0.1
220
- ): number {
221
- if (maxW <= 0 || maxH <= 0) return this.minFontSize();
222
-
223
- const computedStyle = getComputedStyle(this.el.nativeElement);
224
- const fontFamily = computedStyle.fontFamily || 'sans-serif';
225
- const fontWeight = computedStyle.fontWeight || '400';
226
-
227
- let lo = this.minFontSize();
228
- let hi = this.maxFontSize();
229
- let bestFit = this.minFontSize();
230
-
231
- while (hi - lo > precision) {
232
- const mid = (hi + lo) / 2;
233
- this.ctx.font = `${fontWeight} ${mid}px ${fontFamily}`;
234
-
235
- const metrics = this.ctx.measureText(text);
236
- const width = metrics.width;
237
-
238
- // Calculate height based on available metrics
239
- const height = this.calculateTextHeight(metrics, mid);
240
-
241
- if (width <= maxW && height <= maxH) {
242
- bestFit = mid;
243
- lo = mid;
244
- } else {
245
- hi = mid;
246
- }
247
- }
248
-
249
- return Math.floor(bestFit * 100) / 100;
250
- }
251
-
252
- /**
253
- * Calculate text height from metrics
254
- */
255
- private calculateTextHeight(metrics: TextMetrics, fontSize: number): number {
256
- // Use font bounding box metrics if available
257
- if (metrics.fontBoundingBoxAscent && metrics.fontBoundingBoxDescent) {
258
- return metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
259
- }
260
-
261
- // Fallback to actual bounding box
262
- if (metrics.actualBoundingBoxAscent && metrics.actualBoundingBoxDescent) {
263
- return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
264
- }
265
-
266
- // Final fallback using line height
267
- return this.lineHeight() < 10
268
- ? fontSize * this.lineHeight()
269
- : this.lineHeight();
270
- }
271
-
272
- /* ───────────────────────── Observers ─────────────────────────── */
273
- /**
274
- * Observe parent container resizes
275
- */
276
- private observeResize() {
277
- if (!('ResizeObserver' in window)) return;
278
-
279
- this.ro = new ResizeObserver((entries) => {
280
- // Only trigger if size actually changed
281
- const entry = entries[0];
282
- if (entry?.contentRect) {
283
- this.requestFit();
284
- }
285
- });
286
-
287
- const parent = this.el.nativeElement.parentElement;
288
- if (parent) {
289
- this.ro.observe(parent);
290
- }
291
- }
292
-
293
- /**
294
- * Observe text content changes
295
- */
296
- private observeText() {
297
- if (!('MutationObserver' in window)) return;
298
-
299
- this.mo = new MutationObserver((mutations) => {
300
- // Check if text actually changed
301
- const hasTextChange = mutations.some(
302
- (m) =>
303
- m.type === 'characterData' ||
304
- (m.type === 'childList' &&
305
- (m.addedNodes.length > 0 || m.removedNodes.length > 0))
306
- );
307
-
308
- if (hasTextChange) {
309
- this.requestFit();
310
- }
311
- });
312
-
313
- this.mo.observe(this.el.nativeElement, {
314
- characterData: true,
315
- childList: true,
316
- subtree: true,
317
- });
318
- }
319
-
320
- /**
321
- * Cleanup resources
322
- */
323
- private cleanup() {
324
- this.ro?.disconnect();
325
- this.mo?.disconnect();
326
-
327
- if (this.fitTimeout) {
328
- cancelAnimationFrame(this.fitTimeout);
329
- }
330
-
331
- // Clear canvas context
332
- this._ctx = undefined;
333
- }
334
- }