Skip to content

Commit

Permalink
feat: add tool useKeyboardShortcuts
Browse files Browse the repository at this point in the history
  • Loading branch information
robertrosman committed Jun 3, 2024
1 parent ccc673b commit d7e29dd
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/components/VpEditor.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { computed, onMounted, ref, toRef } from 'vue'
import type { DrawEvent, SaveParameters, Settings, Shape, Tool } from '../types'
import VpImage from './VpImage.vue'
import VpToolbar from './VpToolbar.vue'
Expand Down Expand Up @@ -145,7 +145,7 @@ async function reset() {
const shapes = await Promise.all(
props.tools
.filter((tool) => 'onInitialize' in tool)
.flatMap(async (tool) => await tool.onInitialize?.({ tools: props.tools }))
.flatMap(async (tool) => await tool.onInitialize?.({ tools: props.tools, settings: toRef(settings), history: toRef(history) }))
)
history.value = [...shapes.filter(Boolean), ...history.value]
emit('reset')
Expand Down
3 changes: 2 additions & 1 deletion src/composables/tools/useAllTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { useBackground } from './useBackground/useBackground'
import { useTextarea } from './useTextarea/useTextarea'
import { useEraser } from './useEraser/useEraser'
import { useMove, type UseMoveOptions } from './useMove/useMove'
import { useKeyboardShortcuts } from './useKeyboardShortcuts/useKeyboardShortcuts'

export interface Options extends UseMoveOptions {
backgroundImage?: Blob | Promise<Blob>
backgroundColor?: string
}

export function useAllTools(options?: Options) {
const tools = [useFreehand(), useLine(), useArrow(), useRectangle(), useTextarea(), useCrop(), useEraser(), useMove(options)]
const tools = [useFreehand(), useLine(), useArrow(), useRectangle(), useTextarea(), useCrop(), useEraser(), useMove(options), useKeyboardShortcuts()]
if (options?.backgroundImage || options?.backgroundColor) {
return { tools: [useBackground({ blob: options?.backgroundImage, color: options?.backgroundColor }), ...tools] }
}
Expand Down
58 changes: 58 additions & 0 deletions src/composables/tools/useKeyboardShortcuts/useKeyboardShortcuts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Tool, BaseShape, InitializeEvent } from '@/types'
import { computed } from 'vue'
import { useActiveElement, useMagicKeys, whenever } from '@vueuse/core'
import { logicAnd } from '@vueuse/math'

// Feels weird to create a Shape that will never be added to the history. Can we support Tool<undefined>?
export interface KeyboardShortcuts extends BaseShape {
type: 'keyboard-shortcuts'
}

interface UseKeyboardShortcutsOptions {
/** You can use whatever shortcuts you want. Have a look at defaultShortcuts to see the format. If you only want to
* change something small, you might use defaultSettings as a base:
*
* @example
* const shortcuts = Object.assign({}, defaultShortcuts, {
* 's': ({settings}) => settings.value.tool = 'rectangle', // 's' as in 'select'
* 'd': ({settings}) => settings.value.tool = 'eraser', // 'd' as in 'delete'
* })
* const tools = [useKeyboardShortcuts({ shortcuts }), useAnotherTool(), useAThirdTool]
*/
shortcuts?: Shortcuts
}

type Shortcuts = Record<string, (args: InitializeEvent) => void>

export const defaultShortcuts: Shortcuts = {
'f': ({settings}) => settings.value.tool = 'freehand',
'l': ({settings}) => settings.value.tool = 'line',
'a': ({settings}) => settings.value.tool = 'arrow',
'r': ({settings}) => settings.value.tool = 'rectangle',
't': ({settings}) => settings.value.tool = 'textarea',
'c': ({settings}) => settings.value.tool = 'crop',
'e': ({settings}) => settings.value.tool = 'eraser',
'm': ({settings}) => settings.value.tool = 'move',
}

export function useKeyboardShortcuts({ shortcuts = defaultShortcuts }: UseKeyboardShortcutsOptions = {}): Tool<KeyboardShortcuts> {
const type = 'keyboard-shortcuts'

const activeElement = useActiveElement()
const notUsingInput = computed(() =>
activeElement.value?.tagName !== 'INPUT' &&
activeElement.value?.tagName !== 'TEXTAREA'
)

const keys = useMagicKeys()

async function onInitialize(event: InitializeEvent) {
Object.entries(shortcuts).forEach(([keycode, callback]) => {
whenever(logicAnd(keys[keycode], notUsingInput), () => {
callback(event)
})
})
}

return { type, onInitialize }
}
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Background } from './composables/tools/useBackground/useBackground
import type { MaybeRef, Position } from '@vueuse/core'
import type { Textarea } from './composables/tools/useTextarea/useTextarea'
import type { Move } from './composables/tools/useMove/useMove'
import type { Ref } from 'vue'

/** These settings are editable by the user and will affect what tool to use and style etc. */
export interface Settings {
Expand Down Expand Up @@ -234,6 +235,8 @@ export interface DrawEvent {

export interface InitializeEvent {
tools: Tool<Shape>[]
settings: Ref<Settings>
history: ImageHistory<Tool<any>[]>
}

export type Shape = Freehand | Crop | Rectangle | Line | Arrow | Background | Textarea | Eraser | Move
Expand Down

0 comments on commit d7e29dd

Please sign in to comment.