@liveblocks/react-ui
Version:
A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.
1 lines • 6.08 kB
Source Map (JSON)
{"version":3,"file":"custom-links.cjs","sources":["../../../../../src/primitives/Composer/slate/plugins/custom-links.ts"],"sourcesContent":["import type { Editor as SlateEditor } from \"slate\";\nimport {\n Element as SlateElement,\n Node as SlateNode,\n Range as SlateRange,\n Transforms as SlateTransforms,\n} from \"slate\";\n\nimport type { ComposerBodyCustomLink } from \"../../../../types\";\nimport { isPlainText, isText } from \"../../../slate/utils/is-text\";\nimport { filterActiveMarks } from \"../../../slate/utils/marks\";\nimport { selectionContainsInlines } from \"../../../slate/utils/selection-contains-inlines\";\n\nfunction isUrl(string: string) {\n try {\n new URL(string);\n return true;\n } catch (_) {\n return false;\n }\n}\n\nexport function withCustomLinks(editor: SlateEditor): SlateEditor {\n const { isInline, normalizeNode, insertData } = editor;\n\n editor.isInline = (element) => {\n return element.type === \"custom-link\" ? true : isInline(element);\n };\n\n editor.normalizeNode = (entry) => {\n const [node, path] = entry;\n\n // Prevent nested or empty custom links\n if (SlateElement.isElement(node) && node.type === \"custom-link\") {\n if (\n node.children.length === 0 ||\n (node.children.length === 1 && node.children[0]?.text === \"\")\n ) {\n SlateTransforms.removeNodes(editor, { at: path });\n }\n }\n\n if (isText(node)) {\n const parentNode = SlateNode.parent(editor, path);\n\n // Prevent rich text within custom links by removing all marks of inner text nodes\n if (isComposerBodyCustomLink(parentNode)) {\n if (!isPlainText(node)) {\n const marks = filterActiveMarks(node);\n\n SlateTransforms.unsetNodes(editor, marks, { at: path });\n }\n }\n }\n\n normalizeNode(entry);\n };\n\n // Create custom links when pasting URLs while some text is selected\n editor.insertData = (data) => {\n const { selection } = editor;\n const pastedText = data.getData(\"text/plain\");\n\n // Keep track of whether the default behavior should be invoked\n // This allows us to sequence multiple conditions in a performant way,\n // ordering them by likelihood/cost and stopping early whenever possible\n let shouldInvokeDefaultBehavior = true;\n\n // Check if the selection is a range\n if (selection && !SlateRange.isCollapsed(selection)) {\n // Check if the selection is contained in a single block\n if (selection.anchor.path[0] === selection.focus.path[0]) {\n // Check if the pasted text is a valid URL\n if (isUrl(pastedText)) {\n // Check if the selection only contains (rich and/or plain) text nodes\n if (!selectionContainsInlines(editor, (node) => !isText(node))) {\n // If all conditions are met, wrap the selected nodes in a custom link\n SlateTransforms.wrapNodes<ComposerBodyCustomLink>(\n editor,\n {\n type: \"custom-link\",\n url: pastedText,\n children: [],\n },\n {\n at: selection,\n split: true,\n match: isPlainText,\n }\n );\n shouldInvokeDefaultBehavior = false;\n }\n }\n }\n }\n\n if (shouldInvokeDefaultBehavior) {\n insertData(data);\n }\n };\n\n return editor;\n}\n\nexport function isComposerBodyCustomLink(\n node: SlateNode\n): node is ComposerBodyCustomLink {\n return SlateElement.isElement(node) && node.type === \"custom-link\";\n}\n"],"names":["SlateElement","SlateTransforms","isText","SlateNode","isPlainText","marks","filterActiveMarks","SlateRange","selectionContainsInlines"],"mappings":";;;;;;;AAaA,SAAS,MAAM,MAAgB,EAAA;AAC7B,EAAI,IAAA;AACF,IAAA,IAAI,IAAI,MAAM,CAAA,CAAA;AACd,IAAO,OAAA,IAAA,CAAA;AAAA,WACA,CAAP,EAAA;AACA,IAAO,OAAA,KAAA,CAAA;AAAA,GACT;AACF,CAAA;AAEO,SAAS,gBAAgB,MAAkC,EAAA;AAChE,EAAA,MAAM,EAAE,QAAA,EAAU,aAAe,EAAA,UAAA,EAAe,GAAA,MAAA,CAAA;AAEhD,EAAO,MAAA,CAAA,QAAA,GAAW,CAAC,OAAY,KAAA;AAC7B,IAAA,OAAO,OAAQ,CAAA,IAAA,KAAS,aAAgB,GAAA,IAAA,GAAO,SAAS,OAAO,CAAA,CAAA;AAAA,GACjE,CAAA;AAEA,EAAO,MAAA,CAAA,aAAA,GAAgB,CAAC,KAAU,KAAA;AAChC,IAAM,MAAA,CAAC,IAAM,EAAA,IAAI,CAAI,GAAA,KAAA,CAAA;AAGrB,IAAA,IAAIA,cAAa,SAAU,CAAA,IAAI,CAAK,IAAA,IAAA,CAAK,SAAS,aAAe,EAAA;AAC/D,MAAA,IACE,IAAK,CAAA,QAAA,CAAS,MAAW,KAAA,CAAA,IACxB,IAAK,CAAA,QAAA,CAAS,MAAW,KAAA,CAAA,IAAK,IAAK,CAAA,QAAA,CAAS,CAAI,CAAA,EAAA,IAAA,KAAS,EAC1D,EAAA;AACA,QAAAC,gBAAA,CAAgB,WAAY,CAAA,MAAA,EAAQ,EAAE,EAAA,EAAI,MAAM,CAAA,CAAA;AAAA,OAClD;AAAA,KACF;AAEA,IAAI,IAAAC,aAAA,CAAO,IAAI,CAAG,EAAA;AAChB,MAAA,MAAM,UAAa,GAAAC,UAAA,CAAU,MAAO,CAAA,MAAA,EAAQ,IAAI,CAAA,CAAA;AAGhD,MAAI,IAAA,wBAAA,CAAyB,UAAU,CAAG,EAAA;AACxC,QAAI,IAAA,CAACC,kBAAY,CAAA,IAAI,CAAG,EAAA;AACtB,UAAM,MAAAC,OAAA,GAAQC,wBAAkB,IAAI,CAAA,CAAA;AAEpC,UAAAL,gBAAA,CAAgB,WAAW,MAAQ,EAAAI,OAAA,EAAO,EAAE,EAAA,EAAI,MAAM,CAAA,CAAA;AAAA,SACxD;AAAA,OACF;AAAA,KACF;AAEA,IAAA,aAAA,CAAc,KAAK,CAAA,CAAA;AAAA,GACrB,CAAA;AAGA,EAAO,MAAA,CAAA,UAAA,GAAa,CAAC,IAAS,KAAA;AAC5B,IAAM,MAAA,EAAE,WAAc,GAAA,MAAA,CAAA;AACtB,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,YAAY,CAAA,CAAA;AAK5C,IAAA,IAAI,2BAA8B,GAAA,IAAA,CAAA;AAGlC,IAAA,IAAI,SAAa,IAAA,CAACE,WAAW,CAAA,WAAA,CAAY,SAAS,CAAG,EAAA;AAEnD,MAAA,IAAI,UAAU,MAAO,CAAA,IAAA,CAAK,OAAO,SAAU,CAAA,KAAA,CAAM,KAAK,CAAI,CAAA,EAAA;AAExD,QAAI,IAAA,KAAA,CAAM,UAAU,CAAG,EAAA;AAErB,UAAI,IAAA,CAACC,kDAAyB,MAAQ,EAAA,CAAC,SAAS,CAACN,aAAA,CAAO,IAAI,CAAC,CAAG,EAAA;AAE9D,YAAgBD,gBAAA,CAAA,SAAA;AAAA,cACd,MAAA;AAAA,cACA;AAAA,gBACE,IAAM,EAAA,aAAA;AAAA,gBACN,GAAK,EAAA,UAAA;AAAA,gBACL,UAAU,EAAC;AAAA,eACb;AAAA,cACA;AAAA,gBACE,EAAI,EAAA,SAAA;AAAA,gBACJ,KAAO,EAAA,IAAA;AAAA,gBACP,KAAO,EAAAG,kBAAA;AAAA,eACT;AAAA,aACF,CAAA;AACA,YAA8B,2BAAA,GAAA,KAAA,CAAA;AAAA,WAChC;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEA,IAAA,IAAI,2BAA6B,EAAA;AAC/B,MAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAAA,KACjB;AAAA,GACF,CAAA;AAEA,EAAO,OAAA,MAAA,CAAA;AACT,CAAA;AAEO,SAAS,yBACd,IACgC,EAAA;AAChC,EAAA,OAAOJ,aAAa,CAAA,SAAA,CAAU,IAAI,CAAA,IAAK,KAAK,IAAS,KAAA,aAAA,CAAA;AACvD;;;;;"}