diff --git a/devui/editable-select/index.ts b/devui/editable-select/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b053c0c34ec8e88740a3949906891fed6fabf66 --- /dev/null +++ b/devui/editable-select/index.ts @@ -0,0 +1,20 @@ +import type { App } from 'vue' +import EditableSelect from './src/editable-select' +import EditableSelectOption from './src/components/option' + +EditableSelect.install = function (app: App): void { + app.component(EditableSelect.name, EditableSelect) + app.component(EditableSelectOption.name, EditableSelectOption) + +} + +export { EditableSelect, EditableSelectOption } + +export default { + title: 'EditableSelect 可输入下拉选择框', + category: '数据录入', + status: undefined, // TODO: 组件若开发完成则填入"已完成",并删除该注释 + install(app: App): void { + app.use(EditableSelect as any) + } +} diff --git a/devui/editable-select/src/components/option/index.tsx b/devui/editable-select/src/components/option/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e717d15849fa12b0d329e13d6251dd52576065a3 --- /dev/null +++ b/devui/editable-select/src/components/option/index.tsx @@ -0,0 +1,11 @@ +import { defineComponent } from 'vue'; + +export default defineComponent({ + name: 'DEditableSelectOption', + setup(props, ctx) { + const defaultSlot = ctx.slots.default && ctx.slots.default(); + return () => { + return
  • {defaultSlot}
  • ; + }; + }, +}); diff --git a/devui/editable-select/src/editable-select-types.ts b/devui/editable-select/src/editable-select-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b35deb5e3626d97d28325bea4e7211f4607eaaf --- /dev/null +++ b/devui/editable-select/src/editable-select-types.ts @@ -0,0 +1,22 @@ +import type { ExtractPropTypes } from 'vue' + +export const editableSelectProps = { + /* test: { + type: Object as PropType<{ xxx: xxx }> + } */ + width: { + type: Number, + default: 450 + }, + appendToBody: { + type: Boolean, + default: true, + }, + maxHeight: { + type: Number, + default: 300 + } + +} as const + +export type EditableSelectProps = ExtractPropTypes diff --git a/devui/editable-select/src/editable-select.scss b/devui/editable-select/src/editable-select.scss new file mode 100644 index 0000000000000000000000000000000000000000..0af1fe5e0cec5c839d5fa0ac63c173acb19a0148 --- /dev/null +++ b/devui/editable-select/src/editable-select.scss @@ -0,0 +1,106 @@ +@import '../../style/theme/color'; +@import '../../style/core/animation'; + +.devui-select-chevron-icon { + display: inline-flex; + vertical-align: middle; + transition: + transform $devui-animation-duration-slow + $devui-animation-ease-in-out-smooth; +} + +.devui-select-open .devui-select-chevron-icon { + transform: rotate(180deg); +} + +.devui-select-chevron-icon svg path { + fill: $devui-text-weak; // TODO: Color-Question +} + +input::-ms-clear { + display: none; +} + +.devui-no-data-tip { + user-select: none; + cursor: not-allowed; +} + +.devui-form-control { + outline: none; + padding-right: 24px; +} + +.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-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; + + &: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; +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..74d1f04683bed79b66749d42fe214b10ce856c49 --- /dev/null +++ b/devui/editable-select/src/editable-select.tsx @@ -0,0 +1,71 @@ +import './editable-select.scss'; + +import { defineComponent, ref, reactive, renderSlot } from 'vue'; +import { + editableSelectProps, + EditableSelectProps, +} from './editable-select-types'; +import { Icon } from '../../icon'; + +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', + }); + + const toggleMenu = () => { + visible.value = !visible.value; + }; + + return () => { + return ( + <> +
    + + +
    + +
    +
    +
      + {renderSlot(ctx.slots, 'default')} +
    +
    +
    +
    + + ); + }; + }, +}); diff --git a/devui/editable-select/src/utils/index.ts b/devui/editable-select/src/utils/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb6e5e684158315280b0aafec4bfdb4b319298d1 --- /dev/null +++ b/devui/editable-select/src/utils/index.ts @@ -0,0 +1,18 @@ +/** + * 动态获取class字符串 + * @param classStr 是一个字符串,固定的class名 + * @param classOpt 是一个对象,key表示class名,value为布尔值,true则添加,否则不添加 + * @returns 最终的class字符串 + */ +export function className( + classStr: string, + classOpt?: { [key: string]: boolean; } +): string { + let classname = classStr; + if (typeof classOpt === 'object') { + Object.keys(classOpt).forEach((key) => { + classOpt[key] && (classname += ` ${key}`); + }); + } + return classname; +} \ No newline at end of file diff --git a/devui/style/core/_dropdown.scss b/devui/style/core/_dropdown.scss index a4332380bda053d9e8dd811cd61188618ad5fb85..085045d0498aa8864e4a4404185f2783f49df889 100755 --- a/devui/style/core/_dropdown.scss +++ b/devui/style/core/_dropdown.scss @@ -48,6 +48,7 @@ z-index: 1000; display: none; min-width: calc(min(100%, 102px)); + margin: 4px 0; padding-bottom: 5px; background-clip: padding-box; border-radius: 2px; diff --git a/docs/.vitepress/config/sidebar.ts b/docs/.vitepress/config/sidebar.ts index aca6bee619416b36d9e9e6fda5be2b21341e973a..e196e88b9124bf8a192251dd95b3c785693d763c 100644 --- a/docs/.vitepress/config/sidebar.ts +++ b/docs/.vitepress/config/sidebar.ts @@ -12,7 +12,7 @@ const sidebar = { { text: 'Search 搜索框', link: '/components/search/', status: '已完成' }, { text: 'Status 状态', link: '/components/status/', status: '已完成' }, { text: 'Sticky 便贴', link: '/components/sticky/' }, - { text: 'Overlay 遮罩层', link: '/components/overlay/'} + { text: 'Overlay 遮罩层', link: '/components/overlay/' } ] }, { @@ -53,7 +53,7 @@ const sidebar = { { text: 'Checkbox 复选框', link: '/components/checkbox/', status: '已完成' }, { text: 'DatePicker 日期选择器', link: '/components/date-picker/', status: '开发中' }, { text: 'DatePickerPro 日期选择器', link: '/components/date-picker-pro/' }, - { text: 'EditableSelect 可编辑下拉框', link: '/components/editable-select/' }, + { text: 'EditableSelect 可编辑下拉框', link: '/components/editable-select/', status: '开发中' }, { text: 'Form 表单', link: '/components/form/' }, { text: 'Input 文本框', link: '/components/input/', status: '已完成' }, { text: 'InputNumber 数字输入框', link: '/components/input-number/' }, diff --git a/docs/components/editable-select/index.md b/docs/components/editable-select/index.md new file mode 100644 index 0000000000000000000000000000000000000000..f0fc0bbeee85d86c02730b7543fab57b781e4eb1 --- /dev/null +++ b/docs/components/editable-select/index.md @@ -0,0 +1,34 @@ +# EditableSelect 可输入下拉选择框 + +同时支持输入和下拉选择的输入框。 + +### 何时使用 + +当需要同时支持用户输入数据和选择已有数据的时候使用,加入输入联想功能,方便用户搜索已有数据。 + +:::demo +```vue + + + +``` +:::