Skip to content

Commit

Permalink
Change styling of input component
Browse files Browse the repository at this point in the history
  • Loading branch information
owi92 committed Sep 25, 2023
1 parent 7d7bd5c commit f2a4a5d
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 25 deletions.
6 changes: 3 additions & 3 deletions frontend/src/routes/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ const ShareButton: React.FC<{ event: SyncedEvent }> = ({ event }) => {

const { t } = useTranslation();
const [menuState, setMenuState] = useState<MenuState>("closed");
const [timestamp, setTimestamp] = useState<number>(0);
const [timestamp, setTimestamp] = useState(0);
const [addLinkTimestamp, setAddLinkTimestamp] = useState(false);
const [addEmbedTimestamp, setAddEmbedTimestamp] = useState(false);
const isDark = useColorScheme().scheme === "dark";
Expand Down Expand Up @@ -663,7 +663,7 @@ const ShareButton: React.FC<{ event: SyncedEvent }> = ({ event }) => {
<div>
<CopyableInput
label={t("manage.my-videos.details.copy-direct-link-to-clipboard")}
css={{ fontSize: 14, width: 400 }}
css={{ fontSize: 14, width: 400, marginBottom: 6 }}
value={url}
/>
<InputWithCheckbox
Expand Down Expand Up @@ -707,7 +707,7 @@ const ShareButton: React.FC<{ event: SyncedEvent }> = ({ event }) => {
label={t("video.embed.copy-embed-code-to-clipboard")}
value={embedCode}
multiline
css={{ fontSize: 14, width: 400, height: 75 }}
css={{ fontSize: 14, width: 400, height: 75, marginBottom: 6 }}
/>
<InputWithCheckbox
checkboxChecked={addEmbedTimestamp}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/routes/manage/Video/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const Page: React.FC<Props> = ({ event }) => {

const DirectLink: React.FC<Props> = ({ event }) => {
const { t } = useTranslation();
const [timestamp, setTimestamp] = useState<number>(0);
const [timestamp, setTimestamp] = useState(0);
const [checkboxChecked, setCheckboxChecked] = useState(false);

let url = new URL(`/!v/${event.id.slice(2)}`, document.baseURI);
Expand All @@ -92,7 +92,7 @@ const DirectLink: React.FC<Props> = ({ event }) => {
<CopyableInput
label={t("manage.my-videos.details.copy-direct-link-to-clipboard")}
value={url.href}
css={{ width: "100%", fontSize: 14 }}
css={{ width: "100%", fontSize: 14, marginBottom: 6 }}
/>
<InputWithCheckbox
{...{ checkboxChecked, setCheckboxChecked }}
Expand Down
46 changes: 29 additions & 17 deletions frontend/src/ui/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import React, { Fragment, ReactNode, useId, useState } from "react";
import { FiCheck, FiCopy } from "react-icons/fi";
import { WithTooltip } from "@opencast/appkit";

import { focusStyle } from ".";
import { Button } from "./Button";
import { COLORS } from "../color";
import { secondsToTimeString, timeStringToSeconds } from "../util";
import { focusStyle } from ".";


const style = (error: boolean) => ({
Expand Down Expand Up @@ -65,9 +66,13 @@ export const InputWithCheckbox: React.FC<InputWithCheckboxProps> = (
type="checkbox"
checked={checkboxChecked}
onChange={() => setCheckboxChecked(!checkboxChecked)}
css={{ margin: "0 4px" }}
css={{ marginRight: 8 }}
/>
<label css={{ color: COLORS.neutral90, fontSize: 14 }}>
<label css={{
color: checkboxChecked ? COLORS.neutral90 : COLORS.neutral70,
fontSize: 14,
marginRight: 6,
}}>
{label}
</label>
{input}
Expand All @@ -79,6 +84,8 @@ type TimeInputProps = {
disabled: boolean;
}

export type TimeUnit = "h" | "m" | "s";

/** A custom three-part input for time inputs split into hours, minutes and seconds */
export const TimeInput: React.FC<TimeInputProps> = ({ timestamp, setTimestamp, disabled }) => {
const timeParts = (/(\d+h)?(\d+m)?(\d+s)?/)
Expand All @@ -88,52 +95,57 @@ export const TimeInput: React.FC<TimeInputProps> = ({ timestamp, setTimestamp, d
.map(part => part ? parseInt(part.replace(/\D/g, "")) : 0);

const handleTimeChange = (newValue: number, type: TimeUnit) => {
if (isNaN(newValue)) {
if (isNaN(newValue) || newValue < 0 || newValue > 99) {
return;
}

const cappedValue = Math.min(newValue, 59);
const newTimestamp = `${type === "h" ? cappedValue : hours}h`
const timeString = `${type === "h" ? cappedValue : hours}h`
+ `${type === "m" ? cappedValue : minutes}m`
+ `${type === "s" ? cappedValue : seconds}s`;

setTimestamp(timeStringToSeconds(timeString));
};

type TimeUnit = "h" | "m" | "s";
const entries: [number, TimeUnit][] = [
[hours, "h"],
[minutes, "m"],
[seconds, "s"],
];

return (
<div css={{ color: disabled ? COLORS.neutral70 : COLORS.neutral90 }}>
<div css={{
color: disabled ? COLORS.neutral70 : COLORS.neutral90,
fontSize: 14,
borderRadius: 4,
padding: "0 2px",
":focus-within": {
outline: `2.5px solid ${COLORS.focus}`,
},
...!disabled && {
outline: `1px solid ${COLORS.neutral20}`,
},
}}>
{entries.map(([time, unit]) => <Fragment key={`${unit}-input`}>
<input
{...{ disabled }}
value={time}
maxLength={2}
inputMode="numeric"
onChange={e => handleTimeChange(Number(e.target.value), unit)}
css={{
width: time > 9 ? 24 : "2ch",
width: time > 9 ? "2ch" : "1ch",
lineHeight: 1,
padding: 0,
border: 0,
textAlign: "center",
borderRadius: 4,
outline: `1px solid ${COLORS.neutral20}`,
outlineOffset: "-2px",
outline: "none",
backgroundColor: "transparent",
userSelect: "all",
...focusStyle({ inset: true }),
":disabled": {
textAlign: "right",
backgroundColor: "transparent",
outline: "none",
},
}}
/>
<span>{unit}</span>
<span css={{ marginRight: 1 }}>{unit}</span>
</Fragment>)}
</div>
);
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { bug } from "@opencast/appkit";

import CONFIG, { TranslatedString } from "../config";
import { TimeUnit } from "../ui/Input";


/** Retrieves the key of an ID by stripping the "kind" prefix. */
Expand Down Expand Up @@ -180,9 +181,12 @@ export const timeStringToSeconds = (timeString: string): number => {
* e.g. "0h2m4s".
*/
export const secondsToTimeString = (seconds: number): string => {
const hours = Math.floor(seconds / 3600) + "h";
const minutes = Math.floor((seconds % 3600) / 60) + "m";
const remainingSeconds = Math.floor(seconds % 60) + "s";
const formatTime = (time: number, unit: TimeUnit): string =>
time > 0 ? time.toString().padStart(2, "0") + unit : "";

const hours = formatTime(Math.floor(seconds / 3600), "h");
const minutes = formatTime(Math.floor((seconds % 3600) / 60), "m");
const remainingSeconds = formatTime(Math.floor(seconds % 60), "s");

return hours + minutes + remainingSeconds;
};
Expand Down

0 comments on commit f2a4a5d

Please sign in to comment.