Skip to content

Structure

์œ ๋™์‹ edited this page May 25, 2017 · 1 revision

Modules

๋‚ด๋ถ€์ ์œผ๋กœ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ๋ ˆ์ด์–ด๋กœ ๋ถ„๋ฆฌ๊ฐ€ ๋˜๋ฉฐ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ImageEditor - API๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด๋กœ ์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ๋ ˆ์ด์–ด๋ฅผ ๊ฐ€์ง€๋ฉฐ, UI์™€ ์ง์ ‘ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ํ•œ๋‹ค.
    • Middle Layerย - ๊ทธ๋ฆฌ๊ธฐ ๋™์ž‘๊ณผ ๋ฌด๊ด€ํ•œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ง‘ํ•ฉ์œผ๋กœ Invoker, Command, CommandFactory๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
    • Graphics Layerย - ์ผ๋ฐ˜์ ์œผ๋กœ ๋งํ•˜๋Š” canvas๋ฅผ ImageEditor์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ๋“ค๋กœ๋งŒ ์ด๋ฃจ์–ด์ง„ ย Canvasย ๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ธฐ ๋™์ž‘์„ ์ถ”์ƒํ™”ํ•˜๊ณ  ์‹ค์ œ ๊ตฌํ˜„์€ย fabric.jsย ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
      • Component๋Š” ํŠน์ • ๊ธฐ๋Šฅ์˜ ๊ทธ๋ฆฌ๊ธฐ ๋™์ž‘์„ ๋ชจ๋“ˆํ™” ํ•œ ๊ฒƒ์œผ๋กœ Graphics Layer์— ์†Œ์†๋œ๋‹ค.
      • ๋“œ๋กœ์ž‰ ๋ชจ๋“œ ๋˜ํ•œ Graphics Layer์—์„œ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ์—ฌ๊ธฐ์—์„œ ๋‹ด๋‹นํ•œ๋‹ค.

ImageEditor

API๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด๋กœ ์•„๋ž˜์— ์ •์˜ํ•  Invoker์™€ Graphics๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ€์ง„๋‹ค.

CommandFactory

Command๋ฅผ ๋“ฑ๋ก, ์ƒ์„ฑํ•˜๋Š” ํด๋ž˜์Šค์ด๋‹ค. Command๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๊ณ  ImageEditor๋‚˜ Command๋กœ ๋“œ๋กœ์ž‰ ๋™์ž‘์„ ํ•ด์•ผ ํ•˜๋Š” ๊ณณ์—์„œ ๋“ฑ๋ก๋œ Command ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  • register - Command ๋“ฑ๋ก
  • create - ๋“ฑ๋ก๋œ Command ์˜ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ

Command

Command๋Š” ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ธ ์‹คํ–‰ ๋‹จ์œ„์ด๋ฉฐ ๋‹ค๋ฅธ ๋ชจ๋“ˆ๊ณผ ๋…๋ฆฝ์ ์ด๋‹ค. ์ด๋ฏธ์ง€ ์—๋””ํ„ฐ์—์„œ๋Š” Undo/Redo๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ ์‹คํ–‰ ๋‹จ์œ„๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ Command ์ธ์Šคํ„ด์Šค๋ฅผ Invoker์—์„œ ์Šคํƒ์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.
Command ๋“ฑ๋ก์€ name, execute, undo๋ฅผ ์ •์˜ํ•œ ๊ฐ์ฒด actions์™€ args๋ฅผ ์ „๋‹ฌ ๋ฐ›๋Š”๋‹ค.

Command ํด๋ž˜์Šค

class Command {
    constructor(actions, args) {
        this.name = actions.name;
        this.args = args;
        this.execute = actions.execute;
        this.undo = actions.undo;
        this.executeCallback = actions.executeCallback || null;
        this.undoCallback = actions.undoCallback || null;
        this.undoData = {};
    }
}

Command ๋“ฑ๋ก ๊ณผ์ •

import๊ฐ€ ๋  ๋•Œ CommandFactory.register๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. Command ์—์„œ๋Š” ์‚ฌ์šฉํ•  Component๋ฅผ import ํ•˜๊ณ  Graphics ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ๊ธฐ๋Šฅ์ˆ˜ํ–‰ํ•˜๊ฒŒ ํ•œ๋‹ค.

