attributes-kit
Version:
React component for MSON rendering
334 lines (284 loc) • 8.44 kB
JavaScript
import sift from 'sift';
import each from 'lodash/each';
import isEmpty from 'lodash/isEmpty';
import last from 'lodash/last';
import map from 'lodash/map';
import max from 'lodash/max';
import React from 'react';
import PropTypes from 'prop-types';
import values from 'lodash/values';
import Radium from 'radium';
import merge from 'lodash/merge';
import Select from '../Select/Select';
import ObjectProperty from '../ObjectProperty/ObjectProperty';
import ObjectPropertiesGroup from '../ObjectPropertiesGroup/ObjectPropertiesGroup';
import StructuredObjectProperty from '../ObjectProperty/StructuredObjectProperty';
import {
isIncluded,
isInherited,
isSelect,
isStructured,
} from '../../Modules/ElementUtils/ElementUtils';
class ObjectProperties extends React.Component {
static propTypes = {
collapseByDefault: PropTypes.bool,
element: PropTypes.object,
keyWidth: PropTypes.number,
reportKeyWidth: PropTypes.func,
style: PropTypes.object,
};
static contextTypes = {
theme: PropTypes.object,
eventEmitter: PropTypes.object,
includedProperties: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
]),
inheritedProperties: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
]),
};
constructor(props) {
super(props);
this.state = {
keyWidth: this.props.keyWidth || null,
};
this.keyWidthsIndex = {};
};
componentDidMount = () => {
// Everytime the `alignKeys` event is emitted, we'll re-align the keys.
this.subscription = this.context.eventEmitter.addListener('alignKeys', this.alignKeys);
};
componentWillReceiveProps = (nextProps) => {
if (nextProps.keyWidth) {
this.setState({ keyWidth: nextProps.keyWidth });
}
};
componentWillUnmount = () => {
this.subscription.remove();
};
getComponent(element, { key, index }) {
if (!key) {
key = index;
}
if (isSelect(element)) {
return (
<Select
key={key}
index={index}
element={element}
parentElement={this.props.element}
reportKeyWidth={this.reportKeyWidth}
keyWidth={this.state.keyWidth}
/>
);
} else if (isStructured(element)) {
return (
<StructuredObjectProperty
key={key}
index={index}
element={element}
parentElement={this.props.element}
collapseByDefault={this.props.collapseByDefault}
reportKeyWidth={this.reportKeyWidth}
keyWidth={this.state.keyWidth}
/>
);
}
return (
<ObjectProperty
key={key}
index={index}
element={element}
parentElement={this.props.element}
collapseByDefault={this.props.collapseByDefault}
reportKeyWidth={this.reportKeyWidth}
keyWidth={this.state.keyWidth}
/>
);
}
alignKeys = () => {
this.keyWidthsIndex = {};
this.setState({
keyWidth: null,
});
window.setTimeout(() => {
this.context.eventEmitter.emit(
`${this.props.element.meta.id}:alignKey`
);
}, 5);
};
get style() {
const style = {
root: {
width: '100%',
height: 'auto',
},
separator: {
width: '100%',
height: '1px',
backgroundColor: 'rgb(232, 235, 238)',
},
};
return merge(style, this.props.style || {});
}
reportKeyWidth = (keyIdentifier, keyWidth) => {
if (this.props.reportKeyWidth) {
this.props.reportKeyWidth(keyIdentifier, keyWidth);
return;
}
this.keyWidthsIndex[keyIdentifier] = keyWidth;
const keyWidths = values(this.keyWidthsIndex);
if (keyWidths.length === this.props.element.content.length) {
this.setState({
keyWidth: max(keyWidths),
});
}
return;
};
groupElements() {
const elements = this.props.element.content;
// Element groups, each group is represented by an object with
// the following properties.
//
// * type (enum, required)
// * inherited (string) - Group of inherited properites
// * included (string) - Group of properties which were included
// * own (string) - Groupo of own properties
//
// * components (array, required)
// * element (object, required)
const groups = [];
each(elements, (element, index) => {
// Element hasn't been inherited, nor included.
if (!element.meta || !element.meta.ref) {
// The last group is a group of own properties, append the the current
// element to that group.
if (groups.length && last(groups).type === 'own') {
return last(groups).components.push(
this.getComponent(element, { index })
);
}
// Last group is a group of inherited/included properties, create
// a new group of own properties.
return groups.push({
type: 'own',
components: [
this.getComponent(element, { index }),
],
});
}
let group;
// Element has been inherited, or included; let's create a new group.
if (groups.length && last(groups).name === element.meta.ref) {
group = last(groups);
} else {
group = {
name: element.meta.ref,
components: [],
};
if (isInherited(element)) {
group.type = 'inherited';
} else if (isIncluded(element)) {
group.type = 'included';
}
groups.push(group);
}
return group.components.push(
this.getComponent(element, {
key: `${group.name}+${index}`,
index,
})
);
});
return groups;
}
render() {
if (!this.props.element) {
return false;
}
if (isEmpty(this.props.element.content)) {
return false;
}
const elementGroups = this.groupElements();
const ownPropertiesQuery = {
type: 'own',
};
const includedPropertiesQuery = {
type: 'included',
};
const inheritedPropertiesQuery = {
type: 'inherited',
};
const ownOrIncludedGroupsQuery = {
$or: [
ownPropertiesQuery,
includedPropertiesQuery,
],
};
const ownProperties = sift(ownPropertiesQuery, elementGroups);
const inheritedProperties = sift(inheritedPropertiesQuery, elementGroups);
const includedProperties = sift(includedPropertiesQuery, elementGroups);
const ownOrIncludedProperties = sift(ownOrIncludedGroupsQuery, elementGroups);
if (this.context.inheritedProperties === 'placeholder' &&
this.context.includedProperties === 'placeholder') {
return (
<div style={this.style.root}>
{
map(inheritedProperties, (group, groupIndex) => (
<ObjectPropertiesGroup type={group.type} name={group.name} key={groupIndex}>
{
group.components
}
</ObjectPropertiesGroup>
))
}
{
map(includedProperties, (group, groupIndex) => (
<ObjectPropertiesGroup type={group.type} name={group.name} key={groupIndex}>
{
group.components
}
</ObjectPropertiesGroup>
))
}
<div style={this.style.separator} />
{
map(ownProperties, (group, groupIndex) => (
<ObjectPropertiesGroup type={group.type} name={group.name} key={groupIndex}>
{
group.components
}
</ObjectPropertiesGroup>
))
}
</div>
);
}
return (
<div style={this.style.root}>
{
map(ownOrIncludedProperties, (group, groupIndex) => (
<ObjectPropertiesGroup type={group.type} name={group.name} key={groupIndex}>
{
group.components
}
</ObjectPropertiesGroup>
))
}
{
map(inheritedProperties, (group, groupIndex) => (
<ObjectPropertiesGroup type={group.type} name={group.name} key={groupIndex}>
{
group.components
}
</ObjectPropertiesGroup>
))
}
</div>
);
}
}
export default ObjectProperties;