design-system-next 2.22.0 → 2.22.2

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,48 +1,60 @@
1
- import type { PropType, ExtractPropTypes } from 'vue';
2
- import type { MenuListType } from '../list';
3
-
4
- export const listItemPropTypes = {
5
- item: {
6
- type: Object as PropType<MenuListType>,
7
- required: true,
8
- },
9
- isSelected: {
10
- type: Boolean,
11
- required: true,
12
- },
13
- classes: {
14
- type: [String, Array, Object] as PropType<string | string[] | Record<string, boolean>>,
15
- required: true,
16
- },
17
- multiSelect: {
18
- type: Boolean,
19
- default: false,
20
- },
21
- lozenge: {
22
- type: Boolean,
23
- default: false,
24
- },
25
- ladderized: {
26
- type: Boolean,
27
- default: false,
28
- },
29
- noCheck: {
30
- type: Boolean,
31
- default: false,
32
- },
33
- itemIcon: {
34
- type: String,
35
- default: '',
36
- },
37
- disabledUnselectedItems: {
38
- type: Boolean,
39
- default: false,
40
- },
41
- };
42
-
43
- export const listItemEmitTypes = {
44
- select: () => true,
45
- };
46
-
47
- export type ListItemPropTypes = ExtractPropTypes<typeof listItemPropTypes>;
48
- export type ListItemEmitTypes = typeof listItemEmitTypes;
1
+ import type { PropType, ExtractPropTypes } from 'vue';
2
+ import type { MenuListType } from '../list';
3
+
4
+ export const listItemPropTypes = {
5
+ item: {
6
+ type: Object as PropType<MenuListType>,
7
+ required: true,
8
+ },
9
+ isSelected: {
10
+ type: Boolean,
11
+ required: true,
12
+ },
13
+ classes: {
14
+ type: [String, Array, Object] as PropType<string | string[] | Record<string, boolean>>,
15
+ required: true,
16
+ },
17
+ multiSelect: {
18
+ type: Boolean,
19
+ default: false,
20
+ },
21
+ lozenge: {
22
+ type: Boolean,
23
+ default: false,
24
+ },
25
+ ladderized: {
26
+ type: Boolean,
27
+ default: false,
28
+ },
29
+ noCheck: {
30
+ type: Boolean,
31
+ default: false,
32
+ },
33
+ itemIcon: {
34
+ type: String,
35
+ default: '',
36
+ },
37
+ itemIconTone: {
38
+ type: String,
39
+ default: 'plain',
40
+ },
41
+ itemIconFill: {
42
+ type: Boolean,
43
+ default: false,
44
+ },
45
+ disabledUnselectedItems: {
46
+ type: Boolean,
47
+ default: false,
48
+ },
49
+ radioList: {
50
+ type: Boolean,
51
+ default: false,
52
+ },
53
+ };
54
+
55
+ export const listItemEmitTypes = {
56
+ select: () => true,
57
+ };
58
+
59
+ export type ListItemPropTypes = ExtractPropTypes<typeof listItemPropTypes>;
60
+ export type ListItemEmitTypes = typeof listItemEmitTypes;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div :class="props.classes" @click="$emit('select')">
3
3
  <template v-if="showLozengeMode">
4
- <div class="spr-flex spr-items-center spr-gap-1">
4
+ <div class="spr-flex spr-items-center spr-gap-2">
5
5
  <spr-checkbox
6
6
  v-if="props.multiSelect"
7
7
  :disabled="listItem.disabled || (props.disabledUnselectedItems && !isSelected)"
@@ -19,22 +19,32 @@
19
19
  </div>
20
20
  </template>
21
21
  <template v-else>
22
- <div class="spr-flex spr-items-center spr-gap-1">
22
+ <div class="spr-flex spr-items-center spr-gap-2">
23
23
  <spr-checkbox
24
24
  v-if="props.multiSelect"
25
25
  :disabled="listItem.disabled || (props.disabledUnselectedItems && !isSelected)"
26
26
  :checked="isSelected"
27
27
  />
28
+ <spr-radio
29
+ v-if="props.radioList && !props.multiSelect"
30
+ :id="`radio-${props.item?.value}`"
31
+ class="spr-flex spr-items-center"
32
+ name="radio-group"
33
+ :value="props.item?.value"
34
+ :model-value="isSelected ? props.item?.value : undefined"
35
+ :disabled="props.item?.disabled || (props.disabledUnselectedItems && !isSelected)"
36
+ @update:model-value="$emit('select')"
37
+ />
28
38
  <div :class="[listItem.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
