UNPKG

@nextcloud/vue

Version:
1 lines 10.2 kB
{"version":3,"file":"index-DIJxEozm.cjs","sources":["../../src/utils/FindRanges.js","../../src/components/NcHighlight/NcHighlight.vue"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\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 - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\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":[],"mappings":"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"}