@windstream/react-shared-components 0.1.4 → 0.1.6
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 +635 -635
- package/dist/contentful/index.esm.js +1 -1
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +1 -1
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +177 -177
- package/src/components/accordion/Accordion.stories.tsx +230 -230
- package/src/components/accordion/types.ts +11 -11
- package/src/components/alert-card/AlertCard.stories.tsx +171 -171
- package/src/components/alert-card/index.tsx +40 -38
- package/src/components/alert-card/types.ts +12 -12
- package/src/components/brand-button/BrandButton.stories.tsx +223 -223
- package/src/components/brand-button/helpers.ts +35 -35
- package/src/components/brand-button/index.tsx +115 -115
- package/src/components/brand-button/types.ts +37 -37
- package/src/components/button/Button.stories.tsx +108 -108
- package/src/components/button/index.tsx +27 -27
- package/src/components/button/types.ts +14 -14
- package/src/components/call-button/CallButton.stories.tsx +324 -324
- package/src/components/call-button/index.tsx +86 -86
- package/src/components/call-button/types.ts +11 -11
- package/src/components/checkbox/Checkbox.stories.tsx +247 -247
- package/src/components/checkbox/index.tsx +197 -197
- package/src/components/checkbox/types.ts +27 -27
- package/src/components/checklist/Checklist.stories.tsx +150 -150
- package/src/components/checklist/index.tsx +59 -59
- package/src/components/checklist/types.ts +16 -16
- package/src/components/collapse/Collapse.stories.tsx +255 -255
- package/src/components/collapse/index.tsx +46 -46
- package/src/components/collapse/types.ts +6 -6
- package/src/components/divider/Divider.stories.tsx +205 -205
- package/src/components/divider/index.tsx +22 -22
- package/src/components/divider/type.ts +3 -3
- package/src/components/image/Image.stories.tsx +113 -113
- package/src/components/image/index.tsx +25 -25
- package/src/components/image/types.ts +40 -40
- package/src/components/input/Input.stories.tsx +325 -325
- package/src/components/input/index.tsx +177 -177
- package/src/components/input/types.ts +37 -37
- package/src/components/link/Link.stories.tsx +163 -163
- package/src/components/link/types.ts +25 -25
- package/src/components/list/List.stories.tsx +272 -272
- package/src/components/list/index.tsx +88 -88
- package/src/components/list/list-item/index.tsx +38 -38
- package/src/components/list/list-item/types.ts +13 -13
- package/src/components/list/types.ts +29 -29
- package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
- package/src/components/material-icon/constants.ts +98 -98
- package/src/components/material-icon/index.tsx +47 -47
- package/src/components/material-icon/types.ts +31 -31
- package/src/components/modal/Modal.stories.tsx +171 -171
- package/src/components/modal/index.tsx +164 -164
- package/src/components/modal/types.ts +24 -24
- package/src/components/next-image/index.tsx +32 -32
- package/src/components/next-image/types.ts +1 -1
- package/src/components/pagination/index.tsx +100 -100
- package/src/components/pagination/types.ts +6 -6
- package/src/components/radio-button/RadioButton.stories.tsx +307 -307
- package/src/components/radio-button/index.tsx +75 -75
- package/src/components/radio-button/types.ts +21 -21
- package/src/components/see-more/SeeMore.stories.tsx +181 -181
- package/src/components/see-more/index.tsx +44 -44
- package/src/components/see-more/types.ts +4 -4
- package/src/components/select/Select.stories.tsx +411 -411
- package/src/components/select/index.tsx +150 -150
- package/src/components/select/types.ts +35 -35
- package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
- package/src/components/select-plan-button/index.tsx +57 -57
- package/src/components/select-plan-button/types.ts +14 -14
- package/src/components/skeleton/Skeleton.stories.tsx +179 -179
- package/src/components/skeleton/index.tsx +61 -61
- package/src/components/skeleton/types.ts +4 -4
- package/src/components/spinner/Spinner.stories.tsx +335 -335
- package/src/components/spinner/index.tsx +44 -44
- package/src/components/spinner/types.ts +5 -5
- package/src/components/text/Text.stories.tsx +321 -321
- package/src/components/text/index.tsx +25 -25
- package/src/components/text/types.ts +45 -45
- package/src/components/tooltip/Tooltip.stories.tsx +219 -219
- package/src/components/tooltip/index.tsx +74 -74
- package/src/components/tooltip/types.ts +7 -7
- package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
- package/src/components/view-cart-button/index.tsx +42 -42
- package/src/components/view-cart-button/types.ts +5 -5
- package/src/contentful/blocks/blogs-grid/index.tsx +129 -129
- package/src/contentful/blocks/blogs-grid/types.ts +26 -26
- package/src/contentful/blocks/breadcrumbs/index.tsx +55 -55
- package/src/contentful/blocks/button/Button.stories.tsx +40 -40
- package/src/contentful/blocks/button/index.tsx +108 -108
- package/src/contentful/blocks/button/types.ts +34 -34
- package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
- package/src/contentful/blocks/callout/index.tsx +66 -66
- package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
- package/src/contentful/blocks/cards/blog-card/index.tsx +99 -99
- package/src/contentful/blocks/cards/blog-card/types.ts +14 -14
- package/src/contentful/blocks/cards/index.tsx +13 -13
- package/src/contentful/blocks/cards/product-card/index.tsx +208 -208
- package/src/contentful/blocks/cards/product-card/types.ts +28 -28
- package/src/contentful/blocks/cards/testimonial-card/index.tsx +88 -88
- package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
- package/src/contentful/blocks/cards/types.ts +1 -1
- package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
- package/src/contentful/blocks/carousel/helper.tsx +356 -356
- package/src/contentful/blocks/carousel/index.tsx +74 -74
- package/src/contentful/blocks/carousel/types.ts +143 -143
- package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
- package/src/contentful/blocks/cta-callout/index.tsx +60 -60
- package/src/contentful/blocks/cta-callout/types.ts +26 -26
- package/src/contentful/blocks/find-kinetic/index.tsx +130 -130
- package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
- package/src/contentful/blocks/floating-banner/types.ts +22 -22
- package/src/contentful/blocks/footer/Footer.stories.tsx +30 -30
- package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
- package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
- package/src/contentful/blocks/image-promo-bar/index.tsx +45 -41
- package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
- package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
- package/src/contentful/blocks/modal/constants.ts +53 -53
- package/src/contentful/blocks/modal/index.tsx +91 -91
- package/src/contentful/blocks/modal/types.ts +12 -12
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +111 -111
- package/src/contentful/blocks/navigation/index.tsx +385 -385
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +80 -80
- package/src/contentful/blocks/navigation/types.ts +41 -41
- package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
- package/src/contentful/blocks/primary-hero/index.tsx +229 -229
- package/src/contentful/blocks/primary-hero/types.ts +35 -35
- package/src/contentful/blocks/search-block/index.tsx +90 -90
- package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
- package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
- package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
- package/src/contentful/blocks/text/Text.stories.tsx +23 -23
- package/src/contentful/blocks/text/index.tsx +12 -12
- package/src/contentful/blocks/text/types.ts +1 -1
- package/src/contentful/index.ts +69 -69
- package/src/hooks/use-body-scroll-lock.ts +34 -34
- package/src/hooks/use-outside-click.ts +17 -17
- package/src/index.ts +96 -96
- package/src/next/index.ts +5 -5
- package/src/setupTests.ts +46 -46
- package/src/stories/DocsTemplate.tsx +24 -24
- package/src/styles/globals.css +343 -343
- package/src/types/global.d.ts +9 -9
- package/src/types/micro-components.ts +99 -99
- package/src/utils/index.ts +49 -49
|
@@ -66,7 +66,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
66
66
|
className={`image-promo-bar-content ${maxWidth ? "max-w-120 xl:mx-auto" : ""} mx-5 mb-8 mt-16`}
|
|
67
67
|
>
|
|
68
68
|
<div
|
|
69
|
-
className={`flex shrink-0 flex-col items-center gap-8 xl:
|
|
69
|
+
className={`flex shrink-0 flex-col items-center gap-8 xl:gap-[126px] ${mediaPosition ? "xl:flex-row-reverse" : "xl:flex-row"}`}
|
|
70
70
|
>
|
|
71
71
|
<div
|
|
72
72
|
className={`flex flex-[1_0_0] flex-col items-start justify-center gap-8 xl:gap-10 ${color == "dark" ? "text-text" : "text-white"}`}
|
|
@@ -107,53 +107,57 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
107
107
|
{checklist.length > 0 && (
|
|
108
108
|
<Checklist items={checklist} iconPosition="top" iconSize={24} />
|
|
109
109
|
)}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
110
|
+
{imageLinks.length > 0 && (
|
|
111
|
+
<div className="flex gap-4">
|
|
112
|
+
{/* Image Links Collection */}
|
|
113
|
+
{imageLinks?.map(
|
|
114
|
+
(link: { url: string; image: string }, index: number) => (
|
|
115
|
+
<div key={index} className="image-link w-[147px]">
|
|
116
|
+
<Link
|
|
117
|
+
variant="unstyled"
|
|
118
|
+
href={link.url}
|
|
119
|
+
target="_blank"
|
|
120
|
+
rel="noopener noreferrer"
|
|
121
|
+
>
|
|
122
|
+
<Image src={link.image} alt="icon-link" />
|
|
123
|
+
</Link>
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
127
129
|
{/* CTAs and Disclaimers */}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
{(cta || secondaryCta) && (
|
|
131
|
+
<div className="flex w-full flex-col gap-8 xl:flex-row xl:gap-3">
|
|
132
|
+
{cta && (
|
|
133
|
+
<div className="primary-cta w-full xl:w-auto">
|
|
134
|
+
<Button
|
|
135
|
+
{...cta}
|
|
136
|
+
fullWidth={true}
|
|
137
|
+
renderCheckPlans={renderCheckPlans}
|
|
138
|
+
onModalButtonClick={onModalButtonClick}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
{secondaryCta && (
|
|
143
|
+
<div className="secondary-cta">
|
|
144
|
+
<Button
|
|
145
|
+
{...secondaryCta}
|
|
146
|
+
fullWidth={true}
|
|
147
|
+
renderCheckPlans={renderCheckPlans}
|
|
148
|
+
onModalButtonClick={onModalButtonClick}
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
150
154
|
{ctaDisclaimer && <div>{ctaDisclaimer}</div>}
|
|
151
155
|
{disclaimer && <div>{disclaimer}</div>}
|
|
152
156
|
</div>
|
|
153
157
|
<aside className="flex w-full shrink-0 items-center justify-center xl:w-auto">
|
|
154
158
|
{/* Media Section */}
|
|
155
159
|
{image && (
|
|
156
|
-
<div className="relative aspect-[16/9] w-full xl:aspect-square xl:max-h-[486px] xl:w-[486px]">
|
|
160
|
+
<div className="relative flex aspect-[16/9] w-full justify-center xl:aspect-square xl:max-h-[486px] xl:w-[486px]">
|
|
157
161
|
<NextImage
|
|
158
162
|
src={image}
|
|
159
163
|
alt="section-image"
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import { CheckPlansProps } from "@shared/types/micro-components";
|
|
4
|
-
|
|
5
|
-
export type VideoLinkProps = {
|
|
6
|
-
image?: string;
|
|
7
|
-
videoPopup?: boolean;
|
|
8
|
-
link?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type VideoEmbedProps = {
|
|
12
|
-
containerClassName?: string;
|
|
13
|
-
autoplay?: boolean;
|
|
14
|
-
debug?: boolean;
|
|
15
|
-
} & VideoLinkProps;
|
|
16
|
-
|
|
17
|
-
export type ImagePromoBarProps = {
|
|
18
|
-
brow: string;
|
|
19
|
-
enableHeading: boolean;
|
|
20
|
-
title: string;
|
|
21
|
-
subTitle: string;
|
|
22
|
-
image: string;
|
|
23
|
-
imageWidth: number;
|
|
24
|
-
imageHeight: number;
|
|
25
|
-
mediaPosition: boolean;
|
|
26
|
-
description: React.ReactNode;
|
|
27
|
-
openDescriptionLinksOnANewTab: boolean;
|
|
28
|
-
checklist: string[];
|
|
29
|
-
disclaimer: React.ReactNode;
|
|
30
|
-
imageLinks: { url: string; image: string }[];
|
|
31
|
-
cta: any;
|
|
32
|
-
secondaryCta: any;
|
|
33
|
-
ctaDisclaimer: React.ReactNode;
|
|
34
|
-
videoLink: VideoLinkProps;
|
|
35
|
-
maxWidth?: boolean;
|
|
36
|
-
color: "light" | "dark";
|
|
37
|
-
onModalButtonClick?: (id?: string) => void;
|
|
38
|
-
renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type PlayButtonProps = {
|
|
42
|
-
isHovered?: boolean;
|
|
43
|
-
containerClassName?: string;
|
|
44
|
-
};
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { CheckPlansProps } from "@shared/types/micro-components";
|
|
4
|
+
|
|
5
|
+
export type VideoLinkProps = {
|
|
6
|
+
image?: string;
|
|
7
|
+
videoPopup?: boolean;
|
|
8
|
+
link?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type VideoEmbedProps = {
|
|
12
|
+
containerClassName?: string;
|
|
13
|
+
autoplay?: boolean;
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
} & VideoLinkProps;
|
|
16
|
+
|
|
17
|
+
export type ImagePromoBarProps = {
|
|
18
|
+
brow: string;
|
|
19
|
+
enableHeading: boolean;
|
|
20
|
+
title: string;
|
|
21
|
+
subTitle: string;
|
|
22
|
+
image: string;
|
|
23
|
+
imageWidth: number;
|
|
24
|
+
imageHeight: number;
|
|
25
|
+
mediaPosition: boolean;
|
|
26
|
+
description: React.ReactNode;
|
|
27
|
+
openDescriptionLinksOnANewTab: boolean;
|
|
28
|
+
checklist: string[];
|
|
29
|
+
disclaimer: React.ReactNode;
|
|
30
|
+
imageLinks: { url: string; image: string }[];
|
|
31
|
+
cta: any;
|
|
32
|
+
secondaryCta: any;
|
|
33
|
+
ctaDisclaimer: React.ReactNode;
|
|
34
|
+
videoLink: VideoLinkProps;
|
|
35
|
+
maxWidth?: boolean;
|
|
36
|
+
color: "light" | "dark";
|
|
37
|
+
onModalButtonClick?: (id?: string) => void;
|
|
38
|
+
renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type PlayButtonProps = {
|
|
42
|
+
isHovered?: boolean;
|
|
43
|
+
containerClassName?: string;
|
|
44
|
+
};
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import React, { useMemo } from "react";
|
|
2
|
-
import { VideoEmbedProps } from "./types";
|
|
3
|
-
|
|
4
|
-
import { cx } from "@shared/utils";
|
|
5
|
-
|
|
6
|
-
const extractVimeoIdandHash = (href: string) => {
|
|
7
|
-
// Strip query parameters and fragments
|
|
8
|
-
const cleanHref = href.split(/[?#]/)[0].replace(/\/+$/, "");
|
|
9
|
-
const match =
|
|
10
|
-
cleanHref.match(/vimeo\.com\/(?:.*\/)?(\d{6,})/) ||
|
|
11
|
-
cleanHref.match(/player\.vimeo\.com\/video\/(\d{6,})/);
|
|
12
|
-
|
|
13
|
-
const parts = cleanHref.split("/");
|
|
14
|
-
const hash = parts[parts.length - 1];
|
|
15
|
-
|
|
16
|
-
if (!match?.[1]) return "";
|
|
17
|
-
if (match[1] === hash) return match[1];
|
|
18
|
-
return `${match[1]}/${hash}`;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const VimeoEmbed: React.FC<VideoEmbedProps> = props => {
|
|
22
|
-
const { link, containerClassName, autoplay = false, debug = false } = props;
|
|
23
|
-
|
|
24
|
-
const { href, embedId } = useMemo(() => {
|
|
25
|
-
if (!link) return { href: "", embedId: "" };
|
|
26
|
-
const resolved = link.toString();
|
|
27
|
-
const id = extractVimeoIdandHash(resolved);
|
|
28
|
-
return { href: resolved, embedId: id };
|
|
29
|
-
}, [link]);
|
|
30
|
-
|
|
31
|
-
if (debug) {
|
|
32
|
-
// eslint-disable-next-line no-console
|
|
33
|
-
console.log("[VimeoEmbed] href:", href, "id:", embedId);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!embedId) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const params = new URLSearchParams({
|
|
41
|
-
autoplay: autoplay ? "1" : "0",
|
|
42
|
-
muted: "0",
|
|
43
|
-
playsinline: "1",
|
|
44
|
-
dnt: "1",
|
|
45
|
-
title: "0",
|
|
46
|
-
byline: "0",
|
|
47
|
-
portrait: "0",
|
|
48
|
-
autopause: "1",
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const o_src = `https://player.vimeo.com/video/${embedId}?${params.toString()}`;
|
|
52
|
-
const src = normalizeVimeoPlayerSrc(o_src);
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<div
|
|
56
|
-
className={cx(
|
|
57
|
-
"relative h-0 w-full overflow-hidden pb-[56.25%]",
|
|
58
|
-
containerClassName
|
|
59
|
-
)}
|
|
60
|
-
>
|
|
61
|
-
<iframe
|
|
62
|
-
className="absolute left-0 top-0 h-full w-full"
|
|
63
|
-
src={src}
|
|
64
|
-
allow="autoplay; fullscreen; picture-in-picture"
|
|
65
|
-
allowFullScreen={true}
|
|
66
|
-
referrerPolicy="strict-origin-when-cross-origin"
|
|
67
|
-
title="Embedded vimeo"
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
function normalizeVimeoPlayerSrc(src: string): string {
|
|
74
|
-
try {
|
|
75
|
-
const u = new URL(src);
|
|
76
|
-
// Expect path like /video/{id}
|
|
77
|
-
const parts = u.pathname.split("/").filter(Boolean);
|
|
78
|
-
// Handle /video/{id}/{hash} -> move {hash} to ?h=...
|
|
79
|
-
if (parts[0] === "video" && parts.length >= 2) {
|
|
80
|
-
const id = parts[1];
|
|
81
|
-
const maybeHash = parts[2];
|
|
82
|
-
// Rebuild base
|
|
83
|
-
const base = `https://player.vimeo.com/video/${id}`;
|
|
84
|
-
if (maybeHash && /^[a-f0-9]+$/i.test(maybeHash)) {
|
|
85
|
-
u.searchParams.set("h", maybeHash);
|
|
86
|
-
}
|
|
87
|
-
return `${base}?${u.searchParams.toString()}`;
|
|
88
|
-
}
|
|
89
|
-
return src;
|
|
90
|
-
} catch {
|
|
91
|
-
return src;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { VideoEmbedProps } from "./types";
|
|
3
|
+
|
|
4
|
+
import { cx } from "@shared/utils";
|
|
5
|
+
|
|
6
|
+
const extractVimeoIdandHash = (href: string) => {
|
|
7
|
+
// Strip query parameters and fragments
|
|
8
|
+
const cleanHref = href.split(/[?#]/)[0].replace(/\/+$/, "");
|
|
9
|
+
const match =
|
|
10
|
+
cleanHref.match(/vimeo\.com\/(?:.*\/)?(\d{6,})/) ||
|
|
11
|
+
cleanHref.match(/player\.vimeo\.com\/video\/(\d{6,})/);
|
|
12
|
+
|
|
13
|
+
const parts = cleanHref.split("/");
|
|
14
|
+
const hash = parts[parts.length - 1];
|
|
15
|
+
|
|
16
|
+
if (!match?.[1]) return "";
|
|
17
|
+
if (match[1] === hash) return match[1];
|
|
18
|
+
return `${match[1]}/${hash}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const VimeoEmbed: React.FC<VideoEmbedProps> = props => {
|
|
22
|
+
const { link, containerClassName, autoplay = false, debug = false } = props;
|
|
23
|
+
|
|
24
|
+
const { href, embedId } = useMemo(() => {
|
|
25
|
+
if (!link) return { href: "", embedId: "" };
|
|
26
|
+
const resolved = link.toString();
|
|
27
|
+
const id = extractVimeoIdandHash(resolved);
|
|
28
|
+
return { href: resolved, embedId: id };
|
|
29
|
+
}, [link]);
|
|
30
|
+
|
|
31
|
+
if (debug) {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.log("[VimeoEmbed] href:", href, "id:", embedId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!embedId) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const params = new URLSearchParams({
|
|
41
|
+
autoplay: autoplay ? "1" : "0",
|
|
42
|
+
muted: "0",
|
|
43
|
+
playsinline: "1",
|
|
44
|
+
dnt: "1",
|
|
45
|
+
title: "0",
|
|
46
|
+
byline: "0",
|
|
47
|
+
portrait: "0",
|
|
48
|
+
autopause: "1",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const o_src = `https://player.vimeo.com/video/${embedId}?${params.toString()}`;
|
|
52
|
+
const src = normalizeVimeoPlayerSrc(o_src);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className={cx(
|
|
57
|
+
"relative h-0 w-full overflow-hidden pb-[56.25%]",
|
|
58
|
+
containerClassName
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
<iframe
|
|
62
|
+
className="absolute left-0 top-0 h-full w-full"
|
|
63
|
+
src={src}
|
|
64
|
+
allow="autoplay; fullscreen; picture-in-picture"
|
|
65
|
+
allowFullScreen={true}
|
|
66
|
+
referrerPolicy="strict-origin-when-cross-origin"
|
|
67
|
+
title="Embedded vimeo"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function normalizeVimeoPlayerSrc(src: string): string {
|
|
74
|
+
try {
|
|
75
|
+
const u = new URL(src);
|
|
76
|
+
// Expect path like /video/{id}
|
|
77
|
+
const parts = u.pathname.split("/").filter(Boolean);
|
|
78
|
+
// Handle /video/{id}/{hash} -> move {hash} to ?h=...
|
|
79
|
+
if (parts[0] === "video" && parts.length >= 2) {
|
|
80
|
+
const id = parts[1];
|
|
81
|
+
const maybeHash = parts[2];
|
|
82
|
+
// Rebuild base
|
|
83
|
+
const base = `https://player.vimeo.com/video/${id}`;
|
|
84
|
+
if (maybeHash && /^[a-f0-9]+$/i.test(maybeHash)) {
|
|
85
|
+
u.searchParams.set("h", maybeHash);
|
|
86
|
+
}
|
|
87
|
+
return `${base}?${u.searchParams.toString()}`;
|
|
88
|
+
}
|
|
89
|
+
return src;
|
|
90
|
+
} catch {
|
|
91
|
+
return src;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import React, { useMemo } from "react";
|
|
2
|
-
import { VideoEmbedProps } from "./types";
|
|
3
|
-
|
|
4
|
-
import { cx } from "@shared/utils";
|
|
5
|
-
|
|
6
|
-
// import styles from "./styles.module.scss";
|
|
7
|
-
|
|
8
|
-
export type Props = {
|
|
9
|
-
containerClassName?: string;
|
|
10
|
-
autoplay?: boolean; // Add the autoplay prop
|
|
11
|
-
} & VideoEmbedProps;
|
|
12
|
-
|
|
13
|
-
export const YoutubeEmbed: React.FC<Props> = props => {
|
|
14
|
-
const { link, containerClassName, autoplay = false } = props;
|
|
15
|
-
|
|
16
|
-
const embedId = useMemo(() => {
|
|
17
|
-
if (!link) return "";
|
|
18
|
-
|
|
19
|
-
const href = link.toString();
|
|
20
|
-
const regExp =
|
|
21
|
-
/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
|
|
22
|
-
const match = href.match(regExp);
|
|
23
|
-
|
|
24
|
-
return match && match[7].length === 11 ? match[7] : "";
|
|
25
|
-
}, [link]);
|
|
26
|
-
|
|
27
|
-
// Add autoplay parameter to the URL if autoplay is true
|
|
28
|
-
const autoplayParam = autoplay ? "?autoplay=1" : "";
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div
|
|
32
|
-
className={cx(
|
|
33
|
-
"relative h-0 w-full overflow-hidden pb-[56.25%]",
|
|
34
|
-
containerClassName
|
|
35
|
-
)}
|
|
36
|
-
>
|
|
37
|
-
<iframe
|
|
38
|
-
className="absolute left-0 top-0 h-full w-full"
|
|
39
|
-
src={`https://www.youtube.com/embed/${embedId}${autoplayParam}`}
|
|
40
|
-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
41
|
-
allowFullScreen={true}
|
|
42
|
-
title="Embedded youtube"
|
|
43
|
-
/>
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
};
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { VideoEmbedProps } from "./types";
|
|
3
|
+
|
|
4
|
+
import { cx } from "@shared/utils";
|
|
5
|
+
|
|
6
|
+
// import styles from "./styles.module.scss";
|
|
7
|
+
|
|
8
|
+
export type Props = {
|
|
9
|
+
containerClassName?: string;
|
|
10
|
+
autoplay?: boolean; // Add the autoplay prop
|
|
11
|
+
} & VideoEmbedProps;
|
|
12
|
+
|
|
13
|
+
export const YoutubeEmbed: React.FC<Props> = props => {
|
|
14
|
+
const { link, containerClassName, autoplay = false } = props;
|
|
15
|
+
|
|
16
|
+
const embedId = useMemo(() => {
|
|
17
|
+
if (!link) return "";
|
|
18
|
+
|
|
19
|
+
const href = link.toString();
|
|
20
|
+
const regExp =
|
|
21
|
+
/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
|
|
22
|
+
const match = href.match(regExp);
|
|
23
|
+
|
|
24
|
+
return match && match[7].length === 11 ? match[7] : "";
|
|
25
|
+
}, [link]);
|
|
26
|
+
|
|
27
|
+
// Add autoplay parameter to the URL if autoplay is true
|
|
28
|
+
const autoplayParam = autoplay ? "?autoplay=1" : "";
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
className={cx(
|
|
33
|
+
"relative h-0 w-full overflow-hidden pb-[56.25%]",
|
|
34
|
+
containerClassName
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
<iframe
|
|
38
|
+
className="absolute left-0 top-0 h-full w-full"
|
|
39
|
+
src={`https://www.youtube.com/embed/${embedId}${autoplayParam}`}
|
|
40
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
41
|
+
allowFullScreen={true}
|
|
42
|
+
title="Embedded youtube"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
export const backdropAnimationVariants = {
|
|
2
|
-
closed: {
|
|
3
|
-
opacity: 0,
|
|
4
|
-
},
|
|
5
|
-
open: {
|
|
6
|
-
opacity: 1,
|
|
7
|
-
},
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const contentAnimationVariants = {
|
|
11
|
-
closed: {
|
|
12
|
-
opacity: 0,
|
|
13
|
-
scale: 0.96,
|
|
14
|
-
},
|
|
15
|
-
open: {
|
|
16
|
-
opacity: 1,
|
|
17
|
-
scale: 1,
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const popperAnimationVariants = {
|
|
22
|
-
closed: {
|
|
23
|
-
opacity: 0,
|
|
24
|
-
y: "-100%",
|
|
25
|
-
x: "-50%",
|
|
26
|
-
},
|
|
27
|
-
open: {
|
|
28
|
-
opacity: 1,
|
|
29
|
-
y: "-50%",
|
|
30
|
-
x: "-50%",
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const bottomSheetAnimationVariants = {
|
|
35
|
-
closed: {
|
|
36
|
-
opacity: 0,
|
|
37
|
-
y: "100%",
|
|
38
|
-
x: "-50%",
|
|
39
|
-
},
|
|
40
|
-
open: {
|
|
41
|
-
opacity: 1,
|
|
42
|
-
y: "-50%",
|
|
43
|
-
x: "-50%",
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const sizeToPixel = {
|
|
48
|
-
xs: "475px",
|
|
49
|
-
sm: "640px",
|
|
50
|
-
md: "768px",
|
|
51
|
-
lg: "1024px",
|
|
52
|
-
xl: "1280px",
|
|
53
|
-
};
|
|
1
|
+
export const backdropAnimationVariants = {
|
|
2
|
+
closed: {
|
|
3
|
+
opacity: 0,
|
|
4
|
+
},
|
|
5
|
+
open: {
|
|
6
|
+
opacity: 1,
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const contentAnimationVariants = {
|
|
11
|
+
closed: {
|
|
12
|
+
opacity: 0,
|
|
13
|
+
scale: 0.96,
|
|
14
|
+
},
|
|
15
|
+
open: {
|
|
16
|
+
opacity: 1,
|
|
17
|
+
scale: 1,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const popperAnimationVariants = {
|
|
22
|
+
closed: {
|
|
23
|
+
opacity: 0,
|
|
24
|
+
y: "-100%",
|
|
25
|
+
x: "-50%",
|
|
26
|
+
},
|
|
27
|
+
open: {
|
|
28
|
+
opacity: 1,
|
|
29
|
+
y: "-50%",
|
|
30
|
+
x: "-50%",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const bottomSheetAnimationVariants = {
|
|
35
|
+
closed: {
|
|
36
|
+
opacity: 0,
|
|
37
|
+
y: "100%",
|
|
38
|
+
x: "-50%",
|
|
39
|
+
},
|
|
40
|
+
open: {
|
|
41
|
+
opacity: 1,
|
|
42
|
+
y: "-50%",
|
|
43
|
+
x: "-50%",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const sizeToPixel = {
|
|
48
|
+
xs: "475px",
|
|
49
|
+
sm: "640px",
|
|
50
|
+
md: "768px",
|
|
51
|
+
lg: "1024px",
|
|
52
|
+
xl: "1280px",
|
|
53
|
+
};
|