design-system-next 2.13.6 → 2.14.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.
Files changed (38) hide show
  1. package/dist/design-system-next.es.js +11820 -10296
  2. package/dist/design-system-next.es.js.gz +0 -0
  3. package/dist/design-system-next.umd.js +17 -12
  4. package/dist/design-system-next.umd.js.gz +0 -0
  5. package/dist/main.css +1 -1
  6. package/dist/main.css.gz +0 -0
  7. package/dist/package.json.d.ts +3 -1
  8. package/package.json +3 -1
  9. package/src/App.vue +1 -83
  10. package/src/assets/styles/tailwind.css +13 -11
  11. package/src/components/date-picker/date-picker.ts +0 -1
  12. package/src/components/date-picker/date-picker.vue +2 -2
  13. package/src/components/date-picker/date-range-picker/date-range-picker.vue +59 -36
  14. package/src/components/dropdown/dropdown.ts +3 -2
  15. package/src/components/dropdown/dropdown.vue +2 -2
  16. package/src/components/dropdown/use-dropdown.ts +89 -70
  17. package/src/components/input/input.ts +0 -1
  18. package/src/components/input/input.vue +1 -1
  19. package/src/components/list/ladderized-list/ladderized-list-back.vue +2 -1
  20. package/src/components/list/ladderized-list/ladderized-list.vue +3 -18
  21. package/src/components/list/ladderized-list/use-ladderized-list.ts +33 -21
  22. package/src/components/list/list.ts +4 -1
  23. package/src/components/list/list.vue +155 -136
  24. package/src/components/select/select-ladderized/select-ladderized.ts +19 -1
  25. package/src/components/select/select-ladderized/select-ladderized.vue +42 -41
  26. package/src/components/select/select-ladderized/use-select-ladderized.ts +20 -33
  27. package/src/components/select/select-multiple/select-multiple.ts +19 -1
  28. package/src/components/select/select-multiple/select-multiple.vue +18 -15
  29. package/src/components/select/select-multiple/use-select-multiple.ts +5 -15
  30. package/src/components/select/select.ts +19 -1
  31. package/src/components/select/select.vue +50 -53
  32. package/src/components/select/use-select.ts +4 -13
  33. package/src/components/sidenav/use-sidenav.ts +10 -3
  34. package/src/components/table/table.ts +33 -3
  35. package/src/components/table/table.vue +46 -13
  36. package/src/components/table/use-draggable-table-rows.ts +57 -0
  37. package/src/components/table/use-table.ts +143 -7
  38. package/src/components/textarea/textarea.vue +7 -1
@@ -1,169 +1,188 @@
1
1
  <template>
2
2
  <div class="spr-font-main">
3
- <div v-if="props.searchableMenu" class="spr-mb-3 spr-grid spr-gap-3">
3
+ <div
4
+ v-if="props.searchableMenu"
5
+ :class="[
6
+ 'spr-sticky spr-z-20',
7
+ 'spr-grid spr-gap-3 spr-bg-white-50 spr-p-size-spacing-3xs',
8
+ 'spr-border-color-weak spr-border spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid',
9
+ ]"
10
+ :style="{
11
+ top:
12
+ typeof props.stickySearchOffset === 'number'
13
+ ? props.stickySearchOffset + 'px'
14
+ : String(props.stickySearchOffset),
15
+ }"
16
+ >
4
17
  <spr-input-search v-model="searchText" :placeholder="props.searchableMenuPlaceholder" autocomplete="off" />
5
-
6
- <div v-if="isParentMenu" class="spr-background-color-surface spr-h-[1px]"></div>
7
18
  </div>
8
19
 
