Skip to content

Commit

Permalink
[duoyun-ui] <dy-select> support search event
Browse files Browse the repository at this point in the history
Closed #60
  • Loading branch information
mantou132 committed Dec 8, 2023
1 parent 50b92ce commit 0b27d7c
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 111 deletions.
222 changes: 114 additions & 108 deletions packages/duoyun-ui/src/elements/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
part,
globalemitter,
Emitter,
emitter,
} from '@mantou/gem/lib/decorators';
import { GemElement, html, TemplateResult } from '@mantou/gem/lib/element';
import { createCSSSheet, css } from '@mantou/gem/lib/utils';
Expand Down Expand Up @@ -247,6 +248,7 @@ export class DuoyunFormItemElement extends GemElement<FormItemState> {
@boolattribute searchable: boolean;
@boolattribute clearable: boolean;
@boolattribute time: boolean;
@boolattribute loading: boolean;

@state invalid: boolean;

Expand All @@ -260,6 +262,8 @@ export class DuoyunFormItemElement extends GemElement<FormItemState> {

@globalemitter itemchange: Emitter<{ name: string; value: number | string | any[] | any }>;

@emitter search: Emitter<string>;

@refobject slotRef: RefObject<HTMLSlotElement>;

state: FormItemState = {};
Expand Down Expand Up @@ -355,7 +359,9 @@ export class DuoyunFormItemElement extends GemElement<FormItemState> {
class="input"
part=${DuoyunFormItemElement.input}
@change=${this.#onChange}
@search=${(evt: CustomEvent<string>) => this.search(evt.detail)}
.multiple=${this.multiple}
.loading=${this.loading}
.adder=${this.adder}
.value=${this.value}
.placeholder=${this.placeholder}
Expand All @@ -364,119 +370,119 @@ export class DuoyunFormItemElement extends GemElement<FormItemState> {
></dy-select>
`
: this.#type === 'date'
? html`
<dy-date-picker
class="input"
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change(undefined)}
?disabled=${this.disabled}
.value=${this.value}
.time=${this.time}
.placeholder=${this.placeholder}
.clearable=${!this.required}
>
</dy-date-picker>
`
: this.#type === 'date-range'
? html`
<dy-date-range-picker
class="input"
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change(undefined)}
?disabled=${this.disabled}
.value=${this.value}
.placeholder=${this.placeholder}
.clearable=${!this.required}
>
</dy-date-range-picker>
`
: this.#type === 'picker'
? html`
<dy-picker
?disabled=${this.disabled}
class="input"
part=${DuoyunFormItemElement.input}
@change=${this.#onChange}
.multiple=${this.multiple}
.value=${this.value}
.fit=${true}
.placeholder=${this.placeholder}
.options=${this.dataList}
></dy-picker>
`
: this.#type === 'checkbox'
? html`
<dy-checkbox
@change=${this.#onCheckboxChange}
?checked=${this.checked}
?disabled=${this.disabled}
.value=${this.value as string}
>
${this.label}
</dy-checkbox>
`
: this.#type === 'radio-group'
? html`
<dy-radio-group
@change=${this.#onChange}
?disabled=${this.disabled}
.options=${this.dataList}
.value=${this.value}
>
</dy-radio-group>
`
: this.#type === 'checkbox-group'
? html`
<dy-checkbox-group
@change=${this.#onChange}
?disabled=${this.disabled}
.options=${this.dataList}
.value=${this.value}
>
</dy-checkbox-group>
`
: this.name && this.#type !== 'slot'
? this.multiple
? html`
<dy-date-picker
${(this.value as string[])?.map(
(value, index) => html`
<dy-input
class="input"
part=${DuoyunFormItemElement.input}
?disabled=${this.disabled}
.placeholder=${this.placeholder}
@change=${(evt: CustomEvent<string>) => this.#onTextChangeWithIndex(evt, index)}
@clear=${() => this.#onTextCleanWithIndex(index)}
.autofocus=${this.autofocus}
.clearable=${true}
.alwayclearable=${true}
.dataList=${this.dataList}
.value=${value}
></dy-input>
`,
)}
<div class="footer" part=${DuoyunFormItemElement.add}>
<dy-button .color=${'cancel'} @click=${this.#onTextAdd}>+</dy-button>
</div>
`
: html`
<dy-input
class="input"
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change(undefined)}
part=${DuoyunFormItemElement.input}
name=${this.name}
type=${this.#type}
?disabled=${this.disabled}
.value=${this.value}
.time=${this.time}
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change('')}
.autofocus=${this.autofocus}
.clearable=${this.clearable}
.value=${this.value as string}
.placeholder=${this.placeholder}
.clearable=${!this.required}
>
</dy-date-picker>
.required=${this.required}
.dataList=${this.dataList}
></dy-input>
`
: this.#type === 'date-range'
? html`
<dy-date-range-picker
class="input"
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change(undefined)}
?disabled=${this.disabled}
.value=${this.value}
.placeholder=${this.placeholder}
.clearable=${!this.required}
>
</dy-date-range-picker>
`
: this.#type === 'picker'
? html`
<dy-picker
?disabled=${this.disabled}
class="input"
part=${DuoyunFormItemElement.input}
@change=${this.#onChange}
.multiple=${this.multiple}
.value=${this.value}
.fit=${true}
.placeholder=${this.placeholder}
.options=${this.dataList}
></dy-picker>
`
: this.#type === 'checkbox'
? html`
<dy-checkbox
@change=${this.#onCheckboxChange}
?checked=${this.checked}
?disabled=${this.disabled}
.value=${this.value as string}
>
${this.label}
</dy-checkbox>
`
: this.#type === 'radio-group'
? html`
<dy-radio-group
@change=${this.#onChange}
?disabled=${this.disabled}
.options=${this.dataList}
.value=${this.value}
>
</dy-radio-group>
`
: this.#type === 'checkbox-group'
? html`
<dy-checkbox-group
@change=${this.#onChange}
?disabled=${this.disabled}
.options=${this.dataList}
.value=${this.value}
>
</dy-checkbox-group>
`
: this.name && this.#type !== 'slot'
? this.multiple
? html`
${(this.value as string[])?.map(
(value, index) => html`
<dy-input
class="input"
part=${DuoyunFormItemElement.input}
?disabled=${this.disabled}
.placeholder=${this.placeholder}
@change=${(evt: CustomEvent<string>) => this.#onTextChangeWithIndex(evt, index)}
@clear=${() => this.#onTextCleanWithIndex(index)}
.autofocus=${this.autofocus}
.clearable=${true}
.alwayclearable=${true}
.dataList=${this.dataList}
.value=${value}
></dy-input>
`,
)}
<div class="footer" part=${DuoyunFormItemElement.add}>
<dy-button .color=${'cancel'} @click=${this.#onTextAdd}>+</dy-button>
</div>
`
: html`
<dy-input
class="input"
part=${DuoyunFormItemElement.input}
name=${this.name}
type=${this.#type}
?disabled=${this.disabled}
@change=${this.#onChange}
@clear=${(evt: any) => evt.target.change('')}
.autofocus=${this.autofocus}
.clearable=${this.clearable}
.value=${this.value as string}
.placeholder=${this.placeholder}
.required=${this.required}
.dataList=${this.dataList}
></dy-input>
`
: ''}
: ''}
<slot ref=${this.slotRef.ref}></slot>
${invalidMessage
? html`
Expand Down
34 changes: 31 additions & 3 deletions packages/duoyun-ui/src/elements/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
@attribute placeholder: string;
@boolattribute borderless: boolean;
@boolattribute pinselected: boolean;
@boolattribute loading: boolean;

@property dropdownStyle?: StyleObject;
@property options?: Option[];
Expand All @@ -144,6 +145,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
@property renderTag?: (e: Option) => string | TemplateResult;
@state active: boolean;
@globalemitter change: Emitter<any | any[]>;
@globalemitter search: Emitter<string>;

@refobject searchRef: RefObject<HTMLElement>;
@refobject optionsRef: RefObject<HTMLElement>;
Expand All @@ -157,7 +159,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker

get #filteredOptions() {
const { search } = this.state;
return search
return search && !this.#isLoading
? this.options?.filter(({ label, description = '' }) => isIncludesString(html`${label}${description}`, search))
: this.options;
}
Expand All @@ -180,6 +182,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
});
this.addEventListener('click', this.#open);
this.addEventListener('keydown', this.#onKeydown);
this.addEventListener('pointerup', (e) => this.state.open && e.stopPropagation());
}

state = {
Expand All @@ -195,6 +198,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
#open = () => {
if (this.disabled) return;
this.setState({ open: true });
this.searchRef.element?.focus();
};

#close = () => {
Expand Down Expand Up @@ -263,9 +267,14 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker

#onSearch = (evt: CustomEvent<string>) => {
this.setState({ search: evt.detail, open: true });
this.search(evt.detail);
evt.stopPropagation();
};

#onClear = () => {
this.setState({ search: '' });
};

#onChange = (value: any) => {
if (this.multiple) {
if (!this.value) {
Expand All @@ -292,8 +301,12 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker

#valueSet: Set<any>;
#valueOptions: Option[] | undefined;
#isLoading = false;

willMount = () => {
this.memo(() => {
this.#isLoading = this.#isLoading || this.loading;
});
this.memo(
() => {
const map = new Map<any, Option>();
Expand Down Expand Up @@ -349,6 +362,16 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker

#getOptions = () => {
const { search } = this.state;
if (this.loading) {
return [
{
icon: icons.loading,
label: locale.loading,
disabled: true,
onPointerUp: (e: Event) => e.stopPropagation(),
},
];
}
const options = this.#filteredOptions?.map((option) => {
const { value, label, description, disabled, onRemove } = option;
return {
Expand All @@ -365,7 +388,7 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
options?.sort((a, b) => (a.tagIcon && !b.tagIcon ? -1 : 0));
}
return search && options?.length === 0
? [{ label: locale.noData, disabled: true, onPointerUp: () => this.setState({ search: '' }) }]
? [{ label: locale.noData, disabled: true, onPointerUp: (e: Event) => e.stopPropagation() }]
: options;
};

Expand Down Expand Up @@ -418,7 +441,12 @@ export class DuoyunSelectElement extends GemElement<State> implements BasePicker
: ''}
${isEmpty && this.placeholder ? html`<div class="value">${this.placeholder}</div>` : ``}
</div>
<dy-use class="icon" .element=${icons.expand}></dy-use>
<dy-use
class="icon"
@click=${this.#onClear}
.tabIndex=${search ? 0 : -1}
.element=${search ? icons.close : icons.expand}
></dy-use>
${this.options && open
? html`
<dy-reflect .target=${document.body}>
Expand Down
1 change: 1 addition & 0 deletions packages/duoyun-ui/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default {
copyFail: 'Copy fail',
add: 'Add',
search: 'Search',
loading: 'Loading',
ok: 'OK',
cancel: 'Cancel',
prevPage: 'Prev',
Expand Down
1 change: 1 addition & 0 deletions packages/duoyun-ui/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const lang: typeof en = {
copyFail: '复制失败',
add: '新增',
search: '搜索',
loading: '加载中',
ok: '确认',
cancel: '取消',
prevPage: '上一页',
Expand Down
1 change: 1 addition & 0 deletions packages/gem-examples/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default defineConfig({
entry: `/src/${name}/index.ts`,
})),
),
rewrites: [{ from: /^\/$/, to: '/index.html' }],
}),
],
define: {
Expand Down

0 comments on commit 0b27d7c

Please sign in to comment.