Work in progress on subscriptions
This commit is contained in:
parent
d9211377dd
commit
e73c0a8b28
42 changed files with 1558 additions and 678 deletions
|
|
@ -36,7 +36,8 @@ const FormSendMethod = {
|
|||
class Form extends Component {
|
||||
static propTypes = {
|
||||
stateOwner: PropTypes.object.isRequired,
|
||||
onSubmitAsync: PropTypes.func
|
||||
onSubmitAsync: PropTypes.func,
|
||||
inline: PropTypes.bool
|
||||
}
|
||||
|
||||
static childContextTypes = {
|
||||
|
|
@ -77,7 +78,7 @@ class Form extends Component {
|
|||
}
|
||||
} else {
|
||||
return (
|
||||
<form className="form-horizontal" onSubmit={::this.onSubmit}>
|
||||
<form className={props.inline ? 'form-inline' : 'form-horizontal'} onSubmit={::this.onSubmit}>
|
||||
<fieldset disabled={owner.isFormDisabled()}>
|
||||
{props.children}
|
||||
</fieldset>
|
||||
|
|
@ -105,25 +106,53 @@ class Fieldset extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function wrapInput(id, htmlId, owner, label, help, input) {
|
||||
const helpBlock = help ? <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help'}>{help}</div> : '';
|
||||
function wrapInput(id, htmlId, owner, label, help, input, inline) {
|
||||
const className = id ? owner.addFormValidationClass('form-group', id) : 'form-group';
|
||||
|
||||
return (
|
||||
<div className={id ? owner.addFormValidationClass('form-group', id) : 'form-group'} >
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor={htmlId} className="control-label">{label}</label>
|
||||
let helpBlock = null;
|
||||
if (help) {
|
||||
helpBlock = <div className={'help-block' + (!inline ? ' col-sm-offset-2 col-sm-10' : '')}
|
||||
id={htmlId + '_help'}>{help}</div>;
|
||||
}
|
||||
|
||||
let validationBlock = null;
|
||||
if (id) {
|
||||
const validationMsg = id && owner.getFormValidationMessage(id);
|
||||
if (validationMsg) {
|
||||
validationBlock = <div className={'help-block' + (!inline ? ' col-sm-offset-2 col-sm-10' : '')}
|
||||
id={htmlId + '_help_validation'}>{validationMsg}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const labelBlock = <label htmlFor={htmlId} className="control-label">{label}</label>;
|
||||
|
||||
if (inline) {
|
||||
return (
|
||||
<div className={className} >
|
||||
{labelBlock} {input}
|
||||
{helpBlock}
|
||||
{validationBlock}
|
||||
</div>
|
||||
<div className="col-sm-10">
|
||||
{input}
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className} >
|
||||
<div className="col-sm-2">
|
||||
{labelBlock}
|
||||
</div>
|
||||
<div className="col-sm-10">
|
||||
{input}
|
||||
</div>
|
||||
{helpBlock}
|
||||
{validationBlock}
|
||||
</div>
|
||||
{helpBlock}
|
||||
{id && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{owner.getFormValidationMessage(id)}</div>}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function wrapInputInline(id, htmlId, owner, containerClass, label, text, help, input) {
|
||||
function wrapInputWithText(id, htmlId, owner, containerClass, label, text, help, input) {
|
||||
const helpBlock = help ? <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help'}>{help}</div> : '';
|
||||
const validationMsg = id && owner.getFormValidationMessage(id);
|
||||
|
||||
return (
|
||||
<div className={id ? owner.addFormValidationClass('form-group', id) : 'form-group'} >
|
||||
|
|
@ -134,7 +163,7 @@ function wrapInputInline(id, htmlId, owner, containerClass, label, text, help, i
|
|||
<label>{input} {text}</label>
|
||||
</div>
|
||||
{helpBlock}
|
||||
{id && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{owner.getFormValidationMessage(id)}</div>}
|
||||
{id && validationMsg && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{validationMsg}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -216,7 +245,7 @@ class CheckBox extends Component {
|
|||
const id = this.props.id;
|
||||
const htmlId = 'form_' + id;
|
||||
|
||||
return wrapInputInline(id, htmlId, owner, 'checkbox', props.label, props.text, props.help,
|
||||
return wrapInputWithText(id, htmlId, owner, 'checkbox', props.label, props.text, props.help,
|
||||
<input type="checkbox" checked={owner.getFormValue(id)} id={htmlId} aria-describedby={htmlId + '_help'} onChange={evt => owner.updateFormValue(id, !owner.getFormValue(id))}/>
|
||||
);
|
||||
}
|
||||
|
|
@ -251,7 +280,9 @@ class Dropdown extends Component {
|
|||
label: PropTypes.string.isRequired,
|
||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
options: PropTypes.array,
|
||||
optGroups: PropTypes.array
|
||||
optGroups: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
inline: PropTypes.bool
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -276,11 +307,16 @@ class Dropdown extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
let className = 'form-control';
|
||||
if (props.className) {
|
||||
className += ' ' + props.className;
|
||||
}
|
||||
|
||||
return wrapInput(id, htmlId, owner, props.label, props.help,
|
||||
<select id={htmlId} className="form-control" aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)}>
|
||||
<select id={htmlId} className={className} aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)}>
|
||||
{options}
|
||||
</select>
|
||||
</select>,
|
||||
props.inline
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ h3.legend {
|
|||
.mt-secondary-nav {
|
||||
margin: 0px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 8px 5px;
|
||||
padding: 5px 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class Table extends Component {
|
|||
selectionAsArray: PropTypes.bool,
|
||||
onSelectionChangedAsync: PropTypes.func,
|
||||
onSelectionDataAsync: PropTypes.func,
|
||||
actions: PropTypes.func,
|
||||
withHeader: PropTypes.bool,
|
||||
refreshInterval: PropTypes.number
|
||||
}
|
||||
|
|
@ -177,81 +176,82 @@ class Table extends Component {
|
|||
componentDidMount() {
|
||||
const columns = this.props.columns.slice();
|
||||
|
||||
if (this.props.actions) {
|
||||
const createdCellFn = (td, data) => {
|
||||
const linksContainer = jQuery('<span class="mt-action-links"/>');
|
||||
|
||||
let actions = this.props.actions(data);
|
||||
let options = {};
|
||||
|
||||
if (!Array.isArray(actions)) {
|
||||
options = actions;
|
||||
actions = actions.actions;
|
||||
}
|
||||
|
||||
for (const action of actions) {
|
||||
if (action.action) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href="">{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
elem.click((evt) => { evt.preventDefault(); action.action(this) });
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else if (action.link) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.link}>{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
elem.click((evt) => { evt.preventDefault(); this.navigateTo(action.link) });
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else if (action.href) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.href}>{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(action.label);
|
||||
const elem = jQuery(html);
|
||||
linksContainer.append(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.refreshTimeout) {
|
||||
const currentMS = Date.now();
|
||||
|
||||
if (!this.refreshTimeoutAt || this.refreshTimeoutAt > currentMS + options.refreshTimeout) {
|
||||
clearTimeout(this.refreshTimeoutId);
|
||||
|
||||
this.refreshTimeoutAt = currentMS + options.refreshTimeout;
|
||||
|
||||
this.refreshTimeoutId = setTimeout(() => {
|
||||
this.refreshTimeoutAt = 0;
|
||||
this.refresh();
|
||||
}, options.refreshTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(td).html(linksContainer);
|
||||
};
|
||||
|
||||
columns.push({
|
||||
data: null,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
type: 'html',
|
||||
createdCell: createdCellFn
|
||||
});
|
||||
}
|
||||
|
||||
// XSS protection
|
||||
// XSS protection and actions rendering
|
||||
for (const column of columns) {
|
||||
const originalRender = column.render;
|
||||
column.render = (data, ...rest) => {
|
||||
if (originalRender) {
|
||||
const markup = originalRender(data, ...rest);
|
||||
return ReactDOMServer.renderToStaticMarkup(<div>{markup}</div>);
|
||||
} else {
|
||||
return ReactDOMServer.renderToStaticMarkup(<div>{data}</div>)
|
||||
if (column.actions) {
|
||||
const createdCellFn = (td, data, rowData) => {
|
||||
const linksContainer = jQuery('<span class="mt-action-links"/>');
|
||||
|
||||
let actions = column.actions(rowData);
|
||||
let options = {};
|
||||
|
||||
if (!Array.isArray(actions)) {
|
||||
options = actions;
|
||||
actions = actions.actions;
|
||||
}
|
||||
|
||||
for (const action of actions) {
|
||||
if (action.action) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href="">{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
elem.click((evt) => { evt.preventDefault(); action.action(this) });
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else if (action.link) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.link}>{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
elem.click((evt) => { evt.preventDefault(); this.navigateTo(action.link) });
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else if (action.href) {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.href}>{action.label}</a>);
|
||||
const elem = jQuery(html);
|
||||
linksContainer.append(elem);
|
||||
|
||||
} else {
|
||||
const html = ReactDOMServer.renderToStaticMarkup(<span>{action.label}</span>);
|
||||
const elem = jQuery(html);
|
||||
linksContainer.append(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.refreshTimeout) {
|
||||
const currentMS = Date.now();
|
||||
|
||||
if (!this.refreshTimeoutAt || this.refreshTimeoutAt > currentMS + options.refreshTimeout) {
|
||||
clearTimeout(this.refreshTimeoutId);
|
||||
|
||||
this.refreshTimeoutAt = currentMS + options.refreshTimeout;
|
||||
|
||||
this.refreshTimeoutId = setTimeout(() => {
|
||||
this.refreshTimeoutAt = 0;
|
||||
this.refresh();
|
||||
}, options.refreshTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(td).html(linksContainer);
|
||||
};
|
||||
|
||||
column.type = 'html';
|
||||
column.createdCell = createdCellFn;
|
||||
|
||||
if (!('data' in column)) {
|
||||
column.data = null;
|
||||
column.orderable = false;
|
||||
column.searchable = false;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const originalRender = column.render;
|
||||
column.render = (data, ...rest) => {
|
||||
if (originalRender) {
|
||||
const markup = originalRender(data, ...rest);
|
||||
return ReactDOMServer.renderToStaticMarkup(<div>{markup}</div>);
|
||||
} else {
|
||||
return ReactDOMServer.renderToStaticMarkup(<div>{data}</div>)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
column.title = ReactDOMServer.renderToStaticMarkup(<div>{column.title}</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,11 +88,13 @@ class TreeTable extends Component {
|
|||
|
||||
// XSS protection
|
||||
sanitizeTreeData(unsafeData) {
|
||||
const data = unsafeData.slice();
|
||||
for (const entry of data) {
|
||||
const data = [];
|
||||
for (const unsafeEntry of unsafeData) {
|
||||
const entry = Object.assign({}, unsafeEntry);
|
||||
entry.title = ReactDOMServer.renderToStaticMarkup(<div>{entry.title}</div>)
|
||||
entry.description = ReactDOMServer.renderToStaticMarkup(<div>{entry.description}</div>)
|
||||
entry.children = this.sanitizeTreeData(entry.children);
|
||||
data.push(entry);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue