Skip to content

Commit

Permalink
Merge pull request #420 from qor5/feat-vxselect-item
Browse files Browse the repository at this point in the history
WIP: feat: enhance VXSelect component with slot support for custom item rendering
  • Loading branch information
danni-cool authored Feb 26, 2025
2 parents c9e7236 + e0fcbee commit 6f456a8
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 61 deletions.
2 changes: 1 addition & 1 deletion ui/vuetifyx/vuetifyxjs/dist/assets/vuetifyx.min.css

Large diffs are not rendered by default.

90 changes: 45 additions & 45 deletions ui/vuetifyx/vuetifyxjs/dist/vuetifyx.min.js

Large diffs are not rendered by default.

147 changes: 135 additions & 12 deletions ui/vuetifyx/vuetifyxjs/docs/Components/VXSelect/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
# vx-select 选择器

## vx-select
## API

### Slot

#### v-slot:prepend-inner

可以自定义input文字前固定位置的图标的插槽, `selectedItems` 可以用来控制当前选中项

slot scope

```js
{
isActive: Ref<boolean>
isFocused: Ref<boolean>
controlRef: Ref<HTMLElement | undefined>
selectedItems: Array<{string, any}>
focus: () => void
blur: () => void
}
```

#### v-slot:item

每一项的插槽,用来自定义渲染每一项,根元素必须使用 `v-list-item` 及绑定上 `v-bind="props"`

slot scope

```js
{ item: ListItem; index: number; props: Record<string, unknown> }
```

