/** * 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. * Controls the element visibility on the pages, * and also the element "link to page" property * */ import { SelectableState } from 'drag-drop-stage-component/src/ts/Types'; import { Constants } from '../../../Constants'; import { Controller, Model } from '../../types'; import { Dom } from '../../utils/dom'; 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 PagePane extends PaneBase { /** * dropdown list to select a link * link, select page or enter custom link */ linkDropdown: HTMLInputElement; /** * text field used to type an external link */ linkInputTextField: HTMLInputElement; /** * check box "view on mobile" */ viewOnDeviceEl: HTMLDivElement = null; /** * check box "view on all pages" */ viewOnAllPagesCheckbox: HTMLInputElement = null; /** * Array of checkboxes used to add/remove the element from pages */ pageCheckboxes: Array<{checkbox: HTMLInputElement, pageName: string}> = null; constructor(element: HTMLElement, model: Model, controller: Controller) { super(element, model, controller); // init the component this.buildUi(); } /** * build the UI */ buildUi() { this.linkDropdown = this.element.querySelector('.link-combo-box'); this.linkDropdown.onchange = () => this.onLinkChanged(); this.linkInputTextField = this.element.querySelector('.link-input-text'); // hide by default this.linkInputTextField.style.display = 'none'; // Watch for field changes, to display below. this.linkInputTextField.oninput = () => this.onLinkTextChanged(); this.viewOnDeviceEl = (this.element.querySelector('.view-on-mobile') as HTMLDivElement); this.viewOnDeviceEl.onclick = (e) => { const selected: HTMLInputElement = this.element.querySelector('.view-on-mobile input:checked'); const value = selected.value; this.states.forEach((state) => { this.model.element.setHideOnMobile(state.el, value === 'desktop'); this.model.element.setHideOnDesktop(state.el, value === 'mobile'); }); }; this.viewOnAllPagesCheckbox = this.element.querySelector('.view-on-allpages-check'); this.viewOnAllPagesCheckbox.onchange = () => { const elements = this.states.map((state) => state.el); if (this.viewOnAllPagesCheckbox.checked) { this.controller.propertyToolController.visibleOnAllPages(elements); } else { this.controller.propertyToolController.addToPage(elements); } }; } /** * refresh with new pages * @param pages the new list of pages */ setPages(pages: string[]) { this.pageNames = pages; // build an array of obects with name and displayName properties const pageData = pages.map((pageName) => { const pageElement = this.model.file.getContentDocument().getElementById(pageName); if (!pageElement) { // this happens while undoing or redoing return null; } return { name: pageName, displayName: pageElement.innerHTML, linkName: '#!' + pageName, }; }); // link selector const pageDataWithDefaultOptions = [ {name: 'none', displayName: '-', linkName: 'none'}, {name: 'custom', displayName: 'External link', linkName: 'custom'}, ].concat(pageData); const linkContainer = this.element.querySelector('.link-combo-box'); let templateHtml = this.element.querySelector('.link-template').innerHTML; linkContainer.innerHTML = Dom.renderList(templateHtml, pageDataWithDefaultOptions); // render page/visibility template // init page template const pagesContainer = this.element.querySelector('.pages-container'); templateHtml = this.element.querySelector('.pages-selector-template').innerHTML; pagesContainer.innerHTML = Dom.renderList(templateHtml, pageData); // reset page checkboxes if (this.pageCheckboxes) { this.pageCheckboxes.forEach((item) => { if (item.checkbox.parentElement != null ) { item.checkbox.parentElement.removeChild(item.checkbox); } item.checkbox.onchange = null; }); } // create page checkboxes const mainContainer = this.element.querySelector('.pages-container'); const items = (Array.from(mainContainer.querySelectorAll('.page-container')) as HTMLElement[]); this.pageCheckboxes = items.map((item, idx) => { const checkbox: HTMLInputElement = item.querySelector('.page-check'); const name = this.pageNames[idx++]; checkbox.onchange = () => { this.checkPage(name, checkbox); }; return {checkbox, pageName: name}; }); } /** * the user changed the link drop down */ onLinkChanged() { if (this.linkDropdown.value === 'none') { this.controller.propertyToolController.removeLink(this.states.map((state) => state.el)); this.linkInputTextField.style.display = 'none'; } else { if (this.linkDropdown.value === 'custom') { this.linkInputTextField.value = ''; this.linkInputTextField.style.display = 'inherit'; } else { this.controller.propertyToolController.addLink(this.states.map((state) => state.el), this.linkDropdown.value); } } } /** * the user changed the link text field */ onLinkTextChanged() { this.controller.propertyToolController.addLink(this.states.map((state) => state.el), this.linkInputTextField.value); } /** * 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); // update page list this.setPages(pageNames); // View on mobile checkbox Array.from(this.viewOnDeviceEl.querySelectorAll('.view-on-mobile input')) .forEach((el: HTMLInputElement) => el.disabled = !this.model.head.getEnableMobile()); // not available for stage element const statesNoBody = states.filter((data) => data.el !== this.model.body.getBodyElement()); if (statesNoBody.length > 0) { // update the "view on mobile" checkbox const visibility = this.getCommonProperty(states, (state) => { if (this.model.element.getHideOnMobile(state.el)) { return 'desktop'; } else { if (this.model.element.getHideOnDesktop(state.el)) { return 'mobile'; } else { return 'both'; } } }); if (!!visibility) { Array.from(this.viewOnDeviceEl.querySelectorAll('.view-on-mobile input')) .forEach((el: HTMLInputElement) => { el.checked = visibility === el.value; el.indeterminate = false; }); } else { Array.from(this.viewOnDeviceEl.querySelectorAll('.view-on-mobile input')) .forEach((el: HTMLInputElement) => el.indeterminate = true); } // not stage element only this.linkDropdown.disabled = false; // refresh page checkboxes let isInNoPage = true; this.pageCheckboxes.forEach((item) => { // there is a selection item.checkbox.disabled = false; // compute common pages const isInPage = this.getCommonProperty(states, (state) => this.model.page.isInPage(state.el, item.pageName)); // set visibility isInNoPage = isInNoPage && isInPage === false; if (isInPage === null) { // multiple elements selected with different values item.checkbox.indeterminate = true; } else { item.checkbox.indeterminate = false; item.checkbox.checked = isInPage; } }); this.viewOnAllPagesCheckbox.disabled = false; // this.checkAllPages(); if (isInNoPage) { this.viewOnAllPagesCheckbox.checked = true; } else { this.viewOnAllPagesCheckbox.checked = false; } // refresh the link inputs // get the link of the element const elementLink = this.getCommonProperty(states, (state) => state.el.getAttribute(Constants.LINK_ATTR)); // default selection if (!elementLink || elementLink === '') { this.linkDropdown.value = 'none'; this.linkInputTextField.value = ''; } else { if (elementLink.indexOf('#!') === 0) { // case of an internal link // select a page this.linkDropdown.value = elementLink; } else { // in case it is a custom link this.linkInputTextField.value = elementLink; this.linkDropdown.value = 'custom'; } } if (this.linkDropdown.value === 'custom') { this.linkInputTextField.style.display = 'inherit'; } else { this.linkInputTextField.style.display = 'none'; } } else { // body element only this.pageCheckboxes.forEach((item) => { item.checkbox.disabled = true; item.checkbox.indeterminate = true; }); this.linkDropdown.value = 'none'; this.linkDropdown.disabled = true; this.linkInputTextField.style.display = 'none'; this.viewOnAllPagesCheckbox.disabled = true; this.viewOnAllPagesCheckbox.checked = true; Array .from(this.viewOnDeviceEl.querySelectorAll('.view-on-mobile input')) .forEach((el: HTMLInputElement) => el.disabled = true); } } /** * callback for checkboxes click event * changes the visibility of the current component for the given page * @param pageName the page for which the visibility changes * @param checkbox the checkbox clicked */ checkPage(pageName: string, checkbox: HTMLInputElement) { // notify the toolbox if (checkbox.checked) { this.controller.propertyToolController.addToPage(this.states.map((state) => state.el), pageName); } else { this.controller.propertyToolController.removeFromPage(this.states.map((state) => state.el), pageName); } } }