diff --git a/client/src/backend/components/resource/form/kind/Video/Video.styles.js b/client/src/backend/components/resource/form/kind/Video/Video.styles.js new file mode 100644 index 0000000000..d355377821 --- /dev/null +++ b/client/src/backend/components/resource/form/kind/Video/Video.styles.js @@ -0,0 +1,21 @@ +import styled from "@emotion/styled"; + +export const Group = styled.div` + display: flex; + flex-wrap: wrap; + gap: 40px; + + > * { + inline-size: max(200px, min(calc(50% - 20px), 400px)); + display: flex; + flex-direction: column; + + > div { + flex-grow: 1; + } + + > label { + block-size: 15px; + } + } +`; diff --git a/client/src/backend/components/resource/form/kind/Video.js b/client/src/backend/components/resource/form/kind/Video/index.js similarity index 72% rename from client/src/backend/components/resource/form/kind/Video.js rename to client/src/backend/components/resource/form/kind/Video/index.js index 0a92b4a6a4..ef9d05478c 100644 --- a/client/src/backend/components/resource/form/kind/Video.js +++ b/client/src/backend/components/resource/form/kind/Video/index.js @@ -2,6 +2,7 @@ import React, { PureComponent } from "react"; import PropTypes from "prop-types"; import Form from "global/components/form"; import { withTranslation } from "react-i18next"; +import * as Styled from "./Video.styles"; class ResourceFormKindVideo extends PureComponent { static displayName = "Resource.Form.Kind.Video"; @@ -42,15 +43,26 @@ class ResourceFormKindVideo extends PureComponent { renderVideoAttachmentForm() { return ( - + + + + ); } diff --git a/client/src/backend/containers/resource/breadcrumbs.js b/client/src/backend/containers/resource/breadcrumbs.js index 0ddb64bc80..249391a720 100644 --- a/client/src/backend/containers/resource/breadcrumbs.js +++ b/client/src/backend/containers/resource/breadcrumbs.js @@ -24,7 +24,7 @@ export const getBreadcrumbs = (resource, project, belongsToJournalIssue, t) => { } ]; - return belongsToJournalIssue + return belongsToJournalIssue && project.relationships.journal ? [ { to: lh.link("backendJournals"), diff --git a/client/src/config/app/locale/en-US/json/backend/resources.json b/client/src/config/app/locale/en-US/json/backend/resources.json index 20b0a13c38..ba0688a62a 100644 --- a/client/src/config/app/locale/en-US/json/backend/resources.json +++ b/client/src/config/app/locale/en-US/json/backend/resources.json @@ -59,7 +59,8 @@ "presentation": "Presentation", "interactive": "Interactive", "resource_kind": "Resource Kind", - "kind": "Kind" + "kind": "Kind", + "captions": "Captions" }, "properties": { "title_instructions": "This field accepts Markdown.", diff --git a/client/src/frontend/components/resource-player/Video/index.js b/client/src/frontend/components/resource-player/Video/index.js index 62651c3b5e..86c7474e81 100644 --- a/client/src/frontend/components/resource-player/Video/index.js +++ b/client/src/frontend/components/resource-player/Video/index.js @@ -104,23 +104,49 @@ class ResourcePlayerVideo extends Component { this.props.dispatch(notificationActions.addNotification(notification)); }; + trackRef = el => { + if (!el) return; + + el.addEventListener("load", () => { + if (!el.track?.cues) return; + Array.from(el.track.cues).forEach(c => { + // eslint-disable-next-line no-param-reassign + c.line = -3; + }); + }); + }; + renderFileVideo() { if (!this.state.inBrowser) return null; const { variantPosterStyles, - attachmentStyles + attachmentStyles, + captionsTrackUrl } = this.props.resource.attributes; + // Use the proxy if running over ports in dev to avoid CORS error + const captionsSrc = + process.env.NODE_ENV === "development" + ? captionsTrackUrl.replace("3020", "3010") + : captionsTrackUrl; + return ( ); diff --git a/client/src/frontend/components/resource-player/Video/styles.js b/client/src/frontend/components/resource-player/Video/styles.js index b5c5159cc7..6bd816d38a 100644 --- a/client/src/frontend/components/resource-player/Video/styles.js +++ b/client/src/frontend/components/resource-player/Video/styles.js @@ -6,6 +6,13 @@ export const VideoWrapper = styled.div` left: 0; width: 100%; height: 100%; + + video::cue { + font-size: 16px; + background: rgba(0, 0, 0, 0.75); + color: var(--color-base-neutral-white); + font-family: var(--font-family-heading); + } `; export const Video = styled.iframe` diff --git a/client/src/global/components/form/Upload/index.js b/client/src/global/components/form/Upload/index.js index 4692920c29..fa89c074e6 100644 --- a/client/src/global/components/form/Upload/index.js +++ b/client/src/global/components/form/Upload/index.js @@ -57,6 +57,10 @@ export class FormUpload extends Component { accepts: "application/json", extensions: "json" }, + vtt: { + accepts: "text/vtt", + extensions: "vtt" + }, any: { accepts: null, extensions: null diff --git a/client/src/theme/styles/vendor/react-html5video.js b/client/src/theme/styles/vendor/react-html5video.js index 3ed675f395..51eb51cf86 100644 --- a/client/src/theme/styles/vendor/react-html5video.js +++ b/client/src/theme/styles/vendor/react-html5video.js @@ -158,6 +158,16 @@ export default ` .rh5v-Captions_component { position: relative; + + &:has(.rh5v-Captions_activeTrackItem) { + svg { + fill: var(--hover-color); + + &:hover { + fill: var(--color-interaction-dark); + } + } + } } .rh5v-Captions_component:hover { @@ -178,7 +188,8 @@ export default ` } .rh5v-Captions_icon { - padding: 5px; + padding: 1px; + margin-block-start: 4px; } .rh5v-Captions_trackList {