/** @module components/MetaBox */
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import { observer, inject } from 'mobx-react';
import { Icon, Spin } from 'react-aiot';
import { ajax, hooks, rmlOpts } from '../util';
/**
* Show a meta box for the selected folder id. It also supports
* user settings.
*
* @property {string|int} id The id of the folder or 'usersettings'
* @property {module:components/MetaBox~MetaBox~patcher} patcher
* @extends React.Component
*/
@inject('store')
@observer
class MetaBox extends React.Component {
constructor(props) {
super(props);
this.state = {
id: 0, // The current visible id
html: '' // The html
};
}
componentWillMount() {
this.handleAjax(this.props, this.state);
}
componentWillUpdate(...args) {
this.handleAjax(...args);
}
handleAjax(nextProps, nextState) {
// Loading phase
if (nextProps.id !== this.state.id) {
this.state.id = nextProps.id; // Avoid rerendering
this.state.html = '';
const url = nextProps.id === 'usersettings' ? 'usersettings' : 'folders/' + nextProps.id + '/meta';
ajax(url).then(({ html }) => {
this.setState({ html });
}, () => { // An error occured
this.setState({
html: ''
});
});
}
}
handleRef = ref => {
this.refSpan = ref;
/**
* The MetaBox ref element is ready and created.
*
* @event module:util/hooks#folder/meta
* @param {HTMLElement} ref The reference
* @param {string|id} id The folder id or 'usersettings'
* @param {module:store~Store} store The store
*/
hooks.call('folder/meta', [ ref, this.state.id, this.props.store ]);
const { patcher } = this.props;
/**
* This callback is fired when the ref is created
*
* @callback module:components/MetaBox~MetaBox~patcher
* @param {function} handleSave Fire this function if you want to serialize the data and save to server (async)
* @param {HTMLElement} ref The meta box container
*/
patcher && patcher(this.handleSave, ref);
}
handleSave = async () => {
const form = $(this.refSpan).children('form'),
serialize = form.serializeArray(),
data = {};
$.each(serialize, (key, value) => (data[value.name] = value.value));
/**
* The MetaBox is serialized and ready to send.
*
* @event module:util/hooks#folder/meta/serialize
* @param {string|id} id The folder id or 'usersettings'
* @param {module:store~Store} store The store
* @param {object} data The data prepared for the server so you can perhaps modify it
* @param {HTMLElement} form The form container
*/
hooks.call('folder/meta/serialize', [ this.state.id, this.props.store, data, form ]);
const url = this.state.id === 'usersettings' ? 'usersettings' : 'folders/' + this.state.id + '/meta';
const response = await ajax(url, {
method: 'PUT',
data
});
/**
* The MetaBox is saved successfully.
*
* @event module:util/hooks#folder/meta/saved
* @param {string|id} id The folder id or 'usersettings'
* @param {object} response The server response
* @param {object} data The data sent to the server
*/
hooks.call('folder/meta/saved', [ this.state.id, response, data ]);
}
render() {
let selected;
if (this.props.id === 'usersettings') {
selected = {
icon: <Icon type="setting" />,
title: rmlOpts.lang.userSettingsToolTipTitle
};
}else{
selected = this.props.store.getTreeItemById(this.props.id, false);
}
const { html } = this.state,
{ busy = false, errors = [] } = this.props;
if (!selected) {
return null;
}
return (<Spin spinning={ !html || busy } size="small">
<div className="rml-postbox">
<h2><Icon type="ellipsis" /> { selected.icon } { selected.title }</h2>
{ errors.length > 0 && (<ul>{ errors.map((e, i) => (<li key={i}>{e}</li>)) }</ul>) }
{ html && (<div className="inside">
<span dangerouslySetInnerHTML={{ __html: html }} style={{ display: html ? 'block' : 'none' }}
ref={ this.handleRef } />
</div>) }
</div>
</Spin>);
}
}
/**
* Wait for the input field for the cover image and create a media picker.
*
* @see https://wordpress.stackexchange.com/questions/190987/how-do-i-create-a-custom-add-media-button-modal
*/
hooks.register('wprfc/metaCoverImage', function(data) {
const inputFilename = $(this),
inputId = $(this).prev();
$('<div class="rml-drop-zone">' + rmlOpts.lang.coverImageDropHere + '</div>').insertAfter($(this)).droppable({
tolerance: 'pointer',
drop: function(event, ui) {
const { draggable } = ui,
id = draggable.data('id'),
filename = draggable.parents('.attachments-browser').data('backboneView').collection.get(id).get('filename');
inputFilename.val(filename);
inputId.val(id);
}
});
});
export default MetaBox;