9
- <template v-if="props.groupItemsBy">
10
- <template v-if="groupedMenuList && groupedMenuList.length > 0">
11
- <div class="spr-grid spr-gap-2">
12
- <div v-for="(list, listIndex) in groupedMenuList" :key="listIndex" class="spr-grid spr-gap-0.5">
13
- <div
14
- v-if="list.groupLabel !== 'no-group'"
15
- class="spr-label-xs-medium spr-text-color-base spr-px-size-spacing-4xs spr-py-size-spacing-3xs spr-uppercase"
16
- >
17
- <span>
18
- {{ list.groupLabel }}
19
- </span>
20
- </div>
21
- <div
22
- v-for="(item, itemIndex) in list.items"
23
- :key="itemIndex"
24
- :class="getListItemClasses(item)"
25
- @click="handleSelectedItem(item)"
26
- >
27
- <spr-checkbox v-if="props.multiSelect" :checked="isItemSelected(item)" />
28
- <template v-if="props.lozenge">
29
- <spr-lozenge
30
- :label="item.text || item.lozengeProps?.label as string"
31
- :tone="item.lozengeProps?.tone as string & (typeof LOZENGE_TONE)[number]"
32
- :fill="item.lozengeProps?.fill as boolean"
33
- :url="item.lozengeProps?.url as string"
34
- :icon="item.icon || item.lozengeProps?.icon as string"
35
- :postfix-icon="item.lozengeProps?.postfixIcon as string"
36
- />
37
- </template>
38
- <template v-else>
39
- <div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
40
- <span v-if="item.icon" :class="[item.iconColor, 'spr-mt-[2px]']">
41
- <icon :icon="item.icon" width="20px" height="20px" />
42
- </span>
43
- <div class="spr-flex spr-flex-auto spr-flex-col spr-justify-start">
44
- <span class="spr-text-left spr-text-xs">
45
- {{ item.text }}
46
- </span>
47
- <span v-if="item.subtext" class="spr-body-xs-regular spr-text-color-base spr-text-left">
48
- {{ item.subtext }}
49
- </span>
50
- </div>
51
- </div>
52
- <template v-if="!props.multiSelect && !props.dropdown">
53
- <Icon
54
- v-if="isItemSelected(item) && !props.noCheck"
55
- class="spr-text-color-brand-base spr-w-[1.39em]"
56
- icon="ph:check"
20
+ <div class="spr-p-size-spacing-3xs">
21
+ <template v-if="props.groupItemsBy">
22
+ <template v-if="groupedMenuList && groupedMenuList.length > 0">
23
+ <div class="spr-grid spr-gap-2">
24
+ <div v-for="(list, listIndex) in groupedMenuList" :key="listIndex" class="spr-grid spr-gap-0.5">
25
+ <div
26
+ v-if="list.groupLabel !== 'no-group'"
27
+ class="spr-label-xs-medium spr-text-color-base spr-px-size-spacing-4xs spr-py-size-spacing-3xs spr-uppercase"
28
+ >
29
+ <span>
30
+ {{ list.groupLabel }}
31
+ </span>
32
+ </div>
33
+ <div
34
+ v-for="(item, itemIndex) in list.items"
35
+ :key="itemIndex"
36
+ :class="getListItemClasses(item)"
37
+ @click="handleSelectedItem(item)"
38
+ >
39
+ <spr-checkbox v-if="props.multiSelect" :checked="isItemSelected(item)" />
40
+ <template v-if="props.lozenge">
41
+ <spr-lozenge
42
+ :label="item.text || (item.lozengeProps?.label as string)"
43
+ :tone="item.lozengeProps?.tone as string & (typeof LOZENGE_TONE)[number]"
44
+ :fill="item.lozengeProps?.fill as boolean"
45
+ :url="item.lozengeProps?.url as string"
46
+ :icon="item.icon || (item.lozengeProps?.icon as string)"
47
+ :postfix-icon="item.lozengeProps?.postfixIcon as string"
57
48
  />
58
49
  </template>
59
- <template v-if="props.ladderized">
60
- <Icon
61
- v-if="item.sublevel && item.sublevel?.length > 0"
62
- class="spr-text-color-weak spr-size-4"
63
- icon="ph:caret-right"
64
- />
50
+ <template v-else>
51
+ <div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
52
+ <span v-if="item.icon" :class="[item.iconColor, 'spr-mt-[2px]']">
53
+ <icon :icon="item.icon" width="20px" height="20px" />
54
+ </span>
55
+ <div class="spr-flex spr-flex-auto spr-flex-col spr-justify-start">
56
+ <span class="spr-text-left spr-text-xs">
57
+ {{ item.text }}
58
+ </span>
59
+ <span v-if="item.subtext" class="spr-body-xs-regular spr-text-color-base spr-text-left">
60
+ {{ item.subtext }}
61
+ </span>
62
+ </div>
63
+ </div>
64
+ <template v-if="props.ladderized">
65
+ <template v-if="item.sublevel && item.sublevel?.length > 0">
66
+ <Icon class="spr-text-color-weak spr-size-4" icon="ph:caret-right" />
67
+ </template>
68
+ <template v-else>
69
+ <Icon
70
+ v-if="isItemSelected(item) && !props.noCheck"
71
+ class="spr-text-color-brand-base spr-w-[1.39em]"
72
+ icon="ph:check"
73
+ />
74
+ </template>
75
+ </template>
76
+ <template v-else>
77
+ <Icon
78
+ v-if="isItemSelected(item) && !props.noCheck && !props.multiSelect"
79
+ class="spr-text-color-brand-base spr-w-[1.39em]"
80
+ icon="ph:check"
81
+ />
82
+ </template>
65
83
  </template>
66
- </template>
84
+ </div>
67
85
  </div>
68
86
  </div>
69
- </div>
70
- </template>
71
- <template v-else>
72
- <div v-if="props.loading" class="spr-skeletal-loader spr-h-8 spr-w-full spr-rounded-md" />
73
- <div v-else class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
74
- <span class="spr-body-sm-regular spr-m-0">No results found</span>
75
- </div>
87
+ </template>
88
+ <template v-else>
89
+ <div v-if="props.loading" class="spr-skeletal-loader spr-h-8 spr-w-full spr-rounded-md" />
90
+ <div v-else class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
91
+ <span class="spr-body-sm-regular spr-m-0">No results found</span>
92
+ </div>
93
+ </template>
76
94
  </template>
77
- </template>
78
95
 
79
- <template v-else>
80
- <template v-if="localizedMenuList && localizedMenuList.length > 0">
81
- <div
82
- v-for="(item, index) in localizedMenuList"
83
- :key="index"
84
- :class="getListItemClasses(item)"
85
- @click="handleSelectedItem(item)"
86
- >
87
- <spr-checkbox v-if="props.multiSelect" :disabled="item.disabled" :checked="isItemSelected(item)" />
88
- <template v-if="props.lozenge">
89
- <spr-lozenge
90
- :label="item.text || item.lozengeProps?.label as string"
91
- :tone="item.lozengeProps?.tone as string & (typeof LOZENGE_TONE)[number]"
92
- :fill="item.lozengeProps?.fill as boolean"
93
- :url="item.lozengeProps?.url as string"
94
- :icon="item.lozengeProps?.icon as string"
95
- :postfix-icon="item.lozengeProps?.postfixIcon as string"
96
- />
97
- </template>
98
- <template v-else>
99
- <div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
100
- <span v-if="item.icon" :class="[item.iconColor, 'spr-mt-[2px]']"
101
- ><icon :icon="item.icon" width="20px" height="20px"
102
- /></span>
103
- <div
104
- :class="[
105
- 'spr-flex spr-flex-auto spr-flex-col spr-justify-start',
106
- { 'spr-text-color-disabled': item.disabled },
107
- ]"
108
- >
109
- <span class="spr-text-left spr-text-xs">{{ item.text }}</span>
110
- <span
111
- v-if="item.subtext"
96
+ <template v-else>
97
+ <template v-if="localizedMenuList && localizedMenuList.length > 0">
98
+ <div
99
+ v-for="(item, index) in localizedMenuList"
100
+ :key="index"
101
+ :class="getListItemClasses(item)"
102
+ @click="handleSelectedItem(item)"
103
+ >
104
+ <spr-checkbox v-if="props.multiSelect" :disabled="item.disabled" :checked="isItemSelected(item)" />
105
+ <template v-if="props.lozenge">
106
+ <spr-lozenge
107
+ :label="item.text || (item.lozengeProps?.label as string)"
108
+ :tone="item.lozengeProps?.tone as string & (typeof LOZENGE_TONE)[number]"
109
+ :fill="item.lozengeProps?.fill as boolean"
110
+ :url="item.lozengeProps?.url as string"
111
+ :icon="item.lozengeProps?.icon as string"
112
+ :postfix-icon="item.lozengeProps?.postfixIcon as string"
113
+ />
114
+ </template>
115
+ <template v-else>
116
+ <div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
117
+ <span v-if="item.icon" :class="[item.iconColor, 'spr-mt-[2px]']"
118
+ ><icon :icon="item.icon" width="20px" height="20px"
119
+ /></span>
120
+ <div
112
121
  :class="[
