iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
Modern Web

深入 slate.js x 一起打造專屬的富文字編輯器吧!系列 第 29

Day 29. slate × Transforms × Selection & Text

https://ithelp.ithome.com.tw/upload/images/20211014/201393596xn0xs9h1b.png

上一篇我們統整了 NodeTransforms 裡各個 methods 的用法以及參數介紹, 傳送門 在此~

這一篇同樣會以 reference 的形式統整 SelectionTransforms 以及 TextTransforms 的功用以及傳入參數的用途。

SelectionTransforms


collapse


  • 用途:收合編輯器 selection

  • 參數:

    • editor: Editor
    • options: CollapseOptions
  • options :

    interface CollapseOptions {
    	edge?: 'anchor' | 'focus' | 'start' | 'end'
    }
    
    • edge

      決定 selection 要往哪一個 Point 收合:

      • 'anchor'

        selectionanchor point 收合

      • 'focus'

        selectionfocus point 收合

      • 'start'

        會搭配 Range.edges 以及 Range.isBackward method 判斷 selection 是否為『反向的』並得出真正的起始、結束位置

        並往相對起始的 point 收合

      • 'end'

        會搭配 Range.edges 以及 Range.isBackward method 判斷 selection 是否為『反向的』並得出真正的起始、結束位置

        並往相對結束的 point 收合

deselect


  • 用途:取消設置編輯器的 selection value
  • 參數:
    • editor: Editor

裡頭的程式碼很基本,就是直接呼叫 'set_selection' Operation

/**
 * Unset the selection.
 */

deselect(editor: Editor): void {
  const { selection } = editor

  if (selection) {
    editor.apply({
      type: 'set_selection',
      properties: selection,
      newProperties: null,
    })
  }
},

move


  • 用途:向前或向後移動 selection 裡頭的 points 。

  • 參數:

    • editor: Editor
    • options: MoveOptions
  • options :

    interface MoveOptions {
    	distance?: number
      unit?: 'offset' | 'character' | 'word' | 'line'
      reverse?: boolean
      edge?: 'anchor' | 'focus' | 'start' | 'end'
    }
    
    • distance

      搭配 unit (單位)決定一次要移動的實際距離

    • unit

      移動的單位,分為 'offset''character''word''line' 。關於不同的單位實作的細節被集中在 Editor.positions 裡,包含它的演算法也都被作者 comment 在 method 裡。因為太過細節了這邊就不詳細解說,再請有興趣的讀者自行上前查看。

    • reverse

      決定 points 移動的方向:

      • reverse: true :向前移動
      • reverse: false :向後移動
    • edge

      決定要移動的 selection point ,分為:

      • 'anchor'

        只移動 anchor point

      • 'focus'

        只移動 focus point

      • 'start'

        會搭配 Range.isBackward method 判斷 selection 是否為『反向的』,將相對起始的 point ( 'anchor' | 'focus' ) assign 給 edge

      • 'end'

        會搭配 Range.isBackward method 判斷 selection 是否為『反向的』,將相對結束的 point ( 'anchor' | 'focus' ) assign 給 edge

      edge 的預設值為 null ,這會同時移動 anchorfocus point

select


  • 用途:重新設定編輯器的 selection value
  • 參數:
    • editor: Editor
    • target: Location

這裡頭的程式碼也很基本:

  • 包含確保的 target 為 Range type

    target = Editor.range(editor, target)
    
  • 更新 selection 屬性

    const { selection } = editor
    
    if (selection) {
      Transforms.setSelection(editor, target)
      return
    }
    
  • 拋出例外狀況

    if (!Range.isRange(target)) {
      throw new Error(
        `When setting the selection and the current selection is \`null\` you must provide at least an \`anchor\` and \`focus\`, but you passed: ${JSON.stringify(
          target
        )}`
      )
    }
    
  • 執行 Operation

    editor.apply({
      type: 'set_selection',
      properties: selection,
      newProperties: target,
    })
    

setPoint


  • 用途:對編輯器 selection 的其中一個 Point 設定新的 props 屬性

  • 參數:

    • editor: Editor
    • props: Partial<Point>
    • options: SetPointOptions
  • options :

    interface SetPointOptions {
    	edge?: 'anchor' | 'focus' | 'start' | 'end'
    }
    
    • edge

      決定要將 props 取得的屬性設定於 selection 的哪一個 Point

      • 'anchor'

        props 設定於 anchor point 上

      • 'focus'

        props 設定於 focus point 上

      • 'start'

        會搭配 Range.edges 以及 Range.isBackward method 判斷 selection 是否為『反向的』並得出真正的起始、結束位置

        並將 props 設定於相對起始的 point

      • 'end'

        會搭配 Range.edges 以及 Range.isBackward method 判斷 selection 是否為『反向的』並得出真正的起始、結束位置。

        並將 props 設定於相對結束的 point 上。

setSelection


  • 用途:設定新的 props 屬性在編輯器的 selection
  • 參數:
    • editor: Editor
    • props: Partial<Range>

除了設定 custom 屬性之外,它也同時確保不會將 selection 裡的 anchorfocus point 設為 null

