Repository: motion-canvas/motion-canvas
File: packages/ui/src/components/timeline/RangeSelector.tsx (TypeScript)

Code before:
    setStart(startFrame);
    setEnd(endFrame);
  }, [startFrame, endFrame, range[0], range[1]]);

  let normalizedStart = start;
  let normalizedEnd = end;
  if (start > end) {
    normalizedStart = end;
    normalizedEnd = start;
  }

  return (
    <div
      ref={rangeRef}
      className={clsx(
        styles.rangeContainer,
        shiftHeld && controlHeld && styles.active,
      )}
      onPointerDown={event => {
        if (event.button === MouseButton.Left) {
          event.preventDefault();
          event.stopPropagation();
          event.currentTarget.setPointerCapture(event.pointerId);

          setStart(pixelsToFrames(event.clientX - rect.x));
          setEnd(pixelsToFrames(event.clientX - rect.x));
        }
      }}
      onPointerMove={event => {
        if (event.currentTarget.hasPointerCapture(event.pointerId)) {
          setEnd(end + pixelsToFrames(event.movementX));
        }
      }}
      onPointerUp={event => {
        if (event.button === MouseButton.Left) {
          event.currentTarget.releasePointerCapture(event.pointerId);
          onDrop();
        }
      }}
    >
      <div
        style={{
          flexDirection: start > end ? 'row-reverse' : 'row',
          left: `${framesToPercents(Math.ceil(Math.max(0, normalizedStart)))}%`,
          right: `${
            100 - framesToPercents(Math.ceil(Math.min(duration, normalizedEnd)))
          }%`,
        }}
        className={clsx(
          styles.range,
          shiftHeld && !controlHeld && styles.active,

Review comment (suggestion):
Mapping from the pointer's position to the corresponding animation frame is common logic between the playhead and this. I'd suggest we add a function for this to `TimelineState`. This could be done in `Timeline.tsx` when the state is created:
```ts
const startPosition = sizes.paddingLeft + rect.x - offset;
const pointerToFrames: (value: number) =>
  conversion.pixelsToFrames(value - startPosition),
```
And then used both here and in the playhead scrubbing function:
https://github.com/motion-canvas/motion-canvas/blob/f67d691b5f2f6358120e9582a1839ef3d49c77b8/packages/ui/src/components/timeline/Timeline.tsx#L160-L162
It would also remove the need for another `useSize` in this component.

Code after:
  }, [startFrame, endFrame, range[0], range[1]]);

  let normalizedStart = start;
  let normalizedEnd = end;
  if (start > end) {
    normalizedStart = end;
    normalizedEnd = start;
  }

  return (
    <div
      ref={rangeRef}
      className={clsx(
        styles.rangeTrack,
        shiftHeld && controlHeld && styles.active,
      )}
      onPointerDown={event => {
        if (event.button === MouseButton.Left) {
          event.preventDefault();
          event.stopPropagation();
          event.currentTarget.setPointerCapture(event.pointerId);

          setStart(pointerToFrames(event.clientX));
          setEnd(pointerToFrames(event.clientX));
        }
      }}
      onPointerMove={event => {
        if (event.currentTarget.hasPointerCapture(event.pointerId)) {
          setEnd(end + pixelsToFrames(event.movementX));
        }
      }}
      onPointerUp={event => {
        if (event.button === MouseButton.Left) {
          event.currentTarget.releasePointerCapture(event.pointerId);
          onDrop();
        }
      }}
    >
      <div
        style={{
          flexDirection: start > end ? 'row-reverse' : 'row',
          left: `${framesToPercents(Math.ceil(Math.max(0, normalizedStart)))}%`,
          right: `${
            100 - framesToPercents(Math.ceil(Math.min(duration, normalizedEnd)))
          }%`,
        }}
        className={clsx(
          styles.range,
          shiftHeld && !controlHeld && styles.active,
        )}
        onPointerDown={event => {