Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';

import SwatchPicker from '../input/colour-input/SwatchPicker';

Expand All @@ -16,10 +16,13 @@ const ensureHex = (value: string) => {
return value;
};

export default function InlineColourPicker(props: InlineColourPickerProps) {
const { name, value } = props;
export default function InlineColourPicker({ name, value }: InlineColourPickerProps) {
const [colour, setColour] = useState(() => ensureHex(value));

useEffect(() => {
setColour(ensureHex(value));
}, [value]);

return (
<div className={style.inline}>
<SwatchPicker color={colour} onChange={setColour} alwaysDisplayColor />
Expand Down
70 changes: 60 additions & 10 deletions apps/client/src/common/components/view-params-editor/ParamInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState } from 'react';
import { ComponentProps, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router';

import { isStringBoolean } from '../../../features/viewers/common/viewUtils';
import Checkbox from '../checkbox/Checkbox';
import Input from '../input/input/Input';
import Select from '../select/Select';
import Select, { SelectOption } from '../select/Select';
import Switch from '../switch/Switch';

import InlineColourPicker from './InlineColourPicker';
Expand Down Expand Up @@ -35,15 +35,15 @@ export default function ParamInput({ paramField }: ParamInputProps) {
return <span className={style.empty}>No options available</span>;
}

return <Select size='large' name={id} defaultValue={defaultOptionValue} options={paramField.values} />;
return <ControlledSelect id={id} initialValue={defaultOptionValue} options={paramField.values} />;
}

if (type === 'multi-option') {
return <MultiOption paramField={paramField} />;
}

if (type === 'boolean') {
return <ControlledSwitch id={id} initialValue={isStringBoolean(searchParams.get(id)) || defaultValue} />;
return <ControlledSwitch id={id} initialValue={isStringBoolean(searchParams.get(id)) ?? defaultValue} />;
}

if (type === 'number') {
Expand All @@ -63,15 +63,13 @@ export default function ParamInput({ paramField }: ParamInputProps) {
}

if (type === 'colour') {
const currentvalue = `#${searchParams.get(id) ?? defaultValue}`;

return <InlineColourPicker name={id} value={currentvalue} />;
return <InlineColourPicker name={id} value={searchParams.get(id) ?? defaultValue} />;
}

const defaultStringValue = searchParams.get(id) ?? defaultValue;
const defaultStringValue = searchParams.get(id) ?? defaultValue ?? '';
const { placeholder } = paramField;

return <Input height='large' name={id} defaultValue={defaultStringValue} placeholder={placeholder} />;
return <ControlledInput id={id} initialValue={defaultStringValue} placeholder={placeholder} />;
}

interface EditFormMultiOptionProps {
Expand All @@ -83,7 +81,12 @@ function MultiOption({ paramField }: EditFormMultiOptionProps) {
const { id, values, defaultValue = [''] } = paramField;

const optionFromParams = searchParams.getAll(id);
const [paramState, setParamState] = useState<string[]>(optionFromParams || defaultValue);
const [paramState, setParamState] = useState<string[]>(optionFromParams.length ? optionFromParams : defaultValue);

useEffect(() => {
const params = searchParams.getAll(id);
setParamState(params.length ? params : defaultValue);
}, [searchParams, id, defaultValue]);

const toggleValue = (value: string, checked: boolean) => {
if (checked) {
Expand Down Expand Up @@ -129,5 +132,52 @@ interface ControlledSwitchProps {
}
function ControlledSwitch({ id, initialValue }: ControlledSwitchProps) {
const [checked, setChecked] = useState(initialValue);

// synchronise checked state
useEffect(() => {
setChecked(initialValue);
}, [initialValue]);

return <Switch size='large' name={id} checked={checked} onCheckedChange={setChecked} />;
}

interface ControlledSelectProps {
id: string;
initialValue?: string;
options: SelectOption[];
}
function ControlledSelect({ id, initialValue, options }: ControlledSelectProps) {
const [selected, setSelected] = useState(initialValue);

// synchronise selected state
useEffect(() => {
setSelected(initialValue);
}, [initialValue]);

return (
<Select size='large' name={id} options={options} value={selected} onValueChange={(value) => setSelected(value)} />
);
}

interface ControlledInputProps<T extends number | string> extends ComponentProps<typeof Input> {
id: string;
initialValue: T;
}
function ControlledInput<T extends number | string>({ id, initialValue, ...inputProps }: ControlledInputProps<T>) {
const [value, setValue] = useState(initialValue);

// synchronise selected state
useEffect(() => {
setValue(initialValue);
}, [initialValue]);

return (
<Input
height='large'
name={id}
value={value}
onChange={(event) => setValue(event.target.value as T)}
{...inputProps}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormEvent, memo, useReducer } from 'react';
import { FormEvent, memo } from 'react';
import { IoClose } from 'react-icons/io5';
import { useSearchParams } from 'react-router';
import { Dialog } from '@base-ui-components/react/dialog';
Expand All @@ -12,7 +12,7 @@ import Info from '../info/Info';
import { ViewOption } from './viewParams.types';
import { getURLSearchParamsFromObj } from './viewParams.utils';
import { useViewParamsEditorStore } from './viewParamsEditor.store';
import { ViewParamsShare } from './ViewParamShare';
import { ViewParamsPresets } from './ViewParamsPresets';
import ViewParamsSection from './ViewParamsSection';

import style from './ViewParamsEditor.module.scss';
Expand All @@ -24,20 +24,16 @@ interface EditFormDrawerProps {

export default memo(ViewParamsEditor);
function ViewParamsEditor({ target, viewOptions }: EditFormDrawerProps) {
// TODO: can we ensure that the options update when the user loads an alias?
const [_, setSearchParams] = useSearchParams();
const { data: viewSettings } = useViewSettings();
const { isOpen, close } = useViewParamsEditorStore();
// TODO: we dont want this as a permanent option
const forceRender = useReducer((x) => x + 1, 0)[1];

const handleClose = () => {
close();
};

const resetParams = () => {
setSearchParams();
forceRender();
};

const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
Expand All @@ -46,7 +42,6 @@ function ViewParamsEditor({ target, viewOptions }: EditFormDrawerProps) {
const newParamsObject = Object.fromEntries(new FormData(formEvent.currentTarget));
const newSearchParams = getURLSearchParamsFromObj(newParamsObject, viewOptions);
setSearchParams(newSearchParams);
forceRender();
};

return (
Expand All @@ -71,7 +66,7 @@ function ViewParamsEditor({ target, viewOptions }: EditFormDrawerProps) {
{viewSettings.overrideStyles && (
<Info className={style.info}>This view style is being modified by a custom CSS file.</Info>
)}
<ViewParamsShare target={target} />
<ViewParamsPresets target={target} />
<form id='edit-params-form' onSubmit={onParamsFormSubmit} className={style.sectionList}>
{viewOptions.map((section) => (
<ViewParamsSection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
gap: 0.25rem;
padding: 1rem 0.5rem;
margin-bottom: 1rem;
max-height: 10rem;
overflow-y: auto;
scrollbar-gutter: stable;
}

.preset {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import { useViewUrlPresets } from '../../hooks-query/useUrlPresets';
import { cx } from '../../utils/styleUtils';
import Button from '../buttons/Button';

import style from './ViewParamsShare.module.scss';
import style from './ViewParamsPresets.module.scss';

/**
* Shows a list of presets for the current view
*/
export function ViewParamsShare({ target }: { target: OntimeView }) {
export function ViewParamsPresets({ target }: { target: OntimeView }) {
const { viewPresets } = useViewUrlPresets(target);
const [_, setSearchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();

const handleRecall = (preset: URLPreset) => {
setSearchParams(`${preset.search}&alias=${preset.alias}`);
const newSearch = new URLSearchParams(preset.search);
newSearch.set('alias', preset.alias);
setSearchParams(newSearch);
};

if (viewPresets.length === 0) {
Expand All @@ -25,12 +27,12 @@ export function ViewParamsShare({ target }: { target: OntimeView }) {
return (
<div className={style.presetSection}>
{viewPresets.map((preset) => {
const active = window.location.search.includes(`alias=${preset.alias}`);
const active = searchParams.get('alias') === preset.alias;
return (
<div key={preset.alias} className={cx([style.preset, active && style.active])}>
<div>{preset.alias}</div>
<Button
variant='subtle-white'
variant={active ? 'ghosted' : 'subtle-white'}
onClick={() => handleRecall(preset)}
disabled={active}
className={style.presetActions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@
transition: rotate 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
rotate: 180deg;
}

.hidden {
display: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,11 @@ interface SectionContentsProps {
}

function SectionContents({ options, collapsed }: SectionContentsProps) {
if (collapsed) {
return null;
}

return (
<>
{options.map((option) => {
return (
<label key={option.title} className={style.label}>
<label key={option.title} className={cx([style.label, collapsed && style.hidden])}>
<span className={style.title}>{option.title}</span>
<span className={style.description}>{option.description}</span>
<ParamInput paramField={option} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type MultiselectOption = { value: string; label: string; colour: string }
type MultiOptionsField = {
type: 'multi-option';
values: MultiselectOption[];
defaultValue?: string;
defaultValue?: string[];
};

type StringField = { type: 'string'; defaultValue?: string; placeholder?: string };
Expand Down
14 changes: 14 additions & 0 deletions apps/client/src/common/hooks/useEntryAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,19 @@ export const useEntryActions = () => {
[updateEntry],
);

const matchGroupDuration = useCallback(async (eventId: EntryId, groupId: EntryId) => {
const rundown = queryClient.getQueryData<Rundown>(RUNDOWN);
if (!rundown) return;
const group = rundown.entries[groupId];
if (!group || !isOntimeGroup(group) || group.targetDuration === null) return;
const event = rundown.entries[eventId];
if (!event || !isOntimeEvent(event)) return;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could really use a way to show errors like this
maybe the toasters we were thinking of?

const durationDiff = group.targetDuration - group.duration;
const newDuration = event.duration + durationDiff;
if (newDuration < 0) return;
updateTimer(eventId, 'duration', String(newDuration / MILLIS_PER_SECOND) + 's', false);
}, []);

/**
* Updates time of existing event
* @param eventId {EntryId} - id of the event
Expand Down Expand Up @@ -781,6 +794,7 @@ export const useEntryActions = () => {
updateEntry,
updateTimer,
updateCustomField,
matchGroupDuration,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ $inner-padding: 1rem;
th,
td {
padding: 0.5rem;
vertical-align: top;
}

tr:nth-child(even) {
Expand Down
2 changes: 0 additions & 2 deletions apps/client/src/features/rundown/Rundown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,6 @@ export default function Rundown({ data }: RundownProps) {
loaded={rundownMetadata.isLoaded}
hasCursor={hasCursor}
isNext={isNext}
previousEntryId={rundownMetadata.previousEntryId}
previousEventId={rundownMetadata.previousEvent?.id}
playback={rundownMetadata.isLoaded ? featureData.playback : undefined}
isRolling={featureData.playback === Playback.Roll}
isNextDay={rundownMetadata.isNextDay}
Expand Down
Loading