VulpesOS/gaia/apps/keyboard/js/views/candidate_panel_view.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

257 lines
7.2 KiB
JavaScript
Executable file

'use strict';
/* global BaseView */
(function(exports) {
/**
* CandidateView handles the rendering of each candidate, i.e.
* the word suggestion.
*/
function CandidateView() {
BaseView.apply(this, arguments);
}
CandidateView.prototype = Object.create(BaseView.prototype);
CandidateView.prototype.render = function render() {
var span = document.createElement('span');
span.textContent = this.target.text;
span.style.flex = this.options.unit;
// ui/integration test needs this
span.dataset.data = this.target.data;
this.element = span;
this.viewManager.registerView(this.target, this);
};
/**
* CandidateToggleButtonView represents the toggle button to show/hide
* the full candidate panel.
*/
function CandidateToggleButtonView() {
BaseView.apply(this, arguments);
}
CandidateToggleButtonView.prototype = Object.create(BaseView.prototype);
CandidateToggleButtonView.prototype.render = function() {
var toggleButton = document.createElement('div');
toggleButton.classList.add('keyboard-candidate-panel-toggle-button');
toggleButton.classList.add('hide');
this.element = toggleButton;
this.viewManager.registerView(this.target, this);
};
CandidateToggleButtonView.prototype.show = function() {
this.element.classList.remove('hide');
};
CandidateToggleButtonView.prototype.hide = function() {
this.element.classList.add('hide');
};
/**
* CandidatePanelView handles the rendering of candidate panel, used for showing
* word suggestions.
*/
function CandidatePanelView(target, options, viewManager) {
BaseView.apply(this, arguments);
}
// Default value for how many candidates shown in a row.
CandidatePanelView.prototype.COUNT_PER_ROW = 8;
CandidatePanelView.prototype.HEIGHT_IN_REM = 3.2;
CandidatePanelView.prototype.render = function render() {
var candidatePanel = document.createElement('div');
candidatePanel.setAttribute('role', 'group');
candidatePanel.dataset.l10nId = 'wordSuggestions2';
candidatePanel.classList.add('keyboard-candidate-panel');
if (this.options.className) {
candidatePanel.classList.add(this.options.className);
}
var suggestionsContainer = document.createElement('div');
suggestionsContainer.classList.add('suggestions-container');
suggestionsContainer.setAttribute('role', 'listbox');
candidatePanel.appendChild(suggestionsContainer);
this.suggestionsContainer = suggestionsContainer;
// Create toggle button
// we're not getting reference of LayoutManager, so define this manually
var KEYCODE_TOGGLE_CANDIDATE_PANEL = -4;
// TODO: we should not create a business logic object in the view,
// let's move it to somewhere else.
this.toggleButton = new CandidateToggleButtonView({
keyCode: KEYCODE_TOGGLE_CANDIDATE_PANEL },
null,
this.viewManager);
this.toggleButton.render();
candidatePanel.appendChild(this.toggleButton.element);
this.element = candidatePanel;
};
//
// Show candidates
// Each candidate is a string or an array of two strings
//
CandidatePanelView.prototype.showCandidates = function(candidates) {
if (!this.element) {
console.error('CandidatePanelView: CandidatePanel is not ready yet!');
return;
}
var candidatePanel = this.element;
candidatePanel.dataset.candidateIndicator = 0;
this.suggestionsContainer.innerHTML = '';
this.toggleButton.hide();
var docFragment = this.renderCandidatePanel(1, candidates, true);
this.suggestionsContainer.appendChild(docFragment);
};
CandidatePanelView.prototype.renderCandidatePanel = function(rowLimit,
candidates,
indentFirstRow) {
var candidatePanel = this.element;
var docFragment = document.createDocumentFragment();
if (candidates.length === 0) {
candidatePanel.dataset.rowCount = 0;
return docFragment;
}
var rowDiv = document.createElement('div');
rowDiv.classList.add('candidate-row');
if (indentFirstRow) {
rowDiv.classList.add('candidate-row-first');
}
var nowUnit = 0;
var rowCount = 0;
if (rowLimit < 0) {
rowLimit = Number.Infinity;
}
var candidatesLength = candidates.length;
var dummyEl = null;
var i = 0;
candidates.some(function(candidate, index) {
var cand, data;
if (typeof candidate == 'string') {
cand = data = candidate;
} else {
cand = candidate[0];
data = candidate[1];
}
var unit = this.options.widthUnit ? this.options.widthUnit :
(cand.length >> 1) + 1;
// TODO: we should not create a business logic object in the view,
// let's move it to somewhere else.
var candidateView = new CandidateView({ suggestion: true,
selection: true,
text: cand,
data: data },
{ unit: unit },
this.viewManager);
candidateView.render();
nowUnit += unit;
i++;
var needBreak = false;
if (rowCount === 0 && indentFirstRow &&
nowUnit >= this.COUNT_PER_ROW && index != candidatesLength - 1) {
needBreak = true;
}
if (nowUnit > this.COUNT_PER_ROW || needBreak) {
if (rowCount === 0 && needBreak) {
this.toggleButton.show();
// Create a dummy element to represent the toggle button to occupy
// the space that should be taken by it.
dummyEl = document.createElement('span');
dummyEl.style.flex = this.COUNT_PER_ROW - (nowUnit - unit);
rowDiv.appendChild(dummyEl);
}
if (rowCount >= rowLimit - 1) {
return true;
}
docFragment.appendChild(rowDiv);
rowCount++;
// Create the next row
rowDiv = document.createElement('div');
rowDiv.classList.add('candidate-row');
nowUnit = unit;
}
rowDiv.appendChild(candidateView.element);
}, this);
// Insert a dummy element to fill up the flex box to make it look
// like a grid.
if (nowUnit < this.COUNT_PER_ROW &&
typeof this.options.widthUnit === 'undefined') {
dummyEl = document.createElement('span');
dummyEl.style.flex = this.COUNT_PER_ROW - nowUnit;
rowDiv.appendChild(dummyEl);
}
if (i != candidatesLength) {
candidatePanel.dataset.truncated = true;
} else {
delete candidatePanel.dataset.truncated;
}
candidatePanel.dataset.rowCount = rowCount + 1;
candidatePanel.dataset.candidateIndicator =
parseInt(candidatePanel.dataset.candidateIndicator) + i;
docFragment.appendChild(rowDiv);
rowDiv = null;
return docFragment;
};
CandidatePanelView.prototype.showMoreCandidates = function(rowLimit,
candidates) {
if (!candidates) {
return;
}
if (!rowLimit) {
rowLimit = -1;
}
this.suggestionsContainer.appendChild(
this.renderCandidatePanel(rowLimit, candidates));
};
CandidatePanelView.prototype.resetScroll = function() {
this.suggestionsContainer.scrollTop =
this.suggestionsContainer.scrollLeft = 0;
};
CandidatePanelView.prototype.getHeight = function() {
return this.HEIGHT_IN_REM * this.viewManager.getRemToPx();
};
exports.CandidatePanelView = CandidatePanelView;
})(window);