Skip to content

Commit

Permalink
fix(comp: select): sync width on opened (#853)
Browse files Browse the repository at this point in the history
  • Loading branch information
danranVm authored Apr 21, 2022
1 parent 24f173e commit 7b40e6f
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 25 deletions.
4 changes: 2 additions & 2 deletions packages/cdk/scroll/src/virtual/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export const virtualListProps = {
contentRender: { type: Function as PropType<VirtualContentRenderFn>, default: undefined },
dataSource: { type: Array, default: (): unknown[] => [] },
fullHeight: { type: Boolean, default: false },
getKey: { type: [String, Function] as PropType<string | ((item: any) => VKey)>, default: undefined },
getKey: { type: [String, Function] as PropType<string | ((item: any) => VKey)>, required: true },
height: { type: Number, default: 0 },
itemHeight: { type: Number, default: 0 },
/**
* @deprecated
*/
itemKey: { type: [String, Function] as PropType<string | ((item: any) => VKey)>, required: true },
itemKey: { type: [String, Function] as PropType<string | ((item: any) => VKey)>, default: undefined },
itemRender: { type: Function as PropType<VirtualItemRenderFn>, default: undefined },
virtual: { type: Boolean, default: true },
onScroll: [Function, Array] as PropType<MaybeArray<(evt: Event) => void>>,
Expand Down
6 changes: 4 additions & 2 deletions packages/components/_private/selector/src/Selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export default defineComponent({
clearInput,
} = useInputState(props)

const getBoundingClientRect = () => elementRef.value?.getBoundingClientRect()

expose({ focus, blur, clearInput, getBoundingClientRect })

const mergedClearable = computed(() => {
return !props.disabled && !props.readonly && props.clearable && props.value.length > 0
})
Expand Down Expand Up @@ -111,8 +115,6 @@ export default defineComponent({
callEmit(props.onClear, evt)
}

expose({ focus, blur, clearInput })

provide(selectorToken, {
props,
mergedPrefixCls,
Expand Down
1 change: 1 addition & 0 deletions packages/components/_private/selector/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface SelectorBindings {
blur: () => void
focus: (options?: FocusOptions) => void
clearInput: () => void
getBoundingClientRect: () => DOMRect | undefined
}
export type SelectorComponent = DefineComponent<
Omit<HTMLAttributes, keyof SelectorPublicProps> & SelectorPublicProps,
Expand Down
8 changes: 5 additions & 3 deletions packages/components/config/src/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export const defaultConfig: GlobalConfig = {
childrenKey: 'children',
clearIcon: 'close-circle',
labelKey: 'label',
overlayMatchWidth: true,
size: 'md',
suffix: 'down',
valueKey: 'value',
Expand Down Expand Up @@ -319,12 +320,13 @@ export const defaultConfig: GlobalConfig = {
},
treeSelect: {
borderless: false,
clearIcon: 'close-circle',
size: 'md',
suffix: 'down',
childrenKey: 'children',
clearIcon: 'close-circle',
labelKey: 'label',
nodeKey: 'key',
overlayMatchWidth: false,
size: 'md',
suffix: 'down',
},
upload: {
multiple: false,
Expand Down
8 changes: 5 additions & 3 deletions packages/components/config/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ export interface SelectConfig {
childrenKey: string
clearIcon: string
labelKey: string
overlayMatchWidth: boolean
size: FormSize
suffix: string
target?: PortalTargetType
Expand Down Expand Up @@ -468,12 +469,13 @@ export interface TreeConfig {

export interface TreeSelectConfig {
borderless: boolean
clearIcon: string
size: FormSize
suffix: string
childrenKey: string
clearIcon: string
labelKey: string
nodeKey: string
overlayMatchWidth: boolean
size: FormSize
suffix: string
target?: PortalTargetType
}

Expand Down
1 change: 1 addition & 0 deletions packages/components/select/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ order: 0
| `multiple` | 多选模式 | `boolean` | `false` | - | - |
| `multipleLimit` | 最多选中多少项 | `number` | - | - | - |
| `overlayClassName` | 下拉菜单的 `class` | `string` | - | - | - |
| `overlayMatchWidth` | 下拉菜单和选择器同宽 | `boolean` | `true` || - |
| `overlayRender` | 自定义下拉菜单内容的渲染 | `(children:VNode[]) => VNodeTypes` | - | - | - |
| `placeholder` | 选择框默认文本 | `string \| #placeholder` | - | - | - |
| `readonly` | 只读模式 | `boolean` | - | - | - |
Expand Down
8 changes: 6 additions & 2 deletions packages/components/select/src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ export default defineComponent({

expose({ focus, blur, scrollTo })

const { overlayRef, overlayStyle, updateOverlay, overlayOpened, setOverlayOpened } = useOverlayState(props)
const { overlayRef, overlayStyle, updateOverlay, overlayOpened, setOverlayOpened } = useOverlayState(
props,
config,
triggerRef,
)

const accessor = useFormAccessor()

Expand Down Expand Up @@ -195,7 +199,7 @@ export default defineComponent({
clickOutside: true,
disabled: accessor.disabled.value || props.readonly,
offset: defaultOffset,
placement: 'bottom',
placement: 'bottomStart',
target: target.value,
trigger: 'manual',
triggerId: attrs.id,
Expand Down
33 changes: 28 additions & 5 deletions packages/components/select/src/composables/useOverlayState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { type CSSProperties, type ComputedRef, type Ref, computed, onMounted, ref } from 'vue'
import { type CSSProperties, type ComputedRef, type Ref, computed, onMounted, ref, watchEffect } from 'vue'

import { convertCssPixel, useControlledProp, useState } from '@idux/cdk/utils'
import { type ɵOverlayInstance } from '@idux/components/_private/overlay'
import { type ɵSelectorInstance } from '@idux/components/_private/selector'

export interface OverlayStateContext {
overlayRef: Ref<ɵOverlayInstance | undefined>
Expand All @@ -18,21 +19,43 @@ export interface OverlayStateContext {
setOverlayOpened: (open: boolean) => void
}

export function useOverlayState(props: { open?: boolean; autofocus?: boolean }): OverlayStateContext {
export function useOverlayState(
props: {
open?: boolean
autofocus?: boolean
overlayMatchWidth?: boolean
},
config: { overlayMatchWidth: boolean },
triggerRef: Ref<ɵSelectorInstance | undefined>,
): OverlayStateContext {
const overlayRef = ref<ɵOverlayInstance>()
const [overlayWidth, setOverlayWidth] = useState('')
const overlayStyle = computed(() => ({ width: overlayWidth.value }))
const overlayStyle = computed(() => {
const { overlayMatchWidth = config.overlayMatchWidth } = props
return { [overlayMatchWidth ? 'width' : 'minWidth']: overlayWidth.value }
})
const [overlayOpened, setOverlayOpened] = useControlledProp(props, 'open', false)

const updateOverlay = (rect: DOMRect) => {
setOverlayWidth(convertCssPixel(rect.width))
overlayOpened.value && overlayRef.value?.updatePopper()
const { width } = rect
if (width > 0) {
setOverlayWidth(convertCssPixel(width))
overlayOpened.value && overlayRef.value?.updatePopper()
}
}

onMounted(() => {
if (props.autofocus) {
setOverlayOpened(true)
}

// see https://github.com/IDuxFE/idux/issues/488
watchEffect(() => {
const overlayInstance = overlayRef.value
if (overlayInstance && overlayOpened.value) {
updateOverlay(triggerRef.value!.getBoundingClientRect()!)
}
})
})

return { overlayRef, overlayStyle, updateOverlay, overlayOpened, setOverlayOpened }
Expand Down
1 change: 1 addition & 0 deletions packages/components/select/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const selectProps = {
*/
options: { type: Array as PropType<SelectData[]>, default: undefined },
overlayClassName: { type: String, default: undefined },
overlayMatchWidth: { type: Boolean, default: undefined },
overlayRender: { type: Function as PropType<(children: VNode[]) => VNodeChild>, default: undefined },
placeholder: { type: String, default: undefined },
readonly: { type: Boolean, default: false },
Expand Down
1 change: 1 addition & 0 deletions packages/components/tree-select/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ order: 0
| `treeDisabled` | 树的禁用节点的函数 | 参考 [Tree](/components/tree/zh#API) | - | - | - |
| `virtual` | 是否开启虚拟滚动 | `boolean` | `false` | - | - |
| `overlayClassName` | 下拉菜单的 `class` | `string` | - | - | - |
| `overlayMatchWidth` | 下拉菜单和选择器同宽 | `boolean` | `false` || - |
| `overlayRender` | 自定义下拉菜单内容的渲染 | `(children:VNode[]) => VNodeTypes` | - | - | - |
| `placeholder` | 选择框默认文本 | `string` | - | - | - |
| `readonly` | 只读模式 | `boolean` | - | - | - |
Expand Down
8 changes: 6 additions & 2 deletions packages/components/tree-select/src/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export default defineComponent({
accessor,
mergedNodeMap,
)
const { overlayRef, overlayStyle, updateOverlay, overlayOpened, setOverlayOpened } = ɵUseOverlayState(props)
const { overlayRef, overlayStyle, updateOverlay, overlayOpened, setOverlayOpened } = ɵUseOverlayState(
props,
config,
triggerRef,
)

const treeRef = ref<TreeInstance>()
const scrollTo: VirtualScrollToFn = options => {
Expand Down Expand Up @@ -168,7 +172,7 @@ export default defineComponent({
clickOutside: true,
disabled: accessor.disabled.value || props.readonly,
offset: defaultOffset,
placement: 'bottom',
placement: 'bottomStart',
target: target.value,
trigger: 'manual',
triggerId: attrs.id,
Expand Down
1 change: 1 addition & 0 deletions packages/components/tree-select/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const treeSelectProps = {
multiple: { type: Boolean, default: false },
nodeKey: { type: [String, Function] as PropType<string | ((node: TreeSelectNode) => VKey)>, default: undefined },
overlayClassName: { type: String, default: undefined },
overlayMatchWidth: { type: Boolean, default: undefined },
overlayRender: { type: Function as PropType<(children: VNode[]) => VNodeChild>, default: undefined },
placeholder: { type: String, default: undefined },
readonly: { type: Boolean, default: false },
Expand Down
15 changes: 9 additions & 6 deletions packages/site/src/styles/components.less
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@
}

// select
[id^='components-select-demo-'] .ix-selector {
&-single {
width: 120px;
}
[id^='components-select-demo-'],
[id^='components-tree-select-demo-'] {
.ix-selector {
&-single {
width: 120px;
}

&-multiple {
width: 300px;
&-multiple {
width: 300px;
}
}
}

Expand Down

0 comments on commit 7b40e6f

Please sign in to comment.