srcdev-nuxt-components 1.1.6 → 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.
@@ -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,74 +1,29 @@
1
1
  <template>
2
- <div class="layout-reporting-grid" :class="[elementClasses]">
3
- <section class="reporting-block">
4
- <div class="slot-1">
5
- <div class="panel">Panel 1<br />Panel 1<br />Panel 1<br />Panel 1</div>
6
- <div class="panel">Panel 2<br />Panel 2</div>
7
- <div class="panel">Panel 3</div>
8
- <div class="panel">Panel 4<br />Panel 4<br />Panel 4<br />Panel 4<br />Panel 4</div>
9
- <div class="panel">Panel 5<br />Panel 5<br />Panel 5</div>
2
+ <div class="layout-grid-b" :class="[elementClasses]">
3
+ <section class="top-row">
4
+ <div class="top-row-slot-1">
5
+ <div class="top-row-slot-1-inner">
6
+ <div v-for="key in topRowSlot1ItemCount" class="panel">
7
+ <slot :name="`top-row-slot1-${key}-content`"></slot>
8
+ </div>
9
+ </div>
10
10
  </div>
11
- <!-- Delete slot-2 to test not present -->
12
- <div class="slot-2">
11
+
12
+ <div class="top-row-slot-2">
13
13
  <div class="panel">
14
- <h2>Sentiment Panel</h2>
15
- <h2>Content of this cell will fill available space</h2>
16
- <p>Width can be changed by tinkering with the minmax in css</p>
17
- <pre>grid-template-columns: 1fr minmax(calc(4/12 * 100%), 460px);</pre>
18
- <p>
19
- Lorem ipsum odor amet, consectetuer adipiscing elit. Nec elementum maecenas placerat laoreet curae elit convallis himenaeos. Tellus varius cursus convallis commodo suspendisse litora.
20
- Platea accumsan interdum ultrices adipiscing molestie cras dui. Vehicula egestas nisi sagittis fames metus velit. Sodales blandit nisi eu dis sit, ridiculus aliquam. Morbi tellus eu in
21
- penatibus torquent tortor. Platea gravida nam; egestas enim nostra ultricies.
22
- </p>
23
- <p>
24
- Mi nibh quisque taciti porta curabitur nostra volutpat. Habitant sodales arcu habitasse mi duis conubia leo lacinia. Montes torquent sodales adipiscing; proin semper feugiat morbi
25
- ullamcorper praesent. Arcu luctus tempor quam ligula vestibulum sapien faucibus ridiculus. Cursus consequat ultricies consectetur class suscipit quisque convallis eget? Dignissim mattis
26
- luctus enim habitant porta pretium litora. Parturient montes imperdiet massa; sollicitudin varius hac aptent. Eleifend parturient mattis tellus nisi a montes.
27
- </p>
14
+ <slot name="top-row-slot-2"></slot>
28
15
  </div>
29
16
  </div>
30
- <div class="slot-3">
17
+ <div class="top-row-slot-3">
31
18
  <div class="panel">
32
- <h2>Content of this cell will fill available space</h2>
33
- <p>
34
- Lorem ipsum odor amet, consectetuer adipiscing elit. Nec elementum maecenas placerat laoreet curae elit convallis himenaeos. Tellus varius cursus convallis commodo suspendisse litora.
35
- Platea accumsan interdum ultrices adipiscing molestie cras dui. Vehicula egestas nisi sagittis fames metus velit. Sodales blandit nisi eu dis sit, ridiculus aliquam. Morbi tellus eu in
36
- penatibus torquent tortor. Platea gravida nam; egestas enim nostra ultricies.
37
- </p>
38
- <p>
39
- Mi nibh quisque taciti porta curabitur nostra volutpat. Habitant sodales arcu habitasse mi duis conubia leo lacinia. Montes torquent sodales adipiscing; proin semper feugiat morbi
40
- ullamcorper praesent. Arcu luctus tempor quam ligula vestibulum sapien faucibus ridiculus. Cursus consequat ultricies consectetur class suscipit quisque convallis eget? Dignissim mattis
41
- luctus enim habitant porta pretium litora. Parturient montes imperdiet massa; sollicitudin varius hac aptent. Eleifend parturient mattis tellus nisi a montes.
42
- </p>
19
+ <slot name="top-row-slot-3"></slot>
43
20
  </div>
44
21
  </div>
45
22
  </section>
46
23
 
47
- <section class="dashboard-block">
48
- <div class="panel">
49
- <h2>Top Employee Priorities</h2>
50
- <h2>Content of this cell will fill available space and match heights</h2>
51
- <p>Cells set to 50% width gap of 25px</p>
52
- Panel 1<br />Panel 1<br />Panel 1<br />Panel 1
53
- </div>
54
- <div class="panel">
55
- <h2>Highest Red and Green Zone Populations</h2>
56
- <h2>Content of this cell will fill available space and match heights</h2>
57
- <p>Cells set to 50% width gap of 25px</p>
58
- Panel 2<br />Panel 2
59
- </div>
60
- <!-- Delete next panel to test not present -->
61
- <div class="panel">
62
- <h2>Sentiment Analysis</h2>
63
- <h2>Content of this cell will fill available space and match heights</h2>
64
- <p>Cells set to 50% width gap of 25px</p>
65
- Panel 3
66
- </div>
67
- <div class="panel">
68
- <h2>Trend Analysis on Average Satisfaction</h2>
69
- <h2>Content of this cell will fill available space and match heights</h2>
70
- <p>Cells set to 50% width gap of 25px</p>
71
- Panel 4<br />Panel 4<br />Panel 4<br />Panel 4<br />Panel 4
24
+ <section class="bottom-row">
25
+ <div v-for="key in bottomRowItemCount" class="panel">
26
+ <slot :name="`bottom-row-${key}-content`"></slot>
72
27
  </div>
