diff --git a/devui/editable-select/src/components/option/index.tsx b/devui/editable-select/src/components/option/index.tsx index e717d15849fa12b0d329e13d6251dd52576065a3..1dfd5c5c9dcf4814d07c5772d37d4ff5c4cce0f3 100644 --- a/devui/editable-select/src/components/option/index.tsx +++ b/devui/editable-select/src/components/option/index.tsx @@ -1,11 +1,36 @@ -import { defineComponent } from 'vue'; - +import { defineComponent, renderSlot, getCurrentInstance, inject } from 'vue' +import { className } from '../../utils/index' +import { selectKey } from '../../editable-select-types' export default defineComponent({ name: 'DEditableSelectOption', + props: { + label: { + type: [String, Number], + }, + disabled: { + type: Boolean, + default: false, + }, + }, setup(props, ctx) { - const defaultSlot = ctx.slots.default && ctx.slots.default(); + const optionsClassName = className('devui-dropdown-item', { + disabled: props.disabled, + }) + const instance = getCurrentInstance() + + const select = inject(selectKey) + + const selectOptionClick = () => { + if (!props.disabled) { + select.handleOptionSelect(instance) + } + } return () => { - return
  • {defaultSlot}
  • ; - }; + return ( +
  • + {props.label ? props.label : renderSlot(ctx.slots, 'default')} +
  • + ) + } }, -}); +}) diff --git a/devui/editable-select/src/editable-select-types.ts b/devui/editable-select/src/editable-select-types.ts index 4b35deb5e3626d97d28325bea4e7211f4607eaaf..cf83a584267d36446362cd7471f8bbf4aba4cdf1 100644 --- a/devui/editable-select/src/editable-select-types.ts +++ b/devui/editable-select/src/editable-select-types.ts @@ -1,5 +1,6 @@ -import type { ExtractPropTypes } from 'vue' - +import type { ExtractPropTypes, Ref, PropType, InjectionKey } from 'vue' +export type ModelValue = number | string | Array +// porps export const editableSelectProps = { /* test: { type: Object as PropType<{ xxx: xxx }> @@ -15,8 +16,41 @@ export const editableSelectProps = { maxHeight: { type: Number, default: 300 - } - + }, + disabled: { + type: Boolean, + default: false + }, + modelValue: { + type: [String, Number, Array] as PropType + }, + 'onUpdate:modelValue': { + type: Function as PropType<(val: ModelValue) => void>, + }, } as const export type EditableSelectProps = ExtractPropTypes +type HorizontalConnectionPos = 'left' | 'center' | 'right'; +type VerticalConnectionPos = 'top' | 'center' | 'bottom'; + +interface ConnectionPosition { + originX: HorizontalConnectionPos + originY: VerticalConnectionPos + overlayX: HorizontalConnectionPos + overlayY: VerticalConnectionPos +} + +export interface SelectStatesReturnType { + visible: boolean + origin: Ref + position: ConnectionPosition +} +export interface SelectReturnType { + toggleMenu: () => void + handleOptionSelect: (vm: unknown) => void +} + +export const selectKey = 'DSelect' as unknown as InjectionKey +export interface SelectContext { + handleOptionSelect(vm: unknown): void +} diff --git a/devui/editable-select/src/editable-select.scss b/devui/editable-select/src/editable-select.scss index 0b51029ae32c08ee188e25f46a064aebea9e06d7..94567edcb2db2f0608d459a8db2d5e94ee04e624 100644 --- a/devui/editable-select/src/editable-select.scss +++ b/devui/editable-select/src/editable-select.scss @@ -49,58 +49,78 @@ line-height: 26px; } +.devui-dropdown-bg.devui-dropdown-bg { + background-color: inherit; +} // 下拉部分 -.devui-dropdown-wrap { +.devui-editable-select { .devui-dropdown-menu { width: 100%; display: block; + } - .devui-dropdown-item { - cursor: pointer; - display: block; - width: 100%; - padding: 8px 12px; - clear: both; - border: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 14px; - } + .devui-dropdown-item { + cursor: pointer; + display: block; + width: 100%; + padding: 8px 12px; + clear: both; + border: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 14px; + } + .devui-dropdown-menu { .devui-dropdown-item:not(.disabled) { &.selected { color: $devui-list-item-active-text; background-color: $devui-list-item-active-bg; } } - // 选项disabled - .devui-dropdown-item.disabled, - .devui-dropdown-item.disabled:hover { - cursor: not-allowed; - color: $devui-disabled-text; + } + + .devui-no-result-template, + .devui-is-searching-template { + display: block; + width: 100%; + padding: 8px 12px; + clear: both; + border: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: not-allowed; + background-color: $devui-disabled-bg; + color: $devui-disabled-text; + line-height: 14px; + + &:hover, + &:active, + &:hover:active { + background-color: $devui-unavailable; } } -} + // 选项disabled + .devui-dropdown-item.disabled, + .devui-dropdown-item.disabled:hover { + cursor: not-allowed; + color: $devui-disabled-text; + } -.devui-no-result-template, -.devui-is-searching-template { - display: block; - width: 100%; - padding: 8px 12px; - clear: both; - border: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - cursor: not-allowed; - background-color: $devui-disabled-bg; - color: $devui-disabled-text; - line-height: 14px; - - &:hover, - &:active, - &:hover:active { - background-color: $devui-unavailable; + ul.devui-list-unstyled { + margin: 0; + overflow-y: auto; + padding: 0; + } + + .devui-dropdown-bg { + background: $devui-list-item-hover-bg; + } + + .devui-popup-tips { + color: $devui-text-weak; + padding: 4px 12px; } } diff --git a/devui/editable-select/src/editable-select.tsx b/devui/editable-select/src/editable-select.tsx index 74d1f04683bed79b66749d42fe214b10ce856c49..f76b6e430d5eda57392572636429a9d1c7d3c077 100644 --- a/devui/editable-select/src/editable-select.tsx +++ b/devui/editable-select/src/editable-select.tsx @@ -1,55 +1,65 @@ -import './editable-select.scss'; - -import { defineComponent, ref, reactive, renderSlot } from 'vue'; +import './editable-select.scss' +import { + defineComponent, + reactive, + toRefs, + renderSlot, + provide, + SetupContext, +} from 'vue' import { editableSelectProps, EditableSelectProps, -} from './editable-select-types'; -import { Icon } from '../../icon'; - + selectKey, +} from './editable-select-types' +import { Icon } from '../../icon' +import { FlexibleOverlay } from '../../overlay' +import { useSelectStates, useSelect } from './hooks/use-select' +import { className } from './utils/index' export default defineComponent({ name: 'DEditableSelect', props: editableSelectProps, - emits: [], - setup(props: EditableSelectProps, ctx) { - const origin = ref(null); - const visible = ref(false); - const position = reactive({ - originX: 'left', - originY: 'bottom', - overlayX: 'left', - overlayY: 'top', - }); + emits: ['update:modelValue'], + + setup(props: EditableSelectProps, ctx: SetupContext) { + const states = useSelectStates() + const { origin, visible } = toRefs(states) + + const inputCls = className('devui-form-control devui-dropdown-origin', { + disabled: props.disabled, + }) + const { toggleMenu, handleOptionSelect } = useSelect(props, ctx, states) - const toggleMenu = () => { - visible.value = !visible.value; - }; + provide( + selectKey, + reactive({ + handleOptionSelect, + }) + ) return () => { return ( <>
    - +
    -
    @@ -63,9 +73,9 @@ export default defineComponent({
    -
    + - ); - }; + ) + } }, -}); +}) diff --git a/devui/editable-select/src/hooks/use-select.ts b/devui/editable-select/src/hooks/use-select.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb1ad70341dd6095e6f1658c01ac48a80af447af --- /dev/null +++ b/devui/editable-select/src/hooks/use-select.ts @@ -0,0 +1,33 @@ +import { ref, reactive, computed, SetupContext } from 'vue' +import { EditableSelectProps, SelectStatesReturnType, SelectReturnType } from '../editable-select-types' +export type States = ReturnType +export function useSelectStates(): SelectStatesReturnType { + return reactive({ + visible: false, + origin: ref(null), + position: { + originX: 'left', + originY: 'bottom', + overlayX: 'left', + overlayY: 'top', + }, + }) +} + +export function useSelect(props: EditableSelectProps, ctx: SetupContext, states: States,): SelectReturnType { + const selectDisabled = computed(() => props.disabled) + + const toggleMenu = () => { + if (!selectDisabled.value) { + states.visible = !states.visible; + } + }; + const handleOptionSelect = (optionInstance) => { + ctx.emit('update:modelValue', optionInstance.proxy.label) + states.visible = false + } + return { + toggleMenu, + handleOptionSelect + } +} \ No newline at end of file diff --git a/devui/editable-select/src/utils/index.ts b/devui/editable-select/src/utils/index.ts index fb6e5e684158315280b0aafec4bfdb4b319298d1..52113e135dd549f3952652283d02db3ed03a3580 100644 --- a/devui/editable-select/src/utils/index.ts +++ b/devui/editable-select/src/utils/index.ts @@ -8,11 +8,11 @@ export function className( classStr: string, classOpt?: { [key: string]: boolean; } ): string { - let classname = classStr; + let classname = classStr if (typeof classOpt === 'object') { Object.keys(classOpt).forEach((key) => { classOpt[key] && (classname += ` ${key}`); }); } - return classname; + return classname } \ No newline at end of file diff --git a/docs/components/editable-select/index.md b/docs/components/editable-select/index.md index f0fc0bbeee85d86c02730b7543fab57b781e4eb1..3c4124b873591dc98ca0f5059f25f797c9c99fe7 100644 --- a/docs/components/editable-select/index.md +++ b/docs/components/editable-select/index.md @@ -9,8 +9,8 @@ :::demo ```vue