lodash#intersection TypeScript Examples
The following examples show how to use
lodash#intersection.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts From EIP-Bot with Creative Commons Zero v1.0 Universal | 6 votes |
innerJoinAncestors = (
parent: TestResults,
objects: TestResults[]
) => {
const objectPaths = objects.map(getAllTruthyObjectPaths);
const commonPaths = intersection(...objectPaths);
const clearPaths = getAllTruthyObjectPaths(parent).filter(
(path) => !commonPaths.includes(path)
);
return clearPaths.reduce(
(obj, path) => set(obj, path, undefined),
parent
) as TestResults;
}
Example #2
Source File: multisig.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
export function useUnapprovedAccounts() {
const { accounts } = useApi();
const { multisigAccount } = useMultisig();
const getUnapprovedInjectedList = useCallback(
(data: Entry | null) => {
if (!data) {
return [];
}
const extensionAddresses = accounts?.map((item) => item.address) || [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const multisigPairAddresses = (multisigAccount?.meta.addressPair as any[])?.map((item) => item.address);
const extensionInPairs = intersection(extensionAddresses, multisigPairAddresses);
const approvedExtensionAddresses = intersection(extensionInPairs, data.approvals);
return difference(extensionInPairs, approvedExtensionAddresses);
},
[accounts, multisigAccount?.meta.addressPair]
);
return [getUnapprovedInjectedList];
}
Example #3
Source File: selectors.ts From jellyfin-audio-player with MIT License | 6 votes |
selectDownloadedTracks = (trackIds: EntityId[]) => (
createSelector(
selectAllDownloads,
({ entities, ids }) => {
return intersection(trackIds, ids)
.filter((id) => entities[id]?.isComplete);
}
)
)
Example #4
Source File: route-selector.component.ts From fyle-mobile-app with MIT License | 6 votes |
onTxnFieldsChange() {
const keyToControlMap: { [id: string]: AbstractControl } = {
distance: this.form.controls.distance,
};
for (const control of Object.values(keyToControlMap)) {
control.clearValidators();
control.updateValueAndValidity();
}
for (const txnFieldKey of intersection(['distance'], Object.keys(this.txnFields))) {
const control = keyToControlMap[txnFieldKey];
if (this.txnFields[txnFieldKey].is_mandatory) {
if (txnFieldKey === 'distance') {
control.setValidators(
this.isConnected ? Validators.compose([Validators.required, this.customDistanceValidator]) : null
);
}
}
control.updateValueAndValidity();
}
this.form.updateValueAndValidity();
}
Example #5
Source File: offline.service.ts From fyle-mobile-app with MIT License | 6 votes |
getProjectCount(params: { categoryIds: string[] } = { categoryIds: [] }) {
return this.getProjects().pipe(
map((projects) => {
const filterdProjects = projects.filter((project) => {
if (params.categoryIds.length) {
return intersection(params.categoryIds, project.org_category_ids).length > 0;
} else {
return true;
}
});
return filterdProjects.length;
})
);
}
Example #6
Source File: perm-export.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
checkValidRole = (data: Obj, role: Obj) => {
const roleKeys = map(role, 'value');
const newData = cloneDeep(data);
const check = (_d: Obj) => {
// eslint-disable-next-line no-param-reassign
if (_d.role) _d.role = intersection(_d.role, roleKeys);
map(_d, (item) => {
typeof item === 'object' && check(item);
});
};
check(newData);
return newData;
}
Example #7
Source File: text-edit.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
validateSameKey = (val: string, fullConfigData: PIPELINE_CONFIG.ConfigItem[] = []) => {
if (!val) return i18n.t('can not be empty');
const valData: PIPELINE_CONFIG.ConfigItem[] = JSON.parse(val);
const keys = valData.map((item) => item.key);
const otherKeys = fullConfigData.filter((item) => item.type !== ConfigTypeMap.kv.key).map((item) => item.key);
if (uniq(keys).length !== keys.length) {
return i18n.t('the same {key} exists', { key: 'key' });
} else if (intersection(keys, otherKeys).length) {
return i18n.t('{name} already exists in {place}', {
name: intersection(keys, otherKeys).join(','),
place: i18n.t('common:Other Type'),
});
}
return '';
}
Example #8
Source File: validate.ts From backend with MIT License | 6 votes |
async function ensureNeverPreviouslyMatchedTogether(coach: Student, coachee: Pupil, manager: EntityManager) {
const projectMatchesCoach = await coach.projectMatches;
const projectMatchesCoachee = await coachee.projectMatches;
//reload the project match from database, to ensure they really have the pupil and student relationships loaded (they are eager and not always loaded automatically)
const reloadProjectMatches = async pm => reloadProjectMatchesInstances(pm, manager);
const partnersCoach = (await reloadProjectMatches(projectMatchesCoach)).map(m => m.pupil.wix_id);
const partnersCoachee = (await reloadProjectMatches(projectMatchesCoachee)).map(m => m.student.wix_id);
return intersection([...partnersCoach, ...partnersCoachee], [coach.wix_id, coachee.wix_id]).length === 0;
}
Example #9
Source File: validate.ts From backend with MIT License | 6 votes |
async function ensureNeverPreviouslyMatchedTogether(tutor: Student, tutee: Pupil, manager: EntityManager) {
const matchesTutor = await tutor.matches;
const matchesTutee = await tutee.matches;
//reload the match from database, to ensure they really have the pupil and student relationships loaded (they are eager and not always loaded automatically)
const reloadMatches = async m => reloadMatchesInstances(m, manager);
const partnersTutor = (await reloadMatches(matchesTutor)).map(m => m.pupil.wix_id);
const partnersTutee = (await reloadMatches(matchesTutee)).map(m => m.student.wix_id);
return intersection([...partnersTutor, ...partnersTutee], [tutor.wix_id, tutee.wix_id]).length === 0;
}
Example #10
Source File: custom-tree-pivot-data-set.ts From S2 with MIT License | 5 votes |
processDataCfg(dataCfg: S2DataConfig): S2DataConfig {
// 自定义行头有如下几个特点
// 1、rows配置必须是空,需要额外添加 $$extra$$ 定位数据(标记指标的id)
// 2、要有配置 fields.rowCustomTree(行头结构)
// 3、values 不需要参与计算,默认就在行头结构中
dataCfg.fields.rows = [EXTRA_FIELD];
dataCfg.fields.valueInCols = false;
const { data, ...restCfg } = dataCfg;
const { values } = dataCfg.fields;
// 将源数据中的value值,映射为 $$extra$$,$$value$$
// {
// province: '四川', province: '四川',
// city: '成都', => city: '成都',
// price='11' price='11'
// $$extra$$=price
// $$value$$=11
// 此时 province, city 均配置在columns里面
// }
const transformedData = [];
forEach(data, (dataItem) => {
if (isEmpty(intersection(keys(dataItem), values))) {
transformedData.push(dataItem);
} else {
forEach(values, (value) => {
if (has(dataItem, value)) {
transformedData.push({
...dataItem,
[EXTRA_FIELD]: value,
[VALUE_FIELD]: dataItem[value],
});
}
});
}
});
return {
data: uniq(transformedData),
...restCfg,
};
}
Example #11
Source File: smoke.test.ts From analytics-next with MIT License | 5 votes |
function compareSchema(results: RemovePromise<ReturnType<typeof run>>) {
const classicReqs = results.classic.networkRequests
.filter((n) => n.url.includes('api.segment') && !n.url.endsWith('/m'))
.sort()
const nextReqs = results.next.networkRequests
.filter((n) => n.url.includes('api.segment') && !n.url.endsWith('/m'))
.sort()
nextReqs.forEach((req, index) => {
const classic = classicReqs[index]
if (!classic) {
return
}
expect(req.url).toEqual(classic.url)
// @ts-ignore need all sources to be rebuilt first
if (classic.data?._metadata) {
// @ts-ignore need all sources to be rebuilt first
delete classic.data._metadata.bundledIds
// @ts-ignore sort unbundled metadata because of a breaking change in the SegmentIO destination
classic.data._metadata.unbundled = uniq(
// @ts-ignore
classic.data._metadata.unbundled.sort()
)
}
expect(req.data).toContainSchema(classic.data)
const nextSchema = objectSchema(req.data as object)
const classicSchema = objectSchema(classic.data as object)
const intersectionKeys = without(
intersection(nextSchema, classicSchema),
// These are different on purpose
'context.library.name',
'context.library.version',
// We use the same website with a slightly different URL
'context.page.search',
'properties.search',
'context.page.url',
'properties.url',
'messageId',
'anonymousId',
// We do an integrations specific check below since 'next'
// may have action-destinations that 'classic' does not
'integrations'
)
expect((req.data as Record<string, JSONValue>).integrations).toEqual(
expect.objectContaining(
(classic.data as Record<string, JSONValue>).integrations
)
)
const flatNext = flat(req.data) as Record<string, JSONValue>
const flatClassic = flat(classic.data) as Record<string, JSONValue>
intersectionKeys.forEach((key) => {
const comparison = {
url: req.url,
key,
next: flatNext[key],
classic: flatClassic[key],
}
expect({ ...comparison, val: comparison.next }).toEqual({
...comparison,
val: comparison.classic,
})
})
})
}
Example #12
Source File: handlers.ts From leda with MIT License | 5 votes |
createClickHandler = (props: ButtonProps) => (ev: React.MouseEvent<HTMLButtonElement>): void => {
const {
onClick, onValidationFail, isDisabled, isLoading, scrollDelay,
scrollOffset, form: formProp, shouldScrollToInvalidFields,
} = props;
if (isDisabled || isLoading) return; // если кнопка отключена или в состояниии загрузки, прервать выполнение функции
// если кнопка участвует в валидации форм
if (formProp) {
const buttonFormNames = Array.isArray(formProp) ? formProp : [formProp];
const formNames = getForms().map((form) => form.name);
const validButtonFormNames = intersection(formNames, buttonFormNames);
const isEachFormValid = validButtonFormNames
.map((currentForm) => validate(currentForm)) // validate all forms
.every((item) => item); // tell me if all of them are valid
if (!isEachFormValid) {
const invalidForms = getForms()
.filter((currentForm) => validButtonFormNames.includes(currentForm.name))
.filter((currentForm) => currentForm.fields.some((field) => !field.isValid));
if (isFunction(onValidationFail)) {
onValidationFail({ ...ev, invalidForms });
}
if (shouldScrollToInvalidFields && invalidForms.length > 0) {
const firstInvalidFormName = invalidForms[0].name;
// data-form для buttonGroup, использовать просто [from=""] нельзя, т.к. захватит кнопки тоже
const formElements = document.querySelectorAll(`input[form="${firstInvalidFormName}"], [data-form="${firstInvalidFormName}"`);
// ждем пока обновятся данные
setTimeout(() => {
const invalidElement = Array.from(formElements).find((element) => element.getAttribute('aria-invalid') === 'true');
if (invalidElement) {
const invalidElementRect = invalidElement.getBoundingClientRect();
const isIE = !!(document as any).documentMode || /Edge/.test(navigator.userAgent);
const offset = invalidElementRect.top - (scrollOffset ?? 0);
if (isIE) {
window.scrollBy(0, offset);
} else {
window.scrollBy({
top: offset,
behavior: 'smooth',
});
}
}
}, scrollDelay ?? 0);
}
return;
}
}
if (isFunction(onClick)) {
if (!formProp) {
onClick(ev);
return;
}
const forms = getForms(formProp);
const formsObject = fromFormArraytoFormObject(forms);
const customEvent = {
...ev,
forms,
form: formsObject,
};
onClick(customEvent);
}
}
Example #13
Source File: compose.ts From gant-design with MIT License | 5 votes |
findDependencies = (
changedValueObject: object,
{ ...schema }: Schema,
mapSubSchema: mapSubSchema,
form: WrappedFormUtils
): void => {
// 改变的key
// object.product
const changeKeys = objectToPath(changedValueObject)
if (!changeKeys.length) return
const dependenciesChangeValue = {}
function setFieldsValue(data) {
for (const key of Reflect.ownKeys(data)) {
Reflect.set(dependenciesChangeValue, key, data[key])
}
}
/**进入schema子树去寻找依赖项 */
function inner(schemaKey: string[], subSchema): Array<Promise<Change>> {
/**将所有变更推送到同一次更新流程、而不再时单独去在整个树上更新、防止出现一次更新流程中,修改了多个子树,导致前后更新不一致的问题 */
const changedSchema = []
const { dependencies = [], onDependenciesChange, type, ...restSchema } = subSchema
if (type !== Types.object) {
if (get(dependencies, 'length') && get(intersection(dependencies, changeKeys), 'length') && onDependenciesChange) {
const dependenciesValues = dependencies.map(deKey => {
if (changeKeys.includes(deKey)) return get(changedValueObject, deKey)
return form.getFieldValue(deKey)
})
const mergeSchema = onDependenciesChange(dependenciesValues, cloneDeep(restSchema), { ...form, setFieldsValue })
changedSchema.push(
Promise.resolve(mergeSchema).then(
newSubSchema => ({ key: schemaKey, schema: { ...subSchema, ...newSubSchema } })
)
)
}
} else if (subSchema.propertyType) {
subSchema.propertyType = { ...subSchema.propertyType }
const entries = Object.entries(subSchema.propertyType)
for (const [subSchemaKey, schemaValue] of entries) {
const { dependencies = [], onDependenciesChange, type } = schemaValue as any
if (
// 找到了依赖项或者是object,进入递归
(get(dependencies, 'length') && get(intersection(dependencies, changeKeys), 'length') && onDependenciesChange) ||
type === Types.object
) {
const subChangedSchema = inner([...schemaKey, subSchemaKey], schemaValue)
changedSchema.push(...subChangedSchema)
}
}
}
return changedSchema
}
const allChange = inner([], schema)
Promise.all(allChange).then(changes => {
mapSubSchema(schema, changes, () => {
if (!isEmpty(dependenciesChangeValue)) {
form.setFieldsValue(dependenciesChangeValue)
}
})
})
}
Example #14
Source File: AuthHelper.ts From node-experience with MIT License | 5 votes |
static validatePermissions(permissions: string[]): void
{
if (!isEmpty(permissions) && isEmpty(intersection(permissions, Permissions.permissions())))
{
throw new WrongPermissionsException();
}
}
Example #15
Source File: with-auth.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
getAuth = (permObj: IPermObj, checkRole: string[]) => {
if (permObj && permObj.pass) return permObj.pass;
if (permObj && checkRole) {
const l = intersection(permObj.role, checkRole);
if (l.length) return true;
}
return false;
}
Example #16
Source File: org.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
setLocationByAuth = (authObj: { roles: string[]; orgName: string }) => {
const curPathname = location.pathname;
const { roles, orgName } = authObj;
const checkMap = {
dataEngineer: {
isCurPage: curPathname.startsWith(`/${orgName}/fdp`),
authRole: intersection(orgPerm.entryFastData.role, roles),
},
orgCenter: {
isCurPage: curPathname.startsWith(`/${orgName}/orgCenter`),
authRole: intersection(orgPerm.entryOrgCenter.role, roles),
},
msp: {
isCurPage: curPathname.startsWith(`/${orgName}/msp`),
authRole: intersection(orgPerm.entryMsp.role, roles),
},
ecp: {
isCurPage: curPathname.startsWith(`/${orgName}/ecp`),
authRole: intersection(orgPerm.ecp.view.role, roles),
},
cmp: {
isCurPage: curPathname.startsWith(`/${orgName}/cmp`),
authRole: intersection(orgPerm.cmp.showApp.role, roles),
},
dop: {
isCurPage: curPathname.startsWith(`/${orgName}/dop`),
authRole: intersection(orgPerm.dop.read.role, roles),
},
};
map(checkMap, (item) => {
// 当前页,但是无权限,则重置
if (item.isCurPage && !item.authRole.length) {
let resetPath = goTo.resolve.orgRoot({ orgName });
if (roles.toString() === 'DataEngineer') {
// DataEngineer redirect to DataEngineer role page
resetPath = `/${orgName}/fdp/__cluster__/__workspace__/data-govern-platform/data-source`;
} else if (roles.toString() === 'Ops') {
// 企业运维只有云管的权限
resetPath = `/${orgName}/cmp/overview`;
} else if (roles.toString() === 'EdgeOps') {
// 边缘运维工程师只有边缘计算平台的权限
resetPath = `/${orgName}/ecp/application`;
}
goTo(resetPath);
}
});
}
Example #17
Source File: BrickTree.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function BrickTree(props: BrickTreeProps): React.ReactElement {
const {
selectedKeys: _selectedKeys,
checkedKeys: _checkedKeys,
expandedKeys: _expandedKeys,
configProps = {},
dataSource = [],
searchable = false,
searchParent = false,
placeholder = "",
checkAllEnabled,
checkedFilterConfig: { field, value, operator } = {},
suffixBrick,
showSpecificationTitleStyle,
defaultExpandAll,
deselectable,
alsoSearchByKey,
} = props;
const [allChecked, setAllChecked] = useState(false);
const [indeterminate, setIndeterminate] = useState(false);
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>();
const [filterCheckedKeys, setFilterCheckedKeys] = useState<React.Key[]>();
const [checkedKeys, setCheckedKeys] = useState<
React.Key[] | { checked: React.Key[]; halfChecked: React.Key[] }
>();
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>();
const [searchValue, setSearchValue] = useState<string>();
const treeContainerRef = useRef<HTMLDivElement>();
const nodeMatchedRef = useRef<boolean>(false);
const treeData = useMemo(() => getTreeNodes(dataSource), [dataSource]);
const filterTreeKeys = useMemo(
() =>
flat(dataSource)
.filter((v) => compareFunMap[operator]?.(value, get(v, field)))
.map((v) => v.key) || [],
[dataSource]
);
useEffect(() => {
setSelectedKeys(_selectedKeys);
}, [_selectedKeys]);
// istanbul ignore next
useEffect(() => {
setCheckedKeys(_checkedKeys);
setFilterCheckedKeys(difference(_checkedKeys, filterTreeKeys));
if (Array.isArray(_checkedKeys)) {
if (
_checkedKeys.length === 0 ||
intersection(_checkedKeys, getAllKeys(treeData))?.length === 0
) {
setAllChecked(false);
setIndeterminate(false);
} else {
const checkedKeySet = new Set(_checkedKeys);
const allChecked = getAllCheckedState(treeData, checkedKeySet);
setAllChecked(allChecked);
setIndeterminate(!allChecked);
}
}
}, [_checkedKeys]);
useEffect(() => {
setExpandedKeys(_expandedKeys);
}, [_expandedKeys]);
useEffect(() => {
nodeMatchedRef.current = false;
}, [searchValue]);
let searchValueLength: number;
if (searchValue) {
searchValueLength = searchValue.length;
}
const onChange = useCallback(
debounce((value: string) => {
// 等到 expandedKeys 更新后,也就是展开状态改变后,再触发跳转到第一个匹配项
setTimeout(() => {
setSearchValue(value);
});
if (value) {
const expandedKeys: React.Key[] = [];
getExpandedKeysBySearchValue(
treeData,
value.toLocaleLowerCase(),
expandedKeys,
{
searchParent,
alsoSearchByKey,
}
);
setExpandedKeys(expandedKeys);
}
}, 500),
[treeData, searchParent, alsoSearchByKey]
);
const onCheckAllChange = (e: CheckboxChangeEvent) => {
const checked = e.target.checked;
const checkedKeys = checked ? getAllKeys(treeData) : [];
let _filterCheckedKeys: React.Key[] = [];
if (props.checkedFilterConfig) {
_filterCheckedKeys = difference(checkedKeys, filterTreeKeys);
setFilterCheckedKeys(_filterCheckedKeys);
}
setAllChecked(checked);
setIndeterminate(false);
setCheckedKeys(checkedKeys);
props.onCheck?.(
props.checkedFilterConfig ? _filterCheckedKeys : checkedKeys
);
};
const onSelect = (
selectedKeys: React.Key[],
info: {
event: "select";
selected: boolean;
node: EventDataNode;
selectedNodes: DataNode[];
nativeEvent: MouseEvent;
}
) => {
if (!deselectable && selectedKeys.length === 0) {
return;
}
setSelectedKeys(selectedKeys);
props.onSelect?.(selectedKeys, info);
};
const onCheck = (
checkedKeys:
| React.Key[]
| { checked: React.Key[]; halfChecked: React.Key[] }
) => {
let _filterCheckedKeys: React.Key[] = [];
if (props.checkedFilterConfig) {
_filterCheckedKeys = difference(
checkedKeys as React.Key[],
filterTreeKeys
);
Array.isArray(checkedKeys) && setFilterCheckedKeys(_filterCheckedKeys);
}
setCheckedKeys(checkedKeys);
if (Array.isArray(checkedKeys)) {
if (checkedKeys.length === 0) {
setAllChecked(false);
setIndeterminate(false);
} else {
const checkedKeySet = new Set(checkedKeys);
const allChecked = treeData.every((node) =>
checkedKeySet.has(node.key)
);
setAllChecked(allChecked);
setIndeterminate(allChecked ? false : true);
}
}
props.onCheck?.(
props.checkedFilterConfig ? _filterCheckedKeys : checkedKeys
);
};
const onExpand = (expandedKeys: React.Key[]) => {
setExpandedKeys(expandedKeys);
};
return (
<>
{searchable && (
<Input.Search
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
style={{ marginBottom: 8 }}
data-testid="search-input"
/>
)}
{configProps.checkable && checkAllEnabled && (
<div style={{ marginBottom: 6, display: "flex", alignItems: "center" }}>
<Checkbox
checked={allChecked}
indeterminate={indeterminate}
onChange={onCheckAllChange}
data-testid="check-all-checkbox"
>
{i18n.t(`${NS_PRESENTATIONAL_BRICKS}:${K.SELECT_ALL}`)}
</Checkbox>
<span style={{ marginLeft: "auto" }} className="checkedNum">
{i18n.t(`${NS_PRESENTATIONAL_BRICKS}:${K.SELECTED_OPTIONS}`, {
number: props.checkedFilterConfig
? filterCheckedKeys?.length
: (Array.isArray(checkedKeys) && checkedKeys?.length) || 0,
})}
</span>
</div>
)}
<div
className={classNames(styles.treeWrapper, {
[styles.withSuffix]: !isEmpty(suffixBrick?.useBrick),
[styles.titleSpace]: showSpecificationTitleStyle,
})}
ref={treeContainerRef}
>
{treeData?.length ? (
<Tree
{...configProps}
treeData={treeData}
titleRender={(node) => {
const { title: _title, children, key: _key } = node;
let title: React.ReactNode = _title;
//根据ui规范,全部或者默认的节点,字体加粗,间距加宽
const allOrDefaultFlag =
(title === "全部" || title === "默认") && !children;
if (
typeof _title === "string" &&
searchValue &&
(searchParent ? true : !children?.length)
) {
const lowerCaseSearchValue = searchValue.toLocaleLowerCase();
const index = _title
.toLocaleLowerCase()
.indexOf(lowerCaseSearchValue);
const kIndex = alsoSearchByKey
? _key
.toString()
?.toLocaleLowerCase()
.indexOf(lowerCaseSearchValue)
: -1;
if (index >= 0) {
const beforeStr = _title.substring(0, index);
const matchStr = _title.substring(
index,
searchValueLength + index
);
const afterStr = _title.substring(searchValueLength + index);
title = (
<span
ref={(() => {
if (!nodeMatchedRef.current) {
nodeMatchedRef.current = true;
return (el: HTMLElement) => {
if (el) {
const nodeEl =
el.closest(".ant-tree-treenode") || el;
const treeContainerEl = treeContainerRef.current;
treeContainerEl.scrollBy(
undefined,
nodeEl.getBoundingClientRect().top -
treeContainerEl.getBoundingClientRect().top
);
}
};
} else {
return null;
}
})()}
>
{alsoSearchByKey ? (
// 如果也按key搜索,就整体高亮(因为key不会展示)
<span className={styles.matchTextTotal}>{_title}</span>
) : (
<>
{beforeStr}
<span className={styles.matchText}>{matchStr}</span>
{afterStr}
</>
)}
</span>
);
} else if (kIndex >= 0) {
title = (
<span
ref={(() => {
if (!nodeMatchedRef.current) {
nodeMatchedRef.current = true;
return (el: HTMLElement) => {
if (el) {
const nodeEl =
el.closest(".ant-tree-treenode") || el;
const treeContainerEl = treeContainerRef.current;
treeContainerEl.scrollBy(
undefined,
nodeEl.getBoundingClientRect().top -
treeContainerEl.getBoundingClientRect().top
);
}
};
} else {
return null;
}
})()}
>
<span className={styles.matchTextTotal}>{_title}</span>
</span>
);
}
}
if (!isEmpty(suffixBrick?.useBrick)) {
return (
<div className={styles.suffixBrickWrapper}>
<span
className={
showSpecificationTitleStyle && allOrDefaultFlag
? styles.allOrDefault
: null
}
>
{title}
</span>
<BrickAsComponent
useBrick={suffixBrick.useBrick}
data={node}
/>
</div>
);
}
return showSpecificationTitleStyle && allOrDefaultFlag ? (
<span className={styles.allOrDefault}>{title}</span>
) : (
title
);
}}
selectedKeys={selectedKeys}
checkedKeys={checkedKeys}
{...(expandedKeys ? { expandedKeys: expandedKeys } : {})}
defaultExpandAll={defaultExpandAll}
onSelect={onSelect}
onCheck={onCheck}
onExpand={onExpand}
/>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</div>
</>
);
}
Example #18
Source File: add-edit-mileage.page.ts From fyle-mobile-app with MIT License | 4 votes |
ionViewWillEnter() {
from(this.tokenService.getClusterDomain()).subscribe((clusterDomain) => {
this.clusterDomain = clusterDomain;
});
this.navigateBack = this.activatedRoute.snapshot.params.navigate_back;
this.expenseStartTime = new Date().getTime();
this.fg = this.fb.group({
mileage_vehicle_type: [],
dateOfSpend: [, this.customDateValidator],
route: [],
paymentMode: [, Validators.required],
purpose: [],
project: [],
billable: [],
sub_category: [, Validators.required],
custom_inputs: new FormArray([]),
costCenter: [],
add_to_new_report: [],
report: [],
duplicate_detection_reason: [],
});
const today = new Date();
this.maxDate = moment(this.dateService.addDaysToDate(today, 1)).format('y-MM-D');
this.setupDuplicateDetection();
this.fg.reset();
this.title = 'Add Mileage';
this.activeIndex = this.activatedRoute.snapshot.params.activeIndex;
this.reviewList =
this.activatedRoute.snapshot.params.txnIds && JSON.parse(this.activatedRoute.snapshot.params.txnIds);
this.title =
this.activeIndex > -1 && this.reviewList && this.activeIndex < this.reviewList.length ? 'Review' : 'Edit';
if (this.activatedRoute.snapshot.params.id) {
this.mode = 'edit';
}
this.isExpandedView = this.mode !== 'add';
const orgSettings$ = this.offlineService.getOrgSettings();
const orgUserSettings$ = this.offlineService.getOrgUserSettings();
this.isAdvancesEnabled$ = orgSettings$.pipe(
map(
(orgSettings) =>
(orgSettings.advances && orgSettings.advances.enabled) ||
(orgSettings.advance_requests && orgSettings.advance_requests.enabled)
)
);
this.setupNetworkWatcher();
this.recentlyUsedValues$ = this.isConnected$.pipe(
take(1),
switchMap((isConnected) => {
if (isConnected) {
return this.recentlyUsedItemsService.getRecentlyUsed();
} else {
return of(null);
}
})
);
this.recentlyUsedMileageLocations$ = this.recentlyUsedValues$.pipe(
map((recentlyUsedValues) => ({
recent_start_locations: recentlyUsedValues?.recent_start_locations || [],
recent_end_locations: recentlyUsedValues?.recent_end_locations || [],
recent_locations: recentlyUsedValues?.recent_locations || [],
}))
);
this.txnFields$ = this.getTransactionFields();
this.paymentModes$ = this.getPaymentModes();
this.homeCurrency$ = this.offlineService.getHomeCurrency();
this.subCategories$ = this.getSubCategories();
this.setupFilteredCategories(this.subCategories$);
this.projectCategoryIds$ = this.getProjectCategoryIds();
this.isProjectVisible$ = this.projectCategoryIds$.pipe(
switchMap((projectCategoryIds) => this.offlineService.getProjectCount({ categoryIds: projectCategoryIds }))
);
this.comments$ = this.statusService.find('transactions', this.activatedRoute.snapshot.params.id);
this.filteredCategories$.subscribe((subCategories) => {
if (subCategories.length) {
this.fg.controls.sub_category.setValidators(Validators.required);
} else {
this.fg.controls.sub_category.clearValidators();
}
this.fg.controls.sub_category.updateValueAndValidity();
});
this.mileageConfig$ = this.getMileageConfig();
this.etxn$ = iif(() => this.mode === 'add', this.getNewExpense(), this.getEditExpense());
this.setupTfcDefaultValues();
this.isAmountDisabled$ = this.etxn$.pipe(map((etxn) => !!etxn.tx.admin_amount));
this.isIndividualProjectsEnabled$ = orgSettings$.pipe(
map((orgSettings) => orgSettings.advanced_projects && orgSettings.advanced_projects.enable_individual_projects)
);
this.individualProjectIds$ = orgUserSettings$.pipe(
map((orgUserSettings: any) => orgUserSettings.project_ids || [])
);
this.isProjectsEnabled$ = orgSettings$.pipe(
map((orgSettings) => orgSettings.projects && orgSettings.projects.enabled)
);
this.customInputs$ = this.getCustomInputs();
this.isCostCentersEnabled$ = orgSettings$.pipe(map((orgSettings) => orgSettings.cost_centers.enabled));
this.costCenters$ = forkJoin({
orgSettings: orgSettings$,
orgUserSettings: orgUserSettings$,
}).pipe(
switchMap(({ orgSettings, orgUserSettings }) => {
if (orgSettings.cost_centers.enabled) {
return this.offlineService.getAllowedCostCenters(orgUserSettings);
} else {
return of([]);
}
}),
map((costCenters) =>
costCenters.map((costCenter) => ({
label: costCenter.name,
value: costCenter,
}))
)
);
this.recentlyUsedCostCenters$ = forkJoin({
costCenters: this.costCenters$,
recentValue: this.recentlyUsedValues$,
}).pipe(
concatMap(({ costCenters, recentValue }) =>
this.recentlyUsedItemsService.getRecentCostCenters(costCenters, recentValue)
)
);
this.reports$ = this.reportService
.getFilteredPendingReports({ state: 'edit' })
.pipe(map((reports) => reports.map((report) => ({ label: report.rp.purpose, value: report }))));
this.txnFields$
.pipe(
distinctUntilChanged((a, b) => isEqual(a, b)),
switchMap((txnFields) =>
forkJoin({
isConnected: this.isConnected$.pipe(take(1)),
orgSettings: this.offlineService.getOrgSettings(),
costCenters: this.costCenters$,
isIndividualProjectsEnabled: this.isIndividualProjectsEnabled$,
individualProjectIds: this.individualProjectIds$,
}).pipe(
map(({ isConnected, orgSettings, costCenters, isIndividualProjectsEnabled, individualProjectIds }) => ({
isConnected,
txnFields,
orgSettings,
costCenters,
isIndividualProjectsEnabled,
individualProjectIds,
}))
)
)
)
.subscribe(
({ isConnected, txnFields, costCenters, orgSettings, individualProjectIds, isIndividualProjectsEnabled }) => {
const keyToControlMap: { [id: string]: AbstractControl } = {
purpose: this.fg.controls.purpose,
cost_center_id: this.fg.controls.costCenter,
txn_dt: this.fg.controls.dateOfSpend,
project_id: this.fg.controls.project,
billable: this.fg.controls.billable,
};
for (const control of Object.values(keyToControlMap)) {
control.clearValidators();
control.updateValueAndValidity();
}
for (const txnFieldKey of intersection(Object.keys(keyToControlMap), Object.keys(txnFields))) {
const control = keyToControlMap[txnFieldKey];
if (txnFields[txnFieldKey].is_mandatory) {
if (txnFieldKey === 'txn_dt') {
control.setValidators(
isConnected ? Validators.compose([Validators.required, this.customDateValidator]) : null
);
} else if (txnFieldKey === 'cost_center_id') {
control.setValidators(
isConnected && costCenters && costCenters.length > 0 ? Validators.required : null
);
} else if (txnFieldKey === 'project_id') {
control.setValidators(
orgSettings.projects.enabled && isIndividualProjectsEnabled && individualProjectIds.length === 0
? null
: Validators.required
);
} else {
control.setValidators(isConnected ? Validators.required : null);
}
}
control.updateValueAndValidity();
}
this.fg.updateValueAndValidity();
}
);
this.isAmountCapped$ = this.etxn$.pipe(
map((etxn) => isNumber(etxn.tx.admin_amount) || isNumber(etxn.tx.policy_amount))
);
this.isAmountDisabled$ = this.etxn$.pipe(map((etxn) => !!etxn.tx.admin_amount));
this.isCriticalPolicyViolated$ = this.etxn$.pipe(
map((etxn) => isNumber(etxn.tx.policy_amount) && etxn.tx.policy_amount < 0.0001)
);
this.getPolicyDetails();
this.isBalanceAvailableInAnyAdvanceAccount$ = this.fg.controls.paymentMode.valueChanges.pipe(
switchMap((paymentMode) => {
if (paymentMode && paymentMode.acc && paymentMode.acc.type === 'PERSONAL_ACCOUNT') {
return this.offlineService
.getAccounts()
.pipe(
map(
(accounts) =>
accounts.filter(
(account) =>
account &&
account.acc &&
account.acc.type === 'PERSONAL_ADVANCE_ACCOUNT' &&
account.acc.tentative_balance_amount > 0
).length > 0
)
);
}
return of(false);
})
);
this.rate$ = iif(
() => this.mode === 'edit',
// this.etxn$.pipe(
// map(etxn => etxn.tx.mileage_rate)
// )
this.fg.valueChanges.pipe(
map((formValue) => formValue.mileage_vehicle_type),
switchMap((vehicleType) =>
forkJoin({
orgSettings: this.offlineService.getOrgSettings(),
etxn: this.etxn$,
}).pipe(
map(({ orgSettings, etxn }) => {
if (etxn.tx.mileage_rate && etxn.tx.mileage_vehicle_type === vehicleType) {
return etxn.tx.mileage_rate;
} else {
return orgSettings.mileage[vehicleType];
}
})
)
),
shareReplay(1)
),
this.fg.valueChanges.pipe(
map((formValue) => formValue.mileage_vehicle_type),
switchMap((vehicleType) =>
this.offlineService.getOrgSettings().pipe(map((orgSettings) => orgSettings.mileage[vehicleType]))
),
shareReplay(1)
)
);
this.amount$ = combineLatest(this.fg.valueChanges, this.rate$).pipe(
map(([formValue, mileageRate]) => {
const distance = formValue.route?.distance || 0;
return distance * mileageRate;
}),
shareReplay(1)
);
const selectedProject$ = this.etxn$.pipe(
switchMap((etxn) => {
if (etxn.tx.project_id) {
return of(etxn.tx.project_id);
} else {
return forkJoin({
orgSettings: this.offlineService.getOrgSettings(),
orgUserSettings: this.offlineService.getOrgUserSettings(),
}).pipe(
map(({ orgSettings, orgUserSettings }) => {
if (orgSettings.projects.enabled) {
return orgUserSettings && orgUserSettings.preferences && orgUserSettings.preferences.default_project_id;
}
})
);
}
}),
switchMap((projectId) => {
if (projectId) {
return this.projectService.getbyId(projectId);
} else {
return of(null);
}
})
);
const selectedPaymentMode$ = this.etxn$.pipe(
switchMap((etxn) =>
iif(
() => etxn.tx.source_account_id,
this.paymentModes$.pipe(
map((paymentModes) =>
paymentModes
.map((res) => res.value)
.find((paymentMode) => {
if (paymentMode.acc.displayName === 'Personal Card/Cash') {
return paymentMode.acc.id === etxn.tx.source_account_id && !etxn.tx.skip_reimbursement;
} else {
return paymentMode.acc.id === etxn.tx.source_account_id;
}
})
)
),
of(null)
)
)
);
const defaultPaymentMode$ = this.paymentModes$.pipe(
map((paymentModes) =>
paymentModes.map((res) => res.value).find((paymentMode) => paymentMode.acc.displayName === 'Personal Card/Cash')
)
);
this.recentlyUsedProjects$ = forkJoin({
recentValues: this.recentlyUsedValues$,
mileageCategoryIds: this.projectCategoryIds$,
eou: this.authService.getEou(),
}).pipe(
switchMap(({ recentValues, mileageCategoryIds, eou }) =>
this.recentlyUsedItemsService.getRecentlyUsedProjects({
recentValues,
eou,
categoryIds: mileageCategoryIds,
})
)
);
const selectedSubCategory$ = this.etxn$.pipe(
switchMap((etxn) =>
iif(
() => etxn.tx.org_category_id,
this.offlineService
.getAllEnabledCategories()
.pipe(
map((subCategories) =>
subCategories
.filter((subCategory) => subCategory.sub_category?.toLowerCase() !== subCategory?.name.toLowerCase())
.find((subCategory) => subCategory?.id === etxn.tx.org_category_id)
)
),
of(null)
)
)
);
const selectedReport$ = this.etxn$.pipe(
switchMap((etxn) =>
iif(
() => etxn.tx.report_id,
this.reports$.pipe(
map((reportOptions) =>
reportOptions.map((res) => res.value).find((reportOption) => reportOption.rp.id === etxn.tx.report_id)
)
),
of(null)
)
)
);
const selectedCostCenter$ = this.etxn$.pipe(
switchMap((etxn) => {
if (etxn.tx.cost_center_id) {
return of(etxn.tx.cost_center_id);
} else {
return forkJoin({
orgSettings: this.offlineService.getOrgSettings(),
costCenters: this.costCenters$,
}).pipe(
map(({ orgSettings, costCenters }) => {
if (orgSettings.cost_centers.enabled) {
if (costCenters.length === 1 && this.mode === 'add') {
return costCenters[0].value.id;
}
}
})
);
}
}),
switchMap((costCenterId) => {
if (costCenterId) {
return this.costCenters$.pipe(
map((costCenters) =>
costCenters.map((res) => res.value).find((costCenter) => costCenter.id === costCenterId)
)
);
} else {
return of(null);
}
})
);
const selectedCustomInputs$ = this.etxn$.pipe(
switchMap((etxn) =>
this.offlineService
.getCustomInputs()
.pipe(
map((customFields) =>
this.customFieldsService.standardizeCustomFields(
[],
this.customInputsService.filterByCategory(customFields, etxn.tx.org_category_id)
)
)
)
)
);
from(this.loaderService.showLoader('Please wait...', 10000))
.pipe(
switchMap(() =>
combineLatest([
this.etxn$,
selectedPaymentMode$,
selectedProject$,
selectedSubCategory$,
this.txnFields$,
selectedReport$,
selectedCostCenter$,
selectedCustomInputs$,
this.mileageConfig$,
defaultPaymentMode$,
orgUserSettings$,
orgSettings$,
this.recentlyUsedValues$,
this.recentlyUsedProjects$,
this.recentlyUsedCostCenters$,
])
),
take(1),
finalize(() => from(this.loaderService.hideLoader()))
)
.subscribe(
([
etxn,
paymentMode,
project,
subCategory,
txnFields,
report,
costCenter,
customInputs,
mileageConfig,
defaultPaymentMode,
orgUserSettings,
orgSettings,
recentValue,
recentProjects,
recentCostCenters,
]) => {
const customInputValues = customInputs.map((customInput) => {
const cpor =
etxn.tx.custom_properties &&
etxn.tx.custom_properties.find((customProp) => customProp.name === customInput.name);
if (customInput.type === 'DATE') {
return {
name: customInput.name,
value: (cpor && cpor.value && moment(new Date(cpor.value)).format('y-MM-DD')) || null,
};
} else {
return {
name: customInput.name,
value: (cpor && cpor.value) || null,
};
}
});
// Check if auto-fills is enabled
const isAutofillsEnabled =
orgSettings.org_expense_form_autofills &&
orgSettings.org_expense_form_autofills.allowed &&
orgSettings.org_expense_form_autofills.enabled &&
orgUserSettings.expense_form_autofills.allowed &&
orgUserSettings.expense_form_autofills.enabled;
// Check if recent projects exist
const doRecentProjectIdsExist =
isAutofillsEnabled &&
recentValue &&
recentValue.recent_project_ids &&
recentValue.recent_project_ids.length > 0;
if (recentProjects && recentProjects.length > 0) {
this.recentProjects = recentProjects.map((item) => ({ label: item.project_name, value: item }));
}
/* Autofill project during these cases:
* 1. Autofills is allowed and enabled
* 2. During add expense - When project field is empty
* 3. During edit expense - When the expense is in draft state and there is no project already added
* 4. When there exists recently used project ids to auto-fill
*/
if (
doRecentProjectIdsExist &&
(!etxn.tx.id || (etxn.tx.id && etxn.tx.state === 'DRAFT' && !etxn.tx.project_id))
) {
const autoFillProject = recentProjects && recentProjects.length > 0 && recentProjects[0];
if (autoFillProject) {
project = autoFillProject;
this.presetProjectId = project.project_id;
}
}
// Check if recent cost centers exist
const doRecentCostCenterIdsExist =
isAutofillsEnabled &&
recentValue &&
recentValue.recent_cost_center_ids &&
recentValue.recent_cost_center_ids.length > 0;
if (recentCostCenters && recentCostCenters.length > 0) {
this.recentCostCenters = recentCostCenters;
}
/* Autofill cost center during these cases:
* 1. Autofills is allowed and enabled
* 2. During add expense - When cost center field is empty
* 3. During edit expense - When the expense is in draft state and there is no cost center already added - optional
* 4. When there exists recently used cost center ids to auto-fill
*/
if (
doRecentCostCenterIdsExist &&
(!etxn.tx.id || (etxn.tx.id && etxn.tx.state === 'DRAFT' && !etxn.tx.cost_center_id))
) {
const autoFillCostCenter = recentCostCenters && recentCostCenters.length > 0 && recentCostCenters[0];
if (autoFillCostCenter) {
costCenter = autoFillCostCenter.value;
this.presetCostCenterId = autoFillCostCenter.value.id;
}
}
// Check if recent location exists
const isRecentLocationPresent =
orgSettings.org_expense_form_autofills &&
orgSettings.org_expense_form_autofills.allowed &&
orgSettings.org_expense_form_autofills.enabled &&
orgUserSettings.expense_form_autofills.allowed &&
orgUserSettings.expense_form_autofills.enabled &&
recentValue &&
recentValue.recent_start_locations &&
recentValue.recent_start_locations.length > 0;
if (isRecentLocationPresent) {
this.presetLocation = recentValue.recent_start_locations[0];
}
this.fg.patchValue({
mileage_vehicle_type: etxn.tx.mileage_vehicle_type,
dateOfSpend: etxn.tx.txn_dt && moment(etxn.tx.txn_dt).format('y-MM-DD'),
paymentMode: paymentMode || defaultPaymentMode,
purpose: etxn.tx.purpose,
route: {
mileageLocations: etxn.tx.locations,
distance: etxn.tx.distance,
roundTrip: etxn.tx.mileage_is_round_trip,
},
project,
billable: etxn.tx.billable,
sub_category: subCategory,
costCenter,
duplicate_detection_reason: etxn.tx.user_reason_for_duplicate_expenses,
report,
});
this.initialFetch = false;
setTimeout(() => {
this.fg.controls.custom_inputs.patchValue(customInputValues);
this.formInitializedFlag = true;
}, 1000);
}
);
}
Example #19
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
BatchOperation = <T extends Obj>(props: IBatchProps<T>) => { const defaultOperationRender = (op: IRowActions) => { return <div>{allWordsFirstLetterUpper(op.name)}</div>; }; const { rowKey = 'id', dataSource, onSelectChange, operations = [], selectedKeys = emptyKeys, operationRender = defaultOperationRender, } = props; const [{ checkAll, indeterminate }, updater, update] = useUpdate({ checkAll: false, indeterminate: false, }); const getKey = React.useCallback((item: T) => (typeof rowKey === 'function' ? rowKey(item) : item[rowKey]), [rowKey]); React.useEffect(() => { const allKeys: React.Key[] = map(dataSource, getKey); const curChosenKeys = intersection(allKeys, selectedKeys); update({ checkAll: curChosenKeys.length === allKeys.length && allKeys.length > 0, indeterminate: curChosenKeys.length !== 0 && curChosenKeys.length < allKeys.length, }); }, [update, dataSource, rowKey, selectedKeys, getKey]); const onCheckAllChange = () => { const allKeys: React.Key[] = map(dataSource, getKey); if (checkAll) { onSelectChange(difference(selectedKeys, allKeys)); } else { onSelectChange(uniq(selectedKeys.concat(allKeys))); } }; const visibleOperations = operations.filter((op) => typeof op.isVisible === 'function' ? op.isVisible(selectedKeys) : true, ); const dropdownMenu = ( <Menu theme="dark" selectable onClick={({ key }) => { const op = visibleOperations.find((a) => a.key === key); if (op) { const result = op.onClick(selectedKeys); if (isPromise(result)) { result.then(() => onSelectChange([])); } else { onSelectChange([]); } } }} > {map(visibleOperations, (opItem) => { return ( <Menu.Item key={opItem.key} disabled={opItem.disabled}> {operationRender(opItem)} </Menu.Item> ); })} </Menu> ); return ( <div className="flex items-center"> <Checkbox className="ml-0.5 mr-2" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll} /> <span className="mr-2"> {`${i18n.t('{name} items selected', { name: selectedKeys?.length || 0, })}`} </span> {visibleOperations.length > 1 ? ( <Dropdown overlay={dropdownMenu} overlayClassName="dice-cp-table-batch-operations" getPopupContainer={(triggerNode) => triggerNode.parentElement as HTMLElement} > <Button className="flex items-center bg-default-06 border-transparent text-default-8"> {i18n.t('Batch Operations')} <ErdaIcon size="18" type="caret-down" className="ml-1 text-default-4" /> </Button> </Dropdown> ) : visibleOperations.length === 1 ? ( <Button className="flex items-center bg-default-06 border-transparent text-default-8" disabled={visibleOperations[0].disabled} onClick={() => { const result = visibleOperations[0].onClick(selectedKeys); if (isPromise(result)) { result.then(() => onSelectChange([])); } else { onSelectChange([]); } }} > {visibleOperations[0].name} </Button> ) : null} </div> ); }
Example #20
Source File: useSelection.tsx From gio-design with Apache License 2.0 | 4 votes |
useSelection = <RecordType,>( data: RecordType[], rowSelection: RowSelection<RecordType> | undefined, config: { rowKey?: TableProps<RecordType>['rowKey']; } ): [(columns: ColumnsType<RecordType>) => ColumnsType<RecordType>, Key[]] => { const { onChange, selectedRowKeys, columnWidth = 52, fixed, getCheckboxProps } = rowSelection || {}; const { rowKey } = config; const [localSelectedRowKeys, setLocalSelectedRowKeys] = useControlledState<Key[]>(selectedRowKeys, []); // 获取当前页所有row的key const currentPageRowKeys = useMemo(() => flatten(data.map((item) => getRowKey(item, rowKey))), [data]); const isAllChecked = useMemo( () => intersection(localSelectedRowKeys, currentPageRowKeys).length === currentPageRowKeys.length, [currentPageRowKeys, localSelectedRowKeys] ); const atLeastOneChecked = useMemo( () => intersection(currentPageRowKeys, localSelectedRowKeys).length > 0, [currentPageRowKeys, localSelectedRowKeys] ); const isPartChecked = useMemo(() => !isAllChecked && atLeastOneChecked, [isAllChecked, atLeastOneChecked]); const isAllDisabled = useMemo( () => data.every((item) => getCheckboxProps?.(item)?.disabled), [data, getCheckboxProps] ); const isRowAllSelected = (keys: any) => { const childrenKeys = Array.isArray(keys) ? keys.slice(1, keys.length) : [keys]; return childrenKeys.every((keyItem) => localSelectedRowKeys.includes(keyItem)); }; const isRowPartSelected = (keys: any) => Array.isArray(keys) ? keys.slice(1, keys.length).some((keyItem) => localSelectedRowKeys.includes(keyItem)) : false; const allDisabledKey: string[] = []; // 获取所有的disabled选项的key const getAllDisabledKey = (dataTree: any) => { dataTree.forEach((item: any) => { if (isFunction(getCheckboxProps) && getCheckboxProps(item).disabled) { Array.isArray(getRowKey(item, rowKey)) ? allDisabledKey.push(...(getRowKey(item, rowKey) as any)) : allDisabledKey.push(getRowKey(item, rowKey) as any); } else if (item.children) { getAllDisabledKey(item.children); } }); }; // 所有的子元素全部disabled const isParentDisabled = (keys: Key | Key[]) => Array.isArray(keys) ? keys.slice(1).every((key) => allDisabledKey.includes(`${key}`)) : false; // 父元素disabled const isChildDisabled = (keys: Key | Key[]) => (Array.isArray(keys) ? false : allDisabledKey.includes(`${keys}`)); const getSelectRows = useCallback( (_selectedRowKeys) => data.filter((item) => _selectedRowKeys.includes(getRowKey(item, rowKey))), [data] ); // 获取父节点的keys const getParentKeys = (dataTree: any, keys: Key | Key[]): Key[] => { if (!Array.isArray(keys)) { if (data.some((item: any) => item.key === keys)) { return []; } // eslint-disable-next-line no-restricted-syntax for (let item of dataTree) { if (item.children) { if (item.children.some((child: any) => child.key === keys)) { return getRowKey(item, rowKey) as any; } return getParentKeys(item.children, keys); } } } return []; }; // 更新parent的check状态 const updateParentCheck = (selectedKeys: Key[], childKey: Key | Key[]): any => { const parentKeys = getParentKeys(data, childKey); if (parentKeys.length) { /** @todo: 无法执行此代码 */ // if (parentKeys.slice(1).every((key) => selectedKeys.includes(key))) { // // 向上递归更新状态,直至根结点 // return updateParentCheck(flattenDeep(union(selectedKeys, flattenDeep(parentKeys))), parentKeys[0]); // } return selectedKeys.filter((key) => key !== parentKeys[0]); } return selectedKeys; }; const selectionColumn: ColumnType<RecordType> = { title: ( <Checkbox checked={atLeastOneChecked} indeterminate={isPartChecked} onClick={(e) => e.stopPropagation()} onChange={(e) => { getAllDisabledKey(data); const latestLocalSelectedRowKeys = e.target.checked ? flattenDeep(difference(union(localSelectedRowKeys, currentPageRowKeys), allDisabledKey)) : flattenDeep(difference(localSelectedRowKeys, currentPageRowKeys, allDisabledKey)); setLocalSelectedRowKeys(latestLocalSelectedRowKeys); onChange?.(latestLocalSelectedRowKeys, getSelectRows(latestLocalSelectedRowKeys)); }} disabled={isAllDisabled} /> ), fixed, key: 'selection', align: 'center', width: columnWidth, render: (...rest) => { getAllDisabledKey(data); const key = getRowKey(rest[1], rowKey); const thisCheckboxProps = getCheckboxProps?.(rest[1]) || {}; const { tooltipProps, disabled, ...restCheckboxProps } = thisCheckboxProps; const contentNode = ( <div> <Checkbox {...restCheckboxProps} disabled={disabled || isParentDisabled(key) || isChildDisabled(key)} indeterminate={!isRowAllSelected(key) && isRowPartSelected(key)} checked={ Array.isArray(key) ? key.some((keyItem) => localSelectedRowKeys.includes(keyItem)) : localSelectedRowKeys.includes(key) } onClick={(e) => e.stopPropagation()} onChange={(e) => { getAllDisabledKey(data); const latestLocalSelectedRowKeys = e.target.checked ? flattenDeep(difference(union(localSelectedRowKeys, flattenDeep([key])), allDisabledKey)) : flattenDeep(difference(localSelectedRowKeys, flattenDeep([key]), allDisabledKey)); setLocalSelectedRowKeys(latestLocalSelectedRowKeys); const updatedSelectedRowKeys = updateParentCheck(latestLocalSelectedRowKeys, key); setLocalSelectedRowKeys(updatedSelectedRowKeys); onChange?.(updatedSelectedRowKeys, getSelectRows(updatedSelectedRowKeys)); }} > {disabled ? null : undefined} </Checkbox> </div> ); return disabled ? ( <Tooltip placement="topLeft" arrowPointAtCenter {...tooltipProps}> <span>{contentNode}</span> </Tooltip> ) : ( <Tooltip placement="topLeft" arrowPointAtCenter {...tooltipProps}> {contentNode} </Tooltip> ); }, }; const transformSelectionPipeline = useCallback( (columns: ColumnsType<RecordType>) => (!isUndefined(rowSelection) ? [selectionColumn, ...columns] : columns), [selectionColumn, rowSelection] ); return [transformSelectionPipeline, localSelectedRowKeys]; }
Example #21
Source File: base-list.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
BatchOperation = <T extends unknown>(props: IBatchProps<T>) => { const { rowKey, dataSource, onSelectChange, execOperation, selectedRowKeys = emptyKeys, batchRowsHandle } = props; const selectableData = React.useMemo(() => dataSource.filter((item) => item.selectable !== false), [dataSource]); const [{ checkAll, indeterminate }, updater, update] = useUpdate({ checkAll: false, indeterminate: false, }); React.useEffect(() => { const allKeys = map(selectableData, rowKey); const curChosenKeys = intersection(allKeys, selectedRowKeys); update({ checkAll: !!(curChosenKeys.length && curChosenKeys.length === allKeys.length), indeterminate: !!(curChosenKeys.length && curChosenKeys.length < allKeys.length), }); }, [update, selectableData, rowKey, selectedRowKeys]); const optMenus = React.useMemo(() => { const { options } = batchRowsHandle?.serverData; return options.map((mItem) => { const { allowedRowIDs, forbiddenRowIDs } = mItem; const validChosenOpt = intersection(selectedRowKeys, allowedRowIDs || selectedRowKeys).length === selectedRowKeys.length && intersection(selectedRowKeys, forbiddenRowIDs).length === 0; const disabledProps = selectedRowKeys?.length ? { disabled: has(mItem, 'disabled') ? mItem.disabled : !validChosenOpt, disabledTip: i18n.t('exist item which not match operation'), } : { disabled: true, disabledTip: i18n.t('no items selected') }; const reMenu = { ...mItem, ...disabledProps, }; return reMenu; }); }, [batchRowsHandle, selectedRowKeys]); const dropdownMenu = ( <Menu theme="dark"> {map(optMenus, (mItem) => { return ( <Menu.Item key={mItem.id} disabled={!!mItem.disabled}> <OperationAction operation={{ ...mItem, ...batchRowsHandle }} onClick={() => execOperation({ key: 'batchRowsHandle', ...batchRowsHandle, clientData: { dataRef: mItem, selectedOptionsID: mItem.id, selectedRowIDs: selectedRowKeys }, }) } tipProps={{ placement: 'right' }} > <div className="flex-h-center"> {mItem.icon ? ( <ErdaIcon type={typeof mItem.icon === 'string' ? mItem.icon : mItem.icon.type} className="mr-1" /> ) : null} <span>{mItem.text}</span> </div> </OperationAction> </Menu.Item> ); })} </Menu> ); const onCheckAllChange = () => { const allKeys = map(selectableData, rowKey); if (checkAll) { onSelectChange(difference(selectedRowKeys, allKeys)); } else { onSelectChange(compact(selectedRowKeys.concat(allKeys))); } }; return ( <div className="flex items-center"> <Checkbox className="mr-2" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll} /> <span className="mr-2">{`${i18n.t('{name} items selected', { name: selectedRowKeys?.length || 0, })}`}</span> <Dropdown overlay={dropdownMenu} zIndex={1000}> <Button className="flex items-center"> {i18n.t('Batch Operations')} <ErdaIcon size="18" type="caret-down" className="ml-1 text-default-4" /> </Button> </Dropdown> </div> ); }
Example #22
Source File: route.ts From raydium-sdk with GNU General Public License v3.0 | 4 votes |
static async makeSwapTransaction(params: RouteSwapTransactionParams) {
const { connection, fromPoolKeys, toPoolKeys, userKeys, amountIn, amountOut, fixedSide, config } = params;
const { tokenAccounts, owner } = userKeys;
logger.debug("amountIn:", amountIn);
logger.debug("amountOut:", amountOut);
logger.assertArgument(
!amountIn.isZero() && !amountOut.isZero(),
"amounts must greater than zero",
"currencyAmounts",
{
amountIn: amountIn.toFixed(),
amountOut: amountOut.toFixed(),
},
);
const { bypassAssociatedCheck } = {
// default
...{ bypassAssociatedCheck: false },
// custom
...config,
};
// handle currency in & out (convert SOL to WSOL)
const tokenIn = amountIn instanceof TokenAmount ? amountIn.token : Token.WSOL;
const tokenOut = amountOut instanceof TokenAmount ? amountOut.token : Token.WSOL;
const tokenAccountIn = await this._selectTokenAccount({
tokenAccounts,
mint: tokenIn.mint,
owner,
config: { associatedOnly: false },
});
const tokenAccountOut = await this._selectTokenAccount({
tokenAccounts,
mint: tokenOut.mint,
owner,
});
const fromPoolMints = [fromPoolKeys.baseMint.toBase58(), fromPoolKeys.quoteMint.toBase58()];
const toPoolMints = [toPoolKeys.baseMint.toBase58(), toPoolKeys.quoteMint.toBase58()];
const intersectionMints = intersection(fromPoolMints, toPoolMints);
const _middleMint = intersectionMints[0];
const middleMint = new PublicKey(_middleMint);
const tokenAccountMiddle = await this._selectTokenAccount({
tokenAccounts,
mint: middleMint,
owner,
});
const [amountInRaw, amountOutRaw] = [amountIn.raw, amountOut.raw];
const setupInstructions: TransactionInstruction[] = [];
const setupSigners: Signer[] = [];
const swapInstructions: TransactionInstruction[] = [];
const _tokenAccountIn = await this._handleTokenAccount({
connection,
side: "in",
amount: amountInRaw,
mint: tokenIn.mint,
tokenAccount: tokenAccountIn,
owner,
frontInstructions: setupInstructions,
signers: setupSigners,
bypassAssociatedCheck,
});
const _tokenAccountOut = await this._handleTokenAccount({
connection,
side: "out",
amount: 0,
mint: tokenOut.mint,
tokenAccount: tokenAccountOut,
owner,
frontInstructions: setupInstructions,
signers: setupSigners,
bypassAssociatedCheck,
});
const _tokenAccountMiddle = await this._handleTokenAccount({
connection,
side: "in",
amount: 0,
mint: middleMint,
tokenAccount: tokenAccountMiddle,
owner,
frontInstructions: setupInstructions,
signers: setupSigners,
bypassAssociatedCheck,
});
swapInstructions.push(
...this.makeSwapInstruction({
fromPoolKeys,
toPoolKeys,
userKeys: {
inTokenAccount: _tokenAccountIn,
outTokenAccount: _tokenAccountOut,
middleTokenAccount: _tokenAccountMiddle,
middleStatusAccount: await this.getAssociatedMiddleStatusAccount({
programId: ROUTE_PROGRAM_ID_V1,
fromPoolId: fromPoolKeys.id,
middleMint,
owner,
}),
owner,
},
amountIn: amountInRaw,
amountOut: amountOutRaw,
fixedSide,
}),
);
let setupTransaction: UnsignedTransactionAndSigners | null = null;
let swapTransaction: UnsignedTransactionAndSigners | null = null;
if (setupInstructions.length > 0) {
setupTransaction = {
transaction: new Transaction().add(...setupInstructions),
signers: setupSigners,
};
}
if (swapInstructions.length > 0) {
swapTransaction = {
transaction: new Transaction().add(...swapInstructions),
signers: [],
};
}
return { setupTransaction, swapTransaction };
}
Example #23
Source File: route.ts From raydium-sdk with GNU General Public License v3.0 | 4 votes |
// static makeSwapInFixedOutInstruction() {}
// static makeSwapOutFixedOutInstruction() {}
/* ================= compute data ================= */
static computeAmountOut({
fromPoolKeys,
toPoolKeys,
fromPoolInfo,
toPoolInfo,
amountIn,
currencyOut,
slippage,
}: RouteComputeAmountOutParams) {
const { swap: fromPoolSwapEnabled } = Liquidity.getEnabledFeatures(fromPoolInfo);
const { swap: toPoolSwapEnabled } = Liquidity.getEnabledFeatures(toPoolInfo);
logger.assertArgument(fromPoolSwapEnabled && toPoolSwapEnabled, "pools swap not enabled", "pools", {
fromPoolKeys,
toPoolKeys,
fromPoolInfo,
toPoolInfo,
});
const tokenIn = amountIn instanceof TokenAmount ? amountIn.token : Token.WSOL;
const tokenOut = currencyOut instanceof Token ? currencyOut : Token.WSOL;
logger.assertArgument(
Liquidity.includesToken(tokenIn, fromPoolKeys) && Liquidity.includesToken(tokenOut, toPoolKeys),
"pools cannot be routed",
"pools",
{
fromPoolKeys,
toPoolKeys,
},
);
const fromPoolMints = [fromPoolKeys.baseMint.toBase58(), fromPoolKeys.quoteMint.toBase58()];
const toPoolMints = [toPoolKeys.baseMint.toBase58(), toPoolKeys.quoteMint.toBase58()];
const mints = [...fromPoolMints, ...toPoolMints];
const decimals = [
fromPoolInfo.baseDecimals,
fromPoolInfo.quoteDecimals,
toPoolInfo.baseDecimals,
toPoolInfo.quoteDecimals,
];
const mintIn = tokenIn.mint.toBase58();
const mintOut = tokenOut.mint.toBase58();
const xorMints = xor(fromPoolMints, toPoolMints);
logger.assertArgument(
xorMints.length === 2 && xorMints.includes(mintIn) && xorMints.includes(mintOut),
"xor tokens not match",
"pools",
{
fromPoolKeys,
toPoolKeys,
},
);
const intersectionMints = intersection(fromPoolMints, toPoolMints);
logger.assertArgument(intersectionMints.length === 1, "cannot found middle token of two pools", "pools", {
fromPoolKeys,
toPoolKeys,
});
const _middleMint = intersectionMints[0];
const index = mints.indexOf(_middleMint);
// logger.assertArgument(index !== -1, "cannot found middle token", "pools", {
// fromPoolKeys,
// toPoolKeys,
// });
const middleMintDecimals = decimals[index];
const middleMint = new PublicKey(_middleMint);
const middleToken = new Token(middleMint, middleMintDecimals);
logger.debug("from pool:", fromPoolKeys);
logger.debug("to pool:", toPoolKeys);
logger.debug("intersection mints:", intersectionMints);
logger.debug("xor mints:", xorMints);
logger.debug("middleMint:", _middleMint);
// TODO slippage and amount out
const {
amountOut: middleAmountOut,
minAmountOut: minMiddleAmountOut,
priceImpact: firstPriceImpact,
fee: firstFee,
} = Liquidity.computeAmountOut({
poolKeys: fromPoolKeys,
poolInfo: fromPoolInfo,
amountIn,
currencyOut: middleToken,
slippage,
});
const {
amountOut,
minAmountOut,
priceImpact: secondPriceImpact,
fee: secondFee,
} = Liquidity.computeAmountOut({
poolKeys: toPoolKeys,
poolInfo: toPoolInfo,
amountIn: minMiddleAmountOut,
currencyOut,
slippage,
});
let executionPrice: Price | null = null;
const amountInRaw = amountIn.raw;
const amountOutRaw = amountOut.raw;
const currencyIn = amountIn instanceof TokenAmount ? amountIn.token : amountIn.currency;
if (!amountInRaw.isZero() && !amountOutRaw.isZero()) {
executionPrice = new Price(currencyIn, amountInRaw, currencyOut, amountOutRaw);
logger.debug("executionPrice:", `1 ${currencyIn.symbol} ≈ ${executionPrice.toFixed()} ${currencyOut.symbol}`);
logger.debug(
"executionPrice invert:",
`1 ${currencyOut.symbol} ≈ ${executionPrice.invert().toFixed()} ${currencyIn.symbol}`,
);
}
return {
// middleAmountOut,
// minMiddleAmountOut,
amountOut,
minAmountOut,
executionPrice,
priceImpact: firstPriceImpact.add(secondPriceImpact),
fee: [firstFee, secondFee],
};
}
Example #24
Source File: table.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
BatchOperation = <T extends unknown>(props: IBatchProps<T>) => { const { rowKey, dataSource, onSelectChange, execOperation, selectedRowKeys = emptyArr, batchRowsHandle } = props; const [{ checkAll, indeterminate }, updater, update] = useUpdate({ checkAll: false, indeterminate: false, }); React.useEffect(() => { const allKeys = map(dataSource, rowKey); const curChosenKeys = intersection(allKeys, selectedRowKeys); update({ checkAll: !!(curChosenKeys.length && curChosenKeys.length === allKeys.length), indeterminate: !!(curChosenKeys.length && curChosenKeys.length < allKeys.length), }); }, [update, dataSource, rowKey, selectedRowKeys]); const optMenus = React.useMemo(() => { const { options } = batchRowsHandle?.serverData; return options.map((mItem) => { const { allowedRowIDs, forbiddenRowIDs } = mItem; const validChosenOpt = intersection(selectedRowKeys, allowedRowIDs || selectedRowKeys).length === selectedRowKeys.length && intersection(selectedRowKeys, forbiddenRowIDs).length === 0; const disabledProps = selectedRowKeys?.length ? { disabled: has(mItem, 'disabled') ? mItem.disabled : !validChosenOpt, disabledTip: i18n.t('exist item which not match operation'), } : { disabled: true, disabledTip: i18n.t('no items selected') }; const reMenu = { ...mItem, ...disabledProps, }; return reMenu; }); }, [batchRowsHandle, selectedRowKeys]); const dropdownMenu = ( <Menu theme="dark"> {map(optMenus, (mItem) => { return ( <Menu.Item key={mItem.id} disabled={!!mItem.disabled}> <OperationAction operation={{ ...mItem, ...batchRowsHandle }} onClick={() => execOperation({ key: 'batchRowsHandle', ...batchRowsHandle, clientData: { dataRef: mItem, selectedOptionsID: mItem.id, selectedRowIDs: selectedRowKeys }, }) } tipProps={{ placement: 'right' }} > <div>{mItem.text}</div> </OperationAction> </Menu.Item> ); })} </Menu> ); const onCheckAllChange = () => { const allKeys = map(dataSource, rowKey); if (checkAll) { onSelectChange(difference(selectedRowKeys, allKeys)); } else { onSelectChange(compact(selectedRowKeys.concat(allKeys))); } }; return ( <div className="flex items-center"> <Checkbox className="mx-2" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll} /> <span className="mr-2">{`${i18n.t('{name} items selected', { name: selectedRowKeys?.length || 0, })}`}</span> <Dropdown overlay={dropdownMenu} zIndex={1000} overlayClassName="dice-cp-table-batch-operations" getPopupContainer={(triggerNode) => triggerNode.parentElement as HTMLElement} > <Button className="flex items-center"> {i18n.t('Batch Operations')} <ErdaIcon size="18" type="caret-down" className="ml-1 text-black-2" /> </Button> </Dropdown> </div> ); }
Example #25
Source File: Entries.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
// eslint-disable-next-line complexity
export function Entries({
source,
isConfirmed,
isCancelled,
account,
loading,
totalCount,
currentPage,
onChangePage,
}: EntriesProps) {
const { t } = useTranslation();
const isInjected = useIsInjected();
const { network } = useApi();
const renderAction = useCallback(
// eslint-disable-next-line complexity
(row: Entry) => {
if (row.status) {
return <span>{t(`status.${row.status}`)}</span>;
}
const actions: TxActionType[] = [];
// eslint-disable-next-line react/prop-types
const pairs = (account.meta?.addressPair ?? []) as AddressPair[];
const injectedAccounts: string[] = pairs.filter((pair) => isInjected(pair.address)).map((pair) => pair.address);
if (injectedAccounts.includes(row.depositor)) {
actions.push('cancel');
}
const localAccountInMultisigPairList = intersection(
injectedAccounts,
pairs.map((pair) => pair.address)
);
const approvedLocalAccounts = intersection(localAccountInMultisigPairList, row.approvals);
if (approvedLocalAccounts.length !== localAccountInMultisigPairList.length) {
actions.push('approve');
}
if (actions.length === 0) {
// eslint-disable-next-line react/prop-types
if (row.approvals && row.approvals.length === account.meta.threshold) {
actions.push('pending');
}
}
return (
<Space>
{actions.map((action) => {
if (action === 'pending') {
return (
<Button key={action} disabled>
{t(action)}
</Button>
);
} else if (action === 'approve') {
return <TxApprove key={action} entry={row} />;
} else {
return <TxCancel key={action} entry={row} />;
}
})}
</Space>
);
},
[account.meta?.addressPair, account.meta.threshold, isInjected, t]
);
const columns: ColumnsType<Entry> = [
{
title: t(isConfirmed || isCancelled ? 'extrinsic_index' : 'call_data'),
dataIndex: isConfirmed || isCancelled ? 'extrinsicIdx' : 'hexCallData',
width: 300,
align: 'left',
// eslint-disable-next-line complexity
render(data: string) {
let extrinsicHeight = '';
let extrinsicIndex = '';
if ((isConfirmed || isCancelled) && data.split('-').length > 1) {
extrinsicHeight = data.split('-')[0];
extrinsicIndex = data.split('-')[1];
}
return !(isConfirmed || isCancelled) ? (
<>
<Typography.Text copyable={!isEmpty(data) && { text: data }}>
{!isEmpty(data)
? // ? `${data.substring(0, CALL_DATA_LENGTH)}${data.length > CALL_DATA_LENGTH ? '...' : ''}`
toShortString(data, CALL_DATA_LENGTH)
: '-'}
</Typography.Text>
</>
) : (
<SubscanLink extrinsic={{ height: extrinsicHeight, index: extrinsicIndex }}>{data}</SubscanLink>
);
},
},
{
title: t('actions'),
dataIndex: 'callDataJson',
align: 'left',
render: renderMethod,
},
{
title: t('progress'),
dataIndex: 'approvals',
align: 'left',
render(approvals: string[]) {
const cur = (approvals && approvals.length) || 0;
return cur + '/' + account.meta.threshold;
},
},
{
title: t('status.index'),
key: 'status',
align: 'left',
render: (_, row) => renderAction(row),
},
];
const expandedRowRender = (entry: Entry) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const progressColumnsNested: ColumnsType<any> = [
{ dataIndex: 'name', width: 100 },
{
width: 400,
dataIndex: 'address',
render: (address) => (
<Space size="middle">
<BaseIdentityIcon theme="polkadot" size={32} value={address} />
<SubscanLink address={address} copyable />
</Space>
),
},
{
width: 250,
key: 'status',
render: (_, pair) => renderMemberStatus(entry, pair, network, !isCancelled && !isConfirmed),
},
];
// const callDataJson = entry.callData?.toJSON() ?? {};
const args: Required<ArgObj>[] = ((entry.meta?.args ?? []) as Required<ArgObj>[]).map((arg) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = (entry.callDataJson?.args as any)[arg?.name ?? ''];
return { ...arg, value };
});
return (
<div className="record-expand bg-gray-100 py-3 px-5">
<div className=" text-black-800 text-base leading-none mb-3">{t('progress')}</div>
<div className="members">
<Table
columns={progressColumnsNested}
dataSource={account.meta.addressPair as { key: string; name: string; address: string }[]}
pagination={false}
bordered
rowKey="address"
showHeader={false}
className="mb-4 mx-4"
/>
</div>
<div className=" text-black-800 text-base leading-none my-3">{t('parameters')}</div>
<Args
args={args}
className="mb-4 mx-4"
section={entry.callDataJson?.section}
method={entry.callDataJson?.method}
/>
</div>
);
};
return (
<div className="record-table">
<Table
loading={loading}
dataSource={source}
columns={columns}
rowKey={(record) => record.callHash ?? (record.blockHash as string)}
pagination={
isConfirmed || isCancelled
? {
total: totalCount,
pageSize: 10,
current: currentPage,
onChange: onChangePage,
}
: false
}
expandable={{
expandedRowRender,
expandIcon: genExpandIcon(),
expandIconColumnIndex: 4,
}}
className="lg:block hidden"
></Table>
<Space direction="vertical" className="lg:hidden block">
{source.map((data) => {
const { address, hash, callData, approvals } = data;
const approvedCount = approvals.length || 0;
const threshold = (account.meta.threshold as number) || 1;
return (
<Collapse key={address} expandIcon={() => <></>} className="wallet-collapse">
<Panel
header={
<Space direction="vertical" className="w-full mb-4">
<Typography.Text className="mr-4" copyable>
{hash}
</Typography.Text>
<div className="flex items-center">
<Typography.Text>{renderMethod(callData)}</Typography.Text>
<Progress
/* eslint-disable-next-line no-magic-numbers */
percent={parseInt(String((approvedCount / threshold) * 100), 10)}
steps={threshold}
className="ml-4"
/>
</div>
</Space>
}
key={address}
extra={renderAction(data)}
className="overflow-hidden mb-4"
>
<MemberList
data={account}
statusRender={(pair) => renderMemberStatus(data, pair, network, !isCancelled && !isConfirmed)}
/>
</Panel>
</Collapse>
);
})}
{!source.length && <Empty />}
</Space>
</div>
);
}
Example #26
Source File: table.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
BatchOperation = (props: IBatchProps) => {
const {
rowKey,
dataSource,
onSelectChange,
operations,
chosenItems,
execOperation,
selectedRowKeys = emptyArr,
batchOperations,
} = props;
const [{ checkAll, indeterminate }, updater, update] = useUpdate({
checkAll: false,
indeterminate: false,
});
React.useEffect(() => {
const allKeys = map(dataSource, rowKey);
const curChosenKeys = intersection(allKeys, selectedRowKeys);
update({
checkAll: !!(curChosenKeys.length && curChosenKeys.length === allKeys.length),
indeterminate: !!(curChosenKeys.length && curChosenKeys.length < allKeys.length),
});
}, [update, dataSource, rowKey, selectedRowKeys]);
const optMenus = React.useMemo(() => {
const fullMenus = batchOperations.map((btItem) => find(operations, (opItem) => opItem.key === btItem));
const chosenOpts = intersection(...map(chosenItems, 'batchOperations'));
return fullMenus.map((mItem) => {
const disabledProps = selectedRowKeys?.length
? {
disabled: has(mItem, 'disabled') ? mItem.disabled : !chosenOpts.includes(mItem.key),
disabledTip: i18n.t('exist item which not match operation'),
}
: { disabled: true, disabledTip: i18n.t('no items selected') };
const reMenu = {
...mItem,
...disabledProps,
};
return reMenu;
});
}, [batchOperations, chosenItems, operations, selectedRowKeys]);
const dropdownMenu = (
<Menu theme="dark">
{map(optMenus, (mItem) => {
return (
<Menu.Item key={mItem.key} disabled={mItem.disabled}>
<OperationAction
operation={mItem}
onClick={() => execOperation(mItem, { selectedRowKeys })}
tipProps={{ placement: 'right' }}
>
<div>{mItem.text}</div>
</OperationAction>
</Menu.Item>
);
})}
</Menu>
);
const onCheckAllChange = () => {
const allKeys = map(dataSource, rowKey);
if (checkAll) {
onSelectChange(difference(selectedRowKeys, allKeys));
} else {
onSelectChange(compact(selectedRowKeys.concat(allKeys)));
}
};
return (
<div className="flex items-center">
<Checkbox className="mx-2" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll} />
<span className="mr-2">{`${i18n.t('{name} items selected', {
name: selectedRowKeys?.length || 0,
})}`}</span>
<Dropdown
overlay={dropdownMenu}
zIndex={1000}
overlayClassName="dice-cp-table-batch-operations"
getPopupContainer={(triggerNode) => triggerNode.parentElement as HTMLElement}
>
<Button className="flex items-center bg-default-06 border-transparent text-default-8">
{i18n.t('Batch Operations')}
<ErdaIcon size="18" type="caret-down" className="ml-1 text-default-4" />
</Button>
</Dropdown>
</div>
);
}