UNPKG

@nextcloud/vue

Version:
1 lines 11.3 kB
{"version":3,"file":"index-df3f51c6.cjs","sources":["../../src/utils/FindRanges.js","../../src/components/NcHighlight/NcHighlight.vue"],"sourcesContent":["/**\n * @copyright Copyright (c) 2020 Raimund Schlüßler <raimund.schluessler@mailbox.org>\n *\n * @author Raimund Schlüßler <raimund.schluessler@mailbox.org>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\n/**\n * Find the ranges of a substr in a given string\n *\n * @param {string} text The text to search in\n * @param {string} search The text to search for\n * @return {Array} The array of ranges to highlight\n */\nconst FindRanges = (text, search) => {\n\tconst ranges = []\n\tlet currentIndex = 0\n\tlet index = text.toLowerCase().indexOf(search.toLowerCase(), currentIndex)\n\t// Variable to track that we don't iterate more often than the length of the text.\n\t// Shouldn't happen anyway, but just to be sure to not hang the browser for some reason.\n\tlet i = 0\n\twhile (index > -1 && i < text.length) {\n\t\tcurrentIndex = index + search.length\n\t\tranges.push({ start: index, end: currentIndex })\n\n\t\tindex = text.toLowerCase().indexOf(search.toLowerCase(), currentIndex)\n\t\ti++\n\t}\n\treturn ranges\n}\n\nexport default FindRanges\n","<!--\n - @copyright Copyright (c) 2021 Raimund Schlüßler <raimund.schluessler@mailbox.org>\n -\n - @author Raimund Schlüßler <raimund.schluessler@mailbox.org>\n -\n - @license GNU AGPL version 3 or any later version\n -\n - This program is free software: you can redistribute it and/or modify\n - it under the terms of the GNU Affero General Public License as\n - published by the Free Software Foundation, either version 3 of the\n - License, or (at your option) any later version.\n -\n - This program is distributed in the hope that it will be useful,\n - but WITHOUT ANY WARRANTY; without even the implied warranty of\n - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n - GNU Affero General Public License for more details.\n -\n - You should have received a copy of the GNU Affero General Public License\n - along with this program. If not, see <http://www.gnu.org/licenses/>.\n -\n -->\n\n<docs>\n\n### General description\n\nHighlight a string with html &lt;strong&gt;. Accepts a substring to highlight or an array with ranges.\n\n### Usage\n\n```vue\n<template>\n\t<div>\n\t\t<NcHighlight text=\"Highlight me please!\" search=\"me\" />\n\t\t<br />\n\t\t<NcHighlight text=\"Highlight me please!\" :highlight=\"[{ start: 4, end: 12 }]\" />\n\t</div>\n</template>\n```\n</docs>\n\n<script>\nimport FindRanges from '../../utils/FindRanges.js'\n\nexport default {\n\tname: 'NcHighlight',\n\tprops: {\n\t\t/**\n\t\t * The string to display\n\t\t */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t\t/**\n\t\t * The string to match and highlight\n\t\t */\n\t\tsearch: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t\t/**\n\t\t * The ranges to highlight, takes precedence over the search prop.\n\t\t */\n\t\thighlight: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\t},\n\tcomputed: {\n\t\t/**\n\t\t * The indice ranges which should be highlighted.\n\t\t * If an array with ranges is provided, we use it. Otherwise\n\t\t * we calculate it based on the provided substring to highlight.\n\t\t *\n\t\t * @return {Array} The array of ranges to highlight\n\t\t */\n\t\tranges() {\n\t\t\tlet ranges = []\n\t\t\t// If the search term and the highlight array is empty, return early with empty array\n\t\t\tif (!this.search && this.highlight.length === 0) {\n\t\t\t\treturn ranges\n\t\t\t}\n\n\t\t\t// If there are ranges to highlight provided, we use this array.\n\t\t\tif (this.highlight.length > 0) {\n\t\t\t\tranges = this.highlight\n\t\t\t// Otherwise we check the text to highlight for matches of the search term.\n\t\t\t} else {\n\t\t\t\tranges = FindRanges(this.text, this.search)\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Ensure that the start of each range is equal to or smaller than the end\n\t\t\t */\n\t\t\tranges.forEach((range, i) => {\n\t\t\t\tif (range.end < range.start) {\n\t\t\t\t\tranges[i] = {\n\t\t\t\t\t\tstart: range.end,\n\t\t\t\t\t\tend: range.start,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t/**\n\t\t\t * Validate the ranges array to be within the string length\n\t\t\t * and discard ranges which are completely out of bonds.\n\t\t\t */\n\t\t\tranges = ranges.reduce((validRanges, range) => {\n\t\t\t\tif (range.start < this.text.length && range.end > 0) {\n\t\t\t\t\tvalidRanges.push({\n\t\t\t\t\t\tstart: (range.start < 0) ? 0 : range.start,\n\t\t\t\t\t\tend: (range.end > this.text.length) ? this.text.length : range.end,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn validRanges\n\t\t\t}, [])\n\n\t\t\t/**\n\t\t\t * Sort ranges ascendingly (necessary for next step)\n\t\t\t */\n\t\t\tranges.sort((a, b) => {\n\t\t\t\treturn a.start - b.start\n\t\t\t})\n\n\t\t\t/**\n\t\t\t * Merge overlapping or adjacent ranges\n\t\t\t */\n\t\t\tranges = ranges.reduce((mergedRanges, range) => {\n\t\t\t\t// If there are no ranges, just add the range\n\t\t\t\tif (!mergedRanges.length) {\n\t\t\t\t\tmergedRanges.push(range)\n\t\t\t\t} else {\n\t\t\t\t\t// If the range overlaps the last range, merge them\n\t\t\t\t\tconst idx = mergedRanges.length - 1\n\t\t\t\t\tif (mergedRanges[idx].end >= range.start) {\n\t\t\t\t\t\tmergedRanges[idx] = {\n\t\t\t\t\t\t\tstart: mergedRanges[idx].start,\n\t\t\t\t\t\t\tend: Math.max(mergedRanges[idx].end, range.end),\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmergedRanges.push(range)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn mergedRanges\n\t\t\t}, [])\n\n\t\t\treturn ranges\n\t\t},\n\t\t/**\n\t\t * Calculate the different chunks to show based on the ranges to highlight.\n\t\t *\n\t\t * @return {Array} The chunks\n\t\t */\n\t\tchunks() {\n\t\t\t// If the ranges array is empty, show only one chunk with all text\n\t\t\tif (this.ranges.length === 0) {\n\t\t\t\treturn [{\n\t\t\t\t\tstart: 0,\n\t\t\t\t\tend: this.text.length,\n\t\t\t\t\thighlight: false,\n\t\t\t\t\ttext: this.text,\n\t\t\t\t}]\n\t\t\t}\n\t\t\t// Calculate the chunks\n\t\t\tconst chunks = []\n\t\t\tlet currentIndex = 0\n\t\t\tlet currentRange = 0\n\t\t\t// Iterate over all characters in the text\n\t\t\twhile (currentIndex < this.text.length) {\n\t\t\t\t// Get the first range to highlight\n\t\t\t\tconst range = this.ranges[currentRange]\n\t\t\t\t// If the range starts at the current index, construct a chunk to highlight,\n\t\t\t\t// set the next range and continue with the next iteration.\n\t\t\t\tif (range.start === currentIndex) {\n\t\t\t\t\tchunks.push({\n\t\t\t\t\t\t...range,\n\t\t\t\t\t\thighlight: true,\n\t\t\t\t\t\ttext: this.text.slice(range.start, range.end),\n\t\t\t\t\t})\n\t\t\t\t\tcurrentRange++\n\t\t\t\t\tcurrentIndex = range.end\n\t\t\t\t\t// If this was the last range to highlight and we haven't reached the end of the text,\n\t\t\t\t\t// add the rest of the text without highlighting.\n\t\t\t\t\tif (currentRange >= this.ranges.length && currentIndex < this.text.length) {\n\t\t\t\t\t\tchunks.push({\n\t\t\t\t\t\t\tstart: currentIndex,\n\t\t\t\t\t\t\tend: this.text.length,\n\t\t\t\t\t\t\thighlight: false,\n\t\t\t\t\t\t\ttext: this.text.slice(currentIndex),\n\t\t\t\t\t\t})\n\t\t\t\t\t\t// Set the current index so the while loop ends.\n\t\t\t\t\t\tcurrentIndex = this.text.length\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// If the current range does start after the current index, construct a chunk without\n\t\t\t\t// highlighting and set the current index to the start of the current range.\n\t\t\t\tchunks.push({\n\t\t\t\t\tstart: currentIndex,\n\t\t\t\t\tend: range.start,\n\t\t\t\t\thighlight: false,\n\t\t\t\t\ttext: this.text.slice(currentIndex, range.start),\n\t\t\t\t})\n\t\t\t\tcurrentIndex = range.start\n\t\t\t}\n\t\t\treturn chunks\n\t\t},\n\t},\n\t/**\n\t * The render function to display the component\n\t *\n\t * @param {Function} h The function to create VNodes\n\t * @return {object} The created VNode\n\t */\n\trender(h) {\n\t\tif (!this.ranges.length) {\n\t\t\treturn h('span', {}, this.text)\n\t\t}\n\n\t\treturn h('span', {}, this.chunks.map(chunk => {\n\t\t\treturn chunk.highlight ? h('strong', {}, chunk.text) : chunk.text\n\t\t}))\n\t},\n}\n</script>\n"],"names":["FindRanges","text","search","ranges","currentIndex","index","i","_sfc_main","range","validRanges","a","b","mergedRanges","idx","chunks","currentRange","h","chunk"],"mappings":"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"}