29
39
  <span
30
40
  v-if="hasIcon"
31
41
  :class="[
32
- listItem.iconColor,
33
- 'spr-mt-[2px]',
42
+ 'spr-flex spr-h-[30px] spr-w-[30px] spr-items-center spr-justify-center',
43
+ iconClasses,
34
44
  { 'spr-text-color-disabled': listItem.disabled || (props.disabledUnselectedItems && !isSelected) },
35
45
  ]"
36
46
  >
37
- <Icon :icon="iconName" width="20px" height="20px" />
47
+ <Icon class="spr-text-xl" :icon="iconName" />
38
48
  </span>
39
49
  <div
40
50
  :class="[
@@ -59,11 +69,11 @@
59
69
  </div>
60
70
 
61
71
  <!-- Right Side Actions -->
62
- <div class="spr-flex spr-items-center spr-gap-1">
72
+ <div class="spr-flex spr-items-center spr-gap-2">
63
73
  <template v-if="props.ladderized">
64
74
  <Icon v-if="hasSublevels" class="spr-text-color-weak spr-size-4" icon="ph:caret-right" />
65
75
  <Icon
66
- v-else-if="isSelected && !props.noCheck"
76
+ v-else-if="isSelected && !props.noCheck && !props.multiSelect && !props.radioList"
67
77
  class="spr-text-color-brand-base spr-w-[1.39em]"
68
78
  icon="ph:check"
69
79
  />
@@ -80,7 +90,7 @@
80
90
  :postfix-icon="listItem.lozenge?.postfixIcon as string"
81
91
  />
82
92
  <Icon
83
- v-if="isSelected && !props.noCheck && !props.multiSelect"
93
+ v-if="isSelected && !props.noCheck && !props.multiSelect && !props.radioList"
84
94
  class="spr-text-color-brand-base spr-w-[1.39em]"
85
95
  icon="ph:check"
86
96
  />
@@ -94,6 +104,7 @@
94
104
  import { Icon } from '@iconify/vue';
95
105
 
96
106
  import SprCheckbox from '@/components/checkbox/checkbox.vue';
107
+ import SprRadio from '@/components/radio/radio.vue';
97
108
  import SprLozenge from '@/components/lozenge/lozenge.vue';
98
109
 
99
110
  import { LOZENGE_TONE } from '@/components/lozenge/lozenge';
@@ -104,5 +115,5 @@ import { useListItem } from './use-list-item';
104
115
  const props = defineProps(listItemPropTypes);
105
116
  const emit = defineEmits(listItemEmitTypes);
106
117
 
107
- const { listItem, hasIcon, iconName, hasSublevels, showLozengeMode } = useListItem(props, emit);
118
+ const { listItem, hasIcon, iconName, iconClasses, hasSublevels, showLozengeMode } = useListItem(props, emit);
108
119
  </script>
