@webstudio-is/sdk-components-react 0.71.0 → 0.73.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/lib/__generated__/button.props.js +0 -6
- package/lib/__generated__/image.props.js +0 -1
- package/lib/__generated__/link-block.props.js +1 -7
- package/lib/__generated__/link.props.js +1 -7
- package/lib/__generated__/rich-text-link.props.js +1 -7
- package/lib/__generated__/{radio-button-field.props.js → vimeo-play-button.props.js} +14 -0
- package/lib/__generated__/vimeo-preview-image.props.js +441 -0
- package/lib/__generated__/{checkbox-field.props.js → vimeo-spinner.props.js} +0 -1
- package/lib/__generated__/vimeo.props.js +555 -0
- package/lib/blockquote.ws.js +1 -1
- package/lib/box.js +2 -1
- package/lib/button.js +1 -6
- package/lib/button.ws.js +9 -2
- package/lib/checkbox.ws.js +19 -1
- package/lib/cjs/__generated__/button.props.js +0 -6
- package/lib/cjs/__generated__/image.props.js +0 -1
- package/lib/cjs/__generated__/link-block.props.js +1 -7
- package/lib/cjs/__generated__/link.props.js +1 -7
- package/lib/cjs/__generated__/rich-text-link.props.js +1 -7
- package/lib/cjs/__generated__/{text-block.props.js → text.props.js} +3 -3
- package/lib/cjs/__generated__/{radio-button-field.props.js → vimeo-play-button.props.js} +17 -3
- package/lib/cjs/__generated__/vimeo-preview-image.props.js +461 -0
- package/lib/cjs/__generated__/{checkbox-field.props.js → vimeo-spinner.props.js} +3 -4
- package/lib/cjs/__generated__/vimeo.props.js +575 -0
- package/lib/cjs/blockquote.ws.js +1 -1
- package/lib/cjs/box.js +2 -1
- package/lib/cjs/button.js +1 -6
- package/lib/cjs/button.ws.js +9 -2
- package/lib/cjs/checkbox.ws.js +19 -1
- package/lib/cjs/code-text.ws.js +1 -1
- package/lib/cjs/components.js +11 -9
- package/lib/cjs/heading.ws.js +1 -1
- package/lib/cjs/image.js +3 -3
- package/lib/cjs/image.ws.js +12 -9
- package/lib/cjs/label.ws.js +3 -6
- package/lib/cjs/link.js +1 -1
- package/lib/cjs/link.ws.js +3 -3
- package/lib/cjs/list-item.ws.js +1 -1
- package/lib/cjs/metas.js +11 -9
- package/lib/cjs/paragraph.ws.js +2 -2
- package/lib/cjs/props.js +11 -9
- package/lib/cjs/radio-button.ws.js +19 -1
- package/lib/cjs/rich-text-link.ws.js +3 -10
- package/lib/cjs/span.ws.js +1 -1
- package/lib/cjs/{text-block.js → text.js} +6 -6
- package/lib/cjs/{text-block.ws.js → text.ws.js} +10 -10
- package/lib/cjs/{link-block.js → vimeo-play-button.js} +17 -9
- package/lib/cjs/{link-block.ws.js → vimeo-play-button.ws.js} +13 -22
- package/lib/cjs/vimeo-preview-image.js +40 -0
- package/lib/cjs/{radio-button-field.js → vimeo-preview-image.ws.js} +17 -10
- package/lib/cjs/{checkbox-field.js → vimeo-spinner.js} +16 -8
- package/lib/cjs/{checkbox-field.ws.js → vimeo-spinner.ws.js} +11 -30
- package/lib/cjs/vimeo.js +294 -0
- package/lib/cjs/vimeo.ws.js +345 -0
- package/lib/code-text.ws.js +1 -1
- package/lib/components.js +11 -9
- package/lib/heading.ws.js +1 -1
- package/lib/image.js +6 -5
- package/lib/image.ws.js +12 -9
- package/lib/label.ws.js +4 -7
- package/lib/link.js +3 -3
- package/lib/link.ws.js +3 -3
- package/lib/list-item.ws.js +1 -1
- package/lib/metas.js +52 -50
- package/lib/paragraph.ws.js +2 -2
- package/lib/props.js +52 -50
- package/lib/radio-button.ws.js +19 -1
- package/lib/rich-text-link.ws.js +3 -10
- package/lib/span.ws.js +1 -1
- package/lib/{text-block.js → text.js} +3 -3
- package/lib/{text-block.ws.js → text.ws.js} +7 -7
- package/lib/types/__generated__/vimeo-spinner.props.d.ts +2 -0
- package/lib/types/__generated__/vimeo.props.d.ts +2 -0
- package/lib/types/box.d.ts +1 -1
- package/lib/types/button.d.ts +2 -6
- package/lib/types/button.stories.d.ts +2 -6
- package/lib/types/components.d.ts +5 -4
- package/lib/types/image.d.ts +5 -6
- package/lib/types/image.stories.d.ts +555 -6
- package/lib/types/image.ws.d.ts +8 -0
- package/lib/types/link.d.ts +1 -3
- package/lib/types/link.stories.d.ts +2 -6
- package/lib/types/metas.d.ts +5 -4
- package/lib/types/props.d.ts +5 -4
- package/lib/types/rich-text-link.stories.d.ts +2 -6
- package/lib/types/{text-block.d.ts → text.d.ts} +1 -1
- package/lib/types/{text-block.stories.d.ts → text.stories.d.ts} +1 -1
- package/lib/types/vimeo-play-button.d.ts +4 -0
- package/lib/types/vimeo-play-button.stories.d.ts +5 -0
- package/lib/types/vimeo-preview-image.d.ts +281 -0
- package/lib/types/vimeo-preview-image.stories.d.ts +562 -0
- package/lib/types/vimeo-spinner.d.ts +2 -0
- package/lib/types/vimeo-spinner.stories.d.ts +5 -0
- package/lib/types/{link-block.ws.d.ts → vimeo-spinner.ws.d.ts} +1 -1
- package/lib/types/vimeo.d.ts +73 -0
- package/lib/types/vimeo.stories.d.ts +71 -0
- package/lib/types/vimeo.ws.d.ts +3 -0
- package/lib/vimeo-play-button.js +21 -0
- package/lib/vimeo-play-button.ws.js +26 -0
- package/lib/vimeo-preview-image.js +23 -0
- package/lib/vimeo-preview-image.ws.js +20 -0
- package/lib/vimeo-spinner.js +20 -0
- package/lib/vimeo-spinner.ws.js +25 -0
- package/lib/vimeo.js +282 -0
- package/lib/vimeo.ws.js +327 -0
- package/package.json +9 -7
- package/src/LICENSE +21 -0
- package/src/__generated__/button.props.ts +0 -6
- package/src/__generated__/image.props.ts +0 -1
- package/src/__generated__/link-block.props.ts +1 -7
- package/src/__generated__/link.props.ts +1 -7
- package/src/__generated__/rich-text-link.props.ts +1 -7
- package/src/__generated__/{checkbox-field.props.ts → vimeo-play-button.props.ts} +14 -0
- package/src/__generated__/vimeo-preview-image.props.ts +486 -0
- package/src/__generated__/{radio-button-field.props.ts → vimeo-spinner.props.ts} +0 -1
- package/src/__generated__/vimeo.props.ts +621 -0
- package/src/blockquote.ws.tsx +1 -1
- package/src/box.tsx +1 -1
- package/src/button.tsx +3 -11
- package/src/button.ws.tsx +8 -1
- package/src/checkbox.ws.tsx +19 -1
- package/src/code-text.ws.tsx +1 -1
- package/src/components.ts +5 -4
- package/src/heading.ws.tsx +1 -1
- package/src/image.tsx +6 -5
- package/src/image.ws.tsx +11 -9
- package/src/label.ws.tsx +4 -7
- package/src/link.tsx +5 -11
- package/src/link.ws.tsx +3 -3
- package/src/list-item.ws.tsx +1 -1
- package/src/metas.ts +5 -4
- package/src/paragraph.ws.tsx +2 -2
- package/src/props.ts +5 -4
- package/src/radio-button.ws.tsx +19 -1
- package/src/rich-text-link.ws.tsx +3 -11
- package/src/span.ws.tsx +1 -1
- package/src/text.stories.tsx +16 -0
- package/src/{text-block.tsx → text.tsx} +2 -2
- package/src/{text-block.ws.tsx → text.ws.tsx} +8 -8
- package/src/vimeo-play-button.stories.tsx +17 -0
- package/src/vimeo-play-button.tsx +24 -0
- package/src/vimeo-play-button.ws.ts +29 -0
- package/src/vimeo-preview-image.stories.tsx +17 -0
- package/src/vimeo-preview-image.tsx +30 -0
- package/src/vimeo-preview-image.ws.ts +22 -0
- package/src/vimeo-spinner.stories.tsx +17 -0
- package/src/vimeo-spinner.tsx +25 -0
- package/src/vimeo-spinner.ws.ts +27 -0
- package/src/vimeo.stories.tsx +17 -0
- package/src/vimeo.tsx +430 -0
- package/src/vimeo.ws.ts +331 -0
- package/lib/checkbox-field.js +0 -9
- package/lib/checkbox-field.ws.js +0 -44
- package/lib/cjs/radio-button-field.ws.js +0 -62
- package/lib/link-block.js +0 -10
- package/lib/link-block.ws.js +0 -33
- package/lib/radio-button-field.js +0 -9
- package/lib/radio-button-field.ws.js +0 -44
- package/lib/types/checkbox-field.d.ts +0 -3
- package/lib/types/link-block.d.ts +0 -3
- package/lib/types/link-block.stories.d.ts +0 -13
- package/lib/types/radio-button-field.d.ts +0 -3
- package/src/checkbox-field.tsx +0 -10
- package/src/checkbox-field.ws.tsx +0 -47
- package/src/link-block.stories.tsx +0 -16
- package/src/link-block.tsx +0 -9
- package/src/link-block.ws.tsx +0 -38
- package/src/radio-button-field.tsx +0 -10
- package/src/radio-button-field.ws.tsx +0 -47
- package/src/text-block.stories.tsx +0 -16
- /package/lib/__generated__/{text-block.props.js → text.props.js} +0 -0
- /package/lib/types/__generated__/{checkbox-field.props.d.ts → text.props.d.ts} +0 -0
- /package/lib/types/__generated__/{radio-button-field.props.d.ts → vimeo-play-button.props.d.ts} +0 -0
- /package/lib/types/__generated__/{text-block.props.d.ts → vimeo-preview-image.props.d.ts} +0 -0
- /package/lib/types/{checkbox-field.ws.d.ts → text.ws.d.ts} +0 -0
- /package/lib/types/{radio-button-field.ws.d.ts → vimeo-play-button.ws.d.ts} +0 -0
- /package/lib/types/{text-block.ws.d.ts → vimeo-preview-image.ws.d.ts} +0 -0
- /package/src/__generated__/{text-block.props.ts → text.props.ts} +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultStates,
|
|
3
|
+
type PresetStyle,
|
|
4
|
+
type WsComponentMeta,
|
|
5
|
+
type WsComponentPropsMeta,
|
|
6
|
+
} from "@webstudio-is/react-sdk";
|
|
7
|
+
import { props } from "./__generated__/vimeo-spinner.props";
|
|
8
|
+
import { div } from "@webstudio-is/react-sdk/css-normalize";
|
|
9
|
+
import { BoxIcon } from "@webstudio-is/icons/svg";
|
|
10
|
+
|
|
11
|
+
const presetStyle = {
|
|
12
|
+
div,
|
|
13
|
+
} satisfies PresetStyle<"div">;
|
|
14
|
+
|
|
15
|
+
export const meta: WsComponentMeta = {
|
|
16
|
+
type: "container",
|
|
17
|
+
icon: BoxIcon,
|
|
18
|
+
states: defaultStates,
|
|
19
|
+
presetStyle,
|
|
20
|
+
category: "hidden",
|
|
21
|
+
label: "Spinner",
|
|
22
|
+
requiredAncestors: ["Vimeo"],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const propsMeta: WsComponentPropsMeta = {
|
|
26
|
+
props,
|
|
27
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
|
2
|
+
import { Vimeo as VimeoPrimitive } from "./vimeo";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Components/Vimeo",
|
|
6
|
+
component: VimeoPrimitive,
|
|
7
|
+
} as ComponentMeta<typeof VimeoPrimitive>;
|
|
8
|
+
|
|
9
|
+
const Template: ComponentStory<typeof VimeoPrimitive> = (args) => (
|
|
10
|
+
<VimeoPrimitive
|
|
11
|
+
{...args}
|
|
12
|
+
style={{ minHeight: 20, outline: "1px solid black" }}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const Vimeo = Template.bind({});
|
|
17
|
+
Vimeo.args = {};
|
package/src/vimeo.tsx
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
// Many implementation ideas came from
|
|
2
|
+
// https://github.com/slightlyoff/lite-vimeo
|
|
3
|
+
// Main reasons to not use it as is:
|
|
4
|
+
// - we don't want to render player by default
|
|
5
|
+
// - we want to expose Webstudio components to the user for customization
|
|
6
|
+
|
|
7
|
+
import { colord } from "colord";
|
|
8
|
+
import {
|
|
9
|
+
forwardRef,
|
|
10
|
+
useState,
|
|
11
|
+
useRef,
|
|
12
|
+
useEffect,
|
|
13
|
+
type ElementRef,
|
|
14
|
+
type ComponentProps,
|
|
15
|
+
useContext,
|
|
16
|
+
createContext,
|
|
17
|
+
type ContextType,
|
|
18
|
+
useMemo,
|
|
19
|
+
} from "react";
|
|
20
|
+
import { ReactSdkContext } from "@webstudio-is/react-sdk";
|
|
21
|
+
import { shallowEqual } from "shallow-equal";
|
|
22
|
+
|
|
23
|
+
const defaultTag = "div";
|
|
24
|
+
|
|
25
|
+
// https://developer.vimeo.com/player/sdk/embed
|
|
26
|
+
type VimeoPlayerOptions = {
|
|
27
|
+
background?: boolean;
|
|
28
|
+
color?: string;
|
|
29
|
+
controls?: boolean;
|
|
30
|
+
dnt?: boolean;
|
|
31
|
+
interactive_params?: string;
|
|
32
|
+
title?: boolean;
|
|
33
|
+
portrait?: boolean;
|
|
34
|
+
// @todo url type to validate url on the input
|
|
35
|
+
/** The ID or the URL of the video on Vimeo. You must supply one of these values to identify the video. When the video's privacy setting is Private, you must use the URL, and the URL must include the h parameter. For more information, see our introductory guide. */
|
|
36
|
+
url?: string;
|
|
37
|
+
/** Whether to pause the current video when another Vimeo video on the same page starts to play. Set this value to false to permit simultaneous playback of all the videos on the page. This option has no effect if you've disabled cookies in your browser, either through browser settings or with an extension or plugin. */
|
|
38
|
+
autopause?: boolean;
|
|
39
|
+
/** Whether to enable the browser to enter picture-in-picture mode automatically when switching tabs or windows, where supported. */
|
|
40
|
+
autopip?: boolean;
|
|
41
|
+
/** Whether to start playback of the video automatically. This feature might not work on all devices. */
|
|
42
|
+
autoplay?: boolean;
|
|
43
|
+
/** Whether to display the video owner's name. */
|
|
44
|
+
byline?: boolean;
|
|
45
|
+
/** Whether to enable keyboard input to trigger player events. This setting doesn't affect tab control. */
|
|
46
|
+
keyboard?: boolean;
|
|
47
|
+
/** Whether to restart the video automatically after reaching the end. */
|
|
48
|
+
loop?: boolean;
|
|
49
|
+
/** Whether the video is muted upon loading. The true value is required for the autoplay behavior in some browsers. */
|
|
50
|
+
muted?: boolean;
|
|
51
|
+
/** Whether to include the picture-in-picture button among the player controls and enable the picture-in-picture API. */
|
|
52
|
+
pip?: boolean;
|
|
53
|
+
/** Whether the video plays inline on supported mobile devices. To force the device to play the video in fullscreen mode instead, set this value to false. */
|
|
54
|
+
playsinline?: boolean;
|
|
55
|
+
/** For videos on a Vimeo Plus account or higher: the playback quality of the video. Use auto for the best possible quality given available bandwidth and other factors. You can also specify 360p, 540p, 720p, 1080p, 2k, and 4k. */
|
|
56
|
+
quality?: "auto" | "360p" | "540p" | "720p" | "1080p" | "2k" | "4k";
|
|
57
|
+
/** Whether to return a responsive embed code, or one that provides intelligent adjustments based on viewing conditions. We recommend this option for mobile-optimized sites. */
|
|
58
|
+
responsive?: boolean;
|
|
59
|
+
/** Whether the player displays speed controls in the preferences menu and enables the playback rate API. */
|
|
60
|
+
speed?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* The text track to display with the video. Specify the text track by its language code (en), the language code and locale (en-US), or the language code and kind (en.captions). For this argument to work, the video must already have a text track of the given type; see our Help Center or Working with Text Track Uploads for more information.
|
|
63
|
+
* To enable automatically generated closed captions instead, provide the value en-x-autogen. Please note that, at the present time, automatic captions are always in English.
|
|
64
|
+
*/
|
|
65
|
+
texttrack?: string;
|
|
66
|
+
/** Whether the responsive player and transparent background are enabled. */
|
|
67
|
+
transparent?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getUrl = (options: VimeoPlayerOptions) => {
|
|
71
|
+
if (options.url === undefined) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let url;
|
|
75
|
+
try {
|
|
76
|
+
const userUrl = new URL(options.url);
|
|
77
|
+
url = new URL(IFRAME_CDN);
|
|
78
|
+
url.pathname = `/video${userUrl.pathname}`;
|
|
79
|
+
// eslint-disable-next-line no-empty
|
|
80
|
+
} catch {}
|
|
81
|
+
|
|
82
|
+
if (url === undefined) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let option: keyof VimeoPlayerOptions;
|
|
87
|
+
for (option in options) {
|
|
88
|
+
const value = options[option];
|
|
89
|
+
if (option === "url" || value === undefined) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
url.searchParams.append(option, value.toString());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// We always set autoplay to true because we have a button that starts the video
|
|
96
|
+
url.searchParams.set("autoplay", "true");
|
|
97
|
+
|
|
98
|
+
// Vimeo needs a hex color value without the hash
|
|
99
|
+
if (typeof options.color === "string") {
|
|
100
|
+
const color = colord(options.color).toHex().replace("#", "");
|
|
101
|
+
url.searchParams.set("color", color);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Portrait option won't work if at title is not set to true
|
|
105
|
+
if (options.portrait) {
|
|
106
|
+
url.searchParams.set("title", "true");
|
|
107
|
+
}
|
|
108
|
+
// Byline won't show up if portrait and title is not set to true
|
|
109
|
+
if (options.byline) {
|
|
110
|
+
url.searchParams.set("portrait", "true");
|
|
111
|
+
url.searchParams.set("title", "true");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return url.toString();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const preconnect = (url: string) => {
|
|
118
|
+
const link = document.createElement("link");
|
|
119
|
+
link.rel = "preconnect";
|
|
120
|
+
link.href = url;
|
|
121
|
+
link.crossOrigin = "true";
|
|
122
|
+
document.head.append(link);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
let warmed = false;
|
|
126
|
+
|
|
127
|
+
// Host that Vimeo uses to serve JS needed by player
|
|
128
|
+
const PLAYER_CDN = "https://f.vimeocdn.com";
|
|
129
|
+
// The iframe document comes from player.vimeo.com
|
|
130
|
+
const IFRAME_CDN = "https://player.vimeo.com";
|
|
131
|
+
// Image for placeholder comes from i.vimeocdn.com
|
|
132
|
+
const IMAGE_CDN = "https://i.vimeocdn.com";
|
|
133
|
+
|
|
134
|
+
const warmConnections = () => {
|
|
135
|
+
if (warmed) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
preconnect(PLAYER_CDN);
|
|
139
|
+
preconnect(IFRAME_CDN);
|
|
140
|
+
preconnect(IMAGE_CDN);
|
|
141
|
+
warmed = true;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const createPlayer = (
|
|
145
|
+
parent: Element,
|
|
146
|
+
options: VimeoPlayerOptions,
|
|
147
|
+
callback: () => void
|
|
148
|
+
) => {
|
|
149
|
+
const url = getUrl(options);
|
|
150
|
+
if (url === undefined) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const iframe = document.createElement("iframe");
|
|
154
|
+
iframe.setAttribute(
|
|
155
|
+
"allow",
|
|
156
|
+
"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture;"
|
|
157
|
+
);
|
|
158
|
+
iframe.setAttribute("frameborder", "0");
|
|
159
|
+
iframe.setAttribute("allowfullscreen", "true");
|
|
160
|
+
iframe.setAttribute("src", url);
|
|
161
|
+
iframe.setAttribute(
|
|
162
|
+
"style",
|
|
163
|
+
"position: absolute; width: 100%; height: 100%; opacity: 0; transition: opacity 1s;"
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Show iframe only once it's loaded to avoid weird flashes.
|
|
167
|
+
iframe.addEventListener(
|
|
168
|
+
"load",
|
|
169
|
+
() => {
|
|
170
|
+
iframe.style.opacity = "1";
|
|
171
|
+
callback();
|
|
172
|
+
},
|
|
173
|
+
{ once: true }
|
|
174
|
+
);
|
|
175
|
+
parent.appendChild(iframe);
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
iframe.parentElement?.removeChild(iframe);
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const getVideoId = (url: string) => {
|
|
183
|
+
try {
|
|
184
|
+
const parsedUrl = new URL(url);
|
|
185
|
+
const id = parsedUrl.pathname.split("/")[1];
|
|
186
|
+
if (id === "" || id == null) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
return id;
|
|
190
|
+
// eslint-disable-next-line no-empty
|
|
191
|
+
} catch {}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const loadPreviewImage = async (element: HTMLElement, videoUrl: string) => {
|
|
195
|
+
const videoId = getVideoId(videoUrl);
|
|
196
|
+
// API is the video-id based
|
|
197
|
+
// http://vimeo.com/api/v2/video/364402896.json
|
|
198
|
+
const apiUrl = `https://vimeo.com/api/v2/video/${videoId}.json`;
|
|
199
|
+
|
|
200
|
+
// Now fetch the JSON that locates our placeholder from vimeo's JSON API
|
|
201
|
+
const response = (await (await fetch(apiUrl)).json())[0];
|
|
202
|
+
|
|
203
|
+
// Extract the image id, e.g. 819916979, from a URL like:
|
|
204
|
+
// thumbnail_large: "https://i.vimeocdn.com/video/819916979_640.jpg"
|
|
205
|
+
const thumbnail = response.thumbnail_large;
|
|
206
|
+
const imgId = thumbnail.substr(thumbnail.lastIndexOf("/") + 1).split("_")[0];
|
|
207
|
+
|
|
208
|
+
const imageUrl = new URL(IMAGE_CDN);
|
|
209
|
+
imageUrl.pathname = `/video/${imgId}.webp`;
|
|
210
|
+
imageUrl.searchParams.append("mw", "1100");
|
|
211
|
+
imageUrl.searchParams.append("mh", "619");
|
|
212
|
+
imageUrl.searchParams.append("q", "70");
|
|
213
|
+
return imageUrl;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
type PlayerStatus = "initial" | "initialized" | "ready";
|
|
217
|
+
|
|
218
|
+
const useVimeo = ({
|
|
219
|
+
options,
|
|
220
|
+
renderer,
|
|
221
|
+
showPreview,
|
|
222
|
+
}: {
|
|
223
|
+
options: VimeoPlayerOptions;
|
|
224
|
+
showPreview?: boolean;
|
|
225
|
+
renderer: ContextType<typeof ReactSdkContext>["renderer"];
|
|
226
|
+
}) => {
|
|
227
|
+
const [playerStatus, setPlayerStatus] = useState<PlayerStatus>("initial");
|
|
228
|
+
const elementRef = useRef<ElementRef<typeof defaultTag> | null>(null);
|
|
229
|
+
const [previewImageUrl, setPreviewImageUrl] = useState<URL>();
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
setPlayerStatus(
|
|
233
|
+
options.autoplay && renderer !== "canvas" ? "initialized" : "initial"
|
|
234
|
+
);
|
|
235
|
+
}, [options.autoplay, renderer]);
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
if (
|
|
239
|
+
elementRef.current === null ||
|
|
240
|
+
playerStatus === "ready" ||
|
|
241
|
+
options.url === undefined
|
|
242
|
+
) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (showPreview) {
|
|
246
|
+
loadPreviewImage(elementRef.current, options.url).then(
|
|
247
|
+
setPreviewImageUrl
|
|
248
|
+
);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
setPreviewImageUrl(undefined);
|
|
252
|
+
}, [renderer, showPreview, options.url, playerStatus]);
|
|
253
|
+
|
|
254
|
+
const optionsRef = useRef(options);
|
|
255
|
+
const stableOptions = useMemo(() => {
|
|
256
|
+
if (shallowEqual(options, optionsRef.current) === false) {
|
|
257
|
+
optionsRef.current = options;
|
|
258
|
+
}
|
|
259
|
+
return optionsRef.current;
|
|
260
|
+
}, [options]);
|
|
261
|
+
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
if (elementRef.current === null || playerStatus === "initial") {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
return createPlayer(elementRef.current, stableOptions, () => {
|
|
267
|
+
setPlayerStatus("ready");
|
|
268
|
+
});
|
|
269
|
+
}, [stableOptions, playerStatus]);
|
|
270
|
+
return { previewImageUrl, playerStatus, setPlayerStatus, elementRef };
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export type VimeoOptions = Omit<
|
|
274
|
+
VimeoPlayerOptions,
|
|
275
|
+
| "dnt"
|
|
276
|
+
| "interactive_params"
|
|
277
|
+
| "background"
|
|
278
|
+
| "controls"
|
|
279
|
+
| "color"
|
|
280
|
+
| "byline"
|
|
281
|
+
| "title"
|
|
282
|
+
| "portrait"
|
|
283
|
+
> & {
|
|
284
|
+
/** Whether the preview image should be loaded from Vimeo API. Ideally don't use it, because it will show up with some delay and will make your site feel slower. */
|
|
285
|
+
showPreview?: boolean;
|
|
286
|
+
/** Whether to prevent the player from tracking session data, including cookies. Keep in mind that setting this argument to true also blocks video stats. */
|
|
287
|
+
doNotTrack?: VimeoPlayerOptions["dnt"];
|
|
288
|
+
/** Key-value pairs representing dynamic parameters that are utilized on interactive videos with live elements, such as title=my-video,subtitle=interactive. */
|
|
289
|
+
interactiveParams?: VimeoPlayerOptions["interactive_params"];
|
|
290
|
+
/** Whether the player is in background mode, which hides the playback controls, enables autoplay, and loops the video. */
|
|
291
|
+
backgroundMode?: VimeoPlayerOptions["background"];
|
|
292
|
+
/** Whether to display the player's interactive elements, including the play bar and sharing buttons. Set this option to false for a chromeless experience. To control playback when the play/pause button is hidden, set autoplay to true, use keyboard controls (which remain active), or implement our player SDK. */
|
|
293
|
+
showControls?: VimeoPlayerOptions["controls"];
|
|
294
|
+
// @todo use color type to use color control
|
|
295
|
+
/** A color value of the playback controls, which is normally #00ADEF. The embed settings of the video might override this value. */
|
|
296
|
+
controlsColor?: VimeoPlayerOptions["color"];
|
|
297
|
+
/** Whether to display the video owner's name. */
|
|
298
|
+
showByline?: VimeoPlayerOptions["byline"];
|
|
299
|
+
/** Whether the player displays the title overlay. */
|
|
300
|
+
showTitle?: VimeoPlayerOptions["title"];
|
|
301
|
+
/** Whether to display the video owner's portrait. Only works if either title or byline are also enabled */
|
|
302
|
+
showPortrait?: VimeoPlayerOptions["portrait"];
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
type Props = Omit<ComponentProps<typeof defaultTag>, keyof VimeoOptions> &
|
|
306
|
+
VimeoOptions;
|
|
307
|
+
type Ref = ElementRef<typeof defaultTag>;
|
|
308
|
+
|
|
309
|
+
export const Vimeo = forwardRef<Ref, Props>(
|
|
310
|
+
(
|
|
311
|
+
{
|
|
312
|
+
url,
|
|
313
|
+
autoplay = false,
|
|
314
|
+
autopause = true,
|
|
315
|
+
backgroundMode = false,
|
|
316
|
+
showByline = false,
|
|
317
|
+
showControls = true,
|
|
318
|
+
doNotTrack = false,
|
|
319
|
+
keyboard = true,
|
|
320
|
+
loop = false,
|
|
321
|
+
muted = false,
|
|
322
|
+
pip = false,
|
|
323
|
+
playsinline = true,
|
|
324
|
+
showPortrait = true,
|
|
325
|
+
quality = "auto",
|
|
326
|
+
responsive = true,
|
|
327
|
+
speed = false,
|
|
328
|
+
showTitle = false,
|
|
329
|
+
transparent = true,
|
|
330
|
+
showPreview = false,
|
|
331
|
+
autopip,
|
|
332
|
+
controlsColor,
|
|
333
|
+
interactiveParams,
|
|
334
|
+
texttrack,
|
|
335
|
+
children,
|
|
336
|
+
...rest
|
|
337
|
+
},
|
|
338
|
+
ref
|
|
339
|
+
) => {
|
|
340
|
+
const { renderer } = useContext(ReactSdkContext);
|
|
341
|
+
const { previewImageUrl, playerStatus, setPlayerStatus, elementRef } =
|
|
342
|
+
useVimeo({
|
|
343
|
+
renderer,
|
|
344
|
+
showPreview,
|
|
345
|
+
options: {
|
|
346
|
+
url,
|
|
347
|
+
autoplay,
|
|
348
|
+
autopause,
|
|
349
|
+
keyboard,
|
|
350
|
+
loop,
|
|
351
|
+
muted,
|
|
352
|
+
pip,
|
|
353
|
+
playsinline,
|
|
354
|
+
quality,
|
|
355
|
+
responsive,
|
|
356
|
+
speed,
|
|
357
|
+
transparent,
|
|
358
|
+
portrait: showPortrait,
|
|
359
|
+
byline: showByline,
|
|
360
|
+
title: showTitle,
|
|
361
|
+
color: controlsColor,
|
|
362
|
+
controls: showControls,
|
|
363
|
+
interactive_params: interactiveParams,
|
|
364
|
+
background: backgroundMode,
|
|
365
|
+
dnt: doNotTrack,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<VimeoContext.Provider
|
|
371
|
+
value={{
|
|
372
|
+
status: playerStatus,
|
|
373
|
+
previewImageUrl,
|
|
374
|
+
onInitPlayer() {
|
|
375
|
+
if (renderer !== "canvas") {
|
|
376
|
+
setPlayerStatus("initialized");
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
}}
|
|
380
|
+
>
|
|
381
|
+
<div
|
|
382
|
+
{...rest}
|
|
383
|
+
ref={(value: Ref) => {
|
|
384
|
+
elementRef.current = value;
|
|
385
|
+
if (ref !== null) {
|
|
386
|
+
typeof ref === "function" ? ref(value) : (ref.current = value);
|
|
387
|
+
}
|
|
388
|
+
}}
|
|
389
|
+
onPointerOver={() => {
|
|
390
|
+
if (renderer !== "canvas") {
|
|
391
|
+
warmConnections();
|
|
392
|
+
}
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
{url === undefined ? <EmptyState /> : children}
|
|
396
|
+
</div>
|
|
397
|
+
</VimeoContext.Provider>
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
Vimeo.displayName = "Vimeo";
|
|
403
|
+
|
|
404
|
+
const EmptyState = () => {
|
|
405
|
+
return (
|
|
406
|
+
<div
|
|
407
|
+
style={{
|
|
408
|
+
display: "flex",
|
|
409
|
+
width: "100%",
|
|
410
|
+
height: "100%",
|
|
411
|
+
alignItems: "center",
|
|
412
|
+
justifyContent: "center",
|
|
413
|
+
fontSize: "1.2em",
|
|
414
|
+
}}
|
|
415
|
+
>
|
|
416
|
+
Open the Properties panel and paste a video URL, e.g.
|
|
417
|
+
https://vimeo.com/831343124.
|
|
418
|
+
</div>
|
|
419
|
+
);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
export const VimeoContext = createContext<{
|
|
423
|
+
previewImageUrl?: URL;
|
|
424
|
+
onInitPlayer: () => void;
|
|
425
|
+
status: PlayerStatus;
|
|
426
|
+
}>({
|
|
427
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
428
|
+
onInitPlayer: () => {},
|
|
429
|
+
status: "initial",
|
|
430
|
+
});
|