spoko-design-system 1.3.1 → 1.3.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [1.3.3](https://github.com/polo-blue/sds/compare/v1.3.2...v1.3.3) (2025-10-07)
2
+
3
+ ### Bug Fixes
4
+
5
+ * prevent double rendering of links array with slot fallback ([ac7ec23](https://github.com/polo-blue/sds/commit/ac7ec2349b2098912ed27a303a7502c9a1c63678))
6
+
7
+ ## [1.3.2](https://github.com/polo-blue/sds/compare/v1.3.1...v1.3.2) (2025-10-07)
8
+
1
9
  ## [1.3.1](https://github.com/polo-blue/sds/compare/v1.3.0...v1.3.1) (2025-10-05)
2
10
 
3
11
  ## [1.3.0](https://github.com/polo-blue/sds/compare/v1.2.2...v1.3.0) (2025-10-05)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "private": false,
5
5
  "main": "./index.ts",
6
6
  "module": "./index.ts",
@@ -2,30 +2,18 @@
2
2
  import { PropType, computed } from 'vue';
3
3
  import ProductDetailName from './ProductDetailName.vue';
4
4
 
5
- interface ColorCode {
6
- code: string;
7
- name: string;
5
+ interface Link {
6
+ type: string;
7
+ url: string;
8
+ anchor: string;
8
9
  }
9
10
 
10
11
  interface TableItem {
11
12
  id: string;
12
- name: string;
13
- value: unknown; // Może być string, number, boolean, array (ColorCode[] lub string[])
14
- translated?: boolean;
15
- icon?: boolean;
16
- isArrayValue?: boolean;
17
- isColorArray?: boolean; // dla product.colors (color_ids)
18
- isPaintMarks?: boolean; // dla product.paint_marks_text
19
- isGenericArray?: boolean; // dla ogólnych tablic stringów (np. position)
20
- isForExteriorColour?: boolean; // Ta flaga będzie ustawiana przez getProductDetails na true, jeśli 'value' jest tablicą
21
- }
22
-
23
- interface GroupedLink {
24
- id: string;
25
- links: {
26
- name: string;
27
- value: string;
28
- }[];
13
+ label: string; // Display name from API
14
+ value: unknown; // Can be string, number, boolean, string array, or Link array
15
+ type?: string; // 'links' for link arrays
16
+ isGenericArray?: boolean; // for generic string arrays (e.g., position)
29
17
  }
30
18
 
31
19
  const props = defineProps({
@@ -33,21 +21,9 @@ const props = defineProps({
33
21
  caption: { type: String, default: null },
34
22
  });
35
23
 
36
- // Function to check if a value is a link
37
- const isLink = (id: string) => {
38
- return ['blog', 'youtube', 'vimeo'].includes(id);
39
- };
40
-
41
- // Function to check if it's a color array (for 'color_ids' field from product.colors)
42
- // This will still apply to the 'color' detail if its value is an array of ColorCode objects
43
- const isColorArray = (item: TableItem) => {
44
- const colorIds = ['color', 'thread-color']; // lista ID które są kolorami
45
- return (item.isColorArray || colorIds.includes(item.id)) && Array.isArray(item.value);
46
- };
47
-
48
- // Function to check if it's paint marks (value is now a string from API)
49
- const isPaintMarks = (item: TableItem) => {
50
- return item.isPaintMarks && typeof item.value === 'string';
24
+ // Function to check if it's a links array from new API
25
+ const isLinksArray = (item: TableItem) => {
26
+ return item.type === 'links' && Array.isArray(item.value);
51
27
  };
52
28
 
53
29
  // Function to check if it's a generic array (e.g., for position)
@@ -55,33 +31,23 @@ const isGenericArray = (item: TableItem) => {
55
31
  return item.isGenericArray && Array.isArray(item.value);
56
32
  };
57
33
 
58
- // ✅ Zaktualizowana funkcja: Sprawdzamy ID i czy value jest faktycznie tablicą ColorCode[]
59
- const isForExteriorColour = (item: TableItem) => {
60
- return item.id === 'for-exterior-colour' && Array.isArray(item.value);
61
- };
62
-
63
34
  // Function to check if value is HTML string (fallback)
64
35
  const isHtmlValue = (value: unknown): boolean => {
65
36
  return typeof value === 'string' && (value.includes('<span') || value.includes('<br>'));
66
37
  };
67
38
 
68
39
  // Function for specifying header text
69
- const getHeaderText = (row: TableItem | GroupedLink) => {
70
- if (row.id === 'blog') {
71
- return row.id.charAt(0).toUpperCase() + row.id.slice(1);
72
- }
73
- // Użyj `name` z obiektu `TableItem`, jeśli istnieje, w przeciwnym razie sformatuj `id`.
74
- return 'name' in row
75
- ? row.name
76
- : row.id
77
- .split('-')
78
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
79
- .join(' ');
40
+ const getHeaderText = (row: TableItem) => {
41
+ return row.label || row.id
42
+ .split('-')
43
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
44
+ .join(' ');
80
45
  };
81
46
 
82
47
  // Function to determine the icon class for a link type
83
- const getLinkIconClass = (linkId: string) => {
84
- switch (linkId) {
48
+ const getLinkIconClass = (linkType: string) => {
49
+ const type = linkType.toLowerCase();
50
+ switch (type) {
85
51
  case 'blog':
86
52
  return 'i-lucide-book-text';
87
53
  case 'youtube':
@@ -93,46 +59,13 @@ const getLinkIconClass = (linkId: string) => {
93
59
  }
94
60
  };
95
61
 
96
- // Grouping of elements by id
97
- const groupedItems = computed(() => {
98
- // ✅ Add validation to ensure props.items is an array
62
+ // Validate items
63
+ const validatedItems = computed(() => {
99
64
  if (!Array.isArray(props.items)) {
100
65
  console.warn('ProductDetailsList: items prop is not an array:', props.items);
101
66
  return [];
102
67
  }
103
-
104
- const result: (TableItem | GroupedLink)[] = [];
105
- const linkGroups = new Map<string, GroupedLink>();
106
-
107
- // Process all elements
108
- for (const item of props.items) {
109
- // If it's a link (blog, youtube, vimeo)
110
- if (isLink(item.id)) {
111
- // Add a link to the relevant group
112
- if (!linkGroups.has(item.id)) {
113
- linkGroups.set(item.id, {
114
- id: item.id,
115
- links: [],
116
- });
117
- }
118
-
119
- // Add link to the relevant group
120
- linkGroups.get(item.id)?.links.push({
121
- name: item.name,
122
- value: item.value as string,
123
- });
124
- } else {
125
- // If it's not a link, add it normally to the results
126
- result.push(item);
127
- }
128
- }
129
-
130
- // Add all link groups at the end
131
- for (const group of linkGroups.values()) {
132
- result.push(group);
133
- }
134
-
135
- return result;
68
+ return props.items;
136
69
  });
137
70
  </script>
138
71
 
@@ -149,7 +82,7 @@ const groupedItems = computed(() => {
149
82
  </colgroup>
150
83
  <tbody>
151
84
  <tr
152
- v-for="(row, index) in groupedItems"
85
+ v-for="(row, index) in validatedItems"
153
86
  :key="index"
154
87
  class="details-table-row"
155
88
  >
@@ -159,85 +92,38 @@ const groupedItems = computed(() => {
159
92
  class="details-table-header"
160
93
  />
161
94
 
95
+ <!-- Links Array -->
162
96
  <td
163
- v-if="'links' in row"
97
+ v-if="isLinksArray(row)"
164
98
  class="details-table-cell"
165
99
  >
166
100
  <ul class="list-none p-0 m-0">
167
101
  <li
168
- v-for="(link, linkIndex) in row.links"
102
+ v-for="(link, linkIndex) in row.value as Link[]"
169
103
  :key="linkIndex"
170
104
  class="mb-2 last:mb-0 flex items-center"
171
105
  >
172
106
  <span
173
107
  :class="[
174
- getLinkIconClass(row.id),
108
+ getLinkIconClass(link.type),
175
109
  'leading-none inline-block mr-2 w-4 min-w-4 h-4 text-gray-400',
176
110
  ]"
177
111
  />
178
112
  <a
179
- :href="link.value"
113
+ :href="link.url"
180
114
  target="_blank"
181
115
  rel="noopener noreferrer"
182
116
  class="link-primary"
183
117
  >
184
- {{ link.name }}
118
+ {{ link.anchor }}
185
119
  </a>
186
120
  </li>
187
121
  </ul>
188
122
  </td>
189
123
 
124
+ <!-- Generic String Array -->
190
125
  <td
191
- v-else-if="'id' in row && isColorArray(row)"
192
- class="details-table-cell"
193
- >
194
- <ul class="list-none p-0 m-0">
195
- <li
196
- v-for="(colorItem, colorIndex) in row.value as ColorCode[]"
197
- :key="colorIndex"
198
- class="flex items-center gap-1 mb-1 last:mb-0"
199
- >
200
- <template v-if="colorItem.code">
201
- <code class="font-mono text-sm">
202
- {{ colorItem.code }}
203
- </code>
204
- <span class="text-gray-400">-</span>
205
- </template>
206
- <span class="text-gray-700 dark:text-gray-300">{{ colorItem.name }}</span>
207
- </li>
208
- </ul>
209
- </td>
210
-
211
- <td
212
- v-else-if="'id' in row && isPaintMarks(row)"
213
- class="details-table-cell"
214
- >
215
- <span class="text-gray-700 dark:text-gray-300">{{ row.value }}</span>
216
- </td>
217
-
218
- <td
219
- v-else-if="'id' in row && isForExteriorColour(row)"
220
- class="details-table-cell"
221
- >
222
- <ul class="list-none p-0 m-0">
223
- <li
224
- v-for="(colorEntry, colourIndex) in row.value as ColorCode[]"
225
- :key="colourIndex"
226
- class="flex items-center gap-1 mb-1 last:mb-0"
227
- >
228
- <template v-if="colorEntry.code">
229
- <code class="font-mono text-sm">
230
- {{ colorEntry.code }}
231
- </code>
232
- <span class="text-gray-400">-</span>
233
- </template>
234
- <span class="text-gray-700 dark:text-gray-300">{{ colorEntry.name }}</span>
235
- </li>
236
- </ul>
237
- </td>
238
-
239
- <td
240
- v-else-if="'id' in row && isGenericArray(row)"
126
+ v-else-if="isGenericArray(row)"
241
127
  class="details-table-cell"
242
128
  >
243
129
  <ul class="list-none p-0 m-0">
@@ -252,14 +138,16 @@ const groupedItems = computed(() => {
252
138
  </ul>
253
139
  </td>
254
140
 
141
+ <!-- HTML Value -->
255
142
  <td
256
- v-else-if="'id' in row && isHtmlValue(row.value)"
143
+ v-else-if="isHtmlValue(row.value)"
257
144
  class="details-table-cell"
258
145
  v-html="row.value"
259
146
  />
260
147
 
148
+ <!-- Slot or Default Value -->
261
149
  <slot
262
- v-else-if="'id' in row"
150
+ v-else
263
151
  :name="row.id"
264
152
  >
265
153
  <td class="details-table-cell">