// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.ui;
import com.google.collide.client.util.Elements;
import com.google.common.base.Preconditions;
import elemental.css.CSSStyleDeclaration;
import elemental.events.Event;
import elemental.events.EventListener;
import elemental.html.TextAreaElement;
/**
* Creates a GrowableTextArea, it isn't perfect but it does a fairly decent job
* of growing as the user types in new text.
*
*/
/*
* How it works:
*
* This class creates two text areas, one which is returned to the user and a
* second which is hidden and placed off-screen. As users type into the main
* text area, the text is also put into the hidden text area which has an
* explicit row height of 1. We use the scrollHeight of the hidden text area to
* determine the number of row necessary to display the text in the main area
* without scrolling. It is off a little bit due to the scrollbar width changing
* the width of the hidden text area a little bit, but generally it is good
* enough.
*/
public class GrowableTextArea {
public static GrowableTextArea create(String className) {
return createWithMinimumRows(className, 1);
}
/**
* @param minRows The minimum number of rows for this {@link GrowableTextArea}
* to be.
*/
public static GrowableTextArea createWithMinimumRows(String className, int minRows) {
Preconditions.checkArgument(minRows >= 1, "Minimum rows must be >= 1");
// Create the base text element and pass it in
TextAreaElement element = Elements.createTextAreaElement();
element.setClassName(className);
element.setRows(minRows);
element.getStyle().setOverflowY(CSSStyleDeclaration.OverflowY.HIDDEN);
return new GrowableTextArea(element, minRows);
}
private final TextAreaElement element;
private final TextAreaElement clone;
private final int minimumRows;
public GrowableTextArea(TextAreaElement element, int minimumRows) {
this.element = element;
this.minimumRows = minimumRows;
this.clone = Elements.createTextAreaElement();
setupClone();
attachEvents();
}
private void setupClone() {
clone.setClassName(element.getClassName());
CSSStyleDeclaration style = clone.getStyle();
style.setVisibility(CSSStyleDeclaration.Visibility.HIDDEN);
style.setPosition(CSSStyleDeclaration.Position.ABSOLUTE);
style.setOverflow(CSSStyleDeclaration.Overflow.AUTO);
style.setLeft("-5000px");
style.setTop("-5000px");
clone.setRows(1);
}
private void attachEvents() {
element.addEventListener(Event.INPUT, new EventListener() {
@Override
public void handleEvent(Event evt) {
if (clone.getParentElement() == null) {
element.getParentElement().appendChild(clone);
}
clone.setValue(element.getValue());
int requiredRows =
(int) Math.ceil((double) clone.getScrollHeight() / (double) clone.getClientHeight());
int rows = Math.max(requiredRows, minimumRows);
element.setRows(rows);
}
}, false);
}
public TextAreaElement asTextArea() {
return element;
}
}