Skip to content

vue practical example #2

@unstoppablecarl

Description

@unstoppablecarl

I wanted to share a practical example of using reddojs in vue. The useHistory vue composable pacakge is a valid example but is not practical for a non-trivial app. I was going to put it in a PR but wasn't sure if it should just be docs or another vue helper import.

// my-app-history.ts
import type { History } from '@reddojs/core'
import { computed, ref } from 'vue'

// global history instance
let HISTORY: History | undefined

// global unsubscribe
let historyUnsub: (() => void) | undefined

const canUndoRef = ref(false)
const canRedoRef = ref(false)

// readonly
const canUndo = computed(() => canUndoRef.value)
const canRedo = computed(() => canRedoRef.value)

// can be used to call history.execute outside vue component context
export function getGlobalHistory(): History {
  if (!HISTORY) {
    throw new Error('Global History not set. Call setGlobalHistory()')
  }
  return HISTORY
}

export function setGlobalHistory(history: History) {
  if (HISTORY) {
    throw new Error('history already set')
  }

  HISTORY = history
  canUndoRef.value = HISTORY.canUndo
  canRedoRef.value = HISTORY.canRedo

  historyUnsub = HISTORY.subscribe(() => {
    canUndoRef.value = HISTORY!.canUndo
    canRedoRef.value = HISTORY!.canRedo
  })

  return historyUnsub
}

// vue component context history helper
let VUE_HISTORY: VueHistory | undefined

export interface VueHistoryOptions {
  undo?: (history: History) => void
  redo?: (history: History) => void
}

export type VueHistory = ReturnType<typeof makeVueHistory>

function makeVueHistory(history: History, opts?: VueHistoryOptions) {
  function dispose() {
    historyUnsub?.()
    history.clear()
    HISTORY = undefined
    VUE_HISTORY = undefined
    canUndoRef.value = false
    canRedoRef.value = false
  }

  const undo = opts?.undo ? () => opts.undo!(history) : () => history.undo()
  const redo = opts?.redo ? () => opts.redo!(history) : () => history.redo()

  return {
    size: history.size,
    subscribe: history.subscribe,
    canUndo,
    canRedo,
    execute: history.execute,
    undo,
    redo,
    clear: history.clear,
    dispose,
  }
}

export function useVueHistory(): VueHistory {
  if (!VUE_HISTORY) {
    throw new Error('VueHistory not booted. Call bootVueHistory()')
  }

  return VUE_HISTORY
}

export function bootVueHistory(opts?: VueHistoryOptions): VueHistory {
  if (!VUE_HISTORY) {
    VUE_HISTORY = makeVueHistory(getGlobalHistory(), opts)
  }

  return VUE_HISTORY
}

Usage

// main.ts
import { createHistory } from '@reddojs/core/src'
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { setGlobalHistory } from './my-history.ts'

setGlobalHistory(createHistory({ size: 100, coalesce: true }))
createApp(App).mount('#app')
``

```vue
<script setup lang="ts">
// App.vue
import { bootVueHistory } from './my-history.ts'

const { execute, canRedo, canUndo, undo, redo } = bootVueHistory({
  undo(history: History) {
    if (!history.canUndo) {
      // @TODO notify user in UI using in component context behavior
      console.log('no undos left')
      return
    }
    history.undo()
  },
  redo(history: History) {
    if (!history.canRedo) {
      // @TODO notify user in UI using in component context behavior
      console.log('no redos left')
      return
    }
    history.redo()
  },
})
</script>
<template> ... </template>
<script setup lang="ts">
// OtherComponent.vue
// incase you need to access this stuff again
const { execute, canRedo, canUndo, undo, redo } = useVueHistory()
// anywhere in your code (in or out of component)
const { execute } = getGlobalHistory()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions