dropdown_utils.js 6.0 KB
Newer Older
1 2
import { FilteredSearchContainer } from './container';

C
Clement Ho 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
(() => {
  class DropdownUtils {
    static getEscapedText(text) {
      let escapedText = text;
      const hasSpace = text.indexOf(' ') !== -1;
      const hasDoubleQuote = text.indexOf('"') !== -1;

      // Encapsulate value with quotes if it has spaces
      // Known side effect: values's with both single and double quotes
      // won't escape properly
      if (hasSpace) {
        if (hasDoubleQuote) {
          escapedText = `'${text}'`;
        } else {
          // Encapsulate singleQuotes or if it hasSpace
          escapedText = `"${text}"`;
        }
      }

      return escapedText;
    }

P
Phil Hughes 已提交
25
    static filterWithSymbol(filterSymbol, input, item) {
C
Clement Ho 已提交
26
      const updatedItem = item;
C
Clement Ho 已提交
27
      const searchInput = gl.DropdownUtils.getSearchInput(input);
C
Clement Ho 已提交
28

C
Clement Ho 已提交
29 30 31
      const title = updatedItem.title.toLowerCase();
      let value = searchInput.toLowerCase();
      let symbol = '';
C
Clement Ho 已提交
32

C
Clement Ho 已提交
33 34 35 36 37
      // Remove the symbol for filter
      if (value[0] === filterSymbol) {
        symbol = value[0];
        value = value.slice(1);
      }
38

C
Clement Ho 已提交
39 40 41 42
      // Removes the first character if it is a quotation so that we can search
      // with multiple words
      if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
        value = value.slice(1);
43
      }
C
Clement Ho 已提交
44

C
Clement Ho 已提交
45 46 47 48 49 50
      // Eg. filterSymbol = ~ for labels
      const matchWithoutSymbol = symbol === filterSymbol && title.indexOf(value) !== -1;
      const match = title.indexOf(`${symbol}${value}`) !== -1;

      updatedItem.droplab_hidden = !match && !matchWithoutSymbol;

C
Clement Ho 已提交
51 52 53
      return updatedItem;
    }

54
    static filterHint(input, item) {
C
Clement Ho 已提交
55
      const updatedItem = item;
56 57 58 59 60 61 62 63 64
      const searchInput = gl.DropdownUtils.getSearchQuery(input);
      const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput);
      const lastKey = lastToken.key || lastToken || '';
      const allowMultiple = item.type === 'array';
      const itemInExistingTokens = tokens.some(t => t.key === item.hint);

      if (!allowMultiple && itemInExistingTokens) {
        updatedItem.droplab_hidden = true;
      } else if (!lastKey || searchInput.split('').last() === ' ') {
C
Clement Ho 已提交
65
        updatedItem.droplab_hidden = false;
66 67
      } else if (lastKey) {
        const split = lastKey.split(':');
C
Clement Ho 已提交
68 69 70 71
        const tokenName = split[0].split(' ').last();

        const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
        updatedItem.droplab_hidden = tokenName ? match : false;
C
Clement Ho 已提交
72 73 74 75 76
      }

      return updatedItem;
    }

C
Clement Ho 已提交
77
    static setDataValueIfSelected(filter, selected) {
C
Clement Ho 已提交
78 79 80
      const dataValue = selected.getAttribute('data-value');

      if (dataValue) {
C
Clement Ho 已提交
81
        gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
C
Clement Ho 已提交
82 83 84 85 86
      }

      // Return boolean based on whether it was set
      return dataValue !== null;
    }
P
Phil Hughes 已提交
87

C
Clement Ho 已提交
88
    // Determines the full search query (visual tokens + input)
89
    static getSearchQuery(untilInput = false) {
90 91
      const container = FilteredSearchContainer.container;
      const tokens = [].slice.call(container.querySelectorAll('.tokens-container li'));
C
Clement Ho 已提交
92 93
      const values = [];

94 95 96 97 98
      if (untilInput) {
        const inputIndex = _.findIndex(tokens, t => t.classList.contains('input-token'));
        // Add one to include input-token to the tokens array
        tokens.splice(inputIndex + 1);
      }
C
Clement Ho 已提交
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
      tokens.forEach((token) => {
        if (token.classList.contains('js-visual-token')) {
          const name = token.querySelector('.name');
          const value = token.querySelector('.value');
          const symbol = value && value.dataset.symbol ? value.dataset.symbol : '';
          let valueText = '';

          if (value && value.innerText) {
            valueText = value.innerText;
          }

          if (token.className.indexOf('filtered-search-token') !== -1) {
            values.push(`${name.innerText.toLowerCase()}:${symbol}${valueText}`);
          } else {
            values.push(name.innerText);
          }
        } else if (token.classList.contains('input-token')) {
          const { isLastVisualTokenValid } =
            gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();

          const input = document.querySelector('.filtered-search');
          const inputValue = input && input.value;

          if (isLastVisualTokenValid) {
            values.push(inputValue);
          } else {
            const previous = values.pop();
            values.push(`${previous}${inputValue}`);
          }
C
Clement Ho 已提交
129 130 131 132 133 134
        }
      });

      return values.join(' ');
    }

P
Phil Hughes 已提交
135 136
    static getSearchInput(filteredSearchInput) {
      const inputValue = filteredSearchInput.value;
137
      const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
P
Phil Hughes 已提交
138

139
      return inputValue.slice(0, right);
140 141 142
    }

    static getInputSelectionPosition(input) {
143
      const selectionStart = input.selectionStart;
144 145
      let inputValue = input.value;
      // Replace all spaces inside quote marks with underscores
C
Clement Ho 已提交
146
      // (will continue to match entire string until an end quote is found if any)
147
      // This helps with matching the beginning & end of a token:key
C
Clement Ho 已提交
148
      inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
149

150
      // Get the right position for the word selected
P
Phil Hughes 已提交
151
      // Regex matches first space
152 153 154 155
      let right = inputValue.slice(selectionStart).search(/\s/);

      if (right >= 0) {
        right += selectionStart;
156 157
      } else if (right < 0) {
        right = inputValue.length;
158 159
      }

160
      // Get the left position for the word selected
P
Phil Hughes 已提交
161
      // Regex matches last non-whitespace character
162
      let left = inputValue.slice(0, right).search(/\S+$/);
163

164 165 166 167
      if (selectionStart === 0) {
        left = 0;
      } else if (selectionStart === inputValue.length && left < 0) {
        left = inputValue.length;
168 169
      } else if (left < 0) {
        left = selectionStart;
170 171
      }

172 173 174 175
      return {
        left,
        right,
      };
P
Phil Hughes 已提交
176
    }
C
Clement Ho 已提交
177 178 179 180 181
  }

  window.gl = window.gl || {};
  gl.DropdownUtils = DropdownUtils;
})();