113
- 'spr-body-xs-regular spr-text-color-base spr-text-left',
122
+ 'spr-flex spr-flex-auto spr-flex-col spr-justify-start',
114
123
  { 'spr-text-color-disabled': item.disabled },
115
124
  ]"
116
125
  >
117
- {{ item.subtext }}
118
- </span>
126
+ <span class="spr-text-left spr-text-xs">{{ item.text }}</span>
127
+ <span
128
+ v-if="item.subtext"
129
+ :class="[
130
+ 'spr-body-xs-regular spr-text-color-base spr-text-left',
131
+ { 'spr-text-color-disabled': item.disabled },
132
+ ]"
133
+ >
134
+ {{ item.subtext }}
135
+ </span>
136
+ </div>
119
137
  </div>
120
- </div>
121
- <template v-if="!props.multiSelect && !props.dropdown">
122
- <Icon
123
- v-if="isItemSelected(item) && !props.noCheck"
124
- class="spr-text-color-brand-base spr-w-[1.39em]"
125
- icon="ph:check"
126
- />
127
- </template>
128
- <template v-if="props.ladderized">
129
- <Icon
130
- v-if="item.sublevel && item.sublevel?.length > 0"
131
- class="spr-text-color-weak spr-size-4"
132
- icon="ph:caret-right"
133
- />
138
+ <template v-if="props.ladderized">
139
+ <template v-if="item.sublevel && item.sublevel?.length > 0">
140
+ <Icon class="spr-text-color-weak spr-size-4" icon="ph:caret-right" />
141
+ </template>
142
+ <template v-else>
143
+ <Icon
144
+ v-if="isItemSelected(item) && !props.noCheck"
145
+ class="spr-text-color-brand-base spr-w-[1.39em]"
146
+ icon="ph:check"
147
+ />
148
+ </template>
149
+ </template>
150
+ <template v-else>
151
+ <Icon
152
+ v-if="isItemSelected(item) && !props.noCheck && !props.multiSelect"
153
+ class="spr-text-color-brand-base spr-w-[1.39em]"
154
+ icon="ph:check"
155
+ />
156
+ </template>
134
157
  </template>
135
- </template>
136
- </div>
137
- </template>
138
- <template v-else>
139
- <div v-if="props.loading" class="spr-skeletal-loader spr-h-8 spr-w-full spr-rounded-md" />
140
- <div v-else class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
141
- <span class="spr-body-sm-regular spr-m-0">No results found</span>
142
- </div>
158
+ </div>
159
+ </template>
160
+ <template v-else>
161
+ <div v-if="props.loading" class="spr-skeletal-loader spr-h-8 spr-w-full spr-rounded-md" />
162
+ <div v-else class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
163
+ <span class="spr-body-sm-regular spr-m-0">No results found</span>
164
+ </div>
165
+ </template>
143
166
  </template>
144
- </template>
167
+ </div>
145
168
  </div>
146
169
  </template>
147
170
 
148
171
  <script lang="ts" setup>
149
172
  import { Icon } from '@iconify/vue';
150
- import { listPropTypes, listEmitTypes } from './list';
151
- import { useList } from './use-list';
173
+
152
174
  import SprCheckbox from '@/components/checkbox/checkbox.vue';
153
175
  import SprInputSearch from '@/components/input/input-search/input-search.vue';
154
176
  import SprLozenge from '@/components/lozenge/lozenge.vue';
177
+
155
178
  import { LOZENGE_TONE } from '@/components/lozenge/lozenge';
156
179
 
180
+ import { listPropTypes, listEmitTypes } from './list';
181
+ import { useList } from './use-list';
182
+
157
183
  const props = defineProps(listPropTypes);
