UNPKG

@bigfishtv/cockpit

Version:

222 lines (200 loc) 6.53 kB
import PropTypes from 'prop-types' import React, { Component } from 'react' import { withFormValue, update } from '@bigfishtv/react-forms' import deepDuplicate from '../../utils/deepDuplicate' import newId from '../../utils/newId' import { modalHandler } from '../modal/ModalHost' import Breadcrumb from '../breadcrumb/Breadcrumb' import SectionTray from '../SectionTray' import Section from '../container/Section' import ReorderableCellsModal from '../modal/ReorderableCellsModal' import SectionsPublishModal from '../modal/SectionsPublishModal' /* Section types data format: [ { property: 'example_sections', title: 'Example Section Title', icon: 'example-icon', component: ExampleSection, } ] */ // we define this because react-docgen fails when defaultProp directly references an imported component const DefaultSectionTray = props => <SectionTray {...props} /> @withFormValue export default class Sections extends Component { static propTypes = { SectionTypes: PropTypes.array, formValue: PropTypes.object, showTray: PropTypes.bool, } static defaultProps = { collapsible: true, reorderable: true, removable: true, duplicable: true, publishable: false, showTray: true, getBlankSection: (sectionType, formValue) => ({}), SectionTray: DefaultSectionTray, getSectionTitle: (section, sectionType) => section.title || section.name || section.label || <em>{sectionType.title}</em>, } constructor() { super() this.state = { collapsed: false, } } addSection = sectionType => { const { formValue, getBlankSection } = this.props if (!sectionType.property) { console.warn("sectionType.property doesn't exist for " + sectionType.title) return } const values = formValue.select(sectionType.property) const value = values.value && values.value.length ? values.value.slice() : [] value.push({ ...getBlankSection(sectionType, formValue), id: newId(), order: this.getNextOrderId(), isNew: true, }) values.update(value) } duplicateSection(sectionType, index) { let formValue = this.props.formValue let value = formValue.value // update order for all sections greater duplicated section const { order } = value[sectionType.property][index] this.getSections() .filter(_s => _s.section.order > order) .forEach(_s => { const path = [_s.sectionType.property, _s.index, 'order'] value = update(value, path, formValue.select(path).value + 1) }) // duplicate the section let sections = formValue.select(sectionType.property).value const section = { ...deepDuplicate(sections[index]), order: order + 1, } // splice new section into existing array sections = [...sections.slice(0, index + 1), section, ...sections.slice(index + 1)] // update sections array into value value = update(value, [sectionType.property], sections) // update formValue in one fell swoop formValue.update(value) } removeSection(sectionType, index) { if (!confirm('Are you sure you want to remove this?')) { return } const values = this.props.formValue.select(sectionType.property) const value = values.value.filter((val, i) => i !== index) values.update(value) } reorderSections(selectedSection) { const sections = this.getSections().map(section => ({ id: section.sectionType.property + section.index, index: section.index, property: section.sectionType.property, title: this.props.getSectionTitle(section.section, section.sectionType), })) modalHandler.add({ Component: ReorderableCellsModal, props: { value: sections, onSave: this.handleReorder, onClose: () => {}, }, }) } handleReorder = newList => { let { formValue } = this.props formValue.update( newList.reduce((acc, item, order) => update(acc, [item.property, item.index, 'order'], order), formValue.value) ) } publishSections(formValue, sections) { modalHandler.add({ Component: SectionsPublishModal, props: { formValue, sections, }, }) } getNextOrderId() { const sections = this.getSections() return sections.length ? sections[sections.length - 1].section.order + 1 : 0 } /** * Combine all sections into a single array of objects with { sectionType, section, index } keys * ordered by section.order * * It ignores section types that don't have a property or component value. * * @return array The array of sections */ getSections() { const { SectionTypes, formValue } = this.props const val = formValue.value const typesByProperty = {} // filter out duplicate section types or types without components, etc SectionTypes.forEach(sectionType => { if ( sectionType.component && sectionType.property && !typesByProperty[sectionType.property] && val && val[sectionType.property] && val[sectionType.property].length ) { typesByProperty[sectionType.property] = sectionType } }) // join sections together and sort return Object.keys(typesByProperty) .reduce((acc, sectionTypeProperty) => { const sectionType = typesByProperty[sectionTypeProperty] return acc.concat(val[sectionType.property].map((section, index) => ({ sectionType, section, index }))) }, []) .sort((a, b) => a.section.order - b.section.order) } renderSection = ({ sectionType, section, index }, i, allSections) => { const { SectionTypes, formValue, select, ...props } = this.props const sectionFormValue = formValue.select([sectionType.property, index]) return ( <Breadcrumb title={section.title || sectionType.title} key={`${sectionType.property}${index}${this.state.collapsed ? 'a' : 'b'}`}> <Section {...props} Component={sectionType.component} icon={sectionType.icon} sectionProperty={sectionType.property} formValue={sectionFormValue} collapsed={this.state.collapsed} onToggleCollapsed={() => this.setState({ collapsed: !this.state.collapsed })} onDuplicate={() => this.duplicateSection(sectionType, index)} onRemove={() => this.removeSection(sectionType, index)} onReorder={() => this.reorderSections(section)} onPublishEdit={() => this.publishSections(sectionFormValue, allSections)} isNew={section.isNew} allSections={allSections} /> </Breadcrumb> ) } render() { const { SectionTypes, SectionTray, showTray } = this.props return ( <div> {this.getSections().map(this.renderSection)} {showTray && <SectionTray SectionTypes={SectionTypes} addSection={this.addSection} />} </div> ) } }