@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,65 @@
1
+ <template>
2
+ <div class="media-carousel">
3
+ <v-slide-group show-arrows>
4
+ <v-slide-group-item
5
+ v-for="item in items"
6
+ :key="item.id"
7
+ >
8
+ <div class="media-carousel-item" @click="open(item)">
9
+ <component
10
+ :is="thumbComponent(item)"
11
+ :media="item.directus_files_id"
12
+ />
13
+ </div>
14
+ </v-slide-group-item>
15
+ </v-slide-group>
16
+
17
+ <FullscreenMediaModal
18
+ :model-value="!!activeItem"
19
+ :item="activeItem"
20
+ @update:model-value="val => !val && close()"
21
+ />
22
+ </div>
23
+ </template>
24
+
25
+ <script setup>
26
+ import { ref } from 'vue'
27
+ import imageCard from '#shared/app/components/media/imageCard.vue'
28
+ import mediaCard from '#shared/app/components/media/mediaCard.vue'
29
+ import mediaPlayer from '#shared/app/components/media/mediaPlayer.vue'
30
+ import FullscreenMediaModal from '#shared/app/components/media/FullscreenMediaModal.vue'
31
+
32
+ const props = defineProps({
33
+ items: { type: Array, default: () => [] },
34
+ })
35
+
36
+ const activeItem = ref(null)
37
+
38
+ const mime = (item) => item?.directus_files_id?.type || ''
39
+
40
+ // ⭐ THIS IS WHERE thumbComponent GOES
41
+ const thumbComponent = (item) => {
42
+ const t = mime(item)
43
+ if (t.startsWith('image')) return imageCard
44
+ if (t.startsWith('audio') || t.startsWith('video')) return mediaPlayer
45
+ return mediaCard
46
+ }
47
+
48
+ const open = (item) => {
49
+ activeItem.value = item
50
+ }
51
+
52
+ const close = () => {
53
+ activeItem.value = null
54
+ }
55
+ </script>
56
+
57
+ <style scoped>
58
+ .media-carousel {
59
+ margin-top: 1rem;
60
+ }
61
+ .media-carousel-item {
62
+ width: 220px;
63
+ margin-right: 12px;
64
+ }
65
+ </style>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <v-navigation-drawer permanent width="260" class="media-folder-sidebar">
3
+ <v-list>
4
+ <v-list-item>
5
+ <v-list-item-title class="text-h6">Folders</v-list-item-title>
6
+ </v-list-item>
7
+
8
+ <v-list-item v-for="(folder, index) in localFolders" :key="folder.id" draggable="true"
9
+ @dragstart="onDragStart(index)" @dragover.prevent @drop="onDrop(index)"
10
+ @click="$emit('select', folder)">
11
+ <v-list-item-title>{{ folder.name }}</v-list-item-title>
12
+ </v-list-item>
13
+
14
+ <v-divider class="my-3" />
15
+
16
+ <v-list-item>
17
+ <v-btn block color="primary" @click="createNewFolder">
18
+ New Folder
19
+ </v-btn>
20
+ </v-list-item>
21
+ </v-list>
22
+ </v-navigation-drawer>
23
+ </template>
24
+
25
+ <script setup>
26
+ import {
27
+ ref,
28
+ watch
29
+ } from 'vue'
30
+
31
+ const props = defineProps({
32
+ folders: {
33
+ type: Array,
34
+ default: () => []
35
+ },
36
+ })
37
+ const emit = defineEmits(['create', 'select', 'reorder'])
38
+
39
+ const localFolders = ref([...props.folders])
40
+ const dragIndex = ref(null)
41
+
42
+ watch(
43
+ () => props.folders,
44
+ (val) => {
45
+ localFolders.value = [...val]
46
+ }
47
+ )
48
+
49
+ const onDragStart = (index) => {
50
+ dragIndex.value = index
51
+ }
52
+ const onDrop = (targetIndex) => {
53
+ if (dragIndex.value === null) return
54
+ const updated = [...localFolders.value]
55
+ const [moved] = updated.splice(dragIndex.value, 1)
56
+ updated.splice(targetIndex, 0, moved)
57
+ localFolders.value = updated
58
+ dragIndex.value = null
59
+ emit('reorder', updated)
60
+ }
61
+
62
+ const createNewFolder = () => {
63
+ const name = prompt('Folder name')
64
+ if (name) emit('create', name)
65
+ }
66
+ </script>
67
+
68
+ <style scoped>
69
+ .media-folder-sidebar {
70
+ border-right: 1px solid #ddd;
71
+ }
72
+ </style>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <div>
3
+ <video ref="videoPlayer" class="video-js"></video>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { ref, onMounted, onBeforeUnmount } from 'vue'
9
+ import useVideojs from '~/composables/media/useVideojs'
10
+
11
+ defineOptions({ name: 'VideoPlayer' })
12
+
13
+ const props = defineProps({
14
+ options: {
15
+ type: Object,
16
+ default: () => ({})
17
+ },
18
+ // optional media prop (kept for compatibility)
19
+ media: {
20
+ type: Object,
21
+ required: false
22
+ }
23
+ })
24
+
25
+ const { createPlayer, disposePlayer } = useVideojs()
26
+
27
+ const videoPlayer = ref(null)
28
+ const player = ref(null)
29
+
30
+ onMounted(() => {
31
+ const p = createPlayer(videoPlayer.value, props.options, function () {
32
+ this.log && this.log('onPlayerReady', this)
33
+ })
34
+ player.value = p
35
+ })
36
+
37
+ onBeforeUnmount(() => {
38
+ disposePlayer(player.value)
39
+ })
40
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <v-text-field v-model="query" label="Search media" prepend-inner-icon="mdi-magnify" clearable @input="emitSearch" />
3
+ </template>
4
+
5
+ <script setup>
6
+ import {
7
+ ref
8
+ } from 'vue'
9
+
10
+ const query = ref('')
11
+ const emit = defineEmits(['search'])
12
+
13
+ function emitSearch() {
14
+ emit('search', query.value)
15
+ }
16
+ </script>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <client-only>
3
+ <div id="gallery-videojs">
4
+ <NuxtLink data-lg-size="1280-720"
5
+ data-video='{"source": [{"src":"/videos/video1.mp4", "type":"video/mp4"}], "attributes": {"preload": false, "controls": true}}'
6
+ data-poster="/images/demo/youtube-video-poster.jpg"
7
+ data-sub-html="<h4>'Peck Pocketed' by Kevin Herron | Disney Favorite</h4>">
8
+ <img width="300" height="100" class="img-responsive" src="../../assets/images/demo/youtube-video-poster.jpg" />
9
+ </NuxtLink>
10
+ </div>
11
+ </client-only>
12
+ </template>
13
+
14
+ <script setup>
15
+ import {
16
+ ref,
17
+ onMounted
18
+ } from 'vue'
19
+
20
+ const props = defineProps({
21
+ items: {
22
+ type: Array,
23
+ default: () => []
24
+ },
25
+ })
26
+
27
+ const galleryRef = ref(null)
28
+
29
+ const runtime = useRuntimeConfig()
30
+ const base = runtime.public.directus.url
31
+
32
+ const fileUrl = (item) => `${base}assets/${item.directus_files_id.id}`
33
+ const thumbnail = (item) => `${base}assets/${item.directus_files_id.id}?key=thumbnail`
34
+
35
+ const videoConfig = (item) =>
36
+ JSON.stringify({
37
+ source: [{
38
+ src: fileUrl(item),
39
+ type: 'video/mp4',
40
+ }, ],
41
+ attributes: {
42
+ preload: false,
43
+ controls: true,
44
+ },
45
+ })
46
+
47
+ onMounted(async () => {
48
+ const lg = await import('lightgallery')
49
+ const lightGallery = lg.default
50
+
51
+ const lgVideo = (await import('lg-video')).default
52
+
53
+ lightGallery(document.getElementById('gallery-videojs'), {
54
+ plugins: [lgVideo],
55
+ videojs: true,
56
+ videojsOptions: {
57
+ muted: true,
58
+ },
59
+ });
60
+
61
+ })
62
+ </script>
63
+
64
+ <style scoped>
65
+ .video-gallery {
66
+ display: flex;
67
+ flex-wrap: wrap;
68
+ gap: 10px;
69
+ }
70
+
71
+ .gallery-item img {
72
+ width: 200px;
73
+ height: 140px;
74
+ object-fit: cover;
75
+ border-radius: 8px;
76
+ }
77
+ </style>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <SfAccordionItem v-model="internalModelValue" :summary-class="summaryClass" data-testid="accordion-item">
3
+ <template #summary>
4
+ <slot name="summary">
5
+ <p>{{ summary }}</p>
6
+ </slot>
7
+ <SfIconChevronLeft :class="['text-neutral-500', modelValue ? 'rotate-90' : '-rotate-90']" />
8
+ </template>
9
+ <div class="py-2 px-4">
10
+ <slot />
11
+ </div>
12
+ </SfAccordionItem>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { SfAccordionItem, SfIconChevronLeft } from '@storefront-ui/vue';
17
+ import { useVModel } from '@vueuse/core';
18
+ import type { AccordionItemProps } from '~/components/ui/AccordionItem/types';
19
+
20
+ const props = withDefaults(defineProps<AccordionItemProps>(), { modelValue: false, summary: '', summaryClass: '' });
21
+ const emit = defineEmits(['update:modelValue']);
22
+
23
+ const internalModelValue = useVModel(props, 'modelValue', emit, { passive: true });
24
+ </script>
@@ -0,0 +1,14 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import AccordionItem from '~/components/ui/AccordionItem/AccordionItem.vue';
3
+
4
+ describe('<AccordionItem />', () => {
5
+ it('should render component', () => {
6
+ const { getByTestId } = mount(AccordionItem, {
7
+ props: {
8
+ breadcrumbs: [],
9
+ },
10
+ });
11
+
12
+ expect(getByTestId('accordion-item'));
13
+ });
14
+ });
@@ -0,0 +1,5 @@
1
+ export type AccordionItemProps = {
2
+ modelValue?: boolean;
3
+ summary?: string;
4
+ summaryClass?: string;
5
+ };
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div :class="['inline-flex items-center justify-center', classes]">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts">
8
+ const sizeClasses = {
9
+ sm: 'text-xs p-1 gap-1',
10
+ base: 'text-sm p-1.5 gap-1.5',
11
+ };
12
+ </script>
13
+
14
+ <script setup lang="ts">
15
+ import type { AlertProps } from '~/components/ui/Alert/types';
16
+
17
+ const props = withDefaults(defineProps<AlertProps>(), {
18
+ size: 'base',
19
+ strong: false,
20
+ variant: 'primary',
21
+ });
22
+
23
+ const classes = computed(() => [
24
+ sizeClasses[props.size],
25
+ props.strong ? 'text-white font-medium rounded-none' : 'rounded-md font-normal',
26
+ {
27
+ [`text-primary-800 ${props.strong ? 'bg-primary-600' : 'bg-primary-100'}`]: props.variant === 'primary',
28
+ [`text-secondary-800 ${props.strong ? 'bg-secondary-800' : 'bg-secondary-100'}`]: props.variant === 'secondary',
29
+ [`text-negative-800 ${props.strong ? 'bg-negative-600' : 'bg-negative-100'}`]: props.variant === 'negative',
30
+ [`text-neutral-900 border border-neutral-200 ${props.strong ? 'bg-neutral-600' : 'bg-neutral-100'}`]:
31
+ props.variant === 'neutral',
32
+ },
33
+ ]);
34
+ </script>
@@ -0,0 +1,5 @@
1
+ export interface AlertProps {
2
+ size?: 'sm' | 'base';
3
+ strong?: boolean;
4
+ variant?: 'primary' | 'secondary' | 'negative' | 'neutral';
5
+ }
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <nav data-testid="breadcrumbs" class="inline-flex items-center text-sm font-normal font-body">
3
+ <ol class="flex w-auto leading-none group md:flex-wrap">
4
+ <li class="flex items-center sm:hidden text-neutral-500 z-10">
5
+ <NuxtLazyHydrate :on-interaction="['click', 'touchstart']">
6
+ <SfDropdown v-model="dropdownOpened" strategy="absolute" placement="bottom-start" @update:model-value="close">
7
+ <template #trigger>
8
+ <SfButton
9
+ class="relative w-5 h-5 !p-0 rounded-sm outline-secondary-600 hover:bg-transparent active:bg-transparent"
10
+ :aria-label="$t('breadcrumbsDropdownText')"
11
+ variant="tertiary"
12
+ square
13
+ @click="toggle"
14
+ data-testid="breadcrumbs-dropdown-button"
15
+ >
16
+ <template #prefix>
17
+ <SfIconMoreHoriz
18
+ size="sm"
19
+ class="text-neutral-500 hover:text-primary-700 active:text-primary-800 active:bg-transparent"
20
+ />
21
+ </template>
22
+ </SfButton>
23
+ </template>
24
+ <ol class="px-4 py-2 rounded-md shadow-md border-neutral-100 bg-white" data-testid="breadcrumbs-dropdown">
25
+ <li v-for="item in breadcrumbs" :key="item.name" class="py-2 last-of-type:hidden">
26
+ <SfLink
27
+ :tag="NuxtLink"
28
+ :to="item.link"
29
+ variant="secondary"
30
+ class="leading-5 no-underline text-inherit hover:underline active:underline whitespace-nowrap outline-secondary-600"
31
+ >
32
+ {{ item.name }}
33
+ </SfLink>
34
+ </li>
35
+ </ol>
36
+ </SfDropdown>
37
+ </NuxtLazyHydrate>
38
+ </li>
39
+ <li
40
+ v-for="(item, index) in breadcrumbs"
41
+ :key="item.name"
42
+ class="peer hidden sm:flex items-center peer-[:nth-of-type(even)]:before:content-['/'] peer-[:nth-of-type(even)]:before:px-2 peer-[:nth-of-type(even)]:before:leading-5 last-of-type:flex last-of-type:before:font-normal last-of-type:before:text-neutral-500 text-neutral-500 last-of-type:text-neutral-900 last-of-type:font-medium"
43
+ >
44
+ <SfLink
45
+ v-if="index < breadcrumbs.length - 1"
46
+ :tag="NuxtLink"
47
+ :to="item.link"
48
+ variant="secondary"
49
+ class="leading-5 no-underline hover:underline active:underline whitespace-nowrap outline-secondary-600 text-inherit"
50
+ >
51
+ {{ item.name }}
52
+ </SfLink>
53
+ <span v-else>
54
+ {{ item.name }}
55
+ </span>
56
+ </li>
57
+ </ol>
58
+ </nav>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { SfDropdown, SfButton, SfLink, SfIconMoreHoriz } from '@storefront-ui/vue';
63
+ import type { BreadcrumbsProps } from '~/components/ui/Breadcrumbs/types';
64
+
65
+ defineProps<BreadcrumbsProps>();
66
+
67
+ const dropdownOpened = ref(false);
68
+ const close = () => {
69
+ dropdownOpened.value = false;
70
+ };
71
+ const toggle = () => {
72
+ dropdownOpened.value = !dropdownOpened.value;
73
+ };
74
+
75
+ const NuxtLink = resolveComponent('NuxtLink');
76
+ </script>
@@ -0,0 +1,14 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Breadcrumbs from '~/components/ui/Breadcrumbs/Breadcrumbs.vue';
3
+
4
+ describe('<Breadcrumbs />', () => {
5
+ it('should render component', () => {
6
+ const { getByTestId } = mount(Breadcrumbs, {
7
+ props: {
8
+ breadcrumbs: [],
9
+ },
10
+ });
11
+
12
+ expect(getByTestId('breadcrumbs'));
13
+ });
14
+ });
@@ -0,0 +1,8 @@
1
+ export type Breadcrumb = {
2
+ name: string;
3
+ link: string;
4
+ };
5
+
6
+ export type BreadcrumbsProps = {
7
+ breadcrumbs: Breadcrumb[];
8
+ };
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <div
3
+ class="relative flex first:border-t border-b-[1px] border-neutral-200 hover:shadow-lg min-w-[320px] p-4 last:mb-0"
4
+ data-testid="cart-product-card"
5
+ >
6
+ <div class="relative overflow-hidden rounded-md w-[100px] sm:w-[176px]">
7
+ <SfLink :tag="NuxtLink" :to="`${paths.product}${slug}`">
8
+ <NuxtImg
9
+ class="w-full h-auto border rounded-md border-neutral-200"
10
+ :src="imageUrl ?? '/images/product.webp'"
11
+ :alt="imageAlt ?? ''"
12
+ width="300"
13
+ height="300"
14
+ loading="lazy"
15
+ format="webp"
16
+ />
17
+ </SfLink>
18
+ <div class="absolute top-0 left-0 text-white bg-secondary-600 py-1 pl-1.5 pr-2 text-xs font-medium">
19
+ <SfIconSell size="xs" class="mr-1" />
20
+ {{ $t('sale') }}
21
+ </div>
22
+ </div>
23
+ <div class="flex flex-col pl-4 min-w-[180px] flex-1">
24
+ <SfLink
25
+ :tag="NuxtLink"
26
+ :to="`${paths.product}${slug}`"
27
+ variant="secondary"
28
+ class="no-underline typography-text-sm sm:typography-text-lg"
29
+ >
30
+ {{ name }}
31
+ </SfLink>
32
+ <div class="my-2 sm:mb-0">
33
+ <ul class="text-xs font-normal leading-5 sm:typography-text-sm text-neutral-700">
34
+ <li v-for="attribute in attributes" :key="attribute.name">
35
+ <span class="mr-1">{{ attribute.name }}:</span>
36
+ <span class="font-medium">{{ attribute.label }}</span>
37
+ </li>
38
+ </ul>
39
+ </div>
40
+ <div class="items-start sm:items-center sm:mt-auto flex flex-col sm:flex-row">
41
+ <span
42
+ v-if="specialPrice"
43
+ class="text-secondary-700 sm:order-1 font-bold typography-text-sm sm:typography-text-lg sm:ml-auto"
44
+ >
45
+ ${{ specialPrice }}
46
+ <span class="text-neutral-500 ml-2 line-through typography-text-xs sm:typography-text-sm font-normal">
47
+ ${{ price }}
48
+ </span>
49
+ </span>
50
+ <span v-else class="font-bold sm:ml-auto sm:order-1 typography-text-sm sm:typography-text-lg">
51
+ ${{ price }}
52
+ </span>
53
+ <UiQuantitySelector :min-value="minValue" :max-value="maxValue" class="mt-4 sm:mt-0" />
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script setup lang="ts">
60
+ import { SfLink, SfIconSell } from '@storefront-ui/vue';
61
+ import type { CartProductCardProps } from '~/components/ui/CartProductCard/types';
62
+
63
+ defineProps<CartProductCardProps>();
64
+
65
+ const NuxtLink = resolveComponent('NuxtLink');
66
+ </script>
@@ -0,0 +1,18 @@
1
+ export interface Attribute {
2
+ label: string;
3
+ name: string;
4
+ value: string;
5
+ }
6
+
7
+ export type CartProductCardProps = {
8
+ attributes: Attribute[];
9
+ imageUrl?: string | null;
10
+ imageAlt?: string | null;
11
+ maxValue: number;
12
+ minValue: number;
13
+ name: string;
14
+ price: number;
15
+ specialPrice: number;
16
+ value: number;
17
+ slug: string;
18
+ };
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <NuxtLazyHydrate when-visible>
3
+ <div
4
+ class="max-w-screen-3xl mx-auto md:px-10 px-4 mb-10 flex flex-nowrap md:flex-wrap md:justify-center overflow-x-scroll scrollbar-hidden"
5
+ data-testid="category-card"
6
+ >
7
+ <div v-for="item in items" :key="item.name" class="mr-2 md:mr-6 group">
8
+ <NuxtLink
9
+ :to="item.slug"
10
+ class="w-full h-full z-1 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-md"
11
+ >
12
+ <div
13
+ class="relative h-[240px] w-[240px] rounded-full bg-neutral-100 group-hover:shadow-xl group-active:shadow-none"
14
+ >
15
+ <NuxtImg
16
+ :src="item.image"
17
+ :alt="$t('imageOfSth', { name: item.name })"
18
+ width="240"
19
+ height="240"
20
+ loading="lazy"
21
+ format="webp"
22
+ />
23
+ </div>
24
+ <div class="flex justify-center">
25
+ <p
26
+ class="mt-4 font-semibold no-underline text-normal-900 typography-text-base group-hover:underline group-hover:text-primary-800 group-hover:font-normal group-active:text-primary-800 group-active:font-normal"
27
+ >
28
+ {{ item.name }}
29
+ </p>
30
+ </div>
31
+ </NuxtLink>
32
+ </div>
33
+ </div>
34
+ </NuxtLazyHydrate>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import type { CategoryCardProps } from '~/components/ui/CategoryCard/types';
39
+
40
+ defineProps<CategoryCardProps>();
41
+ </script>
@@ -0,0 +1,9 @@
1
+ import type { SfCategory } from '@vue-storefront/unified-data-model';
2
+
3
+ interface CategoryWithImage extends SfCategory {
4
+ image: string;
5
+ }
6
+
7
+ export type CategoryCardProps = {
8
+ items: CategoryWithImage[];
9
+ };
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <NuxtLazyHydrate when-visible>
3
+ <div
4
+ class="flex flex-col md:flex-row flex-wrap gap-6 max-w-screen-3xl mx-auto px-4 md:px-10 mb-10"
5
+ data-testid="display"
6
+ >
7
+ <div
8
+ v-for="item in items"
9
+ :key="item.title"
10
+ class="relative flex md:max-w-screen-3xl md:[&:not(:first-of-type)]:flex-1 md:first-of-type:w-full first:bg-secondary-200 last:bg-warning-200 even:bg-negative-200"
11
+ >
12
+ <div
13
+ :class="[
14
+ 'flex overflow-hidden grow flex-col',
15
+ {
16
+ 'flex-col-reverse': item.reverse,
17
+ 'md:flex-row-reverse': item.reverse,
18
+ },
19
+ ]"
20
+ >
21
+ <div class="flex flex-1 flex-col justify-center items-center md:items-start p-6 lg:p-10 max-w-1/2">
22
+ <p :class="['uppercase typography-text-xs block font-bold tracking-widest', item.subtitleClass]">
23
+ {{ item.subtitle }}
24
+ </p>
25
+ <h2 :class="['mb-4 mt-2 font-bold typography-headline-3', item.titleClass]">
26
+ {{ item.title }}
27
+ </h2>
28
+ <p class="typography-text-base block text-center md:text-left mb-4">{{ item.description }}</p>
29
+ <SfButton class="!bg-black" :tag="NuxtLink" :to="paths.category">
30
+ {{ item.buttonText }}
31
+ </SfButton>
32
+ </div>
33
+ <NuxtImg
34
+ :src="item.image"
35
+ :alt="item.title"
36
+ class="w-full md:w-1/2 self-end object-contain flex-1"
37
+ width="300"
38
+ height="300"
39
+ loading="lazy"
40
+ format="webp"
41
+ />
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </NuxtLazyHydrate>
46
+ </template>
47
+
48
+ <script setup lang="ts">
49
+ import { SfButton } from '@storefront-ui/vue';
50
+ import type { DisplayProps } from '~/components/ui/Display/types';
51
+
52
+ defineProps<DisplayProps>();
53
+
54
+ const NuxtLink = resolveComponent('NuxtLink');
55
+ </script>
@@ -0,0 +1,12 @@
1
+ export type DisplayProps = {
2
+ items: {
3
+ image: string;
4
+ title: string;
5
+ subtitle: string;
6
+ description: string;
7
+ buttonText: string;
8
+ reverse: boolean;
9
+ titleClass: string;
10
+ subtitleClass: string;
11
+ }[];
12
+ };
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <hr class="w-full h-px bg-neutral-200" data-testid="divider" />
3
+ </template>