@vc-shell/framework 1.0.38 → 1.0.40

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 (256) hide show
  1. package/core/api/index.ts +1 -0
  2. package/core/api/platform.ts +8332 -0
  3. package/core/composables/index.ts +8 -0
  4. package/core/composables/useAutosave/index.ts +57 -0
  5. package/core/composables/useFunctions/debounce.ts +18 -0
  6. package/core/composables/useFunctions/delay.ts +7 -0
  7. package/core/composables/useFunctions/index.ts +21 -0
  8. package/core/composables/useFunctions/once.ts +14 -0
  9. package/core/composables/useFunctions/sleep.ts +4 -0
  10. package/core/composables/useFunctions/throttle.ts +17 -0
  11. package/core/composables/useI18n/index.ts +28 -0
  12. package/core/composables/useLogger/index.ts +24 -0
  13. package/core/composables/useNotifications/index.ts +116 -0
  14. package/core/composables/usePermissions/index.ts +32 -0
  15. package/core/composables/useSettings/index.ts +62 -0
  16. package/core/composables/useUser/index.ts +266 -0
  17. package/core/directives/autofocus/index.ts +9 -0
  18. package/core/directives/click-outside/index.ts +21 -0
  19. package/core/directives/index.ts +4 -0
  20. package/core/directives/loading/index.ts +28 -0
  21. package/core/directives/permissions/index.ts +20 -0
  22. package/core/plugins/index.ts +1 -0
  23. package/core/plugins/validation/index.ts +2 -0
  24. package/core/plugins/validation/rules.ts +196 -0
  25. package/core/types/index.ts +92 -0
  26. package/core/utilities/camelToSnake.ts +7 -0
  27. package/core/utilities/index.ts +1 -0
  28. package/dist/core/composables/useNotifications/index.d.ts +1 -1
  29. package/dist/core/composables/useNotifications/index.d.ts.map +1 -1
  30. package/dist/core/composables/useSettings/index.d.ts +10 -1
  31. package/dist/core/composables/useSettings/index.d.ts.map +1 -1
  32. package/dist/core/composables/useUser/index.d.ts +2 -2
  33. package/dist/core/composables/useUser/index.d.ts.map +1 -1
  34. package/dist/core/plugins/validation/index.d.ts.map +1 -1
  35. package/dist/core/types/index.d.ts +1 -1
  36. package/dist/core/types/index.d.ts.map +1 -1
  37. package/dist/framework.js +72 -99
  38. package/dist/framework.js.map +1 -1
  39. package/dist/index.d.ts +3 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts +1 -1
  42. package/dist/shared/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
  43. package/dist/shared/app-switcher/index.d.ts +2 -2
  44. package/dist/shared/app-switcher/index.d.ts.map +1 -1
  45. package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts +1 -1
  46. package/dist/shared/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  47. package/dist/shared/blade-navigation/types/index.d.ts +1 -1
  48. package/dist/shared/blade-navigation/types/index.d.ts.map +1 -1
  49. package/dist/style.css +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -0
  51. package/dist/ui/components/atoms/vc-badge/index.d.ts +5 -0
  52. package/dist/ui/components/atoms/vc-badge/index.d.ts.map +1 -0
  53. package/dist/ui/components/atoms/vc-badge/vc-badge-model.d.ts +28 -0
  54. package/dist/ui/components/atoms/vc-badge/vc-badge-model.d.ts.map +1 -0
  55. package/dist/{components → ui/components}/atoms/vc-badge/vc-badge.stories.d.ts +0 -0
  56. package/dist/ui/components/atoms/vc-badge/vc-badge.stories.d.ts.map +1 -0
  57. package/dist/ui/components/atoms/vc-button/index.d.ts +5 -0
  58. package/dist/ui/components/atoms/vc-button/index.d.ts.map +1 -0
  59. package/dist/ui/components/atoms/vc-button/vc-button-model.d.ts +28 -0
  60. package/dist/ui/components/atoms/vc-button/vc-button-model.d.ts.map +1 -0
  61. package/dist/{components → ui/components}/atoms/vc-button/vc-button.stories.d.ts +0 -0
  62. package/dist/ui/components/atoms/vc-button/vc-button.stories.d.ts.map +1 -0
  63. package/dist/{components → ui/components}/atoms/vc-checkbox/vc-checkbox.stories.d.ts +0 -0
  64. package/dist/ui/components/atoms/vc-checkbox/vc-checkbox.stories.d.ts.map +1 -0
  65. package/dist/{components → ui/components}/atoms/vc-container/vc-container.stories.d.ts +0 -0
  66. package/dist/ui/components/atoms/vc-container/vc-container.stories.d.ts.map +1 -0
  67. package/dist/{components → ui/components}/atoms/vc-hint/vc-hint.stories.d.ts +0 -0
  68. package/dist/ui/components/atoms/vc-hint/vc-hint.stories.d.ts.map +1 -0
  69. package/dist/{components → ui/components}/atoms/vc-icon/vc-icon.stories.d.ts +0 -0
  70. package/dist/ui/components/atoms/vc-icon/vc-icon.stories.d.ts.map +1 -0
  71. package/dist/{components → ui/components}/atoms/vc-image/vc-image.stories.d.ts +0 -0
  72. package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts.map +1 -0
  73. package/dist/{components → ui/components}/atoms/vc-label/vc-label.stories.d.ts +0 -0
  74. package/dist/ui/components/atoms/vc-label/vc-label.stories.d.ts.map +1 -0
  75. package/dist/{components → ui/components}/atoms/vc-link/vc-link.stories.d.ts +0 -0
  76. package/dist/ui/components/atoms/vc-link/vc-link.stories.d.ts.map +1 -0
  77. package/dist/{components → ui/components}/atoms/vc-progress/vc-progress.stories.d.ts +0 -0
  78. package/dist/ui/components/atoms/vc-progress/vc-progress.stories.d.ts.map +1 -0
  79. package/dist/{components → ui/components}/atoms/vc-status/vc-status.stories.d.ts +0 -0
  80. package/dist/ui/components/atoms/vc-status/vc-status.stories.d.ts.map +1 -0
  81. package/dist/{components → ui/components}/atoms/vc-switch/vc-switch.stories.d.ts +0 -0
  82. package/dist/ui/components/atoms/vc-switch/vc-switch.stories.d.ts.map +1 -0
  83. package/dist/{components → ui/components}/index.d.ts +4 -3
  84. package/dist/ui/components/index.d.ts.map +1 -0
  85. package/dist/{components → ui/components}/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.d.ts +0 -0
  86. package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.d.ts.map +1 -0
  87. package/dist/{components → ui/components}/molecules/vc-form/vc-form.stories.d.ts +0 -0
  88. package/dist/ui/components/molecules/vc-form/vc-form.stories.d.ts.map +1 -0
  89. package/dist/ui/components/molecules/vc-input/index.d.ts +5 -0
  90. package/dist/ui/components/molecules/vc-input/index.d.ts.map +1 -0
  91. package/dist/ui/components/molecules/vc-input/vc-input-model.d.ts +140 -0
  92. package/dist/ui/components/molecules/vc-input/vc-input-model.d.ts.map +1 -0
  93. package/dist/ui/components/molecules/vc-input-currency/index.d.ts +5 -0
  94. package/dist/ui/components/molecules/vc-input-currency/index.d.ts.map +1 -0
  95. package/dist/ui/components/molecules/vc-input-currency/vc-input-currency-model.d.ts +124 -0
  96. package/dist/ui/components/molecules/vc-input-currency/vc-input-currency-model.d.ts.map +1 -0
  97. package/dist/{components → ui/components}/molecules/vc-pagination/vc-pagination.stories.d.ts +0 -0
  98. package/dist/ui/components/molecules/vc-pagination/vc-pagination.stories.d.ts.map +1 -0
  99. package/dist/{components → ui/components}/molecules/vc-rating/vc-rating.stories.d.ts +0 -0
  100. package/dist/ui/components/molecules/vc-rating/vc-rating.stories.d.ts.map +1 -0
  101. package/dist/ui/components/molecules/vc-select/index.d.ts +5 -0
  102. package/dist/ui/components/molecules/vc-select/index.d.ts.map +1 -0
  103. package/dist/ui/components/molecules/vc-select/vc-select-model.d.ts +207 -0
  104. package/dist/ui/components/molecules/vc-select/vc-select-model.d.ts.map +1 -0
  105. package/dist/{components → ui/components}/molecules/vc-textarea/vc-textarea.stories.d.ts +0 -0
  106. package/dist/ui/components/molecules/vc-textarea/vc-textarea.stories.d.ts.map +1 -0
  107. package/dist/{components → ui/components}/organisms/vc-app/vc-app.stories.d.ts +0 -0
  108. package/dist/ui/components/organisms/vc-app/vc-app.stories.d.ts.map +1 -0
  109. package/dist/{components → ui/components}/organisms/vc-blade/vc-blade.stories.d.ts +0 -0
  110. package/dist/ui/components/organisms/vc-blade/vc-blade.stories.d.ts.map +1 -0
  111. package/dist/{components → ui/components}/organisms/vc-login-form/vc-login-form.stories.d.ts +0 -0
  112. package/dist/ui/components/organisms/vc-login-form/vc-login-form.stories.d.ts.map +1 -0
  113. package/dist/{components → ui/components}/organisms/vc-popup/vc-popup.stories.d.ts +0 -0
  114. package/dist/ui/components/organisms/vc-popup/vc-popup.stories.d.ts.map +1 -0
  115. package/dist/{components → ui/components}/organisms/vc-table/vc-table.stories.d.ts +0 -0
  116. package/dist/ui/components/organisms/vc-table/vc-table.stories.d.ts.map +1 -0
  117. package/dist/ui/types/index.d.ts +13 -0
  118. package/dist/ui/types/index.d.ts.map +1 -0
  119. package/dist/ui/types/ts-helpers.d.ts +13 -0
  120. package/dist/ui/types/ts-helpers.d.ts.map +1 -0
  121. package/dist/vite.config.d.ts.map +1 -1
  122. package/package.json +30 -10
  123. package/shared/app-switcher/components/index.ts +1 -0
  124. package/shared/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +90 -0
  125. package/shared/app-switcher/composables/index.ts +1 -0
  126. package/shared/app-switcher/composables/useAppSwitcher/index.ts +54 -0
  127. package/shared/app-switcher/index.ts +14 -0
  128. package/shared/assets/components/assets-details/assets-details.vue +138 -0
  129. package/shared/assets/components/index.ts +1 -0
  130. package/shared/assets/index.ts +19 -0
  131. package/shared/assets/locales/en.json +29 -0
  132. package/shared/assets/locales/index.ts +2 -0
  133. package/shared/blade-navigation/components/index.ts +1 -0
  134. package/shared/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +84 -0
  135. package/shared/blade-navigation/composables/index.ts +1 -0
  136. package/shared/blade-navigation/composables/useBladeNavigation/index.ts +216 -0
  137. package/shared/blade-navigation/index.ts +15 -0
  138. package/shared/blade-navigation/types/index.ts +52 -0
  139. package/shared/index.ts +16 -0
  140. package/tailwind.config.js +4 -3
  141. package/ui/components/atoms/vc-badge/index.ts +7 -0
  142. package/ui/components/atoms/vc-badge/vc-badge-model.ts +30 -0
  143. package/ui/components/atoms/vc-badge/vc-badge.stories.ts +27 -0
  144. package/ui/components/atoms/vc-badge/vc-badge.vue +57 -0
  145. package/ui/components/atoms/vc-button/index.ts +7 -0
  146. package/ui/components/atoms/vc-button/vc-button-model.ts +30 -0
  147. package/ui/components/atoms/vc-button/vc-button.stories.ts +34 -0
  148. package/ui/components/atoms/vc-button/vc-button.vue +219 -0
  149. package/ui/components/atoms/vc-card/vc-card.vue +137 -0
  150. package/ui/components/atoms/vc-checkbox/vc-checkbox.stories.ts +25 -0
  151. package/ui/components/atoms/vc-checkbox/vc-checkbox.vue +130 -0
  152. package/ui/components/atoms/vc-col/vc-col.vue +22 -0
  153. package/ui/components/atoms/vc-container/vc-container.stories.ts +31 -0
  154. package/ui/components/atoms/vc-container/vc-container.vue +222 -0
  155. package/ui/components/atoms/vc-hint/vc-hint.stories.ts +23 -0
  156. package/ui/components/atoms/vc-hint/vc-hint.vue +11 -0
  157. package/ui/components/atoms/vc-icon/vc-icon.stories.ts +32 -0
  158. package/ui/components/atoms/vc-icon/vc-icon.vue +36 -0
  159. package/ui/components/atoms/vc-image/vc-image.stories.ts +40 -0
  160. package/ui/components/atoms/vc-image/vc-image.vue +122 -0
  161. package/ui/components/atoms/vc-info-row/vc-info-row.vue +42 -0
  162. package/ui/components/atoms/vc-label/vc-label.stories.ts +23 -0
  163. package/ui/components/atoms/vc-label/vc-label.vue +49 -0
  164. package/ui/components/atoms/vc-link/vc-link.stories.ts +30 -0
  165. package/ui/components/atoms/vc-link/vc-link.vue +46 -0
  166. package/ui/components/atoms/vc-loading/vc-loading.vue +33 -0
  167. package/ui/components/atoms/vc-progress/vc-progress.stories.ts +25 -0
  168. package/ui/components/atoms/vc-progress/vc-progress.vue +65 -0
  169. package/ui/components/atoms/vc-row/vc-row.vue +13 -0
  170. package/ui/components/atoms/vc-status/vc-status.stories.ts +26 -0
  171. package/ui/components/atoms/vc-status/vc-status.vue +78 -0
  172. package/ui/components/atoms/vc-status-icon/vc-status-icon.vue +21 -0
  173. package/ui/components/atoms/vc-switch/vc-switch.stories.ts +27 -0
  174. package/ui/components/atoms/vc-switch/vc-switch.vue +100 -0
  175. package/ui/components/atoms/vc-widget/vc-widget.vue +85 -0
  176. package/ui/components/index.ts +44 -0
  177. package/ui/components/molecules/vc-breadcrumbs/_internal/vc-breadcrumbs-item/vc-breadcrumbs-item.vue +103 -0
  178. package/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.ts +39 -0
  179. package/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue +21 -0
  180. package/ui/components/molecules/vc-currency-input/vc-input.vue +436 -0
  181. package/ui/components/molecules/vc-editor/vc-editor.vue +117 -0
  182. package/ui/components/molecules/vc-file-upload/vc-file-upload.vue +134 -0
  183. package/ui/components/molecules/vc-form/vc-form.stories.ts +23 -0
  184. package/ui/components/molecules/vc-form/vc-form.vue +5 -0
  185. package/ui/components/molecules/vc-input/index.ts +8 -0
  186. package/ui/components/molecules/vc-input/vc-input-model.ts +150 -0
  187. package/ui/components/molecules/vc-input/vc-input.vue +324 -0
  188. package/ui/components/molecules/vc-input-currency/index.ts +8 -0
  189. package/ui/components/molecules/vc-input-currency/vc-input-currency-model.ts +128 -0
  190. package/ui/components/molecules/vc-input-currency/vc-input-currency.vue +86 -0
  191. package/ui/components/molecules/vc-multivalue/vc-multivalue.vue +447 -0
  192. package/ui/components/molecules/vc-notification/vc-notification.vue +101 -0
  193. package/ui/components/molecules/vc-pagination/vc-pagination.stories.ts +23 -0
  194. package/ui/components/molecules/vc-pagination/vc-pagination.vue +169 -0
  195. package/ui/components/molecules/vc-rating/vc-rating.stories.ts +23 -0
  196. package/ui/components/molecules/vc-rating/vc-rating.vue +77 -0
  197. package/ui/components/molecules/vc-select/index.ts +7 -0
  198. package/ui/components/molecules/vc-select/vc-select-model.ts +216 -0
  199. package/ui/components/molecules/vc-select/vc-select.vue +727 -0
  200. package/ui/components/molecules/vc-slider/vc-slider.vue +106 -0
  201. package/ui/components/molecules/vc-textarea/vc-textarea.stories.ts +23 -0
  202. package/ui/components/molecules/vc-textarea/vc-textarea.vue +155 -0
  203. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +150 -0
  204. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +148 -0
  205. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +157 -0
  206. package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +110 -0
  207. package/ui/components/organisms/vc-app/vc-app.stories.ts +75 -0
  208. package/ui/components/organisms/vc-app/vc-app.vue +169 -0
  209. package/ui/components/organisms/vc-blade/_internal/vc-blade-header/vc-blade-header.vue +126 -0
  210. package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue +223 -0
  211. package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +67 -0
  212. package/ui/components/organisms/vc-blade/vc-blade.stories.ts +46 -0
  213. package/ui/components/organisms/vc-blade/vc-blade.vue +87 -0
  214. package/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.vue +426 -0
  215. package/ui/components/organisms/vc-gallery/_internal/vc-gallery-item/vc-gallery-item.vue +123 -0
  216. package/ui/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue +93 -0
  217. package/ui/components/organisms/vc-gallery/vc-gallery.vue +186 -0
  218. package/ui/components/organisms/vc-login-form/vc-login-form.stories.ts +55 -0
  219. package/ui/components/organisms/vc-login-form/vc-login-form.vue +48 -0
  220. package/ui/components/organisms/vc-popup/vc-popup.stories.ts +23 -0
  221. package/ui/components/organisms/vc-popup/vc-popup.vue +97 -0
  222. package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +113 -0
  223. package/ui/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue +29 -0
  224. package/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue +152 -0
  225. package/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +272 -0
  226. package/ui/components/organisms/vc-table/vc-table.stories.ts +99 -0
  227. package/ui/components/organisms/vc-table/vc-table.vue +638 -0
  228. package/ui/types/index.ts +38 -0
  229. package/ui/types/ts-helpers.ts +46 -0
  230. package/dist/components/atoms/vc-badge/vc-badge.stories.d.ts.map +0 -1
  231. package/dist/components/atoms/vc-button/vc-button.stories.d.ts.map +0 -1
  232. package/dist/components/atoms/vc-checkbox/vc-checkbox.stories.d.ts.map +0 -1
  233. package/dist/components/atoms/vc-container/vc-container.stories.d.ts.map +0 -1
  234. package/dist/components/atoms/vc-hint/vc-hint.stories.d.ts.map +0 -1
  235. package/dist/components/atoms/vc-icon/vc-icon.stories.d.ts.map +0 -1
  236. package/dist/components/atoms/vc-image/vc-image.stories.d.ts.map +0 -1
  237. package/dist/components/atoms/vc-label/vc-label.stories.d.ts.map +0 -1
  238. package/dist/components/atoms/vc-link/vc-link.stories.d.ts.map +0 -1
  239. package/dist/components/atoms/vc-progress/vc-progress.stories.d.ts.map +0 -1
  240. package/dist/components/atoms/vc-status/vc-status.stories.d.ts.map +0 -1
  241. package/dist/components/atoms/vc-switch/vc-switch.stories.d.ts.map +0 -1
  242. package/dist/components/index.d.ts.map +0 -1
  243. package/dist/components/molecules/vc-breadcrumbs/vc-breadcrumbs.stories.d.ts.map +0 -1
  244. package/dist/components/molecules/vc-form/vc-form.stories.d.ts.map +0 -1
  245. package/dist/components/molecules/vc-input/vc-input.stories.d.ts +0 -7
  246. package/dist/components/molecules/vc-input/vc-input.stories.d.ts.map +0 -1
  247. package/dist/components/molecules/vc-pagination/vc-pagination.stories.d.ts.map +0 -1
  248. package/dist/components/molecules/vc-rating/vc-rating.stories.d.ts.map +0 -1
  249. package/dist/components/molecules/vc-select/vc-select.stories.d.ts +0 -7
  250. package/dist/components/molecules/vc-select/vc-select.stories.d.ts.map +0 -1
  251. package/dist/components/molecules/vc-textarea/vc-textarea.stories.d.ts.map +0 -1
  252. package/dist/components/organisms/vc-app/vc-app.stories.d.ts.map +0 -1
  253. package/dist/components/organisms/vc-blade/vc-blade.stories.d.ts.map +0 -1
  254. package/dist/components/organisms/vc-login-form/vc-login-form.stories.d.ts.map +0 -1
  255. package/dist/components/organisms/vc-popup/vc-popup.stories.d.ts.map +0 -1
  256. package/dist/components/organisms/vc-table/vc-table.stories.d.ts.map +0 -1
