tyrell-components 1.0.0-RC10
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/LICENSE +21 -0
- package/README.md +221 -0
- package/css/tyrell-brand.css +767 -0
- package/css/tyrell.css +1679 -0
- package/dist/tyrell-brand.css +767 -0
- package/dist/tyrell.css +1679 -0
- package/dist/tyrell.js +2 -0
- package/lib/base/ty-component.d.ts +133 -0
- package/lib/base/ty-component.d.ts.map +1 -0
- package/lib/base/ty-component.js +297 -0
- package/lib/base/ty-component.js.map +1 -0
- package/lib/components/button.d.ts +135 -0
- package/lib/components/button.d.ts.map +1 -0
- package/lib/components/button.js +277 -0
- package/lib/components/button.js.map +1 -0
- package/lib/components/calendar-month.d.ts +132 -0
- package/lib/components/calendar-month.d.ts.map +1 -0
- package/lib/components/calendar-month.js +440 -0
- package/lib/components/calendar-month.js.map +1 -0
- package/lib/components/calendar-navigation.d.ts +137 -0
- package/lib/components/calendar-navigation.d.ts.map +1 -0
- package/lib/components/calendar-navigation.js +366 -0
- package/lib/components/calendar-navigation.js.map +1 -0
- package/lib/components/calendar.d.ts +166 -0
- package/lib/components/calendar.d.ts.map +1 -0
- package/lib/components/calendar.js +774 -0
- package/lib/components/calendar.js.map +1 -0
- package/lib/components/checkbox.d.ts +189 -0
- package/lib/components/checkbox.d.ts.map +1 -0
- package/lib/components/checkbox.js +400 -0
- package/lib/components/checkbox.js.map +1 -0
- package/lib/components/copy.d.ts +180 -0
- package/lib/components/copy.d.ts.map +1 -0
- package/lib/components/copy.js +393 -0
- package/lib/components/copy.js.map +1 -0
- package/lib/components/date-picker.d.ts +379 -0
- package/lib/components/date-picker.d.ts.map +1 -0
- package/lib/components/date-picker.js +1586 -0
- package/lib/components/date-picker.js.map +1 -0
- package/lib/components/dropdown.d.ts +424 -0
- package/lib/components/dropdown.d.ts.map +1 -0
- package/lib/components/dropdown.js +1640 -0
- package/lib/components/dropdown.js.map +1 -0
- package/lib/components/file-upload.d.ts +121 -0
- package/lib/components/file-upload.d.ts.map +1 -0
- package/lib/components/file-upload.js +441 -0
- package/lib/components/file-upload.js.map +1 -0
- package/lib/components/icon.d.ts +118 -0
- package/lib/components/icon.d.ts.map +1 -0
- package/lib/components/icon.js +245 -0
- package/lib/components/icon.js.map +1 -0
- package/lib/components/input.d.ts +270 -0
- package/lib/components/input.d.ts.map +1 -0
- package/lib/components/input.js +721 -0
- package/lib/components/input.js.map +1 -0
- package/lib/components/modal.d.ts +78 -0
- package/lib/components/modal.d.ts.map +1 -0
- package/lib/components/modal.js +497 -0
- package/lib/components/modal.js.map +1 -0
- package/lib/components/multiselect.d.ts +397 -0
- package/lib/components/multiselect.d.ts.map +1 -0
- package/lib/components/multiselect.js +1595 -0
- package/lib/components/multiselect.js.map +1 -0
- package/lib/components/option.d.ts +66 -0
- package/lib/components/option.d.ts.map +1 -0
- package/lib/components/option.js +314 -0
- package/lib/components/option.js.map +1 -0
- package/lib/components/popup.d.ts +43 -0
- package/lib/components/popup.d.ts.map +1 -0
- package/lib/components/popup.js +380 -0
- package/lib/components/popup.js.map +1 -0
- package/lib/components/radio.d.ts +198 -0
- package/lib/components/radio.d.ts.map +1 -0
- package/lib/components/radio.js +437 -0
- package/lib/components/radio.js.map +1 -0
- package/lib/components/resize-observer.d.ts +48 -0
- package/lib/components/resize-observer.d.ts.map +1 -0
- package/lib/components/resize-observer.js +108 -0
- package/lib/components/resize-observer.js.map +1 -0
- package/lib/components/scroll-container.d.ts +51 -0
- package/lib/components/scroll-container.d.ts.map +1 -0
- package/lib/components/scroll-container.js +239 -0
- package/lib/components/scroll-container.js.map +1 -0
- package/lib/components/step.d.ts +26 -0
- package/lib/components/step.d.ts.map +1 -0
- package/lib/components/step.js +75 -0
- package/lib/components/step.js.map +1 -0
- package/lib/components/switch.d.ts +111 -0
- package/lib/components/switch.d.ts.map +1 -0
- package/lib/components/switch.js +240 -0
- package/lib/components/switch.js.map +1 -0
- package/lib/components/tab.d.ts +23 -0
- package/lib/components/tab.d.ts.map +1 -0
- package/lib/components/tab.js +76 -0
- package/lib/components/tab.js.map +1 -0
- package/lib/components/tabs.d.ts +93 -0
- package/lib/components/tabs.d.ts.map +1 -0
- package/lib/components/tabs.js +653 -0
- package/lib/components/tabs.js.map +1 -0
- package/lib/components/tag.d.ts +144 -0
- package/lib/components/tag.d.ts.map +1 -0
- package/lib/components/tag.js +316 -0
- package/lib/components/tag.js.map +1 -0
- package/lib/components/textarea.d.ts +241 -0
- package/lib/components/textarea.d.ts.map +1 -0
- package/lib/components/textarea.js +585 -0
- package/lib/components/textarea.js.map +1 -0
- package/lib/components/tooltip.d.ts +40 -0
- package/lib/components/tooltip.d.ts.map +1 -0
- package/lib/components/tooltip.js +439 -0
- package/lib/components/tooltip.js.map +1 -0
- package/lib/components/wizard.d.ts +86 -0
- package/lib/components/wizard.d.ts.map +1 -0
- package/lib/components/wizard.js +636 -0
- package/lib/components/wizard.js.map +1 -0
- package/lib/icons/fontawesome/brands.d.ts +557 -0
- package/lib/icons/fontawesome/brands.d.ts.map +1 -0
- package/lib/icons/fontawesome/brands.js +557 -0
- package/lib/icons/fontawesome/brands.js.map +1 -0
- package/lib/icons/fontawesome/regular.d.ts +281 -0
- package/lib/icons/fontawesome/regular.d.ts.map +1 -0
- package/lib/icons/fontawesome/regular.js +281 -0
- package/lib/icons/fontawesome/regular.js.map +1 -0
- package/lib/icons/fontawesome/solid.d.ts +1992 -0
- package/lib/icons/fontawesome/solid.d.ts.map +1 -0
- package/lib/icons/fontawesome/solid.js +1992 -0
- package/lib/icons/fontawesome/solid.js.map +1 -0
- package/lib/icons/heroicons/micro.d.ts +324 -0
- package/lib/icons/heroicons/micro.d.ts.map +1 -0
- package/lib/icons/heroicons/micro.js +1032 -0
- package/lib/icons/heroicons/micro.js.map +1 -0
- package/lib/icons/heroicons/mini.d.ts +332 -0
- package/lib/icons/heroicons/mini.d.ts.map +1 -0
- package/lib/icons/heroicons/mini.js +1038 -0
- package/lib/icons/heroicons/mini.js.map +1 -0
- package/lib/icons/heroicons/outline.d.ts +332 -0
- package/lib/icons/heroicons/outline.d.ts.map +1 -0
- package/lib/icons/heroicons/outline.js +993 -0
- package/lib/icons/heroicons/outline.js.map +1 -0
- package/lib/icons/heroicons/solid.d.ts +332 -0
- package/lib/icons/heroicons/solid.d.ts.map +1 -0
- package/lib/icons/heroicons/solid.js +1063 -0
- package/lib/icons/heroicons/solid.js.map +1 -0
- package/lib/icons/lucide.d.ts +1872 -0
- package/lib/icons/lucide.d.ts.map +1 -0
- package/lib/icons/lucide.js +28212 -0
- package/lib/icons/lucide.js.map +1 -0
- package/lib/icons/material/filled.d.ts +2180 -0
- package/lib/icons/material/filled.d.ts.map +1 -0
- package/lib/icons/material/filled.js +14003 -0
- package/lib/icons/material/filled.js.map +1 -0
- package/lib/icons/material/outlined.d.ts +2142 -0
- package/lib/icons/material/outlined.d.ts.map +1 -0
- package/lib/icons/material/outlined.js +14545 -0
- package/lib/icons/material/outlined.js.map +1 -0
- package/lib/icons/material/round.d.ts +2147 -0
- package/lib/icons/material/round.d.ts.map +1 -0
- package/lib/icons/material/round.js +14779 -0
- package/lib/icons/material/round.js.map +1 -0
- package/lib/icons/material/sharp.d.ts +2147 -0
- package/lib/icons/material/sharp.d.ts.map +1 -0
- package/lib/icons/material/sharp.js +14189 -0
- package/lib/icons/material/sharp.js.map +1 -0
- package/lib/icons/material/two-tone.d.ts +2185 -0
- package/lib/icons/material/two-tone.d.ts.map +1 -0
- package/lib/icons/material/two-tone.js +17152 -0
- package/lib/icons/material/two-tone.js.map +1 -0
- package/lib/index.d.ts +86 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +78 -0
- package/lib/index.js.map +1 -0
- package/lib/styles/button.d.ts +14 -0
- package/lib/styles/button.d.ts.map +1 -0
- package/lib/styles/button.js +498 -0
- package/lib/styles/button.js.map +1 -0
- package/lib/styles/calendar-month.d.ts +6 -0
- package/lib/styles/calendar-month.d.ts.map +1 -0
- package/lib/styles/calendar-month.js +275 -0
- package/lib/styles/calendar-month.js.map +1 -0
- package/lib/styles/calendar-navigation.d.ts +6 -0
- package/lib/styles/calendar-navigation.d.ts.map +1 -0
- package/lib/styles/calendar-navigation.js +143 -0
- package/lib/styles/calendar-navigation.js.map +1 -0
- package/lib/styles/calendar.d.ts +6 -0
- package/lib/styles/calendar.d.ts.map +1 -0
- package/lib/styles/calendar.js +28 -0
- package/lib/styles/calendar.js.map +1 -0
- package/lib/styles/checkbox.d.ts +9 -0
- package/lib/styles/checkbox.d.ts.map +1 -0
- package/lib/styles/checkbox.js +19 -0
- package/lib/styles/checkbox.js.map +1 -0
- package/lib/styles/copy.d.ts +7 -0
- package/lib/styles/copy.d.ts.map +1 -0
- package/lib/styles/copy.js +94 -0
- package/lib/styles/copy.js.map +1 -0
- package/lib/styles/custom-scrollbar.d.ts +6 -0
- package/lib/styles/custom-scrollbar.d.ts.map +1 -0
- package/lib/styles/custom-scrollbar.js +157 -0
- package/lib/styles/custom-scrollbar.js.map +1 -0
- package/lib/styles/date-picker.d.ts +6 -0
- package/lib/styles/date-picker.d.ts.map +1 -0
- package/lib/styles/date-picker.js +438 -0
- package/lib/styles/date-picker.js.map +1 -0
- package/lib/styles/dropdown.d.ts +12 -0
- package/lib/styles/dropdown.d.ts.map +1 -0
- package/lib/styles/dropdown.js +1081 -0
- package/lib/styles/dropdown.js.map +1 -0
- package/lib/styles/file-upload.d.ts +2 -0
- package/lib/styles/file-upload.d.ts.map +1 -0
- package/lib/styles/file-upload.js +241 -0
- package/lib/styles/file-upload.js.map +1 -0
- package/lib/styles/icon.d.ts +6 -0
- package/lib/styles/icon.d.ts.map +1 -0
- package/lib/styles/icon.js +241 -0
- package/lib/styles/icon.js.map +1 -0
- package/lib/styles/input.d.ts +7 -0
- package/lib/styles/input.d.ts.map +1 -0
- package/lib/styles/input.js +685 -0
- package/lib/styles/input.js.map +1 -0
- package/lib/styles/modal.d.ts +8 -0
- package/lib/styles/modal.d.ts.map +1 -0
- package/lib/styles/modal.js +134 -0
- package/lib/styles/modal.js.map +1 -0
- package/lib/styles/multiselect.d.ts +6 -0
- package/lib/styles/multiselect.d.ts.map +1 -0
- package/lib/styles/multiselect.js +825 -0
- package/lib/styles/multiselect.js.map +1 -0
- package/lib/styles/option.d.ts +6 -0
- package/lib/styles/option.d.ts.map +1 -0
- package/lib/styles/option.js +116 -0
- package/lib/styles/option.js.map +1 -0
- package/lib/styles/popup.d.ts +8 -0
- package/lib/styles/popup.d.ts.map +1 -0
- package/lib/styles/popup.js +95 -0
- package/lib/styles/popup.js.map +1 -0
- package/lib/styles/radio.d.ts +8 -0
- package/lib/styles/radio.d.ts.map +1 -0
- package/lib/styles/radio.js +160 -0
- package/lib/styles/radio.js.map +1 -0
- package/lib/styles/resize-observer.d.ts +6 -0
- package/lib/styles/resize-observer.d.ts.map +1 -0
- package/lib/styles/resize-observer.js +18 -0
- package/lib/styles/resize-observer.js.map +1 -0
- package/lib/styles/scroll-container.d.ts +6 -0
- package/lib/styles/scroll-container.d.ts.map +1 -0
- package/lib/styles/scroll-container.js +198 -0
- package/lib/styles/scroll-container.js.map +1 -0
- package/lib/styles/step.d.ts +5 -0
- package/lib/styles/step.d.ts.map +1 -0
- package/lib/styles/step.js +50 -0
- package/lib/styles/step.js.map +1 -0
- package/lib/styles/switch.d.ts +9 -0
- package/lib/styles/switch.d.ts.map +1 -0
- package/lib/styles/switch.js +100 -0
- package/lib/styles/switch.js.map +1 -0
- package/lib/styles/tab.d.ts +5 -0
- package/lib/styles/tab.d.ts.map +1 -0
- package/lib/styles/tab.js +51 -0
- package/lib/styles/tab.js.map +1 -0
- package/lib/styles/tabs.d.ts +13 -0
- package/lib/styles/tabs.d.ts.map +1 -0
- package/lib/styles/tabs.js +184 -0
- package/lib/styles/tabs.js.map +1 -0
- package/lib/styles/tag.d.ts +6 -0
- package/lib/styles/tag.d.ts.map +1 -0
- package/lib/styles/tag.js +409 -0
- package/lib/styles/tag.js.map +1 -0
- package/lib/styles/textarea.d.ts +6 -0
- package/lib/styles/textarea.d.ts.map +1 -0
- package/lib/styles/textarea.js +350 -0
- package/lib/styles/textarea.js.map +1 -0
- package/lib/styles/tooltip.d.ts +9 -0
- package/lib/styles/tooltip.d.ts.map +1 -0
- package/lib/styles/tooltip.js +133 -0
- package/lib/styles/tooltip.js.map +1 -0
- package/lib/styles/wizard.d.ts +25 -0
- package/lib/styles/wizard.d.ts.map +1 -0
- package/lib/styles/wizard.js +348 -0
- package/lib/styles/wizard.js.map +1 -0
- package/lib/types/common.d.ts +143 -0
- package/lib/types/common.d.ts.map +1 -0
- package/lib/types/common.js +5 -0
- package/lib/types/common.js.map +1 -0
- package/lib/utils/calendar-utils.d.ts +176 -0
- package/lib/utils/calendar-utils.d.ts.map +1 -0
- package/lib/utils/calendar-utils.js +370 -0
- package/lib/utils/calendar-utils.js.map +1 -0
- package/lib/utils/custom-scrollbar.d.ts +82 -0
- package/lib/utils/custom-scrollbar.d.ts.map +1 -0
- package/lib/utils/custom-scrollbar.js +320 -0
- package/lib/utils/custom-scrollbar.js.map +1 -0
- package/lib/utils/icon-registry.d.ts +78 -0
- package/lib/utils/icon-registry.d.ts.map +1 -0
- package/lib/utils/icon-registry.js +304 -0
- package/lib/utils/icon-registry.js.map +1 -0
- package/lib/utils/loader-registry.d.ts +35 -0
- package/lib/utils/loader-registry.d.ts.map +1 -0
- package/lib/utils/loader-registry.js +43 -0
- package/lib/utils/loader-registry.js.map +1 -0
- package/lib/utils/locale.d.ts +136 -0
- package/lib/utils/locale.d.ts.map +1 -0
- package/lib/utils/locale.js +213 -0
- package/lib/utils/locale.js.map +1 -0
- package/lib/utils/mobile.d.ts +14 -0
- package/lib/utils/mobile.d.ts.map +1 -0
- package/lib/utils/mobile.js +21 -0
- package/lib/utils/mobile.js.map +1 -0
- package/lib/utils/number-format.d.ts +83 -0
- package/lib/utils/number-format.d.ts.map +1 -0
- package/lib/utils/number-format.js +143 -0
- package/lib/utils/number-format.js.map +1 -0
- package/lib/utils/parse-boolean.d.ts +39 -0
- package/lib/utils/parse-boolean.d.ts.map +1 -0
- package/lib/utils/parse-boolean.js +58 -0
- package/lib/utils/parse-boolean.js.map +1 -0
- package/lib/utils/positioning.d.ts +143 -0
- package/lib/utils/positioning.d.ts.map +1 -0
- package/lib/utils/positioning.js +308 -0
- package/lib/utils/positioning.js.map +1 -0
- package/lib/utils/property-capture.d.ts +132 -0
- package/lib/utils/property-capture.d.ts.map +1 -0
- package/lib/utils/property-capture.js +152 -0
- package/lib/utils/property-capture.js.map +1 -0
- package/lib/utils/property-manager.d.ts +90 -0
- package/lib/utils/property-manager.d.ts.map +1 -0
- package/lib/utils/property-manager.js +197 -0
- package/lib/utils/property-manager.js.map +1 -0
- package/lib/utils/resize-observer.d.ts +42 -0
- package/lib/utils/resize-observer.d.ts.map +1 -0
- package/lib/utils/resize-observer.js +71 -0
- package/lib/utils/resize-observer.js.map +1 -0
- package/lib/utils/scroll-lock.d.ts +79 -0
- package/lib/utils/scroll-lock.d.ts.map +1 -0
- package/lib/utils/scroll-lock.js +197 -0
- package/lib/utils/scroll-lock.js.map +1 -0
- package/lib/utils/styles.d.ts +27 -0
- package/lib/utils/styles.d.ts.map +1 -0
- package/lib/utils/styles.js +53 -0
- package/lib/utils/styles.js.map +1 -0
- package/lib/version.d.ts +8 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +11 -0
- package/lib/version.js.map +1 -0
- package/package.json +163 -0
|
@@ -0,0 +1,213 @@
|
|
|
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
|
+
export function getEffectiveLocale(element, explicitLocale) {
|
|
53
|
+
// 1. Explicit locale attribute takes highest priority
|
|
54
|
+
if (explicitLocale) {
|
|
55
|
+
return explicitLocale;
|
|
56
|
+
}
|
|
57
|
+
// 2. Check closest ancestor with lang attribute
|
|
58
|
+
// This allows per-section locale overrides
|
|
59
|
+
const langElement = element.closest('[lang]');
|
|
60
|
+
if (langElement) {
|
|
61
|
+
const lang = langElement.getAttribute('lang');
|
|
62
|
+
if (lang)
|
|
63
|
+
return lang;
|
|
64
|
+
}
|
|
65
|
+
// 3. Check document root (html element)
|
|
66
|
+
if (document.documentElement.lang) {
|
|
67
|
+
return document.documentElement.lang;
|
|
68
|
+
}
|
|
69
|
+
// 4. Use browser's language preference
|
|
70
|
+
if (navigator.language) {
|
|
71
|
+
return navigator.language;
|
|
72
|
+
}
|
|
73
|
+
// 5. Ultimate fallback
|
|
74
|
+
return 'en-US';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Normalize a locale string to BCP 47 format.
|
|
78
|
+
* Handles common variations and ensures consistency.
|
|
79
|
+
*
|
|
80
|
+
* @param locale - The locale string to normalize
|
|
81
|
+
* @returns Normalized locale string
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* normalizeLocale('en') // 'en'
|
|
86
|
+
* normalizeLocale('en_US') // 'en-US'
|
|
87
|
+
* normalizeLocale('EN-us') // 'en-US'
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function normalizeLocale(locale) {
|
|
91
|
+
if (!locale)
|
|
92
|
+
return 'en-US';
|
|
93
|
+
// Replace underscores with hyphens (en_US -> en-US)
|
|
94
|
+
let normalized = locale.replace(/_/g, '-');
|
|
95
|
+
// Split into parts
|
|
96
|
+
const parts = normalized.split('-');
|
|
97
|
+
if (parts.length === 0)
|
|
98
|
+
return 'en-US';
|
|
99
|
+
// Lowercase language code (EN -> en)
|
|
100
|
+
parts[0] = parts[0].toLowerCase();
|
|
101
|
+
// Uppercase country code if present (us -> US)
|
|
102
|
+
if (parts.length > 1) {
|
|
103
|
+
parts[1] = parts[1].toUpperCase();
|
|
104
|
+
}
|
|
105
|
+
return parts.join('-');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if a locale string is valid BCP 47 format.
|
|
109
|
+
*
|
|
110
|
+
* @param locale - The locale string to validate
|
|
111
|
+
* @returns true if valid, false otherwise
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* isValidLocale('en-US') // true
|
|
116
|
+
* isValidLocale('fr') // true
|
|
117
|
+
* isValidLocale('invalid!') // false
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export function isValidLocale(locale) {
|
|
121
|
+
if (!locale)
|
|
122
|
+
return false;
|
|
123
|
+
// BCP 47 language tag pattern
|
|
124
|
+
// Simplified: allows language-region format
|
|
125
|
+
const pattern = /^[a-z]{2,3}(-[A-Z]{2})?$/;
|
|
126
|
+
return pattern.test(locale);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get the language code from a locale (e.g., 'en' from 'en-US').
|
|
130
|
+
*
|
|
131
|
+
* @param locale - The locale string
|
|
132
|
+
* @returns The language code
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* getLanguageCode('en-US') // 'en'
|
|
137
|
+
* getLanguageCode('fr-FR') // 'fr'
|
|
138
|
+
* getLanguageCode('de') // 'de'
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export function getLanguageCode(locale) {
|
|
142
|
+
if (!locale)
|
|
143
|
+
return 'en';
|
|
144
|
+
return locale.split('-')[0].toLowerCase();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the region/country code from a locale (e.g., 'US' from 'en-US').
|
|
148
|
+
*
|
|
149
|
+
* @param locale - The locale string
|
|
150
|
+
* @returns The region code, or undefined if not present
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* getRegionCode('en-US') // 'US'
|
|
155
|
+
* getRegionCode('fr-FR') // 'FR'
|
|
156
|
+
* getRegionCode('de') // undefined
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function getRegionCode(locale) {
|
|
160
|
+
if (!locale)
|
|
161
|
+
return undefined;
|
|
162
|
+
const parts = locale.split('-');
|
|
163
|
+
return parts.length > 1 ? parts[1].toUpperCase() : undefined;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create a MutationObserver to watch for lang attribute changes.
|
|
167
|
+
* Useful for components that need to react to dynamic locale changes.
|
|
168
|
+
*
|
|
169
|
+
* @param element - The element to observe
|
|
170
|
+
* @param callback - Function to call when lang changes
|
|
171
|
+
* @returns Cleanup function to disconnect the observer
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* class TyInput extends HTMLElement {
|
|
176
|
+
* private _localeObserver?: () => void;
|
|
177
|
+
*
|
|
178
|
+
* connectedCallback() {
|
|
179
|
+
* this._localeObserver = observeLocaleChanges(this, () => {
|
|
180
|
+
* this.render(); // Re-render when locale changes
|
|
181
|
+
* });
|
|
182
|
+
* }
|
|
183
|
+
*
|
|
184
|
+
* disconnectedCallback() {
|
|
185
|
+
* this._localeObserver?.();
|
|
186
|
+
* }
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export function observeLocaleChanges(element, callback) {
|
|
191
|
+
let currentLocale = getEffectiveLocale(element);
|
|
192
|
+
// Create observer for lang attribute changes on ancestors
|
|
193
|
+
const observer = new MutationObserver((mutations) => {
|
|
194
|
+
for (const mutation of mutations) {
|
|
195
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'lang') {
|
|
196
|
+
const newLocale = getEffectiveLocale(element);
|
|
197
|
+
if (newLocale !== currentLocale) {
|
|
198
|
+
currentLocale = newLocale;
|
|
199
|
+
callback(newLocale);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
// Observe document and all ancestors
|
|
205
|
+
observer.observe(document.documentElement, {
|
|
206
|
+
attributes: true,
|
|
207
|
+
attributeFilter: ['lang'],
|
|
208
|
+
subtree: true
|
|
209
|
+
});
|
|
210
|
+
// Return cleanup function
|
|
211
|
+
return () => observer.disconnect();
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=locale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale.js","sourceRoot":"","sources":["../../src/utils/locale.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
* Detect mobile touch devices.
|
|
11
|
+
* Returns true when the primary pointer is coarse (touch) AND viewport is narrow.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isMobileTouch(breakpoint?: number): boolean;
|
|
14
|
+
//# sourceMappingURL=mobile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../../src/utils/mobile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,UAAU,GAAE,MAA0B,GAAG,OAAO,CAE7E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
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
|
+
export function isMobileTouch(breakpoint = MOBILE_BREAKPOINT) {
|
|
19
|
+
return window.matchMedia(`(pointer: coarse) and (max-width: ${breakpoint}px)`).matches;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=mobile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../src/utils/mobile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,aAAqB,iBAAiB;IAClE,OAAO,MAAM,CAAC,UAAU,CAAC,qCAAqC,UAAU,KAAK,CAAC,CAAC,OAAO,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
export type NumberFormatType = 'number' | 'currency' | 'percent' | 'compact';
|
|
9
|
+
export interface FormatConfig {
|
|
10
|
+
type: NumberFormatType;
|
|
11
|
+
locale?: string;
|
|
12
|
+
currency?: string;
|
|
13
|
+
precision?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format a number based on configuration
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* formatNumber(1234.56, { type: 'currency', currency: 'USD', locale: 'en-US' })
|
|
21
|
+
* // Returns: "$1,234.56"
|
|
22
|
+
*
|
|
23
|
+
* formatNumber(0.15, { type: 'percent', precision: 2 })
|
|
24
|
+
* // Returns: "15.00%"
|
|
25
|
+
*
|
|
26
|
+
* formatNumber(1234567, { type: 'compact' })
|
|
27
|
+
* // Returns: "1.2M"
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatNumber(value: number, config: FormatConfig): string;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a numeric string to a number
|
|
33
|
+
*
|
|
34
|
+
* Simple rule: the last occurring . or , is the decimal separator.
|
|
35
|
+
* Everything else is stripped. This works for both US (1,234.56) and
|
|
36
|
+
* European (1.234,56) input, and for mobile keyboards that use , as decimal.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* parseNumericValue("1,234.56") // 1234.56 (last separator is .)
|
|
41
|
+
* parseNumericValue("1.234,56") // 1234.56 (last separator is ,)
|
|
42
|
+
* parseNumericValue("12,50") // 12.5 (last separator is ,)
|
|
43
|
+
* parseNumericValue("12.50") // 12.5 (last separator is .)
|
|
44
|
+
* parseNumericValue("1234") // 1234
|
|
45
|
+
* parseNumericValue("$1,234") // 1234
|
|
46
|
+
* parseNumericValue("15%") // 15
|
|
47
|
+
* parseNumericValue("") // null
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseNumericValue(value: string): number | null;
|
|
51
|
+
/**
|
|
52
|
+
* Check if input type requires numeric formatting
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* shouldFormat('currency') // true
|
|
57
|
+
* shouldFormat('percent') // true
|
|
58
|
+
* shouldFormat('text') // false
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function shouldFormat(type: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Format currency with locale
|
|
64
|
+
* Convenience function for currency formatting
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatCurrency(value: number, currency?: string, locale?: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Format percent with locale
|
|
69
|
+
* Convenience function for percent formatting
|
|
70
|
+
*
|
|
71
|
+
* Note: Expects value as 0.15 for 15%
|
|
72
|
+
*/
|
|
73
|
+
export declare function formatPercent(value: number, locale?: string, precision?: number): string;
|
|
74
|
+
/**
|
|
75
|
+
* Format compact notation
|
|
76
|
+
* Convenience function for compact formatting
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* formatCompact(1234567) // "1.2M"
|
|
80
|
+
* formatCompact(1234) // "1.2K"
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatCompact(value: number, locale?: string): string;
|
|
83
|
+
//# sourceMappingURL=number-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-format.d.ts","sourceRoot":"","sources":["../../src/utils/number-format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAE5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,YAAY,GACnB,MAAM,CA4CR;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2B9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAc,EACxB,MAAM,GAAE,MAAgB,GACvB,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAgB,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAER;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAgB,GACvB,MAAM,CAER"}
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
export function formatNumber(value, config) {
|
|
24
|
+
const { type, locale = 'en-US', currency = 'USD', precision } = config;
|
|
25
|
+
const options = {};
|
|
26
|
+
// Add precision if specified
|
|
27
|
+
if (precision !== undefined) {
|
|
28
|
+
options.minimumFractionDigits = precision;
|
|
29
|
+
options.maximumFractionDigits = precision;
|
|
30
|
+
}
|
|
31
|
+
// Configure based on type
|
|
32
|
+
switch (type) {
|
|
33
|
+
case 'currency':
|
|
34
|
+
options.style = 'currency';
|
|
35
|
+
options.currency = currency;
|
|
36
|
+
break;
|
|
37
|
+
case 'percent':
|
|
38
|
+
options.style = 'percent';
|
|
39
|
+
// Note: Intl.NumberFormat expects decimal (0.15 for 15%)
|
|
40
|
+
// We'll handle the division in the input component
|
|
41
|
+
break;
|
|
42
|
+
case 'compact':
|
|
43
|
+
options.notation = 'compact';
|
|
44
|
+
options.compactDisplay = 'short';
|
|
45
|
+
break;
|
|
46
|
+
case 'number':
|
|
47
|
+
default:
|
|
48
|
+
options.style = 'decimal';
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
const formatter = new Intl.NumberFormat(locale, options);
|
|
52
|
+
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
|
+
return formatted
|
|
57
|
+
.replace(/\u202F/g, '\u00A0') // Narrow no-break space → non-breaking space
|
|
58
|
+
.replace(/\u2009/g, '\u00A0'); // Thin space → non-breaking space
|
|
59
|
+
}
|
|
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
|
+
export function parseNumericValue(value) {
|
|
80
|
+
if (!value || value.trim() === '')
|
|
81
|
+
return null;
|
|
82
|
+
// Strip everything except digits, dots, commas, and minus
|
|
83
|
+
let stripped = value
|
|
84
|
+
.replace(/[^\d.,-]/g, '')
|
|
85
|
+
.trim();
|
|
86
|
+
if (stripped === '' || stripped === '-')
|
|
87
|
+
return null;
|
|
88
|
+
// Find the last occurring . or , — that's the decimal separator
|
|
89
|
+
const lastComma = stripped.lastIndexOf(',');
|
|
90
|
+
const lastDot = stripped.lastIndexOf('.');
|
|
91
|
+
const lastSep = Math.max(lastComma, lastDot);
|
|
92
|
+
if (lastSep === -1) {
|
|
93
|
+
// No separators — pure integer
|
|
94
|
+
const parsed = parseFloat(stripped);
|
|
95
|
+
return isNaN(parsed) ? null : parsed;
|
|
96
|
+
}
|
|
97
|
+
// Split at the last separator
|
|
98
|
+
const intPart = stripped.slice(0, lastSep).replace(/[.,]/g, '');
|
|
99
|
+
const decPart = stripped.slice(lastSep + 1);
|
|
100
|
+
const parsed = parseFloat(intPart + '.' + decPart);
|
|
101
|
+
return isNaN(parsed) ? null : parsed;
|
|
102
|
+
}
|
|
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
|
+
export function shouldFormat(type) {
|
|
114
|
+
return ['number', 'currency', 'percent', 'compact'].includes(type);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Format currency with locale
|
|
118
|
+
* Convenience function for currency formatting
|
|
119
|
+
*/
|
|
120
|
+
export function formatCurrency(value, currency = 'USD', locale = 'en-US') {
|
|
121
|
+
return formatNumber(value, { type: 'currency', currency, locale });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Format percent with locale
|
|
125
|
+
* Convenience function for percent formatting
|
|
126
|
+
*
|
|
127
|
+
* Note: Expects value as 0.15 for 15%
|
|
128
|
+
*/
|
|
129
|
+
export function formatPercent(value, locale = 'en-US', precision) {
|
|
130
|
+
return formatNumber(value, { type: 'percent', locale, precision });
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Format compact notation
|
|
134
|
+
* Convenience function for compact formatting
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* formatCompact(1234567) // "1.2M"
|
|
138
|
+
* formatCompact(1234) // "1.2K"
|
|
139
|
+
*/
|
|
140
|
+
export function formatCompact(value, locale = 'en-US') {
|
|
141
|
+
return formatNumber(value, { type: 'compact', locale });
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=number-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-format.js","sourceRoot":"","sources":["../../src/utils/number-format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a value as a boolean following web standards
|
|
3
|
+
*
|
|
4
|
+
* This utility helps web components handle boolean attributes consistently:
|
|
5
|
+
* - HTML5 boolean attributes: presence = true, absence = false, empty string = true
|
|
6
|
+
* - Framework boolean values: true/false/"true"/"false"
|
|
7
|
+
* - Numeric boolean values: 1/0/"1"/"0"
|
|
8
|
+
*
|
|
9
|
+
* @param value - The attribute value to parse
|
|
10
|
+
* @returns boolean - The parsed boolean value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* parseBoolean(null) // false - attribute not present
|
|
14
|
+
* parseBoolean('') // true - HTML boolean attribute (present but empty)
|
|
15
|
+
* parseBoolean('true') // true
|
|
16
|
+
* parseBoolean('false') // false
|
|
17
|
+
* parseBoolean('TRUE') // true - case insensitive
|
|
18
|
+
* parseBoolean('1') // true
|
|
19
|
+
* parseBoolean('0') // false
|
|
20
|
+
* parseBoolean('yes') // true - any other non-empty string
|
|
21
|
+
* parseBoolean('anything') // true - truthy by default unless explicitly false
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseBoolean(value: string | null): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a value looks like a boolean string
|
|
26
|
+
* Used to determine if an attribute should be treated as a boolean
|
|
27
|
+
*
|
|
28
|
+
* @param value - The value to check
|
|
29
|
+
* @returns boolean - True if value represents a boolean
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* isBooleanString('true') // true
|
|
33
|
+
* isBooleanString('false') // true
|
|
34
|
+
* isBooleanString('1') // true
|
|
35
|
+
* isBooleanString('0') // true
|
|
36
|
+
* isBooleanString('custom') // false
|
|
37
|
+
*/
|
|
38
|
+
export declare function isBooleanString(value: string | null): boolean;
|
|
39
|
+
//# sourceMappingURL=parse-boolean.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-boolean.d.ts","sourceRoot":"","sources":["../../src/utils/parse-boolean.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAe1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAI7D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a value as a boolean following web standards
|
|
3
|
+
*
|
|
4
|
+
* This utility helps web components handle boolean attributes consistently:
|
|
5
|
+
* - HTML5 boolean attributes: presence = true, absence = false, empty string = true
|
|
6
|
+
* - Framework boolean values: true/false/"true"/"false"
|
|
7
|
+
* - Numeric boolean values: 1/0/"1"/"0"
|
|
8
|
+
*
|
|
9
|
+
* @param value - The attribute value to parse
|
|
10
|
+
* @returns boolean - The parsed boolean value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* parseBoolean(null) // false - attribute not present
|
|
14
|
+
* parseBoolean('') // true - HTML boolean attribute (present but empty)
|
|
15
|
+
* parseBoolean('true') // true
|
|
16
|
+
* parseBoolean('false') // false
|
|
17
|
+
* parseBoolean('TRUE') // true - case insensitive
|
|
18
|
+
* parseBoolean('1') // true
|
|
19
|
+
* parseBoolean('0') // false
|
|
20
|
+
* parseBoolean('yes') // true - any other non-empty string
|
|
21
|
+
* parseBoolean('anything') // true - truthy by default unless explicitly false
|
|
22
|
+
*/
|
|
23
|
+
export function parseBoolean(value) {
|
|
24
|
+
// No attribute or null = false (HTML standard)
|
|
25
|
+
if (value === null)
|
|
26
|
+
return false;
|
|
27
|
+
// Empty string = true (HTML boolean attribute pattern: <input checked>)
|
|
28
|
+
if (value === '')
|
|
29
|
+
return true;
|
|
30
|
+
// Normalize to lowercase for case-insensitive comparison
|
|
31
|
+
const normalized = value.toLowerCase().trim();
|
|
32
|
+
// Explicit false values
|
|
33
|
+
if (normalized === 'false' || normalized === '0')
|
|
34
|
+
return false;
|
|
35
|
+
// Everything else is truthy (follows HTML/JavaScript conventions)
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a value looks like a boolean string
|
|
40
|
+
* Used to determine if an attribute should be treated as a boolean
|
|
41
|
+
*
|
|
42
|
+
* @param value - The value to check
|
|
43
|
+
* @returns boolean - True if value represents a boolean
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* isBooleanString('true') // true
|
|
47
|
+
* isBooleanString('false') // true
|
|
48
|
+
* isBooleanString('1') // true
|
|
49
|
+
* isBooleanString('0') // true
|
|
50
|
+
* isBooleanString('custom') // false
|
|
51
|
+
*/
|
|
52
|
+
export function isBooleanString(value) {
|
|
53
|
+
if (value === null || value === '')
|
|
54
|
+
return false;
|
|
55
|
+
const normalized = value.toLowerCase().trim();
|
|
56
|
+
return ['true', 'false', '1', '0'].includes(normalized);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=parse-boolean.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-boolean.js","sourceRoot":"","sources":["../../src/utils/parse-boolean.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAoB;IAC/C,+CAA+C;IAC/C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAEhC,wEAAwE;IACxE,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IAE7B,yDAAyD;IACzD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAA;IAE7C,wBAAwB;IACxB,IAAI,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,KAAK,CAAA;IAE9D,kEAAkE;IAClE,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,KAAK,CAAA;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAA;IAC7C,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;AACzD,CAAC"}
|