blob: f58c6c09e27d6720faef394689dae7b84c9b116b [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
{
constructor(delegate, property, index, newlyAdded)
{
super();
this._delegate = delegate || null;
this._property = property;
this._newlyAdded = newlyAdded || false;
this._element = document.createElement("div");
this._nameElement = null;
this._valueElement = null;
this._nameTextField = null;
this._valueTextField = null;
this._update();
property.addEventListener(WI.CSSProperty.Event.OverriddenStatusChanged, this._update, this);
}
// Public
get element() { return this._element; }
get nameTextField() { return this._nameTextField; }
get valueTextField() { return this._valueTextField; }
// Private
_remove()
{
this.element.remove();
this._property.remove();
if (this._delegate && typeof this._delegate.spreadsheetStylePropertyRemoved === "function")
this._delegate.spreadsheetStylePropertyRemoved(this);
}
_update()
{
this.element.removeChildren();
this.element.className = "";
let duplicatePropertyExistsBelow = (cssProperty) => {
let propertyFound = false;
for (let property of this._property.ownerStyle.properties) {
if (property === cssProperty)
propertyFound = true;
else if (property.name === cssProperty.name && propertyFound)
return true;
}
return false;
};
let classNames = ["property"];
if (this._property.overridden)
classNames.push("overridden");
if (this._property.implicit)
classNames.push("implicit");
if (this._property.ownerStyle.inherited && !this._property.inherited)
classNames.push("not-inherited");
if (!this._property.valid && this._property.hasOtherVendorNameOrKeyword())
classNames.push("other-vendor");
else if (!this._property.valid) {
let propertyNameIsValid = false;
if (WI.CSSCompletions.cssNameCompletions)
propertyNameIsValid = WI.CSSCompletions.cssNameCompletions.isValidPropertyName(this._property.name);
if (!propertyNameIsValid || duplicatePropertyExistsBelow(this._property))
classNames.push("invalid");
}
if (!this._property.enabled)
classNames.push("disabled");
this._element.classList.add(...classNames);
if (this._property.editable) {
this._checkboxElement = this.element.appendChild(document.createElement("input"));
this._checkboxElement.classList.add("property-toggle");
this._checkboxElement.type = "checkbox";
this._checkboxElement.checked = this._property.enabled;
this._checkboxElement.tabIndex = -1;
this._checkboxElement.addEventListener("change", () => {
let disabled = !this._checkboxElement.checked;
this._property.commentOut(disabled);
this._update();
});
}
if (!this._property.enabled)
this.element.append("/* ");
this._nameElement = this.element.appendChild(document.createElement("span"));
this._nameElement.classList.add("name");
this._nameElement.textContent = this._property.name;
this.element.append(": ");
this._valueElement = this.element.appendChild(document.createElement("span"));
this._valueElement.classList.add("value");
this._valueElement.textContent = this._property.rawValue;
if (this._property.editable && this._property.enabled) {
this._nameElement.tabIndex = 0;
this._nameTextField = new WI.SpreadsheetTextField(this, this._nameElement);
this._valueElement.tabIndex = 0;
this._valueTextField = new WI.SpreadsheetTextField(this, this._valueElement);
}
this.element.append(";");
if (!this._property.enabled)
this.element.append(" */");
}
spreadsheetTextFieldDidChange(textField)
{
if (textField === this._valueTextField)
this.debounce(WI.SpreadsheetStyleProperty.CommitCoalesceDelay)._handleValueChange();
else if (textField === this._nameTextField)
this.debounce(WI.SpreadsheetStyleProperty.CommitCoalesceDelay)._handleNameChange();
}
spreadsheetTextFieldDidCommit(textField, {direction})
{
let propertyName = this._nameTextField.value.trim();
let propertyValue = this._valueTextField.value.trim();
let willRemoveProperty = false;
// Remove a property with an empty name or value. However, a newly added property
// has an empty name and value at first. Don't remove it when moving focus from
// the name to the value for the first time.
if (!propertyName || (!this._newlyAdded && !propertyValue))
willRemoveProperty = true;
let isEditingName = textField === this._nameTextField;
if (propertyName && isEditingName)
this._newlyAdded = false;
if (direction === "forward") {
if (isEditingName && !willRemoveProperty) {
// Move focus from the name to the value.
this._valueTextField.startEditing();
return;
}
} else {
if (!isEditingName) {
// Move focus from the value to the name.
this._nameTextField.startEditing();
return;
}
}
if (typeof this._delegate.spreadsheetCSSStyleDeclarationEditorFocusMoved === "function") {
// Move focus away from the current property, to the next or previous one, if exists, or to the next or previous rule, if exists.
this._delegate.spreadsheetCSSStyleDeclarationEditorFocusMoved({direction, willRemoveProperty, movedFromProperty: this});
}
if (willRemoveProperty)
this._remove();
}
spreadsheetTextFieldDidBlur(textField)
{
if (textField.value.trim() === "")
this._remove();
}
_handleNameChange()
{
this._property.name = this._nameElement.textContent.trim();
}
_handleValueChange()
{
this._property.rawValue = this._valueElement.textContent.trim();
}
};
WI.SpreadsheetStyleProperty.CommitCoalesceDelay = 250;