/** * Silex, live web creation * http://projects.silexlabs.org/?/silex/ * * Copyright (c) 2012 Silex Labs * http://www.silexlabs.org/ * * Silex is available under the GPL license * http://www.silexlabs.org/silex/silex-licensing/ */ /** * @fileoverview Property pane, displayed in the property tool box * */ import { SelectableState } from 'drag-drop-stage-component/src/ts/Types'; import { Constants } from '../../../Constants'; import { SilexElement } from '../../model/element'; import { Controller, Model } from '../../types'; import { PaneBase } from './pane-base'; /** * on of Silex Editors class * const user edit style of components * @param element container to render the UI * @param model model class which holds * the model instances - views use it for read * operation only * @param controller structure which holds * the controller instances */ export class PropertyPane extends PaneBase { /** * UI for position and size */ leftInput: HTMLInputElement; /** * UI for position and size */ topInput: HTMLInputElement; /** * UI for position and size */ widthInput: HTMLInputElement; /** * UI for position and size */ heightInput: HTMLInputElement; /** * UI for alt and title * only used for images */ altInput: HTMLInputElement; /** * UI for alt and title */ titleInput: HTMLInputElement; constructor(element: HTMLElement, model: Model, controller: Controller) { super(element, model, controller); // init the component this.buildUi(); } /** * build the UI */ buildUi() { this.leftInput = this.initInput('.left-input', (e) => this.onPositionChanged(e)); this.leftInput.setAttribute('data-style-name', 'left'); this.widthInput = this.initInput('.width-input', (e) => this.onPositionChanged(e)); this.widthInput.setAttribute('data-style-name', 'width'); this.topInput = this.initInput('.top-input', (e) => this.onPositionChanged(e)); this.topInput.setAttribute('data-style-name', 'top'); this.heightInput = this.initInput('.height-input', (e) => this.onPositionChanged(e)); this.heightInput.setAttribute('data-style-name', 'min-height'); this.altInput = this.initInput('.alt-input', (e) => this.onAltChanged(e)); this.titleInput = this.initInput('.title-input', (e) => this.onTitleChanged(e)); } /** * position or size changed * callback for number inputs */ onPositionChanged(e: Event) { // get the selected element const input = e.target as HTMLInputElement; // the name of the property to change const name: string = input.getAttribute('data-style-name'); // do nothing if the value is not a number (numeric stepers's value set to // '') if (input.value !== '') { // get the value const value = parseFloat(input.value); // get the old value const oldValue = parseFloat(input.getAttribute('data-prev-value') || '0'); // keep track of the new value for next time input.setAttribute('data-prev-value', value.toString()); // compute the offset const offset = value - oldValue; // apply the change to all elements this.states.forEach((state) => { if (isNaN(oldValue)) { // compute the new value relatively to the old value, // in order to match the group movement const elementStyle = this.model.element.getStyle(state.el, name); let styleValue = 0; if (elementStyle && elementStyle !== '') { styleValue = parseFloat(elementStyle); } const newValue = styleValue + offset; // apply the change to the current element this.styleChanged(name, newValue + 'px', [state.el]); } else { this.styleChanged(name, value + 'px'); } }); } } /** * alt changed * callback for inputs */ onAltChanged(e: Event) { // get the selected element const input = e.target as HTMLInputElement; // apply the change to all elements if (input.value !== '') { this.propertyChanged('alt', input.value, null, true); } else { this.propertyChanged('alt', null, null, true); } } /** * title changed * callback for inputs */ onTitleChanged(e: Event) { // get the selected element const input = e.target as HTMLInputElement; // apply the change to all elements if (input.value !== '') { this.propertyChanged('title', input.value); } else { this.propertyChanged('title'); } } /** * redraw the properties * @param states the elements currently selected * @param pageNames the names of the pages which appear in the current HTML file * @param currentPageName the name of the current page */ redraw(states: SelectableState[], pageNames: string[], currentPageName: string) { super.redraw(states, pageNames, currentPageName); // not available for stage element const statesNoBody: SelectableState[] = states.filter((data) => data.el !== this.model.body.getBodyElement()); this.states = statesNoBody; if (statesNoBody.length > 0) { // not stage element only this.leftInput.disabled = false; this.topInput.disabled = false; this.widthInput.disabled = false; this.heightInput.disabled = false; this.altInput.disabled = false; this.titleInput.disabled = false; // TODO: Use stage metrics here const bb = this.model.property.getBoundingBox(states.map((state) => state.el)); // display position and size this.topInput.value = (bb.top || 0).toString(); this.leftInput.value = (bb.left || 0).toString(); this.widthInput.value = (bb.width || 0).toString(); this.heightInput.value = (bb.height || 0).toString(); // special case of the background / main container only selected element if (statesNoBody.length === 1 && this.model.element.isSection(statesNoBody[0].el)) { this.widthInput.value = ''; this.widthInput.disabled = true; } else { this.widthInput.disabled = false; } // alt, only for images const elementsType = this.getCommonProperty(states, (state) => this.model.element.getType(state.el)); if (elementsType === Constants.TYPE_IMAGE) { this.altInput.disabled = false; const alt = this.getCommonProperty(states, (state) => { const content = this.model.element.getContentNode(state.el); if (content) { return content.getAttribute('alt'); } return null; }); if (alt) { this.altInput.value = alt; } else { this.altInput.value = ''; } } else { this.altInput.value = ''; this.altInput.disabled = true; } // title const title = this.getCommonProperty(states, (state) => state.el.getAttribute('title')); if (title) { this.titleInput.value = title; } else { this.titleInput.value = ''; } } else { // stage element only this.leftInput.disabled = true; this.leftInput.value = ''; this.topInput.disabled = true; this.topInput.value = ''; this.widthInput.disabled = true; this.widthInput.value = ''; this.heightInput.disabled = true; this.heightInput.value = ''; this.altInput.disabled = true; this.altInput.value = ''; this.titleInput.disabled = true; this.titleInput.value = ''; } // keep track of old position and size this.topInput.setAttribute('data-prev-value', this.topInput.value); this.leftInput.setAttribute('data-prev-value', this.leftInput.value); this.widthInput.setAttribute('data-prev-value', this.widthInput.value); this.heightInput.setAttribute('data-prev-value', this.heightInput.value); } /** * helper for other views, * because views (view.workspace.get/setMobileEditor) is not accessible from * other views * FIXME: find another way to expose isMobileEditor to views */ isMobileMode() { return document.body.classList.contains('mobile-mode'); } }