@propelinc/citrus-ui 0.6.0 → 1.0.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 (355) hide show
  1. package/README.md +54 -60
  2. package/dist/colors/colors.d.ts +31 -0
  3. package/dist/colors/theme.d.ts +2 -11
  4. package/dist/colors/util-classes.d.ts +11 -0
  5. package/dist/components/CAccordion.vue.d.ts +21 -0
  6. package/dist/components/CAccordionItem.vue.d.ts +41 -0
  7. package/dist/components/CAppBar.vue.d.ts +156 -0
  8. package/dist/components/CBadge.vue.d.ts +52 -0
  9. package/dist/components/CBottomSheet.vue.d.ts +226 -0
  10. package/dist/components/CButton/CButton.vue.d.ts +231 -0
  11. package/dist/components/CButton/types.d.ts +5 -0
  12. package/dist/components/CButtonStack.vue.d.ts +24 -0
  13. package/dist/components/CCard.vue.d.ts +107 -0
  14. package/dist/components/CCardFooter.vue.d.ts +26 -0
  15. package/dist/components/CCardHeader.vue.d.ts +3 -0
  16. package/dist/components/CCardSection.vue.d.ts +17 -0
  17. package/dist/components/CCheckbox.vue.d.ts +145 -0
  18. package/dist/components/CCol.vue.d.ts +21 -0
  19. package/dist/components/CDivider.vue.d.ts +17 -0
  20. package/dist/components/CDobField.vue.d.ts +2109 -0
  21. package/dist/components/CDobSelect.vue.d.ts +398 -0
  22. package/dist/components/CEmailField.vue.d.ts +699 -0
  23. package/dist/components/CExpandTransition.vue.d.ts +19 -0
  24. package/dist/components/CFadeTransition.vue.d.ts +3 -0
  25. package/dist/components/CFileInput.vue.d.ts +98 -0
  26. package/dist/components/CFixedPageFooter.vue.d.ts +106 -0
  27. package/dist/components/CForm.vue.d.ts +29 -0
  28. package/dist/components/CFormFieldCounter.vue.d.ts +42 -0
  29. package/dist/components/CIconButton.vue.d.ts +390 -0
  30. package/dist/components/CLabel.vue.d.ts +32 -0
  31. package/dist/components/CListItem.vue.d.ts +208 -0
  32. package/dist/components/CListItemContent.vue.d.ts +27 -0
  33. package/dist/components/CListItemIcon.vue.d.ts +54 -0
  34. package/dist/components/CLoader.vue.d.ts +73 -0
  35. package/dist/components/CLogo.vue.d.ts +19 -0
  36. package/dist/components/CMaskedTextField.vue.d.ts +2012 -0
  37. package/dist/components/CMenu.vue.d.ts +6 -0
  38. package/dist/components/CMenuItem.vue.d.ts +170 -0
  39. package/dist/components/CMenuLabel.vue.d.ts +3 -0
  40. package/dist/components/CModal.vue.d.ts +206 -0
  41. package/dist/components/CModalLoading.vue.d.ts +230 -0
  42. package/dist/components/CNotification.vue.d.ts +589 -0
  43. package/dist/components/CPhoneField.vue.d.ts +2088 -0
  44. package/dist/components/CPill.vue.d.ts +42 -0
  45. package/dist/components/CPillGroup.vue.d.ts +70 -0
  46. package/dist/components/CPopup.vue.d.ts +21 -0
  47. package/dist/components/CProgressLinear.vue.d.ts +61 -0
  48. package/dist/components/CProgressRing.vue.d.ts +103 -0
  49. package/dist/components/CRadio.vue.d.ts +73 -0
  50. package/dist/components/CRadioGroup.vue.d.ts +123 -0
  51. package/dist/components/CRebrand.vue.d.ts +28 -0
  52. package/dist/components/CRow.vue.d.ts +67 -0
  53. package/dist/components/CSafeArea.vue.d.ts +18 -0
  54. package/dist/components/CSectionHeader.vue.d.ts +28 -0
  55. package/dist/components/CSelect.vue.d.ts +293 -0
  56. package/dist/components/CSkeleton.vue.d.ts +3 -0
  57. package/dist/components/CSkeletonLoaderCard.vue.d.ts +21 -0
  58. package/dist/components/CSkeletonLoaderCircle.vue.d.ts +5 -0
  59. package/dist/components/CSkeletonLoaderText.vue.d.ts +44 -0
  60. package/dist/components/CSlideFadeTransition.vue.d.ts +58 -0
  61. package/dist/components/CSplitInput.vue.d.ts +2131 -0
  62. package/dist/components/CSquaredIcon.vue.d.ts +47 -0
  63. package/dist/components/CSsnField.vue.d.ts +2083 -0
  64. package/dist/components/CStatusDot.vue.d.ts +27 -0
  65. package/dist/components/CSwitch.vue.d.ts +54 -0
  66. package/dist/components/CSwitchListItem.vue.d.ts +392 -0
  67. package/dist/components/CTextArea.vue.d.ts +240 -0
  68. package/dist/components/CTextField.vue.d.ts +647 -0
  69. package/dist/components/CTextLink.vue.d.ts +55 -0
  70. package/dist/components/CThirdPartyLogo.vue.d.ts +128 -0
  71. package/dist/components/CTimeago.vue.d.ts +12 -0
  72. package/dist/components/CToast.vue.d.ts +458 -0
  73. package/dist/components/CToastsList.vue.d.ts +430 -0
  74. package/dist/components/CValidationMessage.vue.d.ts +45 -0
  75. package/dist/components/CZipcodeField.vue.d.ts +2080 -0
  76. package/dist/components/index.d.ts +66 -25
  77. package/dist/components/internal/CCloseButton.vue.d.ts +14 -0
  78. package/dist/composables/accessibility.d.ts +1 -0
  79. package/dist/composables/animation.d.ts +12 -0
  80. package/dist/composables/binding.d.ts +19 -0
  81. package/dist/composables/colors.d.ts +13 -0
  82. package/dist/composables/elements.d.ts +3 -0
  83. package/dist/composables/fields.d.ts +9 -0
  84. package/dist/composables/gestures.d.ts +53 -0
  85. package/dist/composables/i18n.d.ts +3 -0
  86. package/dist/composables/id.d.ts +11 -0
  87. package/dist/composables/input-mask.d.ts +18 -0
  88. package/dist/composables/router.d.ts +30 -0
  89. package/dist/composables/slots.d.ts +2 -0
  90. package/dist/composables/toast.d.ts +21 -0
  91. package/dist/composables/validations.d.ts +77 -0
  92. package/dist/index.css +1 -0
  93. package/dist/index.d.ts +5 -4
  94. package/dist/index.mjs +11738 -0
  95. package/dist/index.mjs.map +1 -0
  96. package/dist/plugin.d.ts +2 -2
  97. package/dist/services/animation.d.ts +17 -0
  98. package/dist/services/directives/index.d.ts +2 -0
  99. package/dist/services/directives/scroll-into-view.d.ts +7 -0
  100. package/dist/services/directives/tap-animation.d.ts +6 -0
  101. package/dist/services/id.d.ts +22 -0
  102. package/dist/services/injections/accordions.d.ts +3 -0
  103. package/dist/services/injections/animations.d.ts +2 -0
  104. package/dist/services/injections/buttons.d.ts +4 -0
  105. package/dist/services/injections/forms.d.ts +6 -0
  106. package/dist/services/injections/icon-buttons.d.ts +3 -0
  107. package/dist/services/injections/pills.d.ts +4 -0
  108. package/dist/services/injections/radio.d.ts +10 -0
  109. package/dist/styles/main.css +3002 -0
  110. package/dist/styles/utils.css +2319 -0
  111. package/dist/theme/icons.d.ts +35 -2
  112. package/dist/types/CForm.d.ts +12 -0
  113. package/dist/types/font-awesome.d.ts +5 -0
  114. package/dist/types.d.ts +12 -0
  115. package/index.ts +2 -0
  116. package/package.json +60 -45
  117. package/src/assets/fonts/grenette-regular.woff2 +0 -0
  118. package/src/assets/fonts/grenette-semibold.woff2 +0 -0
  119. package/src/assets/fonts/polymath.woff2 +0 -0
  120. package/src/assets/logos/propel/icon.svg +15 -0
  121. package/src/assets/logos/propel/lockup.svg +11 -0
  122. package/src/colors/colors.ts +173 -0
  123. package/src/colors/theme.ts +8 -14
  124. package/src/colors/util-classes.ts +49 -0
  125. package/src/componentResolver.js +33 -0
  126. package/src/components/CAccordion.vue +32 -7
  127. package/src/components/CAccordionItem.vue +109 -36
  128. package/src/components/CAppBar.vue +237 -0
  129. package/src/components/CBadge.vue +74 -0
  130. package/src/components/CBottomSheet.vue +430 -0
  131. package/src/components/CButton/CButton.vue +347 -0
  132. package/src/components/CButton/types.ts +5 -0
  133. package/src/components/CButtonStack.vue +36 -0
  134. package/src/components/CCard.vue +149 -41
  135. package/src/components/CCardFooter.vue +11 -27
  136. package/src/components/CCardHeader.vue +30 -21
  137. package/src/components/CCardSection.vue +23 -12
  138. package/src/components/CCheckbox.vue +191 -21
  139. package/src/components/CCol.vue +55 -0
  140. package/src/components/CDivider.vue +46 -0
  141. package/src/components/CDobField.vue +153 -0
  142. package/src/components/CDobSelect.vue +274 -0
  143. package/src/components/CEmailField.vue +61 -0
  144. package/src/components/CExpandTransition.vue +55 -0
  145. package/src/components/CFadeTransition.vue +23 -0
  146. package/src/components/CFileInput.vue +186 -0
  147. package/src/components/CFixedPageFooter.vue +76 -0
  148. package/src/components/CForm.vue +86 -0
  149. package/src/components/CFormFieldCounter.vue +40 -0
  150. package/src/components/CIconButton.vue +175 -59
  151. package/src/components/CLabel.vue +52 -0
  152. package/src/components/CListItem.vue +149 -45
  153. package/src/components/CListItemContent.vue +60 -0
  154. package/src/components/CListItemIcon.vue +27 -31
  155. package/src/components/CLoader.vue +156 -0
  156. package/src/components/CLogo.vue +23 -0
  157. package/src/components/CMaskedTextField.vue +118 -0
  158. package/src/components/CMenu.vue +24 -0
  159. package/src/components/CMenuItem.vue +106 -0
  160. package/src/components/CMenuLabel.vue +26 -0
  161. package/src/components/CModal.vue +198 -79
  162. package/src/components/CModalLoading.vue +27 -9
  163. package/src/components/CNotification.vue +86 -53
  164. package/src/components/CPhoneField.vue +69 -0
  165. package/src/components/CPill.vue +162 -0
  166. package/src/components/CPillGroup.vue +73 -0
  167. package/src/components/CPopup.vue +66 -0
  168. package/src/components/CProgressLinear.vue +52 -0
  169. package/src/components/CProgressRing.vue +126 -0
  170. package/src/components/CRadio.vue +138 -0
  171. package/src/components/CRadioGroup.vue +142 -0
  172. package/src/components/CRebrand.vue +28 -0
  173. package/src/components/CRow.vue +62 -0
  174. package/src/components/CSafeArea.vue +23 -0
  175. package/src/components/CSectionHeader.vue +50 -0
  176. package/src/components/CSelect.vue +223 -74
  177. package/src/components/CSkeleton.vue +65 -0
  178. package/src/components/CSkeletonLoaderCard.vue +29 -0
  179. package/src/components/CSkeletonLoaderCircle.vue +18 -14
  180. package/src/components/CSkeletonLoaderText.vue +127 -17
  181. package/src/components/CSlideFadeTransition.vue +100 -0
  182. package/src/components/CSplitInput.vue +111 -0
  183. package/src/components/CSquaredIcon.vue +83 -0
  184. package/src/components/CSsnField.vue +86 -0
  185. package/src/components/CStatusDot.vue +70 -0
  186. package/src/components/CSwitch.vue +125 -0
  187. package/src/components/CSwitchListItem.vue +110 -0
  188. package/src/components/CTextArea.vue +193 -47
  189. package/src/components/CTextField.vue +450 -93
  190. package/src/components/CTextLink.vue +48 -38
  191. package/src/components/CThirdPartyLogo.vue +127 -0
  192. package/src/components/CTimeago.vue +63 -0
  193. package/src/components/CToast.vue +259 -0
  194. package/src/components/CToastsList.vue +32 -0
  195. package/src/components/CValidationMessage.vue +70 -0
  196. package/src/components/CZipcodeField.vue +69 -0
  197. package/src/components/index.ts +66 -25
  198. package/src/components/internal/CCloseButton.vue +57 -0
  199. package/src/composables/accessibility.ts +29 -0
  200. package/src/composables/animation.ts +95 -0
  201. package/src/composables/binding.ts +34 -0
  202. package/src/composables/colors.ts +59 -0
  203. package/src/composables/elements.ts +72 -0
  204. package/src/composables/fields.ts +19 -0
  205. package/src/composables/gestures.ts +197 -0
  206. package/src/composables/i18n.ts +13 -0
  207. package/src/composables/id.ts +23 -0
  208. package/src/composables/input-mask.ts +139 -0
  209. package/src/composables/router.ts +64 -0
  210. package/src/composables/slots.ts +57 -0
  211. package/src/composables/toast.ts +64 -0
  212. package/src/composables/validations.ts +214 -0
  213. package/src/index.ts +7 -7
  214. package/src/plugin.ts +13 -6
  215. package/src/services/animation.ts +101 -0
  216. package/src/services/directives/index.ts +2 -0
  217. package/src/services/directives/scroll-into-view.ts +86 -0
  218. package/src/services/directives/tap-animation.ts +71 -0
  219. package/src/services/id.ts +31 -0
  220. package/src/services/injections/accordions.ts +4 -0
  221. package/src/services/injections/animations.ts +3 -0
  222. package/src/services/injections/buttons.ts +5 -0
  223. package/src/services/injections/forms.ts +8 -0
  224. package/src/services/injections/icon-buttons.ts +4 -0
  225. package/src/services/injections/pills.ts +7 -0
  226. package/src/services/injections/radio.ts +12 -0
  227. package/src/shims-vue.d.ts +6 -3
  228. package/src/styles/_animation.scss +19 -0
  229. package/src/styles/_button.scss +61 -0
  230. package/src/styles/_colors.scss +58 -11
  231. package/src/styles/_core.scss +248 -87
  232. package/src/styles/_form-fields.scss +68 -15
  233. package/src/styles/_grenette.scss +13 -0
  234. package/src/styles/_polymath.scss +14 -0
  235. package/src/styles/_reset.scss +105 -0
  236. package/src/styles/_shoelace.scss +46 -0
  237. package/src/styles/_typography.scss +40 -10
  238. package/src/styles/main.scss +6 -3
  239. package/src/styles/utils/a11y.scss +18 -0
  240. package/src/styles/utils/typography.scss +13 -0
  241. package/src/styles/utils.scss +560 -0
  242. package/src/styles/variables.scss +27 -15
  243. package/src/theme/icons.ts +16 -5
  244. package/src/types/CForm.ts +15 -0
  245. package/src/types/font-awesome.ts +6 -0
  246. package/src/types.ts +15 -0
  247. package/.browserslistrc +0 -3
  248. package/.eslintrc.js +0 -4
  249. package/.stylelintrc.js +0 -3
  250. package/babel.config.js +0 -3
  251. package/dist/citrus-ui.common.js +0 -43434
  252. package/dist/citrus-ui.common.js.map +0 -1
  253. package/dist/citrus-ui.css +0 -1
  254. package/dist/citrus-ui.umd.js +0 -43444
  255. package/dist/citrus-ui.umd.js.map +0 -1
  256. package/dist/citrus-ui.umd.min.js +0 -27
  257. package/dist/citrus-ui.umd.min.js.map +0 -1
  258. package/dist/demo.html +0 -10
  259. package/dist/fonts/Blitz-Script.85ed9abe.woff2 +0 -0
  260. package/dist/fonts/ObjectSans-Bold.5492f3d5.woff2 +0 -0
  261. package/dist/fonts/ObjectSans-BoldSlanted.29e2a87e.woff2 +0 -0
  262. package/dist/fonts/ObjectSans-Heavy.d0b2f035.woff2 +0 -0
  263. package/dist/fonts/ObjectSans-HeavySlanted.45e9c063.woff2 +0 -0
  264. package/dist/fonts/ObjectSans-Light.f885dec3.woff2 +0 -0
  265. package/dist/fonts/ObjectSans-LightSlanted.b8eb7c12.woff2 +0 -0
  266. package/dist/fonts/ObjectSans-Regular.e4ea0b90.woff2 +0 -0
  267. package/dist/fonts/ObjectSans-Slanted.57a90be9.woff2 +0 -0
  268. package/dist/fonts/ObjectSans-Thin.86d44227.woff2 +0 -0
  269. package/dist/fonts/ObjectSans-ThinSlanted.20342160.woff2 +0 -0
  270. package/jest.config.js +0 -9
  271. package/plopfile.js +0 -67
  272. package/project.json +0 -69
  273. package/src/assets/fonts/Blitz-Script.woff2 +0 -0
  274. package/src/assets/fonts/ObjectSans-Bold.woff2 +0 -0
  275. package/src/assets/fonts/ObjectSans-BoldSlanted.woff2 +0 -0
  276. package/src/assets/fonts/ObjectSans-Heavy.woff2 +0 -0
  277. package/src/assets/fonts/ObjectSans-HeavySlanted.woff2 +0 -0
  278. package/src/assets/fonts/ObjectSans-Light.woff2 +0 -0
  279. package/src/assets/fonts/ObjectSans-LightSlanted.woff2 +0 -0
  280. package/src/assets/fonts/ObjectSans-Regular.woff2 +0 -0
  281. package/src/assets/fonts/ObjectSans-Slanted.woff2 +0 -0
  282. package/src/assets/fonts/ObjectSans-Thin.woff2 +0 -0
  283. package/src/assets/fonts/ObjectSans-ThinSlanted.woff2 +0 -0
  284. package/src/components/CAlert.vue +0 -78
  285. package/src/components/CBanner.vue +0 -47
  286. package/src/components/CButton.vue +0 -146
  287. package/src/components/CListItemAction.vue +0 -29
  288. package/src/components/CSegmentedButton.vue +0 -47
  289. package/src/components/CSegmentedButtonOption.vue +0 -42
  290. package/src/components/helpers/FormField.vue +0 -48
  291. package/src/components/helpers/SelectInput.vue +0 -115
  292. package/src/shims-scss.d.ts +0 -4
  293. package/src/shims-vuetify.d.ts +0 -4
  294. package/src/styles/_blitz.scss +0 -8
  295. package/src/styles/_object-sans.scss +0 -23
  296. package/storybook-static/0.799c368cbe88266827ba.manager.bundle.js +0 -1
  297. package/storybook-static/0.a9f0a9ad.iframe.bundle.js +0 -3
  298. package/storybook-static/0.a9f0a9ad.iframe.bundle.js.LICENSE.txt +0 -8
  299. package/storybook-static/0.a9f0a9ad.iframe.bundle.js.map +0 -1
  300. package/storybook-static/1.0438fd8d.iframe.bundle.js +0 -3
  301. package/storybook-static/1.0438fd8d.iframe.bundle.js.LICENSE.txt +0 -17
  302. package/storybook-static/1.0438fd8d.iframe.bundle.js.map +0 -1
  303. package/storybook-static/1.9ebd2fb519f6726108de.manager.bundle.js +0 -1
  304. package/storybook-static/10.348d8814.iframe.bundle.js +0 -3
  305. package/storybook-static/10.348d8814.iframe.bundle.js.LICENSE.txt +0 -30
  306. package/storybook-static/10.348d8814.iframe.bundle.js.map +0 -1
  307. package/storybook-static/10.a85ea1a67689be8e19ff.manager.bundle.js +0 -1
  308. package/storybook-static/11.f4e922583ae35da460f3.manager.bundle.js +0 -2
  309. package/storybook-static/11.f4e922583ae35da460f3.manager.bundle.js.LICENSE.txt +0 -12
  310. package/storybook-static/12.1415460941f0bdcb8fa8.manager.bundle.js +0 -1
  311. package/storybook-static/2.75a17459.iframe.bundle.js +0 -3
  312. package/storybook-static/2.75a17459.iframe.bundle.js.LICENSE.txt +0 -26
  313. package/storybook-static/2.75a17459.iframe.bundle.js.map +0 -1
  314. package/storybook-static/5.f459d151315e6780c20f.manager.bundle.js +0 -2
  315. package/storybook-static/5.f459d151315e6780c20f.manager.bundle.js.LICENSE.txt +0 -8
  316. package/storybook-static/6.3bd64d820f3745f262ff.manager.bundle.js +0 -1
  317. package/storybook-static/6.ce8d99b4.iframe.bundle.js +0 -1
  318. package/storybook-static/7.3d04765dbf3f1dcd706c.manager.bundle.js +0 -1
  319. package/storybook-static/7.6633a922.iframe.bundle.js +0 -3
  320. package/storybook-static/7.6633a922.iframe.bundle.js.LICENSE.txt +0 -12
  321. package/storybook-static/7.6633a922.iframe.bundle.js.map +0 -1
  322. package/storybook-static/8.b541eadfcb9164835dfc.manager.bundle.js +0 -1
  323. package/storybook-static/8.fc5e1ebf.iframe.bundle.js +0 -1
  324. package/storybook-static/9.411ac8e451bbb10926c7.manager.bundle.js +0 -1
  325. package/storybook-static/9.724ac3ed.iframe.bundle.js +0 -3
  326. package/storybook-static/9.724ac3ed.iframe.bundle.js.LICENSE.txt +0 -15
  327. package/storybook-static/9.724ac3ed.iframe.bundle.js.map +0 -1
  328. package/storybook-static/css/main.95216119.css +0 -1
  329. package/storybook-static/css/vendors~main.02dc89bf.css +0 -1
  330. package/storybook-static/favicon.ico +0 -0
  331. package/storybook-static/fonts/Blitz-Script.85ed9abe.woff2 +0 -0
  332. package/storybook-static/fonts/ObjectSans-Bold.5492f3d5.woff2 +0 -0
  333. package/storybook-static/fonts/ObjectSans-BoldSlanted.29e2a87e.woff2 +0 -0
  334. package/storybook-static/fonts/ObjectSans-Heavy.d0b2f035.woff2 +0 -0
  335. package/storybook-static/fonts/ObjectSans-HeavySlanted.45e9c063.woff2 +0 -0
  336. package/storybook-static/fonts/ObjectSans-Light.f885dec3.woff2 +0 -0
  337. package/storybook-static/fonts/ObjectSans-LightSlanted.b8eb7c12.woff2 +0 -0
  338. package/storybook-static/fonts/ObjectSans-Regular.e4ea0b90.woff2 +0 -0
  339. package/storybook-static/fonts/ObjectSans-Slanted.57a90be9.woff2 +0 -0
  340. package/storybook-static/fonts/ObjectSans-Thin.86d44227.woff2 +0 -0
  341. package/storybook-static/fonts/ObjectSans-ThinSlanted.20342160.woff2 +0 -0
  342. package/storybook-static/iframe.html +0 -348
  343. package/storybook-static/index.html +0 -51
  344. package/storybook-static/main.7b4aec9c4352d4bb535b.manager.bundle.js +0 -1
  345. package/storybook-static/main.9e8c64c7.iframe.bundle.js +0 -1
  346. package/storybook-static/runtime~main.91a0c7330ab317d35c4a.manager.bundle.js +0 -1
  347. package/storybook-static/runtime~main.e4da100f.iframe.bundle.js +0 -1
  348. package/storybook-static/vendors~main.6660eda6.iframe.bundle.js +0 -7
  349. package/storybook-static/vendors~main.6660eda6.iframe.bundle.js.LICENSE.txt +0 -80
  350. package/storybook-static/vendors~main.6660eda6.iframe.bundle.js.map +0 -1
  351. package/storybook-static/vendors~main.f7f16cebbf3aa96a4f89.manager.bundle.js +0 -2
  352. package/storybook-static/vendors~main.f7f16cebbf3aa96a4f89.manager.bundle.js.LICENSE.txt +0 -110
  353. package/tsconfig.dist.json +0 -7
  354. package/tsconfig.json +0 -24
  355. package/vue.config.js +0 -5
