UNPKG

@atlassian/aui

Version:

Atlassian User Interface Framework

584 lines (475 loc) 22.7 kB
(function (global, factory) { if (typeof define === "function" && define.amd) { define(['module', 'exports', './jquery', './internal/select/option', './internal/amdify', './polyfills/custom-event', './internal/globalize', './key-code', './progressive-data-set', './internal/skate', './internal/state', './internal/select/suggestion-model', './internal/select/suggestions-model', './internal/select/suggestions-view', './internal/select/template', './unique-id', './internal/constants', './button', './i18n', './spin'], factory); } else if (typeof exports !== "undefined") { factory(module, exports, require('./jquery'), require('./internal/select/option'), require('./internal/amdify'), require('./polyfills/custom-event'), require('./internal/globalize'), require('./key-code'), require('./progressive-data-set'), require('./internal/skate'), require('./internal/state'), require('./internal/select/suggestion-model'), require('./internal/select/suggestions-model'), require('./internal/select/suggestions-view'), require('./internal/select/template'), require('./unique-id'), require('./internal/constants'), require('./button'), require('./i18n'), require('./spin')); } else { var mod = { exports: {} }; factory(mod, mod.exports, global.jquery, global.option, global.amdify, global.customEvent, global.globalize, global.keyCode, global.progressiveDataSet, global.skate, global.state, global.suggestionModel, global.suggestionsModel, global.suggestionsView, global.template, global.uniqueId, global.constants, global.button, global.i18n, global.spin); global.select = mod.exports; } })(this, function (module, exports, _jquery, _option, _amdify, _customEvent, _globalize, _keyCode, _progressiveDataSet, _skate, _state, _suggestionModel, _suggestionsModel, _suggestionsView, _template, _uniqueId, _constants) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _jquery2 = _interopRequireDefault(_jquery); var _option2 = _interopRequireDefault(_option); var _amdify2 = _interopRequireDefault(_amdify); var _customEvent2 = _interopRequireDefault(_customEvent); var _globalize2 = _interopRequireDefault(_globalize); var _keyCode2 = _interopRequireDefault(_keyCode); var _progressiveDataSet2 = _interopRequireDefault(_progressiveDataSet); var _skate2 = _interopRequireDefault(_skate); var _state2 = _interopRequireDefault(_state); var _suggestionModel2 = _interopRequireDefault(_suggestionModel); var _suggestionsModel2 = _interopRequireDefault(_suggestionsModel); var _suggestionsView2 = _interopRequireDefault(_suggestionsView); var _template2 = _interopRequireDefault(_template); var _uniqueId2 = _interopRequireDefault(_uniqueId); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DESELECTED = -1; var NO_HIGHLIGHT = -1; var DEFAULT_SS_PDS_SIZE = 20; function clearElementImage(element) { element._input.removeAttribute('style'); (0, _jquery2.default)(element._input).removeClass('aui-select-has-inline-image'); } function deselect(element) { element._select.selectedIndex = DESELECTED; clearElementImage(element); } function hasResults(element) { return element._suggestionModel.getNumberOfResults(); } function waitForAssistive(callback) { setTimeout(callback, 50); } function setBusyState(element) { if (!element._button.isBusy()) { element._button.busy(); element._input.setAttribute('aria-busy', 'true'); element._dropdown.setAttribute('aria-busy', 'true'); } } function setIdleState(element) { element._button.idle(); element._input.setAttribute('aria-busy', 'false'); element._dropdown.setAttribute('aria-busy', 'false'); } function matchPrefix(model, query) { var value = model.get('label').toLowerCase(); return value.indexOf(query.toLowerCase()) === 0; } function hideDropdown(element) { element._suggestionsView.hide(); element._input.setAttribute('aria-expanded', 'false'); } function setInitialVisualState(element) { var initialHighlightedItem = hasResults(element) ? 0 : NO_HIGHLIGHT; element._suggestionModel.highlight(initialHighlightedItem); hideDropdown(element); } function setElementImage(element, imageSource) { (0, _jquery2.default)(element._input).addClass('aui-select-has-inline-image'); element._input.setAttribute('style', 'background-image: url(' + imageSource + ')'); } function suggest(element, autoHighlight, query) { element._autoHighlight = autoHighlight; if (query === undefined) { query = element._input.value; } element._progressiveDataSet.query(query); } function setInputImageToHighlightedSuggestion(element) { var imageSource = element._suggestionModel.highlighted() && element._suggestionModel.highlighted().get('img-src'); if (imageSource) { setElementImage(element, imageSource); } } function setValueAndDisplayFromModel(element, model) { if (!model) { return; } var option = document.createElement('option'); var select = element._select; var value = model.get('value') || model.get('label'); option.setAttribute('selected', ''); option.setAttribute('value', value); option.textContent = model.getLabel(); // Sync element value. element._input.value = option.textContent; select.innerHTML = ''; select.options.add(option); select.dispatchEvent(new _customEvent2.default('change', { bubbles: true })); } function clearValue(element) { element._input.value = ''; element._select.innerHTML = ''; } function selectHighlightedSuggestion(element) { setValueAndDisplayFromModel(element, element._suggestionModel.highlighted()); setInputImageToHighlightedSuggestion(element); setInitialVisualState(element); } function convertOptionToModel(option) { return new _suggestionModel2.default(option.serialize()); } function convertOptionsToModels(element) { var models = []; for (var i = 0; i < element._datalist.children.length; i++) { var option = element._datalist.children[i]; models.push(convertOptionToModel(option)); } return models; } function clearAndSet(element, data) { element._suggestionModel.set(); element._suggestionModel.set(data.results); } function getActiveId(select) { var active = select._dropdown.querySelector('.aui-select-active'); return active && active.id; } function getIndexInResults(id, results) { var resultsIds = _jquery2.default.map(results, function (result) { return result.id; }); return resultsIds.indexOf(id); } function createNewValueModel(element) { var option = new _option2.default(); option.setAttribute('value', element._input.value); var newValueSuggestionModel = convertOptionToModel(option); newValueSuggestionModel.set('new-value', true); return newValueSuggestionModel; } function initialiseProgressiveDataSet(element) { element._progressiveDataSet = new _progressiveDataSet2.default(convertOptionsToModels(element), { model: _suggestionModel2.default, matcher: matchPrefix, queryEndpoint: element._queryEndpoint, maxResults: DEFAULT_SS_PDS_SIZE }); element._isSync = element._queryEndpoint ? false : true; // Progressive data set should indicate whether or not it is busy when processing any async requests. // Check if there's any active queries left, if so: set spinner and state to busy, else set to idle and remove // the spinner. element._progressiveDataSet.on('activity', function () { if (element._progressiveDataSet.activeQueryCount && !element._isSync) { setBusyState(element); (0, _state2.default)(element).set('should-flag-new-suggestions', false); } else { setIdleState(element); (0, _state2.default)(element).set('should-flag-new-suggestions', true); } }); // Progressive data set doesn't do anything if the query is empty so we // must manually convert all data list options into models. // // Otherwise progressive data set can do everything else for us: // 1. Sync matching // 2. Async fetching and matching element._progressiveDataSet.on('respond', function (data) { var optionToHighlight; // This means that a query was made before the input was cleared and // we should cancel the response. if (data.query && !element._input.value) { return; } if ((0, _state2.default)(element).get('should-cancel-response')) { if (!element._progressiveDataSet.activeQueryCount) { (0, _state2.default)(element).set('should-cancel-response', false); } return; } if (!data.query) { data.results = convertOptionsToModels(element); } var isInputExactMatch = getIndexInResults(element._input.value, data.results) !== -1; var isInputEmpty = !element._input.value; if (element.hasAttribute('can-create-values') && !isInputExactMatch && !isInputEmpty) { data.results.push(createNewValueModel(element)); } if (!(0, _state2.default)(element).get('should-include-selected')) { var indexOfValueInResults = getIndexInResults(element.value, data.results); if (indexOfValueInResults >= 0) { data.results.splice(indexOfValueInResults, 1); } } clearAndSet(element, data); optionToHighlight = element._suggestionModel.highlighted() || data.results[0]; if (element._autoHighlight) { element._suggestionModel.setHighlighted(optionToHighlight); waitForAssistive(function () { element._input.setAttribute('aria-activedescendant', getActiveId(element)); }); } element._input.setAttribute('aria-expanded', 'true'); // If the response is async (append operation), has elements to append and has a highlighted element, we need to update the status. if (!element._isSync && element._suggestionsView.getActive() && (0, _state2.default)(element).get('should-flag-new-suggestions')) { element.querySelector('.aui-select-status').innerHTML = AJS.I18n.getText('aui.select.new.suggestions'); } element._suggestionsView.show(); if (element._autoHighlight) { waitForAssistive(function () { element._input.setAttribute('aria-activedescendant', getActiveId(element)); }); } }); } function associateDropdownAndTrigger(element) { element._dropdown.id = element._listId; element.querySelector('button').setAttribute('aria-controls', element._listId); } function bindHighlightMouseover(element) { (0, _jquery2.default)(element._dropdown).on('mouseover', 'li', function (e) { if (hasResults(element)) { element._suggestionModel.highlight((0, _jquery2.default)(e.target).index()); } }); } function bindSelectMousedown(element) { (0, _jquery2.default)(element._dropdown).on('mousedown', 'li', function (e) { if (hasResults(element)) { element._suggestionModel.highlight((0, _jquery2.default)(e.target).index()); selectHighlightedSuggestion(element); element._suggestionsView.hide(); element._input.removeAttribute('aria-activedescendant'); } else { return false; } }); } function initialiseValue(element) { var option = element._datalist.querySelector('aui-option[selected]'); if (option) { setValueAndDisplayFromModel(element, convertOptionToModel(option)); } } function isQueryInProgress(element) { return element._progressiveDataSet.activeQueryCount > 0; } function focusInHandler(element) { //if there is a selected value the single select should do an empty //search and return everything var searchValue = element.value ? '' : element._input.value; var isInputEmpty = element._input.value === ''; (0, _state2.default)(element).set('should-include-selected', isInputEmpty); suggest(element, true, searchValue); } function cancelInProgressQueries(element) { if (isQueryInProgress(element)) { (0, _state2.default)(element).set('should-cancel-response', true); } } function getSelectedLabel(element) { if (element._select.selectedIndex >= 0) { return element._select.options[element._select.selectedIndex].textContent; } } function handleInvalidInputOnFocusOut(element) { var selectCanBeEmpty = !element.hasAttribute('no-empty-values'); var selectionIsEmpty = !element._input.value; var selectionNotExact = element._input.value !== getSelectedLabel(element); var selectionNotValid = selectionIsEmpty || selectionNotExact; if (selectionNotValid) { if (selectCanBeEmpty) { deselect(element); } else { var selection = getSelectedLabel(element); if (typeof selection === 'undefined') { deselect(element); } else { element._input.value = selection; } } } } function handleHighlightOnFocusOut(element) { // Forget the highlighted suggestion. element._suggestionModel.highlight(NO_HIGHLIGHT); } function focusOutHandler(element) { cancelInProgressQueries(element); handleInvalidInputOnFocusOut(element); handleHighlightOnFocusOut(element); hideDropdown(element); } function handleTabOut(element) { var isSuggestionViewVisible = element._suggestionsView.isVisible(); if (isSuggestionViewVisible) { selectHighlightedSuggestion(element); } } var select = (0, _skate2.default)('aui-select', { template: _template2.default, created: function created(element) { element._listId = (0, _uniqueId2.default)(); element._input = element.querySelector('input'); element._select = element.querySelector('select'); element._dropdown = element.querySelector('.aui-popover'); element._datalist = element.querySelector('datalist'); element._button = element.querySelector('button'); element._suggestionsView = new _suggestionsView2.default(element._dropdown, element._input); element._suggestionModel = new _suggestionsModel2.default(); element._suggestionModel.onChange = function (oldSuggestions) { var suggestionsToAdd = []; element._suggestionModel._suggestions.forEach(function (newSuggestion) { var inArray = oldSuggestions.some(function (oldSuggestion) { return newSuggestion.id === oldSuggestion.id; }); if (!inArray) { suggestionsToAdd.push(newSuggestion); } }); element._suggestionsView.render(suggestionsToAdd, oldSuggestions.length, element._listId); }; element._suggestionModel.onHighlightChange = function () { var active = element._suggestionModel.highlightedIndex(); element._suggestionsView.setActive(active); element._input.setAttribute('aria-activedescendant', getActiveId(element)); }; }, attached: function attached(element) { _skate2.default.init(element); initialiseProgressiveDataSet(element); associateDropdownAndTrigger(element); element._input.setAttribute('aria-controls', element._listId); element.setAttribute('tabindex', '-1'); bindHighlightMouseover(element); bindSelectMousedown(element); initialiseValue(element); setInitialVisualState(element); setInputImageToHighlightedSuggestion(element); }, attributes: { id: function id(element, data) { if (element.id) { element.querySelector('input').id = data.newValue + _constants.INPUT_SUFFIX; } }, name: function name(element, data) { element.querySelector('select').setAttribute('name', data.newValue); }, placeholder: function placeholder(element, data) { element.querySelector('input').setAttribute('placeholder', data.newValue); }, src: function src(element, data) { element._queryEndpoint = data.newValue; } }, events: { 'blur input': function blurInput(element) { focusOutHandler(element); }, 'mousedown button': function mousedownButton(element) { if (document.activeElement === element._input && element._dropdown.getAttribute('aria-hidden') === 'false') { (0, _state2.default)(element).set('prevent-open-on-button-click', true); } }, 'click input': function clickInput(element) { focusInHandler(element); }, 'click button': function clickButton(element) { var data = (0, _state2.default)(element); if (data.get('prevent-open-on-button-click')) { data.set('prevent-open-on-button-click', false); } else { element.focus(); } }, input: function input(element) { if (!element._input.value) { hideDropdown(element); } else { (0, _state2.default)(element).set('should-include-selected', true); suggest(element, true); } }, 'keydown input': function keydownInput(element, e) { var currentValue = element._input.value; var handled = false; if (e.keyCode === _keyCode2.default.ESCAPE) { cancelInProgressQueries(element); hideDropdown(element); return; } var isSuggestionViewVisible = element._suggestionsView.isVisible(); if (isSuggestionViewVisible && hasResults(element)) { if (e.keyCode === _keyCode2.default.ENTER) { cancelInProgressQueries(element); selectHighlightedSuggestion(element); e.preventDefault(); } else if (e.keyCode === _keyCode2.default.TAB) { handleTabOut(element); handled = true; } else if (e.keyCode === _keyCode2.default.UP) { element._suggestionModel.highlightPrevious(); e.preventDefault(); } else if (e.keyCode === _keyCode2.default.DOWN) { element._suggestionModel.highlightNext(); e.preventDefault(); } } else if (e.keyCode === _keyCode2.default.UP || e.keyCode === _keyCode2.default.DOWN) { focusInHandler(element); e.preventDefault(); } handled = handled || e.defaultPrevented; setTimeout(function emulateCrossBrowserInputEvent() { if (element._input.value !== currentValue && !handled) { element.dispatchEvent(new _customEvent2.default('input', { bubbles: true })); } }, 0); } }, prototype: { get value() { var selected = this._select.options[this._select.selectedIndex]; return selected ? selected.value : ''; }, set value(value) { if (value === '') { clearValue(this); } else if (value) { var data = this._progressiveDataSet; var model = data.findWhere({ value: value }) || data.findWhere({ label: value }); // Create a new value if allowed and the value doesn't exist. if (!model && this.hasAttribute('can-create-values')) { model = new _suggestionModel2.default({ value: value, label: value }); } setValueAndDisplayFromModel(this, model); } return this; }, get displayValue() { return this._input.value; }, blur: function blur() { this._input.blur(); focusOutHandler(this); return this; }, focus: function focus() { this._input.focus(); focusInHandler(this); return this; } } }); (0, _amdify2.default)('aui/select', select); (0, _globalize2.default)('select', select); exports.default = select; module.exports = exports['default']; }); //# sourceMappingURL=select.js.map