diff --git a/apps/client/src/common/components/view-params-editor/ParamInput.module.scss b/apps/client/src/common/components/view-params-editor/ParamInput.module.scss index 63a3a1797..fd5fa2ae6 100644 --- a/apps/client/src/common/components/view-params-editor/ParamInput.module.scss +++ b/apps/client/src/common/components/view-params-editor/ParamInput.module.scss @@ -32,11 +32,11 @@ margin-left: 0.25rem; width: 0.75em; height: 0.75em; - background: var(--user-bg); + background: var(--user-bg, $gray-900); border-radius: 50%; } } .empty { color: $ui-white; -} \ No newline at end of file +} diff --git a/apps/client/src/common/components/view-params-editor/viewParams.utils.ts b/apps/client/src/common/components/view-params-editor/viewParams.utils.ts index 483d5057c..0a4052d29 100644 --- a/apps/client/src/common/components/view-params-editor/viewParams.utils.ts +++ b/apps/client/src/common/components/view-params-editor/viewParams.utils.ts @@ -46,7 +46,7 @@ export function makeCustomFieldSelectOptions(customFields: CustomFields, filterI options.push({ value: key, label: value.label, - colour: value.colour || 'transparent', + colour: value.colour, }); } diff --git a/apps/client/src/common/hooks/useLongPress.ts b/apps/client/src/common/hooks/useLongPress.ts new file mode 100644 index 000000000..fb5bfec39 --- /dev/null +++ b/apps/client/src/common/hooks/useLongPress.ts @@ -0,0 +1,95 @@ +import React, { useEffect, useMemo, useRef } from 'react'; + +import { throttle } from '../utils/throttle'; + +export interface UseLongPressOptions { + /** Time in milliseconds to trigger the long press, default is 400ms */ + threshold?: number; + + /** Callback triggered when the long press starts */ + onStart?: (event: React.MouseEvent | React.TouchEvent) => void; + + /** Callback triggered when the long press finishes */ + onFinish?: (event: React.MouseEvent | React.TouchEvent) => void; + + /** Callback triggered when the long press is canceled */ + onCancel?: (event: React.MouseEvent | React.TouchEvent) => void; +} + +export interface UseLongPressReturnValue { + onMouseDown: (event: React.MouseEvent) => void; + onMouseUp: (event: React.MouseEvent) => void; + onMouseLeave: (event: React.MouseEvent) => void; + onTouchStart: (event: React.TouchEvent) => void; + onTouchEnd: (event: React.TouchEvent) => void; +} + +export function useLongPress( + onLongPress: (event: React.MouseEvent | React.TouchEvent) => void, + options: UseLongPressOptions = {}, +): UseLongPressReturnValue { + const { threshold = 700, onStart, onFinish, onCancel } = options; + const isLongPressActive = useRef(false); + const isPressed = useRef(false); + const timeout = useRef(-1); + + useEffect(() => () => window.clearTimeout(timeout.current), []); + + return useMemo(() => { + if (typeof onLongPress !== 'function') { + return {} as UseLongPressReturnValue; + } + + const start = (event: React.MouseEvent | React.TouchEvent) => { + if (!isMouseEvent(event) && !isTouchEvent(event)) { + return; + } + + if (onStart) { + onStart(event); + } + + isPressed.current = true; + timeout.current = window.setTimeout(() => { + onLongPress(event); + isLongPressActive.current = true; + }, threshold); + }; + + const cancel = (event: React.MouseEvent | React.TouchEvent) => { + if (!isMouseEvent(event) && !isTouchEvent(event)) { + return; + } + + if (isLongPressActive.current) { + onFinish?.(event); + } else if (isPressed.current) { + onCancel?.(event); + } + + isLongPressActive.current = false; + isPressed.current = false; + + if (timeout.current) { + window.clearTimeout(timeout.current); + } + }; + + return { + onMouseDown: start, + onMouseUp: cancel, + onMouseLeave: cancel, + onTouchStart: start, + onTouchEnd: cancel, + onTouchMove: throttle(cancel, 150), + }; + }, [onLongPress, threshold, onCancel, onFinish, onStart]); +} + +function isTouchEvent(event: React.MouseEvent | React.TouchEvent): event is React.TouchEvent { + return window.TouchEvent ? event.nativeEvent instanceof TouchEvent : 'touches' in event.nativeEvent; +} + +function isMouseEvent(event: React.MouseEvent | React.TouchEvent): event is React.MouseEvent { + return event.nativeEvent instanceof MouseEvent; +} diff --git a/apps/client/src/features/operator/edit-modal/EditModal.module.scss b/apps/client/src/features/operator/edit-modal/EditModal.module.scss index 74e581ae1..fc2b49f90 100644 --- a/apps/client/src/features/operator/edit-modal/EditModal.module.scss +++ b/apps/client/src/features/operator/edit-modal/EditModal.module.scss @@ -30,12 +30,12 @@ flex-direction: column; gap: 0.5rem; - max-height: min(80vh, 600px); + max-height: min(60vh, 600px); overflow-y: auto; } .label { - background-color: var(--user-bg); + background-color: var(--user-bg, $gray-900); width: fit-content; padding-inline: 1rem; } diff --git a/apps/client/src/features/operator/operator-event/OperatorEvent.tsx b/apps/client/src/features/operator/operator-event/OperatorEvent.tsx index cd415407e..81de7fa22 100644 --- a/apps/client/src/features/operator/operator-event/OperatorEvent.tsx +++ b/apps/client/src/features/operator/operator-event/OperatorEvent.tsx @@ -1,8 +1,8 @@ import { memo, RefObject, SyntheticEvent } from 'react'; -import { useLongPress } from '@mantine/hooks'; import { MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils'; import DelayIndicator from '../../../common/components/delay-indicator/DelayIndicator'; +import { useLongPress } from '../../../common/hooks/useLongPress'; import { cx, getAccessibleColour } from '../../../common/utils/styleUtils'; import { formatDuration, formatTime, useTimeUntilExpectedStart } from '../../../common/utils/time'; import RunningTime from '../../viewers/common/running-time/RunningTime'; @@ -55,8 +55,10 @@ function OperatorEvent({ * gather behaviour for long press and context menu */ const handleLongPress = (event?: SyntheticEvent) => { - // we dont have an event out of useLongPress - event?.preventDefault(); + // prevent default if the event is cancelable to avoid browser intervention warnings + if (event && event.cancelable) { + event.preventDefault(); + } if (subscribed) { onLongPress({ id, cue, subscriptions: subscribed }); }