mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
48e90c4998
Summary: - No longer convert data columns to formula by typing a leading "=". Instead, show a tooltip with a link to click if the conversion was intended. - No longer convert a formula column to data by deleting its formula. Leave the column empty instead. - Offer the option "Convert formula to data" in column menu for formulas. - Offer the option to "Clear column" - If a subset of rows is shown, offer "Clear values" and "Clear entire column". - Add logic to detect when a view shows a subset of all rows. - Factor out showTooltip() from showTransientTooltip(). - Add a bunch of test cases to cover various combinations (there are small variations in options depending on whether all rows are shown, on whether multiple columns are selected, and whether columns include data columns). Test Plan: Added a bunch of test cases. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2746
119 lines
4.8 KiB
JavaScript
119 lines
4.8 KiB
JavaScript
var _ = require('underscore');
|
|
var gutil = require('app/common/gutil');
|
|
var dom = require('../lib/dom');
|
|
var kd = require('../lib/koDom');
|
|
var dispose = require('../lib/dispose');
|
|
var BaseEditor = require('./BaseEditor');
|
|
var commands = require('../components/commands');
|
|
const {testId} = require('app/client/ui2018/cssVars');
|
|
const {createMobileButtons, getButtonMargins} = require('app/client/widgets/EditorButtons');
|
|
const {EditorPlacement} = require('app/client/widgets/EditorPlacement');
|
|
|
|
/**
|
|
* Required parameters:
|
|
* @param {RowModel} options.field: ViewSectionField (i.e. column) being edited.
|
|
* @param {Object} options.cellValue: The value in the underlying cell being edited.
|
|
* @param {String} options.editValue: String to be edited, or undefined to use cellValue.
|
|
* @param {Number} options.cursorPos: The initial position where to place the cursor.
|
|
* @param {Object} options.commands: Object mapping command names to functions, to enable as part
|
|
* of the command group that should be activated while the editor exists.
|
|
*
|
|
* Optional parameters:
|
|
* @param {String} options.placeholder: Optional placeholder for the textarea.
|
|
*
|
|
* TextEditor exposes the following members, which derived classes may use:
|
|
* @member {Object} this.options: Options as passed into the constructor.
|
|
* @member {Node} this.dom: The DOM element for the editor.
|
|
* @member {Node} this.textInput: The textarea element of the editor (contained within this.dom).
|
|
* @member {Object} this.commandGroup: The CommandGroup created from options.commands.
|
|
*/
|
|
function TextEditor(options) {
|
|
this.options = options;
|
|
this.commandGroup = this.autoDispose(commands.createGroup(options.commands, null, true));
|
|
this._alignment = options.field.widgetOptionsJson.peek().alignment || 'left';
|
|
|
|
this.dom = dom('div.default_editor',
|
|
dom('div.celleditor_cursor_editor', dom.testId('TextEditor_editor'),
|
|
testId('widget-text-editor'), // new-style testId matches NTextEditor, for more uniform tests.
|
|
this.contentSizer = dom('div.celleditor_content_measure'),
|
|
this.textInput = dom('textarea.celleditor_text_editor',
|
|
kd.attr('placeholder', options.placeholder || ''),
|
|
kd.style('text-align', this._alignment),
|
|
kd.value(gutil.undefDefault(options.editValue, String(options.cellValue))),
|
|
this.commandGroup.attach(),
|
|
|
|
// Resize the textbox whenever user types in it.
|
|
dom.on('input', () => this._resizeInput())
|
|
)
|
|
),
|
|
createMobileButtons(options.commands),
|
|
);
|
|
}
|
|
|
|
dispose.makeDisposable(TextEditor);
|
|
_.extend(TextEditor.prototype, BaseEditor.prototype);
|
|
|
|
TextEditor.prototype.attach = function(cellElem) {
|
|
// Attach the editor dom to page DOM.
|
|
this.editorPlacement = EditorPlacement.create(this, this.dom, cellElem, {margins: getButtonMargins()});
|
|
|
|
// Reposition the editor if needed for external reasons (in practice, window resize).
|
|
this.autoDispose(this.editorPlacement.onReposition.addListener(this._resizeInput, this));
|
|
|
|
this.setSizerLimits();
|
|
|
|
// Once the editor is attached to DOM, resize it to content, focus, and set cursor.
|
|
this._resizeInput();
|
|
this.textInput.focus();
|
|
var pos = Math.min(this.options.cursorPos, this.textInput.value.length);
|
|
this.textInput.setSelectionRange(pos, pos);
|
|
};
|
|
|
|
TextEditor.prototype.getDom = function() {
|
|
return this.dom;
|
|
};
|
|
|
|
TextEditor.prototype.setSizerLimits = function() {
|
|
// Set the max width of the sizer to the max we could possibly grow to, so that it knows to wrap
|
|
// once we reach it.
|
|
const maxSize = this.editorPlacement.calcSizeWithPadding(this.textInput,
|
|
{width: Infinity, height: Infinity}, {calcOnly: true});
|
|
this.contentSizer.style.maxWidth = Math.ceil(maxSize.width) + 'px';
|
|
};
|
|
|
|
TextEditor.prototype.getCellValue = function() {
|
|
return this.textInput.value;
|
|
};
|
|
|
|
TextEditor.prototype.getTextValue = function() {
|
|
return this.textInput.value;
|
|
};
|
|
|
|
TextEditor.prototype.getCursorPos = function() {
|
|
return this.textInput.selectionStart;
|
|
};
|
|
|
|
/**
|
|
* Helper which resizes textInput to match its content. It relies on having a contentSizer element
|
|
* with the same font/size settings as the textInput, and on having `calcSize` helper,
|
|
* which is provided by the EditorPlacement class.
|
|
*/
|
|
TextEditor.prototype._resizeInput = function() {
|
|
var textInput = this.textInput;
|
|
// \u200B is a zero-width space; it is used so the textbox will expand vertically
|
|
// on newlines, but it does not add any width.
|
|
this.contentSizer.textContent = textInput.value + '\u200B';
|
|
var rect = this.contentSizer.getBoundingClientRect();
|
|
|
|
// Allow for a bit of extra space after the cursor (only desirable when text is left-aligned).
|
|
if (this._alignment === 'left') {
|
|
rect.width += 16;
|
|
}
|
|
|
|
var size = this.editorPlacement.calcSizeWithPadding(textInput, rect);
|
|
textInput.style.width = size.width + 'px';
|
|
textInput.style.height = size.height + 'px';
|
|
};
|
|
|
|
module.exports = TextEditor;
|