Adam Shaw 5 лет назад
Родитель
Сommit
de872af51c
2 измененных файлов с 95 добавлено и 73 удалено
  1. 14 2
      packages/core/src/Calendar.tsx
  2. 81 71
      packages/core/src/util/runner.ts

+ 14 - 2
packages/core/src/Calendar.tsx

@@ -250,7 +250,7 @@ export default class Calendar {
     switch (action.type) {
       case 'SET_EVENT_DRAG':
       case 'SET_EVENT_RESIZE':
-        this.renderRunner.drain()
+        this.renderRunner.tryDrain()
     }
   }
 
@@ -272,7 +272,19 @@ export default class Calendar {
 
 
   batchRendering(func) {
-    this.renderRunner.whilePaused(func)
+    this.renderRunner.pause('batchRendering')
+    func()
+    this.renderRunner.resume('batchRendering')
+  }
+
+
+  pauseRendering() { // available to plugins
+    this.renderRunner.pause('pauseRendering')
+  }
+
+
+  resumeRendering() { // available to plugins
+    this.renderRunner.resume('pauseRendering', true)
   }
 
 

+ 81 - 71
packages/core/src/util/runner.ts

@@ -1,9 +1,12 @@
 
+// TODO: try to DRY-up the pausing systems of each of these classes
+
+
 export class DelayedRunner {
 
   private isRunning = false
-  private pauseDepth: number = 0
   private isDirty = false
+  private pauseDepths: { [scope: string]: number } = {}
   private timeoutId: number = 0
 
   constructor(
@@ -14,55 +17,51 @@ export class DelayedRunner {
   request(delay?: number) {
     this.isDirty = true
 
-    if (!this.pauseDepth) {
+    if (!this.isPaused()) {
       this.clearTimeout()
 
       if (delay == null) {
-        this.drain()
+        this.tryDrain()
       } else {
-        this.timeoutId = setTimeout(this.drain.bind(this), delay) as unknown as number // NOT OPTIMAL! TODO: look at debounce
+        this.timeoutId = setTimeout(this.tryDrain.bind(this), delay) as unknown as number // NOT OPTIMAL! TODO: look at debounce
       }
     }
   }
 
-  pause() {
-    this.setPauseDepth(1)
-  }
+  pause(scope = '') {
+    let { pauseDepths } = this
 
-  resume() {
-    this.setPauseDepth(0)
-  }
+    pauseDepths[scope] = (pauseDepths[scope] || 0) + 1
 
-  whilePaused(func) {
-    this.setPauseDepth(this.pauseDepth + 1)
-    func()
-    this.setPauseDepth(this.pauseDepth - 1)
+    this.clearTimeout()
   }
 
-  private setPauseDepth(depth: number) {
-    let oldDepth = this.pauseDepth
-    this.pauseDepth = depth // for this.drain() call
+  resume(scope = '', force?: boolean) {
+    let { pauseDepths } = this
 
-    if (depth) { // wants to pause
-      if (!oldDepth) {
-        this.clearTimeout()
-      }
-    } else { // wants to unpause
-      if (oldDepth) {
-        this.drain()
+    if (scope in pauseDepths) {
+
+      if (force) {
+        delete pauseDepths[scope]
+
+      } else {
+        let depth = --pauseDepths[scope]
+
+        if (depth <= 0) {
+          delete pauseDepths[scope]
+        }
       }
+
+      this.tryDrain()
     }
   }
 
-  private clearTimeout() {
-    if (this.timeoutId) {
-      clearTimeout(this.timeoutId)
-      this.timeoutId = 0
-    }
+  isPaused() {
+    return Object.keys(this.pauseDepths).length
   }
 
-  drain() {
-    if (!this.isRunning && !this.pauseDepth) {
+  tryDrain() {
+    if (!this.isRunning && !this.isPaused()) {
       this.isRunning = true
 
       while (this.isDirty) {
@@ -74,24 +73,32 @@ export class DelayedRunner {
     }
   }
 
-  protected drained() {
-    if (this.drainedOption) {
-      this.drainedOption()
+  clear() {
+    this.clearTimeout()
+    this.isDirty = false
+    this.pauseDepths = {}
+  }
+
+  private clearTimeout() {
+    if (this.timeoutId) {
+      clearTimeout(this.timeoutId)
+      this.timeoutId = 0
     }
   }
 
-  clear() {
-    this.pause()
-    this.isDirty = false
+  protected drained() { // subclasses can implement
+    if (this.drainedOption) {
+      this.drainedOption()
+    }
   }
 
 }
 
 
-export class TaskRunner<Task> {
+export class TaskRunner<Task> { // this class USES the DelayedRunner
 
   private isRunning = false
-  private pauseDepth = 0
+  private pauseDepths: { [scope: string]: number } = {}
   private queue: Task[] = []
   private delayedRunner: DelayedRunner
 
@@ -99,7 +106,7 @@ export class TaskRunner<Task> {
     private runTaskOption?: (task: Task) => void,
     private drainedOption?: (completedTasks: Task[]) => void
   ) {
-    this.delayedRunner = new DelayedRunner(this.drain.bind(this))
+    this.delayedRunner = new DelayedRunner(this.tryDrain.bind(this))
   }
 
   request(task: Task, delay?: number) {
@@ -107,10 +114,40 @@ export class TaskRunner<Task> {
     this.delayedRunner.request(delay)
   }
 
-  drain() {
+  pause(scope = '') {
+    let { pauseDepths } = this
+
+    pauseDepths[scope] = (pauseDepths[scope] || 0) + 1
+  }
+
+  resume(scope = '', force?: boolean) {
+    let { pauseDepths } = this
+
+    if (scope in pauseDepths) {
+
+      if (force) {
+        delete pauseDepths[scope]
+
+      } else {
+        let depth = --pauseDepths[scope]
+
+        if (depth <= 0) {
+          delete pauseDepths[scope]
+        }
+      }
+
+      this.tryDrain()
+    }
+  }
+
+  isPaused() {
+    return Object.keys(this.pauseDepths).length
+  }
+
+  tryDrain() {
     let { queue } = this
 
-    if (!this.isRunning && !this.pauseDepth) {
+    if (!this.isRunning && !this.isPaused()) {
       this.isRunning = true
 
       while (queue.length) {
@@ -129,40 +166,13 @@ export class TaskRunner<Task> {
     }
   }
 
-  pause() {
-    this.setPauseDepth(1)
-  }
-
-  resume() {
-    this.setPauseDepth(0)
-  }
-
-  whilePaused(func) {
-    this.setPauseDepth(this.pauseDepth + 1)
-    func()
-    this.setPauseDepth(this.pauseDepth - 1)
-  }
-
-  private setPauseDepth(depth: number) {
-    let oldDepth = this.pauseDepth
-    this.pauseDepth = depth // for this.drain() call
-
-    if (depth) { // wants to pause
-      ;
-    } else { // wants to unpause
-      if (oldDepth) {
-        this.drain()
-      }
-    }
-  }
-
-  protected runTask(task: Task) {
+  protected runTask(task: Task) { // subclasses can implement
     if (this.runTaskOption) {
       this.runTaskOption(task)
     }
   }
 
-  protected drained(completedTasks: Task[]) {
+  protected drained(completedTasks: Task[]) { // subclasses can implement
     if (this.drainedOption) {
       this.drainedOption(completedTasks)
     }