Skip to content

Commit

Permalink
Merge branch 'master' into feat/swipe
Browse files Browse the repository at this point in the history
  • Loading branch information
lvisei committed Dec 26, 2023
2 parents df7b4bd + 5739aef commit 112e983
Show file tree
Hide file tree
Showing 50 changed files with 2,408 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { FilterDateConfigType } from '@antv/li-p2';
import { FilterDateConfig } from '@antv/li-p2';
import React from 'react';

export interface DateItemProps {
defaultValue: FilterDateConfigType;
onChange: (value: FilterDateConfigType) => void;
}

const DateItem: React.FC<DateItemProps> = (props) => {
const { defaultValue, onChange } = props;

const onValueChange = (val: any) => {
const { type, granularity, value } = val;
onChange({
...defaultValue,
value,
granularity,
params: {
...defaultValue.params,
dateType: type,
},
});
};

return (
<FilterDateConfig
bordered={false}
value={defaultValue.value}
format={defaultValue.params.format}
granularity={defaultValue.granularity}
isRenderExtraFooter={true}
type={defaultValue.params.dateType}
onChange={onValueChange}
/>
);
};

export default DateItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { DownOutlined } from '@ant-design/icons';
import type { FilterNumberConfigType } from '@antv/li-p2';
import { FilterNumberConfig } from '@antv/li-p2';
import { Button, Popover } from 'antd';
import React, { useState } from 'react';
import useStyle from './style';

export interface NumberItemProps {
defaluValue: FilterNumberConfigType;
onChange: (value: FilterNumberConfigType) => void;
}

const NumberItem: React.FC<NumberItemProps> = (props) => {
const { defaluValue, onChange } = props;
const styles = useStyle();
const [open, setOpen] = useState(false);

const [valAndOperator, setValAndOperator] = useState<Pick<FilterNumberConfigType, 'operator' | 'value'>>({
value: defaluValue.value,
operator: defaluValue.operator,
});

const onValueChange = (val: number | [number, number] | undefined, operator: '>=' | '<=' | 'BETWEEN') => {
setValAndOperator({ value: val, operator });
};

const handleOpenChange = (open: boolean) => {
setOpen(open);
};

const onSubmit = () => {
const numberNode = { ...defaluValue, ...valAndOperator } as FilterNumberConfigType;
onChange(numberNode);
setOpen(false);
};

const content = (
<div className={styles.numberContent}>
<FilterNumberConfig value={defaluValue.value} operator={defaluValue.operator} onChange={onValueChange} />
<div className={styles.numberSubmit}>
<Button type="primary" size="small" onClick={onSubmit}>
确定
</Button>
</div>
</div>
);

const title = !valAndOperator.value
? '不限'
: valAndOperator.operator === 'BETWEEN' && Array.isArray(valAndOperator.value)
? `${valAndOperator.value[0]} ~ ${valAndOperator.value[1]}`
: `${valAndOperator.operator} ${valAndOperator.value}`;

return (
<Popover content={content} trigger="click" open={open} onOpenChange={handleOpenChange}>
<div className={styles.numberItem}>
<div>{title}</div>
<DownOutlined />
</div>
</Popover>
);
};

export default NumberItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { FilterStringConfigType } from '@antv/li-p2';
import { FilterStringConfig } from '@antv/li-p2';
import { uniq } from 'lodash-es';
import React, { useMemo } from 'react';

export interface StringItemProps {
defaluValue: FilterStringConfigType;
field: string;
data: Record<string, any>[];
onChange: (value: FilterStringConfigType) => void;
}

const StringItem: React.FC<StringItemProps> = (props) => {
const { defaluValue, field, data, onChange } = props;

const domain = useMemo(() => {
const fieldData = data.map((item) => (typeof item[field] === 'object' ? JSON.stringify(item[field]) : item[field]));
const _domain: string[] = uniq(fieldData).slice(0, 3000);

return _domain;
}, [data, field]);

const onValueChange = (val?: string[]) => {
onChange({
...defaluValue,
value: val,
});
};

return (
<FilterStringConfig
bordered={false}
value={defaluValue.value}
domain={domain}
onChange={onValueChange}
filterType={defaluValue.params.filterType}
maxTagCount={2}
/>
);
};

export default StringItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { FilterConfigType } from 'packages/li-p2';
import type { FilterNode } from 'packages/li-sdk';

export const getFilterNode = (filterConfig: FilterConfigType) => {
let filterNode: FilterNode;

switch (filterConfig.type) {
case 'string':
// 空值的情况,设置无效值; 全选情况,设置无效值;
const value = filterConfig.value === undefined || filterConfig.value.includes('all') ? [] : filterConfig.value;
filterNode = {
id: filterConfig.id,
field: filterConfig.field,
type: filterConfig.type,
operator: filterConfig.operator,
value: value,
};
break;
case 'number':
filterNode = {
id: filterConfig.id,
field: filterConfig.field,
type: filterConfig.type,
operator: filterConfig.operator,
// 空值的情况,设置无效值
value: filterConfig.value || ([] as any),
};
break;
case 'date':
filterNode = {
id: filterConfig.id,
field: filterConfig.field,
type: filterConfig.type,
operator: filterConfig.operator,
// 空值的情况,设置无效值
value: filterConfig.value || (([] as unknown) as [string, string]),
granularity: filterConfig.granularity,
};
break;
}

return filterNode;
};

