diff --git a/assets/scss/main.scss b/assets/scss/main.scss
index 8924c9b..a9c8060 100644
--- a/assets/scss/main.scss
+++ b/assets/scss/main.scss
@@ -16,3 +16,6 @@
// transitions
@import "transitions/fade";
+
+// vendors
+@import "vendors/plyr";
diff --git a/assets/scss/vendors/_plyr.scss b/assets/scss/vendors/_plyr.scss
deleted file mode 100644
index fe7c0ca..0000000
--- a/assets/scss/vendors/_plyr.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-@import "plyr/src/sass/plyr";
-
-.plyr {
- --plyr-video-background: transparent;
- --plyr-video-controls-background: #{color(surface-overlay)};
- --plyr-video-progress-buffered-background: #{rgba(255, 255, 255, 0.3)};
- --plyr-range-fill-background: '#fff';
- --plyr-control-radius: 100%;
- --plyr-control-spacing: #{rem(32)};
- --plyr-video-control-background-hover: #{rgba(color(white), 0.05)};
- --plyr-video-control-color-hover: #{color(white)};
- --plyr-control-icon-size: 24px;
- --plyr-range-track-height: 3px;
-
- max-width: var(--plyr-root-max-width);
-
- @include media('>=vl') {
- --plyr-video-controls-padding: #{rem(16) rem(24)};
- --plyr-video-controls-margin: #{rem(16) rem(24)};
- }
-
- .plyr__controls {
- padding: var(--plyr-video-controls-padding, rem(10) rem(16));
- border-radius: var(--plyr-video-controls-border-radius, rem(2));
- margin: var(--plyr-video-controls-margin, rem(10) rem(16));
-
- input[type='range']::-webkit-slider-thumb {
- opacity: 0;
- }
-
- .plyr__controls__item {
- padding: 0;
- margin-left: rem(24);
-
- .plyr__control {
- padding: 0;
- }
-
- &.plyr__time--current,
- &.plyr__progress__container {
- margin-left: rem(16);
- }
-
- &:first-child {
- margin-left: 0;
- }
- }
- }
-
- .plyr__poster {
- background-size: cover;
- }
-}
diff --git a/components/molecules/VRoadizVideo/Background.stories.vue b/components/molecules/VRoadizVideo/Background.stories.vue
new file mode 100644
index 0000000..e2566c2
--- /dev/null
+++ b/components/molecules/VRoadizVideo/Background.stories.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VRoadizVideo/BackgroundCover.stories.vue b/components/molecules/VRoadizVideo/BackgroundCover.stories.vue
new file mode 100644
index 0000000..9b072e4
--- /dev/null
+++ b/components/molecules/VRoadizVideo/BackgroundCover.stories.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VRoadizVideo/ThumbnailInteraction.stories.vue b/components/molecules/VRoadizVideo/ThumbnailInteraction.stories.vue
new file mode 100644
index 0000000..c503c64
--- /dev/null
+++ b/components/molecules/VRoadizVideo/ThumbnailInteraction.stories.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VRoadizVideo/VRoadizVideo.vue b/components/molecules/VRoadizVideo/VRoadizVideo.vue
new file mode 100644
index 0000000..a825409
--- /dev/null
+++ b/components/molecules/VRoadizVideo/VRoadizVideo.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/Mp4.stories.vue b/components/molecules/VVideo/Mp4.stories.vue
new file mode 100644
index 0000000..675b940
--- /dev/null
+++ b/components/molecules/VVideo/Mp4.stories.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/Mp4Background.stories.vue b/components/molecules/VVideo/Mp4Background.stories.vue
new file mode 100644
index 0000000..9af9a5e
--- /dev/null
+++ b/components/molecules/VVideo/Mp4Background.stories.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/Mp4BackgroundCover.stories.vue b/components/molecules/VVideo/Mp4BackgroundCover.stories.vue
new file mode 100644
index 0000000..afd131a
--- /dev/null
+++ b/components/molecules/VVideo/Mp4BackgroundCover.stories.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/VVideo.vue b/components/molecules/VVideo/VVideo.vue
new file mode 100644
index 0000000..00355e7
--- /dev/null
+++ b/components/molecules/VVideo/VVideo.vue
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/VideoBackgroundCover.stories.vue b/components/molecules/VVideo/VideoBackgroundCover.stories.vue
new file mode 100644
index 0000000..12ed231
--- /dev/null
+++ b/components/molecules/VVideo/VideoBackgroundCover.stories.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/Vimeo.stories.vue b/components/molecules/VVideo/Vimeo.stories.vue
new file mode 100644
index 0000000..04fa142
--- /dev/null
+++ b/components/molecules/VVideo/Vimeo.stories.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/VimeoBackgroundCover.stories.vue b/components/molecules/VVideo/VimeoBackgroundCover.stories.vue
new file mode 100644
index 0000000..62b4dc0
--- /dev/null
+++ b/components/molecules/VVideo/VimeoBackgroundCover.stories.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/Youtube.stories.vue b/components/molecules/VVideo/Youtube.stories.vue
new file mode 100644
index 0000000..9596804
--- /dev/null
+++ b/components/molecules/VVideo/Youtube.stories.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/molecules/VVideo/YoutubeBackgroundCover.stories.vue b/components/molecules/VVideo/YoutubeBackgroundCover.stories.vue
new file mode 100644
index 0000000..b65ac94
--- /dev/null
+++ b/components/molecules/VVideo/YoutubeBackgroundCover.stories.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index 507e7e1..6d87afe 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,6 @@
"@vueuse/core": "^10.10.0",
"lodash": "^4.17.21",
"marked": "^12.0.2",
- "plyr": "^3.7.8",
"tiny-emitter": "^2.1.0",
"vue-multiselect": "^3.0.0"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d4b1d8b..7ebe161 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,9 +32,6 @@ importers:
marked:
specifier: ^12.0.2
version: 12.0.2
- plyr:
- specifier: ^3.7.8
- version: 3.7.8
tiny-emitter:
specifier: ^2.1.0
version: 2.1.0
@@ -2298,9 +2295,6 @@ packages:
core-js-compat@3.37.1:
resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
- core-js@3.37.1:
- resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==}
-
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@@ -2403,9 +2397,6 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
- custom-event-polyfill@1.0.7:
- resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==}
-
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
@@ -3423,9 +3414,6 @@ packages:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
engines: {node: '>=6.11.5'}
- loadjs@4.3.0:
- resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==}
-
local-pkg@0.4.3:
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
engines: {node: '>=14'}
@@ -4000,9 +3988,6 @@ packages:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
- plyr@3.7.8:
- resolution: {integrity: sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA==}
-
postcss-calc@10.0.0:
resolution: {integrity: sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==}
engines: {node: ^18.12 || ^20.9 || >=22.0}
@@ -4284,9 +4269,6 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
- rangetouch@2.0.1:
- resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==}
-
rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
@@ -5071,9 +5053,6 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- url-polyfill@1.1.12:
- resolution: {integrity: sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==}
-
urlpattern-polyfill@8.0.2:
resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==}
@@ -8314,8 +8293,6 @@ snapshots:
dependencies:
browserslist: 4.23.1
- core-js@3.37.1: {}
-
core-util-is@1.0.3: {}
cosmiconfig@9.0.0(typescript@5.4.5):
@@ -8429,8 +8406,6 @@ snapshots:
csstype@3.1.3: {}
- custom-event-polyfill@1.0.7: {}
-
data-uri-to-buffer@4.0.1: {}
db0@0.1.4: {}
@@ -9552,8 +9527,6 @@ snapshots:
loader-runner@4.3.0: {}
- loadjs@4.3.0: {}
-
local-pkg@0.4.3: {}
local-pkg@0.5.0:
@@ -10359,14 +10332,6 @@ snapshots:
pluralize@8.0.0: {}
- plyr@3.7.8:
- dependencies:
- core-js: 3.37.1
- custom-event-polyfill: 1.0.7
- loadjs: 4.3.0
- rangetouch: 2.0.1
- url-polyfill: 1.1.12
-
postcss-calc@10.0.0(postcss@8.4.38):
dependencies:
postcss: 8.4.38
@@ -10618,8 +10583,6 @@ snapshots:
range-parser@1.2.1: {}
- rangetouch@2.0.1: {}
-
rc9@2.1.2:
dependencies:
defu: 6.1.4
@@ -11566,8 +11529,6 @@ snapshots:
dependencies:
punycode: 2.3.1
- url-polyfill@1.1.12: {}
-
urlpattern-polyfill@8.0.2: {}
util-deprecate@1.0.2: {}
diff --git a/utils/embed.ts b/utils/embed.ts
new file mode 100644
index 0000000..296bfda
--- /dev/null
+++ b/utils/embed.ts
@@ -0,0 +1,37 @@
+import { encodeUrlParams } from '~/utils/url'
+
+export function getEmbedSrc(id: string, platform: string, params: Record = {}): string | undefined {
+ if (platform === 'vimeo') return getVimeoEmbedSrc(id, params)
+ if (platform === 'youtube') return getYouTubeEmbedSrc(id, params)
+ if (platform === 'deezer') return getDeezerEmbedSrc(id, params)
+ if (platform === 'spotify') return getSpotifyEmbedSrc(id, params)
+ if (platform === 'soundcloud') return getSoundcloudEmbedSrc(id, params)
+}
+
+export function getVimeoEmbedSrc(id: string, params: Record = {}): string {
+ return getEmbedUrl(`https://player.vimeo.com/video/${id}`, params)
+}
+
+export function getYouTubeEmbedSrc(id: string, params: Record = {}): string {
+ return getEmbedUrl(`https://www.youtube-nocookie.com/embed/${id}`, params)
+}
+
+export function getDeezerEmbedSrc(id: string, params: Record = {}): string {
+ return getEmbedUrl(`https://widget.deezer.com/widget/light/${id}`, params)
+}
+
+export function getSpotifyEmbedSrc(id: string, params: Record = {}): string {
+ return getEmbedUrl(`https://open.spotify.com/embed/${id}`, params)
+}
+
+export function getSoundcloudEmbedSrc(id: string, params: Record = {}): string {
+ return getEmbedUrl(`https://w.soundcloud.com/player/?url=${id}`, params)
+}
+
+export function getEmbedUrl(url: string, params: Record = {}) {
+ const encodedParams = encodeUrlParams(params)
+
+ if (encodedParams.length) url += '?' + encodedParams
+
+ return url
+}
diff --git a/utils/video/video-attributes.ts b/utils/video/video-attributes.ts
new file mode 100644
index 0000000..685470e
--- /dev/null
+++ b/utils/video/video-attributes.ts
@@ -0,0 +1,9 @@
+export function getVideoAttrsValues(props: Record, hasBackgroundProp: boolean) {
+ return {
+ playsinline: props.playsinline || hasBackgroundProp,
+ muted: !!props.muted || hasBackgroundProp,
+ loop: !!props.loop || hasBackgroundProp,
+ autoplay: !!props.autoplay || hasBackgroundProp,
+ controls: props.controls && !hasBackgroundProp,
+ }
+}
diff --git a/utils/video/video-props.ts b/utils/video/video-props.ts
new file mode 100644
index 0000000..7032e7b
--- /dev/null
+++ b/utils/video/video-props.ts
@@ -0,0 +1,34 @@
+import type { PropType } from 'vue'
+
+export const commonVideoProps = {
+ background: Boolean,
+ fit: { type: String as PropType<'cover' | 'contain'> },
+}
+
+export const videoAttributes = {
+ width: { type: [Number, String] },
+ height: { type: [Number, String] },
+ autoplay: Boolean,
+ controls: { type: Boolean, default: true },
+ playsinline: { type: Boolean, default: true },
+ loop: { type: Boolean, default: undefined },
+ muted: { type: Boolean, default: undefined },
+}
+
+export const videoFile = {
+ relativePath: { type: String, required: true },
+ mimeType: { type: String },
+}
+
+export const videoSrc = {
+ src: { type: String },
+ mimeType: { type: String },
+ altSources: { type: Array as PropType<(typeof videoFile)[]> },
+}
+
+export const embedVideoProps = {
+ embedPlatform: { type: String as PropType<'youtube' | 'vimeo'> },
+ embedId: { type: String },
+ youtube: { type: Object as PropType> },
+ vimeo: { type: Object as PropType> },
+}