[example](#slot-item)

## 示例(vx-select)

:::demo

Expand Down Expand Up @@ -60,7 +93,8 @@
item-value="id"
placeholder="choose a item"
closable-chips
/>
>
</vx-select>
<vx-select
v-model="valueNormal"
Expand All @@ -69,7 +103,8 @@
item-title="name"
item-value="id"
placeholder="choose a item"
/>
>
</vx-select>
<vx-select
label="Normal Select + required rule"
Expand Down Expand Up @@ -133,14 +168,102 @@ const srcs = {
5: 'https://cdn.vuetifyjs.com/images/lists/5.jpg'
}
const items = ref([
{ id: 1, name: 'Sandra Adams', group: 'Group 1', avatar: srcs[1] },
{ id: 2, name: 'Ali Connors', group: 'Group 1', avatar: srcs[2] },
{ id: 3, name: 'Trevor Hansen', group: 'Group 1', avatar: srcs[3] },
{ id: 4, name: 'Tucker Smith', group: 'Group 1', avatar: srcs[2] },
{ id: 5, name: 'Britta Holt', group: 'Group 2', avatar: srcs[4] },
{ id: 6, name: 'Jane Smith ', group: 'Group 2', avatar: srcs[5] },
{ id: 7, name: 'John Smith', group: 'Group 2', avatar: srcs[1] },
{ id: 8, name: 'Sandra Williams', group: 'Group 2', avatar: srcs[3] }
{ id: 1, name: 'Sandra Adams', group: 'Group 1', avatar: srcs[1], icon: 'mdi-wifi' },
{ id: 2, name: 'Ali Connors', group: 'Group 1', avatar: srcs[2], icon: 'mdi-wifi' },
{ id: 3, name: 'Trevor Hansen', group: 'Group 1', avatar: srcs[3], icon: 'mdi-wifi' },
{ id: 4, name: 'Tucker Smith', group: 'Group 1', avatar: srcs[2], icon: 'mdi-wifi' },
{ id: 5, name: 'Britta Holt', group: 'Group 2', avatar: srcs[4], icon: 'mdi-wifi' },
{ id: 6, name: 'Jane Smith ', group: 'Group 2', avatar: srcs[5], icon: 'mdi-wifi' },
{ id: 7, name: 'John Smith', group: 'Group 2', avatar: srcs[1], icon: 'mdi-wifi' },
{ id: 8, name: 'Sandra Williams', group: 'Group 2', avatar: srcs[3], icon: 'mdi-wifi' }
])
</script>
<style scoped></style>
```

:::

### Slot(item)

item 的原始数据在 item.raw 里, 如果不希望 prepend 的区域i元素变暗,使用`style: "--v-medium-emphasis-opacity:1"` 来控制

:::demo

```vue
<template>
<div class="mb-4">1. item with prepend element + prepend inner element</div>
<v-row>
<v-col cols="6">
<vx-select
type="autocomplete"
v-model="valueAutoComplete"
label="autoComplete Select"
:items="items"
item-title="name"
item-value="id"
placeholder="choose a item"
closable-chips
>
<template v-slot:prepend-inner="{ selectedItems }">
<v-icon :icon="selectedItems[0].icon" style="--v-medium-emphasis-opacity:1"/>
</template>
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :title="item.title">
<template v-slot:prepend>
<v-icon :icon="item.raw.icon" style="--v-medium-emphasis-opacity:1"/>
</template>
</v-list-item>
</template> </vx-select
></v-col>
<v-col cols="6">
<vx-select
v-model="valueNormal"
label="Normal Select"
:items="items"
item-title="name"
item-value="id"
placeholder="choose a item"
>
<template v-slot:prepend-inner="{ selectedItems }">
<v-icon :icon="selectedItems[0].icon" />
</template>
<template v-slot:item="{ props, item }">
<v-list-item v-bind="props" :title="item.title">
<template v-slot:prepend>
<v-icon :icon="item.raw.icon" />
</template>
</v-list-item>
</template>
</vx-select>
</v-col>
</v-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const valueAutoComplete = ref([1])
const valueNormal = ref([1])
const valueWithErrorMsg = ref([1])
const srcs = {
1: 'https://cdn.vuetifyjs.com/images/lists/1.jpg',
2: 'https://cdn.vuetifyjs.com/images/lists/2.jpg',
3: 'https://cdn.vuetifyjs.com/images/lists/3.jpg',
4: 'https://cdn.vuetifyjs.com/images/lists/4.jpg',
5: 'https://cdn.vuetifyjs.com/images/lists/5.jpg'
}
const items = ref([
{ id: 1, name: 'Sandra Adams', group: 'Group 1', avatar: srcs[1], icon: 'mdi-wifi' },
{ id: 2, name: 'Ali Connors', group: 'Group 1', avatar: srcs[2], icon: 'mdi-plus' },
{ id: 3, name: 'Trevor Hansen', group: 'Group 1', avatar: srcs[3], icon: 'mdi-information' },
{ id: 4, name: 'Tucker Smith', group: 'Group 1', avatar: srcs[2], icon: 'mdi-alert' },
{ id: 5, name: 'Britta Holt', group: 'Group 2', avatar: srcs[4], icon: 'mdi-alert-circle' },
{ id: 6, name: 'Jane Smith ', group: 'Group 2', avatar: srcs[5], icon: 'mdi-domain' },
{ id: 7, name: 'John Smith', group: 'Group 2', avatar: srcs[1], icon: 'mdi-message-text' },
{ id: 8, name: 'Sandra Williams', group: 'Group 2', avatar: srcs[3], icon: 'mdi-dialpad' }
])
</script>
Expand All @@ -149,7 +272,7 @@ const items = ref([

:::

## vx-selectmany
## 示例(vx-selectmany

> legacy component
Expand Down
59 changes: 56 additions & 3 deletions ui/vuetifyx/vuetifyxjs/src/lib/Form/VXSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,26 @@
density="compact"
color="primary"
@update:model-value="onUpdateModelValue"
/>
>
<template
v-if="hasPrependInnerSlot"
#prepend-inner="{ isActive, isFocused, controlRef, focus, blur }"
>
<slot
name="prepend-inner"
:isActive="isActive"
:isFocused="isFocused"
:controlRef="controlRef"
:focus="focus"
:blur="blur"
:selectedItems="selectedItems"
/>
</template>

<template v-if="hasItemSlot" #item="{ props, index, item }">
<slot name="item" :props="props" :index="index" :item="item" />
</template>
</v-autocomplete>
<v-select
v-else
:closable-chips="closableChips"
Expand All @@ -44,17 +63,39 @@
density="compact"
color="primary"
@update:model-value="onUpdateModelValue"
/>
>
<template
v-if="hasPrependInnerSlot"
#prepend-inner="{ isActive, isFocused, controlRef, focus, blur }"
>
<slot
name="prepend-inner"
:isActive="isActive"
:isFocused="isFocused"
:controlRef="controlRef"
:focus="focus"
:blur="blur"
:selectedItems="selectedItems"
/>
</template>

<template v-if="hasItemSlot" #item="{ props, index, item }">
<slot name="item" :props="props" :index="index" :item="item" />
</template>
</v-select>
</div>
</template>

<script setup lang="ts">
import { defineEmits, ref, watch, PropType } from 'vue'
import { defineEmits, ref, watch, PropType, useSlots, Slots, computed } from 'vue'
import VXLabel from '../Common/VXLabel.vue'
import { useFilteredAttrs } from '@/lib/composables/useFilteredAttrs'
const { filteredAttrs } = useFilteredAttrs()
const emit = defineEmits(['update:modelValue'])
const slots: Slots = useSlots()
const hasPrependInnerSlot = slots['prepend-inner'] !== undefined
const hasItemSlot = slots['item'] !== undefined
const props = defineProps({
modelValue: null,
type: String,
Expand All @@ -78,6 +119,18 @@ const props = defineProps({
const selectValue = ref(props.modelValue)
const selectedItems = computed(() => {
return props.items?.filter((item: any) => {
if (props.multiple) {
return selectValue.value?.includes(props.itemValue ? item[props.itemValue] : item)
}
return Array.isArray(selectValue.value)
? selectValue.value[0] === (props.itemValue ? item[props.itemValue] : item)
: selectValue.value === (props.itemValue ? item[props.itemValue] : item)
})
})
watch(
() => props.modelValue,
(newVal) => (selectValue.value = newVal)
Expand Down

0 comments on commit 6f456a8

Please sign in to comment.