@@ -0,0 +1,23 @@
1
+ import type { Ref } from 'vue';
2
+ import { computed } from 'vue';
3
+
4
+ let idCounter = 0;
5
+
6
+ /**
7
+ * Generates a unique ID for a component.
8
+ *
9
+ * This is a very rudimentary backport of Vue 3.5's `useId` composable that is
10
+ * _not_ stable across server and client.
11
+ *
12
+ * @param id - The ID to use. If not provided, a unique ID will be generated.
13
+ * @returns A computed value that returns the provided ID or a unique ID.
14
+ */
15
+ export function useId(id: Ref<string | null | undefined>): Ref<string> {
16
+ return computed(() => {
17
+ if (id.value) {
18
+ return id.value;
19
+ } else {
20
+ return `c-${idCounter++}`;
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,139 @@
1
+ import { Mask, MaskInput, type MaskInputOptions, type MaskOptions, type MaskaDetail } from 'maska';
2
+ import { type Ref, computed, watch } from 'vue';
3
+
4
+ import { useInternalValue } from '@propelinc/citrus-ui/src/composables/binding';
5
+
6
+ const toNumber = (value: number | string | null): number | null => {
7
+ if (value === null || value === '') {
8
+ return null;
9
+ } else if (typeof value === 'number') {
10
+ return value;
11
+ } else {
12
+ const parsed = parseInt(value, 10);
13
+ return isNaN(parsed) ? null : parsed;
14
+ }
15
+ };
16
+
17
+ export function useInputMask(
18
+ /** Element to be masked */
19
+ input: Ref<HTMLInputElement | null>,
20
+ /** Value passed into the input */
21
+ value: Ref<string>,
22
+ /**
23
+ * Options for the mask.
24
+ * @see https://beholdr.github.io/maska/v2/#/?id=mask-options
25
+ */
26
+ options: Ref<MaskInputOptions>
27
+ ): {
28
+ mask: Ref<Mask>;
29
+ maskedValue: Ref<string>;
30
+ unmaskedValue: Ref<string>;
31
+ } {
32
+ const internalValue = useInternalValue(value);
33
+
34
+ const maskOptions = computed<MaskInputOptions>((): MaskInputOptions => {
35
+ const onMaska = options.value.onMaska;
36
+ const onMaskaArray = Array.isArray(onMaska) ? onMaska : onMaska ? [onMaska] : [];
37
+ return {
38
+ ...options.value,
39
+ onMaska: [
40
+ (detail: MaskaDetail): void => {
41
+ internalValue.value = detail.unmasked;
42
+ },
43
+ ...onMaskaArray,
44
+ ],
45
+ };
46
+ });
47
+
48
+ let maskInput: MaskInput | null = null;
49
+ watch(input, (newInput) => {
50
+ if (newInput) {
51
+ maskInput = new MaskInput(newInput, maskOptions.value);
52
+ } else {
53
+ maskInput?.destroy();
54
+ maskInput = null;
55
+ }
56
+ });
57
+
58
+ const mask = computed(() => new Mask(maskOptions.value));
59
+ const maskedValue = computed(() => {
60
+ return mask.value ? mask.value.masked(internalValue.value) : value.value;
61
+ });
62
+
63
+ return {
64
+ mask,
65
+ maskedValue,
66
+ unmaskedValue: internalValue,
67
+ };
68
+ }
69
+
70
+ export function useMaskedMinLength(
71
+ minlength: Ref<number | string | null>,
72
+ value: Ref<string>,
73
+ options: Ref<MaskOptions>
74
+ ): Ref<number | null> {
75
+ const maskTokens = computed(() => {
76
+ // NOTE(mohan): '#', '@', '*' are the default tokens used by maska.
77
+ return ['#', '@', '*', ...Object.keys(options.value.tokens ?? {})];
78
+ });
79
+
80
+ // NOTE(mohan): We want `minlength` to only include token characters
81
+ // (e.g. '#' or '@') and ignore symbols like '-'. So, we iterate through
82
+ // the mask to see how many characters are required to hit minlength tokens.
83
+ const findMaskedMinLength = (mask: string, minlength: number): number => {
84
+ const maskArray = mask.split('');
85
+ const index = maskArray.findIndex((_, index) => {
86
+ const tokensInSlice = maskArray
87
+ .slice(0, index + 1)
88
+ .filter((char) => maskTokens.value.includes(char));
89
+ return tokensInSlice.length === minlength;
90
+ });
91
+
92
+ if (index === -1) {
93
+ return mask.length;
94
+ } else {
95
+ // Convert index to length
96
+ return index + 1;
97
+ }
98
+ };
99
+
100
+ const maskedMinLength = computed(() => {
101
+ const mask = options.value.mask;
102
+ const minlengthValue = toNumber(minlength.value);
103
+
104
+ if (!minlengthValue) {
105
+ return null;
106
+ } else if (!mask) {
107
+ return minlengthValue;
108
+ } else {
109
+ if (typeof mask === 'function') {
110
+ return findMaskedMinLength(mask(value.value), minlengthValue);
111
+ } else if (typeof mask === 'string') {
112
+ return findMaskedMinLength(mask, minlengthValue);
113
+ } else if (Array.isArray(mask)) {
114
+ return Math.min(...mask.map((m) => findMaskedMinLength(m, minlengthValue!)));
115
+ } else {
116
+ return minlengthValue;
117
+ }
118
+ }
119
+ });
120
+
121
+ return maskedMinLength;
122
+ }
123
+
124
+ export function useMaskedMaxLength(
125
+ maxlength: Ref<number | string | null>,
126
+ options: Ref<MaskOptions>
127
+ ): Ref<number | null> {
128
+ return computed(() => {
129
+ const mask = options.value.mask;
130
+ const maxlengthValue = toNumber(maxlength.value);
131
+ if (!maxlengthValue) {
132
+ return null;
133
+ } else if (!mask) {
134
+ return maxlengthValue;
135
+ } else {
136
+ return Math.max(maxlengthValue, mask.length);
137
+ }
138
+ });
139
+ }
@@ -0,0 +1,64 @@
1
+ import { type ComputedRef, type Ref, computed } from 'vue';
2
+ import type { RouteLocationRaw } from 'vue-router';
3
+ import { useLink } from 'vue-router';
4
+
5
+ interface RouterComposableHelpers {
6
+ /**
7
+ * V-bind this to an element to make it accessible as a link.
8
+ */
9
+ linkAttrs: Ref<{
10
+ role?: string;
11
+ tabindex?: string;
12
+ href?: string;
13
+ }>;
14
+ /**
15
+ * The href to link to.
16
+ */
17
+ href: Ref<string | undefined>;
18
+ /**
19
+ * Navigates to the given route or href. If you're not using an `<a>` tag,
20
+ * you'll need to call this on `@click` and on `@keydown.enter`
21
+ */
22
+ navigate: (event: MouseEvent) => Promise<void>;
23
+ }
24
+
25
+ function useLinkAttrs(href: Ref<string | undefined>): ComputedRef<{
26
+ role?: string;
27
+ tabindex?: string;
28
+ href?: string;
29
+ }> {
30
+ return computed(() => (href.value ? { role: 'link', tabindex: '0', href: href.value } : {}));
31
+ }
32
+
33
+ /**
34
+ * Adds router-link functionality to any component. Accepts an `undefined`
35
+ * route, which disables routing.
36
+ *
37
+ * @param to - The route to link to.
38
+ * @returns A few helpers can be used to link to the given route.
39
+ */
40
+ export function useRouterLink(to: Ref<RouteLocationRaw | undefined>): RouterComposableHelpers {
41
+ // NOTE(mohan): We need to provide a default here or else `useLink` will throw
42
+ // an error.
43
+ const toWithDefault = computed(() => to.value ?? '');
44
+ const useLinkResult = useLink({ to: toWithDefault });
45
+ const { navigate: _navigate, href: _href } = useLinkResult;
46
+
47
+ const isRouterLink = computed(() => !!to.value);
48
+ const href = computed(() => (isRouterLink.value ? _href.value : undefined));
49
+ const linkAttrs = useLinkAttrs(href);
50
+ const navigate = async (event: MouseEvent): Promise<void> => {
51
+ if (isRouterLink.value) {
52
+ try {
53
+ await _navigate(event);
54
+ } catch (error) {
55
+ // NOTE(mohan): This matches RouterLink's behavior, which doesn't throw
56
+ // on navigation errors.
57
+ // eslint-disable-next-line no-console
58
+ console.debug(error);
59
+ }
60
+ }
61
+ };
62
+
63
+ return { linkAttrs, href, navigate };
64
+ }
@@ -0,0 +1,57 @@
1
+ import type { Ref, VNode } from 'vue';
2
+ import { Comment, Fragment, computed, useSlots } from 'vue';
3
+
4
+ /**
5
+ * Recursively checks if a VNode or its children are visible.
6
+ */
7
+ function isVisible(vnode: VNode): boolean {
8
+ if (vnode.type === Comment) {
9
+ return false;
10
+ }
11
+
12
+ // Handle fragments and templates recursively
13
+ if (vnode.type === Fragment || vnode.type === 'template') {
14
+ const children = vnode.children;
15
+
16
+ // Empty fragment is not visible
17
+ if (!children || (Array.isArray(children) && children.length === 0)) {
18
+ return false;
19
+ }
20
+
21
+ // If fragment has children, check if any child is visible
22
+ if (Array.isArray(children)) {
23
+ return children.some((child) => {
24
+ // Only check VNode children (filter out strings, numbers, etc.)
25
+ if (typeof child === 'object' && child !== null && 'type' in child) {
26
+ return isVisible(child as VNode);
27
+ }
28
+ // Non-VNode children (like strings) are considered visible
29
+ return true;
30
+ });
31
+ }
32
+ }
33
+
34
+ // All other vnodes are visible
35
+ return true;
36
+ }
37
+
38
+ export function useSlotHasContent(key: string): Ref<boolean> {
39
+ const slots = useSlots();
40
+
41
+ return computed(() => {
42
+ const slot = slots[key];
43
+
44
+ if (!slot) {
45
+ return false;
46
+ }
47
+
48
+ const slotContent = slot();
49
+
50
+ // NOTE(slanden): In Vue 3, nodes which are rendered conditionally with v-if
51
+ // are rendered as a comment like `<!--v-if--/>` when the condition is false.
52
+ // We need to filter these out to get only actually rendered content.
53
+ const visibleContent = slotContent.filter(isVisible);
54
+
55
+ return visibleContent.length > 0;
56
+ });
57
+ }
@@ -0,0 +1,64 @@
1
+ import { type Ref, inject, provide, ref } from 'vue';
2
+
3
+ import type { ToastVariant } from '../types';
4
+
5
+ // NOTE(slanden): Cannot use a symbol here because it may break the inject/provide chain across MFEs
6
+ const GlobalToast = 'GLOBAL_TOAST';
7
+
8
+ export interface Toast {
9
+ id: number;
10
+ dataTest?: string;
11
+ message: string;
12
+ variant: ToastVariant;
13
+ }
14
+
15
+ interface ToastInjection {
16
+ dismissToast: (id: Toast['id']) => void;
17
+ showToast: (toast: Omit<Toast, 'id'>) => void;
18
+ toasts: Ref<Toast[]>;
19
+ }
20
+
21
+ let id = 0;
22
+
23
+ function getNextIdDefault(): number {
24
+ return id++;
25
+ }
26
+
27
+ /**
28
+ * Custom provider for our toasts. Enables us to declaratively manage toasts in event handlers.
29
+ *
30
+ * NOTE: injecting `getNextId` allows us to test the composable without worry of side effects
31
+ */
32
+ export function useToastProvider(getNextId: () => number = getNextIdDefault): ToastInjection {
33
+ const toasts = ref<Toast[]>([]);
34
+
35
+ function showToast(newToast: Omit<Toast, 'id'>): Toast['id'] {
36
+ const toastId = getNextId();
37
+
38
+ toasts.value.push({ ...newToast, id: toastId });
39
+
40
+ return toastId;
41
+ }
42
+
43
+ function dismissToast(id: Toast['id']): void {
44
+ toasts.value = toasts.value.filter((toast) => toast.id !== id);
45
+ }
46
+
47
+ const context: ToastInjection = {
48
+ dismissToast,
49
+ showToast,
50
+ toasts,
51
+ };
52
+
53
+ provide(GlobalToast, context);
54
+
55
+ return context;
56
+ }
57
+
58
+ export function useToast(): ToastInjection {
59
+ return inject<ToastInjection>(GlobalToast, {
60
+ dismissToast: () => {},
61
+ showToast: () => {},
62
+ toasts: ref<Toast[]>([]),
63
+ });
64
+ }
@@ -0,0 +1,214 @@
1
+ import type { Ref } from 'vue';
2
+ import { computed, inject, onBeforeUnmount, ref, unref, watch } from 'vue';
3
+
4
+ import { VALIDATIONS } from '@propelinc/citrus-ui/src/services/injections/forms';
5
+ import type { ValidationState } from '@propelinc/citrus-ui/src/types/CForm';
6
+
7
+ export type Rule<Value extends string | boolean> = (value: Value) => string | boolean;
8
+ export type Rules<Value extends string | boolean> = Rule<Value>[];
9
+
10
+ export type ValidationEvent = 'blur' | 'input' | 'change';
11
+
12
+ export interface UseValidationRuleCheckOptions<Value extends string | boolean> {
13
+ value: Ref<Value>;
14
+ rules: Ref<Rules<Value>>;
15
+ required?: Ref<boolean>;
16
+ onValidationResultChange?: ({
17
+ message,
18
+ valid,
19
+ }: {
20
+ message: string | null;
21
+ valid: boolean;
22
+ }) => void;
23
+ }
24
+
25
+ interface UseValidationRuleCheckReturn {
26
+ /** The validation error message, if any. */
27
+ message: Ref<string | null>;
28
+ /** Whether the input element is valid. This is only set after
29
+ * `startValidating` is called. */
30
+ valid: Ref<boolean>;
31
+ /** Whether the input element is valid regardless of whether `startValidating`
32
+ * has been called. */
33
+ passesRules: Ref<boolean>;
34
+ /** Starts the validation process. */
35
+ startValidating: () => boolean;
36
+ /** Clears the validation results. */
37
+ clearValidations: () => void;
38
+ }
39
+
40
+ /**
41
+ * A composable that provides manual control of validation rules for a form
42
+ * control. For full validation support, please see the `useValidations`
43
+ * composable.
44
+ *
45
+ * @see {@link useValidations}
46
+ *
47
+ * @param options - The options for the composable.
48
+ */
49
+ export function useValidationRuleCheck<Value extends string | boolean>(
50
+ options: UseValidationRuleCheckOptions<Value>
51
+ ): UseValidationRuleCheckReturn {
52
+ const { value, rules, required } = options;
53
+ const active = ref(false);
54
+
55
+ const message = ref<string | null>(null);
56
+ const valid = ref(true);
57
+
58
+ const isEmpty = (v: Value): boolean =>
59
+ v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
60
+
61
+ const requiredRule: Rule<Value> = (val: Value): boolean => {
62
+ return required?.value ? !isEmpty(val) : true;
63
+ };
64
+
65
+ const rulesResult = computed(() => {
66
+ const allRules: Rules<Value> = [...rules.value, requiredRule];
67
+ for (const rule of allRules) {
68
+ const result = rule(value.value);
69
+ if (result === false || typeof result === 'string') {
70
+ return result;
71
+ }
72
+ }
73
+ return true;
74
+ });
75
+
76
+ const passesRules = computed(() => rulesResult.value === true);
77
+
78
+ const validate = (): boolean => {
79
+ if (active.value) {
80
+ valid.value = passesRules.value;
81
+ message.value = typeof rulesResult.value === 'string' ? rulesResult.value : null;
82
+ options.onValidationResultChange?.({ message: message.value, valid: valid.value });
83
+ }
84
+
85
+ return valid.value;
86
+ };
87
+
88
+ watch(value, () => validate(), { immediate: true });
89
+ watch(rules, () => validate());
90
+
91
+ if (required) {
92
+ watch(required, () => validate());
93
+ }
94
+
95
+ const startValidating = (): boolean => {
96
+ active.value = true;
97
+ return validate();
98
+ };
99
+
100
+ const clearValidations = (): void => {
101
+ message.value = null;
102
+ valid.value = true;
103
+ active.value = false;
104
+ };
105
+
106
+ return { message, valid, startValidating, clearValidations, passesRules };
107
+ }
108
+
109
+ export interface UseValidationsOptions<Value extends string | boolean> {
110
+ id?: Ref<string>;
111
+ /** The input element. The `aria-invalid` attribute will be set on this
112
+ * element. */
113
+ field?: Ref<HTMLElement | null>;
114
+ /** The value of the input element. */
115
+ value: Ref<Value>;
116
+ /** The validation rules to apply to the input element. */
117
+ rules: Ref<Rules<Value>>;
118
+ /** Value of the required attribute. */
119
+ required?: Ref<boolean>;
120
+ /** Which event to wait for before starting validation. */
121
+ validateOn?: ValidationEvent | Ref<ValidationEvent>;
122
+ }
123
+
124
+ interface UseValidationsReturn {
125
+ /** The validation error message, if any. */
126
+ message: Ref<string | null>;
127
+ /** Whether the input element is valid. */
128
+ valid: Ref<boolean | null>;
129
+ /** The event listeners to bind to the component using `v-on`. */
130
+ listeners: Record<string, (event: Event) => void>;
131
+ /** Triggers validation. */
132
+ startValidating: () => boolean;
133
+ /** Clears the validation results. */
134
+ clearValidations: () => void;
135
+ }
136
+
137
+ /** Registers this field with a parent form to enable form-wide validation. */
138
+ export type ValidationStateWithOptionalId = Omit<ValidationState, 'id'> & { id?: string };
139
+ export function useFormValidationSupport(state: ValidationStateWithOptionalId): void {
140
+ const formValidations = inject(VALIDATIONS, null);
141
+ if (formValidations) {
142
+ if (!state.id) {
143
+ throw new Error('id is required to support form validations');
144
+ }
145
+
146
+ formValidations.register(state as ValidationState);
147
+ }
148
+
149
+ onBeforeUnmount(() => {
150
+ if (formValidations && state.id) {
151
+ formValidations.unregister(state.id);
152
+ }
153
+ });
154
+ }
155
+
156
+ /**
157
+ * A composable that provides validation support for form controls. Please make sure to
158
+ * `v-bind` the returned `attrs` and `v-on` the returned `listeners` to the component.
159
+ *
160
+ * @param options - The options for the composable.
161
+ */
162
+ export function useValidations<Value extends string | boolean>(
163
+ options: UseValidationsOptions<Value>
164
+ ): UseValidationsReturn {
165
+ const validateOn = computed(() => unref(options.validateOn) ?? 'blur');
166
+
167
+ const onValidationResultChange = ({ valid }: { valid: boolean }): void => {
168
+ if (options.field?.value) {
169
+ options.field.value.setAttribute('aria-invalid', valid ? 'false' : 'true');
170
+ }
171
+ };
172
+
173
+ const { message, valid, startValidating, passesRules, clearValidations } = useValidationRuleCheck(
174
+ {
175
+ value: options.value,
176
+ rules: options.rules,
177
+ required: options.required,
178
+ onValidationResultChange,
179
+ }
180
+ );
181
+
182
+ useFormValidationSupport({
183
+ id: options.id?.value,
184
+ valid: passesRules,
185
+ validate: startValidating,
186
+ field: options.field,
187
+ });
188
+
189
+ const onInput = (): void => {
190
+ if (validateOn.value === 'input') {
191
+ startValidating();
192
+ }
193
+ };
194
+
195
+ const onBlur = (): void => {
196
+ // NOTE(slanden): We always validate on blur, regardless of the validateOn setting.
197
+ startValidating();
198
+ };
199
+
200
+ const onChange = (): void => {
201
+ if (validateOn.value === 'change') {
202
+ startValidating();
203
+ }
204
+ };
205
+
206
+ watch(validateOn, (newValue, oldValue): void => {
207
+ if (['input', 'change'].includes(newValue) && newValue !== oldValue) {
208
+ startValidating();
209
+ }
210
+ });
211
+
212
+ const listeners = { input: onInput, blur: onBlur, change: onChange };
213
+ return { message, valid, listeners, startValidating, clearValidations };
214
+ }
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
- import plugin from '@/plugin';
2
-
3
- import '@/styles/main.scss';
1
+ import plugin from '@propelinc/citrus-ui/src/plugin';
4
2
 
5
3
  export default plugin;
6
4
 
7
- export * as Colors from '@/styles/_colors.scss';
8
- export { default as Theme } from '@/colors/theme';
9
- export { default as Icons } from '@/theme/icons';
5
+ export { default as Colors } from '@propelinc/citrus-ui/src/colors/colors';
6
+ export { default as Theme } from '@propelinc/citrus-ui/src/colors/theme';
7
+ export { default as Icons } from '@propelinc/citrus-ui/src/theme/icons';
10
8
 
11
- export * from '@/components/index';
9
+ export * from '@propelinc/citrus-ui/src/types';
10
+ export * from '@propelinc/citrus-ui/src/components';
11
+ export * from '@propelinc/citrus-ui/src/services/directives';
package/src/plugin.ts CHANGED
@@ -1,12 +1,19 @@
1
- import _Vue, { PluginObject } from 'vue';
1
+ import type { App, DefineComponent, Plugin } from 'vue';
2
2
 
3
- import * as Components from '@/components';
3
+ import * as Components from '@propelinc/citrus-ui/src/components';
4
+ import { scrollIntoView, tapAnimation } from '@propelinc/citrus-ui/src/services/directives';
4
5
 
5
- const plugin: PluginObject<never> = {
6
- install(Vue: typeof _Vue): void {
7
- Object.entries(Components).forEach(([name, component]) => {
8
- Vue.component(name, component);
6
+ const plugin: Plugin = {
7
+ install(app: App): void {
8
+ Object.entries(Components).forEach(([name, component]): void => {
9
+ app.component(name, component as DefineComponent);
9
10
  });
11
+
12
+ // TODO: this has to be passed to the vue() plugin for Vite instead
13
+ app.config.compilerOptions.isCustomElement = (tag): boolean => tag.startsWith('sl-');
14
+
15
+ app.directive('scroll-into-view', scrollIntoView);
16
+ app.directive('tap-animation', tapAnimation);
10
17
  },
11
18
  };
12
19