@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.
- package/README.md +0 -0
- package/app/components/Gallery/Gallery.vue +187 -0
- package/app/components/Gallery/__tests__/Gallery.spec.ts +14 -0
- package/app/components/Heading/Heading.vue +14 -0
- package/app/components/Heading/__tests__/Heading.spec.ts +14 -0
- package/app/components/Heading/types.ts +5 -0
- package/app/components/media/audioGallery.vue +70 -0
- package/app/components/media/dragDropUpload.vue +67 -0
- package/app/components/media/fullscreenMediaModal.vue +66 -0
- package/app/components/media/imageCard.vue +65 -0
- package/app/components/media/imageGallery.vue +40 -0
- package/app/components/media/mediaCard.vue +89 -0
- package/app/components/media/mediaCarousel.vue +65 -0
- package/app/components/media/mediaFolderSidebar.vue +72 -0
- package/app/components/media/mediaPlayer.vue +40 -0
- package/app/components/media/mediaSearchBar.vue +16 -0
- package/app/components/media/videoGallery.vue +77 -0
- package/app/components/ui/AccordionItem/AccordionItem.vue +24 -0
- package/app/components/ui/AccordionItem/__tests__/AccordionItem.spec.ts +14 -0
- package/app/components/ui/AccordionItem/types.ts +5 -0
- package/app/components/ui/Alert/Alert.vue +34 -0
- package/app/components/ui/Alert/types.ts +5 -0
- package/app/components/ui/Breadcrumbs/Breadcrumbs.vue +76 -0
- package/app/components/ui/Breadcrumbs/__tests__/Breadcrumbs.spec.ts +14 -0
- package/app/components/ui/Breadcrumbs/types.ts +8 -0
- package/app/components/ui/CartProductCard/CartProductCard.vue +66 -0
- package/app/components/ui/CartProductCard/types.ts +18 -0
- package/app/components/ui/CategoryCard/CategoryCard.vue +41 -0
- package/app/components/ui/CategoryCard/types.ts +9 -0
- package/app/components/ui/Display/Display.vue +55 -0
- package/app/components/ui/Display/types.ts +12 -0
- package/app/components/ui/Divider/Divider.vue +3 -0
- package/app/components/ui/Divider/__tests__/Divider.spec.tsx +10 -0
- package/app/components/ui/Footer.vue +92 -0
- package/app/components/ui/Form/FormHelperText.vue +5 -0
- package/app/components/ui/Form/FormLabel.vue +5 -0
- package/app/components/ui/Form/FormPasswordInput.vue +15 -0
- package/app/components/ui/Form/__tests__/FormHelperText.spec.ts +10 -0
- package/app/components/ui/Form/__tests__/FormLabel.spec.ts +10 -0
- package/app/components/ui/Hero/Hero.vue +44 -0
- package/app/components/ui/Hero/types.ts +10 -0
- package/app/components/ui/Modal/Modal.vue +19 -0
- package/app/components/ui/Modal/types.ts +8 -0
- package/app/components/ui/Motionable.vue +45 -0
- package/app/components/ui/NavbarBottom.vue +63 -0
- package/app/components/ui/NavbarTop.vue +25 -0
- package/app/components/ui/Overlay/Overlay.vue +14 -0
- package/app/components/ui/Overlay/__tests__/Overlay.spec.ts +14 -0
- package/app/components/ui/Overlay/types.ts +3 -0
- package/app/components/ui/PageBuilder.vue +39 -0
- package/app/components/ui/PageContainer.vue +5 -0
- package/app/components/ui/Pagination/Pagination.vue +151 -0
- package/app/components/ui/Pagination/__tests__/Pagination.spec.ts +17 -0
- package/app/components/ui/Pagination/types.ts +6 -0
- package/app/components/ui/ProductCard/ProductCard.vue +55 -0
- package/app/components/ui/ProductCard/__tests__/ProductCard.spec.ts +16 -0
- package/app/components/ui/ProductCard/types.ts +12 -0
- package/app/components/ui/ProductCardHorizontal/ProductCardHorizontal.vue +34 -0
- package/app/components/ui/ProductCardHorizontal/__tests__/ProductCardHorizontal.spec.ts +36 -0
- package/app/components/ui/ProductCardHorizontal/types.ts +8 -0
- package/app/components/ui/PurchaseCard/PurchaseCard.vue +109 -0
- package/app/components/ui/PurchaseCard/__tests__/PurchaseCard.spec.ts +15 -0
- package/app/components/ui/PurchaseCard/types.ts +5 -0
- package/app/components/ui/QuantitySelector/QuantitySelector.vue +69 -0
- package/app/components/ui/QuantitySelector/__tests__/QuantitySelector.spec.ts +15 -0
- package/app/components/ui/QuantitySelector/types.ts +5 -0
- package/app/components/ui/RadialProgress.vue +44 -0
- package/app/components/ui/Review/Review.vue +57 -0
- package/app/components/ui/Review/__tests__/Review.spec.ts +15 -0
- package/app/components/ui/Review/types.ts +5 -0
- package/app/components/ui/ScrollTop.vue +82 -0
- package/app/components/ui/Search.vue +54 -0
- package/app/components/ui/Tag/Tag.vue +38 -0
- package/app/components/ui/Tag/__tests__/Tag.spec.ts +10 -0
- package/app/components/ui/Tag/types.ts +16 -0
- package/app/components/ui/VsfLogo.vue +7 -0
- package/app/components/ui/forms/BooleanInput.vue +34 -0
- package/app/components/ui/forms/DateTime.vue +44 -0
- package/app/components/ui/forms/DirectusFormElement.vue +60 -0
- package/app/components/ui/forms/DynamicTableElement.vue +57 -0
- package/app/components/ui/forms/FileInput.vue +85 -0
- package/app/components/ui/forms/FormField.vue +34 -0
- package/app/components/ui/forms/RelationSelect.vue +63 -0
- package/app/components/ui/forms/RepeaterInput.vue +121 -0
- package/app/components/ui/forms/SelectInput.vue +65 -0
- package/app/components/ui/forms/TextArea.vue +59 -0
- package/app/components/ui/forms/TextInput.vue +42 -0
- package/app/components/ui/forms/TiptapEditor.vue +136 -0
- package/app/components/ui/forms/[collection].vue +22 -0
- package/app/components/ui/studio/builder.vue +57 -0
- package/app/components/ui/studio/document.vue +69 -0
- package/app/components/ui/studio/email.vue +57 -0
- package/app/composables/globals/uploadFiles.js +41 -0
- package/app/composables/globals/useAdminTable.ts +12 -0
- package/app/composables/globals/useCustomFetch.ts +13 -0
- package/app/composables/globals/useDirectusField.ts +144 -0
- package/app/composables/globals/useDirectusForm.ts +70 -0
- package/app/composables/globals/useDirectusSchema.js +9 -0
- package/app/composables/globals/useFileManager.ts +76 -0
- package/app/composables/globals/useLivePreview.ts +17 -0
- package/app/composables/globals/useLoading.ts +23 -0
- package/app/composables/globals/useNavigation.js +19 -0
- package/app/composables/globals/useNotifications.ts +153 -0
- package/app/composables/globals/usePages.js +36 -0
- package/app/composables/globals/useRichText.ts +33 -0
- package/app/composables/globals/useServerRootMixin.ts +19 -0
- package/app/composables/globals/useVisualEditing.ts +38 -0
- package/app/composables/media/useFile.ts +6 -0
- package/app/composables/media/useMediaCenter.ts +353 -0
- package/app/composables/media/useVideojs.ts +45 -0
- package/app/composables/registry.ts +13 -0
- package/app/composables/types.ts +12 -0
- package/app/composables/useContent.ts +13 -0
- package/app/composables/useDirectusRequest.ts +32 -0
- package/app/stores/index.ts +0 -0
- package/app/types/api/global-search.ts +8 -0
- package/app/types/blocks/block-button-group.ts +7 -0
- package/app/types/blocks/block-button.ts +14 -0
- package/app/types/blocks/block-column.ts +20 -0
- package/app/types/blocks/block-cta.ts +10 -0
- package/app/types/blocks/block-divider.ts +4 -0
- package/app/types/blocks/block-faq.ts +12 -0
- package/app/types/blocks/block-form.ts +8 -0
- package/app/types/blocks/block-gallery.ts +14 -0
- package/app/types/blocks/block-hero.ts +12 -0
- package/app/types/blocks/block-html.ts +4 -0
- package/app/types/blocks/block-logocloud.ts +14 -0
- package/app/types/blocks/block-quote.ts +11 -0
- package/app/types/blocks/block-richtext.ts +7 -0
- package/app/types/blocks/block-steps.ts +22 -0
- package/app/types/blocks/block-team.ts +6 -0
- package/app/types/blocks/block-testimonial.ts +14 -0
- package/app/types/blocks/block-video.ts +10 -0
- package/app/types/blocks/block.ts +49 -0
- package/app/types/blocks/index.ts +18 -0
- package/app/types/componentMap.ts +15 -0
- package/app/types/content/category.ts +11 -0
- package/app/types/content/form.ts +20 -0
- package/app/types/content/index.ts +6 -0
- package/app/types/content/page.ts +76 -0
- package/app/types/content/post.ts +39 -0
- package/app/types/content/team.ts +16 -0
- package/app/types/content/testimonial.ts +19 -0
- package/app/types/directus.d.ts +47 -0
- package/app/types/env.d.ts +8 -0
- package/app/types/help/index.ts +53 -0
- package/app/types/index.d.ts +9 -0
- package/app/types/index.ts +7 -0
- package/app/types/meta/analytics.ts +18 -0
- package/app/types/meta/config.ts +21 -0
- package/app/types/meta/globals.ts +30 -0
- package/app/types/meta/index.ts +6 -0
- package/app/types/meta/navigation.ts +32 -0
- package/app/types/meta/redirect.ts +13 -0
- package/app/types/meta/seo.ts +19 -0
- package/app/types/os/contact.ts +23 -0
- package/app/types/os/conversation.ts +25 -0
- package/app/types/os/index.ts +16 -0
- package/app/types/os/organization.ts +54 -0
- package/app/types/os/os-activity.ts +28 -0
- package/app/types/os/os-deal.ts +45 -0
- package/app/types/os/os-expense.ts +22 -0
- package/app/types/os/os-invoice.ts +48 -0
- package/app/types/os/os-item.ts +18 -0
- package/app/types/os/os-payment.ts +29 -0
- package/app/types/os/os-project.ts +47 -0
- package/app/types/os/os-proposal.ts +84 -0
- package/app/types/os/os-settings.ts +19 -0
- package/app/types/os/os-subscription.ts +12 -0
- package/app/types/os/os-task.ts +34 -0
- package/app/types/os/os-tax-rate.ts +13 -0
- package/app/types/pageComponentMap.ts +8 -0
- package/app/types/schema.d.ts +39 -0
- package/app/types/schema.ts +151 -0
- package/app/types/system/file.ts +46 -0
- package/app/types/system/folder.ts +8 -0
- package/app/types/system/index.ts +4 -0
- package/app/types/system/role.ts +21 -0
- package/app/types/system/user.ts +56 -0
- package/app/utils/Timer.js +44 -0
- package/app/utils/billing-address.ts +24 -0
- package/app/utils/color.ts +14 -0
- package/app/utils/currency.ts +29 -0
- package/app/utils/embed.ts +57 -0
- package/app/utils/errors.ts +9 -0
- package/app/utils/fieldRegistry.js +89 -0
- package/app/utils/fonts.ts +24 -0
- package/app/utils/formkit.ts +75 -0
- package/app/utils/icons.ts +62 -0
- package/app/utils/index.js +0 -0
- package/app/utils/links.ts +28 -0
- package/app/utils/lodash.ts +33 -0
- package/app/utils/markdown.ts +9 -0
- package/app/utils/math.ts +25 -0
- package/app/utils/navigation.ts +11 -0
- package/app/utils/objects.ts +11 -0
- package/app/utils/paths.ts +21 -0
- package/app/utils/relations.ts +33 -0
- package/app/utils/strings.ts +113 -0
- package/app/utils/time.ts +148 -0
- package/app/utils/url.ts +22 -0
- package/app/utils/user-name.ts +21 -0
- package/app/utils/video/README.md +51 -0
- package/app/utils/video/playlist.js +0 -0
- package/dist/api/global-search.d.ts +8 -0
- package/dist/api/global-search.js +1 -0
- package/dist/blocks/block-button-group.d.ts +6 -0
- package/dist/blocks/block-button-group.js +1 -0
- package/dist/blocks/block-button.d.ts +13 -0
- package/dist/blocks/block-button.js +1 -0
- package/dist/blocks/block-column.d.ts +18 -0
- package/dist/blocks/block-column.js +1 -0
- package/dist/blocks/block-cta.d.ts +11 -0
- package/dist/blocks/block-cta.js +1 -0
- package/dist/blocks/block-divider.d.ts +4 -0
- package/dist/blocks/block-divider.js +1 -0
- package/dist/blocks/block-faq.d.ts +11 -0
- package/dist/blocks/block-faq.js +1 -0
- package/dist/blocks/block-form.d.ts +7 -0
- package/dist/blocks/block-form.js +1 -0
- package/dist/blocks/block-gallery.d.ts +13 -0
- package/dist/blocks/block-gallery.js +1 -0
- package/dist/blocks/block-hero.d.ts +11 -0
- package/dist/blocks/block-hero.js +1 -0
- package/dist/blocks/block-html.d.ts +4 -0
- package/dist/blocks/block-html.js +1 -0
- package/dist/blocks/block-logocloud.d.ts +13 -0
- package/dist/blocks/block-logocloud.js +1 -0
- package/dist/blocks/block-quote.d.ts +10 -0
- package/dist/blocks/block-quote.js +1 -0
- package/dist/blocks/block-richtext.d.ts +7 -0
- package/dist/blocks/block-richtext.js +1 -0
- package/dist/blocks/block-steps.d.ts +21 -0
- package/dist/blocks/block-steps.js +1 -0
- package/dist/blocks/block-team.d.ts +6 -0
- package/dist/blocks/block-team.js +1 -0
- package/dist/blocks/block-testimonial.d.ts +13 -0
- package/dist/blocks/block-testimonial.js +1 -0
- package/dist/blocks/block-video.d.ts +9 -0
- package/dist/blocks/block-video.js +1 -0
- package/dist/blocks/block.d.ts +17 -0
- package/dist/blocks/block.js +1 -0
- package/dist/blocks/index.d.ts +18 -0
- package/dist/blocks/index.js +1 -0
- package/dist/componentMap.d.ts +6 -0
- package/dist/componentMap.js +8 -0
- package/dist/content/category.d.ts +10 -0
- package/dist/content/category.js +1 -0
- package/dist/content/form.d.ts +21 -0
- package/dist/content/form.js +1 -0
- package/dist/content/index.d.ts +6 -0
- package/dist/content/index.js +1 -0
- package/dist/content/page.d.ts +38 -0
- package/dist/content/page.js +1 -0
- package/dist/content/post.d.ts +38 -0
- package/dist/content/post.js +1 -0
- package/dist/content/team.d.ts +17 -0
- package/dist/content/team.js +1 -0
- package/dist/content/testimonial.d.ts +18 -0
- package/dist/content/testimonial.js +1 -0
- package/dist/help/index.d.ts +51 -0
- package/dist/help/index.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/meta/analytics.d.ts +21 -0
- package/dist/meta/analytics.js +1 -0
- package/dist/meta/config.d.ts +22 -0
- package/dist/meta/config.js +1 -0
- package/dist/meta/globals.d.ts +33 -0
- package/dist/meta/globals.js +1 -0
- package/dist/meta/index.d.ts +6 -0
- package/dist/meta/index.js +1 -0
- package/dist/meta/navigation.d.ts +31 -0
- package/dist/meta/navigation.js +1 -0
- package/dist/meta/redirect.d.ts +12 -0
- package/dist/meta/redirect.js +1 -0
- package/dist/meta/seo.d.ts +19 -0
- package/dist/meta/seo.js +1 -0
- package/dist/os/contact.d.ts +22 -0
- package/dist/os/contact.js +1 -0
- package/dist/os/conversation.d.ts +23 -0
- package/dist/os/conversation.js +1 -0
- package/dist/os/index.d.ts +16 -0
- package/dist/os/index.js +1 -0
- package/dist/os/organization.d.ts +51 -0
- package/dist/os/organization.js +1 -0
- package/dist/os/os-activity.d.ts +26 -0
- package/dist/os/os-activity.js +1 -0
- package/dist/os/os-deal.d.ts +42 -0
- package/dist/os/os-deal.js +1 -0
- package/dist/os/os-expense.d.ts +21 -0
- package/dist/os/os-expense.js +1 -0
- package/dist/os/os-invoice.d.ts +46 -0
- package/dist/os/os-invoice.js +1 -0
- package/dist/os/os-item.d.ts +17 -0
- package/dist/os/os-item.js +1 -0
- package/dist/os/os-payment.d.ts +27 -0
- package/dist/os/os-payment.js +1 -0
- package/dist/os/os-project.d.ts +45 -0
- package/dist/os/os-project.js +1 -0
- package/dist/os/os-proposal.d.ts +61 -0
- package/dist/os/os-proposal.js +1 -0
- package/dist/os/os-settings.d.ts +17 -0
- package/dist/os/os-settings.js +1 -0
- package/dist/os/os-subscription.d.ts +12 -0
- package/dist/os/os-subscription.js +1 -0
- package/dist/os/os-task.d.ts +32 -0
- package/dist/os/os-task.js +1 -0
- package/dist/os/os-tax-rate.d.ts +12 -0
- package/dist/os/os-tax-rate.js +1 -0
- package/dist/pageComponentMap.d.ts +2 -0
- package/dist/pageComponentMap.js +7 -0
- package/dist/schema.d.ts +78 -0
- package/dist/schema.js +1 -0
- package/dist/system/file.d.ts +47 -0
- package/dist/system/file.js +1 -0
- package/dist/system/folder.d.ts +8 -0
- package/dist/system/folder.js +1 -0
- package/dist/system/index.d.ts +4 -0
- package/dist/system/index.js +1 -0
- package/dist/system/role.d.ts +20 -0
- package/dist/system/role.js +1 -0
- package/dist/system/user.d.ts +57 -0
- package/dist/system/user.js +1 -0
- package/nuxt.config.ts +5 -0
- package/package.json +42 -0
- 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,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,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,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,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>
|