From 7dbac118db1d49fa9f64bfdeab4806669e536519 Mon Sep 17 00:00:00 2001 From: aalizzwell Date: Thu, 20 Oct 2022 19:55:05 +0800 Subject: [PATCH 1/8] fix: remove event import --- .../components/combo-list/src/combo-list.component.tsx | 6 +++--- packages/ui-vue/components/combo-list/src/types.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx index 7ac6bf198..bec08a87d 100644 --- a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx +++ b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx @@ -6,7 +6,7 @@ import { COMBO_LIST_TOKEN, groupIcon } from './const'; import { useState } from './composition/use-state'; import { useComboList } from './composition/use-combo-list'; import FOptions from './components/options'; -import { EventEmitter } from 'events'; +//import { EventEmitter } from 'events'; export default defineComponent({ @@ -18,7 +18,7 @@ export default defineComponent({ inheritAttrs: false, setup(props: ComboListProps, context: SetupContext) { const modelValue = ref(props.modelValue); - const eventEmitter = new EventEmitter(); + // const eventEmitter = new EventEmitter(); // is panel visible const { isPanelVisible, onClear, onButtonClick } = useComboList(props, context, modelValue); const [displayText, setDisplayText] = useState(props.displayText); @@ -28,7 +28,7 @@ export default defineComponent({ provide( COMBO_LIST_TOKEN, reactive({ - eventEmitter + eventEmitter: null }) ); return () => { diff --git a/packages/ui-vue/components/combo-list/src/types.ts b/packages/ui-vue/components/combo-list/src/types.ts index dc75b767f..2e4d5a92e 100644 --- a/packages/ui-vue/components/combo-list/src/types.ts +++ b/packages/ui-vue/components/combo-list/src/types.ts @@ -1,4 +1,4 @@ -import EventEmitter from 'events'; +//import EventEmitter from 'events'; import { ComputedRef, Ref, UnwrapRef } from 'vue'; export type ModelValue = number | string | undefined | Array; /** @@ -34,5 +34,5 @@ export interface IOption { export type Options = Array; export interface ComboListContext { - eventEmitter: EventEmitter + //eventEmitter: EventEmitter } \ No newline at end of file -- Gitee From 163b4c25f51436b5e866c946f5563bffc7aa7595 Mon Sep 17 00:00:00 2001 From: aalizzwell Date: Sat, 22 Oct 2022 16:52:16 +0800 Subject: [PATCH 2/8] chore: add option events --- .../combo-list/src/combo-list.component.tsx | 26 ++++--- .../combo-list/src/components/option.tsx | 33 ++++---- .../src/composition/use-combo-list.ts | 75 +++++++++++++++---- .../combo-list/src/composition/use-option.ts | 35 +++++++-- .../ui-vue/components/combo-list/src/types.ts | 9 ++- packages/ui-vue/src/components/combo-list.vue | 6 +- 6 files changed, 128 insertions(+), 56 deletions(-) diff --git a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx index bec08a87d..3235bab6b 100644 --- a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx +++ b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, provide, reactive, ref, SetupContext } from 'vue'; +import { defineComponent, provide, reactive, ref, SetupContext, Teleport } from 'vue'; import { comboListProps, ComboListProps } from './combo-list.props'; import { ButtonEdit } from '../../button-edit'; import { ViewType } from './types'; @@ -6,29 +6,30 @@ import { COMBO_LIST_TOKEN, groupIcon } from './const'; import { useState } from './composition/use-state'; import { useComboList } from './composition/use-combo-list'; import FOptions from './components/options'; -//import { EventEmitter } from 'events'; export default defineComponent({ name: 'FComboList', props: comboListProps, emits: [ - 'clear' + 'clear', + 'update:modelValue' ], inheritAttrs: false, setup(props: ComboListProps, context: SetupContext) { const modelValue = ref(props.modelValue); - // const eventEmitter = new EventEmitter(); + const panelRef = ref(); + // is panel visible - const { isPanelVisible, onClear, onButtonClick } = useComboList(props, context, modelValue); - const [displayText, setDisplayText] = useState(props.displayText); + const { isPanelVisible, panelContainerClass, displayText, onClear, onButtonClick, onValueChange } = useComboList(props, context, modelValue, panelRef); /** * provider */ provide( COMBO_LIST_TOKEN, reactive({ - eventEmitter: null + onValueChange, + modelValue }) ); return () => { @@ -46,7 +47,7 @@ export default defineComponent({ enableClear={props.enableClear} maxLength={props.maxLength} style="display:block" - modelValue={displayText.value} + v-model={displayText} onClear={onClear} onClickButton={onButtonClick} /> @@ -77,9 +78,12 @@ export default defineComponent({ )} {/** panel area */} {isPanelVisible.value && ( -
- show - +
+
+
+ +
+
)} diff --git a/packages/ui-vue/components/combo-list/src/components/option.tsx b/packages/ui-vue/components/combo-list/src/components/option.tsx index a7e2577d4..e8b92e4fc 100644 --- a/packages/ui-vue/components/combo-list/src/components/option.tsx +++ b/packages/ui-vue/components/combo-list/src/components/option.tsx @@ -1,20 +1,23 @@ -import { defineComponent, SetupContext } from 'vue'; +import { defineComponent, SetupContext, withModifiers } from 'vue'; import { optionProps, OptionProps } from '../combo-list.props'; import { useOption } from '../composition/use-option'; export default defineComponent({ - name: 'FOption', - props: optionProps, - emits: [], - inheritAttrs: false, - setup(props: OptionProps, context: SetupContext) { - const { name } = useOption(props, context); - return () => { - return ( -
  • - {context.slots?.default ? context.slots.default() : name.value} -
  • - ); - }; - } + name: 'FOption', + props: optionProps, + emits: [], + inheritAttrs: false, + setup(props: OptionProps, context: SetupContext) { + const { name, optionClass, onOptionClick } = useOption(props, context); + + return () => { + return ( +
  • + + {context.slots?.default ? context.slots.default() : name.value} + +
  • + ); + }; + } }); \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts b/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts index e90fb97a9..8966d448a 100644 --- a/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts +++ b/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts @@ -1,21 +1,64 @@ -import { ref, Ref, SetupContext } from "vue"; +import { computed, reactive, ref, Ref, SetupContext } from "vue"; import { ComboListProps } from "../combo-list.props"; -import { ModelValue } from "../types"; +import { IOption, ModelValue } from "../types"; -export function useComboList(props: ComboListProps, context: SetupContext, modelValue: Ref) { - const isPanelVisible = ref(false); - //#region events - function onClear($event: Event) { - modelValue.value = ''; - context.emit('clear'); +export function useComboList(props: ComboListProps, context: SetupContext, modelValue: Ref, panelRef: Ref) { + const isPanelVisible = ref(false); + const displayText = ref(''); + const panelContainerClass = computed(() => ({ + comboPanel: true, + 'f-area-hide': !isPanelVisible.value, + 'f-area-show': isPanelVisible.value + })); + //#region events + /** + * 清空事件 + * @param $event event + */ + function onClear($event: Event) { + modelValue.value = ''; + context.emit('clear'); + } + /** + * 下拉按钮点击 + * @param $event event + */ + function onButtonClick($event: Event) { + const clickOutsideEventHandler = (event: any) => { + if (panelRef.value == event.target || panelRef?.value?.contains(event.target)) { + event.stopPropagation(); + } else { + isPanelVisible.value = false; + } + }; + isPanelVisible.value = !isPanelVisible.value; + if (isPanelVisible.value === true) { + // 即将展示下拉面板 + document.addEventListener("click", clickOutsideEventHandler); + } else { + document.removeEventListener('click', clickOutsideEventHandler); } - function onButtonClick($event: Event) { - isPanelVisible.value = !isPanelVisible.value; + } + /** + * + * @param item 值变化事件处理器 + */ + function onValueChange(item: IOption) { + modelValue.value = item.name; + if (props.multiSelect) { + + } else { + context.emit('update:modelValue', item.value); } - //#endregion - return { - isPanelVisible, - onClear, - onButtonClick - }; + isPanelVisible.value = false; + } + //#endregion + return { + panelContainerClass, + isPanelVisible, + displayText, + onClear, + onButtonClick, + onValueChange + }; } \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/composition/use-option.ts b/packages/ui-vue/components/combo-list/src/composition/use-option.ts index 3d706e8df..adb4b09af 100644 --- a/packages/ui-vue/components/combo-list/src/composition/use-option.ts +++ b/packages/ui-vue/components/combo-list/src/composition/use-option.ts @@ -1,12 +1,33 @@ -import { computed, Ref, SetupContext } from "vue"; +import { computed, inject, Ref, SetupContext } from "vue"; import { IUseOption } from "../types"; import { OptionProps } from "../combo-list.props"; +import { COMBO_LIST_TOKEN } from "../const"; export function useOption(props: OptionProps, context: SetupContext): IUseOption { - const name = computed(() => { - return props.name || props.value; - }); - return { - name - }; + const comboList = inject(COMBO_LIST_TOKEN, null); + const name = computed(() => { + return props.name || props.value; + }); + const onOptionClick = ($event: Event) => { + if (!props.disabled) { + comboList?.onValueChange({ + value: props.value, + name: props.name, + disabled: props.disabled + }); + } + }; + const isOptionSelected = computed(() => { + return comboList?.modelValue === props.value; + }); + const optionClass = computed(() => ({ + active: isOptionSelected.value, + 'list-group-item': true, + 'list-group-item-action': true + })); + return { + name, + optionClass, + onOptionClick + }; } \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/types.ts b/packages/ui-vue/components/combo-list/src/types.ts index 2e4d5a92e..4911ceeb3 100644 --- a/packages/ui-vue/components/combo-list/src/types.ts +++ b/packages/ui-vue/components/combo-list/src/types.ts @@ -23,16 +23,19 @@ export interface IUseEventHandler { } export interface IUseOption { name: ComputedRef; + onOptionClick: ($event: Event) => void; + optionClass: ComputedRef; } export interface IOption { - value: string | number; - name: string; + value: string | number | undefined; + name: string | undefined; disabled: boolean; } export type Options = Array; export interface ComboListContext { - //eventEmitter: EventEmitter + onValueChange: (item: IOption) => void; + modelValue: ModelValue } \ No newline at end of file diff --git a/packages/ui-vue/src/components/combo-list.vue b/packages/ui-vue/src/components/combo-list.vue index 49f5e945a..6c9623577 100644 --- a/packages/ui-vue/src/components/combo-list.vue +++ b/packages/ui-vue/src/components/combo-list.vue @@ -5,9 +5,9 @@ const disable = ref(false); const readonly = ref(false); const displayText = ref(''); const forcePlaceholder = ref(true); -const editable = ref(true); +const editable = ref(false); const enableClear = ref(true); -const data = ref([{ value: '1', name: '1', disabled: false }]); +const data = ref([{ value: 'item1', name: '类型1', disabled: false },{ value: 'item2', name: '类型2', disabled: false },{ value: 'item3', name: '类型3', disabled: false }]); function onClear(params: any) { console.log('clear'); @@ -42,8 +42,6 @@ function onClear(params: any) { enableClear }} - forcePlaceholder - -- Gitee From 35ba32c128630e903088a46b639f12115bd11b95 Mon Sep 17 00:00:00 2001 From: aalizzwell Date: Sat, 22 Oct 2022 16:55:47 +0800 Subject: [PATCH 3/8] chore: change class name --- packages/ui-vue/src/components/hello-world.vue | 2 +- packages/ui-vue/src/style.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-vue/src/components/hello-world.vue b/packages/ui-vue/src/components/hello-world.vue index 3ec527ec6..b4fe601f3 100644 --- a/packages/ui-vue/src/components/hello-world.vue +++ b/packages/ui-vue/src/components/hello-world.vue @@ -9,7 +9,7 @@ const count = ref(0); -- Gitee From 513862a92a97023e4548b1b012b8d19179728277 Mon Sep 17 00:00:00 2001 From: aalizzwell Date: Sat, 10 Dec 2022 15:24:43 +0800 Subject: [PATCH 6/8] chore: fix runtime warning when attribute inheritance on multiple root nodes --- .../ui-vue/components/button-edit/src/button-edit.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx index 6fe3cd5ce..45b83837d 100644 --- a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx +++ b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx @@ -89,7 +89,7 @@ export default defineComponent({ return () => { return ( <> -
    +
    Date: Tue, 13 Dec 2022 18:51:03 +0800 Subject: [PATCH 7/8] refactor: refactor combo-list component --- .../combo-list/src/combo-list.component.tsx | 81 +- .../combo-list/src/combo-list.props.ts | 29 +- .../{option.tsx => option.component.tsx} | 3 +- .../options-container.component.tsx | 36 + .../src/components/options.component.tsx | 24 + .../combo-list/src/components/options.tsx | 22 - .../combo-list/src/composition/index.ts | 4 +- .../src/composition/use-combo-list.ts | 111 +- .../combo-list/src/composition/use-option.ts | 13 +- .../src/composition/use-options-container.ts | 47 + .../combo-list/src/composition/use-state.ts | 18 - .../ui-vue/components/combo-list/src/const.ts | 11 +- .../ui-vue/components/combo-list/src/types.ts | 83 +- yarn.lock | 23976 ++++++++-------- 14 files changed, 12540 insertions(+), 11918 deletions(-) rename packages/ui-vue/components/combo-list/src/components/{option.tsx => option.component.tsx} (95%) create mode 100644 packages/ui-vue/components/combo-list/src/components/options-container.component.tsx create mode 100644 packages/ui-vue/components/combo-list/src/components/options.component.tsx delete mode 100644 packages/ui-vue/components/combo-list/src/components/options.tsx create mode 100644 packages/ui-vue/components/combo-list/src/composition/use-options-container.ts delete mode 100644 packages/ui-vue/components/combo-list/src/composition/use-state.ts diff --git a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx index 6bcc66c59..df055d21d 100644 --- a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx +++ b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx @@ -1,91 +1,56 @@ -import { defineComponent, provide, reactive, ref, SetupContext, Teleport } from 'vue'; +import { defineComponent, provide, reactive, SetupContext} from 'vue'; import { comboListProps, ComboListProps } from './combo-list.props'; import { ButtonEdit } from '../../button-edit'; -import { ViewType } from './types'; -import { COMBO_LIST_TOKEN, groupIcon } from './const'; -import { useState } from './composition/use-state'; -import { useComboList } from './composition/use-combo-list'; -import FOptions from './components/options'; - +import { COMBO_LIST_TOKEN, EVENTS } from './const'; +import { useComboList, useComboListEvent } from './composition/use-combo-list'; +import FOptionsContainer from './components/options-container.component'; export default defineComponent({ name: 'FComboList', props: comboListProps, emits: [ - 'clear', + EVENTS.clear, 'update:modelValue' ], - inheritAttrs: false, setup(props: ComboListProps, context: SetupContext) { - const modelValue = ref(props.modelValue); - const panelRef = ref(); - // is panel visible - const { isPanelVisible, panelContainerClass, displayText, onClear, onButtonClick, onValueChange } = useComboList(props, context, modelValue, panelRef); + + // component logic + const { onValueChange, displayText, modelValue, isPanelVisible, position } = useComboList(props, context); + // event logic + const { onClear, onButtonClick, onPanelHidden } = useComboListEvent({ props, context, position, isPanelVisible }); + const comboListContext = reactive({ + isPanelVisible, + comboListProps: props, + onValueChange, + modelValue + }); /** * provider */ - provide( - COMBO_LIST_TOKEN, - reactive({ - onValueChange, - modelValue - }) - ); + provide(COMBO_LIST_TOKEN, comboListContext); return () => { return ( <> {/** main component */} - {/** tag area */} - {props.viewType === ViewType.Tag && ( -
    -
    -
    -
    - - - -
    -
    - -
    -
    -
    - - - - -
    -
    -
    - )} - {/** panel area */} - {isPanelVisible.value && ( -
    -
    -
    - -
    -
    -
    - )} + onClickButton={onButtonClick}> + +
    ); }; diff --git a/packages/ui-vue/components/combo-list/src/combo-list.props.ts b/packages/ui-vue/components/combo-list/src/combo-list.props.ts index 068830f2d..1418a77e5 100644 --- a/packages/ui-vue/components/combo-list/src/combo-list.props.ts +++ b/packages/ui-vue/components/combo-list/src/combo-list.props.ts @@ -1,5 +1,5 @@ import { ExtractPropTypes, PropType } from "vue"; -import { Options, ViewType } from './types'; +import { Options, ViewType, Position } from './types'; /** * 下拉列表属性 @@ -112,7 +112,12 @@ export const comboListProps = { /** * 绑定值 */ - modelValue: { type: String } + modelValue: { type: [String, Number] }, + /** + * 可选,下拉图标 + * 默认为'' + */ + dropDownIcon: { type: String, default: '' } }; export type ComboListProps = ExtractPropTypes; @@ -136,6 +141,26 @@ export const optionProps = { disabled: { default: false, type: Boolean } }; export type OptionProps = ExtractPropTypes; + +/** + * options 属性 + */ +export const optionsContainerProps = { + /** + * 下拉面板是否可见 + */ + isPanelVisible: { type: Boolean, default: false }, + /** + * 下拉数据源 + */ + data: { type: Array as PropType }, + /** + * 下拉面板弹出位置 + */ + position: { type: Object as PropType } +}; +export type OptionsContainerProps = ExtractPropTypes; + /** * options 属性 */ diff --git a/packages/ui-vue/components/combo-list/src/components/option.tsx b/packages/ui-vue/components/combo-list/src/components/option.component.tsx similarity index 95% rename from packages/ui-vue/components/combo-list/src/components/option.tsx rename to packages/ui-vue/components/combo-list/src/components/option.component.tsx index e8b92e4fc..3175f593d 100644 --- a/packages/ui-vue/components/combo-list/src/components/option.tsx +++ b/packages/ui-vue/components/combo-list/src/components/option.component.tsx @@ -5,8 +5,7 @@ import { useOption } from '../composition/use-option'; export default defineComponent({ name: 'FOption', props: optionProps, - emits: [], - inheritAttrs: false, + emits: ['itemClick'], setup(props: OptionProps, context: SetupContext) { const { name, optionClass, onOptionClick } = useOption(props, context); diff --git a/packages/ui-vue/components/combo-list/src/components/options-container.component.tsx b/packages/ui-vue/components/combo-list/src/components/options-container.component.tsx new file mode 100644 index 000000000..dfe72905b --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/components/options-container.component.tsx @@ -0,0 +1,36 @@ +import { defineComponent, ref, SetupContext, toRefs } from 'vue'; +import { OptionsContainerProps, optionsContainerProps } from '../combo-list.props'; +import FOverlay from '../../../overlay/src/overlay.component'; +import FOptions from './options.component'; +import { useOptionsContainerEvent } from '../composition/use-options-container'; +import { EVENTS } from '../const'; + +export default defineComponent({ + name: 'FOptionsContainer', + props: optionsContainerProps, + emits: [ + EVENTS.panelHidden, + EVENTS.panelShow + ], + setup(props: OptionsContainerProps, context: SetupContext) { + const panelContainerRef = ref(); + const panelRef = ref(); + const { isPanelVisible, position } = toRefs(props); + const { onPanelContainerClick, onPanelItemClick, panelContainerClass } = useOptionsContainerEvent({ panelRef, isPanelVisible, position: position, context }); + return () => { + return ( + isPanelVisible.value && ( + +
    +
    +
    + +
    +
    +
    +
    + ) + ); + }; + } +}); \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/components/options.component.tsx b/packages/ui-vue/components/combo-list/src/components/options.component.tsx new file mode 100644 index 000000000..fc84f0589 --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/components/options.component.tsx @@ -0,0 +1,24 @@ +import { defineComponent, SetupContext } from 'vue'; +import { optionsProps, OptionsProps } from '../combo-list.props'; +import { Option } from '../types'; +import FOption from './option.component'; + +export default defineComponent({ + name: 'FOptions', + props: optionsProps, + emits: ['itemClick'], + setup(props: OptionsProps, context: SetupContext) { + const onItemClick = function () { + context.emit('itemClick'); + }; + return () => { + return ( +
      + {props?.options?.map((option: Option) => ( + + ))} +
    + ); + }; + } +}); \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/components/options.tsx b/packages/ui-vue/components/combo-list/src/components/options.tsx deleted file mode 100644 index 15883d91a..000000000 --- a/packages/ui-vue/components/combo-list/src/components/options.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { defineComponent, SetupContext } from 'vue'; -import { optionsProps, OptionsProps } from '../combo-list.props'; -import { IOption } from '../types'; -import FOption from '../components/option'; - -export default defineComponent({ - name: 'FOptions', - props: optionsProps, - emits: [], - inheritAttrs: false, - setup(props: OptionsProps, context: SetupContext) { - return () => { - return ( -
      - {props?.options?.map((option: IOption) => ( - - ))} -
    - ); - }; - } -}); \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/composition/index.ts b/packages/ui-vue/components/combo-list/src/composition/index.ts index d61051f3f..5932263a8 100644 --- a/packages/ui-vue/components/combo-list/src/composition/index.ts +++ b/packages/ui-vue/components/combo-list/src/composition/index.ts @@ -1,3 +1,3 @@ -export * from './use-state'; -// export * from './use-event-handlers'; export * from './use-option'; +export * from './use-combo-list'; +export * from './use-options-container'; \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts b/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts index 379860b6a..e8c6d7ccf 100644 --- a/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts +++ b/packages/ui-vue/components/combo-list/src/composition/use-combo-list.ts @@ -1,15 +1,70 @@ -import { computed, reactive, ref, Ref, SetupContext } from "vue"; +import { ref, Ref, SetupContext, watch } from "vue"; import { ComboListProps } from "../combo-list.props"; -import { IOption, ModelValue } from "../types"; +import { Option, Position, UseComboListEvent, UseComboListEventHandlers } from "../types"; -export function useComboList(props: ComboListProps, context: SetupContext, modelValue: Ref, panelRef: Ref) { - const isPanelVisible = ref(false); +export function useComboList(props: ComboListProps, context: SetupContext) { const displayText = ref(''); - const panelContainerClass = computed(() => ({ - comboPanel: true, - 'f-area-hide': !isPanelVisible.value, - 'f-area-show': isPanelVisible.value - })); + const modelValue = ref(props.modelValue); + const isPanelVisible = ref(false); + const position: Ref = ref(undefined); + /** + * 值变化事件处理器 + * @param item item + */ + function onValueChange(item: Option) { + if (modelValue) { + modelValue.value = item.value; + } + if (props.multiSelect) { + + } else { + displayText.value = item.name; + context.emit('update:modelValue', item.value); + } + } + return { + displayText, + modelValue, + isPanelVisible, + position, + onValueChange + }; +} +/** + * 下拉列表事件处理钩子 + * @param options 上下文 + */ +export function useComboListEvent(options: UseComboListEvent): UseComboListEventHandlers { + // 监听输入框下拉按钮事件 + const { isPanelVisible, context, position } = options; + /** + * window窗口大小变化事件处理器 + * @param event + */ + const windowResizeEventHandler = (event: UIEvent) => { + if (isPanelVisible.value !== false) { + isPanelVisible.value = false; + } + }; + /** + * window窗口滚动条滚动事件处理器 + * @param event + */ + const windowScrollEventHandler = (event: Event) => { + if (isPanelVisible.value !== false) { + isPanelVisible.value = false; + } + }; + watch([isPanelVisible], ([isPanelVisibleValue]) => { + if (isPanelVisibleValue) { + window.addEventListener('resize', windowResizeEventHandler); + window.addEventListener('scroll', windowScrollEventHandler); + } else { + window.removeEventListener('resize', windowResizeEventHandler); + window.removeEventListener('scroll', windowScrollEventHandler); + } + }); + //#region events /** * 清空事件 @@ -23,43 +78,25 @@ export function useComboList(props: ComboListProps, context: SetupContext, model * 下拉按钮点击 * @param $event event */ - function onButtonClick($event: Event) { - const clickOutsideEventHandler = (event: any) => { - if (panelRef.value == event.target || panelRef?.value?.contains(event.target)) { - event.stopPropagation(); - } else { - isPanelVisible.value = false; - } - }; + function onButtonClick($event: any) { + // 下拉组件dom + const combolistEl = $event?.origin?.target?.closest('.input-group'); + // 记录事件上下文 + position.value = combolistEl?.getBoundingClientRect(); isPanelVisible.value = !isPanelVisible.value; - if (isPanelVisible.value === true) { - // 即将展示下拉面板 - document.addEventListener("click", clickOutsideEventHandler); - } else { - document.removeEventListener('click', clickOutsideEventHandler); - } } /** - * - * @param item 值变化事件处理器 + * 下拉面板收起事件处理器 + * @param $event */ - function onValueChange(item: IOption) { - modelValue.value = item.value; - if (props.multiSelect) { - - } else { - displayText.value = item.name; - context.emit('update:modelValue', item.value); - } + function onPanelHidden($event: any) { isPanelVisible.value = false; } //#endregion + return { - panelContainerClass, - isPanelVisible, - displayText, onClear, onButtonClick, - onValueChange + onPanelHidden }; } \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/composition/use-option.ts b/packages/ui-vue/components/combo-list/src/composition/use-option.ts index adb4b09af..a13c49a81 100644 --- a/packages/ui-vue/components/combo-list/src/composition/use-option.ts +++ b/packages/ui-vue/components/combo-list/src/composition/use-option.ts @@ -1,24 +1,25 @@ -import { computed, inject, Ref, SetupContext } from "vue"; -import { IUseOption } from "../types"; +import { computed, inject, SetupContext } from "vue"; +import { UseOption } from "../types"; import { OptionProps } from "../combo-list.props"; import { COMBO_LIST_TOKEN } from "../const"; -export function useOption(props: OptionProps, context: SetupContext): IUseOption { - const comboList = inject(COMBO_LIST_TOKEN, null); +export function useOption(props: OptionProps, context: SetupContext): UseOption { + const comboListContext = inject(COMBO_LIST_TOKEN, null); const name = computed(() => { return props.name || props.value; }); const onOptionClick = ($event: Event) => { if (!props.disabled) { - comboList?.onValueChange({ + comboListContext?.onValueChange({ value: props.value, name: props.name, disabled: props.disabled }); + context.emit('itemClick'); } }; const isOptionSelected = computed(() => { - return comboList?.modelValue === props.value; + return comboListContext?.modelValue === props.value; }); const optionClass = computed(() => ({ active: isOptionSelected.value, diff --git a/packages/ui-vue/components/combo-list/src/composition/use-options-container.ts b/packages/ui-vue/components/combo-list/src/composition/use-options-container.ts new file mode 100644 index 000000000..61917bf61 --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/composition/use-options-container.ts @@ -0,0 +1,47 @@ +import { computed, watch } from "vue"; +import { EVENTS } from "../const"; +import { UseOptionsContainerEvent, UseOptionsContainerEventHandlers } from "../types"; + +export function useOptionsContainer() { +} +/** + * 下拉列表事件处理钩子 + * @param options 上下文 + */ +export function useOptionsContainerEvent(options: UseOptionsContainerEvent): UseOptionsContainerEventHandlers { + // 监听输入框下拉按钮事件 + const { isPanelVisible, panelRef, position, context } = options; + // 下拉面板样式 + const panelContainerClass = computed(() => ({ + 'f-area-hide': !isPanelVisible.value, + 'f-area-show': isPanelVisible.value + })); + // 监听面板显隐变化 + watch([panelRef, isPanelVisible], ([panelEl, isPanelVisibleValue]) => { + if (isPanelVisibleValue && panelEl && position) { + panelEl.style.top = ((position.value?.top || 0) + (position.value?.height || 0)) + 'px'; + panelEl.style.left = position.value?.left + 'px'; + panelEl.style.width = position.value?.width + 'px'; + // 触发面板展示事件 + context.emit(EVENTS.panelShow); + } + }); + + //#region events + /** + * 下拉列表容器点击事件处理器 + * @param $event event + */ + function onPanelContainerClick($event: any) { + context.emit(EVENTS.panelHidden); + } + function onPanelItemClick($event: any){ + context.emit(EVENTS.panelHidden); + } + //#endregion + return { + onPanelContainerClick, + onPanelItemClick, + panelContainerClass + }; +} diff --git a/packages/ui-vue/components/combo-list/src/composition/use-state.ts b/packages/ui-vue/components/combo-list/src/composition/use-state.ts deleted file mode 100644 index f25ca7abb..000000000 --- a/packages/ui-vue/components/combo-list/src/composition/use-state.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ref } from 'vue'; -import { IUseState } from '../types'; - -/** - * state hook - * @param initial 初始值 - * @returns [state,action] - */ -export function useState(initial: T): IUseState { - if (typeof initial === 'undefined') { - throw new Error('invalid initial: initial must have value.'); - } - const state = ref(initial); - const action = (value: any) => { - state.value = value; - }; - return [state, action]; -} \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/const.ts b/packages/ui-vue/components/combo-list/src/const.ts index c72d4eb47..23225d1ec 100644 --- a/packages/ui-vue/components/combo-list/src/const.ts +++ b/packages/ui-vue/components/combo-list/src/const.ts @@ -1,8 +1,15 @@ -import { InjectionKey } from "vue"; +import { InjectionKey, Ref } from "vue"; import { ComboListContext } from "./types"; export const groupIcon = ''; /** * combo list injector token */ -export const COMBO_LIST_TOKEN: InjectionKey = Symbol('fComboList'); \ No newline at end of file +export const COMBO_LIST_TOKEN: InjectionKey> = Symbol('fComboList'); + +export const EVENTS = { + clear: 'clear', + panelShow: 'panelShow', + panelHidden: 'panelHidden', + itemClick: 'itemClick' +}; \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/types.ts b/packages/ui-vue/components/combo-list/src/types.ts index 4911ceeb3..100ba348d 100644 --- a/packages/ui-vue/components/combo-list/src/types.ts +++ b/packages/ui-vue/components/combo-list/src/types.ts @@ -1,5 +1,6 @@ //import EventEmitter from 'events'; -import { ComputedRef, Ref, UnwrapRef } from 'vue'; +import { ComputedRef,Ref, SetupContext } from 'vue'; +import { ComboListProps } from './combo-list.props'; export type ModelValue = number | string | undefined | Array; /** * 数据展现方式 @@ -8,34 +9,78 @@ export enum ViewType { Text = 'text', Tag = 'tag' }; -/** - * IUseState type - * @description 受vue类型系统限制,仅在类型定义中约定state只读,实际返回对象可写 - */ -export type IUseState = [Readonly>>, (state: UnwrapRef) => void]; - -export interface IUseEventHandler { - /** - * 清除事件 - */ - onClear: ($event: Event) => void; - onButtonClick: ($event: Event) => void; -} -export interface IUseOption { +export interface UseOption { name: ComputedRef; onOptionClick: ($event: Event) => void; optionClass: ComputedRef; } -export interface IOption { +export interface Option { value: string | number | undefined; name: string | undefined; disabled: boolean; } -export type Options = Array; +export type Options = Array