+
{input}
{helpBlock}
@@ -150,30 +182,13 @@ function wrapInput(id, htmlId, owner, label, help, input, inline) {
}
}
-function wrapInputWithText(id, htmlId, owner, containerClass, label, text, help, input) {
- const helpBlock = help ?
{help}
: '';
- const validationMsg = id && owner.getFormValidationMessage(id);
-
- return (
-
-
-
-
-
-
-
- {helpBlock}
- {id && validationMsg &&
{validationMsg}
}
-
- );
-}
-
class StaticField extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
- className: PropTypes.string
+ className: PropTypes.string,
+ format: PropTypes.string
}
render() {
@@ -187,7 +202,7 @@ class StaticField extends Component {
className += ' ' + props.className;
}
- return wrapInput(null, htmlId, owner, props.label, props.help,
+ return wrapInput(null, htmlId, owner, props.format, '', props.label, props.help,
{props.children}
);
}
@@ -199,7 +214,8 @@ class InputField extends Component {
label: PropTypes.string.isRequired,
placeholder: PropTypes.string,
type: PropTypes.string,
- help: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+ help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ format: PropTypes.string
}
static defaultProps = {
@@ -221,7 +237,7 @@ class InputField extends Component {
type = 'password';
}
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
owner.updateFormValue(id, evt.target.value)}/>
);
}
@@ -232,7 +248,8 @@ class CheckBox extends Component {
id: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
label: PropTypes.string,
- help: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+ help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ format: PropTypes.string
}
static contextTypes = {
@@ -245,8 +262,11 @@ class CheckBox extends Component {
const id = this.props.id;
const htmlId = 'form_' + id;
- return wrapInputWithText(id, htmlId, owner, 'checkbox', props.label, props.text, props.help,
-
owner.updateFormValue(id, !owner.getFormValue(id))}/>
+ return wrapInput(id, htmlId, owner, props.format, 'checkbox', props.label, props.help,
+
);
}
}
@@ -255,7 +275,8 @@ class TextArea extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
- help: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+ help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ format: PropTypes.string
}
static contextTypes = {
@@ -268,7 +289,7 @@ class TextArea extends Component {
const id = this.props.id;
const htmlId = 'form_' + id;
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
);
}
@@ -282,7 +303,7 @@ class Dropdown extends Component {
options: PropTypes.array,
optGroups: PropTypes.array,
className: PropTypes.string,
- inline: PropTypes.bool
+ format: PropTypes.string
}
static contextTypes = {
@@ -312,11 +333,10 @@ class Dropdown extends Component {
className += ' ' + props.className;
}
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
,
- props.inline
+
);
}
}
@@ -326,7 +346,12 @@ class AlignedRow extends Component {
static propTypes = {
className: PropTypes.string,
label: PropTypes.string,
- htmlId: PropTypes.string
+ htmlId: PropTypes.string,
+ format: PropTypes.string
+ }
+
+ static contextTypes = {
+ formStateOwner: PropTypes.object.isRequired
}
static defaultProps = {
@@ -334,33 +359,28 @@ class AlignedRow extends Component {
}
render() {
- if (this.props.label) {
- return (
-
-
-
- {this.props.children}
-
-
- );
+ const props = this.props;
+ const owner = this.context.formStateOwner;
- } else {
- return (
-
-
- {this.props.children}
-
-
- );
- }
+ return wrapInput(null, props.htmlId, owner, props.format, props.className, props.label, null, this.props.children);
}
}
class ButtonRow extends Component {
+ static propTypes = {
+ className: PropTypes.string,
+ format: PropTypes.string
+ }
+
render() {
+ let className = 'mt-button-row';
+ if (this.props.className) {
+ className += ' ' + this.props.className;
+ }
+
return (
-
{this.props.children}
+
{this.props.children}
);
}
}
@@ -423,7 +443,8 @@ class TreeTableSelect extends Component {
label: PropTypes.string.isRequired,
dataUrl: PropTypes.string,
data: PropTypes.array,
- help: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+ help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ format: PropTypes.string
}
static contextTypes = {
@@ -441,7 +462,7 @@ class TreeTableSelect extends Component {
const id = this.props.id;
const htmlId = 'form_' + id;
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
);
}
@@ -471,7 +492,8 @@ class TableSelect extends Component {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
- help: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+ help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ format: PropTypes.string
}
static defaultProps = {
@@ -530,7 +552,7 @@ class TableSelect extends Component {
const t = props.t;
if (props.dropdown) {
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
@@ -544,7 +566,7 @@ class TableSelect extends Component {
);
} else {
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
this.table = node} data={props.data} dataUrl={props.dataUrl} columns={props.columns} selectMode={props.selectMode} selectionAsArray={this.props.selectionAsArray} withHeader={props.withHeader} selection={owner.getFormValue(id)} onSelectionChangedAsync={::this.onSelectionChangedAsync}/>
@@ -570,7 +592,8 @@ class ACEEditor extends Component {
label: PropTypes.string,
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
height: PropTypes.string,
- mode: PropTypes.string
+ mode: PropTypes.string,
+ format: PropTypes.string
}
static contextTypes = {
@@ -583,7 +606,7 @@ class ACEEditor extends Component {
const id = this.props.id;
const htmlId = 'form_' + id;
- return wrapInput(id, htmlId, owner, props.label, props.help,
+ return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td,
-.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active:hover>td {
+.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active:hover>td,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title:hover,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active:hover span.fancytree-title,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node span.fancytree-title:hover {
background-color: transparent;
}
-.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td span.fancytree-title {
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node span.fancytree-title {
+ cursor: default;
+}
+
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title:hover {
+ border-color: transparent;
+}
+
+.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td span.fancytree-title,
+.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td span.fancytree-expander,
+.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title {
+ outline: 0px none;
color: #333333;
}
@@ -28,6 +69,33 @@
min-width: 150px;
}
+
+
+.mt-treetable-container span.fancytree-node.fancytree-drag-source {
+ background-color: transparent !important;
+}
+
+.mt-treetable-container #fancytree-drop-marker {
+ background-image: url("../../public/fancytree/skin-bootstrap/icons.gif");
+ height: 12px;
+}
+
+.mt-treetable-container #fancytree-drop-marker.fancytree-drop-over {
+ background-position: 0px -130px;
+ width: 22px;
+}
+
+.mt-treetable-container #fancytree-drop-marker.fancytree-drop-after, .mt-treetable-container #fancytree-drop-marker.fancytree-drop-before {
+ background-position: 0px -145px;
+ width: 64px;
+}
+
+.mt-treetable-container span.fancytree-node.fancytree-drop-accept fancytree-expander {
+ visibility: hidden;
+}
+
+
+
.form-group .mt-treetable-container {
border: 1px solid #cccccc;
border-radius: 4px;
@@ -49,3 +117,4 @@
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
}
+
diff --git a/client/src/lib/tree.js b/client/src/lib/tree.js
index a26a18c4..07ae4c87 100644
--- a/client/src/lib/tree.js
+++ b/client/src/lib/tree.js
@@ -69,7 +69,10 @@ class TreeTable extends Component {
onSelectionChangedAsync: PropTypes.func,
actions: PropTypes.func,
withHeader: PropTypes.bool,
- withDescription: PropTypes.bool
+ withDescription: PropTypes.bool,
+ noTable: PropTypes.bool,
+ withDnd: PropTypes.bool,
+ withIcons: PropTypes.bool
}
componentWillReceiveProps(nextProps) {
@@ -106,17 +109,6 @@ class TreeTable extends Component {
this.loadData(this.props.dataUrl);
}
- const glyphOpts = {
- map: {
- expanderClosed: 'glyphicon glyphicon-menu-right',
- expanderLazy: 'glyphicon glyphicon-menu-right', // glyphicon-plus-sign
- expanderOpen: 'glyphicon glyphicon-menu-down', // glyphicon-collapse-down
- checkbox: 'glyphicon glyphicon-unchecked',
- checkboxSelected: 'glyphicon glyphicon-check',
- checkboxUnknown: 'glyphicon glyphicon-share',
- }
- };
-
let createNodeFn;
createNodeFn = (event, data) => {
const node = data.node;
@@ -148,22 +140,70 @@ class TreeTable extends Component {
}
};
- this.tree = jQuery(this.domTable).fancytree({
- extensions: ['glyph', 'table'],
- glyph: glyphOpts,
+ const treeOpts = {
+ extensions: ['glyph'],
+ glyph: {
+ map: {
+ expanderClosed: 'glyphicon glyphicon-menu-right',
+ expanderLazy: 'glyphicon glyphicon-menu-right', // glyphicon-plus-sign
+ expanderOpen: 'glyphicon glyphicon-menu-down', // glyphicon-collapse-down
+ checkbox: 'glyphicon glyphicon-unchecked',
+ checkboxSelected: 'glyphicon glyphicon-check',
+ checkboxUnknown: 'glyphicon glyphicon-share',
+
+ folder: 'glyphicon glyphicon-folder-close',
+ folderOpen: 'glyphicon glyphicon-folder-open',
+ doc: 'glyphicon glyphicon-file',
+ docOpen: 'glyphicon glyphicon-file'
+ }
+ },
selectMode: (this.selectMode === TreeSelectMode.MULTI ? 2 : 1),
- icon: false,
+ icon: !!this.props.withIcons,
autoScroll: true,
scrollParent: jQuery(this.domTableContainer),
source: this.sanitizeTreeData(this.state.treeData),
- table: {
- nodeColumnIdx: 0
- },
+ toggleEffect: false,
createNode: createNodeFn,
checkbox: this.selectMode === TreeSelectMode.MULTI,
activate: (this.selectMode === TreeSelectMode.SINGLE ? ::this.onActivate : null),
- select: (this.selectMode === TreeSelectMode.MULTI ? ::this.onSelect : null)
- }).fancytree("getTree");
+ select: (this.selectMode === TreeSelectMode.MULTI ? ::this.onSelect : null),
+ };
+
+ if (!this.props.noTable) {
+ treeOpts.extensions.push('table');
+ treeOpts.table = {
+ nodeColumnIdx: 0
+ };
+ }
+
+ if (this.props.withDnd) {
+ treeOpts.extensions.push('dnd');
+ treeOpts.dnd = {
+ autoExpandMS: 400,
+ focusOnClick: true,
+ preventVoidMoves: true,
+ preventRecursiveMoves: true,
+ dropMarkerOffsetX: -46, // -22
+ dropMarkerInsertOffsetX: 0,
+ dragStart: (node, data) => {
+ return node.key !== '__mt-tree-end-drop__';
+ },
+ dragEnter: (node, data) => {
+ if (node.folder) {
+ return ['before', 'over'];
+ } else {
+ return ['before'];
+ }
+ },
+ dragDrop: (node, data) => {
+ console.log(node);
+ console.log(data);
+ data.otherNode.moveTo(node, data.hitMode);
+ }
+ };
+ }
+
+ this.tree = jQuery(this.domTable).fancytree(treeOpts).fancytree("getTree");
this.updateSelection();
}
@@ -252,6 +292,10 @@ class TreeTable extends Component {
let containerClass = 'mt-treetable-container';
if (this.selectMode === TreeSelectMode.NONE) {
containerClass += ' mt-treetable-inactivable';
+ } else {
+ if (!props.noTable) {
+ containerClass += ' table-hover';
+ }
}
if (!this.withHeader) {
@@ -260,31 +304,44 @@ class TreeTable extends Component {
// FIXME: style={{ height: '100px', overflow: 'auto'}}
- const container =
- { this.domTableContainer = domElem; }} >
-
{ this.domTable = domElem; }} className="table table-hover table-striped table-condensed">
- {props.withHeader &&
-
-
- {t('Name')} |
- {withDescription && {t('Description')} | }
- {actions && | }
-
-
- }
-
-
- |
- {withDescription && | }
- {actions && | }
-
-
-
-
;
+ if (props.noTable) {
+ return (
+ { this.domTableContainer = domElem; }} >
+
{ this.domTable = domElem; }}>
+
+
+ );
+
+ } else {
+ let tableClass = 'table table-striped table-condensed';
+ if (this.selectMode !== TreeSelectMode.NONE) {
+ tableClass += ' table-hover';
+ }
+
+ return (
+ { this.domTableContainer = domElem; }} >
+
{ this.domTable = domElem; }} className={tableClass}>
+ {props.withHeader &&
+
+
+ {t('Name')} |
+ {withDescription && {t('Description')} | }
+ {actions && | }
+
+
+ }
+
+
+ |
+ {withDescription && | }
+ {actions && | }
+
+
+
+
+ );
+ }
- return (
- container
- );
}
}
diff --git a/client/src/lists/segments/CUD.js b/client/src/lists/segments/CUD.js
index 0d0cc035..fe6c5218 100644
--- a/client/src/lists/segments/CUD.js
+++ b/client/src/lists/segments/CUD.js
@@ -3,14 +3,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { translate, Trans } from 'react-i18next';
-import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../../lib/page';
+import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton, Toolbar} from '../../lib/page';
import {
- withForm, Form, FormSendMethod, InputField, ButtonRow, Button
+ withForm, Form, FormSendMethod, InputField, ButtonRow, Button, Fieldset
} from '../../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import {DeleteModalDialog} from "../../lib/delete";
import interoperableErrors from '../../../../shared/interoperable-errors';
-import {TreeTable} from "../../lib/tree";
+import {TreeSelectMode, TreeTable} from "../../lib/tree";
@translate()
@withForm
@@ -100,57 +100,131 @@ export default class CUD extends Component {
}
}
+ async onRuleSelectionChangedAsync(sel) {
+ this.setState({
+ selectedRule: sel
+ });
+ }
+
render() {
const t = this.props.t;
const isEdit = !!this.props.entity;
+ const treeEnd = {
+ key: '__mt-tree-end-drop__',
+ icon: false,
+ unselectable: true,
+ extraClasses: 'mt-tree-end-drop',
+ beforeActivate: () => false
+ };
+
+ const treeEndWide = { // This one is used after a non-folder sibling that has no children
+ key: '__mt-tree-end-drop__',
+ icon: false,
+ unselectable: true,
+ extraClasses: 'mt-tree-end-drop mt-tree-end-drop-wide',
+ beforeActivate: () => false
+ }
+
const sampleTreeData = [
{
key: 'a',
title: 'A',
expanded: true,
+ folder: true,
children: [
{
key: 'aa',
title: 'AA',
expanded: true,
+ folder: true,
children: [
{
key: 'aaa',
title: 'AAA',
- expanded: true
},
{
key: 'aab',
title: 'AAB',
- expanded: true
- }
+ },
+ {
+ key: 'aab',
+ title: 'AAB',
+ folder: true
+ },
+ treeEnd
]
},
{
key: 'ab',
title: 'AB',
expanded: true,
+ folder: true,
children: [
{
key: 'aba',
- title: 'ABA',
- expanded: true
+ title: 'ABA'
},
{
key: 'abb',
- title: 'ABB',
- expanded: true
- }
+ title: 'ABB'
+ },
+ treeEndWide
]
},
+ treeEnd
]
- }
+ },
+ {
+ key: 'b',
+ title: 'B',
+ expanded: true,
+ folder: true,
+ children: [
+ {
+ key: 'ba',
+ title: 'BA',
+ expanded: true,
+ folder: true,
+ children: [
+ {
+ key: 'baa',
+ title: 'BAA'
+ },
+ {
+ key: 'bab',
+ title: 'BAB'
+ },
+ treeEndWide
+ ]
+ },
+ {
+ key: 'bb',
+ title: 'BB',
+ expanded: true,
+ folder: true,
+ children: [
+ {
+ key: 'bba',
+ title: 'BBA'
+ },
+ {
+ key: 'bbb',
+ title: 'BBB'
+ },
+ treeEndWide
+ ]
+ },
+ treeEnd
+ ]
+ },
+ treeEnd
];
return (
+
{isEdit &&
{isEdit ? t('Edit Segment') : t('Create Segment')}
-
);
diff --git a/client/src/lists/subscriptions/List.js b/client/src/lists/subscriptions/List.js
index 488974fb..85439cc2 100644
--- a/client/src/lists/subscriptions/List.js
+++ b/client/src/lists/subscriptions/List.js
@@ -110,7 +110,7 @@ export default class List extends Component {