@leavepulse/ui 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/component-names.d.ts +1 -0
  4. package/dist/components/LayoutCanvas.vue.d.ts +28 -0
  5. package/dist/components/LayoutNode.vue.d.ts +40 -0
  6. package/dist/components/LpAlert.vue.d.ts +44 -0
  7. package/dist/components/LpAppShell.vue.d.ts +83 -0
  8. package/dist/components/LpAutocomplete.vue.d.ts +50 -0
  9. package/dist/components/LpAvatar.vue.d.ts +20 -0
  10. package/dist/components/LpBadge.vue.d.ts +47 -0
  11. package/dist/components/LpBreadcrumbs.vue.d.ts +14 -0
  12. package/dist/components/LpButton.vue.d.ts +113 -0
  13. package/dist/components/LpCalendar.vue.d.ts +20 -0
  14. package/dist/components/LpCard.vue.d.ts +29 -0
  15. package/dist/components/LpCheckbox.vue.d.ts +28 -0
  16. package/dist/components/LpCodeBlock.vue.d.ts +39 -0
  17. package/dist/components/LpCommandPalette.vue.d.ts +42 -0
  18. package/dist/components/LpConfirmDialog.vue.d.ts +32 -0
  19. package/dist/components/LpContextMenu.vue.d.ts +30 -0
  20. package/dist/components/LpDatePicker.vue.d.ts +25 -0
  21. package/dist/components/LpDisclosure.vue.d.ts +37 -0
  22. package/dist/components/LpDivider.vue.d.ts +17 -0
  23. package/dist/components/LpDrawer.vue.d.ts +88 -0
  24. package/dist/components/LpDropdownMenu.vue.d.ts +23 -0
  25. package/dist/components/LpEmptyState.vue.d.ts +18 -0
  26. package/dist/components/LpFormField.vue.d.ts +21 -0
  27. package/dist/components/LpIcon.vue.d.ts +8 -0
  28. package/dist/components/LpInfraNode.vue.d.ts +21 -0
  29. package/dist/components/LpInput.vue.d.ts +68 -0
  30. package/dist/components/LpLaneNode.vue.d.ts +24 -0
  31. package/dist/components/LpLink.vue.d.ts +27 -0
  32. package/dist/components/LpLogViewer.vue.d.ts +92 -0
  33. package/dist/components/LpModal.vue.d.ts +43 -0
  34. package/dist/components/LpNotificationBell.vue.d.ts +51 -0
  35. package/dist/components/LpNumberField.vue.d.ts +14 -0
  36. package/dist/components/LpOtpInput.vue.d.ts +27 -0
  37. package/dist/components/LpPagination.vue.d.ts +25 -0
  38. package/dist/components/LpPasswordInput.vue.d.ts +28 -0
  39. package/dist/components/LpPhoneInput.vue.d.ts +37 -0
  40. package/dist/components/LpPopover.vue.d.ts +34 -0
  41. package/dist/components/LpProgress.vue.d.ts +12 -0
  42. package/dist/components/LpRadio.vue.d.ts +18 -0
  43. package/dist/components/LpRadioGroup.vue.d.ts +26 -0
  44. package/dist/components/LpScrollArea.vue.d.ts +24 -0
  45. package/dist/components/LpSegmented.vue.d.ts +24 -0
  46. package/dist/components/LpSelect.vue.d.ts +45 -0
  47. package/dist/components/LpServiceNode.vue.d.ts +14 -0
  48. package/dist/components/LpSidebar.vue.d.ts +85 -0
  49. package/dist/components/LpSidebarNav.vue.d.ts +34 -0
  50. package/dist/components/LpSkeleton.vue.d.ts +6 -0
  51. package/dist/components/LpSlider.vue.d.ts +23 -0
  52. package/dist/components/LpStat.vue.d.ts +22 -0
  53. package/dist/components/LpStepper.vue.d.ts +10 -0
  54. package/dist/components/LpSwitch.vue.d.ts +11 -0
  55. package/dist/components/LpTable.vue.d.ts +63 -0
  56. package/dist/components/LpTableOfContents.vue.d.ts +19 -0
  57. package/dist/components/LpTabs.vue.d.ts +45 -0
  58. package/dist/components/LpTextarea.vue.d.ts +17 -0
  59. package/dist/components/LpThemeSwitcher.vue.d.ts +40 -0
  60. package/dist/components/LpToaster.vue.d.ts +3 -0
  61. package/dist/components/LpTooltip.vue.d.ts +19 -0
  62. package/dist/components/LpTopologyCanvas.vue.d.ts +154 -0
  63. package/dist/components/LpUptimeBar.vue.d.ts +30 -0
  64. package/dist/components/codeHighlight.d.ts +8 -0
  65. package/dist/components/countries-data.d.ts +2 -0
  66. package/dist/components/countries.d.ts +20 -0
  67. package/dist/components/dropdown.d.ts +29 -0
  68. package/dist/components/sidebar.d.ts +18 -0
  69. package/dist/composables/useInputFilter.d.ts +14 -0
  70. package/dist/composables/usePillTransition.d.ts +9 -0
  71. package/dist/composables/useToast.d.ts +39 -0
  72. package/dist/countries-data-9rgqJWHo.js +203 -0
  73. package/dist/icons.d.ts +1 -0
  74. package/dist/index.css +1 -0
  75. package/dist/index.d.ts +100 -0
  76. package/dist/index.js +17600 -0
  77. package/dist/layout/registry.d.ts +26 -0
  78. package/dist/layout/tree.d.ts +75 -0
  79. package/dist/layout/useLayout.d.ts +39 -0
  80. package/dist/theme/config.d.ts +16 -0
  81. package/dist/theme/define.d.ts +12 -0
  82. package/dist/theme/presets.d.ts +20 -0
  83. package/dist/theme/tokens.d.ts +72 -0
  84. package/dist/theme/useTheme.d.ts +36 -0
  85. package/package.json +93 -0
  86. package/src/canvas.css +67 -0
  87. package/src/component-names.ts +65 -0
  88. package/src/components/LayoutCanvas.vue +76 -0
  89. package/src/components/LayoutNode.vue +248 -0
  90. package/src/components/LpAlert.vue +59 -0
  91. package/src/components/LpAppShell.vue +210 -0
  92. package/src/components/LpAutocomplete.vue +244 -0
  93. package/src/components/LpAvatar.vue +67 -0
  94. package/src/components/LpBadge.vue +32 -0
  95. package/src/components/LpBreadcrumbs.vue +35 -0
  96. package/src/components/LpButton.vue +111 -0
  97. package/src/components/LpCalendar.vue +134 -0
  98. package/src/components/LpCard.vue +71 -0
  99. package/src/components/LpCheckbox.vue +44 -0
  100. package/src/components/LpCodeBlock.vue +298 -0
  101. package/src/components/LpCommandPalette.vue +323 -0
  102. package/src/components/LpConfirmDialog.vue +49 -0
  103. package/src/components/LpContextMenu.vue +111 -0
  104. package/src/components/LpDatePicker.vue +117 -0
  105. package/src/components/LpDisclosure.vue +92 -0
  106. package/src/components/LpDivider.vue +20 -0
  107. package/src/components/LpDrawer.vue +402 -0
  108. package/src/components/LpDropdownMenu.vue +54 -0
  109. package/src/components/LpEmptyState.vue +21 -0
  110. package/src/components/LpFormField.vue +31 -0
  111. package/src/components/LpIcon.vue +49 -0
  112. package/src/components/LpInfraNode.vue +108 -0
  113. package/src/components/LpInput.vue +147 -0
  114. package/src/components/LpLaneNode.vue +48 -0
  115. package/src/components/LpLink.vue +48 -0
  116. package/src/components/LpLogViewer.vue +569 -0
  117. package/src/components/LpModal.vue +123 -0
  118. package/src/components/LpNotificationBell.vue +238 -0
  119. package/src/components/LpNumberField.vue +56 -0
  120. package/src/components/LpOtpInput.vue +81 -0
  121. package/src/components/LpPagination.vue +165 -0
  122. package/src/components/LpPasswordInput.vue +50 -0
  123. package/src/components/LpPhoneInput.vue +241 -0
  124. package/src/components/LpPopover.vue +42 -0
  125. package/src/components/LpProgress.vue +30 -0
  126. package/src/components/LpRadio.vue +28 -0
  127. package/src/components/LpRadioGroup.vue +43 -0
  128. package/src/components/LpScrollArea.vue +82 -0
  129. package/src/components/LpSegmented.vue +83 -0
  130. package/src/components/LpSelect.vue +0 -0
  131. package/src/components/LpServiceNode.vue +57 -0
  132. package/src/components/LpSidebar.vue +200 -0
  133. package/src/components/LpSidebarNav.vue +118 -0
  134. package/src/components/LpSkeleton.vue +16 -0
  135. package/src/components/LpSlider.vue +53 -0
  136. package/src/components/LpStat.vue +90 -0
  137. package/src/components/LpStepper.vue +46 -0
  138. package/src/components/LpSwitch.vue +19 -0
  139. package/src/components/LpTable.vue +252 -0
  140. package/src/components/LpTableOfContents.vue +183 -0
  141. package/src/components/LpTabs.vue +120 -0
  142. package/src/components/LpTextarea.vue +39 -0
  143. package/src/components/LpThemeSwitcher.vue +168 -0
  144. package/src/components/LpToaster.vue +216 -0
  145. package/src/components/LpTooltip.vue +30 -0
  146. package/src/components/LpTopologyCanvas.vue +370 -0
  147. package/src/components/LpUptimeBar.vue +126 -0
  148. package/src/components/codeHighlight.ts +137 -0
  149. package/src/components/countries-data.ts +207 -0
  150. package/src/components/countries.ts +67 -0
  151. package/src/components/dropdown.ts +64 -0
  152. package/src/components/sidebar.ts +26 -0
  153. package/src/composables/useInputFilter.ts +89 -0
  154. package/src/composables/usePillTransition.ts +19 -0
  155. package/src/composables/useToast.ts +92 -0
  156. package/src/icons.ts +11 -0
  157. package/src/index.css +21 -0
  158. package/src/index.ts +146 -0
  159. package/src/layout/registry.ts +44 -0
  160. package/src/layout/tree.ts +288 -0
  161. package/src/layout/useLayout.ts +134 -0
  162. package/src/nuxt.ts +22 -0
  163. package/src/theme/config.ts +48 -0
  164. package/src/theme/define.ts +50 -0
  165. package/src/theme/presets.ts +279 -0
  166. package/src/theme/tokens.ts +151 -0
  167. package/src/theme/useTheme.ts +288 -0
  168. package/src/tokens/scrollbar.css +138 -0
  169. package/src/tokens/tokens.css +333 -0
  170. package/src/tokens/utilities.css +23 -0
