srcdev-nuxt-components 1.1.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/styles/utils/_margin-helpers.css +26 -0
- package/components/masonry-grid-ordered/MasonryGridOrdered.vue +156 -0
- package/components/presentation/masonry-grid/MasonryGrid.vue +29 -3
- package/components/presentation/masonry-grid-sorted/MasonryGridSorted.vue +113 -0
- package/package.json +1 -1
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
/* Margin */
|
|
2
|
+
.mi-auto {
|
|
3
|
+
margin-inline: auto;
|
|
4
|
+
}
|
|
5
|
+
.mis-auto {
|
|
6
|
+
margin-inline-start: auto;
|
|
7
|
+
}
|
|
8
|
+
.mie-auto {
|
|
9
|
+
margin-inline-end: auto;
|
|
10
|
+
}
|
|
11
|
+
.mb-auto {
|
|
12
|
+
margin-block: auto;
|
|
13
|
+
}
|
|
14
|
+
.mbs-auto {
|
|
15
|
+
margin-block-start: auto;
|
|
16
|
+
}
|
|
17
|
+
.mbe-auto {
|
|
18
|
+
margin-block-end: auto;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.mb-0 {
|
|
22
|
+
margin-block: 0;
|
|
23
|
+
}
|
|
24
|
+
.mi-0 {
|
|
25
|
+
margin-inline: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
2
28
|
.m-0 {
|
|
3
29
|
margin: 0;
|
|
4
30
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="masonry-grid-ordered" :class="[elementClasses]">
|
|
3
|
+
<div class="masonry-grid-ordered-wrapper" ref="gridWrapper">
|
|
4
|
+
<div v-for="item in gridData" :key="item.id" class="masonry-grid-ordered-item" ref="gridItemsRefs">
|
|
5
|
+
<slot :name="item.id"></slot>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { useBreakpoints, useElementSize, useResizeObserver } from '@vueuse/core';
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
gridData: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: {},
|
|
18
|
+
},
|
|
19
|
+
minTileWidth: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 312,
|
|
22
|
+
},
|
|
23
|
+
gap: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 12,
|
|
26
|
+
},
|
|
27
|
+
styleClassPassthrough: {
|
|
28
|
+
type: Array as PropType<string[]>,
|
|
29
|
+
default: () => [],
|
|
30
|
+
},
|
|
31
|
+
mobilePreferredColCount: {
|
|
32
|
+
type: Number,
|
|
33
|
+
default: 1,
|
|
34
|
+
},
|
|
35
|
+
fixedWidth: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false,
|
|
38
|
+
},
|
|
39
|
+
justify: {
|
|
40
|
+
type: String as PropType<String>,
|
|
41
|
+
default: 'left',
|
|
42
|
+
validator: (val: string) => ['left', 'center', 'right'].includes(val),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
47
|
+
|
|
48
|
+
const gridData = toRef(() => props.gridData);
|
|
49
|
+
|
|
50
|
+
const minTileWidth = toRef(() => props.minTileWidth);
|
|
51
|
+
const gridWrapper = ref<null | HTMLDivElement>(null);
|
|
52
|
+
const gridItemsRefs = ref<HTMLDivElement[]>([]);
|
|
53
|
+
const { width } = useElementSize(gridWrapper);
|
|
54
|
+
const columnCount = computed(() => {
|
|
55
|
+
return Math.floor(width.value / minTileWidth.value);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const gapNum = toRef(props.gap);
|
|
59
|
+
const gapStr = toRef(props.gap + 'px');
|
|
60
|
+
|
|
61
|
+
const fixedWidth = toRef(() => props.fixedWidth);
|
|
62
|
+
const minTileWidthStr = toRef(props.minTileWidth + 'px');
|
|
63
|
+
const maxTileWidth = computed(() => {
|
|
64
|
+
return fixedWidth.value ? minTileWidth.value + 'px' : '1fr';
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const justify = computed(() => {
|
|
68
|
+
return fixedWidth.value ? props.justify : 'stretch';
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const updateGrid = () => {
|
|
72
|
+
if (gridWrapper.value !== null) {
|
|
73
|
+
const wrapperWidth = gridWrapper.value?.offsetWidth ?? 0;
|
|
74
|
+
const itemWidth = fixedWidth.value ? minTileWidth.value : Math.floor((wrapperWidth - (columnCount.value - 1) * gapNum.value) / columnCount.value);
|
|
75
|
+
|
|
76
|
+
const colHeights = Array(columnCount.value).fill(0);
|
|
77
|
+
|
|
78
|
+
gridItemsRefs.value.forEach((item) => {
|
|
79
|
+
const minHeight = Math.min(...colHeights);
|
|
80
|
+
const minIndex = colHeights.indexOf(minHeight);
|
|
81
|
+
|
|
82
|
+
// item.style.position = 'absolute';
|
|
83
|
+
// item.style.top = minHeight + 'px';
|
|
84
|
+
// item.style.width = itemWidth + 'px';
|
|
85
|
+
// item.style.left = minIndex * (100 / columnCount.value) + '%';
|
|
86
|
+
|
|
87
|
+
item?.style.setProperty('--_position', 'absolute');
|
|
88
|
+
item?.style.setProperty('--_position-top', minHeight + 'px');
|
|
89
|
+
item?.style.setProperty('--_position-left', minIndex * (100 / columnCount.value) + '%');
|
|
90
|
+
item?.style.setProperty('--_element-width', itemWidth + 'px');
|
|
91
|
+
|
|
92
|
+
colHeights[minIndex] += Math.floor(item.offsetHeight + gapNum.value);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const maxHeight = Math.max(...colHeights);
|
|
96
|
+
|
|
97
|
+
// gridWrapper.value.style.height = maxHeight + 'px';
|
|
98
|
+
gridWrapper.value?.style.setProperty('--_wrapper-height', maxHeight + 'px');
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
useResizeObserver(gridWrapper, () => {
|
|
103
|
+
updateGrid();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
watch(
|
|
107
|
+
() => fixedWidth.value,
|
|
108
|
+
() => {
|
|
109
|
+
updateGrid();
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
watch(
|
|
114
|
+
() => props.styleClassPassthrough,
|
|
115
|
+
() => {
|
|
116
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<style scoped lang="css">
|
|
122
|
+
.masonry-grid-ordered {
|
|
123
|
+
container-type: inline-size;
|
|
124
|
+
position: relative;
|
|
125
|
+
|
|
126
|
+
.masonry-grid-ordered-wrapper {
|
|
127
|
+
display: grid;
|
|
128
|
+
justify-self: v-bind(justify);
|
|
129
|
+
grid-gap: v-bind(gapStr);
|
|
130
|
+
grid-template-columns: repeat(1, minmax(v-bind(minTileWidthStr), v-bind(maxTileWidth)));
|
|
131
|
+
position: relative;
|
|
132
|
+
|
|
133
|
+
height: var(--_wrapper-height);
|
|
134
|
+
|
|
135
|
+
@container (min-width: 768px) {
|
|
136
|
+
grid-template-columns: repeat(2, minmax(v-bind(minTileWidthStr), v-bind(maxTileWidth)));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@container (min-width: 1024px) {
|
|
140
|
+
grid-template-columns: repeat(3, minmax(v-bind(minTileWidthStr), v-bind(maxTileWidth)));
|
|
141
|
+
}
|
|
142
|
+
@container (min-width: 1280px) {
|
|
143
|
+
grid-template-columns: repeat(4, minmax(v-bind(minTileWidthStr), v-bind(maxTileWidth)));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.masonry-grid-ordered-item {
|
|
147
|
+
transition: position 0.3s ease, top 0.3s ease, left 0.3s ease;
|
|
148
|
+
|
|
149
|
+
position: var(--_position);
|
|
150
|
+
top: var(--_position-top);
|
|
151
|
+
left: var(--_position-left);
|
|
152
|
+
width: var(--_element-width);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="masonry-grid-wrapper">
|
|
2
|
+
<div class="masonry-grid-wrapper" :class="[elementClasses]" :style="`--_masonry-grid-gap: ${gap}${unit}; --_item-min-width: ${itemMinWidth}px`">
|
|
3
3
|
<template v-for="item in gridData" :key="item.id">
|
|
4
4
|
<div class="masonry-grid-item">
|
|
5
5
|
<slot :name="item.id"></slot>
|
|
@@ -14,20 +14,46 @@ const props = defineProps({
|
|
|
14
14
|
type: Object,
|
|
15
15
|
default: {},
|
|
16
16
|
},
|
|
17
|
+
itemMinWidth: {
|
|
18
|
+
type: Number,
|
|
19
|
+
default: 300,
|
|
20
|
+
},
|
|
21
|
+
gap: {
|
|
22
|
+
type: Number,
|
|
23
|
+
default: 1.2,
|
|
24
|
+
},
|
|
25
|
+
unit: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: 'rem',
|
|
28
|
+
},
|
|
29
|
+
styleClassPassthrough: {
|
|
30
|
+
type: Array as PropType<string[]>,
|
|
31
|
+
default: () => [],
|
|
32
|
+
},
|
|
17
33
|
});
|
|
18
34
|
|
|
35
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
36
|
+
|
|
19
37
|
const gridData = toRef(() => props.gridData);
|
|
38
|
+
|
|
39
|
+
watch(
|
|
40
|
+
() => props.styleClassPassthrough,
|
|
41
|
+
() => {
|
|
42
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
43
|
+
}
|
|
44
|
+
);
|
|
20
45
|
</script>
|
|
21
46
|
|
|
22
47
|
<style lang="css">
|
|
23
48
|
.masonry-grid-wrapper {
|
|
24
|
-
columns:
|
|
49
|
+
columns: var(--_item-min-width);
|
|
50
|
+
gap: var(--_masonry-grid-gap);
|
|
25
51
|
|
|
26
52
|
.masonry-grid-item {
|
|
27
53
|
break-inside: avoid;
|
|
28
54
|
outline: 0.1rem solid #cdcdcd;
|
|
29
55
|
padding: 1.2rem;
|
|
30
|
-
margin-block-end:
|
|
56
|
+
margin-block-end: var(--_masonry-grid-gap);
|
|
31
57
|
}
|
|
32
58
|
}
|
|
33
59
|
</style>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="masonry-grid-wrapper" :class="[elementClasses]" :style="`--_masonry-grid-gap: ${gap}${unit}; --_item-min-width: ${itemMinWidth}px`" ref="gridWrapper">
|
|
3
|
+
<template v-for="item in rearrangedItems" :key="item.id">
|
|
4
|
+
<div class="masonry-grid-item">
|
|
5
|
+
<slot :name="item.id"></slot>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { useResizeObserver } from '@vueuse/core';
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
gridData: {
|
|
16
|
+
type: Array as PropType<any[]>,
|
|
17
|
+
default: () => [],
|
|
18
|
+
},
|
|
19
|
+
itemMinWidth: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 300,
|
|
22
|
+
},
|
|
23
|
+
gap: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 1.2,
|
|
26
|
+
},
|
|
27
|
+
unit: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: 'rem',
|
|
30
|
+
},
|
|
31
|
+
styleClassPassthrough: {
|
|
32
|
+
type: Array as PropType<string[]>,
|
|
33
|
+
default: () => [],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
38
|
+
|
|
39
|
+
const gridData = toRef(() => props.gridData);
|
|
40
|
+
|
|
41
|
+
const gridWrapper = ref<HTMLDivElement>();
|
|
42
|
+
|
|
43
|
+
const getColumnCountWithinGridWrapper = () => {
|
|
44
|
+
return gridWrapper.value ? Math.floor(gridWrapper.value.clientWidth / props.itemMinWidth) : 0;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// const columns = ref(4);
|
|
48
|
+
const columns = computed(() => {
|
|
49
|
+
return gridWrapper.value ? Math.floor(gridWrapper.value.clientWidth / props.itemMinWidth) : 0;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const rearrangeArray = (items: any[], columns: number): any[] => {
|
|
53
|
+
const rows = Math.ceil(items.length / columns);
|
|
54
|
+
const rearrangedArray = [];
|
|
55
|
+
|
|
56
|
+
for (let col = 0; col < columns; col++) {
|
|
57
|
+
for (let row = 0; row < rows; row++) {
|
|
58
|
+
const index = row * columns + col;
|
|
59
|
+
if (index < items.length) {
|
|
60
|
+
rearrangedArray.push(items[index]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return rearrangedArray;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const rearrangedItems = computed(() => rearrangeArray(props.gridData, columns.value));
|
|
69
|
+
// const rearrangedItems = computed(() => {
|
|
70
|
+
// const rows = Math.ceil(props.gridData.length / columns.value);
|
|
71
|
+
// const rearrangedArray = [];
|
|
72
|
+
|
|
73
|
+
// for (let col = 0; col < columns.value; col++) {
|
|
74
|
+
// for (let row = 0; row < rows; row++) {
|
|
75
|
+
// const index = row * columns.value + col;
|
|
76
|
+
// if (index < props.gridData.length) {
|
|
77
|
+
// rearrangedArray.push(props.gridData[index]);
|
|
78
|
+
// }
|
|
79
|
+
// }
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
// return rearrangedArray;
|
|
83
|
+
// });
|
|
84
|
+
|
|
85
|
+
watch(
|
|
86
|
+
() => props.styleClassPassthrough,
|
|
87
|
+
() => {
|
|
88
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// onMounted(() => {
|
|
93
|
+
// console.log(getColumnCountWithinGridWrapper());
|
|
94
|
+
// });
|
|
95
|
+
|
|
96
|
+
// useResizeObserver(gridWrapper, () => {
|
|
97
|
+
// console.log(getColumnCountWithinGridWrapper());
|
|
98
|
+
// });
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<style lang="css">
|
|
102
|
+
.masonry-grid-wrapper {
|
|
103
|
+
columns: var(--_item-min-width);
|
|
104
|
+
gap: var(--_masonry-grid-gap);
|
|
105
|
+
|
|
106
|
+
.masonry-grid-item {
|
|
107
|
+
break-inside: avoid;
|
|
108
|
+
outline: 0.1rem solid #cdcdcd;
|
|
109
|
+
padding: 1.2rem;
|
|
110
|
+
margin-block-end: var(--_masonry-grid-gap);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
</style>
|
package/package.json
CHANGED