// commandName.js
import commandFactory from '../factory/command';
const command = {
   name: 'commandName',
   execute(graphics, ...args) {

   },
   undo(graphics, ...args) {

   }
};

CommandFactory.register(command);
module.export = command;

Command ์ƒ์„ฑ ๋ฐ ์‹คํ–‰

const command = commandFactory.create('commandName', param1, param2);
command.execute(...command.args).then(value => {
    // push undo stack
    return value;
}).catch(message => {
    // do something
    return Promise.reject(message);
})

Invoker

Command๋ฅผ ์‹คํ–‰ํ•˜๊ณ  Undo/Redo๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์€ ์ œ๊ฑฐํ•œ๋‹ค.

  • Component ๋ฆฌ์ŠคํŠธ ๊ด€๋ฆฌ๋Š” ์—ฌ๊ธฐ์„œ๋Š” ์ œ๊ฑฐํ•œ๋‹ค. ์•„๋ž˜์— ๋ช…์„ธํ•  Canvas๋กœ ์ด๊ด€ํ•œ๋‹ค.

Canvas

ImageEditor์˜ ๋“œ๋กœ์ž‰ ์ฝ”์–ด ๋ชจ๋“ˆ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋“œ๋กœ์ž‰ ๊ธฐ๋Šฅ๋“ค์˜ ๋ชจ๋‘ ๋“ค์–ด ์žˆ๋‹ค. ํ•˜๋ถ€์˜ ์‹ค์ œ ๊ทธ๋ž˜ํ”ฝ ๋ชจ๋“ˆ์€ fabric.js ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ž˜๋Š” ์ด๋ฏธ์ง€ ์—๋””ํ„ฐ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ ๋ชฉ๋ก์ด๋‹ค.(๋ถ‰์€ ์ƒ‰์€ ์‹ ๊ทœ API)

Icon Image Shape Text Flip FreeDrawing LineDrawing Rotate Crop ๊ธฐํƒ€
addIcon addImageObject addShape addText flipX setBrush setBrush rotate crop resizeCanvasDimension
registerIcons loadImageFromFile setDrawingShape changeText flipY setAngle getCropzoneRect toDataURL
changeIconColor loadImageFromURL changeShape changeTextStyle resetFlip getDrawingMode
ApplyFilter setDrawingMode
RemoveFilter getImageName
hasFilter clearObjects
removeActiveObject
destroy
setDefaultPathStyle

<Canvas ๊ด€๋ จ ์ด๋ฒคํŠธ> Canvas ๋Š” CustomEvents๋ฅผ ย mixin ํ•˜๊ณ  Canvas ์™€ ๊ด€๋ จํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ on("eventName", callback); ์„ ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ž˜๋Š” ๋ฆฌํŒฉํ† ๋ง ์ด์ „ ๊ธฐ์ค€์œผ๋กœ fabric.js ๋กœ๋ถ€ํ„ฐ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ ์ค‘์— ๋ณ€๊ฒฝ๋  ์ด๋ฒคํŠธ๋“ค์ด๋‹ค.

์ด๋ฒคํŠธ ์ด๋ฆ„ ํ•ธ๋“ค๋Ÿฌ from to ์™ธ๋ถ€์ด๋ฒคํŠธ ์ด๋ฆ„ ์šฉ๋„
path:created _onCreatePath fabric ImageEditor
->Canvas๋กœ ์ด๋™ ์˜ˆ์ •
N/A path ์Šคํƒ€์ผ์„ ํ•˜๋‚˜๋กœ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉ

Component

Component๋Š” ๊ธฐ์กด๊ณผ ๋™์ผํ•˜๊ฒŒ ํŠน์ • ๋“œ๋กœ์ž‰ ๋™์ž‘์„ ๊ตฌํ˜„ํ•œ ๋ชจ๋“ˆ์ด๋‹ค. ๋‹ค๋ฅธ ์ ์€ Component๋Š” ๋“œ๋กœ์ž‰ ์ง‘ํ•ฉ์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ์ด๋ฏ€๋กœ Canvas๋ฅผ ํ†ตํ•˜์—ฌ์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•œ๋‹ค. ย ์ด์ „์—๋Š” Command์—์„œ Component๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์˜€์ง€๋งŒ ๋ฐ”๋€” ๊ตฌ์กฐ์—์„œ๋Š” Command๋Š” Canvas๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. Canvas๋Š” ๋‹ค์–‘ํ•œ Component๋“ค์„ ๊ด€๋ฆฌํ•œ๋‹ค. ๋˜ํ•œ Component์—์„œ ์™ธ๋ถ€๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” ์ด๋ฒคํŠธ๋Š” Canvas๋ฅผ ํ†ตํ•˜์—ฌ ์ „๋‹ฌํ•˜๋„๋ก ํ•œ๋‹ค. Canvas๋Š” ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ Canvas ์™ธ๋ถ€์—์„œ ๋“ฑ๋กํ•œ ๊ฒฝ์šฐ ๋‹ค์‹œ ์ „๋‹ฌํ•œ๋‹ค. Component ๋ชฉ๋ก์€ ์•„๋ž˜์™€ ๊ฐ™๊ณ  ๊ธฐ์กด์— start/end์™€ ๊ฐ™์ด ๋ชจ๋“œ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ Component๋Š” ํ‘œ์‹œํ•˜์˜€๋‹ค.

์ด๋ฆ„ ๋ชจ๋“œ ํ•„์š” ์šฉ๋„ to-be
Cropper O Crop ๋ชจ๋“ˆ, Crop์šฉ UI์™€ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ํ•„์š” ์œ ์ง€
filter X ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ๋ชจ๋“ˆ ์œ ์ง€
flip X ์ด๋ฏธ์ง€ ๋’ค์ง‘๊ธฐ ๋ชจ๋“ˆ ์œ ์ง€
freeDrawing O ์ž์œ  ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“ˆ ์œ ์ง€
icon X ์•„์ด์ฝ˜ ์ถ”๊ฐ€ ๋ชจ๋“ˆ ์œ ์ง€
imageLoader X ๋ฉ”์ธ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋ชจ๋“ˆ ์œ ์ง€
line O ์ง์„  ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“ˆ ์œ ์ง€
rotation X ๋ฉ”์ธ ์ด๋ฏธ์ง€ ๋ฐ ์˜ค๋ธŒ์ ํŠธ๋“ค ํšŒ์ „ ๋ชจ๋“ˆ ์œ ์ง€
shape O ๋„ํ˜• ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“ˆ ์œ ์ง€
text O ํ…์ŠคํŠธ ์˜ค๋ธŒ์ ํŠธ ์ž…๋ ฅ ๋ชจ๋“ˆ ์œ ์ง€
main X ๊ณตํ†ต ํ”„๋กœํผํ‹ฐ ๊ด€๋ฆฌ ์ œ๊ฑฐ ๋ฐ ๊ธฐ๋Šฅ Canvas๋กœ ์ด๊ด€

๋“œ๋กœ์ž‰ ๋ชจ๋“œ๋Š” ์ƒํ˜ธ ๋ฒ ํƒ€์ , Command ์šด์˜๋Š” ์‚ฌ์šฉ์ž์˜ ๋ชซ

๋“œ๋กœ์ž‰ ๋ชจ๋“œ๋Š” ํ•œ ์ˆœ๊ฐ„์— ํ•˜๋‚˜๋งŒ ํ™œ์„ฑํ™” ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ, ๊ฐ ๋ชจ๋“œ๋งˆ๋‹ค ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฒคํŠธ์™€ UI๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋“œ๋กœ์ž‰ ๋ชจ๋“œ๋Š” ์ƒํ˜ธ ๋ฒ ํƒ€์ ์ด๋‹ค. ๊ทธ์— ๋ฐ˜ํ•ด Command๋Š” ๋“œ๋กœ์ž‰ ๋™์ž‘์— ๋Œ€ํ•ด์„œ ์ •์˜ํ•œ ๋ช…๋ น์ด๊ธฐ ๋•Œ๋ฌธ์— ๋“œ๋กœ์ž‰ ๋ชจ๋“œ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋™์ž‘ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋“œ๋กœ์ž‰ ๋ชจ๋“œ์— ์ข…์†์„ฑ์„ ์—†์• ๊ณ  ๊ทธ ์šด์˜์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์œ„์ž„ํ•œ๋‹ค. ๋“œ๋กœ์ž‰ ๋ชจ๋“œ์™€ Command ์˜ ์‹คํ–‰ ์—ฌ๋ถ€๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ๊ตฌํ˜„์˜ ๊ฐ„๊ฒฐ์„ฑ๊ณผ ๋ฒ„๊ทธ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ, ์œ ์ง€๋ณด์ˆ˜์— ๋Œ€ํ•œ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฏธ์ง€ ์—๋””ํ„ฐ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์‹์ ์ธ API ํ˜ธ์ถœ์„ ํ•œ๋‹ค๊ณ  ๋ฏฟ๋Š”๋‹ค.