@@ -0,0 +1,207 @@
1
+ import type { Country } from "./countries"
2
+
3
+ /*
4
+ * Country dial-code data — split out from countries.ts so it can be code-split
5
+ * into its own chunk and loaded lazily by LpPhoneInput (see loadCountries).
6
+ * E.164 calling codes, sorted by name. Not a substitute for full phone parsing.
7
+ */
8
+ export const COUNTRIES: Country[] = [
9
+ { iso2: "AF", name: "Afghanistan", dial: "93" },
10
+ { iso2: "AL", name: "Albania", dial: "355" },
11
+ { iso2: "DZ", name: "Algeria", dial: "213" },
12
+ { iso2: "AD", name: "Andorra", dial: "376" },
13
+ { iso2: "AO", name: "Angola", dial: "244" },
14
+ { iso2: "AG", name: "Antigua and Barbuda", dial: "1268" },
15
+ { iso2: "AR", name: "Argentina", dial: "54" },
16
+ { iso2: "AM", name: "Armenia", dial: "374" },
17
+ { iso2: "AU", name: "Australia", dial: "61" },
18
+ { iso2: "AT", name: "Austria", dial: "43" },
19
+ { iso2: "AZ", name: "Azerbaijan", dial: "994" },
20
+ { iso2: "BS", name: "Bahamas", dial: "1242" },
21
+ { iso2: "BH", name: "Bahrain", dial: "973" },
22
+ { iso2: "BD", name: "Bangladesh", dial: "880" },
23
+ { iso2: "BB", name: "Barbados", dial: "1246" },
24
+ { iso2: "BY", name: "Belarus", dial: "375" },
25
+ { iso2: "BE", name: "Belgium", dial: "32" },
26
+ { iso2: "BZ", name: "Belize", dial: "501" },
27
+ { iso2: "BJ", name: "Benin", dial: "229" },
28
+ { iso2: "BT", name: "Bhutan", dial: "975" },
29
+ { iso2: "BO", name: "Bolivia", dial: "591" },
30
+ { iso2: "BA", name: "Bosnia and Herzegovina", dial: "387" },
31
+ { iso2: "BW", name: "Botswana", dial: "267" },
32
+ { iso2: "BR", name: "Brazil", dial: "55" },
33
+ { iso2: "BN", name: "Brunei", dial: "673" },
34
+ { iso2: "BG", name: "Bulgaria", dial: "359" },
35
+ { iso2: "BF", name: "Burkina Faso", dial: "226" },
36
+ { iso2: "BI", name: "Burundi", dial: "257" },
37
+ { iso2: "KH", name: "Cambodia", dial: "855" },
38
+ { iso2: "CM", name: "Cameroon", dial: "237" },
39
+ { iso2: "CA", name: "Canada", dial: "1" },
40
+ { iso2: "CV", name: "Cape Verde", dial: "238" },
41
+ { iso2: "CF", name: "Central African Republic", dial: "236" },
42
+ { iso2: "TD", name: "Chad", dial: "235" },
43
+ { iso2: "CL", name: "Chile", dial: "56" },
44
+ { iso2: "CN", name: "China", dial: "86" },
45
+ { iso2: "CO", name: "Colombia", dial: "57" },
46
+ { iso2: "KM", name: "Comoros", dial: "269" },
47
+ { iso2: "CG", name: "Congo", dial: "242" },
48
+ { iso2: "CD", name: "Congo (DRC)", dial: "243" },
49
+ { iso2: "CR", name: "Costa Rica", dial: "506" },
50
+ { iso2: "CI", name: "Côte d’Ivoire", dial: "225" },
51
+ { iso2: "HR", name: "Croatia", dial: "385" },
52
+ { iso2: "CU", name: "Cuba", dial: "53" },
53
+ { iso2: "CY", name: "Cyprus", dial: "357" },
54
+ { iso2: "CZ", name: "Czechia", dial: "420" },
55
+ { iso2: "DK", name: "Denmark", dial: "45" },
56
+ { iso2: "DJ", name: "Djibouti", dial: "253" },
57
+ { iso2: "DM", name: "Dominica", dial: "1767" },
58
+ { iso2: "DO", name: "Dominican Republic", dial: "1809" },
59
+ { iso2: "EC", name: "Ecuador", dial: "593" },
60
+ { iso2: "EG", name: "Egypt", dial: "20" },
61
+ { iso2: "SV", name: "El Salvador", dial: "503" },
62
+ { iso2: "GQ", name: "Equatorial Guinea", dial: "240" },
63
+ { iso2: "ER", name: "Eritrea", dial: "291" },
64
+ { iso2: "EE", name: "Estonia", dial: "372" },
65
+ { iso2: "SZ", name: "Eswatini", dial: "268" },
66
+ { iso2: "ET", name: "Ethiopia", dial: "251" },
67
+ { iso2: "FJ", name: "Fiji", dial: "679" },
68
+ { iso2: "FI", name: "Finland", dial: "358" },
69
+ { iso2: "FR", name: "France", dial: "33" },
70
+ { iso2: "GA", name: "Gabon", dial: "241" },
71
+ { iso2: "GM", name: "Gambia", dial: "220" },
72
+ { iso2: "GE", name: "Georgia", dial: "995" },
73
+ { iso2: "DE", name: "Germany", dial: "49" },
74
+ { iso2: "GH", name: "Ghana", dial: "233" },
75
+ { iso2: "GR", name: "Greece", dial: "30" },
76
+ { iso2: "GD", name: "Grenada", dial: "1473" },
77
+ { iso2: "GT", name: "Guatemala", dial: "502" },
78
+ { iso2: "GN", name: "Guinea", dial: "224" },
79
+ { iso2: "GW", name: "Guinea-Bissau", dial: "245" },
80
+ { iso2: "GY", name: "Guyana", dial: "592" },
81
+ { iso2: "HT", name: "Haiti", dial: "509" },
82
+ { iso2: "HN", name: "Honduras", dial: "504" },
83
+ { iso2: "HK", name: "Hong Kong", dial: "852" },
84
+ { iso2: "HU", name: "Hungary", dial: "36" },
85
+ { iso2: "IS", name: "Iceland", dial: "354" },
86
+ { iso2: "IN", name: "India", dial: "91" },
87
+ { iso2: "ID", name: "Indonesia", dial: "62" },
88
+ { iso2: "IR", name: "Iran", dial: "98" },
89
+ { iso2: "IQ", name: "Iraq", dial: "964" },
90
+ { iso2: "IE", name: "Ireland", dial: "353" },
91
+ { iso2: "IL", name: "Israel", dial: "972" },
92
+ { iso2: "IT", name: "Italy", dial: "39" },
93
+ { iso2: "JM", name: "Jamaica", dial: "1876" },
94
+ { iso2: "JP", name: "Japan", dial: "81" },
95
+ { iso2: "JO", name: "Jordan", dial: "962" },
96
+ { iso2: "KZ", name: "Kazakhstan", dial: "7" },
97
+ { iso2: "KE", name: "Kenya", dial: "254" },
98
+ { iso2: "KI", name: "Kiribati", dial: "686" },
99
+ { iso2: "KW", name: "Kuwait", dial: "965" },
100
+ { iso2: "KG", name: "Kyrgyzstan", dial: "996" },
101
+ { iso2: "LA", name: "Laos", dial: "856" },
102
+ { iso2: "LV", name: "Latvia", dial: "371" },
103
+ { iso2: "LB", name: "Lebanon", dial: "961" },
104
+ { iso2: "LS", name: "Lesotho", dial: "266" },
105
+ { iso2: "LR", name: "Liberia", dial: "231" },
106
+ { iso2: "LY", name: "Libya", dial: "218" },
107
+ { iso2: "LI", name: "Liechtenstein", dial: "423" },
108
+ { iso2: "LT", name: "Lithuania", dial: "370" },
109
+ { iso2: "LU", name: "Luxembourg", dial: "352" },
110
+ { iso2: "MO", name: "Macau", dial: "853" },
111
+ { iso2: "MG", name: "Madagascar", dial: "261" },
112
+ { iso2: "MW", name: "Malawi", dial: "265" },
113
+ { iso2: "MY", name: "Malaysia", dial: "60" },
114
+ { iso2: "MV", name: "Maldives", dial: "960" },
115
+ { iso2: "ML", name: "Mali", dial: "223" },
116
+ { iso2: "MT", name: "Malta", dial: "356" },
117
+ { iso2: "MH", name: "Marshall Islands", dial: "692" },
118
+ { iso2: "MR", name: "Mauritania", dial: "222" },
119
+ { iso2: "MU", name: "Mauritius", dial: "230" },
120
+ { iso2: "MX", name: "Mexico", dial: "52" },
121
+ { iso2: "FM", name: "Micronesia", dial: "691" },
122
+ { iso2: "MD", name: "Moldova", dial: "373" },
123
+ { iso2: "MC", name: "Monaco", dial: "377" },
124
+ { iso2: "MN", name: "Mongolia", dial: "976" },
125
+ { iso2: "ME", name: "Montenegro", dial: "382" },
126
+ { iso2: "MA", name: "Morocco", dial: "212" },
127
+ { iso2: "MZ", name: "Mozambique", dial: "258" },
128
+ { iso2: "MM", name: "Myanmar", dial: "95" },
129
+ { iso2: "NA", name: "Namibia", dial: "264" },
130
+ { iso2: "NR", name: "Nauru", dial: "674" },
131
+ { iso2: "NP", name: "Nepal", dial: "977" },
132
+ { iso2: "NL", name: "Netherlands", dial: "31" },
133
+ { iso2: "NZ", name: "New Zealand", dial: "64" },
134
+ { iso2: "NI", name: "Nicaragua", dial: "505" },
135
+ { iso2: "NE", name: "Niger", dial: "227" },
136
+ { iso2: "NG", name: "Nigeria", dial: "234" },
137
+ { iso2: "KP", name: "North Korea", dial: "850" },
138
+ { iso2: "MK", name: "North Macedonia", dial: "389" },
139
+ { iso2: "NO", name: "Norway", dial: "47" },
140
+ { iso2: "OM", name: "Oman", dial: "968" },
141
+ { iso2: "PK", name: "Pakistan", dial: "92" },
142
+ { iso2: "PW", name: "Palau", dial: "680" },
143
+ { iso2: "PS", name: "Palestine", dial: "970" },
144
+ { iso2: "PA", name: "Panama", dial: "507" },
145
+ { iso2: "PG", name: "Papua New Guinea", dial: "675" },
146
+ { iso2: "PY", name: "Paraguay", dial: "595" },
147
+ { iso2: "PE", name: "Peru", dial: "51" },
148
+ { iso2: "PH", name: "Philippines", dial: "63" },
149
+ { iso2: "PL", name: "Poland", dial: "48" },
150
+ { iso2: "PT", name: "Portugal", dial: "351" },
151
+ { iso2: "QA", name: "Qatar", dial: "974" },
152
+ { iso2: "RO", name: "Romania", dial: "40" },
153
+ { iso2: "RU", name: "Russia", dial: "7" },
154
+ { iso2: "RW", name: "Rwanda", dial: "250" },
155
+ { iso2: "KN", name: "Saint Kitts and Nevis", dial: "1869" },
156
+ { iso2: "LC", name: "Saint Lucia", dial: "1758" },
157
+ { iso2: "VC", name: "Saint Vincent and the Grenadines", dial: "1784" },
158
+ { iso2: "WS", name: "Samoa", dial: "685" },
159
+ { iso2: "SM", name: "San Marino", dial: "378" },
160
+ { iso2: "ST", name: "São Tomé and Príncipe", dial: "239" },
161
+ { iso2: "SA", name: "Saudi Arabia", dial: "966" },
162
+ { iso2: "SN", name: "Senegal", dial: "221" },
163
+ { iso2: "RS", name: "Serbia", dial: "381" },
164
+ { iso2: "SC", name: "Seychelles", dial: "248" },
165
+ { iso2: "SL", name: "Sierra Leone", dial: "232" },
166
+ { iso2: "SG", name: "Singapore", dial: "65" },
167
+ { iso2: "SK", name: "Slovakia", dial: "421" },
168
+ { iso2: "SI", name: "Slovenia", dial: "386" },
169
+ { iso2: "SB", name: "Solomon Islands", dial: "677" },
170
+ { iso2: "SO", name: "Somalia", dial: "252" },
171
+ { iso2: "ZA", name: "South Africa", dial: "27" },
172
+ { iso2: "KR", name: "South Korea", dial: "82" },
173
+ { iso2: "SS", name: "South Sudan", dial: "211" },
174
+ { iso2: "ES", name: "Spain", dial: "34" },
175
+ { iso2: "LK", name: "Sri Lanka", dial: "94" },
176
+ { iso2: "SD", name: "Sudan", dial: "249" },
177
+ { iso2: "SR", name: "Suriname", dial: "597" },
178
+ { iso2: "SE", name: "Sweden", dial: "46" },
179
+ { iso2: "CH", name: "Switzerland", dial: "41" },
180
+ { iso2: "SY", name: "Syria", dial: "963" },
181
+ { iso2: "TW", name: "Taiwan", dial: "886" },
182
+ { iso2: "TJ", name: "Tajikistan", dial: "992" },
183
+ { iso2: "TZ", name: "Tanzania", dial: "255" },
184
+ { iso2: "TH", name: "Thailand", dial: "66" },
185
+ { iso2: "TL", name: "Timor-Leste", dial: "670" },
186
+ { iso2: "TG", name: "Togo", dial: "228" },
187
+ { iso2: "TO", name: "Tonga", dial: "676" },
188
+ { iso2: "TT", name: "Trinidad and Tobago", dial: "1868" },
189
+ { iso2: "TN", name: "Tunisia", dial: "216" },
190
+ { iso2: "TR", name: "Turkey", dial: "90" },
191
+ { iso2: "TM", name: "Turkmenistan", dial: "993" },
192
+ { iso2: "TV", name: "Tuvalu", dial: "688" },
193
+ { iso2: "UG", name: "Uganda", dial: "256" },
194
+ { iso2: "UA", name: "Ukraine", dial: "380" },
195
+ { iso2: "AE", name: "United Arab Emirates", dial: "971" },
196
+ { iso2: "GB", name: "United Kingdom", dial: "44" },
197
+ { iso2: "US", name: "United States", dial: "1" },
198
+ { iso2: "UY", name: "Uruguay", dial: "598" },
199
+ { iso2: "UZ", name: "Uzbekistan", dial: "998" },
200
+ { iso2: "VU", name: "Vanuatu", dial: "678" },
201
+ { iso2: "VA", name: "Vatican City", dial: "39" },
202
+ { iso2: "VE", name: "Venezuela", dial: "58" },
203
+ { iso2: "VN", name: "Vietnam", dial: "84" },
204
+ { iso2: "YE", name: "Yemen", dial: "967" },
205
+ { iso2: "ZM", name: "Zambia", dial: "260" },
206
+ { iso2: "ZW", name: "Zimbabwe", dial: "263" },
207
+ ]
@@ -0,0 +1,67 @@
1
+ /*
2
+ * Lightweight country helpers for LpPhoneInput. The heavy dial-code TABLE lives
3
+ * in ./countries-data.ts and is loaded lazily via loadCountries() so it lands in
4
+ * its own chunk — apps that never render a phone field don't pay for it. Only the
5
+ * type + tiny pure helpers stay here (importing these does not pull the data).
6
+ */
7
+ export interface Country {
8
+ /** ISO 3166-1 alpha-2, uppercase (e.g. "US"). Drives the flag. */
9
+ iso2: string
10
+ name: string
11
+ /** Calling code without "+", e.g. "1", "44", "380". */
12
+ dial: string
13
+ }
14
+
15
+ /** Turn an ISO2 code into its flag emoji (two regional-indicator symbols). */
16
+ export function flagEmoji(iso2: string): string {
17
+ if (!/^[A-Za-z]{2}$/.test(iso2)) return ""
18
+ const base = 0x1f1e6 - 0x41 // regional indicator 'A' minus ASCII 'A'
19
+ return String.fromCodePoint(
20
+ ...iso2.toUpperCase().split("").map((c) => base + c.charCodeAt(0)),
21
+ )
22
+ }
23
+
24
+ let cache: Country[] | undefined
25
+
26
+ /**
27
+ * Load the country table (code-split chunk). Cached after first call, so repeat
28
+ * calls are synchronous-ish (resolved promise) with no re-import cost.
29
+ */
30
+ export async function loadCountries(): Promise<Country[]> {
31
+ if (cache) return cache
32
+ const mod = await import("./countries-data")
33
+ cache = mod.COUNTRIES
34
+ return cache
35
+ }
36
+
37
+ // Several countries share a dial code (notably NANP "+1" → US, CA, …). Without
38
+ // area-code parsing we can't disambiguate, so we pick a sensible primary per
39
+ // shared code. Anything not listed falls back to the first list match.
40
+ const PRIMARY_BY_DIAL: Record<string, string> = {
41
+ "1": "US",
42
+ "7": "RU",
43
+ "39": "IT",
44
+ }
45
+
46
+ /**
47
+ * Best-effort: pick the country whose dial code prefixes the given E.164-ish
48
+ * value (longest match wins, so "1868" beats "1"). For shared codes the primary
49
+ * country is preferred. Pass the list (from loadCountries). Undefined if none.
50
+ */
51
+ export function matchCountryByValue(
52
+ value: string,
53
+ list: Country[],
54
+ ): Country | undefined {
55
+ const digits = value.replace(/[^\d]/g, "")
56
+ if (!digits) return undefined
57
+ let best: Country | undefined
58
+ for (const c of list) {
59
+ if (!digits.startsWith(c.dial)) continue
60
+ if (!best || c.dial.length > best.dial.length) {
61
+ best = c
62
+ } else if (c.dial.length === best.dial.length && PRIMARY_BY_DIAL[c.dial] === c.iso2) {
63
+ best = c // same length, but this one is the designated primary
64
+ }
65
+ }
66
+ return best
67
+ }
@@ -0,0 +1,64 @@
1
+ /*
2
+ * Shared class strings for floating panels (the dropdown/popover surface) and
3
+ * their option rows. These were copy-pasted across LpSelect, LpAutocomplete,
4
+ * LpPhoneInput, LpPopover and LpDropdownMenu — one source of truth means a tweak
5
+ * to the look or the side-aware enter/exit animation lands everywhere at once.
6
+ *
7
+ * The panel is built on different reka primitives per component (Combobox /
8
+ * Popover / DropdownMenu content), so we share CLASSES, not a wrapper component.
9
+ * Callers still own per-use bits: width, padding, overflow, and which primitive.
10
+ *
11
+ * IMPORTANT: a panel that hosts an option cascade must include `group` (it's in
12
+ * POPOVER_PANEL) so each OPTION_ITEM can read the panel's data-side and cascade
13
+ * the right way when the list flips above the trigger.
14
+ */
15
+
16
+ /** Enter/exit animation for state-open panels (Select, Popover, Dropdown, …). */
17
+ export const POPOVER_ANIM =
18
+ "data-[state=open]:animate-[popover-in_140ms_var(--ease-emphasized)] " +
19
+ "data-[state=closed]:animate-[popover-out_120ms_ease] " +
20
+ "data-[side=top]:data-[state=open]:animate-[popover-in-up_140ms_var(--ease-emphasized)] " +
21
+ "data-[side=top]:data-[state=closed]:animate-[popover-out-up_120ms_ease]"
22
+
23
+ /** Tooltip variant: faster, and reka uses `delayed-open` instead of `open`. */
24
+ export const TOOLTIP_ANIM =
25
+ "data-[state=delayed-open]:animate-[popover-in_120ms_var(--ease-emphasized)] " +
26
+ "data-[state=closed]:animate-[popover-out_100ms_ease] " +
27
+ "data-[side=top]:data-[state=delayed-open]:animate-[popover-in-up_120ms_var(--ease-emphasized)] " +
28
+ "data-[side=top]:data-[state=closed]:animate-[popover-out-up_100ms_ease]"
29
+
30
+ /**
31
+ * The bare floating surface (border, raised overlay fill, blur, shadow) without
32
+ * any animation — pair with POPOVER_ANIM or TOOLTIP_ANIM. `group` lets option
33
+ * rows read the panel's data-side for the cascade direction.
34
+ */
35
+ export const PANEL_SURFACE =
36
+ "group rounded-control border border-line bg-surface-overlay shadow-panel backdrop-blur"
37
+
38
+ /**
39
+ * Surface + the state-open animation. Add width/padding/overflow and the z-layer
40
+ * at the call-site (z-layer omitted so tooltips can use z-(--z-tooltip) while the
41
+ * rest use z-(--z-popover)).
42
+ */
43
+ export const POPOVER_PANEL = PANEL_SURFACE + " " + POPOVER_ANIM
44
+
45
+ /**
46
+ * A selectable option row: layout, highlight state, and the staggered cascade.
47
+ * Cascades downward by default and upward when the panel is flipped above the
48
+ * trigger (group-data-[side=top]). Pair with an inline animationDelay for the
49
+ * stagger. `flex` + gap are included; add justify/extra layout if needed.
50
+ */
51
+ export const OPTION_ITEM =
52
+ "flex cursor-pointer items-center gap-2 rounded-md px-2.5 py-1.5 text-sm text-ink outline-none " +
53
+ "animate-[option-in_180ms_var(--ease-emphasized)_both] " +
54
+ "group-data-[side=top]:animate-[option-in-up_180ms_var(--ease-emphasized)_both] " +
55
+ "data-[highlighted]:bg-brand-soft data-[highlighted]:text-brand"
56
+
57
+ /**
58
+ * Micro-animation for a close / clear "✕" icon: a quarter-turn on hover and a
59
+ * dip on press. Put on the <LpIcon>, with `group` on its button. Shared by
60
+ * Modal, Drawer, Toaster and the Autocomplete clear button.
61
+ */
62
+ export const CLOSE_ICON =
63
+ "transition-transform duration-medium ease-[var(--ease-emphasized)] " +
64
+ "group-hover:rotate-90 group-active:scale-90 motion-reduce:group-hover:rotate-0"
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Shared sidebar types. Lives in its own module so LpSidebar and its internal
3
+ * LpSidebarNav body can both import them without a component-to-component cycle.
4
+ * Re-exported from LpSidebar.vue for back-compat with existing import sites.
5
+ */
6
+
7
+ import type { ContextMenuItemDef } from "./LpContextMenu.vue"
8
+
9
+ export interface SidebarItem {
10
+ id: string
11
+ label: string
12
+ icon?: string
13
+ /** Render as a link instead of a button. */
14
+ href?: string
15
+ /** Count chip on the right (number or short string). */
16
+ badge?: number | string
17
+ disabled?: boolean
18
+ /** Right-click menu for this item (e.g. "Open in new tab", "Pin"). */
19
+ menu?: ContextMenuItemDef[]
20
+ }
21
+
22
+ export interface SidebarSection {
23
+ /** Optional heading above the group. */
24
+ title?: string
25
+ items: SidebarItem[]
26
+ }
@@ -0,0 +1,89 @@
1
+ import { computed, type MaybeRefOrGetter, toValue } from "vue"
2
+
3
+ /*
4
+ * Cursor- and IME-safe input filtering + pattern validation for native <input>.
5
+ *
6
+ * Two orthogonal jobs:
7
+ *
8
+ * restrict — a character class (RegExp matching ONE allowed char, e.g. /[0-9]/)
9
+ * that hard-blocks disallowed input. We cancel the offending `beforeinput`
10
+ * instead of rewriting `value` after the fact: rewriting moves the caret to
11
+ * the end and aborts IME composition. By vetoing the event the browser never
12
+ * applies the bad edit, so the caret and any in-flight composition stay put.
13
+ *
14
+ * pattern — a RegExp the WHOLE value must match to be considered valid. This
15
+ * never blocks typing; it only drives the invalid state (border + aria), so
16
+ * partially-typed values aren't punished mid-entry — the caller decides when
17
+ * to surface it.
18
+ *
19
+ * A bare string pattern is treated as a full-string match (anchored), mirroring
20
+ * the native HTML `pattern` attribute.
21
+ */
22
+
23
+ export interface UseInputFilterOptions {
24
+ /** One-char RegExp of allowed characters. Disallowed input is blocked. */
25
+ restrict?: MaybeRefOrGetter<RegExp | undefined>
26
+ /** RegExp/string the full value must match to be valid. Validation only. */
27
+ pattern?: MaybeRefOrGetter<RegExp | string | undefined>
28
+ }
29
+
30
+ function toAnchored(p: RegExp | string): RegExp {
31
+ if (p instanceof RegExp) {
32
+ // Re-anchor so `pattern` validates the whole string, like HTML's attribute.
33
+ const src = p.source.replace(/^\^/, "").replace(/\$$/, "")
34
+ return new RegExp(`^(?:${src})$`, p.flags.replace(/[gy]/g, ""))
35
+ }
36
+ return new RegExp(`^(?:${p})$`)
37
+ }
38
+
39
+ /** Does a string contain only restrict-allowed characters? */
40
+ function allAllowed(text: string, charRe: RegExp): boolean {
41
+ // Strip g/y so lastIndex can't leak state across .test() calls, then reuse
42
+ // the one instance per char (tested by code point, so surrogate pairs hold).
43
+ const re = new RegExp(charRe.source, charRe.flags.replace(/[gy]/g, ""))
44
+ for (const ch of text) {
45
+ if (!re.test(ch)) return false
46
+ }
47
+ return true
48
+ }
49
+
50
+ export function useInputFilter(options: UseInputFilterOptions = {}) {
51
+ const restrict = computed(() => toValue(options.restrict))
52
+ const patternRe = computed(() => {
53
+ const p = toValue(options.pattern)
54
+ return p === undefined ? undefined : toAnchored(p)
55
+ })
56
+
57
+ /** True when a non-empty value fails `pattern`. Empty is treated as valid. */
58
+ function isInvalid(value: string | undefined): boolean {
59
+ const re = patternRe.value
60
+ if (!re || !value) return false
61
+ return !re.test(value)
62
+ }
63
+
64
+ /**
65
+ * Attach to the input's `beforeinput`. Vetoes edits that would introduce a
66
+ * disallowed character. No-op while composing (IME) or when no restrict set.
67
+ */
68
+ function onBeforeInput(event: InputEvent): void {
69
+ const re = restrict.value
70
+ if (!re || event.isComposing) return
71
+ // Deletions / formatting carry no data to validate; let them through.
72
+ const data = event.data
73
+ if (data == null) return
74
+ if (!allAllowed(data, re)) event.preventDefault()
75
+ }
76
+
77
+ /**
78
+ * Attach to `paste`. Veto the paste if any pasted char is disallowed, rather
79
+ * than silently dropping part of it — partial pastes confuse more than help.
80
+ */
81
+ function onPaste(event: ClipboardEvent): void {
82
+ const re = restrict.value
83
+ if (!re) return
84
+ const text = event.clipboardData?.getData("text") ?? ""
85
+ if (text && !allAllowed(text, re)) event.preventDefault()
86
+ }
87
+
88
+ return { isInvalid, onBeforeInput, onPaste, patternRe, restrict }
89
+ }
@@ -0,0 +1,19 @@
1
+ import { useReducedMotion } from "motion-v"
2
+ import { computed, type ComputedRef } from "vue"
3
+
4
+ /*
5
+ * Shared motion-v transition for the sliding "pill" indicator used by LpTabs,
6
+ * LpSegmented and LpPagination (a shared layoutId element that flies under the
7
+ * active item). One spring keeps the feel identical across them; it collapses to
8
+ * an instant move when the OS prefers reduced motion.
9
+ */
10
+ export type PillTransition =
11
+ | { duration: number }
12
+ | { type: "spring"; stiffness: number; damping: number }
13
+
14
+ export function usePillTransition(): ComputedRef<PillTransition> {
15
+ const reduceMotion = useReducedMotion()
16
+ return computed<PillTransition>(() =>
17
+ reduceMotion.value ? { duration: 0 } : { type: "spring", stiffness: 520, damping: 40 },
18
+ )
19
+ }
@@ -0,0 +1,92 @@
1
+ import { reactive } from "vue"
2
+
3
+ export type ToastVariant = "info" | "success" | "warning" | "error"
4
+
5
+ export interface ToastAction {
6
+ label: string
7
+ onClick: () => void | Promise<void>
8
+ }
9
+
10
+ export interface ToastItem {
11
+ id: number
12
+ title?: string
13
+ message: string
14
+ variant: ToastVariant
15
+ duration: number
16
+ /** Epoch ms when this toast auto-dismisses, or null if it never does. */
17
+ dismissAt: number | null
18
+ actions?: ToastAction[]
19
+ /** Whole-toast click handler (UAProject-style clickable toast). */
20
+ onClick?: () => void | Promise<void>
21
+ }
22
+
23
+ export interface ToastOptions {
24
+ title?: string
25
+ duration?: number
26
+ actions?: ToastAction[]
27
+ onClick?: () => void | Promise<void>
28
+ }
29
+
30
+ const DEFAULT_DURATION = 4500
31
+ const MAX_ACTIVE = 4
32
+
33
+ // Pin the queue to a global symbol so the singleton survives module
34
+ // duplication (a barrel import and a deep import can otherwise resolve to two
35
+ // module instances, each with its own state — the toasts-never-show footgun).
36
+ const STATE_KEY = Symbol.for("@leavepulse/ui:toast-queue")
37
+ type GlobalWithQueue = typeof globalThis & {
38
+ [STATE_KEY]?: { queue: ToastItem[]; nextId: number }
39
+ }
40
+ const g = globalThis as GlobalWithQueue
41
+ const state = (g[STATE_KEY] ??= { queue: reactive<ToastItem[]>([]), nextId: 0 })
42
+ const queue = state.queue
43
+
44
+ function push(
45
+ variant: ToastVariant,
46
+ message: string,
47
+ options: ToastOptions = {},
48
+ ): number {
49
+ const id = state.nextId++
50
+ const duration = options.duration ?? DEFAULT_DURATION
51
+ const item: ToastItem = {
52
+ id,
53
+ title: options.title,
54
+ message,
55
+ variant,
56
+ duration,
57
+ dismissAt: duration > 0 ? Date.now() + duration : null,
58
+ actions: options.actions,
59
+ onClick: options.onClick,
60
+ }
61
+
62
+ // Cap the active count: drop the oldest beyond the limit.
63
+ const overflow = queue.length - (MAX_ACTIVE - 1)
64
+ if (overflow > 0) queue.splice(0, overflow)
65
+ queue.push(item)
66
+
67
+ return id
68
+ }
69
+
70
+ function dismiss(id: number): void {
71
+ const i = queue.findIndex((t) => t.id === id)
72
+ if (i !== -1) queue.splice(i, 1)
73
+ }
74
+
75
+ function clear(): void {
76
+ queue.splice(0, queue.length)
77
+ }
78
+
79
+ export function useToast() {
80
+ return {
81
+ queue,
82
+ toast: (message: string, o?: ToastOptions) => push("info", message, o),
83
+ info: (message: string, o?: ToastOptions) => push("info", message, o),
84
+ success: (message: string, o?: ToastOptions) => push("success", message, o),
85
+ warning: (message: string, o?: ToastOptions) => push("warning", message, o),
86
+ error: (message: string, o?: ToastOptions) => push("error", message, o),
87
+ /** Low-level push for bridges (e.g. @leavepulse/ui-sdk apiError). */
88
+ push,
89
+ dismiss,
90
+ clear,
91
+ }
92
+ }
package/src/icons.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { addCollection } from "@iconify/vue"
2
+ import lucide from "@iconify-json/lucide/icons.json"
3
+
4
+ /*
5
+ * Register the lucide collection offline so <LpIcon name="lucide:*"> resolves
6
+ * without hitting the iconify API (required for Tauri / air-gapped use).
7
+ *
8
+ * This pulls the whole lucide set. If bundle size matters in an app, replace
9
+ * this with a curated subset via addIcon() — the LpIcon API is unchanged.
10
+ */
11
+ addCollection(lucide as Parameters<typeof addCollection>[0])
package/src/index.css ADDED
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Public CSS entry for @leavepulse/ui — what `@import "@leavepulse/ui"`
3
+ * resolves to (see the "." export's "style" field and "./css").
4
+ *
5
+ * Mirrors how @nuxt/ui ships styles: the consumer owns Tailwind, the kit owns
6
+ * its tokens + a @source over its own components. So this file deliberately
7
+ * does NOT `@import "tailwindcss"` — the consuming app does that once, then
8
+ * imports this. The @source makes the consumer's Tailwind build emit the
9
+ * utilities our components use (node_modules isn't scanned by default).
10
+ *
11
+ * Consumer usage:
12
+ * @import "tailwindcss";
13
+ * @import "@leavepulse/ui";
14
+ */
15
+ @import "./tokens/tokens.css";
16
+ @import "./tokens/utilities.css";
17
+
18
+ @source "./components";
19
+ @source "./composables";
20
+ @source "./layout";
21
+ @source "./theme";