158
184
  const emit = defineEmits(listEmitTypes);
159
185
 
160
- const {
161
- searchText,
162
- localizedMenuList,
163
- groupedMenuList,
164
- isParentMenu,
165
- isItemSelected,
166
- getListItemClasses,
167
- handleSelectedItem,
168
- } = useList(props, emit);
186
+ const { searchText, localizedMenuList, groupedMenuList, isItemSelected, getListItemClasses, handleSelectedItem } =
187
+ useList(props, emit);
169
188
  </script>
@@ -20,12 +20,12 @@ const PLACEMENTS_TYPES = [
20
20
  ] as const;
21
21
 
22
22
  const POPPER_STRATEGY_TYPES = ['fixed', 'absolute'] as const;
23
+ const TRIGGER_EVENTS = ['click', 'hover', 'focus', 'touch'] as const;
23
24
 
24
25
  export const selectLadderizedPropTypes = {
25
26
  id: {
26
27
  type: String,
27
28
  required: true,
28
- default: 'select-ladderized',
29
29
  },
30
30
  modelValue: {
31
31
  type: Array as PropType<string[]>,
@@ -97,6 +97,24 @@ export const selectLadderizedPropTypes = {
97
97
  validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
98
98
  default: 'bottom',
99
99
  },
100
+ triggers: {
101
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
102
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
103
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
104
+ },
105
+ default: () => [],
106
+ },
107
+ popperTriggers: {
108
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
109
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
110
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
111
+ },
112
+ default: () => [],
113
+ },
114
+ autoHide: {
115
+ type: Boolean,
116
+ default: true,
117
+ },
100
118
  wrapperPosition: {
101
119
  type: String,
102
120
  default: 'relative',
@@ -8,15 +8,15 @@
8
8
  </label>
9
9
 
10
10
  <Menu
11
- :shown="ladderizedSelectPopperState"
11
+ v-model:shown="ladderizedSelectPopperState"
12
12
  aria-id="ladderized-select-wrapper"
13
13
  distance="4"
14
14
  :placement="props.placement"
15
- :triggers="[]"
16
- :popper-hide-triggers="[]"
17
- :auto-hide="false"
15
+ :triggers="props.triggers"
16
+ :popper-triggers="props.popperTriggers"
17
+ :auto-hide="props.autoHide"
18
18
  :disabled="isLadderizedSelectPopperDisabled"
19
- :container="'#ladderized-select-wrapper'"
19
+ :container="`#${props.id}`"
20
20
  :strategy="
21
21
  props.popperStrategy === 'fixed' || props.popperStrategy === 'absolute' ? props.popperStrategy : 'absolute'
22
22
  "
@@ -26,44 +26,46 @@
26
26
  width: props.width,
27
27
  }"
28
28
  >
29
- <div @click="handleOptionsToggle">
30
- <spr-input
31
- :id="`input-${props.id}`"
32
- v-model="inputText"
33
- class="spr-cursor-pointer"
34
- :placeholder="props.placeholder"
35
- autocomplete="off"
36
- :helper-text="props.helperText"
37
- :helper-icon="props.helperIcon"
38
- :display-helper="props.displayHelper"
39
- readonly
40
- :disabled="props.disabled"
41
- :error="props.error"
42
- @keyup="handleSearch"
43
- >
44
- <template #icon>
45
- <div class="spr-flex spr-items-center spr-gap-1">
46
- <Icon
47
- v-if="props.clearable && inputText"
48
- class="spr-cursor-pointer"
49
- icon="ph:x"
50
- @click.stop="handleClear"
51
- />
52
- <Icon icon="ph:caret-down" />
53
- </div>
54
- </template>
29
+ <div ref="ladderizedSelectState">
30
+ <div @click="ladderizedSelectPopperState = !ladderizedSelectPopperState">
31
+ <spr-input
32
+ :id="`input-${props.id}`"
33
+ v-model="inputText"
34
+ class="spr-cursor-pointer"
35
+ :placeholder="props.placeholder"
36
+ autocomplete="off"
37
+ :helper-text="props.helperText"
38
+ :helper-icon="props.helperIcon"
39
+ :display-helper="props.displayHelper"
40
+ readonly
41
+ :disabled="props.disabled"
42
+ :error="props.error"
43
+ >
44
+ <template #icon>
45
+ <div class="spr-flex spr-items-center spr-gap-1">
46
+ <Icon
47
+ v-if="props.clearable && inputText"
48
+ class="spr-cursor-pointer"
49
+ icon="ph:x"
50
+ @click.stop="handleClear"
51
+ />
52
+ <Icon icon="ph:caret-down" />
53
+ </div>
54
+ </template>
55
55
 