@@ -0,0 +1,436 @@
1
+ <template>
2
+ <div
3
+ class="vc-input"
4
+ :class="[
5
+ `vc-input_${type}`,
6
+ {
7
+ 'vc-input_clearable': clearable,
8
+ 'vc-input_error': errorMessage,
9
+ 'vc-input_disabled': disabled,
10
+ },
11
+ ]"
12
+ >
13
+ <!-- Input label -->
14
+ <VcLabel v-if="label" class="tw-mb-2" :required="isRequired">
15
+ <span>{{ label }}</span>
16
+ <template v-if="tooltip" v-slot:tooltip>{{ tooltip }}</template>
17
+ </VcLabel>
18
+
19
+ <!-- Input field -->
20
+ <div class="vc-input__field-wrapper">
21
+ <input
22
+ class="vc-input__field"
23
+ :placeholder="placeholder"
24
+ :type="internalType"
25
+ :value="calcValue"
26
+ :disabled="disabled"
27
+ @input="onInput"
28
+ @blur="$emit('blur')"
29
+ ref="inputRef"
30
+ :max="max"
31
+ :name="name"
32
+ :maxlength="maxchars"
33
+ />
34
+
35
+ <!-- Dropdown button -->
36
+ <div v-if="options && options.length" class="vc-input__dropdown-wrap">
37
+ <div
38
+ @click.stop="showDrop"
39
+ aria-describedby="tooltip"
40
+ ref="toggleDropRef"
41
+ :class="[
42
+ { 'vc-input__dropdown-btn_disabled': disabled },
43
+ 'vc-input__dropdown-btn',
44
+ ]"
45
+ >
46
+ {{ optionsValue }}
47
+ </div>
48
+ <teleport to="#app">
49
+ <div
50
+ v-if="dropActive"
51
+ ref="dropRef"
52
+ role="tooltip"
53
+ class="vc-input__dropdown"
54
+ v-click-outside="closeDrop"
55
+ >
56
+ <p class="vc-input__dropdown-title">{{ optionsTitle }}</p>
57
+ <input
58
+ class="vc-input__dropdown-search"
59
+ v-model="search"
60
+ ref="searchInput"
61
+ />
62
+ <ul class="vc-input__dropdown-list">
63
+ <li v-for="(item, i) in searchFilter" :key="i">
64
+ <button
65
+ @click="onItemSelect(item)"
66
+ :class="[
67
+ 'vc-input__dropdown-selector',
68
+ {
69
+ 'vc-input__dropdown-selector-active':
70
+ item[displayProperty] === optionsValue,
71
+ },
72
+ ]"
73
+ >
74
+ {{ item[displayProperty] }}
75
+ </button>
76
+ </li>
77
+ </ul>
78
+ </div>
79
+ </teleport>
80
+ </div>
81
+
82
+ <!-- Input clear button -->
83
+ <div
84
+ v-if="clearable && modelValue && !disabled && type !== 'password'"
85
+ class="vc-input__clear"
86
+ @click="onReset"
87
+ >
88
+ <VcIcon size="s" icon="fas fa-times"></VcIcon>
89
+ </div>
90
+
91
+ <div
92
+ class="vc-input__showhide"
93
+ v-if="type === 'password' && internalType === 'password'"
94
+ @click="internalType = 'text'"
95
+ >
96
+ <VcIcon size="s" icon="fas fa-eye-slash"></VcIcon>
97
+ </div>
98
+
99
+ <div
100
+ class="vc-input__showhide"
101
+ v-if="type === 'password' && internalType === 'text'"
102
+ @click="internalType = 'password'"
103
+ >
104
+ <VcIcon size="s" icon="fas fa-eye"></VcIcon>
105
+ </div>
106
+ </div>
107
+
108
+ <slot v-if="errorMessage" name="error">
109
+ <VcHint class="vc-input__error">
110
+ {{ errorMessage }}
111
+ </VcHint>
112
+ </slot>
113
+ <slot v-if="fieldDescription" name="error">
114
+ <VcHint class="vc-input__desc">
115
+ {{ fieldDescription }}
116
+ </VcHint>
117
+ </slot>
118
+ </div>
119
+ </template>
120
+
121
+ <script lang="ts" setup>
122
+ import {
123
+ computed,
124
+ getCurrentInstance,
125
+ nextTick,
126
+ onMounted,
127
+ ref,
128
+ unref,
129
+ watch,
130
+ } from "vue";
131
+ import { VcIcon, VcLabel } from "@/ui/components";
132
+ import { createPopper, Instance } from "@popperjs/core";
133
+ import {
134
+ useCurrencyInput,
135
+ UseCurrencyInput,
136
+ parse,
137
+ CurrencyDisplay,
138
+ } from "vue-currency-input";
139
+ import { clickOutside as vClickOutside } from "@/core/directives";
140
+
141
+ export type ValueType = string | number | Date | null;
142
+
143
+ export interface Props {
144
+ placeholder?: string;
145
+ modelValue?: ValueType;
146
+ clearable?: boolean;
147
+ isRequired?: boolean;
148
+ disabled?: boolean;
149
+ type?: string;
150
+ label?: string;
151
+ tooltip?: string;
152
+ name?: string;
153
+ currency?: boolean;
154
+ options?: Record<string, string>[];
155
+ optionsTitle?: string;
156
+ optionsValue?: string;
157
+ keyProperty?: string;
158
+ displayProperty?: string;
159
+ fieldDescription?: string;
160
+ maxchars?: string;
161
+ max?: string | number;
162
+ errorMessage?: string;
163
+ }
164
+
165
+ export interface Emits {
166
+ (event: "update:modelValue", value: ValueType): void;
167
+ (event: "update:optionsValue", value: string): void;
168
+ (event: 'blur'): void;
169
+ }
170
+
171
+ const props = withDefaults(defineProps<Props>(), {
172
+ placeholder: "",
173
+ modelValue: null,
174
+ clearable: false,
175
+ isRequired: false,
176
+ disabled: false,
177
+ type: "text",
178
+ name: "Field",
179
+ currency: false,
180
+ options: () => [],
181
+ optionsTitle: "Select",
182
+ optionsValue: "",
183
+ keyProperty: "id",
184
+ displayProperty: "title",
185
+ fieldDescription: "",
186
+ maxchars: "1024",
187
+ });
188
+
189
+ const emit = defineEmits<Emits>();
190
+
191
+ let currencyConverter: UseCurrencyInput | undefined = undefined;
192
+ const internalType = ref(unref(props.type));
193
+ const toggleDropRef = ref();
194
+ const dropRef = ref();
195
+ const dropActive = ref(false);
196
+ const instance = getCurrentInstance();
197
+ const popper = ref<Instance>();
198
+ const search = ref("");
199
+ const searchInput = ref();
200
+ const calcValue = computed(() => {
201
+ if (props.currency) {
202
+ return currencyConverter?.formattedValue.value;
203
+ } else {
204
+ return props.modelValue;
205
+ }
206
+ });
207
+
208
+ // Init currency composable if input type === currency (created hook)
209
+ if (props.currency) {
210
+ currencyConverter = useCurrencyInput({
211
+ currency: props.optionsValue || "USD",
212
+ autoSign: false,
213
+ currencyDisplay: CurrencyDisplay.hidden,
214
+ });
215
+ }
216
+
217
+ onMounted(() => {
218
+ if (!props.modelValue && props.currency) {
219
+ currencyConverter && currencyConverter.setValue(null);
220
+ }
221
+ });
222
+
223
+ // Change currency settings
224
+ watch(
225
+ () => props.optionsValue,
226
+ (newVal) => {
227
+ currencyConverter &&
228
+ currencyConverter.setOptions({
229
+ currency: newVal,
230
+ autoSign: false,
231
+ currencyDisplay: CurrencyDisplay.hidden,
232
+ });
233
+ }
234
+ );
235
+
236
+ const searchFilter = computed(() => {
237
+ return props.options.filter((opt) =>
238
+ opt[props.displayProperty]
239
+ .toLowerCase()
240
+ .includes(search.value.toLowerCase())
241
+ );
242
+ });
243
+
244
+ const inputRef = currencyConverter && currencyConverter.inputRef;
245
+
246
+ watch(
247
+ () => props.modelValue,
248
+ (value) => {
249
+ let initialValue = unref(value);
250
+ if (
251
+ initialValue &&
252
+ initialValue.toString().length >= parseInt(props.maxchars)
253
+ ) {
254
+ initialValue.toString().slice(0, parseInt(props.maxchars));
255
+
256
+ return;
257
+ }
258
+
259
+ emit("update:modelValue", initialValue);
260
+ }
261
+ );
262
+
263
+ function showDrop() {
264
+ if (!dropActive.value && !props.disabled) {
265
+ dropActive.value = true;
266
+ nextTick(() => {
267
+ searchInput.value.focus();
268
+ popper.value = createPopper(toggleDropRef.value, dropRef.value, {
269
+ placement: "bottom-end",
270
+ modifiers: [
271
+ {
272
+ name: "offset",
273
+ options: {
274
+ offset: [13, 15],
275
+ },
276
+ },
277
+ ],
278
+ });
279
+ });
280
+ } else {
281
+ closeDrop();
282
+ }
283
+ }
284
+
285
+ function closeDrop() {
286
+ dropActive.value = false;
287
+ search.value = "";
288
+ popper.value?.destroy();
289
+ }
290
+
291
+ function onItemSelect(item: { [x: string]: string }) {
292
+ emit("update:optionsValue", item[props.keyProperty]);
293
+ closeDrop();
294
+ }
295
+
296
+ // Handle input event to properly validate value and emit changes
297
+ function onInput(e: Event) {
298
+ const newValue = (e.target as HTMLInputElement).value;
299
+ if (newValue) {
300
+ if (props.currency) {
301
+ const parsed = parse(newValue, { currency: props.optionsValue });
302
+ emit("update:modelValue", parsed);
303
+ } else {
304
+ emit("update:modelValue", newValue);
305
+ }
306
+ } else {
307
+ emit("update:modelValue", null);
308
+ }
309
+ }
310
+
311
+ // Handle input event to propertly reset value and emit changes
312
+ function onReset() {
313
+ if (props.currency) {
314
+ currencyConverter && currencyConverter.setValue(null);
315
+ }
316
+ emit("update:modelValue", null);
317
+ }
318
+ </script>
319
+
320
+ <style lang="scss">
321
+ :root {
322
+ --input-height: 38px;
323
+ --input-border-radius: 3px;
324
+ --input-border-color: #d3dbe9;
325
+ --input-border-color-error: #f14e4e;
326
+ --input-background-color: #ffffff;
327
+ --input-placeholder-color: #a5a5a5;
328
+ --input-clear-color: #43b0e6;
329
+ --input-clear-color-hover: #319ed4;
330
+ }
331
+
332
+ .vc-input {
333
+ @apply tw-overflow-hidden;
334
+
335
+ &_date,
336
+ &_datetime-local {
337
+ @apply tw-max-w-[220px];
338
+
339
+ .vc-app_mobile & {
340
+ @apply tw-max-w-full;
341
+ }
342
+ }
343
+
344
+ &__field-wrapper {
345
+ @apply tw-border tw-border-solid tw-border-[color:var(--input-border-color)]
346
+ tw-rounded-[var(--input-border-radius)]
347
+ tw-bg-[color:var(--input-background-color)] tw-flex tw-items-stretch;
348
+ }
349
+
350
+ &_error &__field-wrapper {
351
+ @apply tw-border tw-border-solid tw-border-[color:var(--input-border-color-error)];
352
+ }
353
+
354
+ &__error {
355
+ @apply tw-mt-1 [--hint-color:var(--input-border-color-error)];
356
+ }
357
+
358
+ &__desc {
359
+ @apply tw-text-[color:var(--multivalue-placeholder-color)] tw-mt-1 tw-break-words tw-p-0;
360
+ }
361
+
362
+ &__field {
363
+ @apply tw-border-none tw-outline-none tw-h-[var(--input-height)] tw-min-w-0 tw-box-border tw-grow tw-pl-3;
364
+
365
+ &::-webkit-input-placeholder {
366
+ @apply tw-text-[color:var(--input-placeholder-color)];
367
+ }
368
+
369
+ &::-moz-placeholder {
370
+ @apply tw-text-[color:var(--input-placeholder-color)];
371
+ }
372
+
373
+ &::-ms-placeholder {
374
+ @apply tw-text-[color:var(--input-placeholder-color)];
375
+ }
376
+
377
+ &::placeholder {
378
+ @apply tw-text-[color:var(--input-placeholder-color)];
379
+ }
380
+
381
+ &::-ms-reveal,
382
+ &::-ms-clear {
383
+ @apply tw-hidden;
384
+ }
385
+ }
386
+
387
+ &__clear {
388
+ @apply tw-cursor-pointer tw-text-[color:var(--input-clear-color)] hover:tw-text-[color:var(--input-clear-color-hover)] tw-px-3 tw-flex tw-items-center;
389
+ }
390
+
391
+ &__showhide {
392
+ @apply tw-cursor-pointer tw-text-[color:var(--input-placeholder-color)] hover:tw-text-[color:var(--input-clear-color-hover)] tw-px-3 tw-flex tw-items-center;
393
+ }
394
+
395
+ &__dropdown-wrap {
396
+ @apply tw-relative tw-px-3 tw-flex tw-items-center;
397
+ }
398
+
399
+ &__dropdown-btn {
400
+ @apply tw-text-[color:var(--input-clear-color)] tw-not-italic tw-font-medium tw-text-[13px] tw-leading-[20px] tw-cursor-pointer;
401
+
402
+ &_disabled {
403
+ @apply tw-text-[#7e8e9d] tw-cursor-default;
404
+ }
405
+ }
406
+
407
+ &__dropdown {
408
+ @apply tw-absolute tw-bg-white tw-shadow-[1px_1px_11px_rgba(141,152,163,0.6)] tw-rounded-[3px] tw-p-[11px] tw-w-[120px] tw-box-border;
409
+ }
410
+
411
+ &__dropdown-title {
412
+ @apply tw-not-italic tw-font-normal tw-text-base tw-leading-[20px] tw-text-[#333333] tw-p-0 tw-mb-[7px];
413
+ }
414
+
415
+ &__dropdown-search {
416
+ @apply tw-bg-white tw-border tw-border-solid tw-border-[#eaecf2] tw-box-border tw-rounded-[4px] tw-h-8 tw-w-full tw-outline-none tw-px-2;
417
+ }
418
+
419
+ &__dropdown-list {
420
+ @apply tw-list-none tw-p-0 tw-mt-2;
421
+ }
422
+
423
+ &__dropdown-selector {
424
+ @apply tw-border-none tw-bg-transparent tw-p-[9px] tw-text-left tw-w-full tw-cursor-pointer tw-text-lg tw-rounded-[3px] hover:tw-bg-[#eff7fc];
425
+
426
+ &-active {
427
+ @apply tw-bg-[#dfeef9];
428
+ }
429
+ }
430
+
431
+ &_disabled &__field-wrapper,
432
+ &_disabled &__field {
433
+ @apply tw-bg-[#fafafa] tw-text-[#424242];
434
+ }
435
+ }
436
+ </style>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <div
3
+ class="vc-editor"
4
+ :class="[
5
+ {
6
+ 'vc-editor_error': errorMessage,
7
+ 'vc-editor_disabled': disabled
8
+ },
9
+ ]"
10
+ >
11
+ <!-- Editor label -->
12
+ <VcLabel v-if="label" class="tw-mb-2" :required="required">
13
+ <span>{{ label }}</span>
14
+ <template v-if="tooltip" v-slot:tooltip>{{ tooltip }}</template>
15
+ </VcLabel>
16
+
17
+ <!-- Editor field -->
18
+ <v-ace-editor
19
+ class="tw-border tw-border-solid tw-border-[color:var(--editor-border-color)] tw-rounded-[var(--editor-border-radius)]"
20
+ v-model:value="content"
21
+ lang="html"
22
+ theme="chrome"
23
+ style="height: 200px"
24
+ @input="onInput"
25
+ />
26
+ <slot v-if="errorMessage" name="error">
27
+ <VcHint class="vc-editor__error">
28
+ {{ errorMessage }}
29
+ </VcHint>
30
+ </slot>
31
+ </div>
32
+ </template>
33
+
34
+ <script lang="ts" setup>
35
+ import { VAceEditor } from "vue3-ace-editor";
36
+ import "ace-builds/src-noconflict/mode-html";
37
+ import "ace-builds/src-noconflict/theme-chrome";
38
+ import {ref, unref, watch} from "vue";
39
+
40
+ const props = defineProps({
41
+ placeholder: {
42
+ type: String,
43
+ default: "",
44
+ },
45
+
46
+ modelValue: {
47
+ type: [String, Number, Date],
48
+ default: null,
49
+ },
50
+
51
+ required: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+
56
+ disabled: {
57
+ type: Boolean,
58
+ default: false,
59
+ },
60
+
61
+ label: {
62
+ type: String,
63
+ default: undefined,
64
+ },
65
+
66
+ tooltip: {
67
+ type: String,
68
+ default: undefined,
69
+ },
70
+
71
+ name: {
72
+ type: String,
73
+ default: "Field",
74
+ },
75
+
76
+ errorMessage: {
77
+ type: String,
78
+ default: undefined
79
+ }
80
+ });
81
+
82
+ const emit = defineEmits(["update:modelValue"]);
83
+ const content = ref();
84
+ let initialValue = unref(props.modelValue);
85
+
86
+ watch(
87
+ () => props.modelValue,
88
+ (value) => {
89
+ let init = unref(value);
90
+ emit("update:modelValue", init);
91
+ }
92
+ );
93
+
94
+ // Handle input event to propertly validate value and emit changes
95
+ function onInput() {
96
+ emit("update:modelValue", content.value);
97
+ }
98
+ </script>
99
+
100
+ <style lang="scss">
101
+ :root {
102
+ --editor-border-radius: 3px;
103
+ --editor-border-color: #d3dbe9;
104
+ --editor-border-color-error: #f14e4e;
105
+ --editor-placeholder-color: #a5a5a5;
106
+ }
107
+
108
+ .vc-editor {
109
+ &__error {
110
+ @apply tw-text-[color:var(--editor-border-color-error)] tw-mt-1;
111
+ }
112
+
113
+ &_error .ace_editor {
114
+ @apply tw-border tw-border-solid tw-border-[color:var(--editor-border-color-error)];
115
+ }
116
+ }
117
+ </style>
@@ -0,0 +1,134 @@
1
+ <template>
2
+ <div class= "tw-flex tw-flex-col tw-flex-1">
3
+ <div
4
+ class="vc-file-upload tw-relative tw-h-[155px] tw-box-border tw-border tw-border-dashed tw-border-[#c8dbea] tw-rounded-[6px] tw-p-2 tw-p-4 tw-flex tw-flex-col tw-items-center tw-justify-center"
5
+ :class="`vc-file-upload_${variant}`"
6
+ @drop.stop.prevent="onDrop"
7
+ @drag.stop.prevent
8
+ @dragstart.stop.prevent
9
+ @dragend.stop.prevent
10
+ @dragover.stop.prevent
11
+ @dragenter.stop.prevent
12
+ @dragleave.stop.prevent
13
+ >
14
+ <VcLoading :active="loading"></VcLoading>
15
+ <VcIcon
16
+ class="tw-text-[#c8dbea]"
17
+ icon="fas fa-cloud-upload-alt"
18
+ size="xxl"
19
+ ></VcIcon>
20
+
21
+ <div class="tw-text-[#9db0be] tw-text-center tw-text-lg tw-leading-lg tw-mt-4">
22
+ <span>Drag and drop file here or</span>&nbsp;
23
+ <br />
24
+ <VcLink @click="toggleUploader">browse your files</VcLink>
25
+ </div>
26
+
27
+ <input
28
+ ref="uploader"
29
+ type="file"
30
+ hidden
31
+ @change="upload"
32
+ :accept="accept"
33
+ :multiple="multiple"
34
+ :name="name"
35
+ />
36
+ </div>
37
+ <slot v-if="errorMessage" name="error">
38
+ <VcHint class="vc-input__error">
39
+ {{ errorMessage }}
40
+ </VcHint>
41
+ </slot>
42
+ </div>
43
+ </template>
44
+
45
+ <script lang="ts" setup>
46
+ import { getCurrentInstance, ref, unref } from "vue";
47
+ import { useField } from "vee-validate";
48
+
49
+ const props = defineProps({
50
+ variant: {
51
+ type: String,
52
+ enum: ["gallery", "file-upload"],
53
+ default: "gallery",
54
+ },
55
+
56
+ loading: {
57
+ type: Boolean,
58
+ default: false,
59
+ },
60
+
61
+ accept: {
62
+ type: String,
63
+ default: ".jpg, .png, .jpeg",
64
+ },
65
+
66
+ multiple: {
67
+ type: Boolean,
68
+ default: false,
69
+ },
70
+
71
+ rules: {
72
+ type: [String, Object],
73
+ },
74
+
75
+ name: {
76
+ type: String,
77
+ default: "Gallery",
78
+ },
79
+ });
80
+
81
+ const emit = defineEmits(["upload"]);
82
+ const instance = getCurrentInstance();
83
+ // Prepare validation rules using required and rules props combination
84
+ let internalRules = unref(props.rules) || "";
85
+
86
+ // Prepare field-level validation
87
+ const { errorMessage, handleChange, errors, validate } = useField(
88
+ `${props.name === "Gallery" ? instance?.uid : props.name}`,
89
+ internalRules
90
+ );
91
+
92
+ const uploader = ref();
93
+
94
+ const upload = async (event: InputEvent) => {
95
+ await handleChange(event.target);
96
+
97
+ const isValid = await validate();
98
+
99
+ if (isValid.valid) {
100
+ const target = event.target as HTMLInputElement;
101
+ const fileList = target.files;
102
+
103
+ if (fileList && fileList.length) {
104
+ emit("upload", fileList);
105
+ }
106
+ }
107
+ };
108
+
109
+ function toggleUploader() {
110
+ uploader.value.value = "";
111
+ uploader.value.click();
112
+ }
113
+
114
+ function onDrop(event: DragEvent) {
115
+ const fileList = event.dataTransfer?.files;
116
+
117
+ if (fileList && fileList.length) {
118
+ emit("upload", fileList);
119
+ }
120
+ }
121
+ </script>
122
+
123
+ <style lang="scss">
124
+ .vc-file-upload {
125
+ // variants
126
+ &_gallery {
127
+ @apply tw-w-[155px] tw-h-[155px];
128
+ }
129
+
130
+ &_file-upload {
131
+ @apply tw-w-full tw-bg-[#f2f8fd];
132
+ }
133
+ }
134
+ </style>
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Form component.
3
+ * @author Iurii A Taranov <me@flanker72.ru>
4
+ */
5
+ import { Story } from "@storybook/vue3";
6
+ import VcForm from "./vc-form.vue";
7
+
8
+ export default {
9
+ title: "molecules/vc-form",
10
+ component: VcForm,
11
+ };
12
+
13
+ const Template: Story = (args) => ({
14
+ components: { VcForm },
15
+ setup() {
16
+ return { args };
17
+ },
18
+ template: '<vc-form v-bind="args"></vc-form>',
19
+ });
20
+
21
+ export const Form = Template.bind({});
22
+ Form.storyName = "vc-form";
23
+ Form.args = {};
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <form>
3
+ <slot>vc-form</slot>
4
+ </form>
5
+ </template>