Skip to content

Commit

Permalink
feat: add setCellCheckboxState & setCellRadioState api #1504
Browse files Browse the repository at this point in the history
  • Loading branch information
Rui-Sun committed Apr 17, 2024
1 parent 86226c7 commit 9e907bf
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"packageName": "@visactor/vtable",
"comment": "feat: add radio column type",
"type": "none"
"type": "minor"
}
],
"packageName": "@visactor/vtable"
Expand Down
10 changes: 10 additions & 0 deletions common/changes/@visactor/vtable/feat-radio_2024-04-17-10-20.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vtable",
"comment": "feat: add setCellCheckboxState & setCellRadioState api #1504",
"type": "none"
}
],
"packageName": "@visactor/vtable"
}
24 changes: 24 additions & 0 deletions docs/assets/api/en/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -1028,3 +1028,27 @@ registerCustomCellStyleArrangement: (cellPosition: { col?: number; row?: number;
- Single cell: `{ row: number, column: number }`
- Cell range: `{ range: { start: { row: number, column: number }, end: { row: number, column: number} } }`
- customStyleId: Custom style id, the same as the id defined when registering the custom style

## setCellCheckboxState(Function)

Set the checkbox state of a cell

```
setCellCheckboxState(col: number, row: number, checked: boolean) => void
```

- col: column number
- row: row number
- checked: whether checked

## setCellRadioState(Function)

Set the cell's radio state to selected

```
setCellRadioState(col: number, row: number, index?: number) => void
```

- col: column number
- row: row number
- index: the index of the updated target radio in the cell
24 changes: 24 additions & 0 deletions docs/assets/api/zh/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -1027,3 +1027,27 @@ registerCustomCellStyleArrangement: (cellPosition: { col?: number; row?: number;
- 单个单元格:`{ row: number, column: number }`
- 单元格区域:`{ range: { start: { row: number, column: number }, end: { row: number, column: number} } }`
- customStyleId: 自定义样式 id,与注册自定义样式时定义的 id 相同

## setCellCheckboxState(Function)

设置单元格的 checkbox 状态

```
setCellCheckboxState(col: number, row: number, checked: boolean) => void
```

- col: 列号
- row: 行号
- checked: 是否选中

## setCellRadioState(Function)

将单元格的 radio 状态设置为选中状态

```
setCellRadioState(col: number, row: number, index?: number) => void
```

- col: 列号
- row: 行号
- index: 更新的目标radio在单元格中的索引
11 changes: 10 additions & 1 deletion packages/vtable/src/ListTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ import { computeRowHeight } from './scenegraph/layout/compute-row-height';
import { defaultOrderFn } from './tools/util';
import type { IEditor } from '@visactor/vtable-editors';
import type { ColumnData, ColumnDefine } from './ts-types/list-table/layout-map/api';
import { getCellRadioState } from './state/radio/radio';
import { getCellRadioState, setCellRadioState } from './state/radio/radio';
import { cloneDeepSpec } from '@visactor/vutils-extension';
import { setCellCheckboxState } from './state/checkbox/checkbox';

export class ListTable extends BaseTable implements ListTableAPI {
declare internalProps: ListTableProtected;
Expand Down Expand Up @@ -934,6 +935,14 @@ export class ListTable extends BaseTable implements ListTableAPI {
getCellRadioState(col: number, row: number): boolean | number {
return getCellRadioState(col, row, this);
}

setCellCheckboxState(col: number, row: number, checked: boolean) {
setCellCheckboxState(col, row, checked, this);
}

setCellRadioState(col: number, row: number, index?: number) {
setCellRadioState(col, row, index, this);
}
/**
* 设置表格数据 及排序状态
* @param records
Expand Down
227 changes: 227 additions & 0 deletions packages/vtable/src/state/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { isObject, isValid } from '@visactor/vutils';
import type { StateManager } from '../state';
import type { CheckboxColumnDefine } from '../../ts-types';
import { getOrApply } from '../../tools/helper';
import type { BaseTableAPI } from '../../ts-types/base-table';
import type { CheckBox } from '@visactor/vrender-components';

export function setCheckedState(
col: number,
row: number,
field: string | number,
checked: boolean,
state: StateManager
) {
const recordIndex = state.table.getRecordShowIndexByCell(col, row);
if (recordIndex >= 0) {
const dataIndex = state.table.dataSource.getIndexKey(recordIndex) as number;
if (state.checkedState[dataIndex]) {
state.checkedState[dataIndex][field] = checked;
} else {
state.checkedState[dataIndex] = {};
state.checkedState[dataIndex][field] = checked;
}
}
}

export function setHeaderCheckedState(field: string | number, checked: boolean, state: StateManager) {
state.headerCheckedState[field] = checked;
state.checkedState?.forEach(recordCheckState => {
recordCheckState[field] = checked;
});
}

//#region CheckedState 状态维护

/**
* 创建cell节点时同步状态 如果状态缓存有则用 如果没有则设置缓存
* @param col
* @param row
* @param field
* @param checked
* @returns
*/
export function syncCheckedState(
col: number,
row: number,
field: string | number,
checked: boolean,
state: StateManager
): boolean | 'indeterminate' {
if (state.table.isHeader(col, row)) {
if (isValid(state.headerCheckedState[field])) {
return state.headerCheckedState[field];
} else if (typeof checked === 'function') {
return undefined;
} else if (isValid(checked)) {
state.headerCheckedState[field] = checked;
} else if (state.checkedState?.length > 0) {
const isAllChecked = state.updateHeaderCheckedState(field);
return isAllChecked;
}
return state.headerCheckedState[field];
}
const recordIndex = state.table.getRecordShowIndexByCell(col, row);
if (recordIndex >= 0) {
const dataIndex = state.table.dataSource.getIndexKey(recordIndex) as number;
if (isValid(state.checkedState[dataIndex]?.[field])) {
return state.checkedState[dataIndex][field];
}
if (state.checkedState[dataIndex]) {
state.checkedState[dataIndex][field] = checked;
} else {
state.checkedState[dataIndex] = {};
state.checkedState[dataIndex][field] = checked;
}
}
return checked;
}

/**
* 初始化check状态
* @param records
*/
export function initCheckedState(records: any[], state: StateManager) {
// clear checkbox state
state.checkedState = [];
state.headerCheckedState = {};
state.radioState = {};

let isNeedInitHeaderCheckedStateFromRecord = false;
state._checkboxCellTypeFields = [];
state._headerCheckFuncs = {};
state.table.internalProps.layoutMap.headerObjects.forEach((hd, index) => {
if (hd.headerType === 'checkbox') {
const headerChecked = (hd.define as CheckboxColumnDefine).checked as boolean;

if (headerChecked === undefined || headerChecked === null || typeof headerChecked === 'function') {
// 如果没有明确指定check的状态 则需要在下面遍历所有数据获取到节点状态 确定这个header的check状态
isNeedInitHeaderCheckedStateFromRecord = true;
if (typeof headerChecked === 'function') {
state._headerCheckFuncs[hd.field as string | number] = headerChecked;
}
} else {
state.headerCheckedState[hd.field as string | number] = headerChecked;
}
if (hd.define.cellType === 'checkbox' && !hd.fieldFormat) {
state._checkboxCellTypeFields.push(hd.field as string | number);
}
}
});
//如果没有明确指定check的状态 遍历所有数据获取到节点状态 确定这个header的check状态
if (isNeedInitHeaderCheckedStateFromRecord) {
records.forEach((record: any, index: number) => {
state._checkboxCellTypeFields.forEach(field => {
const value = record[field] as string | { text: string; checked: boolean; disable: boolean } | boolean;
let isChecked;
if (isObject(value)) {
isChecked = value.checked;
} else if (typeof value === 'boolean') {
isChecked = value;
}
if (isChecked === undefined || isChecked === null) {
const headerCheckFunc = state._headerCheckFuncs[field];
if (headerCheckFunc) {
//如果定义的checked是个函数 则需要每个都去计算这个值
const cellAddr = state.table.getCellAddrByFieldRecord(field, index);
const globalChecked = getOrApply(headerCheckFunc as any, {
col: cellAddr.col,
row: cellAddr.row,
table: state.table,
context: null,
value
});
isChecked = globalChecked;
}
}
if (!state.checkedState[index]) {
state.checkedState[index] = {};
}
state.checkedState[index][field] = isChecked;
});
});
}
}

/**
* 更新header单元checked的状态,依据当前列每一个数据checked的状态。
* @param field
* @returns
*/
export function updateHeaderCheckedState(field: string | number, state: StateManager): boolean | 'indeterminate' {
const allChecked = state.checkedState.every((state: Record<string | number, boolean>) => {
return state[field] === true;
});
if (allChecked) {
state.headerCheckedState[field] = true;
return allChecked;
}
const allUnChecked = state.checkedState.every((state: Record<string | number, boolean>) => {
return state[field] === false;
});
if (allUnChecked) {
state.headerCheckedState[field] = false;
return false;
}
const hasChecked = state.checkedState.find((state: Record<string | number, boolean>) => {
return state[field] === true;
});
if (hasChecked) {
state.headerCheckedState[field] = 'indeterminate';
return 'indeterminate'; //半选状态
}
return false;
}

/**
* setRecords的时候虽然调用了initCheckedState 进行了初始化 但当每个表头的checked状态都用配置了的话 初始化不会遍历全部数据
* @param records
*/
export function initLeftRecordsCheckState(records: any[], state: StateManager) {
for (let index = state.checkedState.length; index < records.length; index++) {
const record = records[index];
state._checkboxCellTypeFields.forEach(field => {
const value = record[field] as string | { text: string; checked: boolean; disable: boolean } | boolean;
let isChecked;
if (isObject(value)) {
isChecked = value.checked;
} else if (typeof value === 'boolean') {
isChecked = value;
}
if (!state.checkedState[index]) {
state.checkedState[index] = {};
}
state.checkedState[index][field] = isChecked;
});
}
}

export function setCellCheckboxState(col: number, row: number, checked: boolean, table: BaseTableAPI) {
const cellGoup = table.scenegraph.getCell(col, row);
const chechbox = cellGoup?.getChildByName('checkbox') as any;
if (!chechbox) {
return;
}
const { checked: oldChecked, indeterminate } = chechbox.attribute;

if (indeterminate) {
if (checked) {
chechbox._handlePointerUp();
} else {
chechbox._handlePointerUp();
chechbox._handlePointerUp();
}
} else if (oldChecked) {
if (checked) {
// do nothing
} else {
chechbox._handlePointerUp();
}
} else {
if (checked) {
chechbox._handlePointerUp();
} else {
// do nothing
}
}
}
25 changes: 20 additions & 5 deletions packages/vtable/src/state/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isBoolean, isNumber, isObject, isValid } from '@visactor/vutils';
import type { StateManager } from '../state';
import type { BaseTableAPI } from '../../ts-types/base-table';
import type { ColumnDefine } from '../../ts-types';
import type { Radio } from '@visactor/vrender-components';

export function setRadioState(
col: number,
Expand Down Expand Up @@ -99,27 +100,41 @@ export function syncRadioState(
state.radioState[field][dataIndex] = true;
}
return true;
} else if (!isValid(state.radioState[field][dataIndex]) && isChecked) {
} else if (!isValid(state.radioState[field]?.[dataIndex]) && isChecked) {
if (isNumber(indexInCell)) {
state.radioState[field][dataIndex] = indexInCell;
} else {
state.radioState[field][dataIndex] = true;
}
return true;
} else if (isBoolean(state.radioState[field][dataIndex]) && !isNumber(indexInCell)) {
} else if (isBoolean(state.radioState[field]?.[dataIndex]) && !isNumber(indexInCell)) {
// single : single
return state.radioState[field][dataIndex];
} else if (isBoolean(state.radioState[field][dataIndex]) && isNumber(indexInCell)) {
} else if (isBoolean(state.radioState[field]?.[dataIndex]) && isNumber(indexInCell)) {
// single : multiple
return false;
} else if (isNumber(state.radioState[field][dataIndex]) && !isNumber(indexInCell)) {
} else if (isNumber(state.radioState[field]?.[dataIndex]) && !isNumber(indexInCell)) {
// multiple : single
return false;
} else if (isNumber(state.radioState[field][dataIndex]) && isNumber(indexInCell)) {
} else if (isNumber(state.radioState[field]?.[dataIndex]) && isNumber(indexInCell)) {
// multiple : multiple
return state.radioState[field][dataIndex] === indexInCell;
}
}
}
return isChecked;
}

export function setCellRadioState(col: number, row: number, index: number | undefined, table: BaseTableAPI) {
const cellGoup = table.scenegraph.getCell(col, row);
if (!cellGoup) {
return;
}
if (isNumber(index)) {
const radio = cellGoup.getChildAt(index) as any;
radio?._handlePointerUp();
} else {
const radio = cellGoup.getChildByName('radio') as any;
radio?._handlePointerUp();
}
}
Loading

0 comments on commit 9e907bf

Please sign in to comment.