ant-design-vue/components/vc-tree/utils/conductUtil.ts

253 lines
6.9 KiB
TypeScript
Raw Normal View History

3.0 ready (#4523) * refactor: transfer、tooltip (#4306) * refactor(transfer): use composition api (#4135) * refactor(transfer): use composition api * fix: remove console * refactor(tooltip): use composition api (#4059) * refactor(tooltip): use composition api * chore: useConfigInject * fix: remove useless * style: format code * refactor: transfer * refactor: tooltip Co-authored-by: ajuner <106791576@qq.com> * Refactor mentions (#4341) * refactor(mentions): use compositionAPI (#4313) * refactor: mentions * refactor: mentions Co-authored-by: ajuner <106791576@qq.com> * Refactor progress (#4358) * fix: timepicker error border not show #4331 * fix(UploadDragger): fix UploadDrager no export (#4334) * refactor(switch): support customize checked value #4329 (#4332) * refactor(switch): support customize checked value #4329 * test: add test case * refactor: update props name * refactor: update ts * refactor: optimize * style: uncheckedValue to unCheckedValue * test: update snap * feat: udpate switch ts * docs: remove ie11 * fix: tree-select throw error when use slot title * fix: TypeScript definition of Table interface for typescript 4.3.5 (#4353) * fix type for typescript 4.3.5 * Update interface.ts close #4296 * fix: dropdown submenu style error #4351 close #4351 * fix(notification): 完善notification类型 (#4346) * refactor(progress): use composition API (#4355) * refactor(progress): use composition API * refactor(vc-progress): update * refactor: progress * refactor: progress * fix: timepicker error border not show #4331 * fix(UploadDragger): fix UploadDrager no export (#4334) * refactor(switch): support customize checked value #4329 (#4332) * refactor(switch): support customize checked value #4329 * test: add test case * refactor: update props name * refactor: update ts * refactor: optimize * style: uncheckedValue to unCheckedValue * test: update snap * feat: udpate switch ts * docs: remove ie11 * fix: tree-select throw error when use slot title * fix: TypeScript definition of Table interface for typescript 4.3.5 (#4353) * fix type for typescript 4.3.5 * Update interface.ts close #4296 * fix: dropdown submenu style error #4351 close #4351 * fix(notification): 完善notification类型 (#4346) * refactor(progress): use composition API (#4355) * refactor(progress): use composition API * refactor(vc-progress): update * refactor: progress * refactor: progress Co-authored-by: Jarvis <35361626+fanhaoyuan@users.noreply.github.com> Co-authored-by: John <John60676@qq.com> Co-authored-by: 艾斯特洛 <axetroy.dev@gmail.com> Co-authored-by: zanllp <qc@zanllp.cn> * docs: add changelog * refactor: tree * refactor: tree * style: lint * refactor: tree * 热factor: tree * refactor: tree * refactor: tree * refactor: tree * refactor: directory tree * refactor: tree * refactor: tree-select * refactor: tree-select * refactor: tree-select * refactor: tree-select * refactor: tree-select * style: lint format * refactor: tree-select * refactor: tree-select * refactor: tree-select * refactor: tree-select * refactor: tree-select * refactor: tree-select * fix: upload ts error * fix: update tree title render & switchIcon * test: update tree test * feat: add VirtualScroll tree * refactor: datePicker & calendar & trigger (#4522) * style: update * test: update calendar test * test: update test * test: update test * refactor: slider * feat: update slider css * refactor: slider to ts * refactor: slider to ts * perf: update default itemHeight * test: update * fix: uddate ts type * fix: update skeleton * fix: update skeleton * refactor: update vc-pagination * refactor: pagination * refactor: timeline * refactor: steps * refactor: collapse * refactor: collapse * refactor: popconfirm * refactor: popover * refactor: dropdown * doc: merge doc * chore: vite for dev (#4602) * style: js to jsx * doc: add site * style: lint * style: format ts type * doc: update * style: format code * style: format site * doc: update * style: dmeo * style: format scripts * chore: remove sub-modules * chore: update vite * site: add site build * test: update snap * doc(select): add tip (#4606) * refactor: table (#4641) * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * refactor: table * fix: column not pass to cell * doc: uppate table * fix: update bodyCell headerCell * doc: remove examples * refactor: table * fix: table title not work * fix: table selection * fix: table checkStrictly * refactor: table * fix: table template error * feat: table support summary * test: update snap * perf: table * docs(table): fix ajax demo (#4639) * test: update table * refactor: remove old table * doc: update table doc * doc: update doc * doc: update select * doc: update summary Co-authored-by: John <John60676@qq.com> * doc: update doc * fix: menu arrow not work * test: update * doc: add next site * style: format * doc: update * doc: update site script * fix: expand icon not fixed * feat: use renderSlot * test: update table snap * feat: confirm support reactively * feat: configProvider.config * feat: message support configprovider.config * feat: notification support configprovider.config * doc: update doc * fix: typescript compile error * style: add import eslint * doc: update demo * chore: set transpileOnly true * style: fix eslint error * test: update snap * doc: update * test: mock date * test: update snap * chore: remove gulp-typescript (#4675) * feat: V3 form (#4678) * chore: update husky * perf: update formItem * perf: useInjectFormItemContext * fix: table ts error * doc: add Customized Form Controls demo * feat: export useInjectFormItemContext * doc: update form doc * doc: update doc * doc: update doc * feat: autocomplete support option slot * doc: update * feat: add form item rest * style: remove omit.js * refactor: autocomplete * doc: add changelog to site * doc: update site anchor * doc: update doc layout * test: update table test * doc: update * chore: udpate gulp script * chore: udpate gulp script * doc: add changelog * doc: update * test: ignore some test wait vue-test-utils * fix: form id error #4582 close #4582 * doc: add select Responsive demo * doc: remove temp doc Co-authored-by: ajuner <106791576@qq.com> Co-authored-by: Jarvis <35361626+fanhaoyuan@users.noreply.github.com> Co-authored-by: John <John60676@qq.com> Co-authored-by: 艾斯特洛 <axetroy.dev@gmail.com> Co-authored-by: zanllp <qc@zanllp.cn> Co-authored-by: Amour1688 <lcz_1996@foxmail.com>
2021-09-25 08:51:32 +00:00
import { warning } from '../../vc-util/warning';
import type { Key, DataEntity, DataNode, GetCheckDisabled } from '../interface';
interface ConductReturnType {
checkedKeys: Key[];
halfCheckedKeys: Key[];
}
function removeFromCheckedKeys(halfCheckedKeys: Set<Key>, checkedKeys: Set<Key>) {
const filteredKeys = new Set<Key>();
halfCheckedKeys.forEach(key => {
if (!checkedKeys.has(key)) {
filteredKeys.add(key);
}
});
return filteredKeys;
}
export function isCheckDisabled(node: DataNode) {
const { disabled, disableCheckbox, checkable } = (node || {}) as DataNode;
return !!(disabled || disableCheckbox) || checkable === false;
}
// Fill miss keys
function fillConductCheck(
keys: Set<Key>,
levelEntities: Map<number, Set<DataEntity>>,
maxLevel: number,
syntheticGetCheckDisabled: GetCheckDisabled<DataNode>,
): ConductReturnType {
const checkedKeys = new Set<Key>(keys);
const halfCheckedKeys = new Set<Key>();
// Add checked keys top to bottom
for (let level = 0; level <= maxLevel; level += 1) {
const entities = levelEntities.get(level) || new Set();
entities.forEach(entity => {
const { key, node, children = [] } = entity;
if (checkedKeys.has(key) && !syntheticGetCheckDisabled(node)) {
children
.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node))
.forEach(childEntity => {
checkedKeys.add(childEntity.key);
});
}
});
}
// Add checked keys from bottom to top
const visitedKeys = new Set<Key>();
for (let level = maxLevel; level >= 0; level -= 1) {
const entities = levelEntities.get(level) || new Set();
entities.forEach(entity => {
const { parent, node } = entity;
// Skip if no need to check
if (syntheticGetCheckDisabled(node) || !entity.parent || visitedKeys.has(entity.parent.key)) {
return;
}
// Skip if parent is disabled
if (syntheticGetCheckDisabled(entity.parent.node)) {
visitedKeys.add(parent.key);
return;
}
let allChecked = true;
let partialChecked = false;
(parent.children || [])
.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node))
.forEach(({ key }) => {
const checked = checkedKeys.has(key);
if (allChecked && !checked) {
allChecked = false;
}
if (!partialChecked && (checked || halfCheckedKeys.has(key))) {
partialChecked = true;
}
});
if (allChecked) {
checkedKeys.add(parent.key);
}
if (partialChecked) {
halfCheckedKeys.add(parent.key);
}
visitedKeys.add(parent.key);
});
}
return {
checkedKeys: Array.from(checkedKeys),
halfCheckedKeys: Array.from(removeFromCheckedKeys(halfCheckedKeys, checkedKeys)),
};
}
// Remove useless key
function cleanConductCheck(
keys: Set<Key>,
halfKeys: Key[],
levelEntities: Map<number, Set<DataEntity>>,
maxLevel: number,
syntheticGetCheckDisabled: GetCheckDisabled<DataNode>,
): ConductReturnType {
const checkedKeys = new Set<Key>(keys);
let halfCheckedKeys = new Set<Key>(halfKeys);
// Remove checked keys from top to bottom
for (let level = 0; level <= maxLevel; level += 1) {
const entities = levelEntities.get(level) || new Set();
entities.forEach(entity => {
const { key, node, children = [] } = entity;
if (!checkedKeys.has(key) && !halfCheckedKeys.has(key) && !syntheticGetCheckDisabled(node)) {
children
.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node))
.forEach(childEntity => {
checkedKeys.delete(childEntity.key);
});
}
});
}
// Remove checked keys form bottom to top
halfCheckedKeys = new Set<Key>();
const visitedKeys = new Set<Key>();
for (let level = maxLevel; level >= 0; level -= 1) {
const entities = levelEntities.get(level) || new Set();
entities.forEach(entity => {
const { parent, node } = entity;
// Skip if no need to check
if (syntheticGetCheckDisabled(node) || !entity.parent || visitedKeys.has(entity.parent.key)) {
return;
}
// Skip if parent is disabled
if (syntheticGetCheckDisabled(entity.parent.node)) {
visitedKeys.add(parent.key);
return;
}
let allChecked = true;
let partialChecked = false;
(parent.children || [])
.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node))
.forEach(({ key }) => {
const checked = checkedKeys.has(key);
if (allChecked && !checked) {
allChecked = false;
}
if (!partialChecked && (checked || halfCheckedKeys.has(key))) {
partialChecked = true;
}
});
if (!allChecked) {
checkedKeys.delete(parent.key);
}
if (partialChecked) {
halfCheckedKeys.add(parent.key);
}
visitedKeys.add(parent.key);
});
}
return {
checkedKeys: Array.from(checkedKeys),
halfCheckedKeys: Array.from(removeFromCheckedKeys(halfCheckedKeys, checkedKeys)),
};
}
/**
* Conduct with keys.
* @param keyList current key list
* @param keyEntities key - dataEntity map
* @param mode `fill` to fill missing key, `clean` to remove useless key
*/
export function conductCheck(
keyList: Key[],
checked: true | { checked: false; halfCheckedKeys: Key[] },
keyEntities: Record<Key, DataEntity>,
getCheckDisabled?: GetCheckDisabled<DataNode>,
): ConductReturnType {
const warningMissKeys: Key[] = [];
let syntheticGetCheckDisabled: GetCheckDisabled<DataNode>;
if (getCheckDisabled) {
syntheticGetCheckDisabled = getCheckDisabled;
} else {
syntheticGetCheckDisabled = isCheckDisabled;
}
// We only handle exist keys
const keys = new Set<Key>(
keyList.filter(key => {
const hasEntity = !!keyEntities[key];
if (!hasEntity) {
warningMissKeys.push(key);
}
return hasEntity;
}),
);
const levelEntities = new Map<number, Set<DataEntity>>();
let maxLevel = 0;
// Convert entities by level for calculation
Object.keys(keyEntities).forEach(key => {
const entity = keyEntities[key];
const { level } = entity;
let levelSet: Set<DataEntity> = levelEntities.get(level);
if (!levelSet) {
levelSet = new Set();
levelEntities.set(level, levelSet);
}
levelSet.add(entity);
maxLevel = Math.max(maxLevel, level);
});
warning(
!warningMissKeys.length,
`Tree missing follow keys: ${warningMissKeys
.slice(0, 100)
.map(key => `'${key}'`)
.join(', ')}`,
);
let result: ConductReturnType;
if (checked === true) {
result = fillConductCheck(keys, levelEntities, maxLevel, syntheticGetCheckDisabled);
} else {
result = cleanConductCheck(
keys,
checked.halfCheckedKeys,
levelEntities,
maxLevel,
syntheticGetCheckDisabled,
);
}
return result;
}