Theming for Segment options
This commit is contained in:
parent
baf9f61465
commit
e5cf2962dc
10 changed files with 1822 additions and 671 deletions
18
client/src/lib/bootstrap-components.js
vendored
18
client/src/lib/bootstrap-components.js
vendored
|
@ -32,6 +32,19 @@ class DismissibleAlert extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Icon extends Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
|
||||
return <span className={'glyphicon glyphicon-' + props.name + (props.className ? ' ' + props.className : '')}></span>;
|
||||
}
|
||||
}
|
||||
|
||||
@withErrorHandling
|
||||
class Button extends Component {
|
||||
static propTypes = {
|
||||
|
@ -59,7 +72,7 @@ class Button extends Component {
|
|||
|
||||
let icon;
|
||||
if (props.icon) {
|
||||
icon = <span className={'glyphicon glyphicon-' + props.icon}></span>
|
||||
icon = <Icon name={props.icon}/>
|
||||
}
|
||||
|
||||
let iconSpacer;
|
||||
|
@ -240,5 +253,6 @@ export {
|
|||
DropdownMenu,
|
||||
ActionLink,
|
||||
DismissibleAlert,
|
||||
ModalDialog
|
||||
ModalDialog,
|
||||
Icon
|
||||
};
|
|
@ -11,7 +11,6 @@ import 'datatables.net';
|
|||
import 'datatables.net-bs';
|
||||
import 'datatables.net-bs/css/dataTables.bootstrap.css';
|
||||
|
||||
import './table.css';
|
||||
import axios from './axios';
|
||||
|
||||
import { withPageHelpers } from '../lib/page'
|
||||
|
|
|
@ -11,8 +11,11 @@ import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handli
|
|||
import {DeleteModalDialog} from "../../lib/delete";
|
||||
import interoperableErrors from '../../../../shared/interoperable-errors';
|
||||
|
||||
import styles from './CUD.scss';
|
||||
import SortableTree from 'react-sortable-tree';
|
||||
import { getRuleTreeNodeRenderer } from './RuleTreeNodeRenderer';
|
||||
import {ActionLink, Icon} from "../../lib/bootstrap-components";
|
||||
|
||||
console.log(styles);
|
||||
|
||||
@translate()
|
||||
@withForm
|
||||
|
@ -23,7 +26,7 @@ export default class CUD extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.compoundRuleTypes = [ 'all', 'some', 'none' ];
|
||||
this.compoundRuleTypes = [ 'all', 'some', 'one', 'none' ];
|
||||
|
||||
const allRule = {
|
||||
type: 'all'
|
||||
|
@ -33,99 +36,44 @@ export default class CUD extends Component {
|
|||
type: 'eq'
|
||||
};
|
||||
|
||||
const sampleRules = [
|
||||
{
|
||||
type: 'all',
|
||||
rules: [
|
||||
{
|
||||
type: 'some',
|
||||
rules: [
|
||||
{
|
||||
type: 'eq',
|
||||
value: 11
|
||||
},
|
||||
{
|
||||
type: 'eq',
|
||||
value: 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'some',
|
||||
rules: [
|
||||
{
|
||||
type: 'eq',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
type: 'eq',
|
||||
value: 7
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
this.state = {
|
||||
rules: [
|
||||
{
|
||||
key: 'a',
|
||||
title: 'A',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'aa',
|
||||
title: 'AA',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'aaa',
|
||||
title:
|
||||
<div><h4>sdfsdf</h4><div>xxx</div><div>yyy<NavButton label="ZZZ" linkTo="/lists"/></div></div>,
|
||||
rule: otherRule
|
||||
},
|
||||
{
|
||||
key: 'aab',
|
||||
title: 'AAB',
|
||||
subtitle: 'sdfwere',
|
||||
rule: otherRule
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'ab',
|
||||
title: 'AB',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'aba',
|
||||
title: 'ABA',
|
||||
rule: otherRule
|
||||
},
|
||||
{
|
||||
key: 'abb',
|
||||
title: 'ABB',
|
||||
rule: otherRule
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'b',
|
||||
title: 'B',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'ba',
|
||||
title: 'BA',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'baa',
|
||||
title: 'BAA',
|
||||
rule: otherRule
|
||||
},
|
||||
{
|
||||
key: 'bab',
|
||||
title: 'BAB',
|
||||
rule: otherRule
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'bb',
|
||||
title: 'BB',
|
||||
expanded: true,
|
||||
rule: allRule,
|
||||
children: [
|
||||
{
|
||||
key: 'bba',
|
||||
title: 'BBA',
|
||||
rule: otherRule
|
||||
},
|
||||
{
|
||||
key: 'bbb',
|
||||
title: 'BBB',
|
||||
rule: otherRule
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
rules: sampleRules,
|
||||
rulesTree: this.getTreeFromRules(sampleRules)
|
||||
};
|
||||
|
||||
this.initForm();
|
||||
|
@ -138,6 +86,37 @@ export default class CUD extends Component {
|
|||
entity: PropTypes.object
|
||||
}
|
||||
|
||||
getRulesFromTree(tree) {
|
||||
const rules = [];
|
||||
for (const node of tree) {
|
||||
const rule = Object.assign({}, node.rule);
|
||||
rule.rules = this.getRulesFromTree(node.children);
|
||||
rules.push(rule);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
getTreeFromRules(rules) {
|
||||
const tree = [];
|
||||
for (const rule of rules) {
|
||||
let title, subtitle;
|
||||
|
||||
title = rule.type; // FIXME
|
||||
subtitle = null;
|
||||
|
||||
tree.push({
|
||||
rule,
|
||||
title,
|
||||
subtitle,
|
||||
expanded: true,
|
||||
children: this.getTreeFromRules(rule.rules || [])
|
||||
});
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.entity) {
|
||||
this.getFormValuesFromEntity(this.props.entity, data => {
|
||||
|
@ -205,16 +184,42 @@ export default class CUD extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
onRuleSelectionClick(data) {
|
||||
async onRuleDelete(data) {
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
async onRuleOptions(data) {
|
||||
this.setState({
|
||||
selectedRule: data.node.key
|
||||
ruleOptionsVisible: true
|
||||
});
|
||||
}
|
||||
|
||||
async onRuleTree() {
|
||||
this.setState({
|
||||
ruleOptionsVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
async onRulesChanged(rulesTree) {
|
||||
this.setState({
|
||||
rulesTree,
|
||||
rules: this.getRulesFromTree(rulesTree)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
const isEdit = !!this.props.entity;
|
||||
|
||||
let ruleOptionsVisibilityClass = '';
|
||||
if ('ruleOptionsVisible' in this.state) {
|
||||
if (this.state.ruleOptionsVisible) {
|
||||
ruleOptionsVisibilityClass = ' ' + styles.ruleOptionsVisible;
|
||||
} else {
|
||||
ruleOptionsVisibilityClass = ' ' + styles.ruleOptionsHidden;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
|
@ -231,35 +236,52 @@ export default class CUD extends Component {
|
|||
|
||||
<Title>{isEdit ? t('Edit Segment') : t('Create Segment')}</Title>
|
||||
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler} format="wide">
|
||||
<ButtonRow format="wide" className="pull-right">
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
|
||||
<ButtonRow format="wide" className={`col-xs-12 ${styles.toolbar}`}>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={t('Save')}/>
|
||||
{isEdit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/lists/fields/${this.props.list.id}/${this.props.entity.id}/delete`}/>}
|
||||
</ButtonRow>
|
||||
|
||||
<h3>{t('Segment Options')}</h3>
|
||||
|
||||
<InputField id="name" label={t('Name')} format="wide"/>
|
||||
<InputField id="name" label={t('Name')}/>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-6" >
|
||||
<div className={styles.rulePane + ruleOptionsVisibilityClass}>
|
||||
<div className={styles.leftPane}>
|
||||
<SortableTree
|
||||
treeData={this.state.rules}
|
||||
onChange={ rules => this.setState({rules}) }
|
||||
treeData={this.state.rulesTree}
|
||||
onChange={rulesTree => this.onRulesChanged(rulesTree)}
|
||||
isVirtualized={false}
|
||||
nodeContentRenderer={getRuleTreeNodeRenderer({
|
||||
onClick: ::this.onRuleSelectionClick,
|
||||
isSelected: data => data.node.key === this.state.selectedRule
|
||||
canDrop={ data => !data.nextParent || this.compoundRuleTypes.includes(data.nextParent.rule.type) }
|
||||
generateNodeProps={data => ({
|
||||
buttons: [
|
||||
<ActionLink onClickAsync={async () => await this.onRuleOptions(data)} className={styles.ruleActionLink}><Icon name="edit"/></ActionLink>,
|
||||
<ActionLink onClickAsync={async () => await this.onRuleDelete(data)} className={styles.ruleActionLink}><Icon name="remove"/></ActionLink>
|
||||
]
|
||||
})}
|
||||
canDrop={data => data.nextParent && this.compoundRuleTypes.includes(data.nextParent.rule.type)}
|
||||
/>
|
||||
|
||||
<div className={styles.leftPaneOverlay} />
|
||||
|
||||
<div className={styles.paneDivider}>
|
||||
<div className={styles.paneDividerSolidBackground}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<h3>{t('Selected Rule Options')}</h3>
|
||||
<InputField id="name" label={t('Name')} format="wide" />
|
||||
|
||||
<div className={styles.rightPane}>
|
||||
<div className={styles.rulePaneRightInner}>
|
||||
<div className={styles.ruleOptions}>
|
||||
<h3>{t('Rule Options')}</h3>
|
||||
<InputField id="name" label={t('Name')}/>
|
||||
|
||||
<ButtonRow>
|
||||
<Button className="btn-primary" icon="chevron-left" label={t('Back')} onClickAsync={::this.onRuleTree}/>
|
||||
</ButtonRow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
134
client/src/lists/segments/CUD.scss
Normal file
134
client/src/lists/segments/CUD.scss
Normal file
|
@ -0,0 +1,134 @@
|
|||
$desktopMinWidth: 768px;
|
||||
|
||||
$mobileLeftPaneResidualWidth: 0px;
|
||||
$mobileAnimationStartPosition: 100px;
|
||||
|
||||
$desktopLeftPaneResidualWidth: 200px;
|
||||
$desktopAnimationStartPosition: 300px;
|
||||
|
||||
.toolbar {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ruleActionLink {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.rulePane {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.leftPane {
|
||||
display: inline-block;
|
||||
width: 0px;
|
||||
|
||||
.leftPaneOverlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
|
||||
width: $mobileLeftPaneResidualWidth;
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
width: $desktopLeftPaneResidualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.paneDivider {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('./divider.png') repeat-y;
|
||||
|
||||
transform: translateX($mobileAnimationStartPosition);
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
transform: translateX($desktopAnimationStartPosition);
|
||||
}
|
||||
|
||||
padding-left: 50px;
|
||||
z-index: 1;
|
||||
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
||||
.paneDividerSolidBackground {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rightPane {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
|
||||
transform: translateX($mobileAnimationStartPosition);
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
transform: translateX($desktopAnimationStartPosition);
|
||||
}
|
||||
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
||||
.rulePaneRightInner {
|
||||
margin-right: $mobileLeftPaneResidualWidth;
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
margin-right: $desktopLeftPaneResidualWidth;
|
||||
}
|
||||
|
||||
.ruleOptions {
|
||||
margin-left: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ruleOptionsVisible {
|
||||
.leftPaneOverlay {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.paneDivider {
|
||||
transition: transform 300ms ease-out, opacity 100ms ease-out;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
|
||||
transform: translateX($mobileLeftPaneResidualWidth);
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
transform: translateX($desktopLeftPaneResidualWidth);
|
||||
}
|
||||
}
|
||||
|
||||
.rightPane {
|
||||
transition: transform 300ms ease-out, opacity 100ms ease-out;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
|
||||
transform: translateX($mobileLeftPaneResidualWidth);
|
||||
@media (min-width: $desktopMinWidth) {
|
||||
transform: translateX($desktopLeftPaneResidualWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ruleOptionsHidden {
|
||||
.paneDivider {
|
||||
transition: visibility 0s linear 300ms, transform 300ms ease-in, opacity 100ms ease-in 200ms;
|
||||
}
|
||||
|
||||
.rightPane {
|
||||
transition: visibility 0s linear 300ms, transform 300ms ease-in, opacity 100ms ease-in 200ms;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
"use strict";
|
||||
// Taken and adapted from https://github.com/fritz-c/react-sortable-tree/blob/cbca55b9c9800a114fa2749866fd057fc1eaeb9c/src/node-renderer-default.js
|
||||
// It adds the onClick listener to .rowContents and wraps the whole class to a function to allow parameterization by the onClickHandler
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import baseStyles from './rule-tree.scss';
|
||||
|
||||
function getIEVersion() {
|
||||
const match = navigator.userAgent.match(/(?:MSIE |Trident\/.*; rv:)(\d+)/);
|
||||
return match ? parseInt(match[1], 10) : undefined;
|
||||
}
|
||||
|
||||
function isDescendant(older, younger) {
|
||||
return (
|
||||
!!older.children &&
|
||||
typeof older.children !== 'function' &&
|
||||
older.children.some(
|
||||
child => child === younger || isDescendant(child, younger)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let styles = baseStyles;
|
||||
// Add extra classes in browsers that don't support flex
|
||||
if (getIEVersion < 10) {
|
||||
styles = {
|
||||
...baseStyles,
|
||||
row: `${styles.row} ${styles.row_NoFlex}`,
|
||||
rowContents: `${styles.rowContents} ${styles.rowContents_NoFlex}`,
|
||||
rowLabel: `${styles.rowLabel} ${styles.rowLabel_NoFlex}`,
|
||||
rowToolbar: `${styles.rowToolbar} ${styles.rowToolbar_NoFlex}`,
|
||||
};
|
||||
}
|
||||
|
||||
export function getRuleTreeNodeRenderer(options) {
|
||||
class RuleTreeNodeRenderer extends Component {
|
||||
render() {
|
||||
const {
|
||||
scaffoldBlockPxWidth,
|
||||
toggleChildrenVisibility,
|
||||
connectDragPreview,
|
||||
connectDragSource,
|
||||
isDragging,
|
||||
canDrop,
|
||||
canDrag,
|
||||
node,
|
||||
title,
|
||||
subtitle,
|
||||
draggedNode,
|
||||
path,
|
||||
treeIndex,
|
||||
isSearchMatch,
|
||||
isSearchFocus,
|
||||
buttons,
|
||||
className,
|
||||
style,
|
||||
didDrop,
|
||||
isOver, // Not needed, but preserved for other renderers
|
||||
parentNode, // Needed for dndManager
|
||||
...otherProps
|
||||
} = this.props;
|
||||
const nodeTitle = title || node.title;
|
||||
const nodeSubtitle = subtitle || node.subtitle;
|
||||
|
||||
let handle;
|
||||
if (canDrag) {
|
||||
if (typeof node.children === 'function' && node.expanded) {
|
||||
// Show a loading symbol on the handle when the children are expanded
|
||||
// and yet still defined by a function (a callback to fetch the children)
|
||||
handle = (
|
||||
<div className={styles.loadingHandle}>
|
||||
<div className={styles.loadingCircle}>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
<div className={styles.loadingCirclePoint}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// Show the handle used to initiate a drag-and-drop
|
||||
handle = connectDragSource(<div className={styles.moveHandle}/>, {
|
||||
dropEffect: 'copy',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const isDraggedDescendant = draggedNode && isDescendant(draggedNode, node);
|
||||
const isLandingPadActive = !didDrop && isDragging;
|
||||
|
||||
return (
|
||||
<div style={{height: '100%'}} {...otherProps}>
|
||||
{toggleChildrenVisibility &&
|
||||
node.children &&
|
||||
node.children.length > 0 &&
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={node.expanded ? 'Collapse' : 'Expand'}
|
||||
className={
|
||||
node.expanded ? styles.collapseButton : styles.expandButton
|
||||
}
|
||||
style={{left: -0.5 * scaffoldBlockPxWidth}}
|
||||
onClick={() =>
|
||||
toggleChildrenVisibility({
|
||||
node,
|
||||
path,
|
||||
treeIndex,
|
||||
})}
|
||||
/>
|
||||
|
||||
{node.expanded &&
|
||||
!isDragging &&
|
||||
<div
|
||||
style={{width: scaffoldBlockPxWidth}}
|
||||
className={styles.lineChildren}
|
||||
/>}
|
||||
</div>}
|
||||
|
||||
<div className={styles.rowWrapper}>
|
||||
{/* Set the row preview to be used during drag and drop */}
|
||||
{connectDragPreview(
|
||||
<div
|
||||
className={
|
||||
styles.row +
|
||||
(isLandingPadActive ? ` ${styles.rowLandingPad}` : '') +
|
||||
(isLandingPadActive && !canDrop
|
||||
? ` ${styles.rowCancelPad}`
|
||||
: '') +
|
||||
(isSearchMatch ? ` ${styles.rowSearchMatch}` : '') +
|
||||
(isSearchFocus ? ` ${styles.rowSearchFocus}` : '') +
|
||||
(className ? ` ${className}` : '')
|
||||
}
|
||||
style={{
|
||||
opacity: isDraggedDescendant ? 0.5 : 1,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{handle}
|
||||
|
||||
<div
|
||||
className={
|
||||
styles.rowContents +
|
||||
(!canDrag ? ` ${styles.rowContentsDragDisabled}` : '') +
|
||||
(options && options.isSelected && options.isSelected({path, node}) ? ` ${styles.rowContentsSelected}` : '') // This has been added compared to the original file
|
||||
}
|
||||
onClick={() => options && options.onClick && options.onClick({path, node})} // This has been added compared to the original file
|
||||
>
|
||||
<div className={styles.rowLabel}>
|
||||
<span
|
||||
className={
|
||||
styles.rowTitle +
|
||||
(node.subtitle ? ` ${styles.rowTitleWithSubtitle}` : '')
|
||||
}
|
||||
>
|
||||
{typeof nodeTitle === 'function'
|
||||
? nodeTitle({
|
||||
node,
|
||||
path,
|
||||
treeIndex,
|
||||
})
|
||||
: nodeTitle}
|
||||
</span>
|
||||
|
||||
{nodeSubtitle &&
|
||||
<span className={styles.rowSubtitle}>
|
||||
{typeof nodeSubtitle === 'function'
|
||||
? nodeSubtitle({
|
||||
node,
|
||||
path,
|
||||
treeIndex,
|
||||
})
|
||||
: nodeSubtitle}
|
||||
</span>}
|
||||
</div>
|
||||
|
||||
<div className={styles.rowToolbar}>
|
||||
{buttons.map((btn, index) =>
|
||||
<div
|
||||
key={index} // eslint-disable-line react/no-array-index-key
|
||||
className={styles.toolbarButton}
|
||||
>
|
||||
{btn}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RuleTreeNodeRenderer.defaultProps = {
|
||||
isSearchMatch: false,
|
||||
isSearchFocus: false,
|
||||
canDrag: false,
|
||||
toggleChildrenVisibility: null,
|
||||
buttons: [],
|
||||
className: '',
|
||||
style: {},
|
||||
parentNode: null,
|
||||
draggedNode: null,
|
||||
canDrop: false,
|
||||
title: null,
|
||||
subtitle: null,
|
||||
};
|
||||
|
||||
RuleTreeNodeRenderer.propTypes = {
|
||||
node: PropTypes.shape({}).isRequired,
|
||||
title: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
||||
subtitle: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
||||
path: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
).isRequired,
|
||||
treeIndex: PropTypes.number.isRequired,
|
||||
isSearchMatch: PropTypes.bool,
|
||||
isSearchFocus: PropTypes.bool,
|
||||
canDrag: PropTypes.bool,
|
||||
scaffoldBlockPxWidth: PropTypes.number.isRequired,
|
||||
toggleChildrenVisibility: PropTypes.func,
|
||||
buttons: PropTypes.arrayOf(PropTypes.node),
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.shape({}),
|
||||
|
||||
// Drag and drop API functions
|
||||
// Drag source
|
||||
connectDragPreview: PropTypes.func.isRequired,
|
||||
connectDragSource: PropTypes.func.isRequired,
|
||||
parentNode: PropTypes.shape({}), // Needed for dndManager
|
||||
isDragging: PropTypes.bool.isRequired,
|
||||
didDrop: PropTypes.bool.isRequired,
|
||||
draggedNode: PropTypes.shape({}),
|
||||
// Drop target
|
||||
isOver: PropTypes.bool.isRequired,
|
||||
canDrop: PropTypes.bool
|
||||
};
|
||||
|
||||
return RuleTreeNodeRenderer;
|
||||
}
|
||||
|
1539
client/src/lists/segments/divider.ai
Normal file
1539
client/src/lists/segments/divider.ai
Normal file
File diff suppressed because one or more lines are too long
BIN
client/src/lists/segments/divider.png
Normal file
BIN
client/src/lists/segments/divider.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 338 B |
|
@ -1,300 +0,0 @@
|
|||
// Taken from https://github.com/fritz-c/react-sortable-tree/blob/d6a9be9e4931b5f0dac7b87511d309694f42355e/src/node-renderer-default.scss
|
||||
|
||||
$row-padding: 10px;
|
||||
|
||||
.rowWrapper {
|
||||
padding: $row-padding $row-padding $row-padding 0;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.row {
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
|
||||
& > * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The outline of where the element will go if dropped, displayed while dragging
|
||||
*/
|
||||
.rowLandingPad {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
outline: none !important;
|
||||
|
||||
* {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: lightblue;
|
||||
border: 3px dashed white;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate appearance of the landing pad when the dragged location is invalid
|
||||
*/
|
||||
.rowCancelPad {
|
||||
@extend .rowLandingPad;
|
||||
|
||||
&::before {
|
||||
background-color: #e6a8ad;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nodes matching the search conditions are highlighted
|
||||
*/
|
||||
.rowSearchMatch {
|
||||
outline: solid 3px #0080ff;
|
||||
}
|
||||
|
||||
/**
|
||||
* The node that matches the search conditions and is currently focused
|
||||
*/
|
||||
.rowSearchFocus {
|
||||
outline: solid 3px #fc6421;
|
||||
}
|
||||
|
||||
%rowItem {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.rowContents {
|
||||
@extend %rowItem;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
border: solid #bbb 1px;
|
||||
border-left: none;
|
||||
box-shadow: 0 2px 2px -2px;
|
||||
padding: 0 5px 0 10px;
|
||||
border-radius: 2px;
|
||||
min-width: 230px;
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.rowContentsDragDisabled {
|
||||
border-left: solid #bbb 1px;
|
||||
}
|
||||
|
||||
.rowLabel {
|
||||
@extend %rowItem;
|
||||
flex: 0 1 auto;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.rowToolbar {
|
||||
@extend %rowItem;
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.moveHandle {
|
||||
@extend %rowItem;
|
||||
|
||||
height: 100%;
|
||||
width: 44px;
|
||||
background: #d9d9d9
|
||||
url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MiIgaGVpZ2h0PSI0MiI+PGcgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIuOSIgPjxwYXRoIGQ9Ik0xNCAxNS43aDE0LjQiLz48cGF0aCBkPSJNMTQgMjEuNGgxNC40Ii8+PHBhdGggZD0iTTE0IDI3LjFoMTQuNCIvPjwvZz4KPC9zdmc+')
|
||||
no-repeat center;
|
||||
border: solid #aaa 1px;
|
||||
box-shadow: 0 2px 2px -2px;
|
||||
cursor: move;
|
||||
border-radius: 1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.loadingHandle {
|
||||
@extend .moveHandle;
|
||||
|
||||
cursor: default;
|
||||
background: #d9d9d9;
|
||||
}
|
||||
|
||||
@keyframes pointFade {
|
||||
0%,
|
||||
19.999%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.loadingCircle {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
margin: 10%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loadingCirclePoint {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
$point-count: 12;
|
||||
$spin-animation-time: 800ms;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 11%;
|
||||
height: 30%;
|
||||
background-color: #fff;
|
||||
border-radius: 30%;
|
||||
animation: pointFade $spin-animation-time infinite ease-in-out both;
|
||||
}
|
||||
|
||||
@for $i from 1 through (($point-count + 1) / 2) {
|
||||
&:nth-of-type(#{$i}) {
|
||||
transform: rotate(360deg / $point-count * ($i - 1));
|
||||
}
|
||||
|
||||
&:nth-of-type(#{$i + $point-count / 2}) {
|
||||
transform: rotate(180deg + 360deg / $point-count * ($i - 1));
|
||||
}
|
||||
|
||||
&:nth-of-type(#{$i}),
|
||||
&:nth-of-type(#{$i + $point-count / 2}) {
|
||||
&:before {
|
||||
animation-delay: - $spin-animation-time + ($spin-animation-time /
|
||||
$point-count * 2 * ($i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbarButton {
|
||||
@extend %rowItem;
|
||||
}
|
||||
|
||||
.rowTitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rowTitleWithSubtitle {
|
||||
font-size: 85%;
|
||||
display: block;
|
||||
height: 0.8rem;
|
||||
}
|
||||
|
||||
.rowSubtitle {
|
||||
font-size: 70%;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.collapseButton,
|
||||
.expandButton {
|
||||
appearance: none;
|
||||
border: none;
|
||||
position: absolute;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0 0 0 1px #000;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px #000, 0 0 1px 3px #83bef9;
|
||||
}
|
||||
|
||||
&:hover:not(:active) {
|
||||
background-size: 24px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.collapseButton {
|
||||
background: #fff
|
||||
url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48L2c+Cjwvc3ZnPg==')
|
||||
no-repeat center;
|
||||
}
|
||||
|
||||
.expandButton {
|
||||
background: #fff
|
||||
url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48cGF0aCBkPSJNOSA0LjV2OSIvPjwvZz4KPC9zdmc+')
|
||||
no-repeat center;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes for IE9 and below
|
||||
*/
|
||||
%fixVertAlign {
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.row_NoFlex {
|
||||
@extend %fixVertAlign;
|
||||
}
|
||||
|
||||
.rowContents_NoFlex {
|
||||
@extend %fixVertAlign;
|
||||
|
||||
display: inline-block;
|
||||
&::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.rowLabel_NoFlex {
|
||||
@extend %rowItem;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.rowToolbar_NoFlex {
|
||||
@extend %rowItem;
|
||||
text-align: right;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line for under a node with children
|
||||
*/
|
||||
.lineChildren {
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
width: 1px;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
height: $row-padding;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
@import './node-renderer-default';
|
||||
|
||||
.rowContents.rowContentsSelected {
|
||||
background-color: #d3d3ff;
|
||||
}
|
|
@ -109,7 +109,7 @@ export default class List extends Component {
|
|||
}
|
||||
|
||||
<div className="well well-sm">
|
||||
<Form inline stateOwner={this}>
|
||||
<Form format="inline" stateOwner={this}>
|
||||
<Dropdown format="inline" className="input-sm" id="segment" label={t('Segment')} options={this.state.segmentOptions}/>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue