UNPKG

@wordpress/core-data

Version:
8 lines (7 loc) 6.3 kB
{ "version": 3, "sources": ["../../src/utils/block-selection-history.ts"], "sourcesContent": ["/**\n * External dependencies\n */\n/**\n * WordPress dependencies\n */\nimport { Y } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport { findBlockByClientIdInDoc } from './crdt-utils';\nimport type { WPBlockSelection, WPSelection } from '../types';\n\n// Default size for selection history (not including current selection)\nconst SELECTION_HISTORY_DEFAULT_SIZE = 5;\n\nexport enum YSelectionType {\n\tRelativeSelection = 'RelativeSelection',\n\tBlockSelection = 'BlockSelection',\n}\n\nexport interface YRelativeSelection {\n\ttype: YSelectionType.RelativeSelection;\n\tattributeKey: string;\n\trelativePosition: Y.RelativePosition;\n\tclientId: string;\n\toffset: number;\n}\n\nexport interface YBlockSelection {\n\ttype: YSelectionType.BlockSelection;\n\tclientId: string;\n}\n\nexport type YSelection = YRelativeSelection | YBlockSelection;\n\nexport type YFullSelection = {\n\tstart: YSelection;\n\tend: YSelection;\n};\n\nexport interface YSelectionHistory {\n\tselection: YFullSelection;\n\tbackupSelections?: YFullSelection[];\n}\n\nexport interface BlockSelectionHistory {\n\tgetSelectionHistory: () => YFullSelection[];\n\tupdateSelection: ( newSelection: WPSelection ) => void;\n}\n\n/**\n * This function is used to track recent block selections to help in restoring\n * a user's selection after an undo or redo operation.\n *\n * Maintains a history array for previous selections, which can be used for\n * backup restoration locations.\n * @param ydoc\n * @param historySize\n */\nexport function createBlockSelectionHistory(\n\tydoc: Y.Doc,\n\thistorySize: number = SELECTION_HISTORY_DEFAULT_SIZE\n): BlockSelectionHistory {\n\tlet history: YFullSelection[] = [];\n\n\t/**\n\t * Get the block history including current selection.\n\t */\n\tconst getSelectionHistory = (): YFullSelection[] => {\n\t\treturn history.slice( 0 );\n\t};\n\n\t/**\n\t * Update the selection history with a new selection.\n\t * @param newSelection\n\t */\n\tconst updateSelection = ( newSelection: WPSelection ): void => {\n\t\tif (\n\t\t\t! newSelection?.selectionStart?.clientId ||\n\t\t\t! newSelection?.selectionEnd?.clientId\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { selectionStart, selectionEnd } = newSelection;\n\t\tconst start = convertWPBlockSelectionToSelection(\n\t\t\tselectionStart,\n\t\t\tydoc\n\t\t);\n\t\tconst end = convertWPBlockSelectionToSelection( selectionEnd, ydoc );\n\n\t\taddToHistory( { start, end } );\n\t};\n\n\t/**\n\t * Add a selection to the history, maintaining only the last `historySize` unique selections.\n\t * New selections are added to the front.\n\t * Removes any existing entries with the same start and end block combination.\n\t * @param yFullSelection\n\t */\n\tconst addToHistory = ( yFullSelection: YFullSelection ): void => {\n\t\t// Remove any existing entries with the same start and end block combination\n\t\tconst startClientId = yFullSelection.start.clientId;\n\t\tconst endClientId = yFullSelection.end.clientId;\n\n\t\thistory = history.filter( ( entry ) => {\n\t\t\tconst isSameBlockCombination =\n\t\t\t\tentry.start.clientId === startClientId &&\n\t\t\t\tentry.end.clientId === endClientId;\n\n\t\t\treturn ! isSameBlockCombination;\n\t\t} );\n\n\t\t// Add the new selection to the front\n\t\thistory.unshift( yFullSelection );\n\n\t\t// Trim to max size (remove oldest entries from the back)\n\t\tif ( history.length > historySize + 1 ) {\n\t\t\thistory = history.slice( 0, historySize + 1 );\n\t\t}\n\t};\n\n\treturn {\n\t\tgetSelectionHistory,\n\t\tupdateSelection,\n\t};\n}\n\n/**\n * Convert a WPBlockSelection to a YSelection.\n * @param selection\n * @param ydoc\n * @return A YSelection object.\n */\nfunction convertWPBlockSelectionToSelection(\n\tselection: WPBlockSelection,\n\tydoc: Y.Doc\n): YSelection {\n\tconst clientId = selection.clientId;\n\tconst block = findBlockByClientIdInDoc( clientId, ydoc );\n\tconst attributes = block?.get( 'attributes' );\n\tconst attributeKey = selection.attributeKey;\n\n\tconst changedYText = attributeKey\n\t\t? attributes?.get( attributeKey )\n\t\t: undefined;\n\n\tconst isYText = changedYText instanceof Y.Text;\n\tconst isFullyDefinedSelection = attributeKey && clientId;\n\n\tif ( ! isYText || ! isFullyDefinedSelection ) {\n\t\t// We either don't have a valid YText (it's been deleted) or we've\n\t\t// been passed a selection that's just a block clientId.\n\t\t// Store as BlockSelection.\n\t\treturn {\n\t\t\ttype: YSelectionType.BlockSelection,\n\t\t\tclientId,\n\t\t};\n\t}\n\n\tconst offset = selection.offset ?? 0;\n\tconst relativePosition = Y.createRelativePositionFromTypeIndex(\n\t\tchangedYText,\n\t\toffset\n\t);\n\n\treturn {\n\t\ttype: YSelectionType.RelativeSelection,\n\t\tattributeKey,\n\t\trelativePosition,\n\t\tclientId,\n\t\toffset,\n\t};\n}\n"], "mappings": ";AAMA,SAAS,SAAS;AAKlB,SAAS,gCAAgC;AAIzC,IAAM,iCAAiC;AAEhC,IAAK,iBAAL,kBAAKA,oBAAL;AACN,EAAAA,gBAAA,uBAAoB;AACpB,EAAAA,gBAAA,oBAAiB;AAFN,SAAAA;AAAA,GAAA;AA4CL,SAAS,4BACf,MACA,cAAsB,gCACE;AACxB,MAAI,UAA4B,CAAC;AAKjC,QAAM,sBAAsB,MAAwB;AACnD,WAAO,QAAQ,MAAO,CAAE;AAAA,EACzB;AAMA,QAAM,kBAAkB,CAAE,iBAAqC;AAC9D,QACC,CAAE,cAAc,gBAAgB,YAChC,CAAE,cAAc,cAAc,UAC7B;AACD;AAAA,IACD;AAEA,UAAM,EAAE,gBAAgB,aAAa,IAAI;AACzC,UAAM,QAAQ;AAAA,MACb;AAAA,MACA;AAAA,IACD;AACA,UAAM,MAAM,mCAAoC,cAAc,IAAK;AAEnE,iBAAc,EAAE,OAAO,IAAI,CAAE;AAAA,EAC9B;AAQA,QAAM,eAAe,CAAE,mBAA0C;AAEhE,UAAM,gBAAgB,eAAe,MAAM;AAC3C,UAAM,cAAc,eAAe,IAAI;AAEvC,cAAU,QAAQ,OAAQ,CAAE,UAAW;AACtC,YAAM,yBACL,MAAM,MAAM,aAAa,iBACzB,MAAM,IAAI,aAAa;AAExB,aAAO,CAAE;AAAA,IACV,CAAE;AAGF,YAAQ,QAAS,cAAe;AAGhC,QAAK,QAAQ,SAAS,cAAc,GAAI;AACvC,gBAAU,QAAQ,MAAO,GAAG,cAAc,CAAE;AAAA,IAC7C;AAAA,EACD;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,EACD;AACD;AAQA,SAAS,mCACR,WACA,MACa;AACb,QAAM,WAAW,UAAU;AAC3B,QAAM,QAAQ,yBAA0B,UAAU,IAAK;AACvD,QAAM,aAAa,OAAO,IAAK,YAAa;AAC5C,QAAM,eAAe,UAAU;AAE/B,QAAM,eAAe,eAClB,YAAY,IAAK,YAAa,IAC9B;AAEH,QAAM,UAAU,wBAAwB,EAAE;AAC1C,QAAM,0BAA0B,gBAAgB;AAEhD,MAAK,CAAE,WAAW,CAAE,yBAA0B;AAI7C,WAAO;AAAA,MACN,MAAM;AAAA,MACN;AAAA,IACD;AAAA,EACD;AAEA,QAAM,SAAS,UAAU,UAAU;AACnC,QAAM,mBAAmB,EAAE;AAAA,IAC1B;AAAA,IACA;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;", "names": ["YSelectionType"] }