From 6e044f18690bd7553e9faeb6d22fd69cdb96f471 Mon Sep 17 00:00:00 2001 From: chenxi_24 Date: Mon, 11 Oct 2021 21:35:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E5=8A=9F=E8=83=BD=E5=A2=9E=E5=8A=A0=E7=A6=81=E7=94=A8?= =?UTF-8?q?=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/option/index.tsx | 44 ++++++- .../src/editable-select-types.ts | 43 +++++- .../editable-select/src/editable-select.scss | 122 +++++++++--------- devui/editable-select/src/editable-select.tsx | 58 +++++---- devui/editable-select/src/hooks/use-select.ts | 33 +++++ docs/components/editable-select/index.md | 9 +- 6 files changed, 216 insertions(+), 93 deletions(-) create mode 100644 devui/editable-select/src/hooks/use-select.ts diff --git a/devui/editable-select/src/components/option/index.tsx b/devui/editable-select/src/components/option/index.tsx index e717d158..d063d4dc 100644 --- a/devui/editable-select/src/components/option/index.tsx +++ b/devui/editable-select/src/components/option/index.tsx @@ -1,11 +1,47 @@ -import { defineComponent } from 'vue'; - +import { + defineComponent, + renderSlot, + computed, + 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 currentLabel = computed(() => { + return props.label; + }); + const instance = getCurrentInstance(); + + const select = inject(selectKey); + + const selectOptionClick = () => { + if (!props.disabled) { + select.handleOptionSelect(instance); + } + }; return () => { - return
  • {defaultSlot}
  • ; + return ( +
  • + {currentLabel.value + ? currentLabel.value + : 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 4b35deb5..f22285f4 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,42 @@ 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>, + default: undefined, + }, } 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 0af1fe5e..1c1cf538 100644 --- a/devui/editable-select/src/editable-select.scss +++ b/devui/editable-select/src/editable-select.scss @@ -34,73 +34,79 @@ input::-ms-clear { .devui-form-group.devui-has-feedback > .devui-form-control-feedback { line-height: 26px; } -// 下拉部分 -.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-bg.devui-dropdown-bg { + background-color: inherit; } +// 下拉部分 +.devui-editable-select { + .devui-dropdown-menu { + width: 100%; + display: block; + } -.devui-dropdown-menu { - .devui-dropdown-item:not(.disabled) { - &.selected { - color: $devui-list-item-active-text; - background-color: $devui-list-item-active-bg; + .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; + } } } -} -.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; + .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; + &: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; } -} -// 选项disabled -.devui-dropdown-item.disabled, -.devui-dropdown-item.disabled:hover { - cursor: not-allowed; - color: $devui-disabled-text; -} -ul.devui-list-unstyled { - margin: 0; - overflow-y: auto; - padding: 0; -} + ul.devui-list-unstyled { + margin: 0; + overflow-y: auto; + padding: 0; + } -.devui-dropdown-bg { - background: $devui-list-item-hover-bg; -} + .devui-dropdown-bg { + background: $devui-list-item-hover-bg; + } -.devui-popup-tips { - color: $devui-text-weak; - padding: 4px 12px; + .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 74d1f046..997b722b 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 { + defineComponent, + reactive, + toRefs, + renderSlot, + provide, + SetupContext, +} from 'vue'; import { editableSelectProps, EditableSelectProps, + 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,7 +73,7 @@ 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 00000000..eb1ad703 --- /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/docs/components/editable-select/index.md b/docs/components/editable-select/index.md index f0fc0bbe..86b02520 100644 --- a/docs/components/editable-select/index.md +++ b/docs/components/editable-select/index.md @@ -9,8 +9,8 @@ :::demo ```vue