@@ -11,10 +11,11 @@ export function useListItem(
11
11
  listItem: ComputedRef<ListItemPropTypes['item']>;
12
12
  hasIcon: ComputedRef<boolean>;
13
13
  iconName: ComputedRef<string>;
14
+ iconClasses: ComputedRef<string>;
14
15
  hasSublevels: ComputedRef<boolean>;
15
16
  showLozengeMode: ComputedRef<boolean>;
16
17
  } {
17
- const { item, itemIcon, lozenge } = toRefs(props);
18
+ const { item, itemIcon, itemIconTone, itemIconFill, lozenge } = toRefs(props);
18
19
 
19
20
  const listItem = computed(() => item?.value);
20
21
 
@@ -22,6 +23,40 @@ export function useListItem(
22
23
 
23
24
  const iconName = computed(() => itemIcon.value || item?.value!.icon || '');
24
25
 
26
+ const iconClasses = computed(() => {
27
+ // If item has a specific iconColor, use that
28
+ if (item?.value?.iconColor) {
29
+ return item.value.iconColor;
30
+ }
31
+
32
+ const tone = itemIconTone?.value || 'plain';
33
+ const fill = itemIconFill?.value || false;
34
+
35
+ const toneClasses: Record<string, string> = {
36
+ plain: 'spr-text-color-base',
37
+ pending: fill
38
+ ? 'spr-bg-yellow-100 spr-text-yellow-700 spr-rounded-md spr-p-1.5'
39
+ : 'spr-text-yellow-600 spr-rounded-md spr-p-1.5',
40
+ information: fill
41
+ ? 'spr-bg-blue-100 spr-text-blue-700 spr-rounded-md spr-p-1.5'
42
+ : 'spr-text-blue-600 spr-rounded-md spr-p-1.5',
43
+ success: fill
44
+ ? 'spr-bg-green-100 spr-text-green-700 spr-rounded-md spr-p-1.5'
45
+ : 'spr-text-green-600 spr-rounded-md spr-p-1.5',
46
+ danger: fill
47
+ ? 'spr-bg-red-100 spr-text-red-700 spr-rounded-md spr-p-1.5'
48
+ : 'spr-text-red-600 spr-rounded-md spr-p-1.5',
49
+ neutral: fill
50
+ ? 'spr-bg-gray-100 spr-text-gray-700 spr-rounded-md spr-p-1.5'
51
+ : 'spr-text-gray-600 spr-rounded-md spr-p-1.5',
52
+ caution: fill
53
+ ? 'spr-bg-orange-100 spr-text-orange-700 spr-rounded-md spr-p-1.5'
54
+ : 'spr-text-orange-600 spr-rounded-md spr-p-1.5',
55
+ };
56
+
57
+ return toneClasses[tone] || toneClasses['plain'];
58
+ });
59
+
25
60
  const hasSublevels = computed(() => !!(item?.value!.sublevel && item?.value!.sublevel.length > 0));
26
61
 
27
62
  const showLozengeMode = computed(() => lozenge.value && !!item?.value!.lozengeProps);
@@ -30,6 +65,7 @@ export function useListItem(
30
65
  listItem,
31
66
  hasIcon,
32
67
  iconName,
68
+ iconClasses,
33
69
  hasSublevels,
34
70
  showLozengeMode,
35
71
  };
@@ -107,10 +107,22 @@ export const listPropTypes = {
107
107
  type: String,
108
108
  default: '',
109
109
  },
110
+ itemIconTone: {
111
+ type: String,
112
+ default: 'plain',
113
+ },
114
+ itemIconFill: {
115
+ type: Boolean,
116
+ default: false,
117
+ },
110
118
  disabledUnselectedItems: {
111
119
  type: Boolean,
112
120
  default: false,
113
121
  },
122
+ radioList: {
123
+ type: Boolean,
124
+ default: false,
125
+ },
114
126
  };
115
127
 
116
128
  export const listEmitTypes = {
@@ -41,7 +41,10 @@
41
41
  :ladderized="props.ladderized"
42
42
  :no-check="props.noCheck"
43
43
  :item-icon="props.itemIcon"
44
+ :item-icon-tone="props.itemIconTone"
45
+ :item-icon-fill="props.itemIconFill"
44
46
  :disabled-unselected-items="props.disabledUnselectedItems"
47
+ :radio-list="props.radioList"
45
48
  @select="handleSelectedItem(item)"
46
49
  />
47
50
  <div v-if="props.infiniteScrollLoader" class="spr-flex spr-items-center spr-justify-center spr-p-2">
@@ -66,7 +69,10 @@
66
69
  :ladderized="props.ladderized"
67
70
  :no-check="props.noCheck"
68
71
  :item-icon="props.itemIcon"
72
+ :item-icon-tone="props.itemIconTone"
73
+ :item-icon-fill="props.itemIconFill"
69
74
  :disabled-unselected-items="props.disabledUnselectedItems"
75
+ :radio-list="props.radioList"
70
76
  @select="handleSelectedItem(item)"
71
77
  />
72
78
  <div v-if="props.infiniteScrollLoader" class="spr-flex spr-items-center spr-justify-center spr-p-2">
@@ -1,6 +1,18 @@
1
1
  import { PropType, ExtractPropTypes } from 'vue';
2
2
 
3
3
  const PROGRESS_BAR_SIZES = ['xs', 'sm', 'lg'] as const;
4
+ const PERCENTAGE_PLACEMENTS = [
5
+ 'top',
6
+ 'top-start',
7
+ 'top-center',
8
+ 'top-end',
9
+ 'bottom',
10
+ 'bottom-start',
11
+ 'bottom-center',
12
+ 'bottom-end',
13
+ 'left',
14
+ 'right',
15
+ ] as const;
4
16
 
5
17
  export const progressBarPropTypes = {
6
18
  size: {
@@ -45,6 +57,15 @@ export const progressBarPropTypes = {
45
57
  return true;
46
58
  },
47
59
  },
60
+ labelPlacement: {
61
+ type: String as PropType<(typeof PERCENTAGE_PLACEMENTS)[number]>,
62
+ validator: (value: (typeof PERCENTAGE_PLACEMENTS)[number]) => PERCENTAGE_PLACEMENTS.includes(value),
63
+ default: 'bottom',
64
+ },
65
+ supportingLabel: {
66
+ type: String as PropType<string>,
67
+ default: '',
68
+ },
48
69
  };
49
70
 
50
71
  export type ProgressBarPropTypes = ExtractPropTypes<typeof progressBarPropTypes>;
@@ -1,22 +1,39 @@
1
1
  <template>
2
2
  <div
3
- class="spr-flex spr-w-full spr-flex-col spr-gap-size-spacing-5xs"
3
+ :class="containerClasses"
4
4
  :aria-valuemin="0"
5
5
  :aria-valuemax="props.max || 100"
6
6
  :aria-valuenow="props.value"
7
7
  role="progressbar"
8
8
  >
9
- <div :class="[handleProgressBarSize, 'spr-overflow-hidden spr-rounded-lg spr-bg-white-100']">
9
+ <div
10
+ :class="[
11
+ handleProgressBarSize,
12
+ 'spr-overflow-hidden spr-rounded-lg spr-bg-white-100',
13
+ {
14
+ 'spr-w-full': [
15
+ 'top',
16
+ 'top-start',
17
+ 'top-center',
18
+ 'top-end',
19
+ 'bottom',
20
+ 'bottom-start',
21
+ 'bottom-center',
22
+ 'bottom-end',
23
+ ].includes(props.labelPlacement),
24
+ 'spr-flex-1': ['left', 'right'].includes(props.labelPlacement),
25
+ },
26
+ ]"
27
+ >
10
28
  <div
11
29
  :class="[`spr-background-color-${validColor}-base`, 'spr-transition-all spr-duration-300']"
12
30
  :style="handleProgressBarStyle"
13
31
  ></div>
14
32
  </div>
15
- <span
16
- v-if="props.label"
17
- class="spr-text-color-base spr-font-weight-regular spr-font-size-100 spr-line-height-100 spr-font-main"
18
- >{{ percentage }}%</span
19
- >
33
+ <div v-if="props.label || props.supportingLabel" class="spr-flex spr-gap-1.5">
34
+ <span v-if="props.label" class="spr-text-color-strong spr-subheading-sm">{{ percentage }}%</span>
35
+ <span v-if="props.supportingLabel" class="spr-body-md-regular">{{ props.supportingLabel }}</span>
36
+ </div>
20
37
  </div>
21
38
  </template>
22
39
 
@@ -26,5 +43,6 @@ import { useProgressBar } from './use-progress-bar';
26
43
 
27
44
  const props = defineProps(progressBarPropTypes);
28
45
 
29
- const { handleProgressBarSize, validColor, percentage, handleProgressBarStyle } = useProgressBar(props);
46
+ const { handleProgressBarSize, validColor, percentage, handleProgressBarStyle, containerClasses } =
47
+ useProgressBar(props);
30
48
  </script>
@@ -1,4 +1,5 @@
1
1
  import { computed } from 'vue';
2
+
2
3
  import type { ProgressBarPropTypes } from './progress-bar';
3
4
 
4
5
  export const useProgressBar = (props: ProgressBarPropTypes) => {
@@ -14,12 +15,12 @@ export const useProgressBar = (props: ProgressBarPropTypes) => {
14
15
  return 'spr-h-3';
15
16
  }
16
17
  });