<์ƒ์‹์  ์‚ฌ์šฉ ์˜ˆ>

editor.setDrawingMode("cropper");
editor.crop(editor.getCropzoneRect());
editor.setDrawingMode("normal");

<๋œฌ๊ธˆ์—†๋Š” ์‚ฌ์šฉ์˜ˆ>

editor.setDrawingMode("cropper");
editor.rotate(90);
editor.setDrawingMode("normal");

๋œฌ๊ธˆ์—†๋Š” ์‚ฌ์šฉ์˜ˆ์ด์ง€๋งŒ, ๋“œ๋กœ์ž‰ ๋ชจ๋“œ์™€ Command๊ฐ€ ๋ฌด๊ด€ํ•จ์„ ๋ณด์—ฌ์ฃผ๋Š” ํ•˜๋‚˜์˜ ์˜ˆ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋ณ€๊ฒฝ๋  API์™€ ๋“œ๋กœ์ž‰๋ชจ๋“œ/Command์˜ ๋ฌด๊ด€์„ฑ ๊ฐœ๋…์„ ๋ฐ”ํƒ•์œผ๋กœ ๋‚ด๋ถ€ ์„ค๊ณ„์— ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•œ๋‹ค.

์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

Canvas Layer์˜ ๋ชจ๋“ˆํ™”๋ฅผ ์œ„ํ•˜์—ฌ Canvas ๋‚ด๋ถ€์—์„œ ์ผ์–ด๋‚˜๋Š” ๋ชจ๋“  ์ด๋ฒคํŠธ๋Š” Canvas๋ฅผ ํ†ตํ•˜์—ฌ ์™ธ๋ถ€๋กœ ์ „๋‹ฌํ•œ๋‹ค. Canvas๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” Component์—์„œ ์ผ์–ด๋‚˜๋Š” ์ด๋ฒคํŠธ๋Š” Canvas์—๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  Canvas๊ฐ€ ์™ธ๋ถ€๋กœ ์ „๋‹ฌํ•œ๋‹ค. ImageEditor๋Š” ์™ธ๋ถ€์™€ ์ง์ ‘์ ์œผ๋กœ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์„ ํ•˜๋ฏ€๋กœ UI๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ชจ๋“  ์ฝœ๋ฐฑ์€ ImageEditor ํ†ตํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ย Canvas์™€ ย Component๋กœ๋ถ€ํ„ฐ ์˜ฌ๋ผ์™€์•ผ ํ•˜๋Š” ์ด๋ฒคํŠธ๋Š” ImageEditor ๋‚ด์—์„œ ย Canvas์— ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ์ „๋‹ฌ ๋ฐ›๊ฒŒ ํ•œ๋‹ค Canvas์—์„œ ์˜ฌ๋ผ์˜จ ์ด๋ฒคํŠธ๊ฐ€ undo ์Šคํƒ์œผ๋กœ ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ImageEditor ์—์„œ Canvas๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ย Invoker ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

UI ์ „๋‹ฌ ์ด๋ฒคํŠธ ์ •๋ฆฌ

UI ๋กœ ์ „๋‹ฌ๋˜๋Š” ์ด๋ฒคํŠธ๋Š” ๋Œ€๋ถ€๋ถ„ Promise ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ์‹คํ–‰ ์™„๋ฃŒ๋ฅผ ์ „๋‹ฌํ•  ๊ฒƒ์ด๊ณ , Undo/Redo์™€ ๊ด€๋ จํ•œ ์ด๋ฒคํŠธ๋Š” UI ํˆด๋ฐ” ์ƒํƒœ๊ฐ’ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•˜์—ฌ ๊ธฐ์กด๊ณผ ๊ฐ™์ด UI๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ๋‚จ๋Š” UI ์ด๋ฒคํŠธ๋Š” 5๊ฐœ์ด๋‹ค. objectActivated, objectStyleChanged, mousedown, redoStackChanged, undoStackChanged

