Mình viết blog này bằng Rails nên dùng luôn trình soạn thảo mặc định của nó là Action Text (AT) để viết bài. AT tới lượt nó lại dùng Trix, editor viết bằng JavaScript (JS) cây nhà lá vườn của công ty 37signals. Thanh toolbar mặc định gồm những nút chức năng như sau:
Toolbar mặc định của Trix
Nhóm 4 nút bên trái để định dạng nội dung được chọn. 7 nút tiếp theo định dạng khối văn bản. Nút hình kẹp để thêm file. 2 nút mũi tên nằm cuối để hoàn tác qua lại. Nhưng lại thiếu mất một chức năng mình cần đó là định dạng code trên dòng. Vì là blog kĩ thuật hay viết code từa lưa mà thiếu nó thì khá cùi. Vậy nên hôm nay mình quyết định thêm chức năng này và sẵn tiện bỏ luôn 2 nút thụt lề trong nhóm thứ 2. Kết quả cuối cùng sẽ như này:
Thanh toolbar mới
Để ý nút thứ 3 hình chữ C bên trong hình vuông, chính nó là đối tượng của bài viết hôm nay đấy.
Mình cần liệt kê các tech stack đang dùng để tiện các bạn theo dõi: Rails 8.1, Tailwind 4, Action Text + Trix editor và Stimulus.
Bản thân AT không hỗ trợ custom toolbar sẵn nên mình phải dùng JS để hack phá nó. Muốn vậy mình phải có một controller kết nối với DOM. Tạo sườn với hàm custom():
// app/javascript/controllers/trix_custom_controller.js
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
custom() {}
}
Hàm custom được dùng để điều chỉnh toolbar theo ý mình. Giờ mình cần phải bắt được sự kiện khi Trix khởi tạo để có thể sửa nó trước khi render toolbar. Để làm được điều này, trước tiên mình phải đọctài liệu!
Và event đó là trix-before-initialize. Mình nhắc qua về cách bắt sự kiện bằng Stimulus, thêm một data attribute vào DOM phát sự kiện (ở đây là form.rich_text_area cung cấp bởi AT) với cú pháp data-action="event->controller#function". Áp dụng:
Giờ là phần chính. Thêm nút với chức năng vào toolbar như thế nào? Trong tài liệu có đoạn:
To change the toolbar without modifying Trix, you can overwrite the Trix.config.toolbar.getDefaultHTML() function. The default toolbar HTML is in config/toolbar.js. Trix uses data attributes to determine how to respond to a toolbar button click.
Có 2 manh mối:
Thay đổi toolbar bằng cách viết đè getDefaultHTML() với nội dung nằm trong file config/toolbar.js
Trix dùng data attribute để xác định cách xử lý một nút nhấn
Vào file toolbar.js sẽ thấy HTML mặc định, copy về và viết đè hàm trong controller:
Vì template mặc định dùng lang nên mình phải tạo một biến lang tương ứng. Để ý dòng button thứ 3. Đây là nút mình mới thêm. Có 2 data attribute cần phải sửa lại đó là:
data-trix-attribute với giá trị là inlineCode. Ta cần bảo Trix làm gì khi nút có data được nhấn và inlineCode là cách để xác định.
data-trix-key với shift+c. Giá trị này để cài đặt phím tắt meta + shift + C.
Tiếp theo, làm thế nào để Trix biết nên làm gì khi nhấn vào nút này? Tài liệu chỉ ra rằng:
Trix will determine that a range of text is selected and will apply the formatting defined in Trix.config.textAttributes (found in config/text_attributes.js).
Xem thử file text_attributes.js thì thấy Trix định nghĩa các format (ở đây là bold) theo cấu trúc như sau:
Về mặt chức năng thì đã ổn rồi, nhưng cần cho nút bấm một icon đại diện. Mình nghĩ tới một chữ C đặt trong khung vẽ bằng SVG. Trix dùng CSS để hiện icon này. Bạn có thể xem các icon mặc định trong file app/assets/stylesheets/actiontext.css: