Skip to content

Commit 467f8df

Browse files
committed
feat(list): support item slot to customize the item rendering
1 parent c0026f8 commit 467f8df

File tree

1 file changed

+67
-49
lines changed
  • packages/anu-vue/src/components/list

1 file changed

+67
-49
lines changed

packages/anu-vue/src/components/list/AList.tsx

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AAvatar, isAvatarUsed } from '@/components/avatar'
88
import type { AvatarOnlyProps } from '@/components/avatar/props'
99
import type { ConfigurableValue } from '@/composables/useConfigurable'
1010
import { useConfigurable } from '@/composables/useConfigurable'
11+
import { isEmptyArray } from '@/utils/helpers'
1112

1213
// TODO: Reuse the existing props and its types. Maybe if we create AListItem component then we can reuse prop types.
1314
interface ListItem extends AvatarOnlyProps {
@@ -70,62 +71,65 @@ export const AList = defineComponent({
7071
},
7172
emits: ['update:modelValue'],
7273
setup(props, { slots, emit }) {
73-
const { getLayerClasses } = useLayer()
74-
75-
const { options, select: selectListItem, value } = useGroupModel({
76-
options: props.items[0].value ? props.items.map(i => i.value) : props.items.length,
77-
multi: props.multi,
78-
})
79-
const isAvatarPropsUsed = computed(() => {
80-
return isAvatarUsed(props.items[0])
81-
})
82-
83-
// 👉 Avatar Renderer
84-
const avatarRenderer = (
85-
content: typeof props.items[number]['content'],
86-
src: typeof props.items[number]['src'],
87-
alt: typeof props.items[number]['alt'],
88-
icon: typeof props.items[number]['icon'],
89-
$avatar: typeof props.items[number]['$avatar'],
90-
) => {
91-
const _alt = alt || 'avatar'
92-
93-
return <AAvatar
74+
// 👉 List items
75+
const listItems = computed(() => {
76+
const { options, select: selectListItem, value } = useGroupModel({
77+
options: !isEmptyArray(props.items) && props.items[0].value
78+
? props.items.map(i => i.value)
79+
: props.items.length,
80+
multi: props.multi,
81+
})
82+
const { getLayerClasses } = useLayer()
83+
84+
const isAvatarPropsUsed = computed(() => {
85+
return isAvatarUsed(props.items[0])
86+
})
87+
88+
// 👉 Avatar Renderer
89+
const avatarRenderer = (
90+
content: typeof props.items[number]['content'],
91+
src: typeof props.items[number]['src'],
92+
alt: typeof props.items[number]['alt'],
93+
icon: typeof props.items[number]['icon'],
94+
$avatar: typeof props.items[number]['$avatar'],
95+
) => {
96+
const _alt = alt || 'avatar'
97+
98+
return <AAvatar
9499
content={content}
95100
src={src}
96101
alt={_alt}
97102
icon={icon}
98103
{...$avatar}
99104
/>
100-
}
105+
}
101106

102-
const handleListItemClick = (index: number) => {
103-
const itemValue = options.value[index].value
104-
selectListItem(itemValue)
105-
if (props.modelValue !== null)
106-
emit('update:modelValue', value.value)
107-
}
107+
const handleListItemClick = (index: number) => {
108+
const itemValue = options.value[index].value
109+
selectListItem(itemValue)
110+
if (props.modelValue !== null)
111+
emit('update:modelValue', value.value)
112+
}
108113

109-
// 👉 List items
110-
const listItems = computed(() => props.items.map((listItem, itemIndex) => {
114+
return props.items.map((listItem, itemIndex) => {
111115
// ℹ️ Reduce the size of title to 1rem. We did the same in ACard as well.
112-
const _titleProp = useConfigurable(listItem.title)
113-
if (Array.isArray(_titleProp.value.classes))
114-
_titleProp.value.classes = [..._titleProp.value.classes, 'uno-layer-base-text-base']
115-
else
116-
_titleProp.value.classes += ' uno-layer-base-text-base'
117-
118-
const isActive = computed(() => options.value[itemIndex].isSelected)
119-
120-
// const [style, classes] = getLayerClasses(layerProps.value, { statesClass: 'states:10' })
121-
const { styles, classes } = getLayerClasses(
122-
computed(() => isActive.value ? props.color || 'primary' : undefined),
123-
computed(() => isActive.value ? props.variant || 'light' : 'text'),
124-
toRef(props, 'states'),
125-
{ statesClass: 'states:10' },
126-
)
127-
128-
return <li
116+
const _titleProp = useConfigurable(listItem.title)
117+
if (Array.isArray(_titleProp.value.classes))
118+
_titleProp.value.classes = [..._titleProp.value.classes, 'uno-layer-base-text-base']
119+
else
120+
_titleProp.value.classes += ' uno-layer-base-text-base'
121+
122+
const isActive = computed(() => options.value[itemIndex].isSelected)
123+
124+
// const [style, classes] = getLayerClasses(layerProps.value, { statesClass: 'states:10' })
125+
const { styles, classes } = getLayerClasses(
126+
computed(() => isActive.value ? props.color || 'primary' : undefined),
127+
computed(() => isActive.value ? props.variant || 'light' : 'text'),
128+
toRef(props, 'states'),
129+
{ statesClass: 'states:10' },
130+
)
131+
132+
return <li
129133
onClick={() => handleListItemClick(itemIndex)}
130134
style={[...styles.value]}
131135
class={[
@@ -136,14 +140,27 @@ export const AList = defineComponent({
136140
: '',
137141
'flex items-center gap-$a-list-item-gap m-$a-list-item-margin p-$a-list-item-padding min-h-$a-list-item-min-height',
138142
]}>
143+
144+
{/* 👉 Slot: prepend */}
139145
{
140146
slots.prepend
141147
? slots.prepend({ listItem, itemIndex })
142148
: isAvatarPropsUsed.value && !props.avatarAppend
143149
? avatarRenderer(listItem.content, listItem.src, listItem.alt, listItem.icon, listItem.$avatar)
144150
: null
145151
}
146-
<ATypography class="flex-grow" title={Object.values(_titleProp.value) as ConfigurableValue} subtitle={listItem.subtitle} text={listItem.text}></ATypography>
152+
153+
{/* Slot: item */}
154+
{
155+
slots.item
156+
? slots.item({
157+
item: listItem,
158+
index: itemIndex,
159+
})
160+
: <ATypography class="flex-grow" title={Object.values(_titleProp.value) as ConfigurableValue} subtitle={listItem.subtitle} text={listItem.text}></ATypography>
161+
}
162+
163+
{/* 👉 Slot: append */}
147164
{
148165
slots.append
149166
? slots.append({ listItem, itemIndex })
@@ -152,7 +169,8 @@ export const AList = defineComponent({
152169
: null
153170
}
154171
</li>
155-
}))
172+
})
173+
})
156174

157175
// 👉 Return
158176
return () => <ul class="a-list grid gap-$a-list-gap">

0 commit comments

Comments
 (0)