Name Purpose
addText when mousedown event occurs in 'TEXT' drawing mode
objectActivated when user selects an object
objectMoved when user drags an object
objectScaled when object is being scaled
textEditing when textbox is being edited
mousedown just mousedown
undoStackChanged undo change event
redoStackChanged redo change event

Example

/*
{
    id: number
    type: type
    left: number,
    top: number,
    width: number,
    fill: string
    stroke: string
    strokeWidth: number
    opacity: number,

    // text object
    text: string,
    fontFamily: string,
    fontSize: number,
    fontStyle: string,
    fontWeight: string,
    textAlign: string,
    textDecoration: string
}
*/
imageEditor.on('objectActivated', function(props) {
    console.log(props);
    console.log(props.type);
    console.log(props.id);
});

ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ

class ServiceUI {
  -ImageEditor _imageEditor
}

class ImageEditor {
  -Invoker _invoker
  -Graphics _graphics

ย ย -void execute(commandName)
}

package "Middle Layer" #DDDDDD {
  class Invoker
  class Command
  class CommandFactory
}

together {
  class Invoker
  class Command
  class CommandFactory
}

package "Graphics Layer" #DDDDDD {
  class Graphics

}

package "Component" {
  class Component
  class Cropper
  class Filter
  class Flip
  class FreeDrawing
  class Icon
  class ImageLoader
  class Line
  class Rotation
  class Shape
  class Text
}

package "DrawingMode" {
  class DrawingMode
  class CropperDrawingMode
  class FreeDrawingMode
  class LineDrawingMode
  class ShapeDrawingMode
  class TextDrawingMode
}

class Invoker {
  -Array _undoStack
  -Array _redoStack

  +Promise execute("commandName")
  +Promise undo()
  +Promise redo()
  +void pushUndoStack(command, isSilent)
  +void pushRedoStack(command, isSilent)
}

class CommandFactory {
  -Map _commands

  +void register(commandObject)
  +Command create("commandName", ...args)
}

class Command {
  +string name
ย ย +Array args
  +Object _undoData

  +Promise execute()
  +Promise undo()
  +Command setExecuteCallback(callback)
  +Command setUndoCallback(callback)
}

class Graphics {
  -DrawingMode[] _drawingModeInstances
  -Component[] _components
  -Fabric.Canvas _canvas

  +setDrawingMode("modeName")
  +setDefaultPathStyle(style)
  +on("eventName", callback)
}

class Component {

}

class DrawingMode {

}

ServiceUI o-- ImageEditor
ImageEditor o-- Graphics
ImageEditor o-- Invoker
ImageEditor <|-- tui.util.CustomEvents
ImageEditor --> CommandFactory
ImageEditor --> Command

Invoker <|-- tui.util.CustomEvents
Invoker --> CommandFactory
Invoker o-- Command

Command o-- Graphics
Command o-- Component

Graphics o-- DrawingMode
Graphics o-- Component
Graphics o-- Fabric.Canvas
Graphics <|-- tui.util.CustomEvents

Component <|-- Cropper
Component <|-- Filter
Component <|-- Flip
Component <|-- FreeDrawing
Component <|-- Icon
Component <|-- ImageLoader
Component <|-- Line
Component <|-- Rotation
Component <|-- Shape
Component <|-- Text

Cropper <-- Fabric.Canvas
Filter <-- Fabric.Canvas
Flip <-- Fabric.Canvas
FreeDrawing <-- Fabric.Canvas
Icon <-- Fabric.Canvas
ImageLoader <-- Fabric.Canvas
Line <-- Fabric.Canvas
Rotation <-- Fabric.Canvas
Shape <-- Fabric.Canvas
Text <-- Fabric.Canvas

DrawingMode --> Component
DrawingMode <|-- CropperDrawingMode
DrawingMode <|-- FreeDrawingMode
DrawingMode <|-- LineDrawingMode
DrawingMode <|-- ShapeDrawingMode
DrawingMode <|-- TextDrawingMode

CropperDrawingMode <-- Cropper
FreeDrawingMode <-- FreeDrawing
LineDrawingMode <-- Line
ShapeDrawingMode <-- Shape
TextDrawingMode <-- Text