diff --git a/.eslintrc.js b/.eslintrc.js index 86a8a2665..14c2ace99 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,9 +4,6 @@ module.exports = { "react-app/jest" ], rules: { - // Both kinds of quotes are used a lot - "quotes": "off", - // "always" gives 319 warning, "never" gives 991. "object-curly-spacing": "off", diff --git a/src/App.tsx b/src/App.tsx index 7de229ba8..9f2603851 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ -import Body from './main/Body'; -import Header from './main/Header'; -import { GlobalStyle } from './cssStyles'; +import Body from "./main/Body"; +import Header from "./main/Header"; +import { GlobalStyle } from "./cssStyles"; function App() { return ( diff --git a/src/config.ts b/src/config.ts index 966744ea5..09bd51e86 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,18 +7,18 @@ * * Also does some global hotkey configuration */ -import parseToml from '@iarna/toml/parse-string'; -import deepmerge from 'deepmerge'; -import { Flavor } from './types'; +import parseToml from "@iarna/toml/parse-string"; +import deepmerge from "deepmerge"; +import { Flavor } from "./types"; /** * Local constants */ -const CONTEXT_SETTINGS_FILE = 'editor-settings.toml'; +const CONTEXT_SETTINGS_FILE = "editor-settings.toml"; // Sources that values can come from. -const SRC_SERVER = 'src-server'; -const SRC_URL = 'src-url'; +const SRC_SERVER = "src-server"; +const SRC_URL = "src-url"; /** * Possible configuration values for a metadata catalog field @@ -30,8 +30,8 @@ export interface configureFieldsAttributes { export interface subtitleTags { lang: string, - 'auto-generated': string, - 'auto-generator': string, + "auto-generated": string, + "auto-generator": string, type: string, } @@ -138,19 +138,19 @@ export const init = async () => { const rawUrlSettings = {}; urlParams.forEach((value, key) => { - // Create empty objects for full path (if the key contains '.') and set + // Create empty objects for full path (if the key contains ".") and set // the value at the end. let obj: { [k: string]: any; } = rawUrlSettings; - if (key.startsWith('opencast.') || key === 'allowedCallbackPrefixes') { + if (key.startsWith("opencast.") || key === "allowedCallbackPrefixes") { return; } // Fallback for old parameter - if (key === 'mediaPackageId') { - key = 'id'; + if (key === "mediaPackageId") { + key = "id"; } - const segments = key.split('.'); + const segments = key.split("."); segments.slice(0, -1).forEach(segment => { if (!(segment in obj)) { obj[segment] = {}; @@ -160,7 +160,7 @@ export const init = async () => { obj[segments[segments.length - 1]] = value; }); - urlParameterSettings = validate(rawUrlSettings, true, SRC_URL, 'given as URL GET parameter'); + urlParameterSettings = validate(rawUrlSettings, true, SRC_URL, "given as URL GET parameter"); // Combine results settings = merge.all([defaultSettings, configFileSettings, urlParameterSettings]) as iSettings; @@ -180,47 +180,47 @@ export const init = async () => { const loadContextSettings = async () => { // Try to retrieve the context settings. - let basepath = process.env.PUBLIC_URL || '/'; - if (!basepath.endsWith('/')) { - basepath += '/'; + let basepath = process.env.PUBLIC_URL || "/"; + if (!basepath.endsWith("/")) { + basepath += "/"; } // Construct path to settings file. If the `REACT_APP_SETTINGS_PATH` is - // given and starts with '/', it is interpreted as absolute path from the + // given and starts with "/", it is interpreted as absolute path from the // server root. const settingsPath = process.env.REACT_APP_SETTINGS_PATH || CONTEXT_SETTINGS_FILE; - const base = settingsPath.startsWith('/') ? '' : basepath; + const base = settingsPath.startsWith("/") ? "" : basepath; const url = `${window.location.origin}${base}${settingsPath}`; let response; try { response = await fetch(url); } catch (e) { - console.warn(`Could not access '${settingsPath}' due to network error!`, e || ""); + console.warn(`Could not access "${settingsPath}" due to network error!`, e || ""); return null; } if (response.status === 404) { // If the settings file was not found, we silently ignore the error. We // expect many installation to provide this file. - console.debug(`'${settingsPath}' returned 404: ignoring`); + console.debug(`"${settingsPath}" returned 404: ignoring`); return null; } else if (!response.ok) { console.error( - `Fetching '${settingsPath}' failed: ${response.status} ${response.statusText}` + `Fetching "${settingsPath}" failed: ${response.status} ${response.statusText}` ); return null; } - if (response.headers.get('Content-Type')?.startsWith('text/html')) { - console.warn(`'${settingsPath}' request has 'Content-Type: text/html' -> ignoring...`); + if (response.headers.get("Content-Type")?.startsWith("text/html")) { + console.warn(`"${settingsPath}" request has "Content-Type: text/html" -> ignoring...`); return null; } try { return parseToml(await response.text()); } catch (e) { - console.error(`Could not parse '${settingsPath}' as TOML: `, e); - throw new SyntaxError(`Could not parse '${settingsPath}' as TOML: ${e}`); + console.error(`Could not parse "${settingsPath}" as TOML: `, e); + throw new SyntaxError(`Could not parse "${settingsPath}" as TOML: ${e}`); } }; @@ -236,7 +236,7 @@ const validate = (obj: Record | null, allowParse: boolean, src: str // Validates `obj` with `schema`. `path` is the current path used for error // messages. const validate = (schema: any, obj: Record | null, path: string) => { - if (typeof schema === 'function') { + if (typeof schema === "function") { return validateValue(schema, obj, path); } else { return validateObj(schema, obj, path); @@ -255,7 +255,7 @@ const validate = (obj: Record | null, allowParse: boolean, src: str return newValue === undefined ? value : newValue; } catch (e) { console.warn( - `Validation of setting '${path}' (${sourceDescription}) with value '${value}' failed: ` + `Validation of setting "${path}" (${sourceDescription}) with value "${value}" failed: ` + `${e}. Ignoring.` ); return null; @@ -280,7 +280,7 @@ const validate = (obj: Record | null, allowParse: boolean, src: str } } else { console.warn( - `'${newPath}' (${sourceDescription}) is not a valid settings key. Ignoring.` + `"${newPath}" (${sourceDescription}) is not a valid settings key. Ignoring.` ); } } @@ -294,65 +294,65 @@ const validate = (obj: Record | null, allowParse: boolean, src: str // Validation functions for different types. const types = { - 'string': (v: any, _allowParse: any) => { - if (typeof v !== 'string') { + "string": (v: any, _allowParse: any) => { + if (typeof v !== "string") { throw new Error("is not a string, but should be"); } }, - 'boolean': (v: string, allowParse: any) => { - if (typeof v === 'boolean') { + "boolean": (v: string, allowParse: any) => { + if (typeof v === "boolean") { return; } if (allowParse) { - if (v === 'true') { + if (v === "true") { return true; } - if (v === 'false') { + if (v === "false") { return false; } - throw new Error("can't be parsed as boolean"); + throw new Error("cant be parsed as boolean"); } else { throw new Error("is not a boolean"); } }, - 'array': (v: any, _allowParse: any) => { + "array": (v: any, _allowParse: any) => { if (!Array.isArray(v)) { throw new Error("is not an array, but should be"); } for (const entry in v) { - if (typeof entry !== 'string') { + if (typeof entry !== "string") { throw new Error("is not a string, but should be"); } } }, - 'map': (v: any, _allowParse: any) => { + "map": (v: any, _allowParse: any) => { for (const key in v) { - if (typeof key !== 'string') { + if (typeof key !== "string") { throw new Error("is not a string, but should be"); } - if (typeof v[key] !== 'string') { + if (typeof v[key] !== "string") { throw new Error("is not a string, but should be"); } } }, - 'objectsWithinObjects': (v: any, _allowParse: any) => { + "objectsWithinObjects": (v: any, _allowParse: any) => { for (const catalogName in v) { - if (typeof catalogName !== 'string') { + if (typeof catalogName !== "string") { throw new Error("is not a string, but should be"); } for (const fieldName in v[catalogName]) { - if (typeof fieldName !== 'string') { + if (typeof fieldName !== "string") { throw new Error("is not a string, but should be"); } for (const attributeName in v[catalogName][fieldName]) { - if (typeof attributeName !== 'string') { + if (typeof attributeName !== "string") { throw new Error("is not a string, but should be"); } - if (attributeName === 'show' && typeof v[catalogName][fieldName][attributeName] !== 'boolean') { + if (attributeName === "show" && typeof v[catalogName][fieldName][attributeName] !== "boolean") { throw new Error("is not a boolean"); } - if (attributeName === 'readonly' && typeof v[catalogName][fieldName][attributeName] !== 'boolean') { + if (attributeName === "readonly" && typeof v[catalogName][fieldName][attributeName] !== "boolean") { throw new Error("is not a boolean"); } } diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 9b0002220..35dcf52f7 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -1,12 +1,12 @@ /** * This file contains general css stylings */ -import { css, Global, keyframes } from '@emotion/react'; +import { css, Global, keyframes } from "@emotion/react"; import React from "react"; -import emotionNormalize from 'emotion-normalize'; -import { checkFlexGapSupport } from './util/utilityFunctions'; -import { createTheme } from '@mui/material/styles'; -import { Theme, useTheme } from './themes'; +import emotionNormalize from "emotion-normalize"; +import { checkFlexGapSupport } from "./util/utilityFunctions"; +import { createTheme } from "@mui/material/styles"; +import { Theme, useTheme } from "./themes"; /** * An emotion component that inserts styles globally @@ -27,7 +27,7 @@ export const globalStyle = (theme: Theme) => css({ body: { backgroundColor: `${theme.background}`, color: `${theme.text}`, - fontSize: 'medium', + fontSize: "medium", // Makes the body span to the bottom of the page minHeight: "100vh", }, @@ -64,10 +64,10 @@ export const flexGapReplacementStyle = (flexGapValue: number, flexDirectionIsRow }, ...(flexDirectionIsRow) && { ">*:first-of-type": { - marginLeft: '0px', + marginLeft: "0px", }, ">*:last-child": { - marginRight: '0px', + marginRight: "0px", }, }, } @@ -79,7 +79,7 @@ export const flexGapReplacementStyle = (flexGapValue: number, flexDirectionIsRow * CSS for buttons */ export const basicButtonStyle = (theme: Theme) => css({ - borderRadius: '5px', + borderRadius: "5px", cursor: "pointer", "&:hover": { backgroundColor: `${theme.button_color}`, @@ -90,11 +90,11 @@ export const basicButtonStyle = (theme: Theme) => css({ color: `${theme.inverted_text}`, }, // Flex position child elements - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + display: "flex", + justifyContent: "center", + alignItems: "center", ...(flexGapReplacementStyle(10, false)), - textAlign: 'center' as const, + textAlign: "center" as const, outline: `${theme.button_outline}`, }); @@ -102,24 +102,24 @@ export const basicButtonStyle = (theme: Theme) => css({ * CSS for deactivated buttons */ export const deactivatedButtonStyle = css({ - borderRadius: '10px', + borderRadius: "10px", cursor: "pointer", opacity: "0.6", // Flex position child elements - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + display: "flex", + justifyContent: "center", + alignItems: "center", ...(flexGapReplacementStyle(10, false)), - textAlign: 'center' as const, + textAlign: "center" as const, }); /** * CSS for nagivation styled buttons */ export const navigationButtonStyle = (theme: Theme) => css({ - width: '200px', - padding: '16px', - justifyContent: 'space-around', + width: "200px", + padding: "16px", + justifyContent: "space-around", boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, }); @@ -128,8 +128,8 @@ export const navigationButtonStyle = (theme: Theme) => css({ * CSS for a container that holds back/forward buttons */ export const backOrContinueStyle = css(({ - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", ...(flexGapReplacementStyle(20, false)), })); @@ -137,15 +137,15 @@ export const backOrContinueStyle = css(({ * CSS for big buttons in a dynamic grid */ export const tileButtonStyle = (theme: Theme) => css({ - width: '290px', - height: '220px', - display: 'flex', - flexDirection: 'column', + width: "290px", + height: "220px", + display: "flex", + flexDirection: "column", fontWeight: "bold", ...(flexGapReplacementStyle(30, false)), boxShadow: `${theme.boxShadow_tiles}`, background: `${theme.element_bg}`, - placeSelf: 'center', + placeSelf: "center", }); /** @@ -153,13 +153,13 @@ export const tileButtonStyle = (theme: Theme) => css({ */ export const disableButtonAnimation = css({ "&:hover": { - transform: 'none', + transform: "none", }, "&:focus": { - transform: 'none', + transform: "none", }, "&:active": { - transform: 'none', + transform: "none", }, }); @@ -167,11 +167,11 @@ export const disableButtonAnimation = css({ * CSS for a title */ export const titleStyle = (theme: Theme) => css(({ - display: 'inline-block', - padding: '15px', + display: "inline-block", + padding: "15px", whiteSpace: "nowrap", - textOverflow: 'ellipsis', - maxWidth: '100%', + textOverflow: "ellipsis", + maxWidth: "100%", color: `${theme.text}`, })); @@ -180,9 +180,9 @@ export const titleStyle = (theme: Theme) => css(({ * Used for page titles */ export const titleStyleBold = (theme: Theme) => css({ - fontWeight: 'bold', - fontSize: '24px', - verticalAlign: '-2.5px', + fontWeight: "bold", + fontSize: "24px", + verticalAlign: "-2.5px", color: `${theme.text}`, }); @@ -190,11 +190,11 @@ export const titleStyleBold = (theme: Theme) => css({ * CSS for ariaLive regions that should not be visible */ export const ariaLive = css({ - position: 'absolute', - left: '-99999px', - height: '1px', - width: '1px', - overflow: 'hidden', + position: "absolute", + left: "-99999px", + height: "1px", + width: "1px", + overflow: "hidden", }); /** @@ -205,9 +205,9 @@ export const errorBoxStyle = (errorStatus: boolean, theme: Theme) => { css({ ...(!errorStatus) && { display: "none" }, borderColor: `${theme.error}`, - borderStyle: 'dashed', - fontWeight: 'bold', - padding: '10px', + borderStyle: "dashed", + fontWeight: "bold", + padding: "10px", }) ); }; @@ -239,7 +239,7 @@ export function selectFieldStyle(theme: Theme) { ...provided, color: theme.inverted_text, background: theme.multiValue, - cursor: 'default', + cursor: "default", }), multiValueLabel: (provided: any) => ({ ...provided, @@ -289,35 +289,35 @@ export const calendarStyle = (theme: Theme) => createTheme({ color: `${theme.text}`, /* Calendar-modal */ - '.MuiYearPicker-root': { - '.PrivatePickersYear-yearButton:hover, .Mui-selected:hover': { + ".MuiYearPicker-root": { + ".PrivatePickersYear-yearButton:hover, .Mui-selected:hover": { background: `${theme.focused}`, color: `${theme.focus_text}`, }, - '.Mui-selected': { + ".Mui-selected": { background: `${theme.selected}`, color: `${theme.selected_text}`, } }, /* Clock-modal */ - '& .MuiClock-clock': { // round clock + "& .MuiClock-clock": { // round clock background: `${theme.clock_bg}`, outline: `${theme.clock_border}`, - '-webkitTextFillColor': `${theme.text}`, // Digits on the clock + "-webkitTextFillColor": `${theme.text}`, // Digits on the clock textShadow: `${theme.text_shadow}` }, /* selected digit (hour/minute) */ - '& .MuiClockPicker-root .Mui-selected': { - '-webkitTextFillColor': `${theme.digit_selected}`, - fontWeight: 'bold', - textShadow: 'none', + "& .MuiClockPicker-root .Mui-selected": { + "-webkitTextFillColor": `${theme.digit_selected}`, + fontWeight: "bold", + textShadow: "none", }, /* clock hands */ - '& .MuiClock-pin, .MuiClockPointer-root': { + "& .MuiClock-pin, .MuiClockPointer-root": { background: `${theme.clock_hands}` }, - '& .MuiClockPointer-thumb': { + "& .MuiClockPointer-thumb": { background: `${theme.clock_hands}`, border: `16px solid ${theme.clock_hands}`, } @@ -330,31 +330,31 @@ export const calendarStyle = (theme: Theme) => createTheme({ root: { /* Calendar- and Clock-modal -> arrows, icon, days */ color: `${theme.text} !important`, - '&.MuiPickersDay-root': { - background: 'transparent !important', + "&.MuiPickersDay-root": { + background: "transparent !important", color: `${theme.text} !important`, }, - '&:hover, &.Mui-selected:hover': { + "&:hover, &.Mui-selected:hover": { background: `${theme.focused} !important`, color: `${theme.focus_text} !important`, }, // Selected day - '&.Mui-selected': { + "&.Mui-selected": { background: `${theme.selected} !important`, color: `${theme.selected_text} !important`, }, // Current day - '&:not(.Mui-selected)': { + "&:not(.Mui-selected)": { borderColor: `${theme.focused} !important`, }, - '&.Mui-disabled': { + "&.Mui-disabled": { color: `${theme.disabled} !important`, }, - '&.MuiClock-amButton, &.MuiClock-pmButton': { - '-webkitTextFillColor': `${theme.text} !important`, - '&:hover': { - '-webkitTextFillColor': `${theme.clock_focus} !important` + "&.MuiClock-amButton, &.MuiClock-pmButton": { + "-webkitTextFillColor": `${theme.text} !important`, + "&:hover": { + "-webkitTextFillColor": `${theme.clock_focus} !important` } }, } @@ -372,7 +372,7 @@ export const calendarStyle = (theme: Theme) => createTheme({ export const subtitleSelectStyle = (theme: Theme) => createTheme({ components: { - /* Label: 'Pick a language' & 'Video Flavor' */ + /* Label: "Pick a language" & "Video Flavor" */ MuiInputLabel: { styleOverrides: { root: { @@ -415,11 +415,11 @@ export const subtitleSelectStyle = (theme: Theme) => createTheme({ MuiMenuItem: { styleOverrides: { root: { - '&:hover, &.Mui-selected:hover': { + "&:hover, &.Mui-selected:hover": { color: `${theme.focus_text}`, background: `${theme.focused}` }, - '&.Mui-selected': { + "&.Mui-selected": { color: `${theme.selected_text}`, background: `${theme.selected}`, }, @@ -437,29 +437,29 @@ export const spinningStyle = css({ }); export const customIconStyle = css(({ - maxWidth: '16px', - height: 'auto' + maxWidth: "16px", + height: "auto" })); export const videosStyle = (theme: Theme) => css(({ - display: 'flex', - flexDirection: 'column', + display: "flex", + flexDirection: "column", - width: '100%', + width: "100%", background: `${theme.menu_background}`, - borderRadius: '5px', + borderRadius: "5px", boxShadow: `${theme.boxShadow_tiles}`, - marginTop: '24px', + marginTop: "24px", boxSizing: "border-box", - padding: '10px', + padding: "10px", ...(flexGapReplacementStyle(10, false)), })); export const backgroundBoxStyle = (theme: Theme) => css(({ background: `${theme.menu_background}`, - borderRadius: '7px', + borderRadius: "7px", boxShadow: `${theme.boxShadow_tiles}`, boxSizing: "border-box", - padding: '20px', + padding: "20px", ...(flexGapReplacementStyle(25, false)), })); diff --git a/src/globalKeys.ts b/src/globalKeys.ts index bd203a02f..0e25b055f 100644 --- a/src/globalKeys.ts +++ b/src/globalKeys.ts @@ -2,19 +2,19 @@ * Contains mappings for special keyboard controls, beyond what is usually expected of a webpage * Learn more about keymaps at https://github.com/greena13/react-hotkeys#defining-key-maps (12.03.2021) * - * Additional global configuration settins are placed in './config.ts' + * Additional global configuration settins are placed in "./config.ts" * (They are not placed here, because that somehow makes the name fields of keymaps undefined for some reason) * * If you add a new keyMap, be sure to add it to the getAllHotkeys function */ -import { match } from '@opencast/appkit'; -import { ParseKeys } from 'i18next'; -import { isMacOs } from 'react-device-detect'; +import { match } from "@opencast/appkit"; +import { ParseKeys } from "i18next"; +import { isMacOs } from "react-device-detect"; // Groups for displaying hotkeys in the overview page const groupVideoPlayer = "keyboardControls.groupVideoPlayer"; -const groupCuttingView = 'keyboardControls.groupCuttingView'; -const groupCuttingViewScrubber = 'keyboardControls.groupCuttingViewScrubber'; +const groupCuttingView = "keyboardControls.groupCuttingView"; +const groupCuttingViewScrubber = "keyboardControls.groupCuttingViewScrubber"; const groupSubtitleList = "keyboardControls.groupSubtitleList"; /** diff --git a/src/i18n/config.tsx b/src/i18n/config.tsx index 617914d0e..88be64624 100644 --- a/src/i18n/config.tsx +++ b/src/i18n/config.tsx @@ -1,18 +1,18 @@ -import i18next, { InitOptions } from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; +import i18next, { InitOptions } from "i18next"; +import { initReactI18next } from "react-i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; -import locales from './locales/locales.json'; +import locales from "./locales/locales.json"; -const debug = Boolean(new URLSearchParams(window.location.search).get('debug')); +const debug = Boolean(new URLSearchParams(window.location.search).get("debug")); const resources: InitOptions["resources"] = {}; for (const lang of locales) { - const code = lang.replace(/\..*$/, ''); - const short = code.replace(/-.*$/, ''); + const code = lang.replace(/\..*$/, ""); + const short = code.replace(/-.*$/, ""); const main = locales.filter(l => l.indexOf(short) === 0).length === 1; /* eslint-disable-next-line @typescript-eslint/no-var-requires */ - const translations = require('./locales/' + lang); + const translations = require("./locales/" + lang); if (!main) { resources[code] = { translation: translations }; } @@ -24,14 +24,14 @@ i18next .use(LanguageDetector) .init({ resources, - fallbackLng: ['en-US', 'en'], + fallbackLng: ["en-US", "en"], nonExplicitSupportedLngs: true, debug: debug, }); if (debug) { - console.debug('language', i18next.language); - console.debug('languages', i18next.languages); + console.debug("language", i18next.language); + console.debug("languages", i18next.languages); } export default i18next; diff --git a/src/index.css b/src/index.css index 6a3f0d283..3e84b3c74 100644 --- a/src/index.css +++ b/src/index.css @@ -1,14 +1,14 @@ body, textarea, input { margin: 0; - font-family: "Roboto Flex Variable", 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + font-family: "Roboto Flex Variable", "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/src/index.tsx b/src/index.tsx index 23f60cedd..6a0631e5d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,23 +1,23 @@ -import React from 'react'; -import ReactDOMClient from 'react-dom/client'; -import './index.css'; -import App from './App'; -import { Provider } from 'react-redux'; -import store from './redux/store'; +import React from "react"; +import ReactDOMClient from "react-dom/client"; +import "./index.css"; +import App from "./App"; +import { Provider } from "react-redux"; +import store from "./redux/store"; -import { init } from './config'; -import { sleep } from './util/utilityFunctions'; +import { init } from "./config"; +import { sleep } from "./util/utilityFunctions"; import "@fontsource-variable/roboto-flex"; -import './i18n/config'; +import "./i18n/config"; -import '@opencast/appkit/dist/colors.css'; -import { ColorSchemeProvider } from '@opencast/appkit'; +import "@opencast/appkit/dist/colors.css"; +import { ColorSchemeProvider } from "@opencast/appkit"; -const container = document.getElementById('root'); +const container = document.getElementById("root"); if (!container) { - throw new Error('Failed to find the root element'); + throw new Error("Failed to find the root element"); } const root = ReactDOMClient.createRoot(container); diff --git a/src/main/Body.tsx b/src/main/Body.tsx index 545a80974..f70ca7786 100644 --- a/src/main/Body.tsx +++ b/src/main/Body.tsx @@ -1,25 +1,25 @@ import React from "react"; -import MainMenu from './MainMenu'; -import MainContent from './MainContent'; -import TheEnd from './TheEnd'; -import Error from './Error'; +import MainMenu from "./MainMenu"; +import MainContent from "./MainContent"; +import TheEnd from "./TheEnd"; +import Error from "./Error"; import Landing from "./Landing"; import Lock from "./Lock"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { useAppSelector } from "../redux/store"; -import { selectIsEnd } from '../redux/endSlice'; +import { selectIsEnd } from "../redux/endSlice"; import { selectIsError } from "../redux/errorSlice"; -import { settings } from '../config'; +import { settings } from "../config"; const Body: React.FC = () => { const isEnd = useAppSelector(selectIsEnd); const isError = useAppSelector(selectIsError); - // If we're in a special state, display a special page + // If we"re in a special state, display a special page // Otherwise display the normal page const main = () => { if (!settings.id) { @@ -49,9 +49,9 @@ const Body: React.FC = () => { }; const bodyStyle = css({ - display: 'flex', - flexDirection: 'row', - height: 'calc(100% - 64px)', + display: "flex", + flexDirection: "row", + height: "calc(100% - 64px)", }); return ( diff --git a/src/main/Cutting.tsx b/src/main/Cutting.tsx index 397ffc0e6..01c7ef13b 100644 --- a/src/main/Cutting.tsx +++ b/src/main/Cutting.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import CuttingActions from "./CuttingActions"; -import Timeline from './Timeline'; +import Timeline from "./Timeline"; import { fetchVideoInformation, selectCurrentlyAt, @@ -16,13 +16,13 @@ import { setIsMuted, setVolume, setIsPlayPreview -} from '../redux/videoSlice'; -import { useTranslation } from 'react-i18next'; +} from "../redux/videoSlice"; +import { useTranslation } from "react-i18next"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { httpRequestState } from '../types'; +import { httpRequestState } from "../types"; import { useTheme } from "../themes"; -import { setError } from '../redux/errorSlice'; -import { selectTitleFromEpisodeDc } from '../redux/metadataSlice'; +import { setError } from "../redux/errorSlice"; +import { selectTitleFromEpisodeDc } from "../redux/metadataSlice"; import { titleStyle, titleStyleBold, videosStyle } from "../cssStyles"; import { LuMoreHorizontal } from "react-icons/lu"; import { css } from "@emotion/react"; @@ -46,10 +46,10 @@ const Cutting: React.FC = () => { // Try to fetch URL from external API useEffect(() => { - if (videoURLStatus === 'idle') { + if (videoURLStatus === "idle") { dispatch(fetchVideoInformation()); - } else if (videoURLStatus === 'failed') { - if (errorReason === 'workflowActive') { + } else if (videoURLStatus === "failed") { + if (errorReason === "workflowActive") { dispatch(setError({ error: true, errorTitle: t("error.workflowActive-errorTitle"), @@ -63,7 +63,7 @@ const Cutting: React.FC = () => { errorDetails: error })); } - } else if (videoURLStatus === 'success') { + } else if (videoURLStatus === "success") { if (duration === null) { dispatch(setError({ error: true, @@ -76,11 +76,11 @@ const Cutting: React.FC = () => { // Style const cuttingStyle = css({ - display: 'flex', - width: 'auto', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', + display: "flex", + width: "auto", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", }); diff --git a/src/main/CuttingActions.tsx b/src/main/CuttingActions.tsx index 4570672ca..e06f695c9 100644 --- a/src/main/CuttingActions.tsx +++ b/src/main/CuttingActions.tsx @@ -1,21 +1,21 @@ import React from "react"; -import { basicButtonStyle, customIconStyle } from '../cssStyles'; +import { basicButtonStyle, customIconStyle } from "../cssStyles"; import { IconType } from "react-icons"; import { LuScissors, LuChevronLeft, LuChevronRight, LuTrash, LuMoveHorizontal } from "react-icons/lu"; -import { ReactComponent as TrashRestore } from '../img/trash-restore.svg'; +import { ReactComponent as TrashRestore } from "../img/trash-restore.svg"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { useAppDispatch, useAppSelector } from "../redux/store"; import { cut, markAsDeletedOrAlive, selectIsCurrentSegmentAlive, mergeLeft, mergeRight, mergeAll -} from '../redux/videoSlice'; +} from "../redux/videoSlice"; import { KEYMAP, rewriteKeys } from "../globalKeys"; import { ActionCreatorWithoutPayload } from "@reduxjs/toolkit"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; import { ThemedTooltip } from "./Tooltip"; import { useHotkeys } from "react-hotkeys-hook"; @@ -72,15 +72,15 @@ const CuttingActions: React.FC = () => { ); const cuttingStyle = css({ - display: 'flex', - flexDirection: 'row' as const, - justifyContent: 'center', - alignItems: 'center', + display: "flex", + flexDirection: "row" as const, + justifyContent: "center", + alignItems: "center", }); const verticalLineStyle = css({ - borderLeft: '2px solid #DDD;', - height: '32px', + borderLeft: "2px solid #DDD;", + height: "32px", }); return ( @@ -89,8 +89,8 @@ const CuttingActions: React.FC = () => { actionName={t("cuttingActions.cut-button")} actionHandler={dispatchAction} action={cut} - tooltip={t('cuttingActions.cut-tooltip', { hotkeyName: rewriteKeys(KEYMAP.cutting.cut.key) })} - ariaLabelText={t('cuttingActions.cut-tooltip-aria', { hotkeyName: rewriteKeys(KEYMAP.cutting.cut.key) })} + tooltip={t("cuttingActions.cut-tooltip", { hotkeyName: rewriteKeys(KEYMAP.cutting.cut.key) })} + ariaLabelText={t("cuttingActions.cut-tooltip-aria", { hotkeyName: rewriteKeys(KEYMAP.cutting.cut.key) })} />
{ actionName={t("cuttingActions.mergeLeft-button")} actionHandler={dispatchAction} action={mergeLeft} - tooltip={t('cuttingActions.mergeLeft-tooltip', { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeLeft.key) })} + tooltip={t("cuttingActions.mergeLeft-tooltip", { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeLeft.key) })} ariaLabelText={ - t('cuttingActions.mergeLeft-tooltip-aria', { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeLeft.key) }) + t("cuttingActions.mergeLeft-tooltip-aria", { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeLeft.key) }) } />
@@ -111,9 +111,9 @@ const CuttingActions: React.FC = () => { actionName={t("cuttingActions.mergeRight-button")} actionHandler={dispatchAction} action={mergeRight} - tooltip={t('cuttingActions.mergeRight-tooltip', { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeRight.key) })} + tooltip={t("cuttingActions.mergeRight-tooltip", { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeRight.key) })} ariaLabelText={ - t('cuttingActions.mergeRight-tooltip-aria', { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeRight.key) }) + t("cuttingActions.mergeRight-tooltip-aria", { hotkeyName: rewriteKeys(KEYMAP.cutting.mergeRight.key) }) } />
@@ -121,8 +121,8 @@ const CuttingActions: React.FC = () => { actionName={t("cuttingActions.merge-all-button")} actionHandler={dispatchAction} action={mergeAll} - tooltip={t('cuttingActions.merge-all-tooltip')} - ariaLabelText={t('cuttingActions.merge-all-tooltip-aria')} + tooltip={t("cuttingActions.merge-all-tooltip")} + ariaLabelText={t("cuttingActions.merge-all-tooltip-aria")} /> {/* { * CSS for cutting buttons */ const cuttingActionButtonStyle = css({ - padding: '16px', + padding: "16px", // boxShadow: `${theme.boxShadow}`, // background: `${theme.element_bg}` }); @@ -209,11 +209,11 @@ const MarkAsDeletedButton: React.FC = ({ const theme = useTheme(); return ( - +
actionHandler(action, ref)} onKeyDown={(event: React.KeyboardEvent) => { if (event.key === " " || event.key === "Enter") { @@ -222,7 +222,7 @@ const MarkAsDeletedButton: React.FC = ({ }} > {isCurrentSegmentAlive ? : } -
{isCurrentSegmentAlive ? t('cuttingActions.delete-button') : t("cuttingActions.restore-button")}
+
{isCurrentSegmentAlive ? t("cuttingActions.delete-button") : t("cuttingActions.restore-button")}
); diff --git a/src/main/Discard.tsx b/src/main/Discard.tsx index 383ff2c51..0a631cf27 100644 --- a/src/main/Discard.tsx +++ b/src/main/Discard.tsx @@ -1,17 +1,17 @@ import React from "react"; -import { css } from '@emotion/react'; -import { basicButtonStyle, backOrContinueStyle, navigationButtonStyle, flexGapReplacementStyle } from '../cssStyles'; +import { css } from "@emotion/react"; +import { basicButtonStyle, backOrContinueStyle, navigationButtonStyle, flexGapReplacementStyle } from "../cssStyles"; import { LuChevronLeft, LuXCircle } from "react-icons/lu"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectFinishState } from '../redux/finishSlice'; -import { setEnd } from '../redux/endSlice'; +import { selectFinishState } from "../redux/finishSlice"; +import { setEnd } from "../redux/endSlice"; -import { PageButton } from './Finish'; +import { PageButton } from "./Finish"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; /** @@ -25,9 +25,9 @@ const Discard: React.FC = () => { const finishState = useAppSelector(selectFinishState); const cancelStyle = css({ - display: finishState !== "Discard changes" ? 'none' : 'flex', - flexDirection: 'column' as const, - alignItems: 'center', + display: finishState !== "Discard changes" ? "none" : "flex", + flexDirection: "column" as const, + alignItems: "center", ...(flexGapReplacementStyle(30, false)), }); @@ -57,7 +57,7 @@ const DiscardButton: React.FC = () => { const theme = useTheme(); const discard = () => { - dispatch(setEnd({ hasEnded: true, value: 'discarded' })); + dispatch(setEnd({ hasEnded: true, value: "discarded" })); }; return ( diff --git a/src/main/Error.tsx b/src/main/Error.tsx index 8a3ae70d5..1e4d16cd3 100644 --- a/src/main/Error.tsx +++ b/src/main/Error.tsx @@ -1,14 +1,14 @@ import React from "react"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { LuFrown } from "react-icons/lu"; import { useAppSelector } from "../redux/store"; -import { selectErrorDetails, selectErrorIcon, selectErrorMessage, selectErrorTitle } from '../redux/errorSlice'; +import { selectErrorDetails, selectErrorIcon, selectErrorMessage, selectErrorTitle } from "../redux/errorSlice"; import { flexGapReplacementStyle } from "../cssStyles"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; /** * This page is to be displayed when the application has run into a critical error @@ -25,17 +25,17 @@ const Error: React.FC = () => { const ErrorIcon = useAppSelector(selectErrorIcon); const detailsStyle = css({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', + display: "flex", + flexDirection: "column", + alignItems: "center", }); const theEndStyle = css({ - height: '100%', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', + height: "100%", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", ...(flexGapReplacementStyle(10, false)), }); diff --git a/src/main/Finish.tsx b/src/main/Finish.tsx index 1e3758327..2c9903e2d 100644 --- a/src/main/Finish.tsx +++ b/src/main/Finish.tsx @@ -8,13 +8,13 @@ import WorkflowConfiguration from "./WorkflowConfiguration"; import { LuDoorOpen } from "react-icons/lu"; -import { css } from '@emotion/react'; -import { basicButtonStyle, navigationButtonStyle } from '../cssStyles'; +import { css } from "@emotion/react"; +import { basicButtonStyle, navigationButtonStyle } from "../cssStyles"; import { IconType } from "react-icons"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectPageNumber, setPageNumber } from '../redux/finishSlice'; +import { selectPageNumber, setPageNumber } from "../redux/finishSlice"; import { useTheme } from "../themes"; import { settings } from "../config"; import { useTranslation } from "react-i18next"; @@ -27,15 +27,15 @@ const Finish: React.FC = () => { const pageNumber = useAppSelector(selectPageNumber); const pageZeroStyle = css({ - display: pageNumber !== 0 ? 'none' : 'block', + display: pageNumber !== 0 ? "none" : "block", }); const pageOneStyle = css({ - display: pageNumber !== 1 ? 'none' : 'block', + display: pageNumber !== 1 ? "none" : "block", }); const pageTwoStyle = css({ - display: pageNumber !== 2 ? 'none' : 'block', + display: pageNumber !== 2 ? "none" : "block", }); return ( @@ -78,9 +78,9 @@ export const PageButton: React.FC<{ }; const pageButtonStyle = css({ - minWidth: '100px', - padding: '16px', - justifyContent: 'center', + minWidth: "100px", + padding: "16px", + justifyContent: "center", boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, }); diff --git a/src/main/FinishMenu.tsx b/src/main/FinishMenu.tsx index e4370b83b..7b8602666 100644 --- a/src/main/FinishMenu.tsx +++ b/src/main/FinishMenu.tsx @@ -1,15 +1,15 @@ import React from "react"; -import { css } from '@emotion/react'; -import { basicButtonStyle, flexGapReplacementStyle, tileButtonStyle } from '../cssStyles'; +import { css } from "@emotion/react"; +import { basicButtonStyle, flexGapReplacementStyle, tileButtonStyle } from "../cssStyles"; import { IconType } from "react-icons"; import { LuSave, LuDatabase, LuXCircle } from "react-icons/lu"; import { useAppDispatch } from "../redux/store"; -import { setState, setPageNumber, finish } from '../redux/finishSlice'; +import { setState, setPageNumber, finish } from "../redux/finishSlice"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { useTheme } from "../themes"; /** @@ -18,10 +18,10 @@ import { useTheme } from "../themes"; const FinishMenu: React.FC = () => { const finishMenuStyle = css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - flexWrap: 'wrap', + display: "flex", + flexDirection: "row", + justifyContent: "center", + flexWrap: "wrap", ...(flexGapReplacementStyle(30, false)), }); @@ -65,19 +65,19 @@ const FinishMenuButton: React.FC<{ Icon: IconType, stateName: finish["value"]; } } const iconStyle = css({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + display: "flex", + justifyContent: "center", + alignItems: "center", background: `${theme.background_finish_menu_icon}`, color: `${theme.text}`, - borderRadius: '50%', - width: '90px', - height: '90px', + borderRadius: "50%", + width: "90px", + height: "90px", }); const labelStyle = css({ - padding: '0px 20px', + padding: "0px 20px", }); return ( diff --git a/src/main/Header.tsx b/src/main/Header.tsx index d5f08f7e4..d4afad631 100644 --- a/src/main/Header.tsx +++ b/src/main/Header.tsx @@ -2,16 +2,16 @@ import React from "react"; import { useAppSelector } from "../redux/store"; import { useTheme } from "../themes"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { useTranslation } from "react-i18next"; import { MainMenuButton } from "./MainMenu"; import { LuMoon, LuSun } from "react-icons/lu"; import { HiOutlineTranslate } from "react-icons/hi"; import { LuKeyboard } from "react-icons/lu"; import { MainMenuStateNames } from "../types"; -import { basicButtonStyle, BREAKPOINT_MEDIUM, BREAKPOINT_SMALL, flexGapReplacementStyle } from '../cssStyles'; +import { basicButtonStyle, BREAKPOINT_MEDIUM, BREAKPOINT_SMALL, flexGapReplacementStyle } from "../cssStyles"; -import { ReactComponent as LogoSvg } from '../img/opencast-editor.svg'; +import { ReactComponent as LogoSvg } from "../img/opencast-editor.svg"; import { selectIsEnd } from "../redux/endSlice"; import { checkboxMenuItem, HeaderMenuItemDef, ProtoButton, useColorScheme, WithHeaderMenu } from "@opencast/appkit"; import { IconType } from "react-icons"; @@ -25,33 +25,33 @@ function Header() { const isEnd = useAppSelector(selectIsEnd); const headerStyle = css({ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', + display: "flex", + alignItems: "center", + justifyContent: "space-between", backgroundColor: `${theme.header_bg}`, }); - const headerStyleThemed = scheme.includes('high-contrast-') + const headerStyleThemed = scheme.includes("high-contrast-") ? css({ - height: '62px', - borderBottom: '2px solid white' + height: "62px", + borderBottom: "2px solid white" }) : css({ - height: '64px', + height: "64px", }); const rightSideButtonsStyle = css({ - display: 'flex', - flexDirection: 'row', - height: '100%', - alignItems: 'center', - paddingRight: '24px', + display: "flex", + flexDirection: "row", + height: "100%", + alignItems: "center", + paddingRight: "24px", ...(flexGapReplacementStyle(16, false)), }); const settingsButtonCSS = css({ - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", alignItems: "center", ...(flexGapReplacementStyle(8, false)), @@ -96,8 +96,8 @@ const Logo: React.FC = () => { const { scheme } = useColorScheme(); const logo = css({ - paddingLeft: '8px', - opacity: scheme === 'dark' ? '0.8' : '1', + paddingLeft: "8px", + opacity: scheme === "dark" ? "0.8" : "1", height: "100%", "> *": { @@ -105,12 +105,12 @@ const Logo: React.FC = () => { }, // Unset a bunch of CSS to keep the logo clean - outline: 'unset', + outline: "unset", "&:hover": { - backgroundColor: `unset`, + backgroundColor: "unset", }, "&:focus": { - backgroundColor: `unset`, + backgroundColor: "unset", }, }); @@ -121,7 +121,7 @@ const Logo: React.FC = () => { bottomText={""} ariaLabelText={t("mainMenu.cutting-button")} customCSS={logo} - iconCustomCSS={css({ width: 'auto', height: '60px' })} + iconCustomCSS={css({ width: "auto", height: "60px" })} /> ); }; @@ -137,7 +137,7 @@ const LanguageButton: React.FC = () => { const languageNames = (language: string) => { return new Intl.DisplayNames([language], { - type: 'language' + type: "language" }).of(language); }; @@ -147,7 +147,7 @@ const LanguageButton: React.FC = () => { return { value: entry, label: languageNames(entry) }; }); - // menuItems can't deal with languages being undefined, so we return early + // menuItems can"t deal with languages being undefined, so we return early // until we reach a rerender with actual information if (languages === undefined) { return (<>); @@ -230,7 +230,7 @@ const HeaderButton = React.forwardRef( color: `${theme.header_text}` }, ":focus": { - backgroundColor: 'inherit', + backgroundColor: "inherit", color: `${theme.header_text}` }, ":focus:hover": { diff --git a/src/main/KeyboardControls.tsx b/src/main/KeyboardControls.tsx index 1b52339f9..788049b26 100644 --- a/src/main/KeyboardControls.tsx +++ b/src/main/KeyboardControls.tsx @@ -7,7 +7,7 @@ import { useTranslation, Trans } from "react-i18next"; import { flexGapReplacementStyle } from "../cssStyles"; import { getGroupName, KEYMAP, rewriteKeys } from "../globalKeys"; import { useTheme } from "../themes"; -import { titleStyle, titleStyleBold } from '../cssStyles'; +import { titleStyle, titleStyleBold } from "../cssStyles"; const Group: React.FC<{ name: ParseKeys, entries: { [key: string]: string[][]; }; }> = ({ name, entries }) => { @@ -15,16 +15,16 @@ const Group: React.FC<{ name: ParseKeys, entries: { [key: string]: string[][]; } const theme = useTheme(); const groupStyle = css({ - display: 'flex', - flexDirection: 'column' as const, - width: '460px', - maxWidth: '50vw', + display: "flex", + flexDirection: "column" as const, + width: "460px", + maxWidth: "50vw", background: `${theme.menu_background}`, - borderRadius: '5px', + borderRadius: "5px", boxShadow: `${theme.boxShadow_tiles}`, boxSizing: "border-box", - padding: '0px 20px 20px 20px', + padding: "0px 20px 20px 20px", }); const headingStyle = css({ @@ -47,43 +47,43 @@ const Entry: React.FC<{ name: string, sequences: string[][]; }> = ({ name, seque const theme = useTheme(); const entryStyle = css({ - display: 'flex', - flexFlow: 'column nowrap', - justifyContent: 'left', - width: '100%', - padding: '10px 0px', + display: "flex", + flexFlow: "column nowrap", + justifyContent: "left", + width: "100%", + padding: "10px 0px", ...(flexGapReplacementStyle(10, true)) }); const labelStyle = css({ - fontWeight: 'bold', - overflow: 'hidden', - textOverflow: 'ellipsis', - wordWrap: 'break-word', + fontWeight: "bold", + overflow: "hidden", + textOverflow: "ellipsis", + wordWrap: "break-word", color: `${theme.text}`, }); const sequenceStyle = css({ - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", ...(flexGapReplacementStyle(10, true)) }); const singleKeyStyle = css({ - borderRadius: '4px', - borderWidth: '2px', - borderStyle: 'solid', + borderRadius: "4px", + borderWidth: "2px", + borderStyle: "solid", borderColor: `${theme.singleKey_border}`, background: `${theme.singleKey_bg}`, boxShadow: `${theme.singleKey_boxShadow}`, - padding: '10px', + padding: "10px", color: `${theme.text}`, }); const orStyle = css({ - alignSelf: 'center', - fontSize: '20px', - fontWeight: 'bold', + alignSelf: "center", + fontSize: "20px", + fontWeight: "bold", }); return ( @@ -111,10 +111,10 @@ const KeyboardControls: React.FC = () => { const theme = useTheme(); const groupsStyle = css({ - display: 'flex', - flexDirection: 'row' as const, - flexWrap: 'wrap', - justifyContent: 'center', + display: "flex", + flexDirection: "row" as const, + flexWrap: "wrap", + justifyContent: "center", ...(flexGapReplacementStyle(30, true)), }); @@ -145,10 +145,10 @@ const KeyboardControls: React.FC = () => { }; const keyboardControlsStyle = css({ - display: 'flex', - flexDirection: 'column' as const, - alignItems: 'center', - width: '100%', + display: "flex", + flexDirection: "column" as const, + alignItems: "center", + width: "100%", }); return ( diff --git a/src/main/Landing.tsx b/src/main/Landing.tsx index 9daff633e..e19b94b75 100644 --- a/src/main/Landing.tsx +++ b/src/main/Landing.tsx @@ -1,8 +1,8 @@ import React from "react"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; /** * This page is to be displayed when the application has run into a critical error @@ -13,22 +13,22 @@ const Landing: React.FC = () => { const { t } = useTranslation(); const landingStyle = css({ - height: '100%', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', + height: "100%", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", a: { - color: '#007bff', - textDecoration: 'none', + color: "#007bff", + textDecoration: "none", }, li: { - margin: '5px', + margin: "5px", }, code: { - userSelect: 'all', - color: '#e83e8c', + userSelect: "all", + color: "#e83e8c", } }); diff --git a/src/main/Lock.tsx b/src/main/Lock.tsx index 12f7aaf43..4a6596a07 100644 --- a/src/main/Lock.tsx +++ b/src/main/Lock.tsx @@ -4,11 +4,11 @@ import { LuLock } from "react-icons/lu"; import { useAppDispatch, useAppSelector } from "../redux/store"; import { settings } from "../config"; import { setLock, video } from "../redux/videoSlice"; -import { selectIsEnd } from '../redux/endSlice'; +import { selectIsEnd } from "../redux/endSlice"; import { setError } from "../redux/errorSlice"; import { client } from "../util/client"; import { useInterval } from "../util/utilityFunctions"; -import { useBeforeunload } from 'react-beforeunload'; +import { useBeforeunload } from "react-beforeunload"; const Lock: React.FC = () => { const endpoint = `${settings.opencast.url}/editor/${settings.id}/lock`; @@ -27,7 +27,7 @@ const Lock: React.FC = () => { const form = `user=${lock.user}&uuid=${lock.uuid}`; client.post(endpoint, form, { headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" } }) .then(() => dispatch(setLock(true))) @@ -37,17 +37,17 @@ const Lock: React.FC = () => { error: true, errorDetails: error, errorIcon: LuLock, - errorTitle: 'Video editing locked', - errorMessage: 'This video is currently being edited by another user' + errorTitle: "Video editing locked", + errorMessage: "This video is currently being edited by another user" })); }); } function releaseLock() { if (lockingActive && lockState) { - client.delete(endpoint + '/' + lock.uuid) + client.delete(endpoint + "/" + lock.uuid) .then(() => { - console.info('Lock released'); + console.info("Lock released"); dispatch(setLock(false)); }); } diff --git a/src/main/MainContent.tsx b/src/main/MainContent.tsx index 2eeab9116..878d1ab66 100644 --- a/src/main/MainContent.tsx +++ b/src/main/MainContent.tsx @@ -1,22 +1,22 @@ import React from "react"; -import Metadata from './Metadata'; -import TrackSelection from './TrackSelection'; +import Metadata from "./Metadata"; +import TrackSelection from "./TrackSelection"; import Subtitle from "./Subtitle"; import Finish from "./Finish"; import KeyboardControls from "./KeyboardControls"; import { LuWrench } from "react-icons/lu"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { useAppSelector } from "../redux/store"; -import { selectMainMenuState } from '../redux/mainMenuSlice'; +import { selectMainMenuState } from "../redux/mainMenuSlice"; -import { MainMenuStateNames } from '../types'; +import { MainMenuStateNames } from "../types"; import { flexGapReplacementStyle } from "../cssStyles"; -import { useBeforeunload } from 'react-beforeunload'; +import { useBeforeunload } from "react-beforeunload"; import { selectHasChanges as videoSelectHasChanges } from "../redux/videoSlice"; import { selectHasChanges as metadataSelectHasChanges } from "../redux/metadataSlice"; import { selectHasChanges as selectSubtitleHasChanges } from "../redux/subtitleSlice"; @@ -44,50 +44,50 @@ const MainContent: React.FC = () => { }); const mainContentStyle = css({ - display: 'flex', - width: '100%', - paddingRight: '20px', - paddingLeft: '20px', + display: "flex", + width: "100%", + paddingRight: "20px", + paddingLeft: "20px", ...(flexGapReplacementStyle(20, false)), background: `${theme.background}`, - overflow: 'auto', + overflow: "auto", }); const cuttingStyle = css({ - flexDirection: 'column', + flexDirection: "column", }); const metadataStyle = css({ }); const trackSelectStyle = css({ - flexDirection: 'column', - alignContent: 'space-around', + flexDirection: "column", + alignContent: "space-around", }); const subtitleSelectStyle = css({ - flexDirection: 'column', - justifyContent: 'space-around', + flexDirection: "column", + justifyContent: "space-around", }); const thumbnailSelectStyle = css({ - flexDirection: 'column', - alignContent: 'space-around', + flexDirection: "column", + alignContent: "space-around", }); const finishStyle = css({ - flexDirection: 'column', - justifyContent: 'space-around', + flexDirection: "column", + justifyContent: "space-around", }); const keyboardControlsStyle = css({ - flexDirection: 'column', + flexDirection: "column", }); const defaultStyle = css({ - flexDirection: 'column', - alignItems: 'center', - padding: '20px', + flexDirection: "column", + alignItems: "center", + padding: "20px", }); const render = () => { diff --git a/src/main/MainMenu.tsx b/src/main/MainMenu.tsx index e50f97b81..36d88a36d 100644 --- a/src/main/MainMenu.tsx +++ b/src/main/MainMenu.tsx @@ -1,22 +1,22 @@ import React from "react"; -import { css, SerializedStyles } from '@emotion/react'; +import { css, SerializedStyles } from "@emotion/react"; import { IconType } from "react-icons"; import { LuScissors, LuFilm, LuFileText, LuCheckSquare } from "react-icons/lu"; import { LuImage } from "react-icons/lu"; -import { ReactComponent as SubtitleIcon } from '../img/subtitle.svg'; +import { ReactComponent as SubtitleIcon } from "../img/subtitle.svg"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { setState, selectMainMenuState, mainMenu } from '../redux/mainMenuSlice'; -import { setPageNumber } from '../redux/finishSlice'; +import { setState, selectMainMenuState, mainMenu } from "../redux/mainMenuSlice"; +import { setPageNumber } from "../redux/finishSlice"; -import { MainMenuStateNames } from '../types'; -import { settings } from '../config'; -import { basicButtonStyle, flexGapReplacementStyle } from '../cssStyles'; +import { MainMenuStateNames } from "../types"; +import { settings } from "../config"; +import { basicButtonStyle, flexGapReplacementStyle } from "../cssStyles"; import { setIsPlaying } from "../redux/videoSlice"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { resetPostRequestState as metadataResetPostRequestState } from "../redux/metadataSlice"; import { resetPostRequestState } from "../redux/workflowPostSlice"; import { setIsDisplayEditView } from "../redux/subtitleSlice"; @@ -33,14 +33,14 @@ const MainMenu: React.FC = () => { const mainMenuStyle = css({ borderRight: `${theme.menuBorder}`, - minWidth: '120px', - maxWidth: '140px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: '20px', - overflowX: 'hidden', - overflowY: 'auto', + minWidth: "120px", + maxWidth: "140px", + display: "flex", + flexDirection: "column", + alignItems: "center", + padding: "20px", + overflowX: "hidden", + overflowY: "auto", background: `${theme.menu_background}`, ...(flexGapReplacementStyle(30, false)), }); @@ -130,20 +130,20 @@ export const MainMenuButton: React.FC = ({ }; const mainMenuButtonStyle = css({ - width: '100%', - height: '100px', + width: "100%", + height: "100px", outline: `${theme.menuButton_outline}`, ...(activeState === stateName) && { backgroundColor: `${theme.button_color}`, color: `${theme.inverted_text}`, boxShadow: `${theme.boxShadow}`, }, - '&:hover': { + "&:hover": { backgroundColor: `${theme.button_color}`, color: `${theme.inverted_text}`, boxShadow: `${theme.boxShadow}`, }, - flexDirection: 'column', + flexDirection: "column", }); return ( @@ -159,8 +159,8 @@ export const MainMenuButton: React.FC = ({ > {bottomText &&
{bottomText}
} diff --git a/src/main/Metadata.tsx b/src/main/Metadata.tsx index 67f1e66db..f7635361b 100644 --- a/src/main/Metadata.tsx +++ b/src/main/Metadata.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; -import { css } from '@emotion/react'; -import { calendarStyle, errorBoxStyle, selectFieldStyle, titleStyle, titleStyleBold } from '../cssStyles'; +import { css } from "@emotion/react"; +import { calendarStyle, errorBoxStyle, selectFieldStyle, titleStyle, titleStyleBold } from "../cssStyles"; import { useAppDispatch, useAppSelector } from "../redux/store"; import { @@ -16,16 +16,16 @@ import { selectPostError, selectPostStatus, setFieldReadonly -} from '../redux/metadataSlice'; +} from "../redux/metadataSlice"; -import { Form, Field, FieldInputProps } from 'react-final-form'; -import Select from 'react-select'; -import CreatableSelect from 'react-select/creatable'; +import { Form, Field, FieldInputProps } from "react-final-form"; +import Select from "react-select"; +import CreatableSelect from "react-select/creatable"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { DateTime as LuxonDateTime } from "luxon"; -import { configureFieldsAttributes, settings } from '../config'; +import { configureFieldsAttributes, settings } from "../config"; import { useTheme } from "../themes"; import { ThemeProvider } from "@mui/material/styles"; import { cloneDeep } from "lodash"; @@ -37,7 +37,7 @@ import { ParseKeys } from "i18next"; * Takes data from a redux slice and throws it into a react-final-form. * When submitting, the state in the redux slice gets updated * - * If something doesn't work, main places of interest are the submit function + * If something doesn"t work, main places of interest are the submit function * and the initialValues function */ const Metadata: React.FC = () => { @@ -55,14 +55,14 @@ const Metadata: React.FC = () => { // Try to fetch URL from external API useEffect(() => { - if (getStatus === 'idle') { + if (getStatus === "idle") { dispatch(fetchMetadata()); } }, [getStatus, dispatch]); // Overwrite readonly property of fields based on config settings useEffect(() => { - if (getStatus === 'success') { + if (getStatus === "success") { for (let catalogIndex = 0; catalogIndex < catalogs.length; catalogIndex++) { if (settings.metadata.configureFields) { const configureFields = settings.metadata.configureFields; @@ -96,45 +96,45 @@ const Metadata: React.FC = () => { */ const metadataStyle = css({ - padding: '20px', - marginLeft: 'auto', - marginRight: 'auto', - minWidth: '50%', - display: 'grid', + padding: "20px", + marginLeft: "auto", + marginRight: "auto", + minWidth: "50%", + display: "grid", }); const catalogStyle = css({ background: `${theme.menu_background}`, - borderRadius: '5px', + borderRadius: "5px", boxShadow: `${theme.boxShadow_tiles}`, - marginTop: '24px', + marginTop: "24px", boxSizing: "border-box", - padding: '10px', + padding: "10px", }); const fieldStyle = css({ - display: 'flex', - flexFlow: 'column nowrap', - lineHeight: '2em', - margin: '10px', + display: "flex", + flexFlow: "column nowrap", + lineHeight: "2em", + margin: "10px", }); const fieldLabelStyle = css({ - width: '110px', - fontSize: '1em', - fontWeight: 'bold', + width: "110px", + fontSize: "1em", + fontWeight: "bold", color: `${theme.text}`, - lineHeight: '32px', + lineHeight: "32px", }); const fieldTypeStyle = (isReadOnly: boolean) => { return css({ - fontSize: '1em', - borderRadius: '5px', - boxShadow: isReadOnly ? '0 0 0px rgba(0, 0, 0, 0.3)' : '0 0 1px rgba(0, 0, 0, 0.3)', + fontSize: "1em", + borderRadius: "5px", + boxShadow: isReadOnly ? "0 0 0px rgba(0, 0, 0, 0.3)" : "0 0 1px rgba(0, 0, 0, 0.3)", ...(isReadOnly && { color: `${theme.text}` }), color: `${theme.text}`, - outline: isReadOnly ? '0px solid transparent' : `${theme.element_outline}`, + outline: isReadOnly ? "0px solid transparent" : `${theme.element_outline}`, "&:hover": { borderColor: isReadOnly ? undefined : theme.metadata_highlight, }, @@ -147,62 +147,62 @@ const Metadata: React.FC = () => { const inputFieldTypeStyle = (isReadOnly: boolean) => { return ( css({ - padding: '10px 10px', - border: '1px solid #ccc', + padding: "10px 10px", + border: "1px solid #ccc", background: isReadOnly ? `${theme.background}` : `${theme.element_bg}`, opacity: isReadOnly ? "0.6" : "1", - resize: 'vertical', + resize: "vertical", }) ); }; const validateStyle = (isError: boolean) => { return css({ - lineHeight: '32px', - marginLeft: '10px', + lineHeight: "32px", + marginLeft: "10px", ...(isError && { color: `${theme.error}` }), - fontWeight: 'bold', + fontWeight: "bold", }); }; // const buttonContainerStyle = css({ - // display: 'flex', - // flexFlow: 'row nowrap', - // justifyContent: 'space-around', - // marginTop: '25px', + // display: "flex", + // flexFlow: "row nowrap", + // justifyContent: "space-around", + // marginTop: "25px", // }) - // // TODO: Rework all div buttons so the ':enabled' pseudo-class does not screw them over + // // TODO: Rework all div buttons so the ":enabled" pseudo-class does not screw them over // const basicButtonStyleCOPY = css({ - // borderRadius: '10px', + // borderRadius: "10px", // cursor: "pointer", // // Animation // transitionDuration: "0.3s", // transitionProperty: "transform", // "&:hover:enabled": { - // transform: 'scale(1.1)', + // transform: "scale(1.1)", // }, // "&:focus:enabled": { - // transform: 'scale(1.1)', + // transform: "scale(1.1)", // }, // "&:active:enabled": { - // transform: 'scale(0.9)', + // transform: "scale(0.9)", // }, // // Flex position child elements - // display: 'flex', - // justifyContent: 'center', - // alignItems: 'center', - // gap: '10px', - // textAlign: 'center' as const, + // display: "flex", + // justifyContent: "center", + // alignItems: "center", + // gap: "10px", + // textAlign: "center" as const, // }); // const submitButtonStyle = css({ - // background: 'snow', - // border: '1px solid #ccc', + // background: "snow", + // border: "1px solid #ccc", // "&[disabled]": { - // opacity: '0.6', - // cursor: 'not-allowed', + // opacity: "0.6", + // cursor: "not-allowed", // }, // }) @@ -292,10 +292,10 @@ const Metadata: React.FC = () => { } let dt = undefined; - if (Object.prototype.toString.call(date) === '[object Date]') { + if (Object.prototype.toString.call(date) === "[object Date]") { dt = LuxonDateTime.fromJSDate(date); } - if (typeof date === 'string') { + if (typeof date === "string") { dt = LuxonDateTime.fromISO(date); } @@ -311,7 +311,7 @@ const Metadata: React.FC = () => { /** * Returns the desired combination of validators for a given field - * TODO: Fix 'composeValidators' so this function can actually work as advertised + * TODO: Fix "composeValidators" so this function can actually work as advertised * @param field */ const getValidators = (field: MetadataField) => { @@ -389,9 +389,9 @@ const Metadata: React.FC = () => { } // If the value is hidden an object due to react-select, extract it - if (typeof value === 'object' && value !== null && Object.prototype.hasOwnProperty.call(value, "submitValue")) { + if (typeof value === "object" && value !== null && Object.prototype.hasOwnProperty.call(value, "submitValue")) { returnValue = value.submitValue; - } else if (typeof value === 'object' && value !== null && value.__isNew__) { + } else if (typeof value === "object" && value !== null && value.__isNew__) { returnValue = value.value; } @@ -402,7 +402,7 @@ const Metadata: React.FC = () => { // For these fields, the value needs to be inside an array if (field && (field.type === "date" || field.type === "time") && - Object.prototype.toString.call(returnValue) === '[object Date]') { + Object.prototype.toString.call(returnValue) === "[object Date]") { // If invalid date if ((isNaN(returnValue.getTime()))) { // Do nothing @@ -459,7 +459,7 @@ const Metadata: React.FC = () => { */ const generateReactSelectLibrary = (field: MetadataField) => { if (field.collection) { - // For whatever reason react-select uses 'value' as their key, which is not at all confusing + // For whatever reason react-select uses "value" as their key, which is not at all confusing const library: [{ value: any, label: any, submitValue: any; }] = [{ value: "", label: "No value", submitValue: "" }]; Object.entries(field.collection).forEach(([key, value]) => { @@ -545,7 +545,7 @@ const Metadata: React.FC = () => { readOnly={field.readOnly} css={[fieldTypeStyle(field.readOnly), inputFieldTypeStyle(field.readOnly), { - resize: 'none', + resize: "none", } ]} data-testid="dateTimePicker" @@ -563,7 +563,7 @@ const Metadata: React.FC = () => { readOnly={field.readOnly} css={[fieldTypeStyle(field.readOnly), inputFieldTypeStyle(field.readOnly), { - resize: 'none', + resize: "none", } ]} /> @@ -599,7 +599,7 @@ const Metadata: React.FC = () => { /** * Wrapper function for component generation. * Handles the special case of DateTimePicker/TimePicker, which - * can't handle empty string as a value (which is what Opencast uses to + * can"t handle empty string as a value (which is what Opencast uses to * represent no date/time) */ const generateComponentWithModifiedInput = (field: MetadataField, input: FieldInputProps) => { @@ -607,7 +607,7 @@ const Metadata: React.FC = () => { const { value, ...other } = input; return generateComponent(field, other); } - // is picky about its value and won't accept + // is picky about its value and won"t accept // global datetime strings, so we have to convert them to local ourselves. // TODO: Also we really should not be modifying the input element like that // so ideally the conversion happens somewhere else in the code @@ -627,7 +627,7 @@ const Metadata: React.FC = () => { {({ input, meta }) => ( diff --git a/src/main/Save.tsx b/src/main/Save.tsx index 005899aa8..c520e0005 100644 --- a/src/main/Save.tsx +++ b/src/main/Save.tsx @@ -1,26 +1,26 @@ import React, { useEffect, useState } from "react"; -import { css } from '@emotion/react'; +import { css } from "@emotion/react"; import { basicButtonStyle, backOrContinueStyle, ariaLive, errorBoxStyle, navigationButtonStyle, flexGapReplacementStyle, spinningStyle -} from '../cssStyles'; +} from "../cssStyles"; import { LuLoader, LuCheckCircle, LuAlertCircle, LuChevronLeft, LuSave, LuCheck } from "react-icons/lu"; import { useAppDispatch, useAppSelector } from "../redux/store"; -import { selectFinishState } from '../redux/finishSlice'; +import { selectFinishState } from "../redux/finishSlice"; import { selectHasChanges, selectSegments, selectTracks, setHasChanges as videoSetHasChanges -} from '../redux/videoSlice'; -import { postVideoInformation, selectStatus, selectError } from '../redux/workflowPostSlice'; +} from "../redux/videoSlice"; +import { postVideoInformation, selectStatus, selectError } from "../redux/workflowPostSlice"; -import { CallbackButton, PageButton } from './Finish'; +import { CallbackButton, PageButton } from "./Finish"; -import { useTranslation } from 'react-i18next'; +import { useTranslation } from "react-i18next"; import { postMetadata, selectPostError, selectPostStatus, setHasChanges as metadataSetHasChanges, selectHasChanges as metadataSelectHasChanges @@ -53,16 +53,16 @@ const Save: React.FC = () => { const subtitleHasChanges = useAppSelector(selectSubtitleHasChanges); const saveStyle = css({ - height: '100%', - display: finishState !== "Save changes" ? 'none' : 'flex', - flexDirection: 'column' as const, - alignItems: 'center', + height: "100%", + display: finishState !== "Save changes" ? "none" : "flex", + flexDirection: "column" as const, + alignItems: "center", ...(flexGapReplacementStyle(30, false)), }); const render = () => { // Post (successful) save - if (postWorkflowStatus === 'success' && postMetadataStatus === 'success' + if (postWorkflowStatus === "success" && postMetadataStatus === "success" && !hasChanges && !metadataHasChanges && !subtitleHasChanges) { return ( <> @@ -75,7 +75,7 @@ const Save: React.FC = () => { } else { return ( <> - + {t("save.info-text")}
@@ -127,22 +127,22 @@ export const SaveButton: React.FC = () => { let Icon = LuSave; let spin = false; let tooltip = null; - if (workflowStatus === 'failed' || metadataStatus === 'failed') { + if (workflowStatus === "failed" || metadataStatus === "failed") { Icon = LuAlertCircle; spin = false; tooltip = t("save.confirmButton-failed-tooltip"); - } else if (workflowStatus === 'success' && metadataStatus === 'success') { + } else if (workflowStatus === "success" && metadataStatus === "success") { Icon = LuCheck; spin = false; tooltip = t("save.confirmButton-success-tooltip"); - } else if (workflowStatus === 'loading' || metadataStatus === 'loading') { + } else if (workflowStatus === "loading" || metadataStatus === "loading") { Icon = LuLoader; spin = true; tooltip = t("save.confirmButton-attempting-tooltip"); } const ariaSaveUpdate = () => { - if (workflowStatus === 'success') { + if (workflowStatus === "success") { return t("save.success-tooltip-aria"); } }; @@ -170,7 +170,7 @@ export const SaveButton: React.FC = () => { // Subsequent save request useEffect(() => { - if (metadataStatus === 'success' && metadataSaveStarted) { + if (metadataStatus === "success" && metadataSaveStarted) { setMetadataSaveStarted(false); dispatch(postVideoInformation({ segments: segments, @@ -184,7 +184,7 @@ export const SaveButton: React.FC = () => { // Let users leave the page without warning after a successful save useEffect(() => { - if (workflowStatus === 'success' && metadataStatus === 'success') { + if (workflowStatus === "success" && metadataStatus === "success") { dispatch(videoSetHasChanges(false)); dispatch(metadataSetHasChanges(false)); dispatch(subtitleSetHasChanges(false)); diff --git a/src/main/SubtitleEditor.tsx b/src/main/SubtitleEditor.tsx index 80d478273..6fd815999 100644 --- a/src/main/SubtitleEditor.tsx +++ b/src/main/SubtitleEditor.tsx @@ -4,7 +4,7 @@ import { basicButtonStyle, flexGapReplacementStyle } from "../cssStyles"; import { LuChevronLeft, LuDownload } from "react-icons/lu"; import { selectSubtitlesFromOpencastById, -} from '../redux/videoSlice'; +} from "../redux/videoSlice"; import { useAppDispatch, useAppSelector } from "../redux/store"; import SubtitleListEditor from "./SubtitleListEditor"; import { @@ -12,7 +12,7 @@ import { selectSelectedSubtitleById, selectSelectedSubtitleId, setSubtitle -} from '../redux/subtitleSlice'; +} from "../redux/subtitleSlice"; import SubtitleVideoArea from "./SubtitleVideoArea"; import SubtitleTimeline from "./SubtitleTimeline"; import { useTranslation } from "react-i18next"; @@ -70,31 +70,31 @@ const SubtitleEditor: React.FC = () => { }; const subtitleEditorStyle = css({ - display: 'flex', - flexDirection: 'column', - paddingRight: '20px', - paddingLeft: '20px', - gap: '20px', - height: '100%', + display: "flex", + flexDirection: "column", + paddingRight: "20px", + paddingLeft: "20px", + gap: "20px", + height: "100%", }); const headerRowStyle = css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + width: "100%", }); const subAreaStyle = css({ - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", flexGrow: 1, // No fixed height, fill available space - justifyContent: 'space-between', - alignItems: 'top', - width: '100%', - paddingTop: '10px', - paddingBottom: '10px', + justifyContent: "space-between", + alignItems: "top", + width: "100%", + paddingTop: "10px", + paddingBottom: "10px", ...(flexGapReplacementStyle(30, true)), borderBottom: `${theme.menuBorder}` }); @@ -138,24 +138,24 @@ const DownloadButton: React.FC = () => { const downloadSubtitles = () => { - const vttFile = new Blob([serializeSubtitle(subtitle.cues)], { type: 'text/vtt' }); + const vttFile = new Blob([serializeSubtitle(subtitle.cues)], { type: "text/vtt" }); const vttFileLink = window.URL.createObjectURL(vttFile); - const vttHyperLink = document.createElement('a'); - vttHyperLink.setAttribute('href', vttFileLink); + const vttHyperLink = document.createElement("a"); + vttHyperLink.setAttribute("href", vttFileLink); const vttFileName = generateButtonTitle(subtitle.tags, t).trimEnd(); - vttHyperLink.setAttribute('download', `${vttFileName}.vtt`); + vttHyperLink.setAttribute("download", `${vttFileName}.vtt`); vttHyperLink.click(); }; const { t } = useTranslation(); const theme = useTheme(); const style = css({ - fontSize: '16px', - height: '10px', - padding: '16px', - justifyContent: 'space-around', + fontSize: "16px", + height: "10px", + padding: "16px", + justifyContent: "space-around", boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, }); @@ -166,7 +166,7 @@ const DownloadButton: React.FC = () => { role="button" onClick={() => downloadSubtitles()} > - + {t("subtitles.downloadButton-title")}
@@ -184,11 +184,11 @@ export const BackButton: React.FC = () => { const dispatch = useAppDispatch(); const backButtonStyle = css({ - height: '10px', - padding: '16px', + height: "10px", + padding: "16px", boxShadow: `${theme.boxShadow}`, background: `${theme.element_bg}`, - justifyContent: 'space-around' + justifyContent: "space-around" }); return ( diff --git a/src/main/SubtitleListEditor.tsx b/src/main/SubtitleListEditor.tsx index 6ccac5495..b27dcb1a1 100644 --- a/src/main/SubtitleListEditor.tsx +++ b/src/main/SubtitleListEditor.tsx @@ -86,26 +86,26 @@ const SubtitleListEditor: React.FC = () => { }, [dispatch, subtitle?.cues, subtitleId]); const listStyle = css({ - display: 'flex', - flexDirection: 'column', - height: '100%', - width: '60%', + display: "flex", + flexDirection: "column", + height: "100%", + width: "60%", ...(flexGapReplacementStyle(20, false)), }); // Old CSS for not yet implemented buttons // const headerStyle = css({ - // display: 'flex', - // flexDirection: 'row', - // justifyContent: 'flex-end', - // flexWrap: 'wrap', + // display: "flex", + // flexDirection: "row", + // justifyContent: "flex-end", + // flexWrap: "wrap", // ...(flexGapReplacementStyle(20, false)), - // paddingRight: '20px', + // paddingRight: "20px", // }) // const cuttingActionButtonStyle = { - // padding: '16px', - // boxShadow: '0 0 10px rgba(0, 0, 0, 0.3)', + // padding: "16px", + // boxShadow: "0 0 10px rgba(0, 0, 0, 0.3)", // }; const calcEstimatedSize = React.useCallback(() => { @@ -159,8 +159,8 @@ const innerElementType = React.forwardRef @@ -286,28 +286,28 @@ const SubtitleListSegment = React.memo((props: subtitleListSegmentProps) => { KEYMAP.subtitleList.jumpBelow.key, KEYMAP.subtitleList.delete.key ], (_, handler) => { - switch (handler.keys?.join('')) { - case KEYMAP.subtitleList.addAbove.key.split('+').pop(): + switch (handler.keys?.join("")) { + case KEYMAP.subtitleList.addAbove.key.split("+").pop(): addCueAbove(); break; - case KEYMAP.subtitleList.addBelow.key.split('+').pop(): + case KEYMAP.subtitleList.addBelow.key.split("+").pop(): addCueBelow(); break; - case KEYMAP.subtitleList.jumpAbove.key.split('+').pop(): + case KEYMAP.subtitleList.jumpAbove.key.split("+").pop(): dispatch(setFocusSegmentTriggered(true)); dispatch(setFocusToSegmentAboveId({ identifier: identifier, segmentId: cue.idInternal })); break; - case KEYMAP.subtitleList.jumpBelow.key.split('+').pop(): + case KEYMAP.subtitleList.jumpBelow.key.split("+").pop(): dispatch(setFocusSegmentTriggered(true)); dispatch(setFocusToSegmentBelowId({ identifier: identifier, segmentId: cue.idInternal })); break; - case KEYMAP.subtitleList.delete.key.split('+').pop(): + case KEYMAP.subtitleList.delete.key.split("+").pop(): dispatch(setFocusSegmentTriggered(true)); dispatch(setFocusToSegmentAboveId({ identifier: identifier, segmentId: cue.idInternal })); deleteCue(); break; } - }, { enableOnFormTags: ['input', 'select', 'textarea'] }, [identifier, cue, props.index]); + }, { enableOnFormTags: ["input", "select", "textarea"] }, [identifier, cue, props.index]); const setTimeToSegmentStart = () => { dispatch(setCurrentlyAt(cue.startTime)); @@ -316,10 +316,10 @@ const SubtitleListSegment = React.memo((props: subtitleListSegmentProps) => { const { scheme } = useColorScheme(); const segmentStyle = css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-around', - alignItems: 'center', + display: "flex", + flexDirection: "row", + justifyContent: "space-around", + alignItems: "center", ...(flexGapReplacementStyle(20, false)), // Make function buttons visible when hovered or focused "&:hover": { @@ -332,55 +332,55 @@ const SubtitleListSegment = React.memo((props: subtitleListSegmentProps) => { visibility: "visible", } }, - '& textarea, input': { + "& textarea, input": { outline: `${theme.element_outline}`, }, - '& input': { - marginTop: (scheme === 'dark-high-contrast' || scheme === 'light-high-contrast' ? '3%' : '0%'), - marginBottom: (scheme === 'dark-high-contrast' || scheme === 'light-high-contrast' ? '3%' : '0%'), + "& input": { + marginTop: (scheme === "dark-high-contrast" || scheme === "light-high-contrast" ? "3%" : "0%"), + marginBottom: (scheme === "dark-high-contrast" || scheme === "light-high-contrast" ? "3%" : "0%"), } }); const timeAreaStyle = css({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - height: '100%', + display: "flex", + flexDirection: "column", + justifyContent: "space-between", + height: "100%", }); const functionButtonAreaStyle = css({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - alignItems: 'center', + display: "flex", + flexDirection: "column", + justifyContent: "space-between", + alignItems: "center", ...(flexGapReplacementStyle(10, false)), - flexGrow: '0.5', - minWidth: '20px', + flexGrow: "0.5", + minWidth: "20px", // Hackily moves buttons beyond the segment border. // Specific value causes buttons from neighboring segments to overlay. - height: '132px', - visibility: 'hidden', + height: "132px", + visibility: "hidden", }); const fieldStyle = css({ - fontSize: '1em', - marginLeft: '15px', - borderRadius: '5px', - borderWidth: '1px', - padding: '10px 10px', + fontSize: "1em", + marginLeft: "15px", + borderRadius: "5px", + borderWidth: "1px", + padding: "10px 10px", background: `${theme.element_bg}`, - border: '1px solid #ccc', + border: "1px solid #ccc", color: `${theme.text}` }); const textFieldStyle = css({ - flexGrow: '7', - height: '80%', - minWidth: '100px', + flexGrow: "7", + height: "80%", + minWidth: "100px", // TODO: Find a way to allow resizing without breaking the UI // Manual or automatic resizing can cause neighboring textareas to overlap // Can use TextareaAutosize from mui, but that does not fix the overlap problem - resize: 'none', + resize: "none", }); return ( @@ -388,10 +388,10 @@ const SubtitleListSegment = React.memo((props: subtitleListSegmentProps) => { ...props.style, // Used for padding in the VariableSizeList top: props.style.top !== undefined ? - `${parseFloat(props.style.top.toString()) + PADDING_SIZE}px` : '0px', + `${parseFloat(props.style.top.toString()) + PADDING_SIZE}px` : "0px", height: props.style.height !== undefined ? - `${parseFloat(props.style.height.toString()) - PADDING_SIZE}px` : '0px', - zIndex: '1000', + `${parseFloat(props.style.height.toString()) - PADDING_SIZE}px` : "0px", + zIndex: "1000", }]}>