@rxdrag/website-lib-core 0.0.127 → 0.0.128
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 +1 -1
- package/index.ts +1 -1
- package/package.json +4 -4
- package/src/astro/animation.ts +146 -146
- package/src/astro/background.ts +82 -86
- package/src/astro/base.ts +7 -0
- package/src/astro/business.ts +13 -0
- package/src/astro/grid/consts.ts +80 -80
- package/src/astro/grid/index.ts +2 -2
- package/src/astro/grid/types.ts +35 -35
- package/src/astro/image.ts +239 -239
- package/src/astro/index.ts +12 -10
- package/src/astro/link.ts +20 -21
- package/src/astro/media.ts +123 -123
- package/src/astro/nav.ts +13 -13
- package/src/astro/section/index.ts +7 -12
- package/src/component-logic/index.ts +1 -1
- package/src/component-logic/link-client.ts +32 -32
- package/src/component-logic/link.ts +61 -61
- package/src/design-tokens.ts +160 -160
- package/src/entify/Entify.ts +533 -530
- package/src/entify/IEntify.ts +151 -177
- package/src/entify/index.ts +4 -4
- package/src/entify/lib/collectCategoryIds.ts +20 -20
- package/src/entify/lib/fulltextSearch.ts +63 -62
- package/src/entify/lib/langFields.ts +14 -14
- package/src/entify/lib/listToTree.ts +23 -23
- package/src/entify/lib/newAvatarQueryOptions.ts +4 -4
- package/src/entify/lib/newOgImageQueryOptions.ts +5 -5
- package/src/entify/lib/newQueryPostOptions.ts +42 -45
- package/src/entify/lib/newQueryProductOptions.ts +96 -98
- package/src/entify/lib/newQueryProductsMediaOptions.ts +28 -28
- package/src/entify/lib/queryAllProducts.ts +40 -40
- package/src/entify/lib/queryBulletin.ts +28 -16
- package/src/entify/lib/queryEntityList.ts +41 -41
- package/src/entify/lib/queryFeaturedProducts.ts +69 -68
- package/src/entify/lib/queryLangs.ts +36 -60
- package/src/entify/lib/queryLatestPosts.ts +92 -91
- package/src/entify/lib/queryOneEntity.ts +63 -63
- package/src/entify/lib/queryOneMedia.ts +27 -27
- package/src/entify/lib/queryOnePostById.ts +21 -21
- package/src/entify/lib/queryOnePostBySlug.ts +40 -39
- package/src/entify/lib/queryOnePostCategoryBySlug.ts +35 -35
- package/src/entify/lib/queryOneProductById.ts +20 -20
- package/src/entify/lib/queryOneProductBySlug.ts +53 -52
- package/src/entify/lib/queryOneProductCategoryBySlug.ts +50 -49
- package/src/entify/lib/queryOneTheme.ts +54 -72
- package/src/entify/lib/queryOneUser.ts +38 -38
- package/src/entify/lib/queryPostCategories.ts +67 -67
- package/src/entify/lib/queryPostSlugs.ts +37 -37
- package/src/entify/lib/queryPosts.ts +175 -174
- package/src/entify/lib/queryProductCategories.ts +59 -59
- package/src/entify/lib/queryProducts.ts +145 -144
- package/src/entify/lib/queryProductsInMenu.ts +45 -45
- package/src/entify/lib/queryTagCategories.ts +58 -58
- package/src/entify/lib/queryTags.ts +57 -57
- package/src/entify/lib/queryUserIds.ts +24 -24
- package/src/entify/lib/queryUserPosts.ts +80 -79
- package/src/entify/lib/queryWebSiteSettings.ts +28 -28
- package/src/entify/lib/queryWebsite.ts +43 -43
- package/src/entify/lib/sendEmail.ts +7 -7
- package/src/entify/lib/toQueryOptions.ts +19 -19
- package/src/entify/lib/upsertEntity.ts +8 -8
- package/src/entify/types/index.ts +1 -1
- package/src/entify/types/utils.ts +11 -6
- package/src/entify/types/variables.ts +5 -6
- package/src/entify/view-model/funcs.ts +230 -230
- package/src/entify/view-model/index.ts +1 -1
- package/src/entify/view-model/models.ts +135 -135
- package/src/global.d.ts +7 -7
- package/src/index.ts +8 -8
- package/src/lib/formatDate.ts +15 -15
- package/src/lib/index.ts +3 -3
- package/src/lib/pagination.ts +114 -114
- package/src/lib/utils.ts +135 -135
- package/src/react/components/Analytics/eventHandlers.ts +173 -173
- package/src/react/components/Analytics/index.tsx +21 -21
- package/src/react/components/Analytics/singleton.ts +214 -214
- package/src/react/components/Analytics/tracking.ts +221 -221
- package/src/react/components/Analytics/types.ts +60 -60
- package/src/react/components/Analytics/utils.ts +95 -95
- package/src/react/components/AttachmentIcon/index.tsx +53 -53
- package/src/react/components/BackgroundHlsVideoPlayer.tsx +97 -97
- package/src/react/components/BackgroundVideoPlayer.tsx +32 -32
- package/src/react/components/Bulletin.tsx +30 -30
- package/src/react/components/ContactForm/ContactForm.tsx +289 -289
- package/src/react/components/ContactForm/Input.tsx +48 -48
- package/src/react/components/ContactForm/Input2.tsx +59 -59
- package/src/react/components/ContactForm/Submit.tsx +48 -48
- package/src/react/components/ContactForm/TelInput.tsx +215 -215
- package/src/react/components/ContactForm/Textarea.tsx +48 -48
- package/src/react/components/ContactForm/Textarea2.tsx +89 -89
- package/src/react/components/ContactForm/funcs.ts +64 -64
- package/src/react/components/ContactForm/index.ts +7 -7
- package/src/react/components/ContactForm/types.ts +68 -68
- package/src/react/components/GoogleConsent/CookieItemPanel.tsx +80 -80
- package/src/react/components/GoogleConsent/CumtomizedModal.tsx +148 -148
- package/src/react/components/GoogleConsent/GoogleConsent.tsx +100 -100
- package/src/react/components/GoogleConsent/gtags.ts +67 -67
- package/src/react/components/GoogleConsent/index.ts +2 -2
- package/src/react/components/GoogleConsent/types.ts +18 -18
- package/src/react/components/GoogleConsent//345/217/202/350/200/203.md +4 -4
- package/src/react/components/Icon/index.tsx +19 -19
- package/src/react/components/Medias/MainMedia.tsx +257 -257
- package/src/react/components/Medias/Thumbnail.tsx +62 -62
- package/src/react/components/Medias/VideoPlayer.tsx +114 -114
- package/src/react/components/Medias/index.tsx +271 -271
- package/src/react/components/ProductCard/ProductCard.tsx +24 -24
- package/src/react/components/ProductCard/ProductCta/index.tsx +28 -28
- package/src/react/components/ProductCard/ProductCta/style.css +3 -3
- package/src/react/components/ProductCard/ProductDescription/index.tsx +12 -12
- package/src/react/components/ProductCard/ProductDescription/style.css +5 -5
- package/src/react/components/ProductCard/ProductMedia/index.tsx +35 -35
- package/src/react/components/ProductCard/ProductMedia/style.css +5 -5
- package/src/react/components/ProductCard/ProductTitle/index.tsx +7 -7
- package/src/react/components/ProductCard/ProductTitle/style.css +3 -3
- package/src/react/components/ProductCard/ProductView.tsx +36 -36
- package/src/react/components/ProductCard/index.ts +4 -4
- package/src/react/components/ProductCard/useQueryProduct.ts +32 -32
- package/src/react/components/ReactModalTrigger.tsx +28 -28
- package/src/react/components/ReactVideoPlayer.tsx +29 -52
- package/src/react/components/RichTextOutline/index.tsx +75 -75
- package/src/react/components/RichTextOutline/useAnchorScroll.ts +23 -23
- package/src/react/components/Scroller.tsx +39 -39
- package/src/react/components/SearchInput.tsx +21 -21
- package/src/react/components/Share/index.tsx +86 -86
- package/src/react/components/Share/socials.tsx +79 -77
- package/src/react/components/Share//350/265/204/346/226/231.md +7 -7
- package/src/react/components/ToTop.tsx +72 -72
- package/src/react/components/VideoPlayIcon.tsx +43 -0
- package/src/react/components/all.ts +38 -38
- package/src/react/components/index.ts +16 -16
- package/src/robots.ts +4 -4
- package/src/entify/lib/newPageMetaOptions.ts +0 -18
- package/src/entify/lib/newQueryPageOptions.ts +0 -14
- package/src/entify/lib/queryOneIcon.ts +0 -27
- package/src/entify/lib/queryPageBySlug.ts +0 -43
- package/src/entify/lib/queryPageByType.ts +0 -44
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import clsx from "clsx"
|
|
2
|
-
import { HTMLAttributes } from "react"
|
|
3
|
-
import "./style.css"
|
|
4
|
-
|
|
5
|
-
export function ProductDescription(props: HTMLAttributes<HTMLParagraphElement>) {
|
|
6
|
-
const { className, ...rest } = props
|
|
7
|
-
return (
|
|
8
|
-
<p
|
|
9
|
-
className={clsx("product-description", className)}
|
|
10
|
-
{...rest}
|
|
11
|
-
/>
|
|
12
|
-
)
|
|
1
|
+
import clsx from "clsx"
|
|
2
|
+
import { HTMLAttributes } from "react"
|
|
3
|
+
import "./style.css"
|
|
4
|
+
|
|
5
|
+
export function ProductDescription(props: HTMLAttributes<HTMLParagraphElement>) {
|
|
6
|
+
const { className, ...rest } = props
|
|
7
|
+
return (
|
|
8
|
+
<p
|
|
9
|
+
className={clsx("product-description", className)}
|
|
10
|
+
{...rest}
|
|
11
|
+
/>
|
|
12
|
+
)
|
|
13
13
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
.x-figure .product-description {
|
|
2
|
-
margin-top: 0;
|
|
3
|
-
margin-bottom: 0;
|
|
4
|
-
padding-top: 0;
|
|
5
|
-
padding-bottom: 0.4rem;
|
|
1
|
+
.x-figure .product-description {
|
|
2
|
+
margin-top: 0;
|
|
3
|
+
margin-bottom: 0;
|
|
4
|
+
padding-top: 0;
|
|
5
|
+
padding-bottom: 0.4rem;
|
|
6
6
|
}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { HtmlHTMLAttributes } from "react";
|
|
2
|
-
import "./style.css";
|
|
3
|
-
import { TProduct } from "../../../../entify";
|
|
4
|
-
|
|
5
|
-
export function ProductMedia(
|
|
6
|
-
props: {
|
|
7
|
-
product?: TProduct;
|
|
8
|
-
width?: number;
|
|
9
|
-
aspect?: string;
|
|
10
|
-
} & HtmlHTMLAttributes<HTMLImageElement>
|
|
11
|
-
) {
|
|
12
|
-
const { product, style, width, className, aspect, ...rest } = props;
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div
|
|
16
|
-
className={"product-media " + className}
|
|
17
|
-
style={{
|
|
18
|
-
aspectRatio: aspect || "4 / 3",
|
|
19
|
-
borderRadius: "1rem",
|
|
20
|
-
overflow: "hidden",
|
|
21
|
-
...style,
|
|
22
|
-
width,
|
|
23
|
-
}}
|
|
24
|
-
{...rest}
|
|
25
|
-
>
|
|
26
|
-
<img
|
|
27
|
-
style={{
|
|
28
|
-
width: "100%",
|
|
29
|
-
}}
|
|
30
|
-
src={product?.coverUrl}
|
|
31
|
-
alt={product?.title}
|
|
32
|
-
/>
|
|
33
|
-
</div>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
1
|
+
import { HtmlHTMLAttributes } from "react";
|
|
2
|
+
import "./style.css";
|
|
3
|
+
import { TProduct } from "../../../../entify";
|
|
4
|
+
|
|
5
|
+
export function ProductMedia(
|
|
6
|
+
props: {
|
|
7
|
+
product?: TProduct;
|
|
8
|
+
width?: number;
|
|
9
|
+
aspect?: string;
|
|
10
|
+
} & HtmlHTMLAttributes<HTMLImageElement>
|
|
11
|
+
) {
|
|
12
|
+
const { product, style, width, className, aspect, ...rest } = props;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={"product-media " + className}
|
|
17
|
+
style={{
|
|
18
|
+
aspectRatio: aspect || "4 / 3",
|
|
19
|
+
borderRadius: "1rem",
|
|
20
|
+
overflow: "hidden",
|
|
21
|
+
...style,
|
|
22
|
+
width,
|
|
23
|
+
}}
|
|
24
|
+
{...rest}
|
|
25
|
+
>
|
|
26
|
+
<img
|
|
27
|
+
style={{
|
|
28
|
+
width: "100%",
|
|
29
|
+
}}
|
|
30
|
+
src={product?.coverUrl}
|
|
31
|
+
alt={product?.title}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
.x-figure .product-media {
|
|
2
|
-
overflow: hidden;
|
|
3
|
-
display: flex;
|
|
4
|
-
align-items: center;
|
|
5
|
-
justify-content: center;
|
|
1
|
+
.x-figure .product-media {
|
|
2
|
+
overflow: hidden;
|
|
3
|
+
display: flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
6
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { HTMLAttributes } from "react";
|
|
2
|
-
import "./style.css";
|
|
3
|
-
|
|
4
|
-
export function ProductTitle(props: HTMLAttributes<HTMLHeadingElement>) {
|
|
5
|
-
const { className, ...rest } = props;
|
|
6
|
-
return <h3 className={"product-title " + className} {...rest} />;
|
|
7
|
-
}
|
|
1
|
+
import { HTMLAttributes } from "react";
|
|
2
|
+
import "./style.css";
|
|
3
|
+
|
|
4
|
+
export function ProductTitle(props: HTMLAttributes<HTMLHeadingElement>) {
|
|
5
|
+
const { className, ...rest } = props;
|
|
6
|
+
return <h3 className={"product-title " + className} {...rest} />;
|
|
7
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.x-figure .product-title{
|
|
2
|
-
margin-top: 0.6rem;
|
|
3
|
-
padding-top: 0;
|
|
1
|
+
.x-figure .product-title{
|
|
2
|
+
margin-top: 0.6rem;
|
|
3
|
+
padding-top: 0;
|
|
4
4
|
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { ProductCardProps } from "./ProductCard";
|
|
2
|
-
import { Figcaption, Figure, FigureContent } from "@rxdrag/tiptap-preview";
|
|
3
|
-
import { ProductMedia } from "./ProductMedia";
|
|
4
|
-
import { ProductTitle } from "./ProductTitle";
|
|
5
|
-
import { ProductDescription } from "./ProductDescription";
|
|
6
|
-
import { ProductCta } from "./ProductCta";
|
|
7
|
-
import { TProduct } from "../../../entify";
|
|
8
|
-
|
|
9
|
-
export function ProductView(
|
|
10
|
-
props: {
|
|
11
|
-
product?: TProduct;
|
|
12
|
-
ctaTitle?: string;
|
|
13
|
-
} & ProductCardProps
|
|
14
|
-
) {
|
|
15
|
-
const { product, ctaTitle, node } = props;
|
|
16
|
-
|
|
17
|
-
const title = node?.title || product?.title;
|
|
18
|
-
const description = node?.description || product?.description;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Figure>
|
|
22
|
-
<FigureContent width={node?.width} align={node?.align}>
|
|
23
|
-
<a href={`/products/${product?.slug}`}>
|
|
24
|
-
<ProductMedia product={product} aspect={node?.aspect} />
|
|
25
|
-
</a>
|
|
26
|
-
</FigureContent>
|
|
27
|
-
<Figcaption width={node?.width} align={node?.align}>
|
|
28
|
-
<a href={`/products/${product?.slug}`}>
|
|
29
|
-
<ProductTitle>{title}</ProductTitle>
|
|
30
|
-
</a>
|
|
31
|
-
<ProductDescription>{description}</ProductDescription>
|
|
32
|
-
<ProductCta product={product}>{ctaTitle}</ProductCta>
|
|
33
|
-
</Figcaption>
|
|
34
|
-
</Figure>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
1
|
+
import { ProductCardProps } from "./ProductCard";
|
|
2
|
+
import { Figcaption, Figure, FigureContent } from "@rxdrag/tiptap-preview";
|
|
3
|
+
import { ProductMedia } from "./ProductMedia";
|
|
4
|
+
import { ProductTitle } from "./ProductTitle";
|
|
5
|
+
import { ProductDescription } from "./ProductDescription";
|
|
6
|
+
import { ProductCta } from "./ProductCta";
|
|
7
|
+
import { TProduct } from "../../../entify";
|
|
8
|
+
|
|
9
|
+
export function ProductView(
|
|
10
|
+
props: {
|
|
11
|
+
product?: TProduct;
|
|
12
|
+
ctaTitle?: string;
|
|
13
|
+
} & ProductCardProps
|
|
14
|
+
) {
|
|
15
|
+
const { product, ctaTitle, node } = props;
|
|
16
|
+
|
|
17
|
+
const title = node?.title || product?.title;
|
|
18
|
+
const description = node?.description || product?.description;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Figure>
|
|
22
|
+
<FigureContent width={node?.width} align={node?.align}>
|
|
23
|
+
<a href={`/products/${product?.slug}`}>
|
|
24
|
+
<ProductMedia product={product} aspect={node?.aspect} />
|
|
25
|
+
</a>
|
|
26
|
+
</FigureContent>
|
|
27
|
+
<Figcaption width={node?.width} align={node?.align}>
|
|
28
|
+
<a href={`/products/${product?.slug}`}>
|
|
29
|
+
<ProductTitle>{title}</ProductTitle>
|
|
30
|
+
</a>
|
|
31
|
+
<ProductDescription>{description}</ProductDescription>
|
|
32
|
+
<ProductCta product={product}>{ctaTitle}</ProductCta>
|
|
33
|
+
</Figcaption>
|
|
34
|
+
</Figure>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from "./ProductCta";
|
|
2
|
-
export * from "./ProductTitle";
|
|
3
|
-
export * from "./ProductDescription";
|
|
4
|
-
export * from "./ProductCard";
|
|
1
|
+
export * from "./ProductCta";
|
|
2
|
+
export * from "./ProductTitle";
|
|
3
|
+
export * from "./ProductDescription";
|
|
4
|
+
export * from "./ProductCard";
|
|
5
5
|
export * from "./ProductMedia";
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
import { Product } from "@rxdrag/rxcms-models";
|
|
3
|
-
|
|
4
|
-
export function useQueryProduct(productId?: string, websiteId?: string) {
|
|
5
|
-
const active = useRef<boolean>(false);
|
|
6
|
-
const [product, setProduct] = useState<Product | undefined>({});
|
|
7
|
-
|
|
8
|
-
const url = websiteId
|
|
9
|
-
? `/${websiteId}/api/product/${productId}`
|
|
10
|
-
: `/api/product/${productId}`;
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
if (active.current && !productId) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fetch(url)
|
|
18
|
-
.then((res) => res.json())
|
|
19
|
-
.then((data) => {
|
|
20
|
-
setProduct(data);
|
|
21
|
-
active.current = true;
|
|
22
|
-
});
|
|
23
|
-
}, [productId]);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
return () => {
|
|
27
|
-
active.current = false;
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return product;
|
|
32
|
-
}
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Product } from "@rxdrag/rxcms-models";
|
|
3
|
+
|
|
4
|
+
export function useQueryProduct(productId?: string, websiteId?: string) {
|
|
5
|
+
const active = useRef<boolean>(false);
|
|
6
|
+
const [product, setProduct] = useState<Product | undefined>({});
|
|
7
|
+
|
|
8
|
+
const url = websiteId
|
|
9
|
+
? `/${websiteId}/api/product/${productId}`
|
|
10
|
+
: `/api/product/${productId}`;
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (active.current && !productId) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fetch(url)
|
|
18
|
+
.then((res) => res.json())
|
|
19
|
+
.then((data) => {
|
|
20
|
+
setProduct(data);
|
|
21
|
+
active.current = true;
|
|
22
|
+
});
|
|
23
|
+
}, [productId]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
return () => {
|
|
27
|
+
active.current = false;
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return product;
|
|
32
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
export function ReactModalTrigger(props: {
|
|
4
|
-
className?: string;
|
|
5
|
-
openableKey?: string;
|
|
6
|
-
callToAction?: string;
|
|
7
|
-
children?: ReactNode;
|
|
8
|
-
}) {
|
|
9
|
-
const {
|
|
10
|
-
className,
|
|
11
|
-
callToAction,
|
|
12
|
-
openableKey = "enquiry-modal",
|
|
13
|
-
children,
|
|
14
|
-
} = props;
|
|
15
|
-
const roleProps = {
|
|
16
|
-
"data-modal-open": openableKey,
|
|
17
|
-
"data-call-to-action": callToAction,
|
|
18
|
-
};
|
|
19
|
-
return (
|
|
20
|
-
<button
|
|
21
|
-
{...roleProps}
|
|
22
|
-
className={className}
|
|
23
|
-
type="button"
|
|
24
|
-
>
|
|
25
|
-
{children || "Get a quote"}
|
|
26
|
-
</button>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export function ReactModalTrigger(props: {
|
|
4
|
+
className?: string;
|
|
5
|
+
openableKey?: string;
|
|
6
|
+
callToAction?: string;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}) {
|
|
9
|
+
const {
|
|
10
|
+
className,
|
|
11
|
+
callToAction,
|
|
12
|
+
openableKey = "enquiry-modal",
|
|
13
|
+
children,
|
|
14
|
+
} = props;
|
|
15
|
+
const roleProps = {
|
|
16
|
+
"data-modal-open": openableKey,
|
|
17
|
+
"data-call-to-action": callToAction,
|
|
18
|
+
};
|
|
19
|
+
return (
|
|
20
|
+
<button
|
|
21
|
+
{...roleProps}
|
|
22
|
+
className={className}
|
|
23
|
+
type="button"
|
|
24
|
+
>
|
|
25
|
+
{children || "Get a quote"}
|
|
26
|
+
</button>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -9,11 +9,13 @@ import Hls from "hls.js";
|
|
|
9
9
|
import type { Media } from "@rxdrag/rxcms-models";
|
|
10
10
|
import { ReactModalTrigger } from "./ReactModalTrigger";
|
|
11
11
|
import clsx from "clsx";
|
|
12
|
+
import { VideoPlayIcon } from "./VideoPlayIcon";
|
|
12
13
|
|
|
13
14
|
export type ID = string | number;
|
|
14
15
|
|
|
15
16
|
export type VideoPlayerClassNames = {
|
|
16
17
|
container?: string;
|
|
18
|
+
aspect?: string;
|
|
17
19
|
video?: string;
|
|
18
20
|
playButton?: string;
|
|
19
21
|
playButtonOuter?: string;
|
|
@@ -31,13 +33,15 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
31
33
|
HTMLDivElement,
|
|
32
34
|
{
|
|
33
35
|
endTitle?: string;
|
|
34
|
-
media
|
|
36
|
+
media?: Media;
|
|
35
37
|
posterUrl?: string;
|
|
36
38
|
eagerLoad?: boolean;
|
|
37
39
|
classNames?: VideoPlayerClassNames;
|
|
38
40
|
callToAction?: string;
|
|
39
41
|
onToggleSelect?: (id: ID) => void;
|
|
40
42
|
designMode?: boolean;
|
|
43
|
+
children?: React.ReactNode;
|
|
44
|
+
|
|
41
45
|
}
|
|
42
46
|
>((props, ref) => {
|
|
43
47
|
const {
|
|
@@ -49,6 +53,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
49
53
|
classNames,
|
|
50
54
|
callToAction,
|
|
51
55
|
designMode,
|
|
56
|
+
children,
|
|
52
57
|
...rest
|
|
53
58
|
} = props;
|
|
54
59
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
@@ -63,11 +68,11 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
63
68
|
|
|
64
69
|
const ensureSourceReady = useCallback(() => {
|
|
65
70
|
const video = videoRef.current;
|
|
66
|
-
const url = media
|
|
71
|
+
const url = media?.file?.original;
|
|
67
72
|
if (!video || !url) return false;
|
|
68
73
|
if (sourceReadyRef.current) return true;
|
|
69
74
|
|
|
70
|
-
if (media
|
|
75
|
+
if (media?.storageType === "cloudflare_stream") {
|
|
71
76
|
if (!Hls.isSupported()) {
|
|
72
77
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
73
78
|
video.src = url;
|
|
@@ -106,7 +111,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
106
111
|
sourceReadyRef.current = true;
|
|
107
112
|
setIsSourceReady(true);
|
|
108
113
|
return true;
|
|
109
|
-
}, [media
|
|
114
|
+
}, [media?.file?.original, media?.storageType]);
|
|
110
115
|
|
|
111
116
|
const handleContainerClick = useCallback(
|
|
112
117
|
(e: React.MouseEvent) => {
|
|
@@ -116,11 +121,11 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
116
121
|
videoRef.current.pause();
|
|
117
122
|
return;
|
|
118
123
|
}
|
|
119
|
-
if (media
|
|
124
|
+
if (media?.id != null) {
|
|
120
125
|
onToggleSelect?.(media.id);
|
|
121
126
|
}
|
|
122
127
|
},
|
|
123
|
-
[isPlaying, media
|
|
128
|
+
[isPlaying, media?.id, onToggleSelect, designMode],
|
|
124
129
|
);
|
|
125
130
|
|
|
126
131
|
const handlePlayClick = useCallback(
|
|
@@ -144,7 +149,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
144
149
|
setIsLoading(false);
|
|
145
150
|
});
|
|
146
151
|
},
|
|
147
|
-
[isPlaying, designMode, ensureSourceReady]
|
|
152
|
+
[isPlaying, designMode, ensureSourceReady],
|
|
148
153
|
);
|
|
149
154
|
|
|
150
155
|
useEffect(() => {
|
|
@@ -206,14 +211,15 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
206
211
|
hlsRef.current = null;
|
|
207
212
|
}
|
|
208
213
|
};
|
|
209
|
-
}, [eagerLoad, ensureSourceReady, media
|
|
214
|
+
}, [eagerLoad, ensureSourceReady, media?.file?.original, media?.storageType]);
|
|
210
215
|
|
|
211
216
|
return (
|
|
212
217
|
<div
|
|
213
218
|
ref={ref}
|
|
214
219
|
className={clsx(
|
|
215
|
-
"relative w-full
|
|
216
|
-
classNames?.
|
|
220
|
+
"relative w-full rounded-2xl",
|
|
221
|
+
classNames?.aspect ?? "aspect-video",
|
|
222
|
+
classNames?.container,
|
|
217
223
|
)}
|
|
218
224
|
onClick={handleContainerClick}
|
|
219
225
|
{...rest}
|
|
@@ -222,64 +228,35 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
222
228
|
ref={videoRef}
|
|
223
229
|
onClick={(e) => !designMode && e.stopPropagation()}
|
|
224
230
|
preload={eagerLoad ? "metadata" : "none"}
|
|
225
|
-
poster={posterUrl ?? media
|
|
231
|
+
poster={posterUrl ?? media?.file?.thumbnail}
|
|
226
232
|
className={clsx(
|
|
227
233
|
"w-full h-full rounded-lg object-cover",
|
|
228
|
-
classNames?.video
|
|
234
|
+
classNames?.video,
|
|
229
235
|
)}
|
|
230
236
|
playsInline
|
|
231
237
|
controls={!designMode && hasStarted}
|
|
232
238
|
disablePictureInPicture
|
|
233
239
|
controlsList="nodownload noremoteplayback noplaybackrate"
|
|
234
240
|
>
|
|
235
|
-
{!media
|
|
241
|
+
{!media?.storageType &&
|
|
242
|
+
media?.file?.original &&
|
|
243
|
+
(eagerLoad || isSourceReady) ? (
|
|
236
244
|
<source src={media.file.original} />
|
|
237
245
|
) : null}
|
|
238
246
|
Your browser does not support video playback.
|
|
239
247
|
</video>
|
|
240
248
|
|
|
241
|
-
{!isPlaying &&
|
|
242
|
-
!isEnded &&
|
|
243
|
-
!isLoading &&
|
|
244
|
-
(
|
|
249
|
+
{!isPlaying && !isEnded && !isLoading && (
|
|
245
250
|
<button
|
|
246
251
|
type="button"
|
|
247
252
|
onClick={handlePlayClick}
|
|
248
253
|
className={clsx(
|
|
249
254
|
"absolute inset-0 items-center justify-center w-full h-full transition rounded-lg z-10",
|
|
250
255
|
hasStarted ? "hidden md:flex" : "flex",
|
|
251
|
-
classNames?.playButton
|
|
256
|
+
classNames?.playButton,
|
|
252
257
|
)}
|
|
253
258
|
>
|
|
254
|
-
<
|
|
255
|
-
className={clsx(
|
|
256
|
-
"flex items-center justify-center w-14 h-14 lg:w-[130px] lg:h-[130px] bg-white/15 rounded-full backdrop-blur-sm hover:shadow-md transition-all duration-300 hover:scale-110 group",
|
|
257
|
-
classNames?.playButtonOuter
|
|
258
|
-
)}
|
|
259
|
-
>
|
|
260
|
-
<div
|
|
261
|
-
className={clsx(
|
|
262
|
-
"w-10 h-10 lg:w-[90px] lg:h-[90px] bg-white relative overflow-hidden rounded-full",
|
|
263
|
-
classNames?.playButtonInner
|
|
264
|
-
)}
|
|
265
|
-
>
|
|
266
|
-
<div className="absolute inset-0 cross-gradient box-shadow-1">
|
|
267
|
-
<div className="w-full h-full flex items-center justify-center">
|
|
268
|
-
<svg
|
|
269
|
-
className={clsx(
|
|
270
|
-
"size-4 lg:size-8 text-gray-700",
|
|
271
|
-
classNames?.playIcon
|
|
272
|
-
)}
|
|
273
|
-
viewBox="0 0 30 32"
|
|
274
|
-
fill="currentColor"
|
|
275
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
276
|
-
>
|
|
277
|
-
<path d="M27.2003 19.4972C29.945 17.9735 29.945 14.0264 27.2003 12.5027L6.44148 0.978573C3.77538 -0.501503 0.5 1.42643 0.5 4.47581V27.5241C0.5 30.5735 3.77537 32.5014 6.44147 31.0214L27.2003 19.4972Z" />
|
|
278
|
-
</svg>
|
|
279
|
-
</div>
|
|
280
|
-
</div>
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
259
|
+
{children || <VideoPlayIcon classNames={classNames} />}
|
|
283
260
|
</button>
|
|
284
261
|
)}
|
|
285
262
|
|
|
@@ -287,14 +264,14 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
287
264
|
<div
|
|
288
265
|
className={clsx(
|
|
289
266
|
"absolute inset-0 flex items-center justify-center w-full h-full rounded-lg z-10 bg-black/20",
|
|
290
|
-
classNames?.loadingOverlay
|
|
267
|
+
classNames?.loadingOverlay,
|
|
291
268
|
)}
|
|
292
269
|
onClick={(e) => e.stopPropagation()}
|
|
293
270
|
>
|
|
294
271
|
<div
|
|
295
272
|
className={clsx(
|
|
296
273
|
"flex items-center gap-3 rounded-full bg-black/50 px-4 py-2 text-white",
|
|
297
|
-
classNames?.loadingContent
|
|
274
|
+
classNames?.loadingContent,
|
|
298
275
|
)}
|
|
299
276
|
>
|
|
300
277
|
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
|
@@ -306,7 +283,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
306
283
|
<div
|
|
307
284
|
className={clsx(
|
|
308
285
|
"absolute inset-0 bg-black/50 flex-col gap-6 items-center justify-center text-center text-white hidden z-10 rounded-2xl",
|
|
309
|
-
classNames?.overlay
|
|
286
|
+
classNames?.overlay,
|
|
310
287
|
)}
|
|
311
288
|
style={{ display: isEnded ? "flex" : "none" }}
|
|
312
289
|
>
|
|
@@ -319,7 +296,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
319
296
|
<ReactModalTrigger
|
|
320
297
|
className={clsx(
|
|
321
298
|
"btn btn-primary px-10 py-2.5 rounded-full ripple",
|
|
322
|
-
classNames?.ctaButton
|
|
299
|
+
classNames?.ctaButton,
|
|
323
300
|
)}
|
|
324
301
|
callToAction={callToAction}
|
|
325
302
|
>
|
|
@@ -329,7 +306,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
329
306
|
type="button"
|
|
330
307
|
className={clsx(
|
|
331
308
|
"px-10 py-2.5 bg-white/20 hover:bg-white/30 rounded-full transition text-sm",
|
|
332
|
-
classNames?.overlayReplayButton
|
|
309
|
+
classNames?.overlayReplayButton,
|
|
333
310
|
)}
|
|
334
311
|
onClick={(e) => {
|
|
335
312
|
e.stopPropagation();
|