17
-
18
+
18
19
  const validColor = computed<string>(() => {
19
20
  const validColors = ['success', 'danger', 'warning', 'info', 'neutral'];
20
21
  return validColors.includes(props.color) ? props.color : 'success';
21
22
  });
22
-
23
+
23
24
  const percentage = computed<number>(() => Math.min((props.value / (props.max || 100)) * 100, 100));
24
25
 
25
26
  const handleProgressBarStyle = computed<{ width: string; height: string }>(() => {
@@ -29,10 +30,38 @@ export const useProgressBar = (props: ProgressBarPropTypes) => {
29
30
  };
30
31
  });
31
32
 
33
+ const containerClasses = computed<string>(() => {
34
+ switch (props.labelPlacement) {
35
+ case 'top':
36
+ return 'spr-flex spr-flex-col-reverse spr-gap-size-spacing-5xs';
37
+ case 'top-start':
38
+ return 'spr-flex spr-flex-col-reverse spr-gap-size-spacing-5xs';
39
+ case 'top-center':
40
+ return 'spr-flex spr-flex-col-reverse spr-gap-size-spacing-5xs spr-items-center';
41
+ case 'top-end':
42
+ return 'spr-flex spr-flex-col-reverse spr-gap-size-spacing-5xs spr-items-end';
43
+ case 'bottom':
44
+ return 'spr-flex spr-flex-col spr-gap-size-spacing-5xs';
45
+ case 'bottom-start':
46
+ return 'spr-flex spr-flex-col spr-gap-size-spacing-5xs';
47
+ case 'bottom-center':
48
+ return 'spr-flex spr-flex-col spr-gap-size-spacing-5xs spr-items-center';
49
+ case 'bottom-end':
50
+ return 'spr-flex spr-flex-col spr-gap-size-spacing-5xs spr-items-end';
51
+ case 'left':
52
+ return 'spr-flex spr-flex-row spr-gap-size-spacing-5xs spr-items-center';
53
+ case 'right':
54
+ return 'spr-flex spr-flex-row-reverse spr-gap-size-spacing-5xs spr-items-center';
55
+ default:
56
+ return 'spr-flex spr-flex-col spr-gap-size-spacing-5xs';
57
+ }
58
+ });
59
+
32
60
  return {
33
61
  handleProgressBarSize,
34
62
  validColor,
35
63
  percentage,
36
64
  handleProgressBarStyle,
65
+ containerClasses,
37
66
  };
