@haloduck/util 2.0.3 → 2.0.5

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.
@@ -0,0 +1,213 @@
1
+ import { Validators } from '@angular/forms';
2
+ import * as i0 from '@angular/core';
3
+ import { InjectionToken, inject, Injectable, Input, Directive } from '@angular/core';
4
+
5
+ function download(href) {
6
+ const link = document.createElement('a');
7
+ link.href = href;
8
+ link.target = '_blank';
9
+ document.body.appendChild(link);
10
+ link.click();
11
+ document.body.removeChild(link);
12
+ }
13
+
14
+ function hasRequired(control) {
15
+ return control?.hasValidator(Validators.required);
16
+ }
17
+ function hasError(control, errorType) {
18
+ return control?.touched && (control?.hasError(errorType) ?? false);
19
+ }
20
+ function getDirtyValues(fg) {
21
+ const dirtyValues = Object.keys(fg.controls)
22
+ .filter((key) => fg.controls[key].dirty)
23
+ .reduce((acc, key) => {
24
+ acc[key] = fg.controls[key].value;
25
+ return acc;
26
+ }, {});
27
+ return dirtyValues;
28
+ }
29
+ const englishAndNumberOnlyValidator = Validators.pattern(/^[0-9A-Za-z\s]*$/);
30
+ function isEqual(a, b) {
31
+ if (a === b)
32
+ return true;
33
+ if (!a || !b)
34
+ return false;
35
+ if (Array.isArray(a) && Array.isArray(b)) {
36
+ return (a.length === b.length && a.every((val, i) => isEqual(val, b[i])));
37
+ }
38
+ if (typeof a === 'object' && typeof b === 'object') {
39
+ const keysA = Object.keys(a);
40
+ const keysB = Object.keys(b);
41
+ return (keysA.length === keysB.length &&
42
+ keysA.every((key) => isEqual(a[key], b[key])));
43
+ }
44
+ return false;
45
+ }
46
+
47
+ const FILE_ICON_SET = new InjectionToken('FILE_ICON_SET', {
48
+ providedIn: 'root',
49
+ factory: () => ({
50
+ pdf: 'assets/pdf.svg',
51
+ stl: 'assets/stl.svg',
52
+ default: 'assets/default.svg',
53
+ }),
54
+ });
55
+ function provideHaloduckUtilIconSet(iconSet) {
56
+ return {
57
+ provide: FILE_ICON_SET,
58
+ useValue: iconSet,
59
+ };
60
+ }
61
+ class UtilService {
62
+ // private readonly environmentInjector = inject(EnvironmentInjector);
63
+ iconSet = inject(FILE_ICON_SET);
64
+ getFileIconUrl(filename) {
65
+ const extension = filename.split('.').pop()?.toLowerCase();
66
+ return this.iconSet[extension || ''] || this.iconSet['default'];
67
+ }
68
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: UtilService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
69
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: UtilService, providedIn: 'root' });
70
+ }
71
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: UtilService, decorators: [{
72
+ type: Injectable,
73
+ args: [{
74
+ providedIn: 'root',
75
+ }]
76
+ }] });
77
+
78
+ /**
79
+ * Shrinks the font-size so the text fits within the element's width.
80
+ * - Only scales down; never scales up beyond the computed font size
81
+ * - Observes element resize and text mutations
82
+ */
83
+ class FitTextDirective {
84
+ elRef;
85
+ zone;
86
+ hddFitTextMinPx = 6; // reasonable minimum for small cards
87
+ hddFitTextMaxPx; // if provided, caps the max font size
88
+ host;
89
+ resizeObserver;
90
+ mutationObserver;
91
+ destroyed = false;
92
+ removeWindowResizeListener;
93
+ rafId = null;
94
+ scheduled = false;
95
+ handleWindowResize = () => this.scheduleFit();
96
+ constructor(elRef, zone) {
97
+ this.elRef = elRef;
98
+ this.zone = zone;
99
+ this.host = elRef.nativeElement;
100
+ }
101
+ ngAfterViewInit() {
102
+ // Avoid layout thrash during Angular change detection
103
+ this.zone.runOutsideAngular(() => {
104
+ // Ensure single line to match width-fit semantics
105
+ this.host.style.whiteSpace = this.host.style.whiteSpace || 'nowrap';
106
+ // Trigger initial fit after current microtask
107
+ setTimeout(() => this.scheduleFit(), 0);
108
+ // Observe element size changes
109
+ if (typeof ResizeObserver !== 'undefined') {
110
+ this.resizeObserver = new ResizeObserver(() => this.scheduleFit());
111
+ this.resizeObserver.observe(this.host);
112
+ }
113
+ else if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
114
+ window.addEventListener('resize', this.handleWindowResize);
115
+ this.removeWindowResizeListener = () => window.removeEventListener('resize', this.handleWindowResize);
116
+ }
117
+ // Observe text/content changes
118
+ this.mutationObserver = new MutationObserver(() => this.scheduleFit());
119
+ this.mutationObserver.observe(this.host, { childList: true, characterData: true, subtree: true });
120
+ });
121
+ }
122
+ ngOnDestroy() {
123
+ this.destroyed = true;
124
+ if (this.resizeObserver) {
125
+ this.resizeObserver.disconnect();
126
+ }
127
+ this.removeWindowResizeListener?.();
128
+ this.mutationObserver?.disconnect();
129
+ if (this.rafId !== null) {
130
+ cancelAnimationFrame(this.rafId);
131
+ this.rafId = null;
132
+ }
133
+ }
134
+ scheduleFit = () => {
135
+ if (this.destroyed || this.scheduled)
136
+ return;
137
+ this.scheduled = true;
138
+ const raf = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (cb) => setTimeout(() => cb(performance.now()), 16);
139
+ this.rafId = raf(() => {
140
+ this.rafId = null;
141
+ this.scheduled = false;
142
+ this.fit();
143
+ });
144
+ };
145
+ fit = () => {
146
+ if (this.destroyed)
147
+ return;
148
+ const element = this.host;
149
+ const availableWidth = element.clientWidth;
150
+ if (!availableWidth)
151
+ return;
152
+ const computed = window.getComputedStyle(element);
153
+ const originalFontSizePx = this.parsePx(computed.fontSize) || 16;
154
+ const maxPx = this.hddFitTextMaxPx ? Math.min(this.hddFitTextMaxPx, originalFontSizePx) : originalFontSizePx;
155
+ let low = this.hddFitTextMinPx;
156
+ let high = Math.max(low, Math.floor(maxPx));
157
+ // Quick accept if it already fits
158
+ element.style.fontSize = `${high}px`;
159
+ if (this.scrollsWithin(element, availableWidth)) {
160
+ return;
161
+ }
162
+ // Binary search for the largest font-size that fits
163
+ while (low < high) {
164
+ const mid = Math.floor((low + high) / 2);
165
+ element.style.fontSize = `${mid}px`;
166
+ if (this.scrollsWithin(element, availableWidth)) {
167
+ low = mid + 1; // try bigger
168
+ }
169
+ else {
170
+ high = mid - 1; // too big
171
+ }
172
+ }
173
+ // After loop, test final candidates
174
+ for (let size = Math.min(high, maxPx); size >= this.hddFitTextMinPx; size--) {
175
+ element.style.fontSize = `${size}px`;
176
+ if (this.scrollsWithin(element, availableWidth)) {
177
+ break;
178
+ }
179
+ }
180
+ };
181
+ scrollsWithin(element, availableWidth) {
182
+ // Add a small epsilon for sub-pixel rounding
183
+ return element.scrollWidth <= Math.ceil(availableWidth + 0.5);
184
+ }
185
+ parsePx(value) {
186
+ const num = Number(String(value || '').replace('px', ''));
187
+ return isFinite(num) ? num : 0;
188
+ }
189
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: FitTextDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
190
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.4", type: FitTextDirective, isStandalone: true, selector: "[haloduckFitText]", inputs: { hddFitTextMinPx: "hddFitTextMinPx", hddFitTextMaxPx: "hddFitTextMaxPx" }, ngImport: i0 });
191
+ }
192
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: FitTextDirective, decorators: [{
193
+ type: Directive,
194
+ args: [{
195
+ selector: '[haloduckFitText]',
196
+ standalone: true
197
+ }]
198
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { hddFitTextMinPx: [{
199
+ type: Input
200
+ }], hddFitTextMaxPx: [{
201
+ type: Input
202
+ }] } });
203
+
204
+ /*
205
+ * Public API Surface of util
206
+ */
207
+
208
+ /**
209
+ * Generated bundle index. Do not edit.
210
+ */
211
+
212
+ export { FILE_ICON_SET, FitTextDirective, UtilService, download, englishAndNumberOnlyValidator, getDirtyValues, hasError, hasRequired, isEqual, provideHaloduckUtilIconSet };
213
+ //# sourceMappingURL=haloduck-util.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haloduck-util.mjs","sources":["../../../../projects/haloduck/util/src/lib/function/util.file.ts","../../../../projects/haloduck/util/src/lib/function/util.form.ts","../../../../projects/haloduck/util/src/lib/service/util.service.ts","../../../../projects/haloduck/util/src/lib/directive/fit-text.directive.ts","../../../../projects/haloduck/util/src/public-api.ts","../../../../projects/haloduck/util/src/haloduck-util.ts"],"sourcesContent":["\nexport function download(href: string) {\n const link = document.createElement('a');\n link.href = href;\n link.target = '_blank';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n}\n","import { AbstractControl, Validators, FormGroup } from '@angular/forms';\n\nexport function hasRequired(control: AbstractControl<any, any> | null) {\n return control?.hasValidator(Validators.required);\n}\n\nexport function hasError(\n control: AbstractControl<any, any> | null,\n errorType: string,\n): boolean | undefined {\n return control?.touched && (control?.hasError(errorType) ?? false);\n}\n\nexport function getDirtyValues(fg: FormGroup) {\n const dirtyValues = Object.keys(fg.controls)\n .filter((key) => fg.controls[key].dirty)\n .reduce((acc: { [key: string]: any }, key) => {\n acc[key] = fg.controls[key].value;\n return acc;\n }, {});\n\n return dirtyValues;\n}\n\nexport const englishAndNumberOnlyValidator =\n Validators.pattern(/^[0-9A-Za-z\\s]*$/);\n\nexport function isEqual(a: any, b: any): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n return (\n a.length === b.length && a.every((val, i) => isEqual(val, b[i]))\n );\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n return (\n keysA.length === keysB.length &&\n keysA.every((key) => isEqual(a[key], b[key]))\n );\n }\n return false;\n}\n","import {\n Injectable,\n inject,\n InjectionToken,\n Provider,\n EnvironmentInjector,\n} from '@angular/core';\n\nexport const FILE_ICON_SET = new InjectionToken<Record<string, string>>(\n 'FILE_ICON_SET',\n {\n providedIn: 'root',\n factory: () => ({\n pdf: 'assets/pdf.svg',\n stl: 'assets/stl.svg',\n default: 'assets/default.svg',\n }),\n },\n);\n\nexport function provideHaloduckUtilIconSet(\n iconSet: Record<string, string>,\n): Provider {\n return {\n provide: FILE_ICON_SET,\n useValue: iconSet,\n };\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class UtilService {\n // private readonly environmentInjector = inject(EnvironmentInjector);\n iconSet = inject(FILE_ICON_SET);\n\n getFileIconUrl(filename: string): string {\n const extension = filename.split('.').pop()?.toLowerCase();\n return this.iconSet[extension || ''] || this.iconSet['default'];\n }\n}\n","import { AfterViewInit, Directive, ElementRef, Input, NgZone, OnDestroy } from '@angular/core';\n\n/**\n * Shrinks the font-size so the text fits within the element's width.\n * - Only scales down; never scales up beyond the computed font size\n * - Observes element resize and text mutations\n */\n@Directive({\n selector: '[haloduckFitText]',\n standalone: true\n})\nexport class FitTextDirective implements AfterViewInit, OnDestroy {\n @Input() hddFitTextMinPx = 6; // reasonable minimum for small cards\n @Input() hddFitTextMaxPx?: number; // if provided, caps the max font size\n\n private readonly host: HTMLElement;\n private resizeObserver?: ResizeObserver;\n private mutationObserver?: MutationObserver;\n private destroyed = false;\n private removeWindowResizeListener?: () => void;\n private rafId: number | null = null;\n private scheduled = false;\n private readonly handleWindowResize = () => this.scheduleFit();\n\n constructor(private readonly elRef: ElementRef<HTMLElement>, private readonly zone: NgZone) {\n this.host = elRef.nativeElement;\n }\n\n ngAfterViewInit(): void {\n // Avoid layout thrash during Angular change detection\n this.zone.runOutsideAngular(() => {\n // Ensure single line to match width-fit semantics\n this.host.style.whiteSpace = this.host.style.whiteSpace || 'nowrap';\n // Trigger initial fit after current microtask\n setTimeout(() => this.scheduleFit(), 0);\n\n // Observe element size changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.scheduleFit());\n this.resizeObserver.observe(this.host);\n } else if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {\n window.addEventListener('resize', this.handleWindowResize);\n this.removeWindowResizeListener = () => window.removeEventListener('resize', this.handleWindowResize);\n }\n\n // Observe text/content changes\n this.mutationObserver = new MutationObserver(() => this.scheduleFit());\n this.mutationObserver.observe(this.host, { childList: true, characterData: true, subtree: true });\n });\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n this.removeWindowResizeListener?.();\n this.mutationObserver?.disconnect();\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n private scheduleFit = (): void => {\n if (this.destroyed || this.scheduled) return;\n this.scheduled = true;\n const raf = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (cb: FrameRequestCallback) => setTimeout(() => cb(performance.now()), 16) as unknown as number;\n this.rafId = raf(() => {\n this.rafId = null;\n this.scheduled = false;\n this.fit();\n });\n };\n\n private fit = (): void => {\n if (this.destroyed) return;\n\n const element = this.host;\n const availableWidth = element.clientWidth;\n if (!availableWidth) return;\n\n const computed = window.getComputedStyle(element);\n const originalFontSizePx = this.parsePx(computed.fontSize) || 16;\n const maxPx = this.hddFitTextMaxPx ? Math.min(this.hddFitTextMaxPx, originalFontSizePx) : originalFontSizePx;\n let low = this.hddFitTextMinPx;\n let high = Math.max(low, Math.floor(maxPx));\n\n // Quick accept if it already fits\n element.style.fontSize = `${high}px`;\n if (this.scrollsWithin(element, availableWidth)) {\n return;\n }\n\n // Binary search for the largest font-size that fits\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n element.style.fontSize = `${mid}px`;\n if (this.scrollsWithin(element, availableWidth)) {\n low = mid + 1; // try bigger\n } else {\n high = mid - 1; // too big\n }\n }\n // After loop, test final candidates\n for (let size = Math.min(high, maxPx); size >= this.hddFitTextMinPx; size--) {\n element.style.fontSize = `${size}px`;\n if (this.scrollsWithin(element, availableWidth)) {\n break;\n }\n }\n };\n\n private scrollsWithin(element: HTMLElement, availableWidth: number): boolean {\n // Add a small epsilon for sub-pixel rounding\n return element.scrollWidth <= Math.ceil(availableWidth + 0.5);\n }\n\n private parsePx(value: string): number {\n const num = Number(String(value || '').replace('px', ''));\n return isFinite(num) ? num : 0;\n }\n}\n\n\n","/*\n * Public API Surface of util\n */\n\nexport * from './lib';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import * as _angular_forms from '@angular/forms';
2
+ import { AbstractControl, FormGroup } from '@angular/forms';
3
+ import * as i0 from '@angular/core';
4
+ import { InjectionToken, Provider, AfterViewInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
5
+
6
+ declare function download(href: string): void;
7
+
8
+ declare function hasRequired(control: AbstractControl<any, any> | null): boolean | undefined;
9
+ declare function hasError(control: AbstractControl<any, any> | null, errorType: string): boolean | undefined;
10
+ declare function getDirtyValues(fg: FormGroup): {
11
+ [key: string]: any;
12
+ };
13
+ declare const englishAndNumberOnlyValidator: _angular_forms.ValidatorFn;
14
+ declare function isEqual(a: any, b: any): boolean;
15
+
16
+ declare const FILE_ICON_SET: InjectionToken<Record<string, string>>;
17
+ declare function provideHaloduckUtilIconSet(iconSet: Record<string, string>): Provider;
18
+ declare class UtilService {
19
+ iconSet: Record<string, string>;
20
+ getFileIconUrl(filename: string): string;
21
+ static ɵfac: i0.ɵɵFactoryDeclaration<UtilService, never>;
22
+ static ɵprov: i0.ɵɵInjectableDeclaration<UtilService>;
23
+ }
24
+
25
+ /**
26
+ * Shrinks the font-size so the text fits within the element's width.
27
+ * - Only scales down; never scales up beyond the computed font size
28
+ * - Observes element resize and text mutations
29
+ */
30
+ declare class FitTextDirective implements AfterViewInit, OnDestroy {
31
+ private readonly elRef;
32
+ private readonly zone;
33
+ hddFitTextMinPx: number;
34
+ hddFitTextMaxPx?: number;
35
+ private readonly host;
36
+ private resizeObserver?;
37
+ private mutationObserver?;
38
+ private destroyed;
39
+ private removeWindowResizeListener?;
40
+ private rafId;
41
+ private scheduled;
42
+ private readonly handleWindowResize;
43
+ constructor(elRef: ElementRef<HTMLElement>, zone: NgZone);
44
+ ngAfterViewInit(): void;
45
+ ngOnDestroy(): void;
46
+ private scheduleFit;
47
+ private fit;
48
+ private scrollsWithin;
49
+ private parsePx;
50
+ static ɵfac: i0.ɵɵFactoryDeclaration<FitTextDirective, never>;
51
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FitTextDirective, "[haloduckFitText]", never, { "hddFitTextMinPx": { "alias": "hddFitTextMinPx"; "required": false; }; "hddFitTextMaxPx": { "alias": "hddFitTextMaxPx"; "required": false; }; }, {}, never, never, true, never>;
52
+ }
53
+
54
+ export { FILE_ICON_SET, FitTextDirective, UtilService, download, englishAndNumberOnlyValidator, getDirtyValues, hasError, hasRequired, isEqual, provideHaloduckUtilIconSet };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haloduck/util",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "HaloDuck Util Library - Angular",
5
5
  "keywords": [
6
6
  "angular",
@@ -30,16 +30,14 @@
30
30
  "exports": {
31
31
  ".": {
32
32
  "import": "./fesm2022/haloduck-util.mjs",
33
- "require": "./bundles/haloduck-util.umd.js"
33
+ "require": "./bundles/haloduck-util.umd.js",
34
+ "types": "./index.d.ts",
35
+ "default": "./fesm2022/haloduck-util.mjs"
36
+ },
37
+ "./package.json": {
38
+ "default": "./package.json"
34
39
  }
35
40
  },
36
- "scripts": {
37
- "build": "ng build @haloduck/util",
38
- "build:prod": "ng build @haloduck/util --configuration production",
39
- "test": "ng test @haloduck/util",
40
- "lint": "ng lint @haloduck/util",
41
- "prepublishOnly": "npm run build:prod"
42
- },
43
41
  "peerDependencies": {
44
42
  "@angular/common": "^20.0.0",
45
43
  "@angular/core": "^20.0.0",
@@ -52,4 +50,4 @@
52
50
  "publishConfig": {
53
51
  "access": "public"
54
52
  }
55
- }
53
+ }