dropdown_utils.js 6.0 KB
Newer Older
P
Phil Hughes 已提交
1
import FilteredSearchContainer from './container';
2

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
      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();

120
          const input = FilteredSearchContainer.container.querySelector('.filtered-search');
121 122 123 124 125 126 127 128
          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;
})();