mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-10 08:39:34 +00:00
refactor: improve section style and composition
This commit is contained in:
committed by
Carlos Valente
parent
2f2c26ed00
commit
6199d0ad10
@@ -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}>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user