refactor: improve section style and composition

This commit is contained in:
Carlos Valente
2024-12-18 19:48:17 +01:00
committed by Carlos Valente
parent 2f2c26ed00
commit 6199d0ad10
12 changed files with 128 additions and 80 deletions
@@ -2,6 +2,8 @@ $input-delayed-border-color: $ontime-delay-text;
.timeInput { .timeInput {
border: 1px solid transparent; border: 1px solid transparent;
color: $label-gray;
&.delayed { &.delayed {
border: 1px solid $input-delayed-border-color; border: 1px solid $input-delayed-border-color;
} }
@@ -8,7 +8,7 @@
color: $ui-white; color: $ui-white;
transition-property: color; transition-property: color;
transition-duration: $transition-time-action; transition-duration: $transition-time-action;
border-radius: 99px; border-radius: $component-border-radius-full;
// similar styles to ontime-button-subtle // similar styles to ontime-button-subtle
background-color: $gray-1050; background-color: $gray-1050;
@@ -27,13 +27,16 @@
.title { .title {
font-size: 1rem; font-size: 1rem;
color: $ui-white; color: $title-gray;
display: flex;
align-items: center;
justify-content: space-between;
} }
.label { .label {
display: block; display: block;
font-size: calc(1rem - 3px); font-size: $aux-text-size;
color: $label-gray; color: $label-gray;
margin-bottom: 0.25rem; margin-bottom: $element-inner-spacing;
max-width: max-content; // prevent label from taking entire row max-width: max-content; // prevent label from taking entire row
} }
@@ -1,4 +1,4 @@
import type { LabelHTMLAttributes, ReactNode } from 'react'; import type { HTMLAttributes, LabelHTMLAttributes } from 'react';
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp'; import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
import { type IconBaseProps } from '@react-icons/all-files/lib'; import { type IconBaseProps } from '@react-icons/all-files/lib';
@@ -10,8 +10,13 @@ export function Corner({ className, ...elementProps }: IconBaseProps) {
return <IoArrowUp className={cx([style.corner, className])} {...elementProps} />; return <IoArrowUp className={cx([style.corner, className])} {...elementProps} />;
} }
export function Title({ children }: { children: ReactNode }) { export function Title({ children, className, ...elementProps }: HTMLAttributes<HTMLHeadingElement>) {
return <h3 className={style.title}>{children}</h3>; const classes = cx([style.title, className]);
return (
<h3 className={classes} {...elementProps}>
{children}
</h3>
);
} }
export function Label({ children, className, ...elementProps }: LabelHTMLAttributes<HTMLLabelElement>) { export function Label({ children, className, ...elementProps }: LabelHTMLAttributes<HTMLLabelElement>) {
@@ -36,7 +36,7 @@
.side { .side {
max-height: 100%; max-height: 100%;
max-width: 45rem; max-width: 45rem;
margin: 2rem 0; margin: 0.5rem 0;
padding: 1rem; padding: 1rem;
padding-right: 0; padding-right: 0;
background-color: $gray-1325; background-color: $gray-1325;
@@ -1,8 +1,7 @@
.eventEditor { .eventEditor {
color: $label-gray;
max-height: 100%; max-height: 100%;
overflow-y: auto; overflow-y: auto;
padding: 0.5rem; padding-inline: 0.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -16,7 +15,7 @@
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1.5rem;
overflow-y: auto; overflow-y: auto;
} }
@@ -30,19 +29,22 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
margin-top: 0.5rem;
h3 {
margin-bottom: -0.5rem; // bring the title closer to the section elements
}
} }
.decorated { .decorated {
color: var(--decorator-color, $ui-white); color: var(--decorator-color, $ui-white);
background-color: var(--decorator-bg, $gray-1100); background-color: var(--decorator-bg, $gray-1100);
width: fit-content; width: fit-content;
padding: 0 0.5rem; padding-inline: 0.5rem;
border-radius: 2px; border-radius: $component-border-radius-sm;
} }
.delayLabel { .delayLabel {
font-size: calc(1rem - 3px); font-size: $aux-text-size;
color: $ontime-delay-text; color: $ontime-delay-text;
&::after { &::after {
@@ -56,7 +58,8 @@
gap: 0.5rem; gap: 0.5rem;
max-width: max-content; max-width: max-content;
cursor: pointer; cursor: pointer;
height: 30px; // manually match the height of a text input height: 2rem; // manually match the height of a text input
margin-bottom: 0; // reset margin from label component
} }
.inline { .inline {
@@ -68,6 +71,13 @@
.splitTwo { .splitTwo {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
column-gap: 1.5rem; column-gap: 1rem;
row-gap: 1rem; row-gap: 1rem;
} }
.tooltipIcon {
color: $blue-500;
display: inline-block;
font-size: 1.25em;
margin-left: 0.25em;
}
@@ -79,14 +79,14 @@ export default function EventEditor(props: EventEditorProps) {
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
/> />
<div className={style.column}> <div className={style.column}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <Editor.Title>
<Editor.Title>Custom Fields</Editor.Title> Custom Fields
{isEditor && ( {isEditor && (
<Button variant='ontime-subtle' size='sm' onClick={handleOpenCustomManager}> <Button variant='ontime-subtle' size='sm' onClick={handleOpenCustomManager}>
Manage Manage
</Button> </Button>
)} )}
</div> </Editor.Title>
{Object.keys(customFields).map((fieldKey) => { {Object.keys(customFields).map((fieldKey) => {
const key = `${event.id}-${fieldKey}`; const key = `${event.id}-${fieldKey}`;
const fieldName = `custom-${fieldKey}`; const fieldName = `custom-${fieldKey}`;
@@ -38,8 +38,6 @@
} }
.prompt { .prompt {
font-size: 1rem;
text-align: left;
margin-left: 4rem; margin-left: 4rem;
} }
@@ -1,7 +1,8 @@
import { memo } from 'react'; import { memo, PropsWithChildren } from 'react';
import { Kbd } from '@chakra-ui/react'; import { Kbd } from '@chakra-ui/react';
import { deviceAlt, deviceMod } from '../../../common/utils/deviceUtils'; import { deviceAlt, deviceMod } from '../../../common/utils/deviceUtils';
import * as Editor from '../../editors/editor-utils/EditorUtils';
import style from './EventEditorEmpty.module.scss'; import style from './EventEditorEmpty.module.scss';
@@ -11,7 +12,7 @@ function EventEditorEmpty() {
return ( return (
<div className={style.eventEditor} data-testid='editor-container'> <div className={style.eventEditor} data-testid='editor-container'>
<div className={style.shortcutSection}> <div className={style.shortcutSection}>
<div className={style.prompt}>Rundown shortcuts:</div> <Editor.Title className={style.prompt}>Rundown shortcuts</Editor.Title>
<table className={style.shortcuts}> <table className={style.shortcuts}>
<tbody> <tbody>
<tr> <tr>
@@ -168,6 +169,6 @@ function EventEditorEmpty() {
); );
} }
function AuxKey({ children }: { children: React.ReactNode }) { function AuxKey({ children }: PropsWithChildren) {
return <span className={style.divider}>{children}</span>; return <span className={style.divider}>{children}</span>;
} }
@@ -1,5 +1,6 @@
import { memo } from 'react'; import { memo } from 'react';
import { Select, Switch } from '@chakra-ui/react'; import { Select, Switch, Tooltip } from '@chakra-ui/react';
import { IoInformationCircle } from '@react-icons/all-files/io5/IoInformationCircle';
import { EndAction, MaybeString, TimerType, TimeStrategy } from 'ontime-types'; import { EndAction, MaybeString, TimerType, TimeStrategy } from 'ontime-types';
import { millisToString, parseUserTime } from 'ontime-utils'; import { millisToString, parseUserTime } from 'ontime-utils';
@@ -80,8 +81,8 @@ function EventEditorTimes(props: EventEditorTimesProps) {
return ( return (
<> <>
<div className={style.column}> <div className={style.column}>
<Editor.Title>Event schedule</Editor.Title>
<div> <div>
<Editor.Label>Event schedule</Editor.Label>
<div className={style.inline}> <div className={style.inline}>
<TimeInputFlow <TimeInputFlow
eventId={eventId} eventId={eventId}
@@ -92,11 +93,15 @@ function EventEditorTimes(props: EventEditorTimesProps) {
linkStart={linkStart} linkStart={linkStart}
delay={delay} delay={delay}
countToEnd={countToEnd} countToEnd={countToEnd}
showLabels
/> />
</div> </div>
<div className={style.delayLabel}>{delayLabel}</div> <div className={style.delayLabel}>{delayLabel}</div>
</div> </div>
</div>
<div className={style.column}>
<Editor.Title>Event Behaviour</Editor.Title>
<div className={style.splitTwo}> <div className={style.splitTwo}>
<div> <div>
<Editor.Label htmlFor='endAction'>End Action</Editor.Label> <Editor.Label htmlFor='endAction'>End Action</Editor.Label>
@@ -129,7 +134,16 @@ function EventEditorTimes(props: EventEditorTimesProps) {
</div> </div>
</div> </div>
</div> </div>
<div className={style.column}> <div className={style.column}>
<Editor.Title>
<Tooltip label='Changes how the timer is displayed in different views. It is not reflected in the rundown'>
<span>
Display Options
<IoInformationCircle className={style.tooltipIcon} />
</span>
</Tooltip>
</Editor.Title>
<div className={style.splitTwo}> <div className={style.splitTwo}>
<div> <div>
<Editor.Label htmlFor='timerType'>Timer Type</Editor.Label> <Editor.Label htmlFor='timerType'>Timer Type</Editor.Label>
@@ -29,6 +29,7 @@ const EventEditorTitles = (props: EventEditorTitlesProps) => {
return ( return (
<div className={style.column}> <div className={style.column}>
<Editor.Title>Event Data</Editor.Title>
<div className={style.splitTwo}> <div className={style.splitTwo}>
<div> <div>
<Editor.Label htmlFor='eventId'>Event ID (read only)</Editor.Label> <Editor.Label htmlFor='eventId'>Event ID (read only)</Editor.Label>
@@ -11,6 +11,7 @@ import TimeInputWithButton from '../../../common/components/input/time-input/Tim
import { useEventAction } from '../../../common/hooks/useEventAction'; import { useEventAction } from '../../../common/hooks/useEventAction';
import { cx } from '../../../common/utils/styleUtils'; import { cx } from '../../../common/utils/styleUtils';
import { tooltipDelayFast, tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayFast, tooltipDelayMid } from '../../../ontimeConfig';
import * as Editor from '../../editors/editor-utils/EditorUtils';
import style from './TimeInputFlow.module.scss'; import style from './TimeInputFlow.module.scss';
@@ -23,10 +24,11 @@ interface EventBlockTimerProps {
timeStrategy: TimeStrategy; timeStrategy: TimeStrategy;
linkStart: MaybeString; linkStart: MaybeString;
delay: number; delay: number;
showLabels?: boolean;
} }
function TimeInputFlow(props: EventBlockTimerProps) { function TimeInputFlow(props: EventBlockTimerProps) {
const { eventId, countToEnd, timeStart, timeEnd, duration, timeStrategy, linkStart, delay } = props; const { eventId, countToEnd, timeStart, timeEnd, duration, timeStrategy, linkStart, delay, showLabels } = props;
const { updateEvent, updateTimer } = useEventAction(); const { updateEvent, updateTimer } = useEventAction();
// In sync with EventEditorTimes // In sync with EventEditorTimes
@@ -62,60 +64,69 @@ function TimeInputFlow(props: EventBlockTimerProps) {
return ( return (
<> <>
<TimeInputWithButton<TimeField> <div>
name='timeStart' {showLabels && <Editor.Label className={style.sectionTitle}>Start time</Editor.Label>}
submitHandler={handleSubmit} <TimeInputWithButton<TimeField>
time={timeStart} name='timeStart'
hasDelay={hasDelay} submitHandler={handleSubmit}
placeholder='Start' time={timeStart}
disabled={Boolean(linkStart)} hasDelay={hasDelay}
> placeholder='Start'
<Tooltip label='Link start to previous end' openDelay={tooltipDelayMid}> disabled={Boolean(linkStart)}
<InputRightElement className={activeStart} onClick={() => handleLink(!linkStart)}> >
<span className={style.timeLabel}>S</span> <Tooltip label='Link start to previous end' openDelay={tooltipDelayMid}>
<span className={style.fourtyfive}>{linkStart ? <IoLink /> : <IoUnlink />}</span> <InputRightElement className={activeStart} onClick={() => handleLink(!linkStart)}>
</InputRightElement> <span className={style.timeLabel}>S</span>
</Tooltip> <span className={style.fourtyfive}>{linkStart ? <IoLink /> : <IoUnlink />}</span>
</TimeInputWithButton> </InputRightElement>
</Tooltip>
</TimeInputWithButton>
</div>
<TimeInputWithButton<TimeField> <div>
name='timeEnd' {showLabels && <Editor.Label>End time</Editor.Label>}
submitHandler={handleSubmit} <TimeInputWithButton<TimeField>
time={timeEnd} name='timeEnd'
hasDelay={hasDelay} submitHandler={handleSubmit}
disabled={isLockedDuration} time={timeEnd}
placeholder='End' hasDelay={hasDelay}
> disabled={isLockedDuration}
<Tooltip label='Lock end' openDelay={tooltipDelayMid}> placeholder='End'
<InputRightElement >
className={activeEnd} <Tooltip label='Lock end' openDelay={tooltipDelayMid}>
onClick={() => handleChangeStrategy(TimeStrategy.LockEnd)} <InputRightElement
data-testid='lock__end' className={activeEnd}
> onClick={() => handleChangeStrategy(TimeStrategy.LockEnd)}
<span className={style.timeLabel}>E</span> data-testid='lock__end'
{isLockedEnd ? <IoLockClosed /> : <IoLockOpenOutline />} >
</InputRightElement> <span className={style.timeLabel}>E</span>
</Tooltip> {isLockedEnd ? <IoLockClosed /> : <IoLockOpenOutline />}
</TimeInputWithButton> </InputRightElement>
</Tooltip>
</TimeInputWithButton>
</div>
<TimeInputWithButton<TimeField> <div>
name='duration' {showLabels && <Editor.Label>Duration</Editor.Label>}
submitHandler={handleSubmit} <TimeInputWithButton<TimeField>
time={duration} name='duration'
disabled={isLockedEnd} submitHandler={handleSubmit}
placeholder='Duration' time={duration}
> disabled={isLockedEnd}
<Tooltip label='Lock duration' openDelay={tooltipDelayMid}> placeholder='Duration'
<InputRightElement >
className={activeDuration} <Tooltip label='Lock duration' openDelay={tooltipDelayMid}>
onClick={() => handleChangeStrategy(TimeStrategy.LockDuration)} <InputRightElement
data-testid='lock__duration' className={activeDuration}
> onClick={() => handleChangeStrategy(TimeStrategy.LockDuration)}
<span className={style.timeLabel}>D</span> data-testid='lock__duration'
{isLockedDuration ? <IoLockClosed /> : <IoLockOpenOutline />} >
</InputRightElement> <span className={style.timeLabel}>D</span>
</Tooltip> {isLockedDuration ? <IoLockClosed /> : <IoLockOpenOutline />}
</TimeInputWithButton> </InputRightElement>
</Tooltip>
</TimeInputWithButton>
</div>
{warnings.length > 0 && ( {warnings.length > 0 && (
<div className={style.timerNote}> <div className={style.timerNote}>
+3
View File
@@ -5,6 +5,7 @@ $transition-time-feedback: 0.3s;
$component-border-radius-md: 3px; $component-border-radius-md: 3px;
$component-border-radius-sm: 2px; $component-border-radius-sm: 2px;
$component-border-radius-full: 99px;
// semantic colours // semantic colours
$action-blue: #3182ce; $action-blue: #3182ce;
@@ -50,12 +51,14 @@ $main-spacing: 2rem;
// interface text // interface text
$ontime-font-family: "Open Sans", "Segoe UI", sans-serif; $ontime-font-family: "Open Sans", "Segoe UI", sans-serif;
$title-gray: $gray-200;
$label-gray: $gray-400; $label-gray: $gray-400;
$secondary-text-gray: $gray-400; $secondary-text-gray: $gray-400;
$muted-gray: $gray-600; $muted-gray: $gray-600;
$section-white: $ui-white; $section-white: $ui-white;
$inner-section-text-size: calc(1rem - 2px); $inner-section-text-size: calc(1rem - 2px);
$text-body-size: calc(1rem - 1px); $text-body-size: calc(1rem - 1px);
$aux-text-size: calc(1rem - 3px);
// media queries // media queries
$min-tablet: 500px; $min-tablet: 500px;