Skip to content

Commit

Permalink
feat(select): add prepend and append slots
Browse files Browse the repository at this point in the history
  • Loading branch information
qmhc committed Oct 18, 2023
1 parent 7bb9822 commit 6eb864f
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 121 deletions.
4 changes: 2 additions & 2 deletions components/form/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ export function useFieldStore<V = unknown>(onFocus?: () => void) {

// Block the provided if there are dependencies between control components.
// e.g. AutoComplete -> Select, ColorPicker -> Input
provide(FIELD_OPTIONS, null)
provide(FIELD_OPTIONS, null!)
fieldOptions.sync(instance)
onFocus && fieldOptions.emitter.on('focus', onFocus)

onBeforeUnmount(() => {
fieldOptions.unsync(instance)
fieldOptions.unSync(instance)
onFocus && fieldOptions.emitter.off('focus', onFocus)
})

Expand Down
151 changes: 82 additions & 69 deletions components/select/select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -228,74 +228,87 @@
@click.stop="focus"
@after-leave="currentFilter = ''"
>
<VirtualList
ref="virtualList"
inherit
:class="[nh.be('list'), props.listClass]"
:style="{
height: listHeight,
maxHeight: `${props.maxListHeight}px`
}"
:items="totalOptions"
:item-size="32"
use-y-bar
:height="'100%'"
id-key="value"
:items-attrs="{
class: [nh.be('options'), props.optionCheck ? nh.bem('options', 'has-check') : ''],
role: 'listbox',
ariaLabel: 'options'
}"
<div
:class="[
nh.be('list'),
($slots.prepend || $slots.append) && nh.bem('list', 'with-extra'),
props.listClass
]"
>
<template #default="{ item: option, index }">
<li
v-if="option.group"
:class="[nh.ns('option-vars'), nh.be('group')]"
:title="option.label"
>
<slot name="group" :option="option" :index="index">
<div
:class="[nh.be('label'), nh.bem('label', 'group')]"
:style="{ paddingInlineStart: `${option.depth * 6}px` }"
>
{{ option.label }}
</div>
</slot>
</li>
<Option
v-else
:label="option.label"
:value="option.value"
:disabled="option.disabled || (limited && !isSelected(option))"
:divided="option.divided"
:no-title="option.title"
:hitting="option.hitting"
:selected="isSelected(option)"
no-hover
@select="handleSelect(option)"
@mousemove="updateHitting(index, false)"
>
<slot :option="option" :index="index" :selected="isSelected(option)">
<span
:class="nh.be('label')"
:style="{ paddingInlineStart: `${option.depth * 6}px` }"
>
{{ option.label }}
</span>
<Transition v-if="props.optionCheck" :name="nh.ns('fade')" appear>
<Icon v-if="isSelected(option)" v-bind="icons.check" :class="nh.be('check')"></Icon>
</Transition>
</slot>
</Option>
</template>
<template #empty>
<div :class="nh.be('empty')">
<slot name="empty">
{{ props.emptyText ?? locale.empty }}
</slot>
</div>
</template>
</VirtualList>
<slot v-if="$slots.prepend" name="prepend"></slot>
<VirtualList
ref="virtualList"
inherit
:style="{
height: undefined,
maxHeight: `${props.maxListHeight}px`
}"
:items="totalOptions"
:item-size="32"
use-y-bar
:height="'100%'"
id-key="value"
:items-attrs="{
class: [nh.be('options'), props.optionCheck ? nh.bem('options', 'has-check') : ''],
role: 'listbox',
ariaLabel: 'options'
}"
>
<template #default="{ item: option, index }">
<li
v-if="option.group"
:class="[nh.ns('option-vars'), nh.be('group')]"
:title="option.label"
>
<slot name="group" :option="option" :index="index">
<div
:class="[nh.be('label'), nh.bem('label', 'group')]"
:style="{ paddingInlineStart: `${option.depth * 6}px` }"
>
{{ option.label }}
</div>
</slot>
</li>
<Option
v-else
:label="option.label"
:value="option.value"
:disabled="option.disabled || (limited && !isSelected(option))"
:divided="option.divided"
:no-title="option.title"
:hitting="option.hitting"
:selected="isSelected(option)"
no-hover
@select="handleSelect(option)"
@mousemove="updateHitting(index, false)"
>
<slot :option="option" :index="index" :selected="isSelected(option)">
<span
:class="nh.be('label')"
:style="{ paddingInlineStart: `${option.depth * 6}px` }"
>
{{ option.label }}
</span>
<Transition v-if="props.optionCheck" :name="nh.ns('fade')" appear>
<Icon
v-if="isSelected(option)"
v-bind="icons.check"
:class="nh.be('check')"
></Icon>
</Transition>
</slot>
</Option>
</template>
<template #empty>
<div :class="nh.be('empty')">
<slot name="empty">
{{ props.emptyText ?? locale.empty }}
</slot>
</div>
</template>
</VirtualList>
<slot v-if="$slots.append" name="append"></slot>
</div>
</Popper>
</div>
</template>
Expand Down Expand Up @@ -470,7 +483,7 @@ export default defineComponent({
const currentIndex = ref(-1)
const placement = toRef(props, 'placement')
const transfer = toRef(props, 'transfer')
const listHeight = ref<string>()
// const listHeight = ref<string>()
const baseOptions = ref<SelectOptionState[]>([])
const currentFilter = ref('')
const anchorWidth = ref(0)
Expand Down Expand Up @@ -1223,7 +1236,7 @@ export default defineComponent({
currentValues,
currentLabels,
transferTo,
listHeight,
// listHeight,
optionStates,
isHover,
currentFilter,
Expand Down
16 changes: 16 additions & 0 deletions components/select/tests/select.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -756,4 +756,20 @@ describe('Select', () => {

expect(wrapper.find('.vxp-select__control').text()).toEqual('1' + OPTIONS[0])
})

it('prepend and append slots', async () => {
const wrapper = mount(Select, {
props: {
value: OPTIONS[0],
options: OPTIONS
},
slots: {
prepend: () => <span class={'prepend'}></span>,
append: () => <span class={'append'}></span>
}
})

expect(wrapper.find('.prepend').exists()).toBe(true)
expect(wrapper.find('.append').exists()).toBe(true)
})
})
41 changes: 41 additions & 0 deletions docs/demos/select/popper-extra/demo.en-US.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<Select v-model:value="value" :options="options">
<template #prepend>
<div
style="
width: 100%;
padding: 4px 16px 8px;
margin-bottom: 8px;
border-bottom: var(--vxp-border-light-2);
"
>
Put something here
</div>
</template>
<template #append>
<div
style="
width: 100%;
padding: 8px 16px 4px;
margin-top: 8px;
border-top: var(--vxp-border-light-2);
"
>
Put something here
</div>
</template>
</Select>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const value = ref('')
const options = ['Option 1', 'Option 2', 'Option 3']
</script>

<style scoped>
.vxp-select {
max-width: 400px;
}
</style>
41 changes: 41 additions & 0 deletions docs/demos/select/popper-extra/demo.zh-CN.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<Select v-model:value="value" :options="options">
<template #prepend>
<div
style="
width: 100%;
padding: 4px 16px 8px;
margin-bottom: 8px;
border-bottom: var(--vxp-border-light-2);
"
>
随便放点东西
</div>
</template>
<template #append>
<div
style="
width: 100%;
padding: 8px 16px 4px;
margin-top: 8px;
border-top: var(--vxp-border-light-2);
"
>
随便放点东西
</div>
</template>
</Select>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const value = ref('')
const options = ['选项1', '选项2', '选项3']
</script>

<style scoped>
.vxp-select {
max-width: 400px;
}
</style>
28 changes: 28 additions & 0 deletions docs/demos/select/prefix-suffix/demo.en-US.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<Space vertical>
<Select v-model:value="value" :prefix="User" :options="options"></Select>
<Select v-model:value="value" :suffix="ArrowUp" :options="options"></Select>
<Select
v-model:value="value"
:suffix="ArrowUp"
static-suffix
:options="options"
></Select>
<Select v-model:value="value" :options="options" no-suffix></Select>
</Space>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ArrowUp, User } from '@vexip-ui/icons'
const value = ref('')
const options = ['Option 1', 'Option 2', 'Option 3']
</script>

<style scoped>
.vxp-select {
max-width: 400px;
}
</style>
28 changes: 28 additions & 0 deletions docs/demos/select/prefix-suffix/demo.zh-CN.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<Space vertical>
<Select v-model:value="value" :prefix="User" :options="options"></Select>
<Select v-model:value="value" :suffix="ArrowUp" :options="options"></Select>
<Select
v-model:value="value"
:suffix="ArrowUp"
static-suffix
:options="options"
></Select>
<Select v-model:value="value" :options="options" no-suffix></Select>
</Space>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ArrowUp, User } from '@vexip-ui/icons'
const value = ref('')
const options = ['选项1', '选项2', '选项3']
</script>

<style scoped>
.vxp-select {
max-width: 400px;
}
</style>
18 changes: 0 additions & 18 deletions docs/demos/select/prefix/demo.en-US.vue

This file was deleted.

18 changes: 0 additions & 18 deletions docs/demos/select/prefix/demo.zh-CN.vue

This file was deleted.

Loading

0 comments on commit 6eb864f

Please sign in to comment.