@rancher/shell 3.0.9-rc.6 → 3.0.9

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.
Files changed (40) hide show
  1. package/components/IconOrSvg.vue +61 -42
  2. package/components/SortableTable/index.vue +2 -2
  3. package/components/form/BannerSettings.vue +2 -2
  4. package/components/form/NotificationSettings.vue +2 -2
  5. package/config/product/manager.js +0 -1
  6. package/detail/fleet.cattle.io.cluster.vue +1 -1
  7. package/dialog/FeatureFlagListDialog.vue +1 -1
  8. package/edit/catalog.cattle.io.clusterrepo.vue +1 -1
  9. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  10. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  11. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  12. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  13. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  14. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  15. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  16. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  17. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  18. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  19. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  20. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  21. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  22. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  23. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  24. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  25. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +14 -12
  26. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -5
  27. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +18 -3
  28. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +100 -76
  29. package/list/provisioning.cattle.io.cluster.vue +2 -2
  30. package/models/__tests__/chart.test.ts +2 -2
  31. package/models/chart.js +3 -3
  32. package/package.json +1 -1
  33. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
  34. package/pages/c/_cluster/apps/charts/index.vue +1 -11
  35. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  36. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  37. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  38. package/rancher-components/RcItemCard/RcItemCard.vue +8 -1
  39. package/types/shell/index.d.ts +1 -0
  40. package/utils/svg-filter.js +4 -3
