design-system-next 2.21.10 → 2.22.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,48 +1,48 @@
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
+ 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;
@@ -5,9 +5,9 @@ const SIDEPANEL_POSITION = ['right'] as const;
5
5
 
6
6
  export const sidepanelPropTypes = {
7
7
  /**
8
- * @description Controls whether the side panel is open.
8
+ * @description Controls whether the side panel is open.
9
9
  * Set to `true` to display the side panel or `false` to hide it.
10
- */
10
+ */
11
11
  isOpen: {
12
12
  type: Boolean,
13
13
  default: false,
@@ -15,16 +15,16 @@ export const sidepanelPropTypes = {
15
15
  /**
16
16
  * @description The title displayed in the side panel's header.
17
17
  * If not provided, defaults to 'Sidepanel Header'.
18
- */
18
+ */
19
19
  headerTitle: {
20
20
  type: String,
21
- default: 'Sidepanel Header'
21
+ default: 'Sidepanel Header',
22
22
  },
23
23
  /**
24
24
  * @description Specifies the size of the side panel.
25
25
  * Acceptable values are: `'sm'`, `'md'`, `'lg'`, `'xl'`.
26
26
  * Defaults to `'sm'`.
27
- */
27
+ */
28
28
  size: {
29
29
  type: String as PropType<(typeof SIDEPANEL_SIZE)[number]>,
30
30
  validator: (value: (typeof SIDEPANEL_SIZE)[number]) => SIDEPANEL_SIZE.includes(value),
@@ -34,7 +34,7 @@ export const sidepanelPropTypes = {
34
34
  * @description Sets the height of the side panel.
35
35
  * Accepts a string (e.g., `'500px'`, `'70vh'`) or a number (interpreted as pixels).
36
36
  * Defaults to `'calc(100vh - 32px)'`.
37
- */
37
+ */
38
38
  height: {
39
39
  type: [String, Number],
40
40
  default: 'calc(100vh - 32px)',
@@ -43,16 +43,16 @@ export const sidepanelPropTypes = {
43
43
  * @description Controls the visibility of the side panel header.
44
44
  * Set to `true` to hide the header, or `false` to display it.
45
45
  * Defaults to `false`.
46
- */
46
+ */
47
47
  hideHeader: {
48
48
  type: Boolean,
49
- default: false
49
+ default: false,
50
50
  },
51
51
  /**
52
52
  * @description Specifies the position of the side panel.
53
53
  * Currently, only `'right'` is supported.
54
54
  * Defaults to `'right'`.
55
- */
55
+ */
56
56
  position: {
57
57
  type: String as PropType<(typeof SIDEPANEL_POSITION)[number]>,
58
58
  validator: (value: (typeof SIDEPANEL_POSITION)[number]) => SIDEPANEL_POSITION.includes(value),
@@ -62,16 +62,16 @@ export const sidepanelPropTypes = {
62
62
  * @description Determines whether a backdrop is displayed behind the side panel.
63
63
  * Set to `true` to enable the backdrop, or `false` to disable it.
64
64
  * Defaults to `true`.
65
- */
65
+ */
66
66
  hasBackdrop: {
67
67
  type: Boolean,
68
- default: true
68
+ default: true,
69
69
  },
70
70
  /**
71
71
  * @description Controls whether clicking outside the side panel should close it.
72
72
  * Set to `true` to enable closing on outside click, or `false` to disable it.
73
73
  * Defaults to `false`.
74
- */
74
+ */
75
75
  closeOutside: {
76
76
  type: Boolean,
77
77
  default: true,
@@ -80,7 +80,7 @@ export const sidepanelPropTypes = {
80
80
  * @description Controls whether clicking ESC button should close it.
81
81
  * Set to `true` to enable closing on ESC button click, or `false` to disable it.
82
82
  * Defaults to `true`.
83
- */
83
+ */
84
84
  escapeClose: {
85
85
  type: Boolean,
86
86
  default: true,
@@ -92,12 +92,22 @@ export const sidepanelPropTypes = {
92
92
  footerNoPadding: {
93
93
  type: Boolean,
94
94
  default: false,
95
+ },
96
+ isExpandable: {
97
+ type: Boolean,
98
+ default: false
99
+ },
100
+ isExpanded: {
101
+ type: Boolean,
102
+ default: false
95
103
  }
96
- }
104
+ };
97
105
 
98
106
  export const sidepanelEmitTypes = {
99
107
  close: Function,
100
108
  onClose: Function,
109
+ shrink: Function,
110
+ expand: Function,
101
111
  };
102
112
 
103
113
  export type SidepanelPropTypes = ExtractPropTypes<typeof sidepanelPropTypes>;
@@ -24,7 +24,15 @@
24
24
  <div id="sidepanel-title" :class="sidepanelClasses.sidepanelHeaderTitleClasses">
25
25
  {{ headerTitle }}
26
26
  </div>
27
- <Icon :class="sidepanelClasses.sidepanelHeaderIconClasses" icon="ph:x" @click="handleClose" />
27
+ <div class="spr-flex spr-items-center spr-gap-size-spacing-3xs">
28
+ <Icon
29
+ v-if="props.isExpandable"
30
+ :class="sidepanelClasses.sidepanelHeaderIconClasses"
31
+ :icon="isExpanded ? 'ph:arrows-in-simple' : 'ph:arrows-out-simple'"
32
+ @click="handlePanelExpansion"
33
+ />
34
+ <Icon :class="sidepanelClasses.sidepanelHeaderIconClasses" icon="ph:x" @click="handleClose" />
35
+ </div>
28
36
  </div>
29
37
  <div v-else>
30
38
  <slot name="header"></slot>
@@ -51,5 +59,5 @@ import { sidepanelPropTypes, sidepanelEmitTypes } from './sidepanel';
51
59
  const props = defineProps(sidepanelPropTypes);
52
60
  const emit = defineEmits(sidepanelEmitTypes);
53
61
 
54
- const { sidepanelClasses, sidepanelRef, handleClose } = useSidepanel(props, emit);
62
+ const { sidepanelClasses, sidepanelRef, handleClose, isExpanded, handlePanelExpansion } = useSidepanel(props, emit);
55
63
  </script>
@@ -29,11 +29,13 @@ 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 } =
32
+ const { showPanel, hidePanel, stackingSidepanelClasses, stackingSidepanelBaseTransform, activePanels, handleExpandPanel, expandedPanel } =
33
33
  useStackingSidepanel(props, emits, stackingSidepanelBase);
34
34
 
35
35
  defineExpose({
36
36
  showPanel,
37
37
  hidePanel,
38
+ handleExpandPanel,
39
+ expandedPanel
38
40
  });
39
41
  </script>
@@ -9,6 +9,8 @@ export const useStackingSidepanel = (
9
9
  stackingSidepanelBase: Readonly<ShallowRef<HTMLDivElement | null>>,
10
10
  ) => {
11
11
  const activePanels = useVModel(props, 'stack', emits, { deep: true });
12
+ const expandedPanel = ref('');
13
+ const isTransitioning = ref(false);
12
14
 
13
15
  // Ensure activePanels is an array
14
16
  watchDeep(activePanels, (newValue) => {
@@ -17,6 +19,7 @@ export const useStackingSidepanel = (
17
19
  });
18
20
 
19
21
  const showPanel = (name: string) => {
22
+ expandedPanel.value = '';
20
23
  if (!activePanels.value.includes(name)) {
21
24
  activePanels.value.push(name);
22
25
  // Update transform immediately
@@ -25,17 +28,20 @@ export const useStackingSidepanel = (
25
28
  };
26
29
 
27
30
  const hidePanel = (name: string) => {
31
+ expandedPanel.value = '';
28
32
  const index = activePanels.value.indexOf(name);
29
33
  if (index !== -1) {
30
- activePanels.value.splice(index, 1);
31
- // Undo resize tracker two times of history
32
- undoResizeTracker();
34
+ activePanels.value.splice(index, 1);
33
35
  undoResizeTracker();
34
36
  // Update transform immediately
35
37
  updateTransform();
36
38
  }
37
39
  };
38
40
 
41
+ const handleExpandPanel = (action: 'expand' | 'shrink', name: string) => {
42
+ expandedPanel.value = action === 'expand' ? name : '';
43
+ };
44
+
39
45
  // Styling for the stacking sidepanel
40
46
  const stackingSidepanelBaseTransform = ref('transform: translateX()');
41
47
  // Track the width of the stacking sidepanel base
@@ -43,10 +49,13 @@ export const useStackingSidepanel = (
43
49
  const { history, undo: undoResizeTracker } = useRefHistory(resizeTracker);
44
50
 
45
51
  // Watch for changes in the active panels to update the transform
46
- useResizeObserver(stackingSidepanelBase, (entries) => {
52
+ useResizeObserver(stackingSidepanelBase, (entries) => {
53
+ //stop observer if panel is expanding to stop population of history of resizeTracker
54
+ if (!!isTransitioning.value || !!expandedPanel.value) return;
55
+
47
56
  const entry = entries[0];
48
57
  const { width } = entry.contentRect;
49
- resizeTracker.value = resizeTracker.value !== width ? width : resizeTracker.value;
58
+ resizeTracker.value = resizeTracker.value !== width ? width : resizeTracker.value;
50
59
  updateTransform();
51
60
  });
52
61
 
@@ -58,7 +67,14 @@ export const useStackingSidepanel = (
58
67
  return;
59
68
  }
60
69
 
61
- stackingSidepanelBaseTransform.value = `transform: translateX(${history.value[0].snapshot}px);`;
70
+ let snapshot = history.value[0].snapshot;
71
+
72
+ const numOfActivePanels = activePanels.value.length - 1;
73
+ const numOfSnapshotHistory = history.value.length - 1;
74
+ snapshot = history.value[numOfSnapshotHistory - numOfActivePanels]?.snapshot;
75
+ if(!snapshot) return
76
+
77
+ stackingSidepanelBaseTransform.value = `transform: translateX(${snapshot}px);`;
62
78
  };
63
79
 
64
80
  // Watch for changes in active panels and update transform immediately
@@ -70,6 +86,17 @@ export const useStackingSidepanel = (
70
86
  { flush: 'post' },
71
87
  );
72
88
 
89
+ watch(
90
+ expandedPanel,
91
+ () => {
92
+ // transition delay of expanding panel
93
+ isTransitioning.value = true;
94
+ setTimeout(() => {
95
+ isTransitioning.value = false;
96
+ }, 150);
97
+ },
98
+ );
99
+
73
100
  const stackingSidepanelClasses = computed(() => {
74
101
  const sidepanelStackBackdropClasses =
75
102
  'spr-fixed spr-left-0 spr-top-0 spr-z-[1010] spr-h-full spr-w-full spr-bg-mushroom-700/60';
@@ -114,5 +141,7 @@ export const useStackingSidepanel = (
114
141
  stackingSidepanelClasses,
115
142
  stackingSidepanelBaseTransform,
116
143
  activePanels,
144
+ expandedPanel,
145
+ handleExpandPanel,
117
146
  };
118
147
  };
@@ -19,16 +19,20 @@ interface SidepanelClasses {
19
19
  }
20
20
 
21
21
  export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<SidepanelEmitTypes>['emit']) => {
22
- const { size, position, isStacking, footerNoPadding } = toRefs(props);
22
+ const { size, position, isStacking, footerNoPadding, isExpanded } = toRefs(props);
23
23
 
24
24
  const sidepanelClasses: ComputedRef<SidepanelClasses> = computed(() => {
25
25
  const sidepanelBaseClasses = classNames(
26
- 'spr-right-4 spr-top-1/2 spr-z-[1015] spr-flex spr-h-full spr-min-h-[200px] spr-translate-y-[-50%] spr-flex-col spr-rounded-border-radius-xl spr-bg-white-50 spr-drop-shadow',
26
+ 'spr-right-4 spr-top-1/2 spr-z-[1015] spr-flex spr-h-full spr-min-h-[200px] spr-translate-y-[-50%] spr-flex-col spr-rounded-border-radius-xl spr-bg-white-50 spr-drop-shadow spr-transition-all spr-ease-[ease-in-out] spr-duration-[150ms]',
27
27
  {
28
28
  'spr-fixed': !isStacking.value,
29
- 'spr-w-[360px] sm:spr-w-[calc(100%-35px)]': size.value === 'sm',
30
- 'spr-w-[420px] sm:spr-w-[calc(100%-35px)]': size.value === 'md',
31
- 'spr-w-[480px] sm:spr-w-[calc(100%-35px)]': size.value === 'lg',
29
+ 'spr-w-[360px]': size.value === 'sm' && !isExpanded.value,
30
+ 'spr-w-[420px]': size.value === 'md' && !isExpanded.value,
31
+ 'spr-w-[480px]': size.value === 'lg' && !isExpanded.value,
32
+ '[@media(max-width:360px)]:spr-w-[calc(100vw-35px)]': size.value === 'sm' && !isExpanded.value && !isStacking.value,
33
+ '[@media(max-width:420px)]:spr-w-[calc(100vw-35px)]': size.value === 'md' && !isExpanded.value && !isStacking.value,
34
+ '[@media(max-width:480px)]:spr-w-[calc(100vw-35px)]': size.value === 'lg' && !isExpanded.value && !isStacking.value,
35
+ 'spr-w-[calc(100vw-50px)]': isExpanded.value,
32
36
  },
33
37
  );
34
38
 
@@ -45,13 +49,13 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
45
49
  const sidepanelFooterClasses = classNames(
46
50
  'spr-bottom-0 spr-left-0 spr-w-full spr-rounded-b-border-radius-xl spr-border-0 spr-border-t spr-border-solid spr-border-mushroom-200 spr-bg-white-50 ',
47
51
  {
48
- 'spr-py-3': !footerNoPadding.value
49
- }
52
+ 'spr-py-3': !footerNoPadding.value,
53
+ },
50
54
  );
51
55
 
52
- const sidepanelTransitionActiveClasses = classNames(
53
- { 'spr-transition-all spr-duration-[150ms] spr-ease-[ease-in-out]': !isStacking.value },
54
- );
56
+ const sidepanelTransitionActiveClasses = classNames({
57
+ 'spr-transition-all spr-duration-[150ms] spr-ease-[ease-in-out]': !isStacking.value,
58
+ });
55
59
 
56
60
  const sidepanelTransitionHiddenClasses = classNames('spr-opacity-0', {
57
61
  'spr-translate-x-full -spr-translate-y-2/4': !isStacking.value && position.value === 'right',
@@ -84,6 +88,14 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
84
88
  emit('close');
85
89
  };
86
90
 
91
+ const handlePanelExpansion = () => {
92
+ if (isExpanded.value) {
93
+ emit('shrink');
94
+ } else {
95
+ emit('expand');
96
+ }
97
+ };
98
+
87
99
  let ignoreClick = false;
88
100
 
89
101
  const handleClickOutside = (event: MouseEvent) => {
@@ -111,7 +123,7 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
111
123
  emit('onClose');
112
124
  }
113
125
  },
114
- );
126
+ );
115
127
 
116
128
  onMounted(() => {
117
129
  document.addEventListener('click', handleClickOutside);
@@ -126,6 +138,8 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
126
138
  return {
127
139
  sidepanelClasses,
128
140
  sidepanelRef,
141
+ isExpanded,
129
142
  handleClose,
143
+ handlePanelExpansion,
130
144
  };
131
145
  };