添加 search-box 已经多个容器组件添加 onQuery 响应 (#1036)

This commit is contained in:
liaoxuezhi 2020-11-05 18:17:06 +08:00 committed by GitHub
parent ef5a24fab4
commit 34c61c5c89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 216 additions and 17 deletions

View File

@ -137,6 +137,7 @@ $gap-lg: px2rem(30px) !default;
$icon-color: $gray600 !default;
$icon-onHover-color: $gray900 !default;
$icon-onDisabled-color: $gray500 !default;
$label--default-bg: $gray700 !default;
$label--primary-bg: $primary !default;

View File

@ -39,6 +39,11 @@
.#{$ns}Page-toolbar {
text-align: right;
padding-right: $gap-base;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
flex-wrap: wrap;
}
}

View File

@ -30,6 +30,7 @@
}
&-activeBtn,
&-searchBtn,
&-cancelBtn {
cursor: pointer;
color: $icon-color;
@ -39,6 +40,13 @@
}
}
&.is-disabled &-activeBtn,
&.is-disabled &-searchBtn,
&.is-disabled &-cancelBtn {
color: $icon-onDisabled-color;
pointer-events: none;
}
> input {
outline: none;
border: none;

View File

@ -47,14 +47,11 @@ import {WrapperSchema} from './renderers/Wrapper';
import {TableSchema} from './renderers/Table';
import {DialogSchema} from './renderers/Dialog';
import {DrawerSchema} from './renderers/Drawer';
import {SearchBoxSchema} from './renderers/SearchBox';
// 每加个类型,这补充一下。
export type SchemaType =
| 'page'
| 'form'
| 'tpl'
| 'html'
| 'remark'
| 'button'
| 'submit'
| 'reset'
@ -99,6 +96,7 @@ export type SchemaType =
| 'map'
| 'mapping'
| 'nav'
| 'page'
| 'operation'
| 'panel'
| 'plain'
@ -106,12 +104,16 @@ export type SchemaType =
| 'progress'
| 'qrcode'
| 'qr-code'
| 'remark'
| 'search-box'
| 'service'
| 'status'
| 'switch'
| 'table'
| 'static-table' // 这个几个跟表单项同名再form下面用必须带前缀 static-
| 'tabs'
| 'html'
| 'tpl'
| 'tasks'
| 'vbox'
| 'video'
@ -157,6 +159,7 @@ export type SchemaObject =
| PlainSchema
| ProgressSchema
| QRCodeSchema
| SearchBoxSchema
| ServiceSchema
| StatusSchema
| SwitchSchema

View File

@ -4,13 +4,19 @@ import {Icon} from './icons';
import {uncontrollable} from 'uncontrollable';
import {autobind} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
import debounce from 'lodash/debounce';
export interface SearchBoxProps extends ThemeProps, LocaleProps {
name?: string;
disabled?: boolean;
mini?: boolean;
searchImediately?: boolean;
onChange?: (text: string) => void;
placeholder?: string;
defaultValue?: string;
value?: string;
active?: boolean;
defaultActive?: boolean;
onActiveChange?: (active: boolean) => void;
onSearch?: (value: string) => void;
onCancel?: () => void;
@ -18,6 +24,26 @@ export interface SearchBoxProps extends ThemeProps, LocaleProps {
export class SearchBox extends React.Component<SearchBoxProps> {
inputRef: React.RefObject<HTMLInputElement> = React.createRef();
static defaultProps = {
mini: true,
searchImediately: true
};
lazyEmitSearch = debounce(
() => {
const onSearch = this.props.onSearch;
onSearch?.(this.props.value || '');
},
250,
{
leading: false,
trailing: true
}
);
componentWillUnmount() {
this.lazyEmitSearch.cancel();
}
@autobind
handleActive() {
@ -36,9 +62,23 @@ export class SearchBox extends React.Component<SearchBoxProps> {
@autobind
handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const {onChange, onSearch} = this.props;
const {onChange, onSearch, searchImediately} = this.props;
onChange?.(e.currentTarget.value);
onSearch?.(e.currentTarget.value);
searchImediately && this.lazyEmitSearch();
}
@autobind
handleSearch() {
const {value, onSearch} = this.props;
onSearch?.(value || '');
}
@autobind
handleKeyUp(e: React.KeyboardEvent<any>) {
if (e.key === 'Enter') {
this.handleSearch();
e.preventDefault();
}
}
render() {
@ -48,21 +88,36 @@ export class SearchBox extends React.Component<SearchBoxProps> {
active,
name,
onChange,
disabled,
placeholder,
mini,
translate: __
} = this.props;
return (
<div className={cx('SearchBox', active ? 'is-active' : '')}>
<div
className={cx(
'SearchBox',
disabled ? 'is-disabled' : '',
!mini || active ? 'is-active' : ''
)}
>
<input
name={name}
disabled={disabled}
onChange={this.handleChange}
value={value || ''}
placeholder={__(placeholder || '输入关键字')}
ref={this.inputRef}
autoComplete="off"
onKeyUp={this.handleKeyUp}
/>
{active ? (
{!mini ? (
<a className={cx('SearchBox-searchBtn')} onClick={this.handleSearch}>
<Icon icon="search" className="icon" />
</a>
) : active ? (
<a className={cx('SearchBox-cancelBtn')} onClick={this.handleCancel}>
<Icon icon="close" className="icon" />
</a>

View File

@ -132,6 +132,7 @@ import './renderers/Switch';
import './renderers/Wizard';
import './renderers/Chart';
import './renderers/Container';
import './renderers/SearchBox';
import './renderers/Service';
import './renderers/Video';
import './renderers/Audio';

View File

@ -396,7 +396,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
this.handleChildPopOverClose = this.handleChildPopOverClose.bind(this);
this.search = this.search.bind(this);
this.silentSearch = this.silentSearch.bind(this);
this.handlQuery = this.handlQuery.bind(this);
this.handleQuery = this.handleQuery.bind(this);
this.renderHeaderToolbar = this.renderHeaderToolbar.bind(this);
this.renderFooterToolbar = this.renderFooterToolbar.bind(this);
this.clearSelection = this.clearSelection.bind(this);
@ -1345,7 +1345,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
}
}
handlQuery(values: object, forceReload: boolean = false) {
handleQuery(values: object, forceReload: boolean = false) {
const {store, syncLocation, env, pageField, perPageField} = this.props;
store.updateQuery(
@ -1371,7 +1371,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
}
receive(values: object) {
this.handlQuery(values, true);
this.handleQuery(values, true);
}
reloadTarget(target: string, data: any) {
@ -1801,6 +1801,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
lastPage: store.lastPage,
perPage: store.perPage,
total: store.total,
onQuery: this.handleQuery,
onAction: this.handleAction,
onChangePage: this.handleChangePage,
onBulkAction: this.handleBulkAction,
@ -1992,7 +1993,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
onAction: this.handleAction,
onSave: this.handleSave,
onSaveOrder: this.handleSaveOrder,
onQuery: this.handlQuery,
onQuery: this.handleQuery,
onSelect: this.handleSelect,
onPopOverOpened: this.handleChildPopOverOpen,
onPopOverClosed: this.handleChildPopOverClose,

View File

@ -368,6 +368,7 @@ export default class Form extends React.Component<FormProps, object> {
this.onInit = this.onInit.bind(this);
this.handleAction = this.handleAction.bind(this);
this.handleQuery = this.handleQuery.bind(this);
this.handleDialogConfirm = this.handleDialogConfirm.bind(this);
this.handleDialogClose = this.handleDialogClose.bind(this);
this.handleDrawerConfirm = this.handleDrawerConfirm.bind(this);
@ -954,6 +955,14 @@ export default class Form extends React.Component<FormProps, object> {
}
}
handleQuery(query: any) {
if (this.props.initApi) {
this.receive(query);
} else {
this.props.onQuery?.(query);
}
}
handleDialogConfirm(
values: object[],
action: Action,
@ -1188,6 +1197,7 @@ export default class Form extends React.Component<FormProps, object> {
disabled: disabled || (control as Schema).disabled || form.loading,
btnDisabled: form.loading || form.validating,
onAction: this.handleAction,
onQuery: this.handleQuery,
onChange:
formLazyChange === false ? this.handleChange : this.lazyHandleChange,
addHook: this.addHook,
@ -1355,6 +1365,7 @@ export default class Form extends React.Component<FormProps, object> {
children: body,
actions: this.buildActions(),
onAction: this.handleAction,
onQuery: this.handleQuery,
disabled: store.loading,
btnDisabled: store.loading || store.validating,
headerClassName,

View File

@ -184,6 +184,7 @@ export default class Page extends React.Component<PageProps> {
// autobind 会让继承里面的 super 指向有问题,所以先这样!
bulkBindFunctions<Page /*为毛 this 的类型自动识别不出来*/>(this, [
'handleAction',
'handleQuery',
'handleDialogConfirm',
'handleDialogClose',
'handleDrawerConfirm',
@ -305,6 +306,10 @@ export default class Page extends React.Component<PageProps> {
}
}
handleQuery(query: any) {
this.receive(query);
}
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
@ -428,12 +433,14 @@ export default class Page extends React.Component<PageProps> {
toolbar,
render,
store,
initApi,
env,
classnames: cx
} = this.props;
const subProps = {
onAction: this.handleAction
onAction: this.handleAction,
onQuery: initApi ? this.handleQuery : undefined
};
let header, right;
@ -496,11 +503,13 @@ export default class Page extends React.Component<PageProps> {
asideClassName,
classnames: cx,
header,
showErrorMsg
showErrorMsg,
initApi
} = this.props;
const subProps = {
onAction: this.handleAction,
onQuery: initApi ? this.handleQuery : undefined,
loading: store.loading
};
@ -560,7 +569,8 @@ export default class Page extends React.Component<PageProps> {
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction
onAction: this.handleAction,
onQuery: initApi ? this.handleQuery : undefined
}
)}
@ -577,7 +587,8 @@ export default class Page extends React.Component<PageProps> {
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction
onAction: this.handleAction,
onQuery: initApi ? this.handleQuery : undefined
}
)}
</div>

View File

@ -0,0 +1,89 @@
import Spinner from '../components/Spinner';
import {Renderer, RendererProps} from '../factory';
import React from 'react';
import {BaseSchema} from '../Schema';
import SearchBox from '../components/SearchBox';
import {autobind, getVariable, setVariable} from '../utils/helper';
/**
*
*/
export interface SearchBoxSchema extends BaseSchema {
/**
*
*
* https://baidu.gitee.io/amis/docs/components/search-box
*/
type: 'search-box';
/**
*
*
* @default keywords
*/
name?: string;
/**
* Mini
*/
mini?: boolean;
/**
*
*/
searchImediately?: boolean;
}
interface SearchBoxProps extends RendererProps, SearchBoxSchema {
name: string;
onQuery?: (query: {[propName: string]: string}) => void;
}
@Renderer({
test: /(^|\/)search\-box$/,
name: 'search'
})
export class SearchBoxRenderer extends React.Component<SearchBoxProps> {
static defaultProps = {
name: 'keywords',
mini: false,
searchImediately: false
};
static propsList: Array<string> = ['mini', 'searchImediately'];
@autobind
handleCancel() {
const name = this.props.name;
const onQuery = this.props.onQuery;
const data: any = {};
setVariable(data, name, '');
onQuery?.(data);
}
@autobind
handleSearch(text: string) {
const {name, onQuery: onQuery} = this.props;
const data: any = {};
setVariable(data, name, text);
onQuery?.(data);
}
render() {
const {data, name, onQuery: onQuery, mini, searchImediately} = this.props;
const value = getVariable(data, name);
return (
<SearchBox
name={name}
disabled={!onQuery}
defaultActive={!!value}
defaultValue={value}
mini={mini}
searchImediately={searchImediately}
onSearch={this.handleSearch}
onCancel={this.handleCancel}
/>
);
}
}

View File

@ -261,7 +261,11 @@ export default class Service extends React.Component<ServiceProps> {
}
handleQuery(query: any) {
this.receive(query);
if (this.props.api || this.props.schemaApi) {
this.receive(query);
} else {
this.props.onQuery?.(query);
}
}
reloadTarget(target: string, data?: any) {

View File

@ -539,6 +539,15 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
}
}
@autobind
handleQuery(query: any) {
if (this.props.initApi) {
this.receive(query);
} else {
this.props.onQuery?.(query);
}
}
openFeedback(dialog: any, ctx: any) {
return new Promise(resolve => {
const {store} = this.props;
@ -945,6 +954,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
onReset: this.handleReset,
onSubmit: this.handleSubmit,
onAction: this.handleAction,
onQuery: this.handleQuery,
disabled: store.loading,
popOverContainer:
popOverContainer || this.getPopOverContainer,