tyrell-components 1.0.0-RC12 → 1.0.0-RC14
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/README.md +2 -1
- package/css/tyrell-colors-static.css +316 -0
- package/css/tyrell-theme.css +1443 -0
- package/css/tyrell.css +127 -423
- package/dist/tyrell-colors-static.css +316 -0
- package/dist/tyrell-theme.css +1443 -0
- package/dist/tyrell.css +127 -423
- package/dist/tyrell.js +1 -1
- package/lib/base/ty-component.d.ts +20 -10
- package/lib/base/ty-component.js +21 -144
- package/lib/components/button.d.ts +9 -16
- package/lib/components/button.js +12 -37
- package/lib/components/calendar-month.d.ts +17 -46
- package/lib/components/calendar-month.js +97 -145
- package/lib/components/calendar-navigation.d.ts +3 -48
- package/lib/components/calendar-navigation.js +3 -126
- package/lib/components/calendar.d.ts +6 -81
- package/lib/components/calendar.js +24 -236
- package/lib/components/checkbox.d.ts +8 -74
- package/lib/components/checkbox.js +21 -177
- package/lib/components/copy.d.ts +7 -59
- package/lib/components/copy.js +19 -137
- package/lib/components/date-picker.d.ts +23 -161
- package/lib/components/date-picker.js +127 -588
- package/lib/components/file-upload.d.ts +9 -15
- package/lib/components/file-upload.js +19 -41
- package/lib/components/icon.d.ts +3 -24
- package/lib/components/icon.js +1 -90
- package/lib/components/input.d.ts +10 -109
- package/lib/components/input.js +24 -209
- package/lib/components/modal.d.ts +17 -26
- package/lib/components/modal.js +67 -192
- package/lib/components/option.d.ts +2 -20
- package/lib/components/option.js +1 -148
- package/lib/components/popup.d.ts +2 -17
- package/lib/components/popup.js +35 -132
- package/lib/components/radio.d.ts +7 -24
- package/lib/components/radio.js +24 -68
- package/lib/components/resize-observer.d.ts +3 -30
- package/lib/components/resize-observer.js +12 -46
- package/lib/components/scroll-container.d.ts +2 -12
- package/lib/components/scroll-container.js +3 -46
- package/lib/components/select.d.ts +130 -151
- package/lib/components/select.js +327 -516
- package/lib/components/selected-tags.d.ts +8 -38
- package/lib/components/selected-tags.js +20 -78
- package/lib/components/step.d.ts +1 -6
- package/lib/components/step.js +0 -6
- package/lib/components/switch.d.ts +4 -14
- package/lib/components/switch.js +15 -61
- package/lib/components/tab.d.ts +1 -6
- package/lib/components/tab.js +0 -7
- package/lib/components/tabs.d.ts +5 -63
- package/lib/components/tabs.js +159 -267
- package/lib/components/tag.d.ts +2 -36
- package/lib/components/tag.js +5 -105
- package/lib/components/textarea.d.ts +6 -86
- package/lib/components/textarea.js +2 -173
- package/lib/components/tooltip.d.ts +7 -13
- package/lib/components/tooltip.js +83 -180
- package/lib/components/wizard.d.ts +6 -54
- package/lib/components/wizard.js +27 -221
- package/lib/icons/fontawesome/brands.d.ts +0 -1
- package/lib/icons/fontawesome/brands.js +0 -8
- package/lib/icons/fontawesome/regular.d.ts +0 -1
- package/lib/icons/fontawesome/regular.js +0 -8
- package/lib/icons/fontawesome/solid.d.ts +0 -1
- package/lib/icons/fontawesome/solid.js +0 -8
- package/lib/icons/heroicons/micro.d.ts +0 -1
- package/lib/icons/heroicons/micro.js +0 -8
- package/lib/icons/heroicons/mini.d.ts +0 -1
- package/lib/icons/heroicons/mini.js +0 -8
- package/lib/icons/heroicons/outline.d.ts +0 -1
- package/lib/icons/heroicons/outline.js +0 -8
- package/lib/icons/heroicons/solid.d.ts +0 -1
- package/lib/icons/heroicons/solid.js +0 -8
- package/lib/icons/lucide.d.ts +0 -1
- package/lib/icons/lucide.js +0 -1872
- package/lib/icons/material/filled.d.ts +0 -1
- package/lib/icons/material/filled.js +0 -10
- package/lib/icons/material/outlined.d.ts +0 -1
- package/lib/icons/material/outlined.js +0 -10
- package/lib/icons/material/round.d.ts +0 -1
- package/lib/icons/material/round.js +0 -10
- package/lib/icons/material/sharp.d.ts +0 -1
- package/lib/icons/material/sharp.js +0 -10
- package/lib/icons/material/two-tone.d.ts +0 -1
- package/lib/icons/material/two-tone.js +0 -10
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -21
- package/lib/styles/button.d.ts +19 -7
- package/lib/styles/button.js +180 -77
- package/lib/styles/calendar-month.d.ts +4 -6
- package/lib/styles/calendar-month.js +57 -59
- package/lib/styles/calendar-navigation.d.ts +2 -6
- package/lib/styles/calendar-navigation.js +3 -28
- package/lib/styles/calendar.d.ts +1 -2
- package/lib/styles/calendar.js +1 -14
- package/lib/styles/checkbox.d.ts +3 -2
- package/lib/styles/checkbox.js +24 -68
- package/lib/styles/copy.d.ts +3 -2
- package/lib/styles/copy.js +25 -17
- package/lib/styles/custom-scrollbar.d.ts +1 -2
- package/lib/styles/custom-scrollbar.js +5 -20
- package/lib/styles/date-picker.d.ts +4 -6
- package/lib/styles/date-picker.js +80 -130
- package/lib/styles/file-upload.d.ts +1 -2
- package/lib/styles/file-upload.js +4 -19
- package/lib/styles/icon.d.ts +2 -6
- package/lib/styles/icon.js +7 -21
- package/lib/styles/input.d.ts +4 -7
- package/lib/styles/input.js +55 -160
- package/lib/styles/modal.d.ts +1 -2
- package/lib/styles/modal.js +21 -23
- package/lib/styles/option.d.ts +2 -6
- package/lib/styles/option.js +17 -63
- package/lib/styles/popup.d.ts +1 -2
- package/lib/styles/popup.js +26 -30
- package/lib/styles/radio.d.ts +3 -2
- package/lib/styles/radio.js +21 -32
- package/lib/styles/resize-observer.d.ts +1 -2
- package/lib/styles/resize-observer.js +0 -7
- package/lib/styles/scroll-container.d.ts +1 -2
- package/lib/styles/scroll-container.js +0 -19
- package/lib/styles/select-base.d.ts +8 -0
- package/lib/styles/{multiselect.js → select-base.js} +264 -127
- package/lib/styles/select.d.ts +3 -4
- package/lib/styles/select.js +103 -43
- package/lib/styles/step.d.ts +1 -2
- package/lib/styles/step.js +0 -4
- package/lib/styles/switch.d.ts +3 -2
- package/lib/styles/switch.js +20 -23
- package/lib/styles/tab.d.ts +1 -2
- package/lib/styles/tab.js +0 -5
- package/lib/styles/tabs.d.ts +3 -11
- package/lib/styles/tabs.js +77 -45
- package/lib/styles/tag.d.ts +1 -5
- package/lib/styles/tag.js +6 -32
- package/lib/styles/textarea.d.ts +1 -2
- package/lib/styles/textarea.js +7 -50
- package/lib/styles/tooltip.d.ts +4 -3
- package/lib/styles/tooltip.js +0 -125
- package/lib/styles/wizard.d.ts +5 -21
- package/lib/styles/wizard.js +23 -91
- package/lib/types/common.d.ts +1 -3
- package/lib/types/common.js +0 -10
- package/lib/utils/calendar-utils.d.ts +4 -21
- package/lib/utils/calendar-utils.js +4 -177
- package/lib/utils/custom-scrollbar.d.ts +6 -20
- package/lib/utils/custom-scrollbar.js +1 -45
- package/lib/utils/flavor-sheet.d.ts +0 -1
- package/lib/utils/flavor-sheet.js +0 -23
- package/lib/utils/icon-registry.d.ts +0 -2
- package/lib/utils/icon-registry.js +1 -107
- package/lib/utils/icons.d.ts +9 -0
- package/lib/utils/icons.js +1 -0
- package/lib/utils/loader-registry.d.ts +2 -3
- package/lib/utils/loader-registry.js +0 -32
- package/lib/utils/locale.d.ts +2 -23
- package/lib/utils/locale.js +0 -86
- package/lib/utils/mobile.d.ts +0 -1
- package/lib/utils/mobile.js +0 -17
- package/lib/utils/number-format.d.ts +6 -3
- package/lib/utils/number-format.js +11 -67
- package/lib/utils/positioning.d.ts +77 -3
- package/lib/utils/positioning.js +67 -58
- package/lib/utils/property-capture.d.ts +5 -27
- package/lib/utils/property-capture.js +0 -108
- package/lib/utils/property-manager.d.ts +0 -1
- package/lib/utils/property-manager.js +0 -70
- package/lib/utils/resize-observer.d.ts +0 -1
- package/lib/utils/resize-observer.js +0 -36
- package/lib/utils/scroll-lock.d.ts +4 -11
- package/lib/utils/scroll-lock.js +0 -107
- package/lib/utils/styles.d.ts +0 -1
- package/lib/utils/styles.js +0 -24
- package/lib/version.d.ts +1 -2
- package/lib/version.js +1 -11
- package/package.json +8 -5
- package/css/tyrell-brand.css +0 -1015
- package/dist/tyrell-brand.css +0 -1015
- package/lib/base/ty-component.d.ts.map +0 -1
- package/lib/base/ty-component.js.map +0 -1
- package/lib/components/button.d.ts.map +0 -1
- package/lib/components/button.js.map +0 -1
- package/lib/components/calendar-month.d.ts.map +0 -1
- package/lib/components/calendar-month.js.map +0 -1
- package/lib/components/calendar-navigation.d.ts.map +0 -1
- package/lib/components/calendar-navigation.js.map +0 -1
- package/lib/components/calendar.d.ts.map +0 -1
- package/lib/components/calendar.js.map +0 -1
- package/lib/components/checkbox.d.ts.map +0 -1
- package/lib/components/checkbox.js.map +0 -1
- package/lib/components/copy.d.ts.map +0 -1
- package/lib/components/copy.js.map +0 -1
- package/lib/components/date-picker.d.ts.map +0 -1
- package/lib/components/date-picker.js.map +0 -1
- package/lib/components/file-upload.d.ts.map +0 -1
- package/lib/components/file-upload.js.map +0 -1
- package/lib/components/icon.d.ts.map +0 -1
- package/lib/components/icon.js.map +0 -1
- package/lib/components/input.d.ts.map +0 -1
- package/lib/components/input.js.map +0 -1
- package/lib/components/modal.d.ts.map +0 -1
- package/lib/components/modal.js.map +0 -1
- package/lib/components/option.d.ts.map +0 -1
- package/lib/components/option.js.map +0 -1
- package/lib/components/popup.d.ts.map +0 -1
- package/lib/components/popup.js.map +0 -1
- package/lib/components/radio.d.ts.map +0 -1
- package/lib/components/radio.js.map +0 -1
- package/lib/components/resize-observer.d.ts.map +0 -1
- package/lib/components/resize-observer.js.map +0 -1
- package/lib/components/scroll-container.d.ts.map +0 -1
- package/lib/components/scroll-container.js.map +0 -1
- package/lib/components/select.d.ts.map +0 -1
- package/lib/components/select.js.map +0 -1
- package/lib/components/selected-tags.d.ts.map +0 -1
- package/lib/components/selected-tags.js.map +0 -1
- package/lib/components/step.d.ts.map +0 -1
- package/lib/components/step.js.map +0 -1
- package/lib/components/switch.d.ts.map +0 -1
- package/lib/components/switch.js.map +0 -1
- package/lib/components/tab.d.ts.map +0 -1
- package/lib/components/tab.js.map +0 -1
- package/lib/components/tabs.d.ts.map +0 -1
- package/lib/components/tabs.js.map +0 -1
- package/lib/components/tag.d.ts.map +0 -1
- package/lib/components/tag.js.map +0 -1
- package/lib/components/textarea.d.ts.map +0 -1
- package/lib/components/textarea.js.map +0 -1
- package/lib/components/tooltip.d.ts.map +0 -1
- package/lib/components/tooltip.js.map +0 -1
- package/lib/components/wizard.d.ts.map +0 -1
- package/lib/components/wizard.js.map +0 -1
- package/lib/icons/fontawesome/brands.d.ts.map +0 -1
- package/lib/icons/fontawesome/brands.js.map +0 -1
- package/lib/icons/fontawesome/regular.d.ts.map +0 -1
- package/lib/icons/fontawesome/regular.js.map +0 -1
- package/lib/icons/fontawesome/solid.d.ts.map +0 -1
- package/lib/icons/fontawesome/solid.js.map +0 -1
- package/lib/icons/heroicons/micro.d.ts.map +0 -1
- package/lib/icons/heroicons/micro.js.map +0 -1
- package/lib/icons/heroicons/mini.d.ts.map +0 -1
- package/lib/icons/heroicons/mini.js.map +0 -1
- package/lib/icons/heroicons/outline.d.ts.map +0 -1
- package/lib/icons/heroicons/outline.js.map +0 -1
- package/lib/icons/heroicons/solid.d.ts.map +0 -1
- package/lib/icons/heroicons/solid.js.map +0 -1
- package/lib/icons/lucide.d.ts.map +0 -1
- package/lib/icons/lucide.js.map +0 -1
- package/lib/icons/material/filled.d.ts.map +0 -1
- package/lib/icons/material/filled.js.map +0 -1
- package/lib/icons/material/outlined.d.ts.map +0 -1
- package/lib/icons/material/outlined.js.map +0 -1
- package/lib/icons/material/round.d.ts.map +0 -1
- package/lib/icons/material/round.js.map +0 -1
- package/lib/icons/material/sharp.d.ts.map +0 -1
- package/lib/icons/material/sharp.js.map +0 -1
- package/lib/icons/material/two-tone.d.ts.map +0 -1
- package/lib/icons/material/two-tone.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/styles/button.d.ts.map +0 -1
- package/lib/styles/button.js.map +0 -1
- package/lib/styles/calendar-month.d.ts.map +0 -1
- package/lib/styles/calendar-month.js.map +0 -1
- package/lib/styles/calendar-navigation.d.ts.map +0 -1
- package/lib/styles/calendar-navigation.js.map +0 -1
- package/lib/styles/calendar.d.ts.map +0 -1
- package/lib/styles/calendar.js.map +0 -1
- package/lib/styles/checkbox.d.ts.map +0 -1
- package/lib/styles/checkbox.js.map +0 -1
- package/lib/styles/copy.d.ts.map +0 -1
- package/lib/styles/copy.js.map +0 -1
- package/lib/styles/custom-scrollbar.d.ts.map +0 -1
- package/lib/styles/custom-scrollbar.js.map +0 -1
- package/lib/styles/date-picker.d.ts.map +0 -1
- package/lib/styles/date-picker.js.map +0 -1
- package/lib/styles/file-upload.d.ts.map +0 -1
- package/lib/styles/file-upload.js.map +0 -1
- package/lib/styles/icon.d.ts.map +0 -1
- package/lib/styles/icon.js.map +0 -1
- package/lib/styles/input.d.ts.map +0 -1
- package/lib/styles/input.js.map +0 -1
- package/lib/styles/modal.d.ts.map +0 -1
- package/lib/styles/modal.js.map +0 -1
- package/lib/styles/multiselect.d.ts +0 -6
- package/lib/styles/multiselect.d.ts.map +0 -1
- package/lib/styles/multiselect.js.map +0 -1
- package/lib/styles/option.d.ts.map +0 -1
- package/lib/styles/option.js.map +0 -1
- package/lib/styles/popup.d.ts.map +0 -1
- package/lib/styles/popup.js.map +0 -1
- package/lib/styles/radio.d.ts.map +0 -1
- package/lib/styles/radio.js.map +0 -1
- package/lib/styles/resize-observer.d.ts.map +0 -1
- package/lib/styles/resize-observer.js.map +0 -1
- package/lib/styles/scroll-container.d.ts.map +0 -1
- package/lib/styles/scroll-container.js.map +0 -1
- package/lib/styles/select.d.ts.map +0 -1
- package/lib/styles/select.js.map +0 -1
- package/lib/styles/step.d.ts.map +0 -1
- package/lib/styles/step.js.map +0 -1
- package/lib/styles/switch.d.ts.map +0 -1
- package/lib/styles/switch.js.map +0 -1
- package/lib/styles/tab.d.ts.map +0 -1
- package/lib/styles/tab.js.map +0 -1
- package/lib/styles/tabs.d.ts.map +0 -1
- package/lib/styles/tabs.js.map +0 -1
- package/lib/styles/tag.d.ts.map +0 -1
- package/lib/styles/tag.js.map +0 -1
- package/lib/styles/textarea.d.ts.map +0 -1
- package/lib/styles/textarea.js.map +0 -1
- package/lib/styles/tooltip.d.ts.map +0 -1
- package/lib/styles/tooltip.js.map +0 -1
- package/lib/styles/wizard.d.ts.map +0 -1
- package/lib/styles/wizard.js.map +0 -1
- package/lib/types/common.d.ts.map +0 -1
- package/lib/types/common.js.map +0 -1
- package/lib/utils/calendar-utils.d.ts.map +0 -1
- package/lib/utils/calendar-utils.js.map +0 -1
- package/lib/utils/custom-scrollbar.d.ts.map +0 -1
- package/lib/utils/custom-scrollbar.js.map +0 -1
- package/lib/utils/flavor-sheet.d.ts.map +0 -1
- package/lib/utils/flavor-sheet.js.map +0 -1
- package/lib/utils/icon-registry.d.ts.map +0 -1
- package/lib/utils/icon-registry.js.map +0 -1
- package/lib/utils/loader-registry.d.ts.map +0 -1
- package/lib/utils/loader-registry.js.map +0 -1
- package/lib/utils/locale.d.ts.map +0 -1
- package/lib/utils/locale.js.map +0 -1
- package/lib/utils/mobile.d.ts.map +0 -1
- package/lib/utils/mobile.js.map +0 -1
- package/lib/utils/number-format.d.ts.map +0 -1
- package/lib/utils/number-format.js.map +0 -1
- package/lib/utils/parse-boolean.d.ts +0 -39
- package/lib/utils/parse-boolean.d.ts.map +0 -1
- package/lib/utils/parse-boolean.js +0 -58
- package/lib/utils/parse-boolean.js.map +0 -1
- package/lib/utils/positioning.d.ts.map +0 -1
- package/lib/utils/positioning.js.map +0 -1
- package/lib/utils/property-capture.d.ts.map +0 -1
- package/lib/utils/property-capture.js.map +0 -1
- package/lib/utils/property-manager.d.ts.map +0 -1
- package/lib/utils/property-manager.js.map +0 -1
- package/lib/utils/resize-observer.d.ts.map +0 -1
- package/lib/utils/resize-observer.js.map +0 -1
- package/lib/utils/scroll-lock.d.ts.map +0 -1
- package/lib/utils/scroll-lock.js.map +0 -1
- package/lib/utils/styles.d.ts.map +0 -1
- package/lib/utils/styles.js.map +0 -1
- package/lib/version.d.ts.map +0 -1
- package/lib/version.js.map +0 -1
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Icon Registry System
|
|
3
|
-
* Simple icon storage and retrieval with Cache API persistence
|
|
4
|
-
* PORTED FROM: clj/ty/icons.cljs
|
|
5
|
-
*/
|
|
6
1
|
import { VERSION } from '../version.js';
|
|
7
|
-
/** Icon registry - maps icon names to SVG strings */
|
|
8
2
|
const iconRegistry = new Map();
|
|
9
|
-
/** Watchers for registry changes - now receives Set of changed icon names */
|
|
10
3
|
const watchers = new Map();
|
|
11
|
-
/** Track which icons each watcher cares about (for selective notification) */
|
|
12
4
|
const watcherIconNames = new Map();
|
|
13
|
-
/** Pending notifications - batched for performance */
|
|
14
5
|
let pendingNotifications = null;
|
|
15
6
|
let notificationTimer = null;
|
|
16
|
-
/** Cache API configuration */
|
|
17
7
|
const CACHE_NAME = `ty-icons-v${VERSION}`;
|
|
18
|
-
// Use a fake URL base for Cache API (requires valid URL format)
|
|
19
8
|
const CACHE_URL_BASE = 'https://ty-icons.local/';
|
|
20
|
-
/** Track in-flight cache reads to avoid duplicate work */
|
|
21
9
|
const cacheReadPromises = new Map();
|
|
22
|
-
/** Flag to track if old caches have been cleared */
|
|
23
10
|
let oldCachesCleared = false;
|
|
24
|
-
/**
|
|
25
|
-
* Clear old cache versions on first use
|
|
26
|
-
* This runs lazily the first time any icon is requested
|
|
27
|
-
*/
|
|
28
11
|
async function clearOldCaches() {
|
|
29
12
|
if (oldCachesCleared || !('caches' in window))
|
|
30
13
|
return;
|
|
@@ -41,11 +24,6 @@ async function clearOldCaches() {
|
|
|
41
24
|
console.warn('[ty-icons] Failed to clear old caches:', err);
|
|
42
25
|
}
|
|
43
26
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Cache an icon in Cache API (non-blocking background operation)
|
|
46
|
-
* @param name Icon name
|
|
47
|
-
* @param svg SVG string
|
|
48
|
-
*/
|
|
49
27
|
async function cacheIcon(name, svg) {
|
|
50
28
|
if (!('caches' in window))
|
|
51
29
|
return;
|
|
@@ -54,30 +32,19 @@ async function cacheIcon(name, svg) {
|
|
|
54
32
|
const response = new Response(svg, {
|
|
55
33
|
headers: { 'Content-Type': 'image/svg+xml' }
|
|
56
34
|
});
|
|
57
|
-
// Cache API requires valid URLs
|
|
58
35
|
await cache.put(`${CACHE_URL_BASE}${name}`, response);
|
|
59
36
|
}
|
|
60
37
|
catch (err) {
|
|
61
|
-
// Silent fail - caching is an optimization, not critical
|
|
62
38
|
console.warn(`[ty-icons] Failed to cache icon "${name}":`, err);
|
|
63
39
|
}
|
|
64
40
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Read icon directly from cache (for immediate rendering)
|
|
67
|
-
* This is the key function that prevents icon twitching!
|
|
68
|
-
* @param name Icon name
|
|
69
|
-
* @returns Promise<string | null> - SVG string from cache or null
|
|
70
|
-
*/
|
|
71
41
|
export async function getCachedIcon(name) {
|
|
72
|
-
// Check if we already have a read in progress for this icon
|
|
73
42
|
const existingPromise = cacheReadPromises.get(name);
|
|
74
43
|
if (existingPromise) {
|
|
75
44
|
return existingPromise;
|
|
76
45
|
}
|
|
77
|
-
// Create new read promise
|
|
78
46
|
const readPromise = (async () => {
|
|
79
47
|
try {
|
|
80
|
-
// Clear old caches on first cache access
|
|
81
48
|
await clearOldCaches();
|
|
82
49
|
const cache = await caches.open(CACHE_NAME);
|
|
83
50
|
const response = await cache.match(`${CACHE_URL_BASE}${name}`);
|
|
@@ -90,77 +57,35 @@ export async function getCachedIcon(name) {
|
|
|
90
57
|
}
|
|
91
58
|
return null;
|
|
92
59
|
})();
|
|
93
|
-
// Store promise to avoid duplicate work
|
|
94
60
|
cacheReadPromises.set(name, readPromise);
|
|
95
|
-
// Clean up promise after completion
|
|
96
61
|
readPromise.finally(() => {
|
|
97
62
|
cacheReadPromises.delete(name);
|
|
98
63
|
});
|
|
99
64
|
return readPromise;
|
|
100
65
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Register multiple icons at once
|
|
103
|
-
* Always updates memory and notifies watchers immediately
|
|
104
|
-
* Cache update happens in background (only if different from cached version)
|
|
105
|
-
* @param icons Object mapping icon names to SVG strings
|
|
106
|
-
*/
|
|
107
66
|
export function registerIcons(icons) {
|
|
108
67
|
const changedIcons = new Set();
|
|
109
68
|
Object.entries(icons).forEach(([name, svg]) => {
|
|
110
|
-
// Always set in memory registry (fast, synchronous)
|
|
111
69
|
iconRegistry.set(name, svg);
|
|
112
70
|
changedIcons.add(name);
|
|
113
|
-
// Compare with cache in background to avoid unnecessary cache writes
|
|
114
|
-
/*getCachedIcon(name).then(cachedSvg => {
|
|
115
|
-
// Only write to cache if content actually changed
|
|
116
|
-
if (cachedSvg !== svg) {
|
|
117
|
-
cacheIcon(name, svg).catch(() => {
|
|
118
|
-
// Error already logged in cacheIcon
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
// If cachedSvg === svg, skip cache write (already up to date)
|
|
122
|
-
}).catch(() => {
|
|
123
|
-
// No cached version or error reading cache - write new icon
|
|
124
|
-
cacheIcon(name, svg).catch(() => {
|
|
125
|
-
// Error already logged in cacheIcon
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
*/
|
|
129
71
|
});
|
|
130
|
-
// Notify watchers immediately (synchronous)
|
|
131
72
|
if (changedIcons.size > 0) {
|
|
132
73
|
scheduleNotification(changedIcons);
|
|
133
74
|
}
|
|
134
75
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Lookup an icon by name from memory
|
|
137
|
-
* NOTE: This does NOT check cache - use getCachedIcon() for that
|
|
138
|
-
* @param name Icon name
|
|
139
|
-
* @returns SVG string or undefined
|
|
140
|
-
*/
|
|
141
76
|
export function getIcon(name) {
|
|
142
77
|
return iconRegistry.get(name);
|
|
143
78
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Check if an icon exists in memory
|
|
146
|
-
* NOTE: This does NOT check cache
|
|
147
|
-
* @param name Icon name
|
|
148
|
-
* @returns true if icon is registered in memory
|
|
149
|
-
*/
|
|
150
79
|
export function hasIcon(name) {
|
|
151
80
|
return iconRegistry.has(name);
|
|
152
81
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Clear all registered icons from memory and cache
|
|
155
|
-
*/
|
|
156
82
|
export async function clearIcons() {
|
|
157
83
|
const allIcons = new Set(iconRegistry.keys());
|
|
158
84
|
iconRegistry.clear();
|
|
159
|
-
// Clear cache
|
|
160
85
|
if ('caches' in window) {
|
|
161
86
|
try {
|
|
162
87
|
await caches.delete(CACHE_NAME);
|
|
163
|
-
oldCachesCleared = false;
|
|
88
|
+
oldCachesCleared = false;
|
|
164
89
|
}
|
|
165
90
|
catch (err) {
|
|
166
91
|
console.warn('[ty-icons] Failed to clear icon cache:', err);
|
|
@@ -168,40 +93,24 @@ export async function clearIcons() {
|
|
|
168
93
|
}
|
|
169
94
|
scheduleNotification(allIcons);
|
|
170
95
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Add a watcher for registry changes
|
|
173
|
-
* @param id Unique watcher ID
|
|
174
|
-
* @param iconName Optional icon name to watch (for selective notification)
|
|
175
|
-
* @param callback Function to call when watched icons change
|
|
176
|
-
*/
|
|
177
96
|
export function addWatcher(id, iconName, callback) {
|
|
178
97
|
watchers.set(id, callback);
|
|
179
98
|
if (iconName) {
|
|
180
99
|
watcherIconNames.set(id, iconName);
|
|
181
100
|
}
|
|
182
101
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Remove a watcher
|
|
185
|
-
* @param id Watcher ID to remove
|
|
186
|
-
*/
|
|
187
102
|
export function removeWatcher(id) {
|
|
188
103
|
watchers.delete(id);
|
|
189
104
|
watcherIconNames.delete(id);
|
|
190
105
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Schedule notification for changed icons (batched and deferred)
|
|
193
|
-
*/
|
|
194
106
|
function scheduleNotification(changedIcons) {
|
|
195
|
-
// Accumulate changed icons
|
|
196
107
|
if (!pendingNotifications) {
|
|
197
108
|
pendingNotifications = new Set();
|
|
198
109
|
}
|
|
199
110
|
changedIcons.forEach(name => pendingNotifications.add(name));
|
|
200
|
-
// Cancel existing timer
|
|
201
111
|
if (notificationTimer !== null) {
|
|
202
112
|
clearTimeout(notificationTimer);
|
|
203
113
|
}
|
|
204
|
-
// Schedule notification using requestIdleCallback or setTimeout
|
|
205
114
|
const scheduleCallback = typeof requestIdleCallback !== 'undefined'
|
|
206
115
|
? requestIdleCallback
|
|
207
116
|
: (cb) => setTimeout(cb, 0);
|
|
@@ -209,41 +118,27 @@ function scheduleNotification(changedIcons) {
|
|
|
209
118
|
const toNotify = pendingNotifications;
|
|
210
119
|
pendingNotifications = null;
|
|
211
120
|
notificationTimer = null;
|
|
212
|
-
// Only notify if we have icons to notify about
|
|
213
121
|
if (toNotify && toNotify.size > 0) {
|
|
214
122
|
notifyWatchers(toNotify);
|
|
215
123
|
}
|
|
216
124
|
});
|
|
217
125
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Notify watchers of changed icons (selective notification)
|
|
220
|
-
*/
|
|
221
126
|
function notifyWatchers(changedIcons) {
|
|
222
127
|
watchers.forEach((callback, watcherId) => {
|
|
223
128
|
const watchedIcon = watcherIconNames.get(watcherId);
|
|
224
|
-
// If watcher is watching a specific icon, only notify if that icon changed
|
|
225
129
|
if (watchedIcon) {
|
|
226
130
|
if (changedIcons.has(watchedIcon)) {
|
|
227
131
|
callback(new Set([watchedIcon]));
|
|
228
132
|
}
|
|
229
133
|
}
|
|
230
134
|
else {
|
|
231
|
-
// Watcher wants all changes
|
|
232
135
|
callback(changedIcons);
|
|
233
136
|
}
|
|
234
137
|
});
|
|
235
138
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Get all registered icon names (memory only, not cache)
|
|
238
|
-
* @returns Array of icon names
|
|
239
|
-
*/
|
|
240
139
|
export function getIconNames() {
|
|
241
140
|
return Array.from(iconRegistry.keys());
|
|
242
141
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Get cache statistics
|
|
245
|
-
* @returns Promise with cache info
|
|
246
|
-
*/
|
|
247
142
|
export async function getCacheInfo() {
|
|
248
143
|
const info = {
|
|
249
144
|
version: VERSION,
|
|
@@ -266,4 +161,3 @@ export async function getCacheInfo() {
|
|
|
266
161
|
return info;
|
|
267
162
|
}
|
|
268
163
|
}
|
|
269
|
-
//# sourceMappingURL=icon-registry.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared inline SVG icons for shadow-DOM components.
|
|
3
|
+
*
|
|
4
|
+
* Kept inline (not the icon registry) because shadow roots must render
|
|
5
|
+
* without registry setup. Styling hooks target the wrapper element
|
|
6
|
+
* (e.g. `.required-icon svg`), never the svg's own classes.
|
|
7
|
+
*/
|
|
8
|
+
/** Lucide `asterisk` — required-field indicator. */
|
|
9
|
+
export declare const REQUIRED_ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 6v12\"/><path d=\"M17.196 9 6.804 15\"/><path d=\"m6.804 9 10.392 6\"/></svg>";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const REQUIRED_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 6v12"/><path d="M17.196 9 6.804 15"/><path d="m6.804 9 10.392 6"/></svg>`;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Global loader-icon registry.
|
|
3
3
|
*
|
|
4
4
|
* Set once at app bootstrap to override the default spinner SVG used by
|
|
5
|
-
* every loading-aware component (`ty-button`, `ty-
|
|
6
|
-
*
|
|
5
|
+
* every loading-aware component (`ty-button`, `ty-select`, etc.). Components
|
|
6
|
+
* call `getLoaderSvg()` on every render, so changing the
|
|
7
7
|
* registered SVG affects future renders.
|
|
8
8
|
*
|
|
9
9
|
* The wrapper element rotates the SVG with `transform: rotate()`, so prefer
|
|
@@ -32,4 +32,3 @@ export declare function setLoaderSvg(svg: string | null): void;
|
|
|
32
32
|
export declare function getLoaderSvg(): string;
|
|
33
33
|
/** Reset the registered loader back to the built-in default. */
|
|
34
34
|
export declare function resetLoaderSvg(): void;
|
|
35
|
-
//# sourceMappingURL=loader-registry.d.ts.map
|
|
@@ -1,43 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global loader-icon registry.
|
|
3
|
-
*
|
|
4
|
-
* Set once at app bootstrap to override the default spinner SVG used by
|
|
5
|
-
* every loading-aware component (`ty-button`, `ty-dropdown`, `ty-multiselect`,
|
|
6
|
-
* etc.). Components call `getLoaderSvg()` on every render, so changing the
|
|
7
|
-
* registered SVG affects future renders.
|
|
8
|
-
*
|
|
9
|
-
* The wrapper element rotates the SVG with `transform: rotate()`, so prefer
|
|
10
|
-
* a *static* SVG. If you must use a self-animating SVG, disable the wrapper
|
|
11
|
-
* spin with the `--ty-loader-animation: none` CSS custom property.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* import { setLoaderSvg } from 'tyrell-components'
|
|
15
|
-
* setLoaderSvg('<svg viewBox="0 0 24 24">...</svg>')
|
|
16
|
-
*
|
|
17
|
-
* // CDN / vanilla
|
|
18
|
-
* window.tyLoader.set('<svg ...>...</svg>')
|
|
19
|
-
*
|
|
20
|
-
* // Reset to default
|
|
21
|
-
* setLoaderSvg(null)
|
|
22
|
-
*/
|
|
23
1
|
const DEFAULT_LOADER_SVG = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" stroke-opacity="0.25"/><path d="M22 12a10 10 0 0 1-10 10" stroke="currentColor" stroke-width="3" stroke-linecap="round"/></svg>`;
|
|
24
2
|
let customLoaderSvg = null;
|
|
25
|
-
/**
|
|
26
|
-
* Register a custom spinner SVG (markup string). Pass `null` to reset to
|
|
27
|
-
* the built-in default.
|
|
28
|
-
*/
|
|
29
3
|
export function setLoaderSvg(svg) {
|
|
30
4
|
customLoaderSvg = svg && svg.trim() ? svg : null;
|
|
31
5
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Get the currently registered spinner SVG, or the built-in default if
|
|
34
|
-
* none has been set. Components call this on every render.
|
|
35
|
-
*/
|
|
36
6
|
export function getLoaderSvg() {
|
|
37
7
|
return customLoaderSvg ?? DEFAULT_LOADER_SVG;
|
|
38
8
|
}
|
|
39
|
-
/** Reset the registered loader back to the built-in default. */
|
|
40
9
|
export function resetLoaderSvg() {
|
|
41
10
|
customLoaderSvg = null;
|
|
42
11
|
}
|
|
43
|
-
//# sourceMappingURL=loader-registry.js.map
|
package/lib/utils/locale.d.ts
CHANGED
|
@@ -1,28 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Locale Resolution Utilities
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 2. Closest ancestor's `lang` attribute
|
|
7
|
-
* 3. Document root's `lang` attribute
|
|
8
|
-
* 4. Browser's language preference
|
|
9
|
-
* 5. 'en-US' fallback (lowest priority)
|
|
10
|
-
*
|
|
11
|
-
* This allows developers to set locale at any level:
|
|
12
|
-
* - Per-component: <ty-input locale="fr-FR">
|
|
13
|
-
* - Per-section: <div lang="de-DE"><ty-input></ty-input></div>
|
|
14
|
-
* - Per-page: <html lang="es-ES">
|
|
15
|
-
* - Browser default: Uses navigator.language
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* // In a component
|
|
20
|
-
* class TyInput extends HTMLElement {
|
|
21
|
-
* get locale(): string {
|
|
22
|
-
* return getEffectiveLocale(this, this.getAttribute('locale'));
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
4
|
+
* Cascade for resolving locale/language preferences, so developers can set it
|
|
5
|
+
* per-component, per-section, per-page, or fall back to the browser default.
|
|
26
6
|
*/
|
|
27
7
|
/**
|
|
28
8
|
* Get the effective locale for an element using the resolution cascade.
|
|
@@ -76,4 +56,3 @@ export declare function getEffectiveLocale(element: HTMLElement, explicitLocale?
|
|
|
76
56
|
* ```
|
|
77
57
|
*/
|
|
78
58
|
export declare function observeLocaleChanges(element: HTMLElement, callback: (newLocale: string) => void): () => void;
|
|
79
|
-
//# sourceMappingURL=locale.d.ts.map
|
package/lib/utils/locale.js
CHANGED
|
@@ -1,106 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Locale Resolution Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides a cascade system for resolving locale/language preferences:
|
|
5
|
-
* 1. Component's explicit `locale` attribute (highest priority)
|
|
6
|
-
* 2. Closest ancestor's `lang` attribute
|
|
7
|
-
* 3. Document root's `lang` attribute
|
|
8
|
-
* 4. Browser's language preference
|
|
9
|
-
* 5. 'en-US' fallback (lowest priority)
|
|
10
|
-
*
|
|
11
|
-
* This allows developers to set locale at any level:
|
|
12
|
-
* - Per-component: <ty-input locale="fr-FR">
|
|
13
|
-
* - Per-section: <div lang="de-DE"><ty-input></ty-input></div>
|
|
14
|
-
* - Per-page: <html lang="es-ES">
|
|
15
|
-
* - Browser default: Uses navigator.language
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* // In a component
|
|
20
|
-
* class TyInput extends HTMLElement {
|
|
21
|
-
* get locale(): string {
|
|
22
|
-
* return getEffectiveLocale(this, this.getAttribute('locale'));
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* Get the effective locale for an element using the resolution cascade.
|
|
29
|
-
*
|
|
30
|
-
* Resolution order:
|
|
31
|
-
* 1. Explicit locale attribute on the element
|
|
32
|
-
* 2. Closest ancestor element with a `lang` attribute
|
|
33
|
-
* 3. Document root element's `lang` attribute
|
|
34
|
-
* 4. Browser's navigator.language
|
|
35
|
-
* 5. Fallback to 'en-US'
|
|
36
|
-
*
|
|
37
|
-
* @param element - The element to get locale for
|
|
38
|
-
* @param explicitLocale - Optional explicit locale value (from component's locale attribute)
|
|
39
|
-
* @returns The resolved locale string (e.g., 'en-US', 'fr-FR')
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```html
|
|
43
|
-
* <html lang="en-US">
|
|
44
|
-
* <div lang="fr-FR">
|
|
45
|
-
* <ty-input></ty-input> <!-- Uses 'fr-FR' -->
|
|
46
|
-
* <ty-input locale="de-DE"></ty-input> <!-- Uses 'de-DE' -->
|
|
47
|
-
* </div>
|
|
48
|
-
* <ty-input></ty-input> <!-- Uses 'en-US' -->
|
|
49
|
-
* </html>
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
1
|
export function getEffectiveLocale(element, explicitLocale) {
|
|
53
|
-
// 1. Explicit locale attribute takes highest priority
|
|
54
2
|
if (explicitLocale) {
|
|
55
3
|
return explicitLocale;
|
|
56
4
|
}
|
|
57
|
-
// 2. Check closest ancestor with lang attribute
|
|
58
|
-
// This allows per-section locale overrides
|
|
59
5
|
const langElement = element.closest('[lang]');
|
|
60
6
|
if (langElement) {
|
|
61
7
|
const lang = langElement.getAttribute('lang');
|
|
62
8
|
if (lang)
|
|
63
9
|
return lang;
|
|
64
10
|
}
|
|
65
|
-
// 3. Check document root (html element)
|
|
66
11
|
if (document.documentElement.lang) {
|
|
67
12
|
return document.documentElement.lang;
|
|
68
13
|
}
|
|
69
|
-
// 4. Use browser's language preference
|
|
70
14
|
if (navigator.language) {
|
|
71
15
|
return navigator.language;
|
|
72
16
|
}
|
|
73
|
-
// 5. Ultimate fallback
|
|
74
17
|
return 'en-US';
|
|
75
18
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Create a MutationObserver to watch for lang attribute changes.
|
|
78
|
-
* Useful for components that need to react to dynamic locale changes.
|
|
79
|
-
*
|
|
80
|
-
* @param element - The element to observe
|
|
81
|
-
* @param callback - Function to call when lang changes
|
|
82
|
-
* @returns Cleanup function to disconnect the observer
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```typescript
|
|
86
|
-
* class TyInput extends HTMLElement {
|
|
87
|
-
* private _localeObserver?: () => void;
|
|
88
|
-
*
|
|
89
|
-
* connectedCallback() {
|
|
90
|
-
* this._localeObserver = observeLocaleChanges(this, () => {
|
|
91
|
-
* this.render(); // Re-render when locale changes
|
|
92
|
-
* });
|
|
93
|
-
* }
|
|
94
|
-
*
|
|
95
|
-
* disconnectedCallback() {
|
|
96
|
-
* this._localeObserver?.();
|
|
97
|
-
* }
|
|
98
|
-
* }
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
19
|
export function observeLocaleChanges(element, callback) {
|
|
102
20
|
let currentLocale = getEffectiveLocale(element);
|
|
103
|
-
// Create observer for lang attribute changes on ancestors
|
|
104
21
|
const observer = new MutationObserver((mutations) => {
|
|
105
22
|
for (const mutation of mutations) {
|
|
106
23
|
if (mutation.type === 'attributes' && mutation.attributeName === 'lang') {
|
|
@@ -112,13 +29,10 @@ export function observeLocaleChanges(element, callback) {
|
|
|
112
29
|
}
|
|
113
30
|
}
|
|
114
31
|
});
|
|
115
|
-
// Observe document and all ancestors
|
|
116
32
|
observer.observe(document.documentElement, {
|
|
117
33
|
attributes: true,
|
|
118
34
|
attributeFilter: ['lang'],
|
|
119
35
|
subtree: true
|
|
120
36
|
});
|
|
121
|
-
// Return cleanup function
|
|
122
37
|
return () => observer.disconnect();
|
|
123
38
|
}
|
|
124
|
-
//# sourceMappingURL=locale.js.map
|
package/lib/utils/mobile.d.ts
CHANGED
package/lib/utils/mobile.js
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mobile detection utility
|
|
3
|
-
*
|
|
4
|
-
* Uses CSS media query via matchMedia for reliable detection:
|
|
5
|
-
* - `pointer: coarse` identifies touch-primary devices (not laptops with touchscreens)
|
|
6
|
-
* - Breakpoint is evaluated by the browser, always in sync with CSS
|
|
7
|
-
* - Called on each use (not cached), so it responds to viewport changes and rotation
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* Default mobile breakpoint (px).
|
|
11
|
-
* Matches phones in portrait and most phones in landscape.
|
|
12
|
-
*/
|
|
13
1
|
const MOBILE_BREAKPOINT = 768;
|
|
14
|
-
/**
|
|
15
|
-
* Detect mobile touch devices.
|
|
16
|
-
* Returns true when the primary pointer is coarse (touch) AND viewport is narrow.
|
|
17
|
-
*/
|
|
18
2
|
export function isMobileTouch(breakpoint = MOBILE_BREAKPOINT) {
|
|
19
3
|
return window.matchMedia(`(pointer: coarse) and (max-width: ${breakpoint}px)`).matches;
|
|
20
4
|
}
|
|
21
|
-
//# sourceMappingURL=mobile.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Number Formatting Utilities
|
|
3
|
-
* PORTED FROM: clj/ty/i18n/number.cljs
|
|
4
3
|
*
|
|
5
4
|
* Uses native Intl.NumberFormat for locale-aware number formatting
|
|
6
5
|
* Supports currency, percent, compact notation, and custom precision
|
|
@@ -34,6 +33,10 @@ export declare function formatNumber(value: number, config: FormatConfig): strin
|
|
|
34
33
|
* Simple rule: the last occurring . or , is the decimal separator.
|
|
35
34
|
* Everything else is stripped. This works for both US (1,234.56) and
|
|
36
35
|
* European (1.234,56) input, and for mobile keyboards that use , as decimal.
|
|
36
|
+
* One grouping exception: a repeated separator ("1.234.567") can't be a
|
|
37
|
+
* decimal point, so it's stripped as thousands grouping. A LONE separator is
|
|
38
|
+
* always decimal — "$1,234" pasted from formatted text parses as 1.234; this
|
|
39
|
+
* known ambiguity is resolved in favor of typed input ("12,50" → 12.5).
|
|
37
40
|
*
|
|
38
41
|
* @example
|
|
39
42
|
* ```typescript
|
|
@@ -42,7 +45,8 @@ export declare function formatNumber(value: number, config: FormatConfig): strin
|
|
|
42
45
|
* parseNumericValue("12,50") // 12.5 (last separator is ,)
|
|
43
46
|
* parseNumericValue("12.50") // 12.5 (last separator is .)
|
|
44
47
|
* parseNumericValue("1234") // 1234
|
|
45
|
-
* parseNumericValue("
|
|
48
|
+
* parseNumericValue("1.234.567") // 1234567 (repeated separator = grouping)
|
|
49
|
+
* parseNumericValue("$1,234") // 1.234 (lone separator = decimal; known paste ambiguity)
|
|
46
50
|
* parseNumericValue("15%") // 15
|
|
47
51
|
* parseNumericValue("") // null
|
|
48
52
|
* ```
|
|
@@ -59,4 +63,3 @@ export declare function parseNumericValue(value: string): number | null;
|
|
|
59
63
|
* ```
|
|
60
64
|
*/
|
|
61
65
|
export declare function shouldFormat(type: string): boolean;
|
|
62
|
-
//# sourceMappingURL=number-format.d.ts.map
|
|
@@ -1,34 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Number Formatting Utilities
|
|
3
|
-
* PORTED FROM: clj/ty/i18n/number.cljs
|
|
4
|
-
*
|
|
5
|
-
* Uses native Intl.NumberFormat for locale-aware number formatting
|
|
6
|
-
* Supports currency, percent, compact notation, and custom precision
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Format a number based on configuration
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* formatNumber(1234.56, { type: 'currency', currency: 'USD', locale: 'en-US' })
|
|
14
|
-
* // Returns: "$1,234.56"
|
|
15
|
-
*
|
|
16
|
-
* formatNumber(0.15, { type: 'percent', precision: 2 })
|
|
17
|
-
* // Returns: "15.00%"
|
|
18
|
-
*
|
|
19
|
-
* formatNumber(1234567, { type: 'compact' })
|
|
20
|
-
* // Returns: "1.2M"
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
1
|
export function formatNumber(value, config) {
|
|
24
2
|
const { type, locale = 'en-US', currency = 'USD', precision } = config;
|
|
25
3
|
const options = {};
|
|
26
|
-
// Add precision if specified
|
|
27
4
|
if (precision !== undefined) {
|
|
28
5
|
options.minimumFractionDigits = precision;
|
|
29
6
|
options.maximumFractionDigits = precision;
|
|
30
7
|
}
|
|
31
|
-
// Configure based on type
|
|
32
8
|
switch (type) {
|
|
33
9
|
case 'currency':
|
|
34
10
|
options.style = 'currency';
|
|
@@ -36,8 +12,6 @@ export function formatNumber(value, config) {
|
|
|
36
12
|
break;
|
|
37
13
|
case 'percent':
|
|
38
14
|
options.style = 'percent';
|
|
39
|
-
// Note: Intl.NumberFormat expects decimal (0.15 for 15%)
|
|
40
|
-
// We'll handle the division in the input component
|
|
41
15
|
break;
|
|
42
16
|
case 'compact':
|
|
43
17
|
options.notation = 'compact';
|
|
@@ -50,67 +24,37 @@ export function formatNumber(value, config) {
|
|
|
50
24
|
}
|
|
51
25
|
const formatter = new Intl.NumberFormat(locale, options);
|
|
52
26
|
const formatted = formatter.format(value);
|
|
53
|
-
// Normalize Unicode spaces for better HTML input compatibility
|
|
54
|
-
// Replace narrow no-break space (U+202F) and thin space (U+2009) with regular non-breaking space (U+00A0)
|
|
55
|
-
// This ensures proper rendering in HTML input elements
|
|
56
27
|
return formatted
|
|
57
|
-
.replace(/\u202F/g, '\u00A0')
|
|
58
|
-
.replace(/\u2009/g, '\u00A0');
|
|
28
|
+
.replace(/\u202F/g, '\u00A0')
|
|
29
|
+
.replace(/\u2009/g, '\u00A0');
|
|
59
30
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Parse a numeric string to a number
|
|
62
|
-
*
|
|
63
|
-
* Simple rule: the last occurring . or , is the decimal separator.
|
|
64
|
-
* Everything else is stripped. This works for both US (1,234.56) and
|
|
65
|
-
* European (1.234,56) input, and for mobile keyboards that use , as decimal.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```typescript
|
|
69
|
-
* parseNumericValue("1,234.56") // 1234.56 (last separator is .)
|
|
70
|
-
* parseNumericValue("1.234,56") // 1234.56 (last separator is ,)
|
|
71
|
-
* parseNumericValue("12,50") // 12.5 (last separator is ,)
|
|
72
|
-
* parseNumericValue("12.50") // 12.5 (last separator is .)
|
|
73
|
-
* parseNumericValue("1234") // 1234
|
|
74
|
-
* parseNumericValue("$1,234") // 1234
|
|
75
|
-
* parseNumericValue("15%") // 15
|
|
76
|
-
* parseNumericValue("") // null
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
31
|
export function parseNumericValue(value) {
|
|
80
32
|
if (!value || value.trim() === '')
|
|
81
33
|
return null;
|
|
82
|
-
// Strip everything except digits, dots, commas, and minus
|
|
83
34
|
let stripped = value
|
|
84
35
|
.replace(/[^\d.,-]/g, '')
|
|
85
36
|
.trim();
|
|
86
37
|
if (stripped === '' || stripped === '-')
|
|
87
38
|
return null;
|
|
88
|
-
// Find the last occurring . or , — that's the decimal separator
|
|
89
39
|
const lastComma = stripped.lastIndexOf(',');
|
|
90
40
|
const lastDot = stripped.lastIndexOf('.');
|
|
91
41
|
const lastSep = Math.max(lastComma, lastDot);
|
|
92
42
|
if (lastSep === -1) {
|
|
93
|
-
// No separators — pure integer
|
|
94
43
|
const parsed = parseFloat(stripped);
|
|
95
44
|
return isNaN(parsed) ? null : parsed;
|
|
96
45
|
}
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
46
|
+
const sepChar = stripped[lastSep];
|
|
47
|
+
const before = stripped.slice(0, lastSep);
|
|
48
|
+
const after = stripped.slice(lastSep + 1);
|
|
49
|
+
if (before.includes(sepChar)) {
|
|
50
|
+
const parsed = parseFloat(stripped.replace(/[.,]/g, ''));
|
|
51
|
+
return isNaN(parsed) ? null : parsed;
|
|
52
|
+
}
|
|
53
|
+
const intPart = before.replace(/[.,]/g, '');
|
|
54
|
+
const decPart = after;
|
|
100
55
|
const parsed = parseFloat(intPart + '.' + decPart);
|
|
101
56
|
return isNaN(parsed) ? null : parsed;
|
|
102
57
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Check if input type requires numeric formatting
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```typescript
|
|
108
|
-
* shouldFormat('currency') // true
|
|
109
|
-
* shouldFormat('percent') // true
|
|
110
|
-
* shouldFormat('text') // false
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
58
|
export function shouldFormat(type) {
|
|
114
59
|
return ['number', 'currency', 'percent', 'compact'].includes(type);
|
|
115
60
|
}
|
|
116
|
-
//# sourceMappingURL=number-format.js.map
|