• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

caleb531 / flip-book / 19905804943

03 Dec 2025 07:13PM UTC coverage: 74.679% (-13.5%) from 88.197%
19905804943

push

github

caleb531
Update all dependencies to latest

170 of 246 branches covered (69.11%)

Branch coverage included in aggregate %.

470 of 611 relevant lines covered (76.92%)

20.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.07
/scripts/components/timeline.jsx
1
import clsx from 'clsx';
2
import FrameComponent from './frame.jsx';
3

4
const FRAME_THUMBNAIL_WIDTH = 128;
6✔
5
const FRAME_THUMBNAIL_HEIGHT = 72;
6✔
6

7
class TimelineComponent {
8
  async selectThumbnail(target, story) {
9
    if (target.dataset.index) {
2!
10
      story.selectFrame(Number(target.dataset.index));
2✔
11
      this.scrollSelectedThumbnailIntoView(target);
2✔
12
      await story.save();
2✔
13
    }
14
  }
15

16
  scrollSelectedThumbnailIntoView(thumbnailElement) {
17
    if (thumbnailElement.classList.contains('selected')) {
80✔
18
      let timelineElement = thumbnailElement.parentElement;
46✔
19
      let scrollLeft = timelineElement.scrollLeft;
46✔
20
      let scrollRight = scrollLeft + timelineElement.offsetWidth;
46✔
21
      let offsetLeft = thumbnailElement.offsetLeft;
46✔
22
      let offsetRight = thumbnailElement.offsetLeft + thumbnailElement.offsetWidth;
46✔
23

24
      // If the visible timeline is not wide enough to show a full thumbnail,
25
      // do nothing
26
      if (timelineElement.offsetWidth < thumbnailElement.offsetWidth) {
46!
27
        return;
×
28
      }
29

30
      if (offsetRight > scrollRight) {
46!
31
        timelineElement.scrollLeft += offsetRight - scrollRight;
×
32
      } else if (offsetLeft < scrollLeft) {
46!
33
        timelineElement.scrollLeft += offsetLeft - scrollLeft;
×
34
      }
35
    }
36
  }
37

38
  handleFrameDragstart(event) {
39
    if (event.target.dataset.index) {
×
40
      this.oldFrameIndex = Number(event.target.dataset.index);
×
41
    }
42
    event.redraw = false;
×
43
  }
44

45
  async handleFrameDragenter(event, story) {
46
    event.preventDefault();
×
47
    event.redraw = false;
×
48
    if (event.target.dataset.index) {
×
49
      this.newFrameIndex = Number(event.target.dataset.index);
×
50
      if (this.newFrameIndex !== story.selectedFrameIndex) {
×
51
        story.moveFrame(this.oldFrameIndex, this.newFrameIndex);
×
52
        story.selectFrame(this.newFrameIndex);
×
53
        this.oldFrameIndex = this.newFrameIndex;
×
54
        await story.save();
×
55
        // Do not defer the next redraw
56
        delete event.redraw;
×
57
      }
58
    }
59
  }
60

61
  handleFrameDragover(event) {
62
    event.preventDefault();
×
63
    event.dataTransfer.dropEffect = 'move';
×
64
    event.redraw = false;
×
65
  }
66

67
  handleFrameDrop(event) {
68
    // This placeholder handler simply exists to allow Mithril to automatically
69
    // redraw the timeline when the thumbnail is dropped into its new location
70
    // (simply by virtue of the event listener existing)
71
  }
72

73
  view({ attrs: { story } }) {
74
    return (
46✔
75
      <ol
76
        className="timeline"
77
        aria-label="Story timeline"
78
        onclick={({ target }) => this.selectThumbnail(target, story)}
2✔
79
        ondragstart={(event) => this.handleFrameDragstart(event)}
×
80
        ondragover={(event) => this.handleFrameDragover(event, story)}
×
81
        ondragenter={(event) => this.handleFrameDragenter(event, story)}
×
82
        ondrop={(event) => this.handleFrameDrop(event, story)}
×
83
      >
84
        {story.frames.map((frame, f) => {
85
          return (
78✔
86
            <li
87
              draggable={true}
88
              key={`timeline-thumbnail-${frame.temporaryId}`}
89
              oncreate={({ dom }) => this.scrollSelectedThumbnailIntoView(dom)}
20✔
90
              onupdate={({ dom }) => this.scrollSelectedThumbnailIntoView(dom)}
58✔
91
              className={clsx('timeline-thumbnail', {
92
                selected: story.selectedFrameIndex === f
93
              })}
94
              data-index={f}
95
            >
96
              <FrameComponent
97
                className="timeline-thumbnail-canvas"
98
                frame={frame}
99
                width={FRAME_THUMBNAIL_WIDTH}
100
                height={FRAME_THUMBNAIL_HEIGHT}
101
              />
102
            </li>
103
          );
104
        })}
105
      </ol>
106
    );
107
  }
108
}
109

110
export default TimelineComponent;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc