chore: model access statement

This commit is contained in:
jialin
2025-10-13 15:21:17 +08:00
parent 663b6c7b4e
commit bb7c8e9c85
18 changed files with 976 additions and 357 deletions
+1
View File
@@ -29,6 +29,7 @@ interface AutoTooltipProps extends Omit<TagProps, 'title'> {
}
const StyledTag = styled(Tag)`
margin: 0;
&.tag-filled {
border: none;
background-color: var(--ant-color-fill-secondary);
@@ -0,0 +1,121 @@
import { isNotEmptyValue } from '@/utils/index';
import { useIntl } from '@umijs/max';
import type { SelectProps } from 'antd';
import { Form } from 'antd';
import { cloneDeep } from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import SimpleSelect from './simple-select';
import { SealFormItemProps } from './types';
import Wrapper from './wrapper';
import SelectWrapper from './wrapper/select';
const SealSelect: React.FC<
SelectProps & SealFormItemProps & { showTags?: boolean }
> = (props) => {
const {
label,
placeholder,
children,
required,
description,
options,
allowNull,
isInFormItems = true,
notFoundContent = null,
...rest
} = props;
const intl = useIntl();
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
let status = '';
// the status can be controlled by Form.Item
if (isInFormItems) {
const statusData = Form?.Item?.useStatus?.();
status = statusData?.status || '';
} else {
status = props.status || '';
}
const _options = useMemo(() => {
if (!options?.length) {
return [];
}
const list = cloneDeep(options);
return list.map((item: any) => {
if (item.locale) {
item.label = intl.formatMessage({ id: item.label as string });
}
return item;
});
}, [options, intl]);
useEffect(() => {
if (
isNotEmptyValue(props.value) ||
(allowNull && (props.value === null || props.value === undefined))
) {
setIsFocus(true);
}
}, [props.value, allowNull]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (val: any, options: any) => {
if (isNotEmptyValue(val) || (allowNull && val === null)) {
setIsFocus(true);
} else {
setIsFocus(false);
}
props.onChange?.(val, options);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
if (allowNull && props.value === null) {
setIsFocus(true);
} else if (!props.value) {
setIsFocus(false);
}
props.onBlur?.(e);
};
return (
<SelectWrapper>
<Wrapper
className="seal-select-wrapper"
status={status}
label={label}
isFocus={isFocus}
required={required}
description={description}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<SimpleSelect
{...rest}
ref={inputRef}
options={children ? null : _options}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onChange={handleChange}
notFoundContent={notFoundContent}
>
{children}
</SimpleSelect>
</Wrapper>
</SelectWrapper>
);
};
export default SealSelect;
+208 -187
View File
@@ -2,7 +2,7 @@ import { useIntl } from '@umijs/max';
import type { SelectProps } from 'antd';
import { Checkbox, Tag } from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import React, { useEffect } from 'react';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import styled from 'styled-components';
import AutoTooltip from '../auto-tooltip';
import BaseSelect from './base/select';
@@ -43,218 +43,239 @@ const TagWrapper = styled(Tag)`
border-radius: 12px;
`;
const SimpleSelect: React.FC<SelectProps> = (props) => {
const intl = useIntl();
const { options = [], ...restProps } = props;
const SimpleSelect: React.FC<SelectProps & { ref?: any; showTags?: boolean }> =
forwardRef((props, ref) => {
const intl = useIntl();
const { options = [], showTags, ...restProps } = props;
const [allSelection, setAllSelection] = React.useState<{
checked: boolean;
indeterminate: boolean;
}>({
checked: false,
indeterminate: false
});
const [optionsList, setOptionsList] = React.useState<any[]>(options || []);
const selectRef = React.useRef<any>(null);
useEffect(() => {
setOptionsList(options || []);
}, [options]);
const optionRender = (option: any, info: any) => {
const { value, label } = option;
return (
<OptionWrapper>
{restProps.value?.includes(value) ? (
<Checkbox checked></Checkbox>
) : (
<Checkbox></Checkbox>
)}
<AutoTooltip ghost>{label}</AutoTooltip>
</OptionWrapper>
);
};
const handleOnCheckboxChange = (e: CheckboxChangeEvent) => {
const isChecked = e.target.checked;
const allValues = optionsList?.map((opt: any) => opt.value) || [];
setAllSelection({
checked: isChecked,
const [allSelection, setAllSelection] = React.useState<{
checked: boolean;
indeterminate: boolean;
}>({
checked: false,
indeterminate: false
});
const [optionsList, setOptionsList] = React.useState<any[]>(options || []);
const selectRef = React.useRef<any>(null);
const selectorRef = React.useRef<any>(null);
let allSelectedValues = [...(restProps.value || [])];
useEffect(() => {
setOptionsList(options || []);
}, [options]);
if (isChecked) {
// Select all options
allSelectedValues = Array.from(
new Set([...allSelectedValues, ...allValues])
const optionRender = (option: any, info: any) => {
const { value, label } = option;
return (
<OptionWrapper>
{restProps.value?.includes?.(value) ? (
<Checkbox checked></Checkbox>
) : (
<Checkbox></Checkbox>
)}
<AutoTooltip ghost>{label}</AutoTooltip>
</OptionWrapper>
);
} else {
// Deselect all options
allSelectedValues = allSelectedValues.filter(
(value) => !allValues.includes(value)
);
}
};
restProps.onChange?.(allSelectedValues, optionsList || []);
};
const handleOnCheckboxChange = (e: CheckboxChangeEvent) => {
const isChecked = e.target.checked;
const allValues = optionsList?.map((opt: any) => opt.value) || [];
const dropdownRender = (originPanel: React.ReactNode) => {
return (
<DropdownWrapper>
{restProps.mode === 'multiple' && (
<SelectAllWrapper>
<Checkbox
checked={allSelection.checked}
indeterminate={allSelection.indeterminate}
onChange={handleOnCheckboxChange}
>
{intl.formatMessage({ id: 'common.checbox.all' })}
</Checkbox>
</SelectAllWrapper>
)}
{originPanel}
</DropdownWrapper>
);
};
const handleOnChange = (value: any, option: any) => {
const selectedValues = Array.isArray(value) ? value : [value];
const allSelected = optionsList?.map((opt: any) => opt.value) || [];
const isAllSelected = selectedValues.length === allSelected?.length;
setAllSelection({
checked: isAllSelected,
indeterminate: !isAllSelected && selectedValues.length > 0
});
restProps.onChange?.(selectedValues, option);
};
const filterOption = (inputValue: string, option: any) => {
if (!option || !option.label) return false;
return option.label.toLowerCase().includes(inputValue.toLowerCase());
};
const checkAllSelection = (list: Global.BaseOption<string | number>[]) => {
if (
!restProps.value ||
!Array.isArray(restProps.value) ||
list.length === 0
) {
setAllSelection({
checked: false,
checked: isChecked,
indeterminate: false
});
return;
}
const selectedValues = new Set(restProps.value);
const allValues = list?.map((opt: any) => opt.value) || [];
const isAllSelected = allValues.every((val: any) =>
selectedValues.has(val)
);
let allSelectedValues = [...(restProps.value || [])];
const isSomeSelected = allValues.some((val: any) =>
selectedValues.has(val)
);
if (isChecked) {
// Select all options
allSelectedValues = Array.from(
new Set([...allSelectedValues, ...allValues])
);
} else {
// Deselect all options
allSelectedValues = allSelectedValues.filter(
(value) => !allValues.includes(value)
);
}
setAllSelection({
checked: isAllSelected,
indeterminate: isSomeSelected && !isAllSelected
});
};
restProps.onChange?.(allSelectedValues, optionsList || []);
};
const TagRender = (props: any) => {
const { label } = props;
const count = props.isMaxTag ? label.slice(0, -3).slice(1) : label;
const dropdownRender = (originPanel: React.ReactNode) => {
return (
<DropdownWrapper>
{restProps.mode === 'multiple' && (
<SelectAllWrapper>
<Checkbox
checked={allSelection.checked}
indeterminate={allSelection.indeterminate}
onChange={handleOnCheckboxChange}
>
{intl.formatMessage({ id: 'common.checbox.all' })}
</Checkbox>
</SelectAllWrapper>
)}
{originPanel}
</DropdownWrapper>
);
};
return (
<TagWrapper
bordered={false}
style={{
height: 24,
backgroundColor: 'var(--ant-color-fill-tertiary)',
fontSize: 'var(--ant-font-size)'
}}
className="flex-center"
>
{intl.formatMessage({ id: 'common.select.count' }, { count: count })}
</TagWrapper>
);
};
const handleOnChange = (value: any, option: any) => {
const selectedValues = Array.isArray(value) ? value : [value];
const allSelected = optionsList?.map((opt: any) => opt.value) || [];
const isAllSelected = selectedValues.length === allSelected?.length;
const handleOnSearch = (value: string) => {
if (restProps.onSearch) {
restProps.onSearch(value);
} else {
const filteredOptions = options?.filter((option: any) =>
option.label.toLowerCase().includes(value.toLowerCase())
) as Global.BaseOption<string | number>[];
setOptionsList(filteredOptions || []);
checkAllSelection(filteredOptions || []);
}
};
setAllSelection({
checked: isAllSelected,
indeterminate: !isAllSelected && selectedValues.length > 0
});
const handleOnBlur = (e: any) => {
restProps.onBlur?.(e);
};
restProps.onChange?.(selectedValues, option);
};
const handleOnFocus = (e: any) => {
restProps.onFocus?.(e);
};
const filterOption = (inputValue: string, option: any) => {
if (!option || !option.label) return false;
return option.label.toLowerCase().includes(inputValue.toLowerCase());
};
const handleOnOpenChange = (open: boolean) => {
if (!open) {
checkAllSelection(options as Global.BaseOption<string | number>[]);
setOptionsList(options || []);
}
};
useEffect(() => {
const input = selectRef.current?.querySelector?.('input');
if (!input) return;
const handler = (event: KeyboardEvent) => {
const checkAllSelection = (list: Global.BaseOption<string | number>[]) => {
if (
event.key === 'Backspace' &&
(input as HTMLInputElement).value === ''
!restProps.value ||
!Array.isArray(restProps.value) ||
list.length === 0
) {
event.stopPropagation();
event.preventDefault();
setAllSelection({
checked: false,
indeterminate: false
});
return;
}
const selectedValues = new Set(restProps.value);
const allValues = list?.map((opt: any) => opt.value) || [];
const isAllSelected = allValues.every((val: any) =>
selectedValues.has(val)
);
const isSomeSelected = allValues.some((val: any) =>
selectedValues.has(val)
);
setAllSelection({
checked: isAllSelected,
indeterminate: isSomeSelected && !isAllSelected
});
};
const TagRender = (props: any) => {
const { label } = props;
const count = props.isMaxTag ? label.slice(0, -3).slice(1) : label;
return (
<TagWrapper
bordered={false}
closable={props.closable}
onClose={props.onClose}
style={{
height: 24,
backgroundColor: 'var(--ant-color-fill-tertiary)',
fontSize: 'var(--ant-font-size)'
}}
className="flex-center"
>
{showTags
? label
: intl.formatMessage(
{ id: 'common.select.count' },
{ count: count }
)}
</TagWrapper>
);
};
const handleOnSearch = (value: string) => {
if (restProps.onSearch) {
restProps.onSearch(value);
} else {
const filteredOptions = options?.filter((option: any) =>
option.label.toLowerCase().includes(value.toLowerCase())
) as Global.BaseOption<string | number>[];
setOptionsList(filteredOptions || []);
checkAllSelection(filteredOptions || []);
}
};
input.addEventListener('keydown', handler);
return () => {
input.removeEventListener('keydown', handler);
const handleOnBlur = (e: any) => {
restProps.onBlur?.(e);
};
}, [selectRef.current]);
return (
<div ref={selectRef}>
<BaseSelect
{...restProps}
options={optionsList}
maxTagCount={0}
defaultActiveFirstOption={false}
popupRender={dropdownRender}
optionRender={optionRender}
menuItemSelectedIcon={false}
onChange={handleOnChange}
tagRender={TagRender}
onBlur={handleOnBlur}
onFocus={handleOnFocus}
onSearch={handleOnSearch}
filterOption={filterOption}
onOpenChange={handleOnOpenChange}
></BaseSelect>
</div>
);
};
const handleOnFocus = (e: any) => {
restProps.onFocus?.(e);
};
const handleOnOpenChange = (open: boolean) => {
if (!open) {
checkAllSelection(options as Global.BaseOption<string | number>[]);
setOptionsList(options || []);
}
};
useEffect(() => {
const input = selectRef.current?.querySelector?.('input');
if (!input) return;
const handler = (event: KeyboardEvent) => {
if (
event.key === 'Backspace' &&
(input as HTMLInputElement).value === ''
) {
event.stopPropagation();
event.preventDefault();
}
};
input.addEventListener('keydown', handler);
return () => {
input.removeEventListener('keydown', handler);
};
}, [selectRef.current]);
useImperativeHandle(ref, () => ({
focus: () => {
selectorRef.current?.focus();
},
blur: () => {
selectorRef.current?.blur();
}
}));
return (
<div ref={selectRef} style={{ width: 'inherit' }}>
<BaseSelect
{...restProps}
ref={selectorRef}
options={optionsList}
maxTagCount={restProps.maxTagCount || 0}
defaultActiveFirstOption={false}
popupRender={dropdownRender}
optionRender={optionRender}
menuItemSelectedIcon={false}
onChange={handleOnChange}
tagRender={TagRender}
onBlur={handleOnBlur}
onFocus={handleOnFocus}
onSearch={handleOnSearch}
filterOption={filterOption}
onOpenChange={handleOnOpenChange}
>
{props.children}
</BaseSelect>
</div>
);
});
export default SimpleSelect;