56
- <template #helperMessage>
57
- <slot name="helperMessage" />
58
- </template>
59
- </spr-input>
60
- </div>
56
+ <template #helperMessage>
57
+ <slot name="helperMessage" />
58
+ </template>
59
+ </spr-input>
60
+ </div>
61
61
 
62
- <div id="ladderized-select-wrapper" :style="{ width: props.popperWidth }"></div>
62
+ <!-- This div used to poppulate popper menu -->
63
+ <div :id="props.id" :style="{ width: props.popperWidth }"></div>
64
+ </div>
63
65
 
64
66
  <template #popper>
65
67
  <div
66
- ref="ladderizedSelectRef"
68
+ ref="ladderizedSelectPopperRef"
67
69
  class="spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden"
68
70
  >
69
71
  <template v-if="ladderizedSelectOptions.length > 0">
@@ -106,15 +108,14 @@ const emit = defineEmits(selectLadderizedEmitTypes);
106
108
 
107
109
  const {
108
110
  ladderizedClasses,
111
+ ladderizedSelectState,
109
112
  ladderizedSelectPopperState,
110
- ladderizedSelectRef,
113
+ ladderizedSelectPopperRef,
111
114
  ladderizedSelectOptions,
112
115
  isLadderizedSelectPopperDisabled,
113
116
  ladderizedSelectModel,
114
117
  inputText,
115
118
  handleSelectedLadderizedItem,
116
- handleSearch,
117
119
  handleClear,
118
- handleOptionsToggle,
119
120
  } = useSelectLadderized(props, emit as SelectLadderizedEmitFn);
120
121
  </script>
@@ -1,5 +1,5 @@
1
1
  import { ref, toRefs, computed, watch } from 'vue';
2
- import { useVModel, useDebounceFn, onClickOutside } from '@vueuse/core';
2
+ import { useVModel, onClickOutside } from '@vueuse/core';
3
3
 
4
4
  import type { SelectLadderizedPropTypes } from './select-ladderized';
5
5
 
@@ -17,9 +17,11 @@ export const useSelectLadderized = (
17
17
  supportingLabelClasses: 'spr-body-sm-regular spr-text-color-supporting',
18
18
  }));
19
19
 
20
+ const ladderizedSelectState = ref<HTMLDivElement | null>(null);
21
+
20
22
  // Popper Variables
21
- const ladderizedSelectPopperState = ref(false);
22
- const ladderizedSelectRef = ref(null);
23
+ const ladderizedSelectPopperState = ref<boolean>(false);
24
+ const ladderizedSelectPopperRef = ref<HTMLElement | null>(null);
23
25
  const isLadderizedSelectPopperDisabled = computed(() => disabled.value);
24
26
 
25
27
  // Ladderized Select Model
