123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- import React from 'react'
- import classnames from 'classnames'
- import { ViewModelVisualTypes } from 'containers/View/constants'
- import DropboxItem from './DropboxItem'
- import DropboxContent from './DropboxContent'
- import ColorPanel from '../ColorPanel'
- import SizePanel from '../SizePanel'
- import { IChartInfo, WidgetMode } from '../../Widget'
- import { IFieldConfig } from '../../Config/Field'
- import { IFieldFormatConfig } from '../../Config/Format'
- import { IFieldSortConfig, FieldSortTypes } from '../../Config/Sort'
- import { decodeMetricName } from '../../util'
- import { Popover, Icon } from 'antd'
- import { IFilter } from 'app/components/Control/types'
- const styles = require('../Workbench.less')
- export type DragType = 'category' | 'value'
- export type DropboxType = DragType | 'all'
- export type DropboxItemType = DragType | 'add'
- export type DropType = 'outside' | 'inside' | 'unmoved'
- export type AggregatorType = 'sum' | 'avg' | 'count' | 'COUNTDISTINCT' | 'max' | 'min' | 'median' | 'var' | 'dev'
- interface IDataColumn {
- name: string
- from?: string
- sort?: IFieldSortConfig
- agg?: AggregatorType
- field?: IFieldConfig
- format?: IFieldFormatConfig
- }
- export interface IDataParamSource extends IDataColumn {
- type: DragType
- visualType: ViewModelVisualTypes
- title?: string
- chart?: IChartInfo
- config?: IDataParamConfig
- }
- export interface IDragItem extends IDataParamSource {
- checked?: boolean
- }
- export interface IDataParamConfig {
- actOn?: string
- values?: {
- [key: string]: string
- },
- sql?: string
- sqlModel?: IFilter[]
- filterSource?: any
- field?: {
- alias: string,
- desc: string
- }
- }
- export interface IDataParamSourceInBox extends IDataColumn {
- type: DropboxItemType
- visualType?: ViewModelVisualTypes
- chart?: IChartInfo
- config?: IDataParamConfig
- }
- interface IDropboxProps {
- name: string
- title: string
- type: DropboxType
- value: object
- items: IDataParamSource[]
- mode: WidgetMode
- selectedChartId: number
- dragged: IDataParamSource
- panelList: IDataParamSource[]
- dimetionsCount: number
- metricsCount: number
- onValueChange: (key: string, value: string) => void
- onItemDragStart: (item: IDataParamSource, e: React.DragEvent<HTMLLIElement | HTMLParagraphElement>) => void
- onItemDragEnd: (dropType: DropType) => void
- onItemRemove: (name: string) => (e) => void
- onItemSort: (item: IDataParamSource, sort: FieldSortTypes) => void
- onItemChangeAgg: (item: IDataParamSource, agg: AggregatorType) => void
- onItemChangeColorConfig: (item: IDataParamSource) => void
- onItemChangeFilterConfig: (item: IDataParamSource) => void
- onItemChangeFieldConfig: (item: IDataParamSource) => void
- onItemChangeFormatConfig: (item: IDataParamSource) => void
- onItemChangeChart: (item: IDataParamSource) => (chart: IChartInfo) => void
- beforeDrop: (name: string, cachedItem: IDataParamSource, resolve: (next: boolean) => void) => void
- onDrop: (name: string, dropIndex: number, dropType: DropType, changedItems: IDataParamSource[], config?: IDataParamConfig) => void
- }
- interface IDropboxStates {
- entering: boolean
- items: IDataParamSourceInBox[]
- dropIndex: number
- dropType: DropType
- }
- export class Dropbox extends React.PureComponent<IDropboxProps, IDropboxStates> {
- constructor (props) {
- super(props)
- this.state = {
- entering: false,
- items: [],
- dropIndex: -1,
- dropType: void 0
- }
- }
- private container: HTMLDivElement = null
- private width: number = 0
- private x: number = 0
- private y: number = 0
- private PADDING = 5
- private BOX_MIN_HEIGHT = 54
- private ITEM_HEIGHT = 28
- public componentWillMount () {
- this.getItems(this.props)
- }
- public componentWillReceiveProps (nextProps) {
- if (nextProps.items !== this.props.items) {
- this.getItems(nextProps)
- }
- }
- private getItems = (props) => {
- this.setState({
- items: [...props.items]
- })
- }
- private getBoxRect = () => {
- const rect = this.container.getBoundingClientRect() as DOMRect
- this.width = rect.width
- this.x = rect.x
- this.y = rect.y
- }
- private dragEnter = () => {
- this.getBoxRect()
- this.setState({
- dropIndex: 0,
- entering: true
- })
- }
- private dragOver = (e) => {
- e.preventDefault()
- const { items, dragged } = this.props
- if (!(dragged.type === 'category'
- && !dragged.from
- && items.find((i) => i.name === dragged.name))) {
- // if (this.props.size === 'large') {
- const { clientX, clientY } = e
- const physicalDropIndex = this.calcPhysicalDropIndex(clientX, clientY)
- this.previewDropPosition(physicalDropIndex)
- // } else {
- // if (this.state.dropIndex === -1) {
- // this.setState({
- // dropIndex: 0
- // })
- // }
- // }
- }
- }
- private dragLeave = () => {
- this.setState({
- items: this.state.items.filter((i) => i.type !== 'add'),
- entering: false,
- dropIndex: -1,
- dropType: void 0
- })
- }
- private drop = () => {
- const { name, items, dragged, beforeDrop, onDrop } = this.props
- const { items: itemsState, dropIndex, dropType } = this.state
- if (dropIndex >= 0) {
- const alreadyHaveIndex = items.findIndex((i) => i.name === dragged.name)
- if (!(dragged.type === 'category' && alreadyHaveIndex >= 0 && dragged.from !== name)) {
- beforeDrop(name, dragged, (data: boolean | IDataParamConfig) => {
- if (data) {
- onDrop(name, dropIndex, dropType, itemsState as IDataParamSource[], data as IDataParamConfig)
- } else {
- this.dragLeave()
- }
- })
- }
- }
- this.setState({
- entering: false,
- dropIndex: -1,
- dropType: dropType === 'outside'
- ? void 0
- : dropType === void 0
- ? 'unmoved'
- : dropType
- })
- }
- private itemDragEnd = () => {
- this.props.onItemDragEnd(this.state.dropType)
- this.setState({
- dropType: void 0
- })
- }
- private calcPhysicalDropIndex = (dragX, dragY): number => {
- const relX = dragX - this.x
- const relY = dragY - this.y
- const limitX = this.width - this.PADDING
- const limitY = Math.max(this.BOX_MIN_HEIGHT - this.PADDING, this.state.items.length * this.ITEM_HEIGHT + this.PADDING)
- if (relX > this.PADDING && relY > this.PADDING && relX < limitX && relY < limitY) {
- // const row = Math.floor((relX - this.PADDING) / this.ITEM_WIDTH)
- // const col = Math.floor((relY - this.PADDING) / this.ITEM_HEIGHT)
- // return col * 2 + row
- return Math.floor((relY - this.PADDING) / this.ITEM_HEIGHT)
- }
- }
- private previewDropPosition = (physicalDropIndex) => {
- const { items, dragged } = this.props
- const { items: itemsState } = this.state
- const draggedItemIndex = items.findIndex((i) => i.name === dragged.name)
- const draggedItemLocalIndex = itemsState.findIndex((is) => is.name === dragged.name)
- let itemLength = items.length
- if (draggedItemIndex >= 0) {
- itemLength -= 1
- }
- const realDropIndex = physicalDropIndex !== void 0
- ? Math.min(itemLength, physicalDropIndex)
- : itemLength
- if (draggedItemIndex < 0) {
- if (draggedItemLocalIndex < 0 || draggedItemLocalIndex !== realDropIndex) {
- this.setState({
- items: [
- ...items.slice(0, realDropIndex),
- {
- name: dragged.type === 'category' ? dragged.name : decodeMetricName(dragged.name),
- type: 'add'
- },
- ...items.slice(realDropIndex)
- ],
- dropIndex: realDropIndex,
- dropType: 'outside'
- })
- }
- } else {
- if (draggedItemLocalIndex !== realDropIndex) {
- const temp = itemsState.filter((i, index) => index !== draggedItemLocalIndex)
- temp.splice(realDropIndex, 0, dragged)
- this.setState({
- items: temp,
- dropIndex: realDropIndex,
- dropType: 'inside'
- })
- }
- }
- }
- public render () {
- const {
- name,
- title,
- type,
- value,
- panelList,
- mode,
- selectedChartId,
- dragged,
- dimetionsCount,
- metricsCount,
- onValueChange,
- onItemDragStart,
- onItemSort,
- onItemChangeAgg,
- onItemChangeColorConfig,
- onItemChangeFilterConfig,
- onItemChangeFieldConfig,
- onItemChangeFormatConfig,
- onItemChangeChart,
- onItemRemove
- } = this.props
- const { entering, items } = this.state
- let shouldResponse = false
- let shouleEnter = false
- let dragType = ''
- if (dragged) {
- dragType = dragged.type
- if (type === 'all' || type === dragType) {
- shouldResponse = true
- shouleEnter = entering
- }
- }
- const containerClass = classnames({
- [styles.dropContainer]: true,
- [styles.dragOver]: shouldResponse
- })
- const maskClass = classnames({
- [styles.mask]: true,
- [styles.onTop]: shouldResponse,
- [styles.enter]: shouleEnter,
- [styles.category]: dragType === 'category',
- [styles.value]: dragType === 'value'
- })
- let setting
- if (['color', 'size'].includes(name)) {
- let panel
- switch (name) {
- case 'color':
- panel = (
- <ColorPanel
- list={panelList}
- value={value}
- showAll={mode === 'pivot'}
- onValueChange={onValueChange}
- />
- )
- break
- case 'size':
- panel = (
- <SizePanel
- list={panelList}
- value={value}
- hasTabs={mode === 'pivot'}
- onValueChange={onValueChange}
- />
- )
- break
- }
- setting = (
- <Popover
- content={panel}
- trigger="click"
- placement="right"
- >
- <span className={styles.setting}>
- <Icon type="setting" /> 设置
- </span>
- </Popover>
- )
- }
- const itemContent = items.length
- ? items.map((item) => (
- <DropboxItem
- key={item.name}
- container={name}
- item={item}
- dimetionsCount={dimetionsCount}
- metricsCount={metricsCount}
- onDragStart={onItemDragStart}
- onDragEnd={this.itemDragEnd}
- onSort={onItemSort}
- onChangeAgg={onItemChangeAgg}
- onChangeFieldConfig={onItemChangeFieldConfig}
- onChangeFormatConfig={onItemChangeFormatConfig}
- onChangeColorConfig={onItemChangeColorConfig}
- onChangeFilterConfig={onItemChangeFilterConfig}
- onChangeChart={onItemChangeChart}
- onRemove={onItemRemove(item.name)}
- />
- ))
- : (
- <DropboxContent
- title={title}
- type={type}
- />
- )
- return (
- <div className={styles.dropbox}>
- <p className={styles.title}>
- {title}
- {setting}
- </p>
- <div
- className={containerClass}
- ref={(f) => this.container = f}
- >
- {itemContent}
- <div
- className={maskClass}
- onDragEnter={this.dragEnter}
- onDragOver={this.dragOver}
- onDragLeave={this.dragLeave}
- onDrop={this.drop}
- />
- </div>
- </div>
- )
- }
- }
- export default Dropbox
|