@@ -19,6 +19,7 @@ interface Props {
19
19
  mode?: string;
20
20
  value: string | string[];
21
21
  nginxSupported: boolean;
22
+ traefikSupported: boolean;
22
23
  nginxChart: string;
23
24
  traefikChart: string;
24
25
  userChartValues: any;
@@ -30,6 +31,7 @@ const {
30
31
  nginxChart,
31
32
  traefikChart,
32
33
  nginxSupported,
34
+ traefikSupported,
33
35
  userChartValues,
34
36
  versionInfo
35
37
  } = defineProps<Props>();
@@ -44,6 +46,9 @@ const showAdvanced = ref<Boolean>(false);
44
46
  const isView = computed(() => mode === _VIEW);
45
47
  const isEdit = computed(() => mode === _EDIT);
46
48
  const showTraefikBanner = ref<Boolean>(false);
49
+ const traefikMerged = ref(initYamlEditor(traefikChart));
50
+ const nginxMerged = ref(initYamlEditor(nginxChart));
51
+ const showConfig = computed(() => !!versionInfo[traefikChart] || !!versionInfo[nginxChart]);
47
52
 
48
53
  const ingressSelection = computed(() => {
49
54
  if (Array.isArray(value) ) {
@@ -55,9 +60,21 @@ const ingressSelection = computed(() => {
55
60
  }
56
61
  });
57
62
  const ingressOptions = computed(() => {
58
- return INGRESS_OPTIONS.filter((option) => !(option.id === INGRESS_DUAL && mode === _CREATE) &&
59
- !((option.id === INGRESS_NGINX || option.id === INGRESS_DUAL) && !nginxSupported)
60
- ).map((option) => {
63
+ return INGRESS_OPTIONS.filter((option) => {
64
+ if (option.id === INGRESS_NGINX) {
65
+ return nginxSupported;
66
+ } else if (option.id === TRAEFIK) {
67
+ return traefikSupported;
68
+ } else if (option.id === INGRESS_DUAL) {
69
+ if (mode === _CREATE) {
70
+ return false;
71
+ } else {
72
+ return traefikSupported && nginxSupported;
73
+ }
74
+ }
75
+
76
+ return true;
77
+ }).map((option) => {
61
78
  return {
62
79
  ...option,
63
80
  selected: option.id === ingressSelection.value
@@ -73,7 +90,13 @@ const ingressEnabled = computed({
73
90
  if (!val) {
74
91
  emit('update:value', INGRESS_NONE);
75
92
  } else {
76
- emit('update:value', TRAEFIK);
93
+ if (traefikSupported) {
94
+ emit('update:value', TRAEFIK);
95
+ } else if (nginxSupported) {
96
+ emit('update:value', INGRESS_NGINX);
97
+ } else {
98
+ emit('update:value', INGRESS_NONE);
99
+ }
77
100
  }
78
101
  }
79
102
  });
@@ -102,9 +125,6 @@ function preconfigureTraefik() {
102
125
  emit('update-values', traefikChart, traefikMerged.value);
103
126
  }
104
127
 
105
- const traefikMerged = ref(initYamlEditor(traefikChart));
106
- const nginxMerged = ref(initYamlEditor(nginxChart));
107
-
108
128
  const nginxHttp = computed({
109
129
  get() {
110
130
  return get(nginxMerged.value, 'controller.hostPort.ports.http');
@@ -193,12 +213,14 @@ function updateYaml(component: any, value: any) {
193
213
  </div>
194
214
  <div v-else>
195
215
  <Banner
216
+ v-if="traefikSupported"
196
217
  color="info"
197
218
  label-key="cluster.ingress.banners.transitioning.label"
198
219
  />
199
220
  <IngressCards
200
221
  :options="ingressOptions"
201
222
  :mode="mode"
223
+ :class="!traefikSupported ? 'mt-10' : ''"
202
224
  @select="selectIngress"
203
225
  />
204
226
  <Banner
@@ -219,79 +241,81 @@ function updateYaml(component: any, value: any) {
219
241
  </template>
220
242
  </RichTranslation>
221
243
  </Banner>
222
- <div class="mt-20">
223
- <IngressConfiguration
224
- v-model:compatibility-mode="compatibilityMode"
225
- v-model:nginxHttp="nginxHttp"
226
- v-model:nginxHttps="nginxHttps"
227
- v-model:traefikHttp="traefikHttp"
228
- v-model:traefikHttps="traefikHttps"
229
- :mode="mode"
230
- :ingress-selection="ingressSelection"
231
- @validation-changed="emit('config-validation-changed', $event)"
232
- />
233
- </div>
234
- <div>
235
- <button
236
- type="button"
237
- class="btn role-link advanced-toggle mb-0"
238
- @click="showAdvanced = !showAdvanced"
239
- >
240
- {{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
241
- </button>
242
- </div>
243
- <template v-if="showAdvanced">
244
- <div class="row">
245
- <div
246
- v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
247
- :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
244
+ <div v-if="showConfig">
245
+ <div class="mt-20">
246
+ <IngressConfiguration
247
+ v-model:compatibility-mode="compatibilityMode"
248
+ v-model:nginxHttp="nginxHttp"
249
+ v-model:nginxHttps="nginxHttps"
250
+ v-model:traefikHttp="traefikHttp"
251
+ v-model:traefikHttps="traefikHttps"
252
+ :mode="mode"
253
+ :ingress-selection="ingressSelection"
254
+ @validation-changed="emit('config-validation-changed', $event)"
255
+ />
256
+ </div>
257
+ <div>
258
+ <button
259
+ type="button"
260
+ class="btn role-link advanced-toggle mb-0"
261
+ @click="showAdvanced = !showAdvanced"
248
262
  >
249
- <p
250
- v-if="ingressSelection === INGRESS_DUAL"
251
- class="mb-10"
263
+ {{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
264
+ </button>
265
+ </div>
266
+ <template v-if="showAdvanced">
267
+ <div class="row">
268
+ <div
269
+ v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
270
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
252
271
  >
253
- {{ t('cluster.ingress.traefik.header') }}
254
- </p>
255
- <YamlEditor
256
- ref="traefik-yaml"
257
- class="ingress-yaml-editor"
258
- data-testid="traefik-yaml-editor"
259
- :value="traefikMerged"
260
- :mode="mode"
261
- :scrolling="true"
262
- :as-object="true"
263
- :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
264
- :hide-preview-buttons="true"
265
- @update:value="emit('update-values', traefikChart, $event)"
266
- @validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
267
- />
268
- </div>
269
- <div
270
- v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
271
- :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
272
- >
273
- <p
274
- v-if="ingressSelection === INGRESS_DUAL"
275
- class="mb-10"
272
+ <p
273
+ v-if="ingressSelection === INGRESS_DUAL"
274
+ class="mb-10"
275
+ >
276
+ {{ t('cluster.ingress.traefik.header') }}
277
+ </p>
278
+ <YamlEditor
279
+ ref="traefik-yaml"
280
+ class="ingress-yaml-editor"
281
+ data-testid="traefik-yaml-editor"
282
+ :value="traefikMerged"
283
+ :mode="mode"
284
+ :scrolling="true"
285
+ :as-object="true"
286
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
287
+ :hide-preview-buttons="true"
288
+ @update:value="emit('update-values', traefikChart, $event)"
289
+ @validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
290
+ />
291
+ </div>
292
+ <div
293
+ v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
294
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
276
295
  >
277
- {{ t('cluster.ingress.nginx.header') }}
278
- </p>
279
- <YamlEditor
280
- ref="nginx-yaml"
281
- class="ingress-yaml-editor"
282
- data-testid="ingress-nginx-yaml-editor"
283
- :value="nginxMerged"
284
- :mode="mode"
285
- :scrolling="true"
286
- :as-object="true"
287
- :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
288
- :hide-preview-buttons="true"
289
- @update:value="emit('update-values', nginxChart, $event)"
290
- @validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
291
- />
296
+ <p
297
+ v-if="ingressSelection === INGRESS_DUAL"
298
+ class="mb-10"
299
+ >
300
+ {{ t('cluster.ingress.nginx.header') }}
301
+ </p>
302
+ <YamlEditor
303
+ ref="nginx-yaml"
304
+ class="ingress-yaml-editor"
305
+ data-testid="ingress-nginx-yaml-editor"
306
+ :value="nginxMerged"
307
+ :mode="mode"
308
+ :scrolling="true"
309
+ :as-object="true"
310
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
311
+ :hide-preview-buttons="true"
312
+ @update:value="emit('update-values', nginxChart, $event)"
313
+ @validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
314
+ />
315
+ </div>
292
316
  </div>
293
- </div>
294
- </template>
317
+ </template>
318
+ </div>
295
319
  </div>
296
320
  </template>
297
321
 
@@ -300,7 +300,7 @@ export default {
300
300
  </div>
301
301
  </template>
302
302
 
303
- <stye scoped lang="scss">
303
+ <style scoped lang="scss">
304
304
  .capi-unsupported {
305
305
  &.has-description {
306
306
  border-bottom: none;
@@ -317,4 +317,4 @@ export default {
317
317
  display: flex;
318
318
  }
319
319
  }
320
- </stye>
320
+ </style>
@@ -236,12 +236,12 @@ describe('class Chart', () => {
236
236
 
237
237
  expect(result.footerItems).toHaveLength(3);
238
238
 
239
- const categoryItem = result.footerItems.find((i) => i.icon === 'icon-category-alt');
239
+ const categoryItem = result.footerItems.find((i) => i.icon === 'category-alt');
240
240
 
241
241
  expect(categoryItem).toBeDefined();
242
242
  expect(categoryItem?.labels).toContain('database');
243
243
 
244
- const tagItem = result.footerItems.find((i) => i.icon === 'icon-tag-alt');
244
+ const tagItem = result.footerItems.find((i) => i.icon === 'tag-alt');
245
245
 
246
246
  expect(tagItem).toBeDefined();
247
247
  expect(tagItem?.labels).toStrictEqual(expect.arrayContaining(['linux', 'experimentl']));
package/models/chart.js CHANGED
@@ -164,7 +164,7 @@ export default class Chart extends SteveModel {
164
164
  const footerItems = [
165
165
  {
166
166
  type: REPO,
167
- icon: 'icon-repository-alt',
167
+ icon: 'repository-alt',
168
168
  iconTooltip: { key: 'tableHeaders.repoName' },
169
169
  labels: [this.repoNameDisplay],
170
170
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.repo') }, true)
@@ -174,7 +174,7 @@ export default class Chart extends SteveModel {
174
174
  if (this.categories.length) {
175
175
  footerItems.push( {
176
176
  type: CATEGORY,
177
- icon: 'icon-category-alt',
177
+ icon: 'category-alt',
178
178
  iconTooltip: { key: 'generic.category' },
179
179
  labels: this.categories,
180
180
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.category') }, true)
@@ -184,7 +184,7 @@ export default class Chart extends SteveModel {
184
184
  if (this.tags.length) {
185
185
  footerItems.push({
186
186
  type: TAG,
187
- icon: 'icon-tag-alt',
187
+ icon: 'tag-alt',
188
188
  iconTooltip: { key: 'generic.tags' },
189
189
  labels: this.tags,
190
190
  labelTooltip: this.t('catalog.charts.findSimilar.message', { type: this.t('catalog.charts.findSimilar.types.tag') }, true)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.9-rc.6",
3
+ "version": "3.0.9",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { RcItemCardAction } from '@components/RcItemCard';
3
3
  import { RcButton } from '@components/RcButton';
4
+ import { RcIcon } from '@components/RcIcon';
4
5
 
5
6
  interface FooterItem {
6
7
  icon?: string;
@@ -31,13 +32,19 @@ function onClickItem(type: string, label: string) {
31
32
  class="app-chart-card-footer-item"
32
33
  data-testid="app-chart-card-footer-item"
33
34
  >
35
+ <RcIcon
36
+ v-if="footerItem.icon"
37
+ v-clean-tooltip="footerItem.iconTooltip?.key ? t(footerItem.iconTooltip?.key) : undefined"
38
+ class="app-chart-card-footer-item-icon"
39
+ :type="footerItem.icon"
40
+ />
34
41
  <template
35
42
  v-for="(label, j) in footerItem.labels"
36
43
  :key="j"
37
44
  >
38
45
  <rc-item-card-action
39
46
  v-if="clickable && footerItem.type"
40
- class="app-chart-card-footer-item-text"
47
+ class="app-chart-card-footer-item-action"
41
48
  >
42
49
  <rc-button
43
50
  v-clean-tooltip="footerItem.labelTooltip"
@@ -47,18 +54,12 @@ function onClickItem(type: string, label: string) {
47
54
  :aria-label="t('catalog.charts.appChartCard.footerItem.ariaLabel', { filter: label })"
48
55
  @click="onClickItem(footerItem.type, label)"
49
56
  >
50
- <template
51
- v-if="footerItem.icon"
52
- #before
53
- >
54
- <i
55
- v-clean-tooltip="t(footerItem.iconTooltip?.key)"
56
- :class="['icon', 'app-chart-card-footer-item-icon', footerItem.icon]"
57
- />
58
- </template>
59
- {{ label }}
60
- <span v-if="footerItem.labels.length > 1 && j !== footerItem.labels.length - 1">, </span>
57
+ <span class="app-chart-card-footer-button-label">{{ label }}</span>
61
58
  </rc-button>
59
+ <span
60
+ v-if="footerItem.labels.length > 1 && j !== footerItem.labels.length - 1"
61
+ class="app-chart-card-footer-item-separator"
62
+ >,</span>
62
63
  </rc-item-card-action>
63
64
  <span
64
65
  v-else
@@ -67,7 +68,10 @@ function onClickItem(type: string, label: string) {
67
68
  data-testid="app-chart-card-footer-item-text"
68
69
  >
69
70
  {{ label }}
70
- <span v-if="footerItem.labels.length > 1 && j !== footerItem.labels.length - 1">, </span>
71
+ <span
72
+ v-if="footerItem.labels.length > 1 && j !== footerItem.labels.length - 1"
73
+ class="app-chart-card-footer-item-separator"
74
+ >,</span>
71
75
  </span>
72
76
  </template>
73
77
  </div>
@@ -78,6 +82,7 @@ function onClickItem(type: string, label: string) {
78
82
  .app-chart-card-footer {
79
83
  display: flex;
80
84
  flex-wrap: wrap;
85
+ max-width: 100%;
81
86
 
82
87
  &-item {
83
88
  display: flex;
@@ -85,18 +90,31 @@ function onClickItem(type: string, label: string) {
85
90
  color: var(--link-text-secondary);
86
91
  margin-top: 8px;
87
92
  margin-right: 8px;
93
+ min-width: 0;
94
+ max-width: 100%;
95
+
96
+ &-action {
97
+ display: flex;
98
+ align-items: center;
99
+ min-width: 0; // Critical for truncation in flex containers
100
+ max-width: 100%;
101
+ }
88
102
 
89
103
  &-text {
90
- margin-right: 8px;
91
- display: -webkit-box;
92
- -webkit-line-clamp: 1;
93
- -webkit-box-orient: vertical;
104
+ display: block;
94
105
  overflow: hidden;
95
106
  text-overflow: ellipsis;
96
- word-break: break-all;
107
+ white-space: nowrap;
108
+ min-width: 0;
109
+ }
110
+
111
+ &-separator {
112
+ flex-shrink: 0;
113
+ margin-right: 4px;
97
114
  }
98
115
 
99
116
  &-icon {
117
+ flex-shrink: 0;
100
118
  width: 20px;
101
119
  height: 20px;
102
120
  display: flex;
@@ -109,6 +127,15 @@ function onClickItem(type: string, label: string) {
109
127
 
110
128
  &-button {
111
129
  text-transform: capitalize;
130
+ min-width: 0;
131
+ max-width: 100%;
132
+
133
+ &-label {
134
+ display: block;
135
+ overflow: hidden;
136
+ text-overflow: ellipsis;
137
+ white-space: nowrap;
138
+ }
112
139
  }
113
140
  }
114
141
 
@@ -321,7 +321,7 @@ export default {
321
321
  pill: chart.featured ? { label: { key: 'generic.shortFeatured' }, tooltip: { key: 'generic.featured' } } : undefined,
322
322
  header: {
323
323
  title: { text: chart.chartNameDisplay },
324
- statuses: this.addStatusesToCharts(chart),
324
+ statuses: chart.cardContent.statuses
325
325
  },
326
326
  subHeaderItems: chart.cardContent.subHeaderItems,
327
327
  image: { src: chart.latestCompatibleVersion.icon, alt: { text: this.t('catalog.charts.iconAlt', { app: get(chart, 'chartNameDisplay') }) } },
@@ -553,16 +553,6 @@ export default {
553
553
  }
554
554
  },
555
555
 
556
- addStatusesToCharts(chart) {
557
- if (this.suseAppCollectionRepo.includes(chart.repoName)) {
558
- return [{
559
- icon: 'icon-notify-info', color: 'info', tooltip: { key: 'catalog.charts.isFromSuseAppCoRepository' }
560
- }, ...chart.cardContent.statuses];
561
- }
562
-
563
- return chart.cardContent.statuses;
564
- },
565
-
566
556
  async closeSuseAppCollectionBanner() {
567
557
  this.showAppCollectionBanner = false;
568
558
  await this.$store.dispatch('prefs/set', { key: HIDE_SUSE_APP_COLLECTION_REPO_BANNER, value: true });
@@ -98,7 +98,7 @@ export default {
98
98
  },
99
99
  subHeaderItems: chart.cardContent.subHeaderItems,
100
100
  footerItems: chart.deploysOnWindows ? [{
101
- icon: 'icon-tag-alt',
101
+ icon: 'tag-alt',
102
102
  iconTooltip: { key: 'generic.tags' },
103
103
  labels: [this.t('catalog.charts.deploysOnWindows')],
104
104
  }] : [],
@@ -9,7 +9,7 @@ import {
9
9
  ID_UNLINKED,
10
10
  NAME_UNLINKED,
11
11
  } from '@shell/config/table-headers';
12
- import { allHash } from 'utils/promise';
12
+ import { allHash } from '@shell/utils/promise';
13
13
 
14
14
  export default {
15
15
  components: {
@@ -873,7 +873,7 @@ export default {
873
873
  }
874
874
 
875
875
  return labels.length ? [{
876
- icon: 'icon-tag-alt',
876
+ icon: 'tag-alt',
877
877
  iconTooltip: { key: 'generic.tags' },
878
878
  labels,
879
879
  }] : [];
@@ -314,7 +314,10 @@ const cursorValue = computed(() => props.clickable ? 'pointer' : 'auto');
314
314
  </div>
315
315
  </div>
316
316
 
317
- <slot name="item-card-sub-header" />
317
+ <slot name="item-card-sub-header">
318
+ <!-- DIV added to add the gap if the sub-header is not provided -->
319
+ <div />
320
+ </slot>
318
321
 
319
322
  <template v-if="$slots['item-card-content']">
320
323
  <slot name="item-card-content">
@@ -407,6 +410,10 @@ $image-medium-box-width: 48px;
407
410
  height: 24px;
408
411
  color: var(--body-text);
409
412
 
413
+ &.small {
414
+ height: 32px;
415
+ }
416
+
410
417
  &-left,
411
418
  &-right {
412
419
  display: flex;
@@ -5240,6 +5240,7 @@ export class Solver {
5240
5240
  values: any;
5241
5241
  loss: number;
5242
5242
  filter: string;
5243
+ filterVal: string;
5243
5244
  };
5244
5245
  solveWide(): {
5245
5246
  loss: number;
@@ -169,9 +169,10 @@ export class Solver {
169
169
  const result = this.solveNarrow(this.solveWide());
170
170
 
171
171
  return {
172
- values: result.values,
173
- loss: result.loss,
174
- filter: this.css(result.values),
172
+ values: result.values,
173
+ loss: result.loss,
174
+ filter: this.css(result.values),
175
+ filterVal: this.css(result.values).replace('filter: ', '').replace(';', '')
175
176
  };
176
177
  }
177
178