-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eab420d
commit 6c7dad0
Showing
5 changed files
with
340 additions
and
318 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// @flow | ||
import invariant from 'tiny-invariant'; | ||
|
||
type Args = {| | ||
collector: Collector, | ||
publisher: Publisher, | ||
|} | ||
|
||
type Phase = 'IDLE' | 'RUNNING'; | ||
|
||
const rafWait = () => new Promise(resolve => requestAnimationFrame(resolve)); | ||
|
||
export default ({ collector, publisher }: Args) => { | ||
let phase: Phase = 'IDLE'; | ||
let isRunQueued: boolean = false; | ||
|
||
const reset = () => { | ||
// forcing phase to IDLE | ||
phase = 'IDLE'; | ||
isRunQueued = false; | ||
}; | ||
|
||
const stopIfIdle = () => (phase === 'IDLE' ? Promise.reject() : Promise.resolve()); | ||
|
||
const run = () => { | ||
phase = 'RUNNING'; | ||
|
||
// This would be easier to read with async/await but the runtime is 10kb | ||
|
||
rafWait() | ||
.then(stopIfIdle) | ||
.then(collector.perform) | ||
.then(rafWait) | ||
.then(stopIfIdle) | ||
.then(publisher.perform) | ||
// collection was stopped - we can just exit | ||
.catch() | ||
.then(() => { | ||
if (isRunQueued) { | ||
run(); | ||
return; | ||
} | ||
reset(); | ||
}); | ||
}; | ||
|
||
const execute = () => { | ||
// A run is already queued | ||
if (isRunQueued) { | ||
return; | ||
} | ||
|
||
// We are already performing a run | ||
if (phase === 'RUNNING') { | ||
return; | ||
} | ||
|
||
run(); | ||
}; | ||
|
||
return { execute, reset }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// @flow | ||
import invariant from 'tiny-invariant'; | ||
import * as timings from '../../debug/timings'; | ||
import type { | ||
DraggableId, | ||
DroppableId, | ||
DraggableDimension, | ||
DroppableDimension, | ||
ScrollOptions, | ||
} from '../../types'; | ||
import type { ToBeCollected } from './dimension-marshal-types'; | ||
|
||
type Collected = {| | ||
draggables: DraggableDimension[], | ||
droppables: DroppableDimension[], | ||
|} | ||
|
||
type Args = {| | ||
getToBeCollected: () => ToBeCollected, | ||
getDraggable: (id: DraggableId) => DraggableDimension, | ||
getDroppable: (id: DroppableId) => DroppableDimension, | ||
publish: (droppables: DroppableDimension[], draggables: DraggableDimension[]) => void, | ||
|} | ||
|
||
export type Collector = {| | ||
start: (options: ScrollOptions) => void, | ||
stop: () => void, | ||
collect: () => void, | ||
|} | ||
|
||
export default ({ | ||
publish, | ||
getDraggable, | ||
getDroppable, | ||
getToBeCollected, | ||
}: Args): Collector => { | ||
let isActive: boolean = false; | ||
let frameId: ?AnimationFrameID = null; | ||
let isQueued: boolean = false; | ||
let isRunning: boolean = false; | ||
|
||
const collectFromDOM = (toBeCollected: ToBeCollected): Collected => { | ||
const droppables: DroppableDimension[] = toBeCollected.droppables | ||
.map((id: DroppableId): DroppableDimension => getDroppable(id)); | ||
|
||
const draggables: DraggableDimension[] = toBeCollected.draggables | ||
.map((id: DraggableId): DraggableDimension => getDraggable(id)); | ||
|
||
return { draggables, droppables }; | ||
}; | ||
|
||
const run = () => { | ||
invariant(isRunning, 'Cannot start a new run when a run is already occurring'); | ||
|
||
isRunning = true; | ||
|
||
// Perform DOM collection in next frame | ||
frameId = requestAnimationFrame(() => { | ||
timings.start('DOM collection'); | ||
const toBeCollected: ToBeCollected = getToBeCollected(); | ||
const collected: Collected = collectFromDOM(toBeCollected); | ||
timings.finish('DOM collection'); | ||
|
||
// Perform publish in next frame | ||
frameId = requestAnimationFrame(() => { | ||
timings.start('Bulk dimension publish'); | ||
publish(collected.droppables, collected.draggables); | ||
timings.finish('Bulk dimension publish'); | ||
|
||
// TODO: what if publish caused collection? | ||
|
||
frameId = null; | ||
isRunning = false; | ||
|
||
if (isQueued) { | ||
isQueued = false; | ||
run(); | ||
} | ||
}); | ||
}); | ||
}; | ||
|
||
const start = () => { | ||
invariant(!isActive, 'Collector has already been started'); | ||
isActive = true; | ||
}; | ||
|
||
const collect = () => { | ||
invariant(isActive, 'Can only collect when active'); | ||
// A run is already queued | ||
if (isQueued) { | ||
return; | ||
} | ||
|
||
// We are running and a collection is not queued | ||
// Queue another run | ||
if (isRunning) { | ||
isQueued = true; | ||
} | ||
|
||
run(); | ||
}; | ||
|
||
const stop = () => { | ||
if (frameId) { | ||
cancelAnimationFrame(frameId); | ||
} | ||
isRunning = false; | ||
isQueued = false; | ||
isActive = false; | ||
}; | ||
|
||
return { | ||
start, | ||
stop, | ||
collect, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.