();
- // 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, modelValue });
+ 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 068830f2defa62955d2a998c1c8cc44a8ddf1d52..1418a77e55c76609aec15e5d617e7172697d43a8 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 e8b92e4fc3fe449753a37d60758686c6fd65c0e5..3175f593d59a0ca17fc245b4170dc3919cda8fb5 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 0000000000000000000000000000000000000000..dfe72905bc1cfb147103cf5c334647708812bd9a
--- /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 0000000000000000000000000000000000000000..fc84f0589045c9b184a97419e91332d95d431c27
--- /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 15883d91a9cbd68f7c59165d98cf7f24ef88655f..0000000000000000000000000000000000000000
--- 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 d61051f3f3a58b49fef2de39e8d103d1c7ebf4ea..5932263a8cb8016772c8fd3e43d7f3d7570f7486 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 379860b6a76c042c60b7d4f7ad6dd8dc6423f5e7..90fe4e83ae8e6649fe0a62f8fd021d0edd6cad47 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,65 +1,102 @@
-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, modelValue } = 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
/**
* 清空事件
* @param $event event
*/
function onClear($event: Event) {
- //modelValue.value = '';
+ 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;
- }
- };
+ 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 adb4b09af1b445ea5ba2661a927d94d64c8e848c..a13c49a81a95f3035c68ec0380a31c433a903a0a 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 0000000000000000000000000000000000000000..61917bf613d56e5927c56e6eb0845d8faeb56920
--- /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 f25ca7abbb0f93e581d42a89d77c214eb55613ac..0000000000000000000000000000000000000000
--- 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 c72d4eb47a0478c87a0c6f6a7a8d2a75ef350e9d..23225d1ec589dca7e7f148bf258cf90a3ce4b06a 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 4911ceeb34d647fa1d013c5619ddee48c6be7153..48d31e3606cb9cbe708ef7344b4431310bc6aa55 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,82 @@ 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]