├── .editorconfig
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── test.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .nvmrc
├── LICENSE
├── build.mjs
├── dev.mjs
├── dev
├── TestComponent.tsx
├── constants
│ └── testVideos.ts
├── index.html
├── index.tsx
└── utils
│ ├── ComponentProfiler.tsx
│ └── LoadingSpinnerOverlay.tsx
├── docs
├── .vuepress
│ ├── config.ts
│ └── public
│ │ └── CNAME
├── BREAKING.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── README.md
└── assets
│ └── images
│ ├── heading_demo.gif
│ ├── hover_overlay_prop_demo.gif
│ ├── loading_overlay_prop_demo.gif
│ ├── paused_overlay_prop_demo.gif
│ └── playback_range_demo.gif
├── package-lock.json
├── package.json
├── scripts
├── .eslintrc
├── copyDocsReadmeToRoot.ts
├── tsconfig.json
└── validateReadme.ts
├── src
├── HoverVideoPlayer.styles.ts
├── HoverVideoPlayer.tsx
├── HoverVideoPlayer.types.ts
└── index.ts
├── tests
├── assets
│ ├── captions.vtt
│ ├── subtitles-ga.vtt
│ ├── video.mp4
│ └── video.webm
├── constants.ts
├── index.html
├── index.tsx
├── playwright.config.ts
├── serveTests.mjs
└── specs
│ ├── hoverTarget
│ ├── hoverTarget.spec.ts
│ └── index.tsx
│ ├── loadingStateTimeout
│ ├── index.tsx
│ └── loadingStateTimeout.spec.ts
│ ├── overlays
│ ├── index.tsx
│ └── overlays.spec.ts
│ ├── playback
│ ├── index.tsx
│ ├── playback-focus.spec.ts
│ ├── playback-mouse.spec.ts
│ └── playback-touch.spec.ts
│ ├── playbackRange
│ ├── index.tsx
│ └── playbackRange.spec.ts
│ ├── playbackStartDelay
│ ├── index.tsx
│ └── playbackStartDelay.spec.ts
│ ├── sizingMode
│ ├── index.tsx
│ └── sizingMode.spec.ts
│ ├── unloadVideoOnPause
│ ├── index.tsx
│ └── unloadVideoOnPause.spec.ts
│ ├── videoCaptions
│ ├── index.tsx
│ └── videoCaptions.spec.ts
│ ├── videoSrc
│ ├── index.tsx
│ └── videoSrc.spec.ts
│ └── videoSrcChange
│ ├── index.tsx
│ └── videoSrcChange.spec.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ignorePatterns": ["node_modules"],
3 | "parser": "@typescript-eslint/parser",
4 | "parserOptions": {
5 | "ecmaVersion": 2020,
6 | "sourceType": "module",
7 | "ecmaFeatures": {
8 | "jsx": true
9 | }
10 | },
11 | "settings": {
12 | "react": {
13 | "version": "detect"
14 | },
15 | "import/resolver": {
16 | "node": {
17 | "paths": ["src"],
18 | "extensions": [".js", ".jsx", ".ts", ".tsx"]
19 | }
20 | }
21 | },
22 | "extends": [
23 | "plugin:react/recommended",
24 | "plugin:@typescript-eslint/eslint-recommended",
25 | "plugin:@typescript-eslint/recommended",
26 | "plugin:import/errors",
27 | "plugin:import/warnings"
28 | ],
29 | "plugins": ["@typescript-eslint", "jsx-a11y", "react-hooks"],
30 | "rules": {
31 | "react/prop-types": 0,
32 | "no-underscore-dangle": 0,
33 | "import/imports-first": ["error", "absolute-first"],
34 | "import/newline-after-import": "error",
35 | "import/prefer-default-export": 0,
36 | "import/no-extraneous-dependencies": "off",
37 | "semi": "error",
38 | "react-hooks/rules-of-hooks": "error",
39 | "react-hooks/exhaustive-deps": "warn",
40 | "no-console": [
41 | "error",
42 | {
43 | "allow": ["warn", "error"]
44 | }
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: "Let's squash some bugs \U0001F41B"
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: "Got an idea? \U0001F4A1"
4 | title: "[FEAT]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **What problem do you want to solve? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Checklist
2 |
3 | - [ ] I have read the [CONTRIBUTING](https://github.com/Gyanreyer/react-hover-video-player/blob/main/CONTRIBUTING.md) doc.
4 | - [ ] I have added/updated unit tests to cover my changes.
5 | - [ ] Unit tests pass locally.
6 | - [ ] I have added appropriate documentation for my changes.
7 | - [ ] I have updated the README to reflect any changes that will affect the component's public API (if applicable).
8 |
9 | ## Problem
10 |
11 | Describe the problem that these changes are solving. If this is addressing an open issue, please link to it here.
12 |
13 | ## Solution
14 |
15 | Describe your code changes in detail and how they solve the problem described above.
16 |
17 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | jobs:
9 | test:
10 | timeout-minutes: 60
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: 16
17 | - name: Install dependencies
18 | run: npm ci
19 | - name: Install Playwright Browsers
20 | run: npx playwright install --with-deps
21 | - name: Run Playwright tests
22 | run: npm run test
23 | - uses: actions/upload-artifact@v3
24 | if: always()
25 | with:
26 | name: playwright-report
27 | path: playwright-report/
28 | retention-days: 30
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | .nyc_output
3 | /dist
4 | /node_modules
5 | /dev/build
6 | npm-debug.log*
7 | .DS_Store
8 | /README.md
9 | /docs/.vuepress/dist
10 | /docs/.vuepress/.temp
11 | /docs/.vuepress/.cache
12 | /test-results/
13 | /playwright-report/
14 | /playwright/.cache/
15 | /tests/index.js
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.13.2
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ryan Geyer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/build.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from "esbuild";
2 | import fs from "fs";
3 |
4 | const outputDir = "dist";
5 |
6 | const shouldClean = process.argv.includes("--clean");
7 |
8 | if (shouldClean) {
9 | // Clean up any files in the output directory, or make sure the output directory exists
10 | if (fs.existsSync(outputDir)) {
11 | fs.rmSync(outputDir, {
12 | recursive: true,
13 | force: true,
14 | });
15 | }
16 |
17 | fs.mkdirSync(outputDir);
18 | }
19 |
20 | const sharedOptions = {
21 | entryPoints: ["src/index.ts"],
22 | sourcemap: true,
23 | bundle: true,
24 | packages: "external",
25 | target: "es6",
26 | logLevel: "info",
27 | };
28 |
29 | const esmOptions = {
30 | ...sharedOptions,
31 | outfile: `${outputDir}/index.mjs`,
32 | format: "esm",
33 | };
34 |
35 | const cjsOptions = {
36 | ...sharedOptions,
37 | outfile: `${outputDir}/index.cjs`,
38 | format: "cjs",
39 | };
40 |
41 | const shouldWatch = process.argv.includes("--watch");
42 |
43 | if (shouldWatch) {
44 | const context = await esbuild.context(esmOptions);
45 |
46 | await context.watch();
47 | await context.serve();
48 | } else {
49 | let buildTargets = ["all"];
50 | const buildsArgIndex = process.argv.indexOf("--builds");
51 | if (buildsArgIndex >= 0) {
52 | buildTargets = process.argv[buildsArgIndex + 1].split(",");
53 | }
54 |
55 | const builds = [];
56 |
57 | buildTargets.forEach((buildTarget) => {
58 | if (buildTarget === "esm" || buildTarget === "all") {
59 | builds.push(esbuild.build(esmOptions));
60 | }
61 |
62 | if (buildTarget === "cjs" || buildTarget === "all") {
63 | builds.push(esbuild.build(cjsOptions));
64 | }
65 | });
66 |
67 | if (builds.length === 0) {
68 | console.error("No valid builds specified for the --builds arg.");
69 | process.exit(1);
70 | }
71 |
72 | await Promise.all(builds);
73 | }
74 |
--------------------------------------------------------------------------------
/dev.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from 'esbuild';
2 |
3 | const context = await esbuild.context({
4 | entryPoints: ['dev/index.tsx'],
5 | outfile: 'dev/build/index.js',
6 | sourcemap: true,
7 | bundle: true,
8 | target: 'es6',
9 | logLevel: 'info',
10 | });
11 |
12 | await context.watch();
13 | await context.serve({
14 | servedir: 'dev',
15 | port: 8080,
16 | });
17 |
--------------------------------------------------------------------------------
/dev/TestComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { css } from "emotion";
3 |
4 | import HoverVideoPlayer from "../src";
5 | import LoadingSpinnerOverlay from "./utils/LoadingSpinnerOverlay";
6 | import ComponentProfiler from "./utils/ComponentProfiler";
7 |
8 | interface TestComponentProps {
9 | videoSrc: string;
10 | thumbnailImageSrc: string;
11 | }
12 |
13 | /**
14 | * Do all of your testing on this component!
15 | * It is wrapped with a ComponentProfiler component by default,
16 | * which will log out render times each time the component re-renders.
17 | *
18 | * You may modify this file however you want for testing,
19 | * but your changes should not be committed. If you think your changes should be committed,
20 | * please contact the maintainer.
21 | */
22 | const TestComponent = ({
23 | videoSrc,
24 | thumbnailImageSrc,
25 | }: TestComponentProps): JSX.Element => {
26 | return (
27 |
28 | {/* TEST COMPONENT HERE */}
29 | }
31 | pausedOverlay={
32 |
41 | }
42 | loadingOverlay={}
43 | className={css`
44 | padding-top: 75%;
45 | `}
46 | sizingMode="container"
47 | preload="none"
48 | unloadVideoOnPaused
49 | restartOnPaused
50 | />
51 |
52 | );
53 | };
54 |
55 | export default TestComponent;
56 |
--------------------------------------------------------------------------------
/dev/constants/testVideos.ts:
--------------------------------------------------------------------------------
1 | // Public test videos courtesy of https://gist.github.com/jsturgis/
2 | interface Video {
3 | videoSrc: string;
4 | thumbnailImageSrc: string;
5 | }
6 |
7 | const testVideos: Video[] = [
8 | {
9 | videoSrc:
10 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
11 | thumbnailImageSrc:
12 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg',
13 | },
14 | {
15 | videoSrc:
16 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
17 | thumbnailImageSrc:
18 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ElephantsDream.jpg',
19 | },
20 | {
21 | videoSrc:
22 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4',
23 | thumbnailImageSrc:
24 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/Sintel.jpg',
25 | },
26 | {
27 | videoSrc:
28 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4',
29 | thumbnailImageSrc:
30 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/TearsOfSteel.jpg',
31 | },
32 | {
33 | videoSrc:
34 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
35 | thumbnailImageSrc:
36 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ForBiggerBlazes.jpg',
37 | },
38 | {
39 | videoSrc:
40 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
41 | thumbnailImageSrc:
42 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ForBiggerEscapes.jpg',
43 | },
44 | {
45 | videoSrc:
46 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
47 | thumbnailImageSrc:
48 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ForBiggerJoyrides.jpg',
49 | },
50 | {
51 | videoSrc:
52 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4',
53 | thumbnailImageSrc:
54 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ForBiggerMeltdowns.jpg',
55 | },
56 | ];
57 |
58 | export default testVideos;
59 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dev Page
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/dev/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { css } from "emotion";
4 |
5 | import TestComponent from "./TestComponent";
6 | import testVideos from "./constants/testVideos";
7 |
8 | const DevPage = (): JSX.Element => (
9 |
14 | REACT HOVER VIDEO PLAYER
15 |
22 | {testVideos.map(({ videoSrc, thumbnailImageSrc }) => (
23 |
28 | ))}
29 |
30 |
31 | );
32 |
33 | const rootElement = document.createElement("div");
34 | document.body.appendChild(rootElement);
35 |
36 | ReactDOM.render(, rootElement);
37 |
38 | export default DevPage;
39 |
--------------------------------------------------------------------------------
/dev/utils/ComponentProfiler.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import React from 'react';
3 |
4 | interface RenderTiming {
5 | averageRenderTime: number;
6 | renderCount: number;
7 | }
8 |
9 | interface ComponentProfilerProps {
10 | profilerID: string;
11 | children: React.ReactChild;
12 | }
13 |
14 | /**
15 | * Util component logs out render timings for whatever component it's wrapped aroud
16 | */
17 | const ComponentProfiler = ({
18 | profilerID,
19 | children,
20 | }: ComponentProfilerProps): JSX.Element => {
21 | // Use a ref to track render timing for this component in a persistent store
22 | const renderTiming = React.useRef();
23 | if (!renderTiming.current) {
24 | renderTiming.current = {
25 | averageRenderTime: 0,
26 | renderCount: 0,
27 | };
28 | }
29 |
30 | // Logs out helpful render timing info for performance measurements
31 | const onProfilerRender = React.useCallback(
32 | (
33 | id, // the "id" prop of the Profiler tree that has just committed
34 | phase: string, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
35 | actualDuration: number // time spent rendering the committed update
36 | ) => {
37 | if (phase === 'mount') {
38 | console.log(`${profilerID} | MOUNT: ${actualDuration}ms`);
39 | } else {
40 | renderTiming.current.renderCount += 1;
41 | renderTiming.current.averageRenderTime +=
42 | (actualDuration - renderTiming.current.averageRenderTime) /
43 | renderTiming.current.renderCount;
44 | console.log(
45 | `${profilerID} | UPDATE: ${actualDuration}ms | New average: ${renderTiming.current.averageRenderTime}ms`
46 | );
47 | }
48 | },
49 | [profilerID]
50 | );
51 | return (
52 |
53 | {children}
54 |
55 | );
56 | };
57 |
58 | export default ComponentProfiler;
59 |
--------------------------------------------------------------------------------
/dev/utils/LoadingSpinnerOverlay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { css, cx } from 'emotion';
3 |
4 | // Shared styles
5 | const loadingOverlayWrapper = css`
6 | width: 100%;
7 | height: 100%;
8 |
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | `;
13 |
14 | const darkenedBackground = css`
15 | background-color: rgba(0, 0, 0, 0.7);
16 | `;
17 |
18 | // LoadingSpinnerOverlay styles
19 | const animateStroke = css`
20 | animation-name: spinner-stroke-animation;
21 | animation-timing-function: ease-in-out;
22 | animation-iteration-count: infinite;
23 |
24 | @keyframes spinner-stroke-animation {
25 | 0%,
26 | 20% {
27 | stroke-dashoffset: 54;
28 | transform: rotate(0);
29 | }
30 |
31 | 60%,
32 | 80% {
33 | stroke-dashoffset: 14;
34 | transform: rotate(45deg);
35 | }
36 |
37 | 100% {
38 | stroke-dashoffset: 54;
39 | transform: rotate(360deg);
40 | }
41 | }
42 | `;
43 |
44 | interface LoadingSpinnerOverlayProps {
45 | spinnerDiameter?: number;
46 | animationDuration?: number;
47 | shouldAnimateStroke?: boolean;
48 | shouldShowDarkenedBackground?: boolean;
49 | shouldShowSemiTransparentRing?: boolean;
50 | strokeColor?: string;
51 | className?: string;
52 | }
53 |
54 | /**
55 | * @component LoadingSpinnerOverlay
56 | *
57 | * Renders a loading overlay for the HoverVideoPlayer which shows an animated rotating semi-circle spinner
58 | *
59 | * @param {number} [spinnerDiameter=60] - The pixel width that the spinner circle should display at
60 | * @param {number} [animationDuration=1000] - The duration in ms that it should take for the spinner circle to complete a single rotation
61 | * @param {bool} [shouldAnimateStroke=true] - Whether the circle's outline stroke should be animated so that it appears to expand and contract
62 | * @param {bool} [shouldShowDarkenedBackground=true] - Whether the loading overlay should have a semi-transparent background which darkens the contents behind it
63 | * @param {bool} [shouldShowSemiTransparentRing=false] - Whether the spinner should have a semi-transparent circle behind the main animated stroke
64 | * @param {string} [strokeColor="#ffffff"] - The color to apply to the spinner circle's stroke
65 | * @param {string} [className] - Custom className to apply to the loading overlay wrapper
66 | */
67 | const LoadingSpinnerOverlay = ({
68 | spinnerDiameter = 60,
69 | animationDuration = 1000,
70 | shouldAnimateStroke = true,
71 | shouldShowDarkenedBackground = true,
72 | shouldShowSemiTransparentRing = false,
73 | strokeColor = '#ffffff',
74 | className = '',
75 | }: LoadingSpinnerOverlayProps): JSX.Element => (
76 |
85 |
150 |
151 | );
152 |
153 | export default LoadingSpinnerOverlay;
154 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.ts:
--------------------------------------------------------------------------------
1 | import { defineUserConfig } from 'vuepress';
2 | import type { DefaultThemeOptions } from 'vuepress';
3 |
4 | export default defineUserConfig({
5 | description:
6 | 'A React component for setting up a video that plays on hover. Supports both desktop mouse events and mobile touch events, and provides an easy interface for adding thumbnails and loading states.',
7 | head: [
8 | // Set the favicon to the film strip emoji
9 | [
10 | 'link',
11 | {
12 | rel: 'icon',
13 | href: 'data:image/svg+xml,',
14 | },
15 | ],
16 | // Add GA tags to the of the page
17 | [
18 | 'script',
19 | {
20 | async: true,
21 | src: 'https://www.googletagmanager.com/gtag/js?id=G-D7PD421E1W',
22 | },
23 | ],
24 | [
25 | 'script',
26 | {},
27 | `window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-D7PD421E1W');`,
28 | ],
29 | ],
30 | themeConfig: {
31 | docsDir: 'docs',
32 | navbar: [
33 | { text: 'Home', link: '/' },
34 | { text: 'Contributing', link: '/CONTRIBUTING.md' },
35 | ],
36 | repo: 'gyanreyer/react-hover-video-player',
37 | sidebar: 'auto',
38 | sidebarDepth: 2,
39 | },
40 | markdown: {
41 | links: {
42 | externalIcon: false,
43 | },
44 | },
45 | plugins: ['@vuepress/plugin-search'],
46 | });
47 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/CNAME:
--------------------------------------------------------------------------------
1 | react-hover-video-player.dev
--------------------------------------------------------------------------------
/docs/BREAKING.md:
--------------------------------------------------------------------------------
1 | # Breaking changes
2 |
3 | ## 10.0.0 breaking changes
4 |
5 | - [Deprecates passing config objects to `videoSrc` prop](#deprecates-passing-config-objects-to-videosrc-prop)
6 | - [Deprecates passing config objects to `videoCaptions` prop](#deprecates-passing-config-objects-to-videocaptions-prop)
7 | - [Removes `shouldSuppressPlaybackInterruptedErrors` prop](#removes-shouldsuppressplaybackinterruptederrors-prop)
8 |
9 | ### Deprecates passing config objects to `videoSrc` prop
10 |
11 | The `videoSrc` prop still supports URL strings,
12 | but no longer accepts config objects or arrays of config objects for video sources.
13 | Instead, sources like this must now be defined as `` elements.
14 |
15 | Migration for a single source config object is fairly straightforward; the `src` and `type` properties
16 | on the config objects map directly to the attributes that need to be set on the `` elements:
17 |
18 | ```diff
19 |
29 | + )}
30 | />
31 | ```
32 |
33 | For an array of source config objects, simply wrap your `` elements in a fragment:
34 |
35 | ```diff
36 |
49 | +
50 | +
51 | + >
52 | + )}
53 | />
54 | ```
55 |
56 | ### Deprecates passing config objects to `videoCaptions` prop
57 |
58 | The `videoCaptions` prop also no longer accepts config objects for caption tracks.
59 | Instead, caption tracks must be defined as `