silex-website-builder-goodevent
Version:
Free and easy website builder for everyone.
318 lines (282 loc) • 10.7 kB
text/typescript
/**
* 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);
}
}
}