TrixEditor.client.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <template>
  2. <div>
  3. <input
  4. :id="`${id}_input`"
  5. :value="initialValue"
  6. type="hidden"
  7. />
  8. <trix-editor
  9. :id="id"
  10. ref="trixEditor"
  11. :input="`${id}_input`"
  12. class="trix-content"
  13. v-bind="$attrs"
  14. @trix-change="onTrixChange"
  15. @trix-focus="onTrixFocus"
  16. @trix-blur="onTrixBlur"
  17. @trix-before-initialize="onTrixBeforeInitialize"
  18. @trix-initialize="onTrixInitialize"
  19. @trix-paste="onTrixPaste"
  20. @trix-selection-change="onTrixSelectionChange"
  21. @trix-file-accept.prevent="onTrixFileAccept"
  22. @trix-attachment-add="onTrixAttachmentAdd"
  23. @trix-attachment-remove="onTrixAttachmentRemove"
  24. />
  25. </div>
  26. </template>
  27. <script setup>
  28. import Trix from "trix"
  29. defineOptions({ inheritAttrs: false })
  30. const model = defineModel()
  31. const props = defineProps({
  32. id: {
  33. type: String,
  34. required: false,
  35. default: () => useId()
  36. },
  37. config: {
  38. type: Object,
  39. required: false,
  40. default: () => {}
  41. }
  42. })
  43. const emit = defineEmits([
  44. "blur",
  45. "change",
  46. "focus",
  47. "input",
  48. "trix-attachment-add",
  49. "trix-attachment-remove",
  50. "trix-before-initialize",
  51. "trix-blur",
  52. "trix-change",
  53. "trix-file-accept",
  54. "trix-focus",
  55. "trix-initialize",
  56. "trix-paste",
  57. "trix-selection-change"
  58. ])
  59. defineExpose({
  60. id: props.id,
  61. modelValue: model,
  62. reset() {
  63. reset()
  64. }
  65. })
  66. const trixEditor = useTemplateRef("trixEditor")
  67. const initialValue = ref(_isNull(model.value) ? "" : model.value)
  68. onBeforeMount(() => {
  69. _merge(Trix.config, trixConfig, props.config)
  70. })
  71. async function reset() {
  72. if (_isNull(model.value)) {
  73. model.value = ""
  74. await nextTick()
  75. }
  76. trixEditor.value?.editor.loadHTML(model.value)
  77. }
  78. function onTrixChange(event) {
  79. model.value = event.target.value
  80. emit("change", event)
  81. emit("input", event)
  82. emit("trix-change", event)
  83. }
  84. function onTrixInitialize(event) {
  85. reset()
  86. emit("trix-initialize", event)
  87. }
  88. function onTrixFocus(event) {
  89. emit("focus", event)
  90. emit("trix-focus", event)
  91. }
  92. function onTrixBlur(event) {
  93. emit("blur", event)
  94. emit("trix-blur", event)
  95. }
  96. function onTrixBeforeInitialize(event) {
  97. emit("trix-before-initialize", event)
  98. }
  99. function onTrixPaste(event) {
  100. emit("trix-paste", event)
  101. }
  102. function onTrixSelectionChange(event) {
  103. emit("trix-selection-change", event)
  104. }
  105. function onTrixFileAccept(file) {
  106. emit("trix-file-accept", file)
  107. }
  108. function onTrixAttachmentAdd(file) {
  109. emit("trix-attachment-add", file)
  110. }
  111. function onTrixAttachmentRemove(file) {
  112. emit("trix-attachment-remove", file)
  113. }
  114. </script>
  115. <style>
  116. @reference "~/assets/css/main.css";
  117. trix-editor {
  118. /* textarea.root */
  119. /* min-height: py-2 [--spacing(4)] + border [2 * 1px = 2px] + line-height [1.5rem] */
  120. @apply max-w-full! min-h-[calc(--spacing(4)+2px+1.5rem)] appearance-none rounded-md border
  121. border-surface-300 bg-surface-0 px-3 py-2 text-surface-700
  122. shadow-[0_1px_2px_0_rgba(18,18,23,0.05)] outline-hidden transition-colors
  123. duration-200 placeholder:text-surface-500 enabled:hover:border-surface-400
  124. enabled:focus:border-primary disabled:bg-surface-200 disabled:text-surface-500
  125. dark:border-surface-700 dark:bg-surface-950 dark:text-surface-0
  126. dark:placeholder:text-surface-400 dark:enabled:hover:border-surface-600
  127. dark:disabled:bg-surface-700 dark:disabled:text-surface-400
  128. p-invalid:border-red-400 p-invalid:placeholder:text-red-600
  129. dark:p-invalid:border-red-300 dark:p-invalid:placeholder:text-red-400
  130. p-small:px-2.5 p-small:py-1.5 p-small:text-sm p-large:px-3.5 p-large:py-2.5
  131. p-large:text-lg p-fluid:w-full p-filled:bg-surface-50
  132. dark:p-filled:bg-surface-800;
  133. }
  134. </style>