@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
1 lines • 8.19 kB
Source Map (JSON)
{"version":3,"file":"utils.cjs","sources":["../../../../../components/rich_text_editor/extensions/custom_link/utils.js"],"sourcesContent":["import { getMarksBetween } from '@tiptap/core';\nimport {\n getPhoneNumberRegex,\n linkRegex,\n} from '@/common/utils';\n\n/**\n * Get matches in a string and return the ones that pass the optional extra\n * validation or if no validator is provided return all matches.\n */\nexport function getRegexMatches (text, regex, validator = () => true) {\n const matches = [];\n\n // Reset the lastIndex since the last time this was run.\n regex.lastIndex = 0;\n\n let match;\n while ((match = regex.exec(text))) {\n if (validator(text, match)) {\n matches.push(match);\n }\n }\n\n return matches;\n}\n\n/**\n * Validate the prefix of a match in a string not to contain certain characters.\n */\nexport function hasValidPrefix (text, match) {\n // The string match can't start with # or @ or have either preceding the match\n // as they're reserved for mentions and hashtags.\n return !['#', '@'].includes(text.charAt(match.index)) &&\n !['#', '@'].includes(text.charAt(match.index - 1));\n}\n\n/**\n * Trim punctuation characters at the a of the string, e.g. \"dialpad.com!\" =>\n * \"dialpad.com\"\n */\nexport function trimEndPunctiation (string) {\n const endPunctuationRegex = new RegExp(\n '(?:' +\n [\n '[!?.,:;\\'\"]',\n '(?:&|&)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)+$',\n ].join('|'),\n 'g',\n );\n return string.replace(endPunctuationRegex, '');\n}\n\n/**\n * Find the word from a string at a given index. For example for \"example here\"\n * - indices 0-7 => \"example\"\n * - indices 8-12 => \"here\".\n * Modified from https://stackoverflow.com/a/5174867\n */\nexport function getWordAt (text, index) {\n // Position of the first non-whitespace character following a possible\n // whitespace when looking from the index to the left.\n const left = text.slice(0, index + 1).search(/\\S+\\s*$/);\n\n // Position of the first whitespace when looking from the index to the right.\n const right = text.slice(index).search(/\\s/);\n\n // If the word is at the end of the string, right is -1.\n if (right < 0) {\n const word = text.slice(left);\n return {\n text: word,\n from: left,\n to: left + word.length,\n };\n }\n\n return {\n text: text.slice(left, right + index),\n from: left,\n to: right + index,\n };\n}\n\n/**\n * Helper to check if a word at a given index matches a regex and if true, finds\n * the previous or next word until the regex doesn't match anymore. Useful to\n * find for example the entire phone number when it is separated by whitespace.\n */\nexport function getWordAtUntil (text, index, direction, regex) {\n const word = getWordAt(text, index);\n\n // Reset the lastIndex since the last time this was run.\n regex.lastIndex = 0;\n\n // If the word doesn't match the regex we can just return it.\n if (!regex.test(word.text)) {\n return word;\n }\n\n // Depending on the direction take one step to the left or right to find the\n // preceding or following word.\n const newIndex = direction === 'left' ? word.from - 1 : word.to + 1;\n\n // Prevent an infinite loop for the base cases.\n if (newIndex <= 0 || newIndex >= text.length || newIndex === index) {\n return word;\n }\n\n // Run the preceding/following word through the validator until we meet the\n // string boundaries or find a word that doesn't match the regex.\n return getWordAtUntil(text, newIndex, direction, regex);\n}\n\n/**\n * Remove marks from a range.\n */\nexport function removeMarks (range, doc, tr, type) {\n const from = Math.max(range.from - 1, 0);\n const to = Math.min(range.to + 1, doc.content.size);\n const marksInRange = getMarksBetween(from, to, doc);\n\n for (const mark of marksInRange) {\n if (mark.mark.type !== type) {\n continue;\n }\n\n tr.removeMark(mark.from, mark.to, type);\n }\n}\n\n// Regex to match partial phone numbers.\nconst partialPhoneNumberRegex = getPhoneNumberRegex(1, 15);\n\n/**\n * Find matches from text and add marks on them.\n */\nexport function addMarks (text, pos, from, to, tr, type) {\n if (!text) {\n return;\n }\n\n // from = start index for the change\n // pos = start index of the node\n // 1 = range uses 1-based indexing, deduct 1\n let rangeFrom = from - pos - 1;\n\n // If the change spans multiple nodes/paragraphs the start index can become\n // negative, so default to 0.\n rangeFrom = rangeFrom < 0 ? 0 : rangeFrom;\n\n // to = end index of the change\n // pos = start index of the node\n const rangeTo = to - pos;\n\n // Get the first word in the range.\n const firstWordInRange = getWordAtUntil(\n text,\n rangeFrom,\n 'left',\n partialPhoneNumberRegex,\n );\n\n // Get the last word in the range.\n const lastWordInRange = getWordAtUntil(\n text,\n rangeTo,\n 'right',\n partialPhoneNumberRegex,\n );\n\n // Create a substring that consists of whole words only.\n const wordsInRange = text.slice(firstWordInRange.from, lastWordInRange.to);\n\n // Find all valid matches within the substring.\n const matches = getRegexMatches(wordsInRange, linkRegex, hasValidPrefix);\n\n // Loop through the matches and add marks.\n matches.forEach(match => {\n // Trim any punctuation characters at the end of the match.\n const word = trimEndPunctiation(match[0]);\n\n // pos = start index of the node\n // firstWordInRange.from = start index of the first word in range\n // match.index = index of the regex match\n // 1 = addMark() uses 1-based indexing, add 1\n const from = pos + firstWordInRange.from + match.index + 1;\n\n // Sum up the from index and the match length to get the end index.\n const to = from + word.length;\n\n tr.addMark(from, to, type.create());\n });\n}\n"],"names":["getMarksBetween","getPhoneNumberRegex","linkRegex","from","to"],"mappings":"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"}