FileBlot.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import Quill from 'quill';
  2. const BlockEmbed = Quill.import('blots/block/embed');
  3. const Parchment = Quill.import('parchment');
  4. class FileBlot extends BlockEmbed {
  5. static blotName = 'fileCard';
  6. static tagName = 'div';
  7. static className = 'file-card';
  8. static scope = Parchment.Scope.BLOCK_BLOT;
  9. static create(value) {
  10. const node = super.create();
  11. //node.setAttribute('contenteditable', 'false');
  12. node.setAttribute('data-file-name', value.name);
  13. node.setAttribute('data-file-size', value.size);
  14. node.setAttribute('draggable', 'true');
  15. node.innerHTML = `
  16. <div class="file-card-content">
  17. <div class="file-card-info">
  18. <span>📄 <span class="file-name">${value.name} (${(value.size / 1024 / 1024).toFixed(2)}MB)</span></span>
  19. </div>
  20. <div class="file-card-actions">
  21. <button data-align="start">
  22. <img src="/resources/editor/align-left.svg" alt="왼쪽" />
  23. </button>
  24. <button data-align="center">
  25. <img src="/resources/editor/align-center.svg" alt="중앙앙" />
  26. </button>
  27. <button data-align="end">
  28. <img src="/resources/editor/align-right.svg" alt="오른쪽" />
  29. </button>
  30. <button class="file-card-delete">
  31. <img src="/resources/editor/trash.svg" alt="삭제" />
  32. </button>
  33. </div>
  34. </div>
  35. `;
  36. // 클릭 시 이벤트 발생
  37. node.addEventListener('click', (e) => {
  38. e.preventDefault();
  39. e.stopPropagation();
  40. const quill = FileBlot.getQuillInstance();
  41. if (quill) {
  42. const uploader = quill?.getModule('fileUploader');
  43. // 첨부파일 활성화 해제
  44. uploader.clearActiveCards();
  45. // 에디터 커서 표시 해제
  46. quill.root.blur();
  47. }
  48. node.classList.add('active');
  49. });
  50. // 삭제 버튼
  51. node.querySelector('.file-card-delete')?.addEventListener('click', () => {
  52. node.remove();
  53. });
  54. // 정렬 버튼
  55. node.querySelectorAll('button[data-align]').forEach(btn => {
  56. btn.addEventListener('click', (e) => {
  57. const target = e.currentTarget;
  58. const align = target.dataset.align;
  59. node.querySelectorAll('button[data-align]').forEach(el => el.classList.remove('active'));
  60. node.style.justifySelf = align || 'start';
  61. target.classList.add('active');
  62. });
  63. });
  64. // 드래그 시작
  65. node.addEventListener('dragstart', (e) => {
  66. const value = {
  67. name: node.getAttribute('data-file-name'),
  68. size: node.getAttribute('data-file-size')
  69. };
  70. e.dataTransfer?.setData('text/plain', JSON.stringify(value));
  71. e.dataTransfer.effectAllowed = 'move';
  72. node.classList.add('dragging');
  73. });
  74. node.addEventListener('dragend', () => {
  75. node.classList.remove('dragging');
  76. });
  77. return node;
  78. }
  79. static value(node) {
  80. return {
  81. name: node.getAttribute('data-file-name'),
  82. size: node.getAttribute('data-file-size')
  83. };
  84. }
  85. static getQuillInstance() {
  86. return Quill.quillInstance || null;
  87. }
  88. static setQuillInstance(quill) {
  89. Quill.quillInstance = quill;
  90. }
  91. }
  92. export default FileBlot;