今天開始第一個內建 module 的介紹,在第六天的時候我們就已經介紹如何自訂工具列,這篇就當作複習,跟著技術文件介紹來練習體驗。

工具列模組 (Toolbar module) 可以讓使用者輕鬆的將文本內容套用格式。Toolbar 除了初始化設置要開啟的功能後直接渲染,我們也可以自行定義 container 內容以及工具列功能的處理器 (handler)。

Toolbar module 設定

Toolbar 的設定方式可分為兩種,一種是指定 toolbar 的容器 (container),並視需求加上 HTML 控制項以及對應的處理器 (handler),另一種則是直接使用陣列來設置。

透過指定 container 的設置方式:

<p>toolbar-practice works!</p>
<div #myToolbar></div>
<div #quillContainer></div>
@ViewChild('quillContainer') quillContainer!: ElementRef;
@ViewChild('myToolbar') myToolbar!: ElementRef;
quill!: Quill;
ngAfterViewInit(): void {
    this.quill = new Quill(this.quillContainer.nativeElement, { 
      modules: { 
        toolbar: { 
          container: this.myToolbar.nativeElement, 
          handlers: { 
            bold: (value: boolean) => {
              console.log('value', value);
              this.quill.format('bold', value);
            }, 
          }
        } 
      } 
    });
}

toolbar 也可直接給 container 的 id selector,這裡我們直接用 template reference

const quill = new Quill(this.quillContainer.nativeElement, { 
    modules: { // Equivalent to { toolbar: { container: '#toolbar' }} 
        toolbar: this.myToolbar.nativeElement 
    } 
});

可以看到直接指定 container 之後,就可以直接渲染,但因為沒有設定要放哪些功能按鈕在工具列上,所以現在看到是還沒有任何按鈕的:

現在看到是還沒有任何按鈕

Container

工具列的控制項可以帶入控制項名稱的陣列或自定義 HTML 容器來指定。
從基本的陣列來設定 toolbar 開始:

const toolbarOptions = ['bold', 'italic', 'underline', 'strike']; 
this.quill = new Quill(this.quillContainer.nativeElement, { 
    modules: { 
        toolbar: toolbarOptions 
    } 
});

控制項分組與自定義

控制項也能放在巢狀陣列來表示設定分組,這樣可以將同一組的控制項放在 classNameql-formats<span> 標籤下以提供佈景主題利用,例如在佈景主題 snow) 時,就會在這些分組間加上間距,方便使用者進行操作:

const toolbarOptions = [['bold', 'italic'], ['link', 'image']];

另外可以使用一個物件來指定自定義值的按鈕,並將格式名稱作為 key:

const toolbarOptions = [{ 'header': '3' }];

上面這個範例會在工具列上加入一個按鈕,這個按鈕代表的是 header 格式,並且按鈕會套用 3 這個自定義的值。換句話說,當點擊這個按鈕時,選定的文字會變成第三級標題 <h3>

下拉選單

下拉選單也是透過物件來定義,但與其他元素不同的地方在於,這裡會用一個陣列來存入所有可能的選項值。下拉選單選項的視覺表現(例如文字標籤或顏色)是由 CSS 來控制的。

例如,設定字體大小 size 的選項:

// Note false, not 'normal', is the correct value 
// quill.format('size', false) removes the format, 
// allowing default styling to work 
const toolbarOptions = [ { size: [ 'small', false, 'large', 'huge' ]} ];

size 陣列中的 false 是用於移除格式,也就是把文字的大小回到預設的狀態。

佈景主題和預設值

某些佈景主題,例如 Snow,會為下拉選單(如顏色和背景格式)提供預設值。當設定空的陣列在 colorbackground 時, Snow 將預設提供 35 種顏色選項:

const toolbarOptions = [ 
    ['bold', 'italic', 'underline', 'strike'], // toggled buttons 
    ['blockquote', 'code-block'], 
    
    [{ 'header': 1 }, { 'header': 2 }], // custom button values 
    [{ 'list': 'ordered'}, { 'list': 'bullet' }], 
    [{ 'script': 'sub'}, { 'script': 'super' }], // 升冪與降冪
    [{ 'indent': '-1'}, { 'indent': '+1' }], // 縮排與減少縮排
    [{ 'direction': 'rtl' }], // text direction 
    
    [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown 
    [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 
    
    [{ 'color': [] }, { 'background': [] }], // dropdown 從 theme 獲取預設值 
    [{ 'font': [] }], 
    [{ 'align': [] }], 
    
    ['clean'] // 移除格式 
]; 

this.quill = new Quill(this.quillContainer.nativeElement, { 
    modules: { 
        toolbar: toolbarOptions 
    }, 
    theme: 'snow' 
});

進階客製化(Advanced Customization)

如果需要對工具列更多的客製化,也能直接用 HTML 來手動創建工具列。只需要將 DOM 元素或選擇器傳遞給 Quill 即可。ql-toolbar 類會被添加到工具列容器中,而 Quill 會自動為具有 ql-${format} 格式名稱的 <button><select> 元素附加對應的內建 handler。

<!-- Create toolbar container --> 
<div #myToolbar>
<!-- Add font size dropdown --> 
    <select class="ql-size">
        <option value="small"></option>
        <!-- Note a missing, thus falsy value, is used to reset to default -->
        <option selected></option>
        <option value="large"></option>
        <option value="huge"></option>
    </select>
    <!-- Add a bold button -->
    <button class="ql-bold"></button>
    <!-- Add subscript and superscript buttons --> 
    <button class="ql-script" value="sub"></button>
    <button class="ql-script" value="super"></button>
</div>
<div #quillContainer></div> <!-- Initialize editor with toolbar --> 
this.quill = new Quill(this.quillContainer.nativeElement, { 
    modules: { 
        toolbar: this.myToolbar.nativeElement 
    } 
}); 

自訂按鈕(Custom Buttons)

當我們提供自己的 HTML 元素作為 Quill 的工具列時,Quill 會尋找特定的輸入元素來綁定功能。然而,除了 Quill 會自動識別和處理的元素外,你仍然可以加入和設計與 Quill 無關的自定義輸入元素。這些自定義的輸入元素可以和 Quill 的元素共存,且不會產生衝突。

<div #myToolbar>
<!-- Add buttons as you would before -->
    <button class="ql-bold"></button>
    <button class="ql-italic"></button>
    <!-- But you can also add your own -->
    <button class="custom-button" (click)="doSomething()">do something</button>
</div>
<div id="editor"></div>
this.quill = new Quill(this.quillContainer.nativeElement, {
    modules: {
        toolbar: this.myToolbar.nativeElement
    }
}); 

處理器 (Handler)

工具列的控制項預設會套用或移除格式,但我們也可以用自定義的 handler 來取代行為,例如顯示外部的使用者介面。
Handler function 會綁定到工具列,並且傳入輸入元素的 value 屬性。如果相對應的格式是非啟動狀態,則會傳入 false。加入自定義的 handler 會覆寫預設的工具列和主題行為。

const toolbarOptions = {
  handlers: {
    // handlers 物件會與預設的 handler 物件合併
    link: (value: string) => {
      if (value) {
        const href = prompt('Enter the URL');
        this.quill.format('link', href);
      } else {
        this.quill.format('link', false);
      }
    },
  }
}

this.quill = new Quill(this.quillContainer.nativeElement, {
  modules: {
    toolbar: toolbarOptions
  }
});

// Handler 也可以在初始化之後加入
const toolbar = this.quill.getModule('toolbar');
toolbar.addHandler('image', showImageUI);

在上面的範例,為 link 格式定義了一個 custom handler。當使用者點擊工具列的連結按鈕時,會彈出一個提示框可以輸入URL。當使用者輸入URL 則會把選取的文字變成一個連結。反之當使用者取消操作會移除文字的連結格式。另外,可以在 Quill 初始化之後,動態地加入更多的 handler,如 showImageUI 這個函數用來處理圖像插入的 UI。

小結

Quill 在初始化的時候,就算沒有給任何的 toolbar 設定,也會提供預設的功能選項直接使用,設定 toolbar 主要有兩種方式:

  1. 使用 HTML 設置功能按鈕並指定 toolbar 的 container
    • Quill 會依照 ql-* class 名稱的 HTML 帶入對應的內建功能
  2. 使用 toolbar options 陣列設置需要的功能
    • 不需要額外給定 HTML 或 container 即可初始化後渲染到 Quill 容器上

透過這些方式,我們可以靈活設計和調整 Quill 編輯器的工具列來滿足各種需求。

雜記

今天對於北北基桃來說是個再正常不過的上班日了,但不知道為啥就沒有上班的氛圍,我想應該是新竹以南的夥伴們都在家防颱吧。沒有會議的一天可以完全專注的在開發工作上,雖然過程也遇到一些意外的挑戰,但還好都有初步解決了。明天還有一天班,之後就要好好充電休息一下,但還是要持續發文到 15 號了,希望扛的住…XD

Reference

文章同步發表於2023 iThome 鐵人賽