for (const k in props) {
  if (
    (k === 'anchor' &&
      props.anchor != null &&
      !Point.equals(props.anchor, selection.anchor)) ||
    (k === 'focus' &&
      props.focus != null &&
      !Point.equals(props.focus, selection.focus)) ||
    (k !== 'anchor' && k !== 'focus' && props[k] !== selection[k])
  ) {
    oldProps[k] = selection[k]
    newProps[k] = props[k]
  }
}

TextTransforms


delete


  • 用途:移除編輯器內的文字內容

  • 參數:

    • editor: Editor
    • options: DeleteOptions
  • options :

    interface DeleteOptions {
    	at?: Location
      distance?: number
      unit?: 'character' | 'word' | 'line' | 'block'
      reverse?: boolean
      hanging?: boolean
      voids?: boolean
    }
    
    • at

      欲刪除的文字內容於編輯器裡的位置,分為三種刪除情形:

      • at 傳入一組 Path → 將整個節點刪除
      • at 傳入一組 Range → 將 Range 集合的文字內容刪除
      • at 傳入一個 Point → 搭配 distanceunit 決定要從該 Point 向前刪除多少文字內容
    • distance

      搭配 unit (單位)決定一次要移動的實際距離

    • unit

      移動的單位,分為 'offset''character''word''line' 。關於不同的單位實作的細節被集中在 Editor.positions 裡,包含它的演算法也都被作者 comment 在 method 裡。因為太過細節了這邊就不詳細解說,再請有興趣的讀者自行上前查看。

    • reverse

      如果傳入的 atPoint 的話, reverse 會決定節點的刪除方向

      • reverse: true :向前刪除
      • reverse: false :向後刪除
    • hanging

      這個 value 會決定 Range 是否要另外修正為 unhanging type 。

      hanging 在 Slate 裡頭的意思代表『這段 Range 涵蓋到了不存在的節點』。

      我們假設目前的 Slate Document 如下:

      [{text: 'one '}, {text: 'two', bold: true}, {text: ' three'}]
      

      這時使用者看到的顯示方式應該如下:

      one two three

      假設使用者選取了 "two" ,此時的 selection 會有幾種 anchorfocus points 的可能性出現

      // 1 -- no hanging
      {
        anchor: { path: [1], offset: 0 },
        focus: { path: [1], offset: 3 }
      }
      
      // 2 -- anchor hanging
      {
        anchor: { path: [0], offset: 4 },
        focus: { path: [1], offset: 3 }
      }
      
      // 3 -- focus hanging
      {
        anchor: { path: [1], offset: 0 },
        focus: { path: [2], offset: 0 }
      }
      
      // 4 -- both hanging
      {
        anchor: { path: [0], offset: 4 },
        focus: { path: [2], offset: 0 }
      }
      

      當我們傳入 hanging: false 時, Slate 就會將這組 Range 傳入 Editor.unhangRange method 裡確保 Range 維持在第一種情形。

    • voids

      決定在這個 Transform method 有呼叫到的所有操作行為中,是否要略過或避開 void nodes 的出現 。

這個 method 也有處理跨節點相關的情境,筆者這邊暫且不多做介紹,未來有機會再追加補充。

insertFragment


  • 用途:插入一組 Fragment 進指定的 at Location 裡

  • 參數:

    • editor: Editor
    • fragment: Node[]
    • options: InsertFragmentOptions
  • options :

    interface InsertFragmentOptions {
    	at?: Location
      hanging?: boolean
      voids?: boolean
    }
    
    • at

      要插入 Fragment nodes 的位置,分為三種插入情形:

      • at 傳入一組 Path → 將 Fragments 插入進節點的開頭位置
      • at 傳入一組 Range → 將 Range 集合的文字內容移除後插入 Fragments
      • at 傳入一個 Point → 直接將 Fragments 插入進該 Point 之後
    • hanging

      如果傳入的 at 為 Range type ,則這個 value 會決定 Range 是否要另外修正為 unhanging type 。

      詳細的解釋請參照上方的 DeleteOptions

    • voids

      決定在這個 Transform method 有呼叫到的所有操作行為中,是否要略過或避開 void nodes 的出現 。

insertText


  • 用途:插入一段文字字串進編輯器裡

  • 參數:

    • editor: Editor
    • text: string
    • options: InsertTextOptions
  • options :

    interface InsertTextoptions {
    	at?: Location
      voids?: boolean
    }
    
    • at

      欲將文字插入於編輯器中的位置,分為三種輸入情形:

      • at 傳入一組 Path → 將整個節點的文字內容修改為新輸入的內容
      • at 傳入一組 Range → 將 Range 集合的文字內容修改為新輸入的內容
      • at 傳入一個 Point → 直接將新輸入的內容插入進該 Point 之後
    • void

      一組布林值,決定文字插入的節點所屬的 branch 的上層祖先節點能否有 void node 存在。


上一篇
Day 28. slate × Transforms × Node
下一篇
Day 30. Goal × 微結語
系列文
深入 slate.js x 一起打造專屬的富文字編輯器吧!30

尚未有邦友留言

立即登入留言