38
67
  };
@@ -100,6 +100,10 @@ export const sidepanelPropTypes = {
100
100
  isExpanded: {
101
101
  type: Boolean,
102
102
  default: false
103
+ },
104
+ isActivePanel: {
105
+ type: Boolean,
106
+ default: false
103
107
  }
104
108
  };
105
109
 
@@ -29,13 +29,22 @@ const stackingSidepanelBase = useTemplateRef('stacking-sidepanel-base');
29
29
  const props = defineProps(stackingSidePanelProps);
30
30
  const emits = defineEmits(stackingSidePanelEmits);
31
31
 
32
- const { showPanel, hidePanel, stackingSidepanelClasses, stackingSidepanelBaseTransform, activePanels, handleExpandPanel, expandedPanel } =
33
- useStackingSidepanel(props, emits, stackingSidepanelBase);
32
+ const {
33
+ showPanel,
34
+ hidePanel,
35
+ stackingSidepanelClasses,
36
+ stackingSidepanelBaseTransform,
37
+ activePanels,
38
+ handleExpandPanel,
39
+ expandedPanel,
40
+ activePanel
41
+ } = useStackingSidepanel(props, emits, stackingSidepanelBase);
34
42
 
35
43
  defineExpose({
36
44
  showPanel,
37
45
  hidePanel,
38
46
  handleExpandPanel,
39
- expandedPanel
47
+ expandedPanel,
48
+ activePanel
40
49
  });
41
50
  </script>
@@ -11,6 +11,7 @@ export const useStackingSidepanel = (
11
11
  const activePanels = useVModel(props, 'stack', emits, { deep: true });
12
12
  const expandedPanel = ref('');
13
13
  const isTransitioning = ref(false);
14
+ const activePanel = computed(() => activePanels.value[activePanels.value.length -1] || null);
14
15
 
15
16
  // Ensure activePanels is an array
16
17
  watchDeep(activePanels, (newValue) => {
@@ -143,5 +144,6 @@ export const useStackingSidepanel = (
143
144
  activePanels,
144
145
  expandedPanel,
145
146
  handleExpandPanel,
147
+ activePanel
146
148
  };
147
149
  };
@@ -19,7 +19,7 @@ interface SidepanelClasses {
19
19
  }
20
20
 
21
21
  export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<SidepanelEmitTypes>['emit']) => {
22
- const { size, position, isStacking, footerNoPadding, isExpanded } = toRefs(props);
22
+ const { size, position, isStacking, footerNoPadding, isExpanded, isActivePanel } = toRefs(props);
23
23
 
24
24
  const sidepanelClasses: ComputedRef<SidepanelClasses> = computed(() => {
25
25
  const sidepanelBaseClasses = classNames(
@@ -33,6 +33,7 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
33
33
  '[@media(max-width:420px)]:spr-w-[calc(100vw-35px)]': size.value === 'md' && !isExpanded.value && !isStacking.value,
34
34
  '[@media(max-width:480px)]:spr-w-[calc(100vw-35px)]': size.value === 'lg' && !isExpanded.value && !isStacking.value,
35
35
  'spr-w-[calc(100vw-50px)]': isExpanded.value,
36
+ 'spr-pointer-events-none': !isActivePanel.value && isStacking.value
36
37
  },
37
38
  );
38
39