73
28
  </section>
74
29
  </div>
@@ -76,6 +31,14 @@
76
31
 
77
32
  <script setup lang="ts">
78
33
  const props = defineProps({
34
+ topRowSlot1ItemCount: {
35
+ type: Number as PropType<number>,
36
+ default: 6,
37
+ },
38
+ bottomRowItemCount: {
39
+ type: Number as PropType<number>,
40
+ default: 4,
41
+ },
79
42
  styleClassPassthrough: {
80
43
  type: Array as PropType<string[]>,
81
44
  default: () => [],
@@ -93,17 +56,13 @@ watch(
93
56
  </script>
94
57
 
95
58
  <style lang="css">
96
- .layout-reporting-grid {
97
- --brand-purple: #781d7d;
98
- --brand-blue: #00bbe4;
99
- --brand-green: #8ec73f;
100
- --brand-light-grey: #cccccc;
101
- --brand-dark-grey: #333333;
102
- --box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
59
+ .layout-grid-b {
60
+ --_border-color: light-dark(hsl(0, 29%, 3%), hsl(0, 0%, 92%));
61
+ --_color: light-dark(hsl(0, 29%, 3%), hsl(0, 0%, 92%));
62
+ --_gap: 12px;
103
63
 
104
64
  .panel {
105
- background-color: white;
106
- border: 1px solid var(--brand-light-grey);
65
+ border: 1px solid var(--_border-color);
107
66
  border-radius: 12px;
108
67
  padding: 12px;
109
68
  height: auto;
@@ -112,88 +71,62 @@ watch(
112
71
  container-type: inline-size;
113
72
  display: grid;
114
73
  grid-template-columns: 1fr;
115
- gap: 25px;
74
+ gap: var(--_gap);
116
75
  width: 100%;
117
76
  margin-inline: auto;
118
77
 
119
- /* Adding a media/container query to reporting-block can make this responsive mobile upwards */
120
- .reporting-block {
78
+ .top-row {
121
79
  display: grid;
80
+ gap: var(--_gap);
81
+ width: 100%;
82
+
122
83
  grid-template-columns: 1fr;
123
84
  grid-template-areas:
124
85
  'slot1'
86
+ 'slot2'
125
87
  'slot3';
126
- gap: 25px;
127
- width: 100%;
128
88
 
129
- &:has(.slot-2) {
130
- grid-template-columns: 1fr;
89
+ @container (min-width: 1024px) {
90
+ grid-template-columns: 1fr minmax(460px, 33%);
131
91
  grid-template-areas:
132
- 'slot1'
133
- 'slot2'
134
- 'slot3';
135
-
136
- @container (min-width: 1024px) {
137
- grid-template-columns: 1fr minmax(460px, 33%);
138
- grid-template-areas:
139
- 'slot1 slot2'
140
- 'slot3 slot2';
141
- }
92
+ 'slot1 slot2'
93
+ 'slot3 slot2';
142
94
  }
143
95
  }
144
96
 
145
- .slot-1 {
146
- display: grid;
97
+ .top-row-slot-1 {
147
98
  grid-area: slot1;
148
- /* Force 5 equally paced items */
149
- grid-template-columns: 1fr;
150
-
151
- gap: 12px;
99
+ container-type: inline-size;
152
100
 
153
- /* Columns will be fluid between 250px and full width depending on space available */
154
- @container (min-width: 680px) {
155
- grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
156
- gap: 12px;
157
- }
158
-
159
- .panel {
101
+ .top-row-slot-1-inner {
160
102
  display: grid;
161
- color: white;
103
+ grid-template-columns: repeat(2, 1fr);
162
104
 
163
- &:nth-child(1) {
164
- background-color: var(--brand-purple);
165
- }
105
+ gap: var(--_gap);
166
106
 
167
- &:nth-child(2) {
168
- background-color: var(--brand-blue);
107
+ @container (min-width: 680px) {
108
+ grid-template-columns: repeat(3, 1fr);
169
109
  }
170
110
 
171
- &:nth-child(3) {
172
- background-color: var(--brand-green);
173
- }
174
-
175
- &:nth-child(4) {
176
- background-color: var(--brand-light-grey);
177
- }
178
-
179
- &:nth-child(5) {
180
- background-color: var(--brand-dark-grey);
111
+ .panel {
112
+ display: grid;
181
113
  }
182
114
  }
183
115
  }
184
- .slot-2 {
116
+
117
+ .top-row-slot-2 {
185
118
  grid-area: slot2;
186
119
  display: grid;
187
120
  }
188
- .slot-3 {
121
+ .top-row-slot-3 {
189
122
  grid-area: slot3;
190
123
  display: grid;
191
124
  }
192
125
 
193
- .dashboard-block {
126
+ .bottom-row {
194
127
  display: grid;
195
128
  grid-template-columns: repeat(2, 1fr);
196
- gap: 25px;
129
+ gap: var(--_gap);
197
130
  }
198
131
  }
199
132
  </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: 300px;
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: 1.2rem;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "1.1.6",
4
+ "version": "1.2.0",
5
5
  "main": "nuxt.config.ts",
6
6
  "scripts": {
7
7
  "clean": "rm -rf .nuxt && rm -rf .output && rm -rf .playground/.nuxt && rm -rf .playground/.output",