@@ -28,8 +30,7 @@ export const useSelectLadderized = (
28
30
 
29
31
  // Input Variables
30
32
  const inputText = ref<string>('');
31
- const isSearching = ref(false);
32
- const wasCleared = ref(false);
33
+ const wasCleared = ref<boolean>(false);
33
34
 
34
35
  const isLeafNode = (item: MenuListType): boolean => {
35
36
  return !item.sublevel || item.sublevel.length === 0;
@@ -102,8 +103,6 @@ export const useSelectLadderized = (
102
103
 
103
104
  if (leafItem && isLeafNode(leafItem)) {
104
105
  ladderizedSelectPopperState.value = false;
105
-
106
- emit('popper-state', false);
107
106
  }
108
107
 
109
108
  return;
@@ -141,24 +140,12 @@ export const useSelectLadderized = (
141
140
 
142
141
  if (isLeafNode(itemToCheck)) {
143
142
  ladderizedSelectPopperState.value = false;
144
-
145
- emit('popper-state', false);
146
143
  }
147
144
  } else if (selectedItems.length === 0 && !wasCleared.value) {
148
145
  inputText.value = '';
149
146
  }
150
147
  };
151
148
 
152
- const handleSearch = () => {
153
- isSearching.value = true;
154
-
155
- debouncedEmitSearch();
156
- };
157
-
158
- const debouncedEmitSearch = useDebounceFn(() => {
159
- // Optionally emit search event here if needed
160
- }, 300);
161
-
162
149
  const handleClear = () => {
163
150
  wasCleared.value = true;
164
151
 
@@ -167,14 +154,6 @@ export const useSelectLadderized = (
167
154
  emit('update:modelValue', []);
168
155
  };
169
156
 
170
- const handleOptionsToggle = () => {
171
- ladderizedSelectPopperState.value = true;
172
-
173
- isSearching.value = false;
174
-
175
- emit('popper-state', true);
176
- };
177
-
178
157
  // Watch for changes in modelValue to update inputText
179
158
  watch(
180
159
  () => ladderizedSelectModel.value,
@@ -209,23 +188,31 @@ export const useSelectLadderized = (
209
188
  { immediate: true },
210
189
  );
211
190
 
212
- onClickOutside(ladderizedSelectRef, () => {
213
- ladderizedSelectPopperState.value = false;
191
+ watch(ladderizedSelectPopperState, (newState) => {
192
+ emit('popper-state', newState);
193
+ });
214
194
 
215
- emit('popper-state', false);
195
+ // Close only when clicking completely outside both the popper and the trigger wrapper.
196
+ onClickOutside(ladderizedSelectPopperRef, (event) => {
197
+ const triggerWrapper = ladderizedSelectState.value;
198
+ if (triggerWrapper && triggerWrapper.contains(event.target as Node)) {
199
+ return; // ignore clicks on trigger content
200
+ }
201
+ if (ladderizedSelectPopperState.value) {
202
+ ladderizedSelectPopperState.value = false;
203
+ }
216
204
  });
217
205
 
218
206
  return {
219
207
  ladderizedClasses,
208
+ ladderizedSelectState,
220
209
  ladderizedSelectPopperState,
221
- ladderizedSelectRef,
210
+ ladderizedSelectPopperRef,
222
211
  ladderizedSelectOptions,
223
212
  isLadderizedSelectPopperDisabled,
224
213
  ladderizedSelectModel,
225
214
  inputText,
226
215
  handleSelectedLadderizedItem,
227
- handleSearch,
228
216
  handleClear,
229
- handleOptionsToggle,
230
217
  };
231
218
  };
@@ -24,12 +24,12 @@ const PLACEMENTS_TYPES = [
24
24
  ] as const;
25
25
 
26
26
  const POPPER_STRATEGY_TYPES = ['fixed', 'absolute'] as const;
27
+ const TRIGGER_EVENTS = ['click', 'hover', 'focus', 'touch'] as const;
27
28
 
28
29
  export const multiSelectPropTypes = {
29
30
  id: {
30
31
  type: String,
31
32
  required: true,
32
- default: 'multi-select',
33
33
  },
34
34
  modelValue: {
35
35
  type: Array as PropType<(string | number | Record<string, unknown>)[]>,
@@ -76,6 +76,24 @@ export const multiSelectPropTypes = {
76
76
  validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
77
77
  default: 'bottom',
78
78
  },
79
+ triggers: {
80
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
81
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
82
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
83
+ },
84
+ default: () => [],
85
+ },
86
+ popperTriggers: {
87
+ type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
88
+ validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
89
+ return value.every((val) => TRIGGER_EVENTS.includes(val));
90
+ },
91
+ default: () => [],
92
+ },
93
+ autoHide: {
94
+ type: Boolean,
95
+ default: false,
96
+ },
79
97
  popperStrategy: {
80
98
  type: String,
81
99
  validator: (value: 'fixed' | 'absolute') => POPPER_STRATEGY_TYPES.includes(value),