Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
committerRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
commit6438df3a1e0fb944485cebf07976160184697d72 (patch)
tree00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /app/assets/javascripts/search_settings
parent42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff)
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'app/assets/javascripts/search_settings')
-rw-r--r--app/assets/javascripts/search_settings/components/search_settings.vue129
-rw-r--r--app/assets/javascripts/search_settings/constants.js11
-rw-r--r--app/assets/javascripts/search_settings/index.js23
3 files changed, 163 insertions, 0 deletions
diff --git a/app/assets/javascripts/search_settings/components/search_settings.vue b/app/assets/javascripts/search_settings/components/search_settings.vue
new file mode 100644
index 00000000000..820055dc656
--- /dev/null
+++ b/app/assets/javascripts/search_settings/components/search_settings.vue
@@ -0,0 +1,129 @@
+<script>
+import { GlSearchBoxByType } from '@gitlab/ui';
+import { uniq } from 'lodash';
+import { EXCLUDED_NODES, HIDE_CLASS, HIGHLIGHT_CLASS, TYPING_DELAY } from '../constants';
+
+const findSettingsSection = (sectionSelector, node) => {
+ return node.parentElement.closest(sectionSelector);
+};
+
+const resetSections = ({ sectionSelector, expandSection, collapseSection }) => {
+ document.querySelectorAll(sectionSelector).forEach((section, index) => {
+ section.classList.remove(HIDE_CLASS);
+
+ if (index === 0) {
+ expandSection(section);
+ } else {
+ collapseSection(section);
+ }
+ });
+};
+
+const clearHighlights = () => {
+ document
+ .querySelectorAll(`.${HIGHLIGHT_CLASS}`)
+ .forEach((element) => element.classList.remove(HIGHLIGHT_CLASS));
+};
+
+const hideSectionsExcept = (sectionSelector, visibleSections) => {
+ Array.from(document.querySelectorAll(sectionSelector))
+ .filter((section) => !visibleSections.includes(section))
+ .forEach((section) => {
+ section.classList.add(HIDE_CLASS);
+ });
+};
+
+const highlightElements = (elements = []) => {
+ elements.forEach((element) => element.classList.add(HIGHLIGHT_CLASS));
+};
+
+const displayResults = ({ sectionSelector, expandSection }, matches) => {
+ const elements = matches.map((match) => match.parentElement);
+ const sections = uniq(elements.map((element) => findSettingsSection(sectionSelector, element)));
+
+ hideSectionsExcept(sectionSelector, sections);
+ sections.forEach(expandSection);
+ highlightElements(elements);
+};
+
+const clearResults = (params) => {
+ resetSections(params);
+ clearHighlights();
+};
+
+const includeNode = (node, lowerSearchTerm) =>
+ node.textContent.toLowerCase().includes(lowerSearchTerm) &&
+ EXCLUDED_NODES.every((excluded) => !node.parentElement.closest(excluded));
+
+const search = (root, searchTerm) => {
+ const iterator = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, {
+ acceptNode(node) {
+ return includeNode(node, searchTerm.toLowerCase())
+ ? NodeFilter.FILTER_ACCEPT
+ : NodeFilter.FILTER_REJECT;
+ },
+ });
+ const results = [];
+
+ for (let currentNode = iterator.nextNode(); currentNode; currentNode = iterator.nextNode()) {
+ results.push(currentNode);
+ }
+
+ return results;
+};
+
+export default {
+ components: {
+ GlSearchBoxByType,
+ },
+ props: {
+ searchRoot: {
+ type: Element,
+ required: true,
+ },
+ sectionSelector: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ searchTerm: '',
+ };
+ },
+ methods: {
+ search(value) {
+ const displayOptions = {
+ sectionSelector: this.sectionSelector,
+ expandSection: this.expandSection,
+ collapseSection: this.collapseSection,
+ };
+
+ this.searchTerm = value;
+
+ clearResults(displayOptions);
+
+ if (value.length) {
+ displayResults(displayOptions, search(this.searchRoot, value));
+ }
+ },
+ expandSection(section) {
+ this.$emit('expand', section);
+ },
+ collapseSection(section) {
+ this.$emit('collapse', section);
+ },
+ },
+ TYPING_DELAY,
+};
+</script>
+<template>
+ <div class="gl-mt-5">
+ <gl-search-box-by-type
+ :value="searchTerm"
+ :debounce="$options.TYPING_DELAY"
+ :placeholder="__('Search settings')"
+ @input="search"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/search_settings/constants.js b/app/assets/javascripts/search_settings/constants.js
new file mode 100644
index 00000000000..499e42854ed
--- /dev/null
+++ b/app/assets/javascripts/search_settings/constants.js
@@ -0,0 +1,11 @@
+// We do not consider these nodes in the search index
+export const EXCLUDED_NODES = ['OPTION'];
+
+// Used to hide the sections that do not match * the search term
+export const HIDE_CLASS = 'gl-display-none';
+
+// used to highlight the text that matches the * search term
+export const HIGHLIGHT_CLASS = 'gl-bg-orange-50';
+
+// How many seconds to wait until the user * stops typing
+export const TYPING_DELAY = 400;
diff --git a/app/assets/javascripts/search_settings/index.js b/app/assets/javascripts/search_settings/index.js
new file mode 100644
index 00000000000..1fb1a378ffb
--- /dev/null
+++ b/app/assets/javascripts/search_settings/index.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import $ from 'jquery';
+import { expandSection, closeSection } from '~/settings_panels';
+import SearchSettings from '~/search_settings/components/search_settings.vue';
+
+const initSearch = ({ el }) =>
+ new Vue({
+ el,
+ render: (h) =>
+ h(SearchSettings, {
+ ref: 'searchSettings',
+ props: {
+ searchRoot: document.querySelector('#content-body'),
+ sectionSelector: 'section.settings',
+ },
+ on: {
+ collapse: (section) => closeSection($(section)),
+ expand: (section) => expandSection($(section)),
+ },
+ }),
+ });
+
+export default initSearch;