export const getFilterNodes = (list: FilterConfigType[]) => {
const filterNodes = list.map((item) => getFilterNode(item));

return filterNodes;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { CustomControl } from '@antv/larkmap';
import type { FilterConfigType } from '@antv/li-p2';
import type { ImplementWidgetProps, LocalOrRemoteDataset } from '@antv/li-sdk';
import { useDataset, useDatasetFilter } from '@antv/li-sdk';
import { useMount, useUpdateEffect } from 'ahooks';
import classNames from 'classnames';
import React, { useMemo, useState } from 'react';
import type { Properties } from '../registerForm';
import DateItem from './DateItem';
import { getFilterNode, getFilterNodes } from './helper';
import NumberItem from './NumberItem';
import StringItem from './StringItem';
import useStyle from './style';

const CLS_PREFIX = 'li-filter-control';
export interface LIFilterControlProps extends Properties, ImplementWidgetProps {}

const LIFilterControl: React.FC<LIFilterControlProps> = (props) => {
const { defaultFilters, datasetId = '', position } = props;
const styles = useStyle();
const [filterList, setFilterList] = useState(defaultFilters);
const [filter, { addFilterNode, updateFilterNode }] = useDatasetFilter(datasetId);
// 排除自生产筛选条件
const oimtSelfFilter = useMemo(() => {
const filterIds = defaultFilters.map((item) => item.id);
const children = filter?.children.filter((item) => !filterIds.includes(item.id)) || [];
return { relation: 'AND' as const, children };
}, []);
const [dataset] = useDataset<LocalOrRemoteDataset>(datasetId, {
filter: oimtSelfFilter,
});
const { data: tableData = [] } = dataset || {};

// 首次挂载
// TODO: 添加 FilterNode 需要在「初始化」消费数据过滤条件前执行,避免「初始化」多次消费数据过滤条件
useMount(() => {
const filterNodes = getFilterNodes(defaultFilters);
filterNodes.forEach((item) => {
addFilterNode(item);
});
});

// TODO: 支持同步更新(在事件订阅之前)
// const firstMountRef = useRef(false);
// if (!firstMountRef.current) {
// const filterNodes = getFilterNodes(defaultFilters);
// filterNodes.forEach((item) => {
// addFilterNode(item);
// });
// firstMountRef.current = true;
// }

// 配置初始筛选条件变更,配置态运行
useUpdateEffect(() => {
const filterIds = filterList.map((item) => item.id);
const filterNodes = getFilterNodes(defaultFilters);

filterNodes.forEach((item) => {
// 筛选条件已经存在进行更新 updateFilterNode,不存在添加 addFilterNode
if (filterIds.includes(item.id)) {
updateFilterNode(item.id, item);
} else {
addFilterNode(item);
}
});
setFilterList(defaultFilters);
}, [defaultFilters]);

const onValueChange = (val: FilterConfigType) => {
const updateNode = getFilterNode(val);
updateFilterNode(val.id, updateNode);
};

if (!defaultFilters.length) {
return null;
}

return (
<CustomControl position={position}>
<div className={classNames(CLS_PREFIX, styles.filterControl)}>
{filterList.map((item) => {
return (
<div key={item.id} className={classNames(`${CLS_PREFIX}__filter-item`, styles.filterItem)}>
<div className={classNames(`${CLS_PREFIX}__filter-item__title`, styles.filterItemTitle)}>
{item.title}:
</div>
<div className={classNames(`${CLS_PREFIX}__filter-item__content`, styles.filterItemContent)}>
{item.type === 'string' && (
<StringItem defaluValue={item} field={item.field} data={tableData} onChange={onValueChange} />
)}
{item.type === 'number' && <NumberItem defaluValue={item} onChange={onValueChange} />}
{item.type === 'date' && <DateItem defaultValue={item} onChange={onValueChange} />}
</div>
</div>
);
})}
</div>
</CustomControl>
);
};

export default LIFilterControl;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { css } from '@emotion/css';
import { theme } from 'antd';

const useStyle = () => {
const { useToken } = theme;
const { token } = useToken();

const { colorTextDescription, colorBgContainer, borderRadius } = token;

return {
filterControl: css`
display: flex;
flex-wrap: wrap;
color: ${colorTextDescription};
align-items: center;
background: ${colorBgContainer};
border-radius: ${borderRadius}px;
`,

filterItem: css`
display: flex;
align-items: center;
`,

filterItemTitle: css`
margin: 0 10px;
font-size: 14px;
`,

filterItemContent: css`
min-width: 150px;
max-width: 300px;
font-size: 14px;
.ant-select-selector {
color: ${colorTextDescription};
}
.ant-picker-input > input {
color: ${colorTextDescription};
}
`,

numberItem: css`
display: flex;
justify-content: space-between;
padding: 10px;
height: 32px;
line-height: 32px;
padding: 0 5px;
cursor: pointer;
`,

numberContent: css`
padding: 5px;
`,

numberSubmit: css`
text-align: center;
margin-top: 20px;
`,
};
};

export default useStyle;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## FilterControl
19 changes: 19 additions & 0 deletions packages/li-analysis-assets/src/widgets/FilterControl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { implementWidget } from '@antv/li-sdk';
import component from './Component';
import registerForm from './registerForm';

export default implementWidget({
version: 'v0.1',
metadata: {
name: 'FilterControl',
displayName: '数据筛选控件',
description: '在地图上的实时筛选数据,支持默认值设置',
type: 'Auto',
category: 'DataAnalysis',
},
defaultProperties: {
defaultFilters: [],
},
component,
registerForm,
});
Loading

0 comments on commit 112e983

Please sign in to comment.