20260720172840
This commit is contained in:
1 parent
cda7f16d3a
commit
94207c77ff
3117 files changed
+201407
-1236
No files matched your search
@@ -0,0 +1,47 @@
|
||||
# _semantic
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { SemanticPreview } from '@/components/semantic'
|
||||
import { useComponentLocale } from '@/composables/use-locale'
|
||||
import { locales } from '../locales'
|
||||
|
||||
const { t } = useComponentLocale(locales)
|
||||
|
||||
const semantics = computed(() => [
|
||||
{ name: 'root', desc: t('root') },
|
||||
{ name: 'item', desc: t('item') },
|
||||
])
|
||||
|
||||
const heights = [75, 50, 70, 60, 85, 75, 50].map((height, index) => ({
|
||||
key: `item-${index}`,
|
||||
data: height,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SemanticPreview
|
||||
component-name="Masonry"
|
||||
:semantics="semantics"
|
||||
>
|
||||
<template #default="{ classes }">
|
||||
<a-masonry
|
||||
:columns="3"
|
||||
:gutter="16"
|
||||
:items="heights"
|
||||
style="width: 100%"
|
||||
:classes="classes"
|
||||
>
|
||||
<template #itemRender="{ data, index }">
|
||||
<a-card size="small" :style="{ height: data }">
|
||||
{{ index + 1 }}
|
||||
</a-card>
|
||||
</template>
|
||||
</a-masonry>
|
||||
</template>
|
||||
</SemanticPreview>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
# Basic
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Basic usage.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const heights = [150, 50, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 60, 50, 80].map((height, index) => {
|
||||
return {
|
||||
key: `item-${index}`,
|
||||
data: height,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-masonry :columns="4" :gutter="16" :items="heights">
|
||||
<template #itemRender="{ index, data }">
|
||||
<template v-if="index === 4">
|
||||
<a-card size="small">
|
||||
<template #cover>
|
||||
<img
|
||||
alt="food"
|
||||
src="https://images.unsplash.com/photo-1491961865842-98f7befd1a60?w=523&auto=format"
|
||||
>
|
||||
</template>
|
||||
<a-card-meta title="I'm Special" description="Let's have a meal" />
|
||||
</a-card>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-card size="small" :style="{ height: `${data}px` }">
|
||||
{{ index + 1 }}
|
||||
</a-card>
|
||||
</template>
|
||||
</template>
|
||||
</a-masonry>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,93 @@
|
||||
# Dynamic
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Dynamically add and remove items. The `layoutChange` event provides column information for each item.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { CloseOutlined } from '@antdv-next/icons'
|
||||
import { theme } from 'antdv-next'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { token } = theme.useToken()
|
||||
|
||||
const heights = [150, 50, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 70, 50, 80]
|
||||
|
||||
interface ItemType {
|
||||
key: number
|
||||
column?: number
|
||||
data: number
|
||||
}
|
||||
|
||||
const items = ref<ItemType[]>(
|
||||
heights.map((height, index) => ({
|
||||
key: index,
|
||||
column: index % 4,
|
||||
data: height,
|
||||
})),
|
||||
)
|
||||
|
||||
function removeItem(removeKey: number) {
|
||||
items.value = items.value.filter(({ key }) => key !== removeKey)
|
||||
}
|
||||
|
||||
function addItem() {
|
||||
const lastItem = items.value[items.value.length - 1]
|
||||
items.value = [
|
||||
...items.value,
|
||||
{
|
||||
key: lastItem ? lastItem.key + 1 : 0,
|
||||
data: Math.floor(Math.random() * 100) + 50,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function handleLayoutChange(sortedItems: ItemType[]) {
|
||||
items.value = items.value.map((item) => {
|
||||
const matchItem = sortedItems.find(sortedItem => sortedItem.key === item.key)
|
||||
return matchItem
|
||||
? {
|
||||
...item,
|
||||
column: matchItem.column,
|
||||
}
|
||||
: item
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-flex vertical :gap="16">
|
||||
<a-masonry
|
||||
:columns="4"
|
||||
:gutter="16"
|
||||
:items="items"
|
||||
@layout-change="handleLayoutChange"
|
||||
>
|
||||
<template #itemRender="{ data, key }">
|
||||
<a-card size="small" :style="{ height: `${data}px` }">
|
||||
{{ Number(key) + 1 }}
|
||||
<a-button
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
insetBlockStart: `${token.paddingSM}px`,
|
||||
insetInlineEnd: `${token.paddingSM}px`,
|
||||
}"
|
||||
size="small"
|
||||
@click="() => removeItem(key as any)"
|
||||
>
|
||||
<template #icon>
|
||||
<CloseOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-card>
|
||||
</template>
|
||||
</a-masonry>
|
||||
<a-button block @click="addItem">
|
||||
Add Item
|
||||
</a-button>
|
||||
</a-flex>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
# Fresh
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
When `fresh` is enabled, the masonry will continuously monitor size changes of child items and automatically re-layout. Click on cards to change their height.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { Card } from 'antdv-next'
|
||||
import { defineComponent, h, ref } from 'vue'
|
||||
|
||||
const RandomHeightCard = defineComponent({
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
defaultHeight: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const height = ref(props.defaultHeight)
|
||||
|
||||
const handleClick = () => {
|
||||
height.value = Math.floor(Math.random() * 100) + 50
|
||||
}
|
||||
|
||||
return () =>
|
||||
h(
|
||||
Card,
|
||||
{
|
||||
size: 'small',
|
||||
style: { height: `${height.value}px`, transition: 'height 0.3s' },
|
||||
onClick: handleClick,
|
||||
},
|
||||
() => `${props.index + 1} - Click`,
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const heights = [150, 50, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 60, 50, 80].map(
|
||||
(height, index) => {
|
||||
const item: {
|
||||
key: string
|
||||
data: number
|
||||
column?: number
|
||||
} = {
|
||||
key: `item-${index}`,
|
||||
data: height,
|
||||
}
|
||||
|
||||
if (index === 4) {
|
||||
item.column = 0
|
||||
}
|
||||
|
||||
return item
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-masonry
|
||||
fresh
|
||||
:columns="4"
|
||||
:gutter="16"
|
||||
:items="heights"
|
||||
>
|
||||
<template #itemRender="{ data, index }">
|
||||
<RandomHeightCard :index="index" :default-height="data" />
|
||||
</template>
|
||||
</a-masonry>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# Image
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Display image list using masonry layout.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const imageList = [
|
||||
'https://images.unsplash.com/photo-1510001618818-4b4e3d86bf0f',
|
||||
'https://images.unsplash.com/photo-1507513319174-e556268bb244',
|
||||
'https://images.unsplash.com/photo-1474181487882-5abf3f0ba6c2',
|
||||
'https://images.unsplash.com/photo-1492778297155-7be4c83960c7',
|
||||
'https://images.unsplash.com/photo-1508062878650-88b52897f298',
|
||||
'https://images.unsplash.com/photo-1506158278516-d720e72406fc',
|
||||
'https://images.unsplash.com/photo-1552203274-e3c7bd771d26',
|
||||
'https://images.unsplash.com/photo-1528163186890-de9b86b54b51',
|
||||
'https://images.unsplash.com/photo-1727423304224-6d2fd99b864c',
|
||||
'https://images.unsplash.com/photo-1675090391405-432434e23595',
|
||||
'https://images.unsplash.com/photo-1554196967-97a8602084d9',
|
||||
'https://images.unsplash.com/photo-1491961865842-98f7befd1a60',
|
||||
'https://images.unsplash.com/photo-1721728613411-d56d2ddda959',
|
||||
'https://images.unsplash.com/photo-1731901245099-20ac7f85dbaa',
|
||||
'https://images.unsplash.com/photo-1617694455303-59af55af7e58',
|
||||
'https://images.unsplash.com/photo-1709198165282-1dab551df890',
|
||||
]
|
||||
|
||||
const items = imageList.map((img, index) => ({
|
||||
key: `item-${index}`,
|
||||
data: img,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-masonry :columns="4" :gutter="16" :items="items">
|
||||
<template #itemRender="{ data }">
|
||||
<img :src="`${data}?w=523&auto=format`" alt="sample" style="width: 100%">
|
||||
</template>
|
||||
</a-masonry>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
# Responsive
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Achieve responsive masonry layout for different screen sizes by setting `columns` and `gutter` properties to responsive configuration objects.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const heights = [120, 55, 85, 160, 95, 140, 75, 110, 65, 130, 90, 145, 55, 100, 80]
|
||||
|
||||
const items = heights.map((height, index) => ({
|
||||
key: `item-${index}`,
|
||||
data: height,
|
||||
index,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-masonry
|
||||
:columns="{ xs: 1, sm: 2, md: 3, lg: 4 }"
|
||||
:gutter="{ xs: 8, sm: 12, md: 16 }"
|
||||
:items="items"
|
||||
>
|
||||
<template #itemRender="item">
|
||||
<a-card size="small" :style="{ height: `${item.data}px` }">
|
||||
{{ item.index + 1 }}
|
||||
</a-card>
|
||||
</template>
|
||||
</a-masonry>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,120 @@
|
||||
# Custom semantic dom styling
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Customize semantic structure styles and class names. Supports both object and function forms.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MasonryProps } from 'antdv-next'
|
||||
|
||||
interface MasonryItemType<T = any> {
|
||||
key: string
|
||||
data: T
|
||||
}
|
||||
|
||||
const items = [120, 80, 100, 60, 140, 90, 110, 70].map<MasonryItemType<number>>(
|
||||
(height, index) => ({
|
||||
key: `item-${index}`,
|
||||
data: height,
|
||||
}),
|
||||
)
|
||||
|
||||
const classes = {
|
||||
root: 'custom-masonry-root',
|
||||
item: 'custom-masonry-item',
|
||||
}
|
||||
|
||||
const styles: MasonryProps['styles'] = {
|
||||
root: {
|
||||
borderRadius: '12px',
|
||||
padding: '20px',
|
||||
height: '260px',
|
||||
backgroundColor: 'rgba(250,250,250,0.5)',
|
||||
},
|
||||
item: {
|
||||
transform: 'scale(0.98)',
|
||||
transition: 'transform 0.2s ease',
|
||||
border: '1px solid #ccc',
|
||||
},
|
||||
}
|
||||
|
||||
const stylesFn: MasonryProps['styles'] = (info) => {
|
||||
const { props } = info
|
||||
return {
|
||||
root: {
|
||||
border: `2px solid ${typeof props.columns === 'number' && props.columns > 2 ? '#1890ff' : '#52c41a'}`,
|
||||
padding: '20px',
|
||||
height: '280px',
|
||||
backgroundColor: 'rgba(240,248,255,.6)',
|
||||
},
|
||||
item: {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
border: '1px solid #1890ff',
|
||||
},
|
||||
} satisfies MasonryProps['styles']
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-flex vertical :gap="24">
|
||||
<div>
|
||||
<a-typography-title :level="4">
|
||||
classNames and styles Object
|
||||
</a-typography-title>
|
||||
<a-masonry
|
||||
:columns="4"
|
||||
:gutter="16"
|
||||
:items="items"
|
||||
:classes="classes"
|
||||
:styles="styles"
|
||||
>
|
||||
<template #itemRender="{ data, index }">
|
||||
<a-card size="small" :style="{ height: `${data}px` }">
|
||||
{{ index + 1 }}
|
||||
</a-card>
|
||||
</template>
|
||||
</a-masonry>
|
||||
</div>
|
||||
<a-divider />
|
||||
<div>
|
||||
<a-typography-title :level="4">
|
||||
classNames and styles Function
|
||||
</a-typography-title>
|
||||
<a-masonry
|
||||
:columns="3"
|
||||
:gutter="12"
|
||||
:items="items.slice(0, 6)"
|
||||
:classes="classes"
|
||||
:styles="stylesFn"
|
||||
>
|
||||
<template #itemRender="{ data, index }">
|
||||
<a-card size="small" :style="{ height: `${data}px` }">
|
||||
{{ index + 1 }}
|
||||
</a-card>
|
||||
</template>
|
||||
</a-masonry>
|
||||
</div>
|
||||
</a-flex>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.custom-masonry-root) {
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
height: 260px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.custom-masonry-item) {
|
||||
transform: scale(0.98);
|
||||
transition: transform 0.2s ease;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Masonry
|
||||
description: A masonry layout component for displaying content with different heights.
|
||||
---
|
||||
|
||||
## When To Use
|
||||
|
||||
- When displaying images or cards with irregular heights.
|
||||
- When content needs to be evenly distributed in columns.
|
||||
- When column count needs to be responsive.
|
||||
|
||||
## Demos
|
||||
|
||||
| Demo | Path |
|
||||
| --- | --- |
|
||||
| Basic | demo/basic.md |
|
||||
| Responsive | demo/responsive.md |
|
||||
| Image | demo/image.md |
|
||||
| Dynamic | demo/dynamic.md |
|
||||
| Custom semantic dom styling | demo/style-class.md |
|
||||
| Fresh | demo/fresh.md |
|
||||
|
||||
## API
|
||||
|
||||
Common props ref:[Common props](../../docs/vue/common-props.md)
|
||||
|
||||
### Props
|
||||
|
||||
| Property | Description | Type | Default | Version | [Global Config](../config-provider/docs.md#component-config) |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| columns | Number of columns, can be a fixed value or a responsive configuration | number \| { xs?: number; sm?: number; md?: number; lg?: number; xl?: number; xxl?: number } | 3 | - | × |
|
||||
| fresh | Whether to continuously monitor the size changes of child items | boolean | false | - | × |
|
||||
| gutter | Spacing, can be a fixed value, responsive configuration, or a configuration for horizontal and vertical spacing | [Gap](#gap) \| [[Gap](#gap), [Gap](#gap)] | 0 | - | × |
|
||||
| items | Masonry items | [MasonryItem](#masonryitem)[] | - | - | × |
|
||||
| itemRender | Custom item rendering function | (item: MasonryItem) => VueNode | - | - | × |
|
||||
|
||||
### Events
|
||||
|
||||
| Event | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| layoutChange | Callback for column sorting changes | (sortInfo: { key: Key; column: number }[]) => void | - |
|
||||
|
||||
### Slots
|
||||
|
||||
| Slot | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| itemRender | Custom item rendering slot | (itemInfo: MasonryItem & { index: number }) => VueNode | - |
|
||||
|
||||
## Types
|
||||
|
||||
### MasonryItem
|
||||
|
||||
| Parameter | Description | Type | Default Value |
|
||||
| --- | --- | --- | --- |
|
||||
| key | Unique identifier for the item | string \| number | - |
|
||||
| height | Height of the item | number | - |
|
||||
| column | Specifies the column to which the item belongs | number | - |
|
||||
| data | Custom data storage | T | - |
|
||||
| children | Custom display content, takes precedence over `itemRender` | VueNode | - |
|
||||
|
||||
### Gap
|
||||
|
||||
`Gap` represents the spacing between items. It can either be a fixed value or a responsive configuration.
|
||||
|
||||
```ts
|
||||
type Gap = undefined | number | Partial<Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number>>
|
||||
```
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
| _semantic | demo/_semantic.md |
|
||||
Reference in new issue
Block a user