@meeovi/layer-shared 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 (327) hide show
  1. package/README.md +0 -0
  2. package/app/components/Gallery/Gallery.vue +187 -0
  3. package/app/components/Gallery/__tests__/Gallery.spec.ts +14 -0
  4. package/app/components/Heading/Heading.vue +14 -0
  5. package/app/components/Heading/__tests__/Heading.spec.ts +14 -0
  6. package/app/components/Heading/types.ts +5 -0
  7. package/app/components/media/audioGallery.vue +70 -0
  8. package/app/components/media/dragDropUpload.vue +67 -0
  9. package/app/components/media/fullscreenMediaModal.vue +66 -0
  10. package/app/components/media/imageCard.vue +65 -0
  11. package/app/components/media/imageGallery.vue +40 -0
  12. package/app/components/media/mediaCard.vue +89 -0
  13. package/app/components/media/mediaCarousel.vue +65 -0
  14. package/app/components/media/mediaFolderSidebar.vue +72 -0
  15. package/app/components/media/mediaPlayer.vue +40 -0
  16. package/app/components/media/mediaSearchBar.vue +16 -0
  17. package/app/components/media/videoGallery.vue +77 -0
  18. package/app/components/ui/AccordionItem/AccordionItem.vue +24 -0
  19. package/app/components/ui/AccordionItem/__tests__/AccordionItem.spec.ts +14 -0
  20. package/app/components/ui/AccordionItem/types.ts +5 -0
  21. package/app/components/ui/Alert/Alert.vue +34 -0
  22. package/app/components/ui/Alert/types.ts +5 -0
  23. package/app/components/ui/Breadcrumbs/Breadcrumbs.vue +76 -0
  24. package/app/components/ui/Breadcrumbs/__tests__/Breadcrumbs.spec.ts +14 -0
  25. package/app/components/ui/Breadcrumbs/types.ts +8 -0
  26. package/app/components/ui/CartProductCard/CartProductCard.vue +66 -0
  27. package/app/components/ui/CartProductCard/types.ts +18 -0
  28. package/app/components/ui/CategoryCard/CategoryCard.vue +41 -0
  29. package/app/components/ui/CategoryCard/types.ts +9 -0
  30. package/app/components/ui/Display/Display.vue +55 -0
  31. package/app/components/ui/Display/types.ts +12 -0
  32. package/app/components/ui/Divider/Divider.vue +3 -0
  33. package/app/components/ui/Divider/__tests__/Divider.spec.tsx +10 -0
  34. package/app/components/ui/Footer.vue +92 -0
  35. package/app/components/ui/Form/FormHelperText.vue +5 -0
  36. package/app/components/ui/Form/FormLabel.vue +5 -0
  37. package/app/components/ui/Form/FormPasswordInput.vue +15 -0
  38. package/app/components/ui/Form/__tests__/FormHelperText.spec.ts +10 -0
  39. package/app/components/ui/Form/__tests__/FormLabel.spec.ts +10 -0
  40. package/app/components/ui/Hero/Hero.vue +44 -0
  41. package/app/components/ui/Hero/types.ts +10 -0
  42. package/app/components/ui/Modal/Modal.vue +19 -0
  43. package/app/components/ui/Modal/types.ts +8 -0
  44. package/app/components/ui/Motionable.vue +45 -0
  45. package/app/components/ui/NavbarBottom.vue +63 -0
  46. package/app/components/ui/NavbarTop.vue +25 -0
  47. package/app/components/ui/Overlay/Overlay.vue +14 -0
  48. package/app/components/ui/Overlay/__tests__/Overlay.spec.ts +14 -0
  49. package/app/components/ui/Overlay/types.ts +3 -0
  50. package/app/components/ui/PageBuilder.vue +39 -0
  51. package/app/components/ui/PageContainer.vue +5 -0
  52. package/app/components/ui/Pagination/Pagination.vue +151 -0
  53. package/app/components/ui/Pagination/__tests__/Pagination.spec.ts +17 -0
  54. package/app/components/ui/Pagination/types.ts +6 -0
  55. package/app/components/ui/ProductCard/ProductCard.vue +55 -0
  56. package/app/components/ui/ProductCard/__tests__/ProductCard.spec.ts +16 -0
  57. package/app/components/ui/ProductCard/types.ts +12 -0
  58. package/app/components/ui/ProductCardHorizontal/ProductCardHorizontal.vue +34 -0
  59. package/app/components/ui/ProductCardHorizontal/__tests__/ProductCardHorizontal.spec.ts +36 -0
  60. package/app/components/ui/ProductCardHorizontal/types.ts +8 -0
  61. package/app/components/ui/PurchaseCard/PurchaseCard.vue +109 -0
  62. package/app/components/ui/PurchaseCard/__tests__/PurchaseCard.spec.ts +15 -0
  63. package/app/components/ui/PurchaseCard/types.ts +5 -0
  64. package/app/components/ui/QuantitySelector/QuantitySelector.vue +69 -0
  65. package/app/components/ui/QuantitySelector/__tests__/QuantitySelector.spec.ts +15 -0
  66. package/app/components/ui/QuantitySelector/types.ts +5 -0
  67. package/app/components/ui/RadialProgress.vue +44 -0
  68. package/app/components/ui/Review/Review.vue +57 -0
  69. package/app/components/ui/Review/__tests__/Review.spec.ts +15 -0
  70. package/app/components/ui/Review/types.ts +5 -0
  71. package/app/components/ui/ScrollTop.vue +82 -0
  72. package/app/components/ui/Search.vue +54 -0
  73. package/app/components/ui/Tag/Tag.vue +38 -0
  74. package/app/components/ui/Tag/__tests__/Tag.spec.ts +10 -0
  75. package/app/components/ui/Tag/types.ts +16 -0
  76. package/app/components/ui/VsfLogo.vue +7 -0
  77. package/app/components/ui/forms/BooleanInput.vue +34 -0
  78. package/app/components/ui/forms/DateTime.vue +44 -0
  79. package/app/components/ui/forms/DirectusFormElement.vue +60 -0
  80. package/app/components/ui/forms/DynamicTableElement.vue +57 -0
  81. package/app/components/ui/forms/FileInput.vue +85 -0
  82. package/app/components/ui/forms/FormField.vue +34 -0
  83. package/app/components/ui/forms/RelationSelect.vue +63 -0
  84. package/app/components/ui/forms/RepeaterInput.vue +121 -0
  85. package/app/components/ui/forms/SelectInput.vue +65 -0
  86. package/app/components/ui/forms/TextArea.vue +59 -0
  87. package/app/components/ui/forms/TextInput.vue +42 -0
  88. package/app/components/ui/forms/TiptapEditor.vue +136 -0
  89. package/app/components/ui/forms/[collection].vue +22 -0
  90. package/app/components/ui/studio/builder.vue +57 -0
  91. package/app/components/ui/studio/document.vue +69 -0
  92. package/app/components/ui/studio/email.vue +57 -0
  93. package/app/composables/globals/uploadFiles.js +41 -0
  94. package/app/composables/globals/useAdminTable.ts +12 -0
  95. package/app/composables/globals/useCustomFetch.ts +13 -0
  96. package/app/composables/globals/useDirectusField.ts +144 -0
  97. package/app/composables/globals/useDirectusForm.ts +70 -0
  98. package/app/composables/globals/useDirectusSchema.js +9 -0
  99. package/app/composables/globals/useFileManager.ts +76 -0
  100. package/app/composables/globals/useLivePreview.ts +17 -0
  101. package/app/composables/globals/useLoading.ts +23 -0
  102. package/app/composables/globals/useNavigation.js +19 -0
  103. package/app/composables/globals/useNotifications.ts +153 -0
  104. package/app/composables/globals/usePages.js +36 -0
  105. package/app/composables/globals/useRichText.ts +33 -0
  106. package/app/composables/globals/useServerRootMixin.ts +19 -0
  107. package/app/composables/globals/useVisualEditing.ts +38 -0
  108. package/app/composables/media/useFile.ts +6 -0
  109. package/app/composables/media/useMediaCenter.ts +353 -0
  110. package/app/composables/media/useVideojs.ts +45 -0
  111. package/app/composables/registry.ts +13 -0
  112. package/app/composables/types.ts +12 -0
  113. package/app/composables/useContent.ts +13 -0
  114. package/app/composables/useDirectusRequest.ts +32 -0
  115. package/app/stores/index.ts +0 -0
  116. package/app/types/api/global-search.ts +8 -0
  117. package/app/types/blocks/block-button-group.ts +7 -0
  118. package/app/types/blocks/block-button.ts +14 -0
  119. package/app/types/blocks/block-column.ts +20 -0
  120. package/app/types/blocks/block-cta.ts +10 -0
  121. package/app/types/blocks/block-divider.ts +4 -0
  122. package/app/types/blocks/block-faq.ts +12 -0
  123. package/app/types/blocks/block-form.ts +8 -0
  124. package/app/types/blocks/block-gallery.ts +14 -0
  125. package/app/types/blocks/block-hero.ts +12 -0
  126. package/app/types/blocks/block-html.ts +4 -0
  127. package/app/types/blocks/block-logocloud.ts +14 -0
  128. package/app/types/blocks/block-quote.ts +11 -0
  129. package/app/types/blocks/block-richtext.ts +7 -0
  130. package/app/types/blocks/block-steps.ts +22 -0
  131. package/app/types/blocks/block-team.ts +6 -0
  132. package/app/types/blocks/block-testimonial.ts +14 -0
  133. package/app/types/blocks/block-video.ts +10 -0
  134. package/app/types/blocks/block.ts +49 -0
  135. package/app/types/blocks/index.ts +18 -0
  136. package/app/types/componentMap.ts +15 -0
  137. package/app/types/content/category.ts +11 -0
  138. package/app/types/content/form.ts +20 -0
  139. package/app/types/content/index.ts +6 -0
  140. package/app/types/content/page.ts +76 -0
  141. package/app/types/content/post.ts +39 -0
  142. package/app/types/content/team.ts +16 -0
  143. package/app/types/content/testimonial.ts +19 -0
  144. package/app/types/directus.d.ts +47 -0
  145. package/app/types/env.d.ts +8 -0
  146. package/app/types/help/index.ts +53 -0
  147. package/app/types/index.d.ts +9 -0
  148. package/app/types/index.ts +7 -0
  149. package/app/types/meta/analytics.ts +18 -0
  150. package/app/types/meta/config.ts +21 -0
  151. package/app/types/meta/globals.ts +30 -0
  152. package/app/types/meta/index.ts +6 -0
  153. package/app/types/meta/navigation.ts +32 -0
  154. package/app/types/meta/redirect.ts +13 -0
  155. package/app/types/meta/seo.ts +19 -0
  156. package/app/types/os/contact.ts +23 -0
  157. package/app/types/os/conversation.ts +25 -0
  158. package/app/types/os/index.ts +16 -0
  159. package/app/types/os/organization.ts +54 -0
  160. package/app/types/os/os-activity.ts +28 -0
  161. package/app/types/os/os-deal.ts +45 -0
  162. package/app/types/os/os-expense.ts +22 -0
  163. package/app/types/os/os-invoice.ts +48 -0
  164. package/app/types/os/os-item.ts +18 -0
  165. package/app/types/os/os-payment.ts +29 -0
  166. package/app/types/os/os-project.ts +47 -0
  167. package/app/types/os/os-proposal.ts +84 -0
  168. package/app/types/os/os-settings.ts +19 -0
  169. package/app/types/os/os-subscription.ts +12 -0
  170. package/app/types/os/os-task.ts +34 -0
  171. package/app/types/os/os-tax-rate.ts +13 -0
  172. package/app/types/pageComponentMap.ts +8 -0
  173. package/app/types/schema.d.ts +39 -0
  174. package/app/types/schema.ts +151 -0
  175. package/app/types/system/file.ts +46 -0
  176. package/app/types/system/folder.ts +8 -0
  177. package/app/types/system/index.ts +4 -0
  178. package/app/types/system/role.ts +21 -0
  179. package/app/types/system/user.ts +56 -0
  180. package/app/utils/Timer.js +44 -0
  181. package/app/utils/billing-address.ts +24 -0
  182. package/app/utils/color.ts +14 -0
  183. package/app/utils/currency.ts +29 -0
  184. package/app/utils/embed.ts +57 -0
  185. package/app/utils/errors.ts +9 -0
  186. package/app/utils/fieldRegistry.js +89 -0
  187. package/app/utils/fonts.ts +24 -0
  188. package/app/utils/formkit.ts +75 -0
  189. package/app/utils/icons.ts +62 -0
  190. package/app/utils/index.js +0 -0
  191. package/app/utils/links.ts +28 -0
  192. package/app/utils/lodash.ts +33 -0
  193. package/app/utils/markdown.ts +9 -0
  194. package/app/utils/math.ts +25 -0
  195. package/app/utils/navigation.ts +11 -0
  196. package/app/utils/objects.ts +11 -0
  197. package/app/utils/paths.ts +21 -0
  198. package/app/utils/relations.ts +33 -0
  199. package/app/utils/strings.ts +113 -0
  200. package/app/utils/time.ts +148 -0
  201. package/app/utils/url.ts +22 -0
  202. package/app/utils/user-name.ts +21 -0
  203. package/app/utils/video/README.md +51 -0
  204. package/app/utils/video/playlist.js +0 -0
  205. package/dist/api/global-search.d.ts +8 -0
  206. package/dist/api/global-search.js +1 -0
  207. package/dist/blocks/block-button-group.d.ts +6 -0
  208. package/dist/blocks/block-button-group.js +1 -0
  209. package/dist/blocks/block-button.d.ts +13 -0
  210. package/dist/blocks/block-button.js +1 -0
  211. package/dist/blocks/block-column.d.ts +18 -0
  212. package/dist/blocks/block-column.js +1 -0
  213. package/dist/blocks/block-cta.d.ts +11 -0
  214. package/dist/blocks/block-cta.js +1 -0
  215. package/dist/blocks/block-divider.d.ts +4 -0
  216. package/dist/blocks/block-divider.js +1 -0
  217. package/dist/blocks/block-faq.d.ts +11 -0
  218. package/dist/blocks/block-faq.js +1 -0
  219. package/dist/blocks/block-form.d.ts +7 -0
  220. package/dist/blocks/block-form.js +1 -0
  221. package/dist/blocks/block-gallery.d.ts +13 -0
  222. package/dist/blocks/block-gallery.js +1 -0
  223. package/dist/blocks/block-hero.d.ts +11 -0
  224. package/dist/blocks/block-hero.js +1 -0
  225. package/dist/blocks/block-html.d.ts +4 -0
  226. package/dist/blocks/block-html.js +1 -0
  227. package/dist/blocks/block-logocloud.d.ts +13 -0
  228. package/dist/blocks/block-logocloud.js +1 -0
  229. package/dist/blocks/block-quote.d.ts +10 -0
  230. package/dist/blocks/block-quote.js +1 -0
  231. package/dist/blocks/block-richtext.d.ts +7 -0
  232. package/dist/blocks/block-richtext.js +1 -0
  233. package/dist/blocks/block-steps.d.ts +21 -0
  234. package/dist/blocks/block-steps.js +1 -0
  235. package/dist/blocks/block-team.d.ts +6 -0
  236. package/dist/blocks/block-team.js +1 -0
  237. package/dist/blocks/block-testimonial.d.ts +13 -0
  238. package/dist/blocks/block-testimonial.js +1 -0
  239. package/dist/blocks/block-video.d.ts +9 -0
  240. package/dist/blocks/block-video.js +1 -0
  241. package/dist/blocks/block.d.ts +17 -0
  242. package/dist/blocks/block.js +1 -0
  243. package/dist/blocks/index.d.ts +18 -0
  244. package/dist/blocks/index.js +1 -0
  245. package/dist/componentMap.d.ts +6 -0
  246. package/dist/componentMap.js +8 -0
  247. package/dist/content/category.d.ts +10 -0
  248. package/dist/content/category.js +1 -0
  249. package/dist/content/form.d.ts +21 -0
  250. package/dist/content/form.js +1 -0
  251. package/dist/content/index.d.ts +6 -0
  252. package/dist/content/index.js +1 -0
  253. package/dist/content/page.d.ts +38 -0
  254. package/dist/content/page.js +1 -0
  255. package/dist/content/post.d.ts +38 -0
  256. package/dist/content/post.js +1 -0
  257. package/dist/content/team.d.ts +17 -0
  258. package/dist/content/team.js +1 -0
  259. package/dist/content/testimonial.d.ts +18 -0
  260. package/dist/content/testimonial.js +1 -0
  261. package/dist/help/index.d.ts +51 -0
  262. package/dist/help/index.js +1 -0
  263. package/dist/index.d.ts +7 -0
  264. package/dist/index.js +1 -0
  265. package/dist/meta/analytics.d.ts +21 -0
  266. package/dist/meta/analytics.js +1 -0
  267. package/dist/meta/config.d.ts +22 -0
  268. package/dist/meta/config.js +1 -0
  269. package/dist/meta/globals.d.ts +33 -0
  270. package/dist/meta/globals.js +1 -0
  271. package/dist/meta/index.d.ts +6 -0
  272. package/dist/meta/index.js +1 -0
  273. package/dist/meta/navigation.d.ts +31 -0
  274. package/dist/meta/navigation.js +1 -0
  275. package/dist/meta/redirect.d.ts +12 -0
  276. package/dist/meta/redirect.js +1 -0
  277. package/dist/meta/seo.d.ts +19 -0
  278. package/dist/meta/seo.js +1 -0
  279. package/dist/os/contact.d.ts +22 -0
  280. package/dist/os/contact.js +1 -0
  281. package/dist/os/conversation.d.ts +23 -0
  282. package/dist/os/conversation.js +1 -0
  283. package/dist/os/index.d.ts +16 -0
  284. package/dist/os/index.js +1 -0
  285. package/dist/os/organization.d.ts +51 -0
  286. package/dist/os/organization.js +1 -0
  287. package/dist/os/os-activity.d.ts +26 -0
  288. package/dist/os/os-activity.js +1 -0
  289. package/dist/os/os-deal.d.ts +42 -0
  290. package/dist/os/os-deal.js +1 -0
  291. package/dist/os/os-expense.d.ts +21 -0
  292. package/dist/os/os-expense.js +1 -0
  293. package/dist/os/os-invoice.d.ts +46 -0
  294. package/dist/os/os-invoice.js +1 -0
  295. package/dist/os/os-item.d.ts +17 -0
  296. package/dist/os/os-item.js +1 -0
  297. package/dist/os/os-payment.d.ts +27 -0
  298. package/dist/os/os-payment.js +1 -0
  299. package/dist/os/os-project.d.ts +45 -0
  300. package/dist/os/os-project.js +1 -0
  301. package/dist/os/os-proposal.d.ts +61 -0
  302. package/dist/os/os-proposal.js +1 -0
  303. package/dist/os/os-settings.d.ts +17 -0
  304. package/dist/os/os-settings.js +1 -0
  305. package/dist/os/os-subscription.d.ts +12 -0
  306. package/dist/os/os-subscription.js +1 -0
  307. package/dist/os/os-task.d.ts +32 -0
  308. package/dist/os/os-task.js +1 -0
  309. package/dist/os/os-tax-rate.d.ts +12 -0
  310. package/dist/os/os-tax-rate.js +1 -0
  311. package/dist/pageComponentMap.d.ts +2 -0
  312. package/dist/pageComponentMap.js +7 -0
  313. package/dist/schema.d.ts +78 -0
  314. package/dist/schema.js +1 -0
  315. package/dist/system/file.d.ts +47 -0
  316. package/dist/system/file.js +1 -0
  317. package/dist/system/folder.d.ts +8 -0
  318. package/dist/system/folder.js +1 -0
  319. package/dist/system/index.d.ts +4 -0
  320. package/dist/system/index.js +1 -0
  321. package/dist/system/role.d.ts +20 -0
  322. package/dist/system/role.js +1 -0
  323. package/dist/system/user.d.ts +57 -0
  324. package/dist/system/user.js +1 -0
  325. package/nuxt.config.ts +5 -0
  326. package/package.json +42 -0
  327. package/tsconfig.json +15 -0
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'inline-flex items-center justify-center',
5
+ strong ? 'font-medium rounded-none' : 'rounded-md font-normal',
6
+ getVariantClasses,
7
+ sizeClasses,
8
+ ]"
9
+ data-testid="tag"
10
+ >
11
+ <slot />
12
+ </div>
13
+ </template>
14
+ <script setup lang="ts">
15
+ import { type TagProps, TagSize } from '~/components/ui/Tag/types';
16
+
17
+ const props = withDefaults(defineProps<TagProps>(), {
18
+ variant: 'primary',
19
+ strong: false,
20
+ size: 'base',
21
+ });
22
+
23
+ const sizeClasses = computed(() => (props.size === TagSize.sm ? 'text-xs p-1 gap-1' : 'text-sm p-1.5 gap-1.5'));
24
+
25
+ const getVariantClasses = computed(() => {
26
+ switch (props.variant) {
27
+ case 'secondary': {
28
+ return ['text-white', props.strong ? 'bg-secondary-800' : 'bg-secondary-100'];
29
+ }
30
+ case 'negative': {
31
+ return ['text-negative-800', props.strong ? 'bg-negative-600' : 'bg-negative-100'];
32
+ }
33
+ default: {
34
+ return ['text-primary-800', props.strong ? 'bg-primary-600' : 'bg-primary-100'];
35
+ }
36
+ }
37
+ });
38
+ </script>
@@ -0,0 +1,10 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Tag from '~/components/ui/Tag/Tag.vue';
3
+
4
+ describe('<Tag />', () => {
5
+ it('should render component', () => {
6
+ const wrapper = mount(Tag);
7
+
8
+ expect(wrapper.getByTestId('tag'));
9
+ });
10
+ });
@@ -0,0 +1,16 @@
1
+ export enum TagVariant {
2
+ primary = 'primary',
3
+ secondary = 'secondary',
4
+ negative = 'negative',
5
+ }
6
+
7
+ export enum TagSize {
8
+ sm = 'sm',
9
+ base = 'base',
10
+ }
11
+
12
+ export interface TagProps {
13
+ size?: `${TagSize}`;
14
+ strong?: boolean;
15
+ variant?: `${TagVariant}`;
16
+ }
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" class="fill-current h-full w-auto" viewBox="0 0 205 28">
3
+ <path
4
+ d="M150.233 11.135h-2.65v3.12h2.65v9.104h3.701v-9.104h3.175v-3.12h-3.175v-.67c0-.462.144-.805.434-1.028s.716-.335 1.279-.335a6.7 6.7 0 0 1 .697.035 7.54 7.54 0 0 1 .765.127V6.167a7.8 7.8 0 0 0-.925-.162 8.49 8.49 0 0 0-1.04-.069c-1.583 0-2.798.385-3.643 1.155s-1.268 1.872-1.268 3.304v.739zM85.84 20.886l2.193-2.889a11.33 11.33 0 0 0 2.696 1.71 6.67 6.67 0 0 0 2.627.531c.898 0 1.603-.139 2.113-.416s.765-.662.765-1.155a1.23 1.23 0 0 0-.124-.587 1.19 1.19 0 0 0-.389-.453c-.343-.246-.91-.439-1.702-.578l-3.518-.624c-1.295-.231-2.292-.732-2.992-1.502s-1.051-1.748-1.051-2.935c0-1.556.582-2.784 1.747-3.686s2.768-1.352 4.808-1.352a11.02 11.02 0 0 1 3.598.624 10.03 10.03 0 0 1 3.118 1.687l-2.079 2.935a9.56 9.56 0 0 0-2.456-1.456 6.78 6.78 0 0 0-2.456-.462c-.792 0-1.42.127-1.885.381s-.697.597-.697 1.028a1.11 1.11 0 0 0 .106.525 1.09 1.09 0 0 0 .339.411c.297.224.781.389 1.451.497l3.312.555c1.538.247 2.707.763 3.506 1.548s1.199 1.802 1.199 3.05c0 1.633-.628 2.927-1.884 3.882s-2.965 1.433-5.128 1.433a10.81 10.81 0 0 1-3.826-.716 11.23 11.23 0 0 1-3.392-1.987zm-41.755 2.473L37.621 7.183h4.249l4.546 12.039 4.637-12.039h4.089l-6.556 16.175h-4.5zM58.59 11.135v7.025a2.12 2.12 0 0 0 .133.868c.104.276.265.528.472.738a2.08 2.08 0 0 0 .729.478c.273.106.566.151.858.134a2.95 2.95 0 0 0 1.256-.254 2.47 2.47 0 0 0 .914-.716v-8.273h3.7v12.224h-3.7v-.832a5.06 5.06 0 0 1-1.496.786 5.49 5.49 0 0 1-1.747.277c-1.416 0-2.574-.462-3.472-1.387s-1.348-2.103-1.348-3.535v-7.533h3.7zm19.085 12.016c.856-.327 1.649-.804 2.341-1.41l-2.444-2.195a3 3 0 0 1-1.051.682 3.71 3.71 0 0 1-1.37.243 3.1 3.1 0 0 1-1.827-.543 3.22 3.22 0 0 1-1.142-1.398h8.612v-.924a7.64 7.64 0 0 0-.457-2.669 6.3 6.3 0 0 0-1.268-2.114c-.559-.605-1.237-1.087-1.99-1.413a5.85 5.85 0 0 0-2.384-.482c-.85-.007-1.693.159-2.479.485a6.12 6.12 0 0 0-1.999 1.34 6.34 6.34 0 0 0-1.336 2.022 6.33 6.33 0 0 0-.491 2.484 6.08 6.08 0 0 0 .514 2.484 6.36 6.36 0 0 0 1.394 2.022 6.34 6.34 0 0 0 2.09 1.34 6.87 6.87 0 0 0 2.581.485 7.84 7.84 0 0 0 2.707-.439zm-4.626-8.665a2.51 2.51 0 0 1 1.576-.508c.566-.009 1.119.178 1.565.532a2.96 2.96 0 0 1 .971 1.41h-5.048c.151-.568.479-1.07.937-1.433zm29.9-.232v5.384c0 1.279.369 2.246 1.108 2.9s1.831.982 3.278.982c.436-.007.871-.041 1.302-.104a7.58 7.58 0 0 0 1.28-.266v-3.027a5.86 5.86 0 0 1-.846.196c-.272.04-.547.059-.822.058-.594 0-1.009-.112-1.245-.335s-.354-.604-.354-1.144v-4.645h3.381v-3.12h-3.381v-3.95l-3.701.809v3.143h-2.444v3.12h2.444zm7.972 3.004c-.004-.855.171-1.702.514-2.484.33-.759.804-1.446 1.394-2.022.602-.58 1.307-1.039 2.078-1.352a6.9 6.9 0 0 1 5.14 0 6.55 6.55 0 0 1 2.079 1.352 6.38 6.38 0 0 1 1.393 2.022c.339.783.514 1.629.514 2.484s-.175 1.701-.514 2.484a6.37 6.37 0 0 1-1.393 2.022 6.39 6.39 0 0 1-2.079 1.34 7.05 7.05 0 0 1-5.14 0 6.37 6.37 0 0 1-2.078-1.34 6.37 6.37 0 0 1-1.394-2.022c-.343-.782-.518-1.629-.514-2.484zm6.556 3.097a2.76 2.76 0 0 0 1.14-.226 2.8 2.8 0 0 0 .951-.676c.55-.6.856-1.388.856-2.207s-.306-1.607-.856-2.207a2.9 2.9 0 0 0-.955-.666c-.359-.155-.745-.235-1.136-.235a2.87 2.87 0 0 0-1.136.235 2.92 2.92 0 0 0-.954.666c-.55.6-.856 1.388-.856 2.207s.306 1.607.856 2.207a2.81 2.81 0 0 0 .95.676 2.77 2.77 0 0 0 1.14.226zm8.292-9.22v12.224h3.701v-7.764a3.33 3.33 0 0 1 1.199-1.132c.492-.277 1.047-.42 1.61-.416.315-.001.629.034.937.104a4.55 4.55 0 0 1 .822.266v-3.258a1.36 1.36 0 0 0-.548-.22c-.28-.045-.562-.072-.845-.081-.616-.009-1.226.126-1.782.393a4.21 4.21 0 0 0-1.393 1.109v-1.225h-3.701zm20.49 10.606a7.75 7.75 0 0 1-2.341 1.41c-.871.304-1.787.453-2.708.439a6.87 6.87 0 0 1-2.581-.485 6.35 6.35 0 0 1-2.09-1.34c-.59-.576-1.064-1.262-1.393-2.022a6.08 6.08 0 0 1-.514-2.484c-.005-.853.162-1.699.491-2.484a6.35 6.35 0 0 1 1.336-2.022 6.01 6.01 0 0 1 1.999-1.34 6.33 6.33 0 0 1 2.478-.485 5.85 5.85 0 0 1 2.385.482c.753.326 1.43.807 1.99 1.413.557.613.988 1.332 1.268 2.114a7.65 7.65 0 0 1 .456 2.669v.924h-8.611a3.22 3.22 0 0 0 1.142 1.398 3.11 3.11 0 0 0 1.827.543 3.71 3.71 0 0 0 1.371-.243c.393-.15.751-.383 1.051-.682l2.444 2.195zm-5.391-7.764a2.51 2.51 0 0 0-1.576.508c-.458.362-.786.865-.937 1.433h5.049c-.179-.557-.517-1.047-.971-1.41a2.46 2.46 0 0 0-1.565-.532zm17.794 9.382V11.135h3.701v1.225a4.21 4.21 0 0 1 1.393-1.109c.556-.267 1.166-.402 1.782-.393.283.009.565.036.845.081a1.36 1.36 0 0 1 .548.22v3.258a4.49 4.49 0 0 0-.823-.266 4.21 4.21 0 0 0-.936-.104c-.563-.004-1.118.139-1.61.416a3.33 3.33 0 0 0-1.199 1.132v7.764h-3.701zm9.377-8.585a6.08 6.08 0 0 0-.514 2.484 6.08 6.08 0 0 0 .514 2.484c.329.759.803 1.446 1.393 2.022a6.37 6.37 0 0 0 2.079 1.34c1.653.647 3.486.647 5.139 0a6.37 6.37 0 0 0 2.079-1.34c.59-.576 1.064-1.262 1.393-2.022.339-.783.514-1.629.514-2.484s-.175-1.701-.514-2.484a6.38 6.38 0 0 0-1.393-2.022 6.54 6.54 0 0 0-2.079-1.352c-1.65-.662-3.488-.662-5.139 0a6.54 6.54 0 0 0-2.079 1.352 6.38 6.38 0 0 0-1.393 2.022zm7.181 5.355a2.76 2.76 0 0 1-1.14.226 2.76 2.76 0 0 1-1.139-.226c-.36-.155-.684-.386-.95-.676-.551-.6-.857-1.388-.857-2.207a3.26 3.26 0 0 1 .857-2.207 2.88 2.88 0 0 1 2.09-.901c.391 0 .776.08 1.136.235a2.91 2.91 0 0 1 .954.666c.55.6.856 1.388.856 2.207s-.306 1.607-.856 2.207a2.79 2.79 0 0 1-.951.676zm7.152-8.994h3.701v.832c.452-.344.957-.61 1.496-.786a5.5 5.5 0 0 1 1.747-.277c1.417 0 2.574.462 3.472 1.386s1.348 2.103 1.348 3.535v7.533h-3.7v-7.025a2.14 2.14 0 0 0-.133-.868 2.12 2.12 0 0 0-.473-.738 2.07 2.07 0 0 0-.729-.478 2.06 2.06 0 0 0-.858-.135 2.95 2.95 0 0 0-1.256.254 2.47 2.47 0 0 0-.914.716v8.272h-3.701V11.135zm15.214 3.119v5.384c-.001 1.279.369 2.246 1.107 2.9s1.831.982 3.278.982c.436-.007.871-.041 1.302-.104a7.56 7.56 0 0 0 1.279-.266v-3.027a5.82 5.82 0 0 1-.845.196c-.272.04-.547.059-.822.058-.594 0-1.009-.112-1.245-.335s-.354-.604-.354-1.144v-4.645h3.381v-3.12h-3.381v-3.95l-3.7.809v3.143h-2.445v3.12h2.445zM12.458.942c-.247.155-.489.401-.974.891l-.881.985c-.508.827-.508 1.874 0 2.701.154.25.396.495.88.985h0c.485.49.727.735.974.891.817.514 1.852.514 2.67 0 .247-.155.489-.401.974-.891l.881-.985c.508-.827.508-1.874 0-2.701-.153-.25-.396-.495-.88-.985s-.727-.735-.974-.891c-.817-.514-1.853-.514-2.67 0zm3.189 15.339l5.64-5.705c.234-.236.511-.424.816-.552a2.49 2.49 0 0 1 1.926 0 2.5 2.5 0 0 1 .816.552l2.834 2.867-13.839 14-13.84-14 2.864-2.897c.234-.236.511-.424.816-.552a2.49 2.49 0 0 1 1.925 0 2.51 2.51 0 0 1 .816.552l5.669 5.735c.234.236.511.424.816.552s.632.194.963.194a2.49 2.49 0 0 0 .963-.194 2.51 2.51 0 0 0 .816-.552z"
5
+ />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+
3
+ const props = withDefaults(defineProps<{
4
+ modelValue?: boolean
5
+ label?: string
6
+ autoGenerated?: boolean
7
+ }>(), {
8
+ modelValue: false,
9
+ label: '',
10
+ autoGenerated: false,
11
+ })
12
+
13
+ const emit = defineEmits(['update:modelValue'])
14
+
15
+ const model = ref<boolean>(props.modelValue ?? false)
16
+
17
+ watch(model, (v) => emit('update:modelValue', v))
18
+ </script>
19
+
20
+ <template>
21
+ <div style="display:flex;align-items:center;gap:8px">
22
+ <v-switch
23
+ v-model="model"
24
+ :label="props.label || (model ? 'True' : 'False')"
25
+ inset
26
+ hide-details
27
+ />
28
+ <span v-if="props.autoGenerated" class="auto-badge">Auto</span>
29
+ </div>
30
+ </template>
31
+
32
+ <style scoped>
33
+ .auto-badge{font-size:12px;padding:4px 6px;border-radius:4px;background:#eee;color:#333}
34
+ </style>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ defaultValue?: string
6
+ label?: string
7
+ width?: string | null
8
+ field: string
9
+ required?: boolean
10
+ options?: Record<string, any> | null
11
+ modelValue?: string
12
+ autoGenerated?: boolean
13
+ }>(), {
14
+ required: false,
15
+ options: null,
16
+ width: null,
17
+ autoGenerated: false,
18
+ })
19
+
20
+ const emit = defineEmits(['update:modelValue'])
21
+
22
+ const fieldWidth = props.width === 'full' ? '100%' : '50%'
23
+
24
+ const dateTimeValue = computed({
25
+ get() {
26
+ return props.modelValue || props.defaultValue || ''
27
+ },
28
+ set(value) {
29
+ emit('update:modelValue', value)
30
+ }
31
+ })
32
+ </script>
33
+
34
+ <template>
35
+ <div :style="'width: ' + fieldWidth + '; display:flex; align-items:center; gap:8px'">
36
+ <v-text-field :name="field" v-model="dateTimeValue" type="datetime-local" :required="required" :label="label" style="flex:1" />
37
+ <span v-if="props.autoGenerated" class="auto-badge">Auto</span>
38
+ </div>
39
+ </template>
40
+
41
+ <style scoped>
42
+ .auto-badge{font-size:12px;padding:4px 6px;border-radius:4px;background:#eee;color:#333}
43
+ input {width:100%;}
44
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div v-if="loading">Loading form…</div>
3
+
4
+ <form v-else @submit.prevent="submit" class="space-y-4">
5
+ <FormField v-for="field in schema" :key="field.key" :field="field" :form="engine.form" />
6
+
7
+ <button type="submit" class="btn-primary">
8
+ {{ submitLabel || 'Submit' }}
9
+ </button>
10
+ </form>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import {
15
+ ref,
16
+ onMounted
17
+ } from 'vue';
18
+ import {
19
+ useVueDirectus
20
+ } from '@meeovi/directus-client';
21
+ import {
22
+ generateFieldSchema,
23
+ createFormEngine
24
+ } from '@meeovi/directus-client';
25
+
26
+ const props = defineProps < {
27
+ collection: string;
28
+ submitLabel ? : string;
29
+ } > ();
30
+
31
+ const emit = defineEmits(['submitted']);
32
+
33
+ const directus = useVueDirectus();
34
+ const loading = ref(true);
35
+ const schema = ref < any[] > ([]);
36
+ const engine = ref < any > (null);
37
+
38
+ onMounted(async () => {
39
+ const fields = await directus.client.request(
40
+ directus.readFieldsByCollection(props.collection)
41
+ );
42
+
43
+ schema.value = generateFormSchema(fields);
44
+
45
+ engine.value = createFormEngine(
46
+ props.collection,
47
+ fields,
48
+ directus, {
49
+ clearOnSuccess: false
50
+ }
51
+ );
52
+
53
+ loading.value = false;
54
+ });
55
+
56
+ async function submit() {
57
+ const result = await engine.value.submit();
58
+ emit('submitted', result);
59
+ }
60
+ </script>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ ref,
4
+ onMounted
5
+ } from 'vue';
6
+ import {
7
+ useVueDirectus
8
+ } from '@meeovi/directus-client';
9
+ import {
10
+ generateTableSchema
11
+ } from '@meeovi/directus-client';
12
+
13
+ const props = defineProps < {
14
+ collection: string;
15
+ } > ();
16
+
17
+ const directus = useVueDirectus();
18
+ const loading = ref(true);
19
+ const rows = ref < any[] > ([]);
20
+ const columns = ref < any[] > ([]);
21
+
22
+ onMounted(async () => {
23
+ const fields = await directus.client.request(
24
+ directus.readFieldsByCollection(props.collection)
25
+ );
26
+
27
+ columns.value = generateTableSchema(fields);
28
+
29
+ rows.value = await directus.client.request(
30
+ directus.readItems(props.collection)
31
+ );
32
+
33
+ loading.value = false;
34
+ });
35
+ </script>
36
+
37
+ <template>
38
+ <div v-if="loading">Loading table…</div>
39
+
40
+ <table v-else class="auto-table">
41
+ <thead>
42
+ <tr>
43
+ <th v-for="col in columns" :key="col.key">
44
+ {{ col.label }}
45
+ </th>
46
+ </tr>
47
+ </thead>
48
+
49
+ <tbody>
50
+ <tr v-for="row in rows" :key="row.id">
51
+ <td v-for="col in columns" :key="col.key">
52
+ {{ row[col.key] }}
53
+ </td>
54
+ </tr>
55
+ </tbody>
56
+ </table>
57
+ </template>
@@ -0,0 +1,85 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ defaultValue?: string
6
+ label?: string
7
+ options?: Record<string, any> | null
8
+ width?: string | null
9
+ field: string
10
+ required?: boolean
11
+ modelValue?: any
12
+ autoGenerated?: boolean
13
+ }>(), {
14
+ required: false,
15
+ options: null,
16
+ width: null,
17
+ autoGenerated: false,
18
+ })
19
+
20
+ const emit = defineEmits(['update:modelValue'])
21
+
22
+ const fieldWidth = props.width === 'full' ? '100%' : '50%'
23
+
24
+ const fileInput = ref(null)
25
+ const selectedFile = ref<File[] | null>(null)
26
+
27
+ const fileInputValue = computed({
28
+ get() {
29
+ return props.modelValue || props.defaultValue || null
30
+ },
31
+ set(value) {
32
+ selectedFile.value = value
33
+
34
+ // If we have a file, prepare it for upload
35
+ if (value && value.length > 0) {
36
+ const file = value[0]
37
+
38
+ // Create FormData for file upload
39
+ const formData = new FormData()
40
+ formData.append('file', file)
41
+
42
+ // Store the file object to be used during form submission
43
+ emit('update:modelValue', {
44
+ file: file,
45
+ formData: formData,
46
+ fileName: file.name,
47
+ fileType: file.type,
48
+ fileSize: file.size
49
+ })
50
+ } else {
51
+ emit('update:modelValue', null)
52
+ }
53
+ }
54
+ })
55
+ </script>
56
+
57
+ <template>
58
+ <div :style="'width: ' + fieldWidth + '; display:flex; align-items:center; gap:8px'">
59
+ <v-file-input
60
+ ref="fileInput"
61
+ clearable
62
+ density="compact"
63
+ :name="field"
64
+ v-model="fileInputValue"
65
+ :label="label"
66
+ :required="required"
67
+ variant="solo-inverted"
68
+ accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.csv,.txt"
69
+ prepend-icon="fas:fa fa-paperclip"
70
+ :hint="selectedFile && selectedFile.length ? `${selectedFile.length} file(s) selected` : 'No file selected'"
71
+ persistent-hint
72
+ />
73
+ <span v-if="props.autoGenerated" class="auto-badge">Auto</span>
74
+ </div>
75
+ </template>
76
+
77
+ <style scoped>
78
+ input {
79
+ width: 100%;
80
+ }
81
+ </style>
82
+
83
+ <style scoped>
84
+ .auto-badge{font-size:12px;padding:4px 6px;border-radius:4px;background:#eee;color:#333}
85
+ </style>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ widgetRegistry
4
+ } from '@meeovi/directus-client';
5
+
6
+ const props = defineProps < {
7
+ field: any;
8
+ form: Record < string,
9
+ any > ;
10
+ } > ();
11
+
12
+ const widget = widgetRegistry[props.field.widget];
13
+ </script>
14
+
15
+ <template>
16
+ <div v-if="widget">
17
+ <!-- Basic text input -->
18
+ <TextInput v-if="widget.component === 'TextInput'" v-model="form[field.key]" :label="field.key" />
19
+
20
+ <!-- Dropdown -->
21
+ <SelectInput v-else-if="widget.component === 'SelectInput'" v-model="form[field.key]"
22
+ :options="field.options?.choices" :label="field.key" />
23
+
24
+ <!-- Repeater -->
25
+ <RepeaterInput v-else-if="widget.component === 'RepeaterInput'" v-model="form[field.key]" :fields="field.fields"
26
+ :label="field.key" />
27
+
28
+ <!-- File upload -->
29
+ <FileInput v-else-if="widget.component === 'FileInput'" v-model="form[field.key]" :label="field.key" />
30
+
31
+ <!-- Fallback -->
32
+ <TextInput v-else v-model="form[field.key]" :label="field.key" />
33
+ </div>
34
+ </template>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <v-select v-model="internalValue" :items="options" :label="label" :multiple="multiple" item-title="display"
3
+ item-value="id" clearable />
4
+ </template>
5
+
6
+ <script setup>
7
+ import {
8
+ ref,
9
+ onMounted,
10
+ computed
11
+ } from "vue";
12
+
13
+ const props = defineProps({
14
+ modelValue: {
15
+ type: [String, Array],
16
+ default: null
17
+ },
18
+ collection: {
19
+ type: String,
20
+ required: true
21
+ }, // related collection name
22
+ label: {
23
+ type: String,
24
+ default: ""
25
+ },
26
+ multiple: {
27
+ type: Boolean,
28
+ default: false
29
+ },
30
+ });
31
+
32
+ const emit = defineEmits(["update:modelValue"]);
33
+ const {
34
+ $directus,
35
+ $readItems
36
+ } = useNuxtApp();
37
+
38
+ const internalValue = computed({
39
+ get: () => props.modelValue,
40
+ set: (v) => emit("update:modelValue", v),
41
+ });
42
+
43
+ const options = ref([]);
44
+
45
+ onMounted(async () => {
46
+ try {
47
+ const {
48
+ data
49
+ } = await $directus.request(
50
+ $readItems(props.collection, {
51
+ limit: 50, // adjust as needed
52
+ })
53
+ );
54
+ // Map to display-friendly format
55
+ options.value = data.map((item) => ({
56
+ id: item.id,
57
+ display: item.name || item.title || `Item ${item.id}`,
58
+ }));
59
+ } catch (err) {
60
+ console.error("Failed to load relation options:", err);
61
+ }
62
+ });
63
+ </script>
@@ -0,0 +1,121 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, watch } from 'vue'
3
+ import DirectusFormElement from './DirectusFormElement.vue'
4
+
5
+ const props = withDefaults(defineProps<{
6
+ modelValue?: any
7
+ field: string
8
+ label?: string
9
+ options?: Record<string, any>
10
+ required?: boolean
11
+ width?: string | null
12
+ autoGenerated?: boolean
13
+ }>(), {
14
+ options: () => ({} as Record<string, any>),
15
+ label: '',
16
+ required: false,
17
+ width: null,
18
+ autoGenerated: false,
19
+ })
20
+
21
+ const emit = defineEmits(['update:modelValue'])
22
+
23
+ const items = ref<Array<any>>(Array.isArray(props.modelValue) ? JSON.parse(JSON.stringify(props.modelValue)) : [])
24
+ const jsonMode = ref(false)
25
+
26
+ watch(() => props.modelValue, (v) => {
27
+ items.value = Array.isArray(v) ? JSON.parse(JSON.stringify(v)) : []
28
+ })
29
+
30
+ const createEmptyItem = () => {
31
+ const fields = (props.options && Array.isArray(props.options.fields)) ? props.options.fields : null
32
+ if (!fields) return ''
33
+ const obj: Record<string, any> = {}
34
+ for (const f of fields) {
35
+ obj[f.field] = f.schema?.default_value ?? ''
36
+ }
37
+ return obj
38
+ }
39
+
40
+ const addItem = () => {
41
+ items.value.push(createEmptyItem())
42
+ emit('update:modelValue', items.value)
43
+ }
44
+
45
+ const removeItem = (idx: number) => {
46
+ items.value.splice(idx, 1)
47
+ emit('update:modelValue', items.value)
48
+ }
49
+
50
+ const updateItem = (idx: number, val: any) => {
51
+ items.value[idx] = val
52
+ emit('update:modelValue', items.value)
53
+ }
54
+
55
+ const toggleJson = () => {
56
+ jsonMode.value = !jsonMode.value
57
+ }
58
+
59
+ /**
60
+ * Handle textarea input for JSON mode in script (typed), avoiding template typing issues.
61
+ */
62
+ const onJsonInput = (e: Event) => {
63
+ const target = e.target as HTMLTextAreaElement | null
64
+ if (!target) return
65
+ const text = target.value
66
+ try {
67
+ const parsed = JSON.parse(text)
68
+ emit('update:modelValue', parsed)
69
+ items.value = Array.isArray(parsed) ? parsed : []
70
+ } catch (err) {
71
+ // ignore invalid json until saved
72
+ }
73
+ }
74
+ </script>
75
+
76
+ <template>
77
+ <div :style="'width: ' + (props.width === 'full' ? '100%' : '50%')">
78
+ <label style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
79
+ <span>{{ props.label || props.field }}</span>
80
+ <button type="button" @click="toggleJson" style="font-size:12px">{{ jsonMode ? 'Edit Items' : 'Edit JSON' }}</button>
81
+ </label>
82
+
83
+ <div v-if="!jsonMode">
84
+ <div v-for="(it, idx) in items" :key="idx" style="border:1px solid #eee;padding:8px;margin-bottom:8px">
85
+ <div style="display:flex;flex-direction:column;gap:8px">
86
+ <template v-if="props.options && Array.isArray(props.options.fields) && props.options.fields.length">
87
+ <DirectusFormElement
88
+ v-for="sub in props.options.fields"
89
+ :key="sub.field"
90
+ :field="sub"
91
+ v-model="items[idx][sub.field]"
92
+ />
93
+ </template>
94
+ <template v-else>
95
+ <div style="display:flex;gap:8px;align-items:center;margin-bottom:6px">
96
+ <input type="text" v-model="items[idx]" @input="updateItem(idx, items[idx])" style="flex:1;padding:8px" />
97
+ </div>
98
+ </template>
99
+ </div>
100
+
101
+ <div style="display:flex;justify-content:flex-end;margin-top:8px">
102
+ <button type="button" @click="removeItem(idx)" style="padding:6px">Remove</button>
103
+ </div>
104
+ </div>
105
+
106
+ <button type="button" @click="addItem" style="padding:6px">Add item</button>
107
+ </div>
108
+
109
+ <div v-else>
110
+ <textarea
111
+ style="width:100%;min-height:200px;padding:8px"
112
+ :value="JSON.stringify(items, null, 2)"
113
+ @input="onJsonInput"
114
+ ></textarea>
115
+ </div>
116
+ </div>
117
+ </template>
118
+
119
+ <style scoped>
120
+ button{cursor:pointer}
121
+ </style>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ interface Choice {
5
+ text: string;
6
+ value: string;
7
+ }
8
+
9
+ const props = withDefaults(defineProps<{
10
+ defaultValue?: string
11
+ label?: string
12
+ options?: Record<string, any>
13
+ width?: string | null
14
+ field: string
15
+ required?: boolean
16
+ modelValue?: string
17
+ autoGenerated?: boolean
18
+ }>(), {
19
+ required: false,
20
+ options: {},
21
+ width: null,
22
+ autoGenerated: false,
23
+ })
24
+
25
+ const emit = defineEmits(['update:modelValue'])
26
+
27
+ const fieldWidth = props.width === 'full' ? '100%' : '50%'
28
+
29
+ const selectValue = computed({
30
+ get() {
31
+ return props.modelValue || props.defaultValue || ''
32
+ },
33
+ set(value) {
34
+ emit('update:modelValue', value)
35
+ }
36
+ })
37
+
38
+ // Transform options for v-select
39
+ const items = computed(() => {
40
+ return props.options?.choices?.map((choice: Choice) => ({
41
+ title: choice.text,
42
+ value: choice.value
43
+ })) || []
44
+ })
45
+ </script>
46
+
47
+ <template>
48
+ <div :style="'width: ' + fieldWidth + '; display:flex; align-items:center; gap:8px'">
49
+ <v-select
50
+ :name="field"
51
+ v-model="selectValue"
52
+ :items="items"
53
+ :label="label"
54
+ :required="required"
55
+ variant="solo-inverted"
56
+ density="compact"
57
+ style="flex:1"
58
+ />
59
+ <span v-if="props.autoGenerated" class="auto-badge">Auto</span>
60
+ </div>
61
+ </template>
62
+
63
+ <style scoped>
64
+ .auto-badge{font-size:12px;padding:4px 6px;border-radius:4px;background:#eee;color:#333}
65
+ </style>