@youcan/celeste 0.6.15 → 0.6.16

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 (88) hide show
  1. package/dist/celeste.css +597 -198
  2. package/dist/cjs/chunks/combobox-anchor.js +31 -0
  3. package/dist/cjs/chunks/combobox-root.js +38 -0
  4. package/dist/cjs/chunks/index.js +17 -0
  5. package/dist/cjs/combobox-anchor.js +3 -0
  6. package/dist/cjs/combobox-empty.js +32 -0
  7. package/dist/cjs/combobox-group.js +42 -0
  8. package/dist/cjs/combobox-input.js +67 -0
  9. package/dist/cjs/combobox-item.js +109 -0
  10. package/dist/cjs/combobox-list.js +62 -0
  11. package/dist/cjs/combobox-root.js +3 -0
  12. package/dist/cjs/combobox-separator.js +32 -0
  13. package/dist/cjs/combobox-trigger.js +46 -0
  14. package/dist/cjs/combobox.js +208 -0
  15. package/dist/cjs/dropdown.js +10 -3
  16. package/dist/cjs/index.js +30 -0
  17. package/dist/cjs/text-input-icon.js +2 -2
  18. package/dist/components/combobox/combobox-anchor.d.ts +22 -0
  19. package/dist/components/combobox/combobox-empty.d.ts +22 -0
  20. package/dist/components/combobox/combobox-group.d.ts +23 -0
  21. package/dist/components/combobox/combobox-input.d.ts +17 -0
  22. package/dist/components/combobox/combobox-item.d.ts +31 -0
  23. package/dist/components/combobox/combobox-list.d.ts +36 -0
  24. package/dist/components/combobox/combobox-root.d.ts +29 -0
  25. package/dist/components/combobox/combobox-separator.d.ts +22 -0
  26. package/dist/components/combobox/combobox-trigger.d.ts +20 -0
  27. package/dist/components/combobox/combobox.d.ts +53 -0
  28. package/dist/components/combobox/index.d.ts +20 -0
  29. package/dist/components/dropdown/dropdown.d.ts +5 -1
  30. package/dist/components/dropdown-menu/dropdown-menu-sub-content.d.ts +4 -4
  31. package/dist/components/index.d.ts +1 -0
  32. package/dist/components/modal/modal-content.d.ts +2 -2
  33. package/dist/components/popover/popover-content.d.ts +2 -2
  34. package/dist/es/chunks/combobox-anchor.js +32 -0
  35. package/dist/es/chunks/combobox-root.js +39 -0
  36. package/dist/es/chunks/index.js +59 -42
  37. package/dist/es/combobox-anchor.js +4 -0
  38. package/dist/es/combobox-empty.js +33 -0
  39. package/dist/es/combobox-group.js +43 -0
  40. package/dist/es/combobox-input.js +68 -0
  41. package/dist/es/combobox-item.js +110 -0
  42. package/dist/es/combobox-list.js +63 -0
  43. package/dist/es/combobox-root.js +4 -0
  44. package/dist/es/combobox-separator.js +33 -0
  45. package/dist/es/combobox-trigger.js +47 -0
  46. package/dist/es/combobox.js +209 -0
  47. package/dist/es/command-group.js +1 -1
  48. package/dist/es/command-input.js +1 -1
  49. package/dist/es/command-separator.js +1 -1
  50. package/dist/es/dropdown-menu-group.js +1 -1
  51. package/dist/es/dropdown-menu-item.js +1 -1
  52. package/dist/es/dropdown-menu-label.js +1 -1
  53. package/dist/es/dropdown-menu-separator.js +1 -1
  54. package/dist/es/dropdown-menu-trigger.js +1 -1
  55. package/dist/es/dropdown-menu.js +1 -1
  56. package/dist/es/dropdown.js +11 -4
  57. package/dist/es/index.js +75 -45
  58. package/dist/es/label.js +1 -1
  59. package/dist/es/modal-content.js +1 -1
  60. package/dist/es/modal-header.js +1 -1
  61. package/dist/es/popover-content.js +1 -1
  62. package/dist/es/popover-trigger.js +1 -1
  63. package/dist/es/segmented-control-item.js +1 -1
  64. package/dist/es/segmented-control.js +1 -1
  65. package/dist/es/select-content.js +1 -1
  66. package/dist/es/select-item.js +1 -1
  67. package/dist/es/select-trigger.js +1 -1
  68. package/dist/es/select-value.js +1 -1
  69. package/dist/es/tab-menu-content.js +1 -1
  70. package/dist/es/tab-menu-item.js +1 -1
  71. package/dist/es/tab-menu-list.js +1 -1
  72. package/dist/es/text-input-icon.js +2 -2
  73. package/dist/es/tooltip.js +1 -1
  74. package/package.json +3 -3
  75. package/src/components/combobox/combobox-anchor.vue +25 -0
  76. package/src/components/combobox/combobox-empty.vue +30 -0
  77. package/src/components/combobox/combobox-group.vue +42 -0
  78. package/src/components/combobox/combobox-input.vue +64 -0
  79. package/src/components/combobox/combobox-item.vue +196 -0
  80. package/src/components/combobox/combobox-list.vue +100 -0
  81. package/src/components/combobox/combobox-root.vue +14 -0
  82. package/src/components/combobox/combobox-separator.vue +31 -0
  83. package/src/components/combobox/combobox-trigger.vue +249 -0
  84. package/src/components/combobox/combobox.vue +257 -0
  85. package/src/components/combobox/index.ts +29 -0
  86. package/src/components/combobox/stories/combobox.stories.ts +99 -0
  87. package/src/components/dropdown/dropdown.vue +33 -2
  88. package/src/components/index.ts +1 -0
@@ -0,0 +1,249 @@
1
+ <script setup lang="ts">
2
+ import type { ComboboxTriggerProps } from 'radix-vue';
3
+ import clsx from 'clsx';
4
+ import { ComboboxTrigger, useForwardProps } from 'radix-vue';
5
+ import { computed, type HTMLAttributes } from 'vue';
6
+
7
+ const props = withDefaults(defineProps<CustomComboboxTriggerProps>(), {
8
+ type: 'normal',
9
+ });
10
+
11
+ const delegatedProps = computed(() => {
12
+ const { class: _, ...delegated } = props;
13
+
14
+ return delegated;
15
+ });
16
+
17
+ const forwarded = useForwardProps(delegatedProps);
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ export interface CustomComboboxTriggerProps extends ComboboxTriggerProps {
22
+ class?: HTMLAttributes['class'];
23
+ size?: 'xs' | 'sm' | 'md';
24
+ filled?: boolean;
25
+ type?: 'normal' | 'compact' | 'inline' | 'compact-input';
26
+ error?: boolean;
27
+ }
28
+ </script>
29
+
30
+ <template>
31
+ <ComboboxTrigger
32
+ v-bind="forwarded"
33
+ tabindex="0"
34
+ :class="clsx(
35
+ 'celeste-dropdown-anchor-trigger',
36
+ `celeste-dropdown-anchor-trigger-size-${props.size}`,
37
+ `celeste-dropdown-anchor-trigger-type-${props.type}`,
38
+ { 'celeste-dropdown-anchor-trigger-error': props.error },
39
+ { 'celeste-dropdown-anchor-trigger-filled': filled },
40
+ props.class,
41
+ )"
42
+ >
43
+ <slot />
44
+ </ComboboxTrigger>
45
+ </template>
46
+
47
+ <style lang="scss" scoped>
48
+ @use 'sass:map';
49
+
50
+ @mixin transition-default {
51
+ transition-property: background-color, transform, opacity;
52
+ transition-duration: var(--animation-fast);
53
+ transition-timing-function: ease-out;
54
+ }
55
+
56
+ .celeste-dropdown-anchor-trigger {
57
+ @include transition-default;
58
+
59
+ display: flex;
60
+ align-items: center;
61
+ border: 1px solid var(--color-stroke-soft-200);
62
+ background-color: var(--color-bg-white-0);
63
+ box-shadow: var(--shadow-regular-xs);
64
+ gap: var(--spacing-8);
65
+ cursor: pointer;
66
+
67
+ :deep(i) {
68
+ width: 20px;
69
+ height: 20px;
70
+ color: var(--color-icon-soft-400);
71
+ }
72
+
73
+ &:hover {
74
+ background: var(--color-bg-weak-50);
75
+ }
76
+
77
+ &:focus,
78
+ &:focus-visible,
79
+ &:focus-within {
80
+ outline: none;
81
+ box-shadow: var(--shadow-buttons-important-focus);
82
+ }
83
+
84
+ .celeste-dropdown-anchor-trigger-prefix {
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+
89
+ &-selected {
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ }
94
+ }
95
+
96
+ .celeste-dropdown-input {
97
+ display: flex;
98
+ flex: 1 0 0;
99
+ align-items: center;
100
+ justify-content: space-between;
101
+ background: inherit;
102
+ color: var(--color-text-sub-600);
103
+ font: var(--paragraph-sm);
104
+ cursor: pointer;
105
+ gap: var(--spacing-8);
106
+ }
107
+
108
+ .celeste-dropdown-trigger {
109
+ @include transition-default;
110
+
111
+ display: flex;
112
+ place-items: center center;
113
+ border: none;
114
+ background: none;
115
+ color: var(--color-icon-soft-400);
116
+ cursor: pointer;
117
+ }
118
+
119
+ &[data-state='open']:not([data-disabled]) {
120
+ :deep(.celeste-dropdown-trigger) {
121
+ transform: rotate(180deg);
122
+ }
123
+ }
124
+
125
+ &-error:not(.celeste-dropdown-anchor-trigger-type-compact) {
126
+ border: 1px solid var(--color-state-error-base);
127
+ }
128
+
129
+ &[data-disabled] {
130
+ background: var(--color-bg-weak-50);
131
+ color: var(--color-text-disabled-300);
132
+ cursor: not-allowed;
133
+
134
+ .celeste-dropdown-input {
135
+ color: var(--color-text-disabled-300);
136
+ cursor: not-allowed;
137
+ }
138
+
139
+ &:deep(i),
140
+ .celeste-dropdown-trigger {
141
+ color: var(--color-icon-disabled-300);
142
+ cursor: not-allowed;
143
+ }
144
+ }
145
+
146
+ &[data-state='open']:not(
147
+ [data-disabled],
148
+ .celeste-dropdown-anchor-trigger-type-compact,
149
+ .celeste-dropdown-anchor-trigger-type-compact-input
150
+ ) {
151
+ border: 1px solid var(--color-stroke-strong-950);
152
+ box-shadow: var(--shadow-buttons-important-focus);
153
+ color: var(--color-text-strong-950);
154
+
155
+ .celeste-dropdown-input {
156
+ color: var(--color-text-strong-950);
157
+ }
158
+
159
+ i {
160
+ color: var(--color-icon-strong-950);
161
+ }
162
+ }
163
+
164
+ &-filled {
165
+ color: var(--color-text-strong-950);
166
+
167
+ :deep(.celeste-dropdown-input) {
168
+ color: var(--color-text-strong-950);
169
+ }
170
+ }
171
+
172
+ &.celeste-dropdown-anchor-trigger-type-compact {
173
+ width: fit-content;
174
+ border: none;
175
+ background: transparent;
176
+ box-shadow: none;
177
+
178
+ &:deep(.celeste-dropdown-input) {
179
+ flex: inherit;
180
+ }
181
+
182
+ &:hover:not([data-disabled]),
183
+ &[data-state='open']:not([data-disabled]) {
184
+ .celeste-dropdown-input {
185
+ color: var(--color-text-strong-950);
186
+ }
187
+
188
+ &:deep(i) {
189
+ color: var(--color-icon-strong-950);
190
+ }
191
+ }
192
+ }
193
+
194
+ &.celeste-dropdown-anchor-trigger-type-compact-input {
195
+ width: fit-content;
196
+ border: none;
197
+ border-radius: var(--radius-0);
198
+ box-shadow: none;
199
+
200
+ &:deep(.celeste-dropdown-input) {
201
+ flex: inherit;
202
+ }
203
+
204
+ &[data-state='open'] {
205
+ box-shadow: none;
206
+ }
207
+ }
208
+
209
+ &.celeste-dropdown-anchor-trigger-type-inline {
210
+ width: fit-content;
211
+ gap: var(--spacing-4);
212
+
213
+ &:deep(.celeste-dropdown-input) {
214
+ flex: inherit;
215
+ }
216
+ }
217
+
218
+ &.celeste-dropdown-anchor-trigger-type-normal {
219
+ width: 100%;
220
+ min-width: 300px;
221
+ }
222
+
223
+ $sizes-map: (
224
+ 'xs': (
225
+ 'padding': var(--spacing-6),
226
+ 'border-radius': var(--radius-8),
227
+ 'height': 32px,
228
+ ),
229
+ 'sm': (
230
+ 'padding': var(--spacing-8),
231
+ 'border-radius': var(--radius-8),
232
+ 'height': 36px,
233
+ ),
234
+ 'md': (
235
+ 'padding': var(--spacing-10),
236
+ 'border-radius': var(--radius-10),
237
+ 'height': 40px,
238
+ ),
239
+ );
240
+
241
+ @each $size, $values in $sizes-map {
242
+ &-size-#{$size} {
243
+ height: map.get($values, 'height');
244
+ padding: map.get($values, 'padding');
245
+ border-radius: map.get($values, 'border-radius');
246
+ }
247
+ }
248
+ }
249
+ </style>
@@ -0,0 +1,257 @@
1
+ <script setup lang="ts">
2
+ import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue';
3
+ import type { BadgeProps } from '../badge/badge.vue';
4
+ import type { ComboboxItemPropsType } from './combobox-item.vue';
5
+
6
+ import { useForwardPropsEmits } from 'radix-vue';
7
+ import { computed, ref } from 'vue';
8
+ import Badge from '../badge/badge.vue';
9
+ import ComboboxAnchor from './combobox-anchor.vue';
10
+ import ComboboxEmpty from './combobox-empty.vue';
11
+ import ComboboxGroup from './combobox-group.vue';
12
+ import ComboboxInput from './combobox-input.vue';
13
+ import ComboboxItem from './combobox-item.vue';
14
+ import ComboboxList from './combobox-list.vue';
15
+ import ComboboxRoot from './combobox-root.vue';
16
+ import ComboboxSeparator from './combobox-separator.vue';
17
+ import ComboboxTrigger from './combobox-trigger.vue';
18
+
19
+ const props = withDefaults(defineProps<ComboboxPropsType>(), {
20
+ placeholder: 'Select',
21
+ size: 'sm',
22
+ });
23
+ const emits = defineEmits<ComboboxRootEmits>();
24
+
25
+ const delegatedProps = computed(() => {
26
+ const { open, modelValue, filterFunction, ...delegated } = props;
27
+
28
+ return delegated;
29
+ });
30
+
31
+ const forwarded = useForwardPropsEmits(delegatedProps, emits);
32
+ const selected = ref<ComboboxItemPropsType[] | ComboboxItemPropsType | undefined>(props.multiple ? [] : undefined);
33
+
34
+ function isSelected(value: ComboboxItemPropsType['value']): boolean | undefined {
35
+ if (!Array.isArray(selected.value) || !props.multiple)
36
+ return;
37
+
38
+ if (value === undefined || value === null)
39
+ return false;
40
+
41
+ return selected.value.some((selectedItem: ComboboxItemPropsType) => {
42
+ const { value: selectedValue } = selectedItem;
43
+
44
+ if (
45
+ props.valueBy
46
+ && Object.prototype.hasOwnProperty.call(value, props.valueBy)
47
+ && Object.prototype.hasOwnProperty.call(selectedValue, props.valueBy)) {
48
+ return (value as Record<string, any>)[props.valueBy] === (selectedValue as Record<string, any>)[props.valueBy];
49
+ }
50
+
51
+ return selectedValue === value;
52
+ });
53
+ }
54
+
55
+ function filterFunction(list: any[], searchTerm: string) {
56
+ return list.filter((option: ComboboxItemPropsType) => {
57
+ const { value, label } = option;
58
+
59
+ if (!value || !label)
60
+ return false;
61
+
62
+ if (
63
+ props.valueBy
64
+ && typeof value === 'object'
65
+ && props.valueBy in value) {
66
+ return String(value[props.valueBy]).toLowerCase().includes(searchTerm.toLowerCase())
67
+ || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
68
+ }
69
+
70
+ return value.toString().toLowerCase().includes(searchTerm.toLowerCase())
71
+ || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
72
+ });
73
+ }
74
+
75
+ const mergedBadgeProps = computed(() => ({
76
+ ...props.badgeProps,
77
+ disabled: props.disabled,
78
+ }));
79
+ </script>
80
+
81
+ <script lang="ts">
82
+ export interface ComboboxPropsType extends ComboboxRootProps {
83
+ options: ComboboxItemPropsType[];
84
+ valueBy?: string;
85
+ placeholder?: string;
86
+ type?: 'normal' | 'compact' | 'inline' | 'compact-input';
87
+ size?: 'xs' | 'sm' | 'md';
88
+ emptyLabel?: string;
89
+ badgeProps?: BadgeProps;
90
+ searchable?: boolean;
91
+ }
92
+ </script>
93
+
94
+ <template>
95
+ <div class="root">
96
+ <ComboboxRoot
97
+ v-bind="forwarded"
98
+ v-model:model-value="selected"
99
+ :filter-function="filterFunction"
100
+ >
101
+ <ComboboxAnchor as-child>
102
+ <ComboboxTrigger
103
+ :type="type"
104
+ :filled="!!selected"
105
+ :size="size"
106
+ >
107
+ <div class="celeste-dropdown-anchor-trigger-prefix">
108
+ <div v-if="selected && !Array.isArray(selected)" class="celeste-dropdown-anchor-trigger-prefix-selected">
109
+ <i v-if="selected.icon" :class="selected.icon" />
110
+ <div v-if="selected.image">
111
+ <slot name="image" v-bind="{ selected }">
112
+ <img
113
+ :src="selected.image"
114
+ alt="Selected option"
115
+ class="selected-dropdown-image"
116
+ >
117
+ </slot>
118
+ </div>
119
+ </div>
120
+ <slot
121
+ v-else
122
+ name="prefix"
123
+ class="celeste-dropdown-anchor-trigger-prefix-default"
124
+ />
125
+ </div>
126
+
127
+ <div
128
+ v-if="type !== 'inline'"
129
+ class="celeste-dropdown-input"
130
+ >
131
+ <span v-if="multiple">{{ placeholder }}</span>
132
+ <span v-else>
133
+ {{ selected && !Array.isArray(selected) ? selected.label : props.placeholder }}
134
+ </span>
135
+ <span v-if="mergedBadgeProps && badgeProps" class="celeste-input-badge">
136
+ <Badge v-bind="mergedBadgeProps" />
137
+ </span>
138
+ </div>
139
+ <div class="celeste-dropdown-trigger">
140
+ <i class="i-celeste-arrow-down-s-line" />
141
+ </div>
142
+ </ComboboxTrigger>
143
+ </ComboboxAnchor>
144
+
145
+ <ComboboxList>
146
+ <div class="celeste-dropdown-inner-input" :class="{ 'sr-only': !searchable }">
147
+ <ComboboxInput :searchable="searchable" />
148
+ <ComboboxSeparator v-if="searchable" />
149
+ </div>
150
+
151
+ <ComboboxEmpty>
152
+ <template v-if="emptyLabel" #default>
153
+ {{ emptyLabel }}
154
+ </template>
155
+ </ComboboxEmpty>
156
+
157
+ <ComboboxGroup>
158
+ <ComboboxItem
159
+ v-for="option in props.options"
160
+ :key="option.label"
161
+ :label="option.label"
162
+ :sublabel="option.sublabel"
163
+ :description="option.description"
164
+ :checkbox="props.multiple"
165
+ :value="option"
166
+ :selected="isSelected(option.value)"
167
+ >
168
+ <template #prefix>
169
+ <i v-if="option.icon" :class="option.icon" />
170
+ <img
171
+ v-if="option.image"
172
+ :src="option.image"
173
+ alt="option"
174
+ class="celeste-dropdown-item-image"
175
+ >
176
+ </template>
177
+ </ComboboxItem>
178
+ </ComboboxGroup>
179
+ </ComboboxList>
180
+ </ComboboxRoot>
181
+ </div>
182
+ </template>
183
+
184
+ <style lang="scss" scoped>
185
+ * {
186
+ box-sizing: border-box;
187
+ }
188
+
189
+ .celeste-dropdown-anchor-trigger-prefix {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+
194
+ &-selected {
195
+ display: flex;
196
+ align-items: center;
197
+ justify-content: center;
198
+ }
199
+ }
200
+
201
+ .celeste-dropdown-item-image,
202
+ .selected-dropdown-image {
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ width: 20px;
207
+ height: 20px;
208
+ object-fit: cover;
209
+ border-radius: var(--radius-full);
210
+ }
211
+
212
+ .celeste-dropdown-input {
213
+ display: flex;
214
+ flex: 1 0 0;
215
+ align-items: center;
216
+ justify-content: space-between;
217
+ background: inherit;
218
+ color: var(--color-text-sub-600);
219
+ font: var(--paragraph-sm);
220
+ cursor: pointer;
221
+ gap: var(--spacing-8);
222
+ }
223
+
224
+ .celeste-dropdown-trigger {
225
+ display: flex;
226
+ place-items: center center;
227
+ transition-property: transform;
228
+ transition-duration: var(--animation-fast);
229
+ transition-timing-function: ease-out;
230
+ border: none;
231
+ background: none;
232
+ color: var(--color-icon-soft-400);
233
+ cursor: pointer;
234
+ }
235
+
236
+ .celeste-dropdown-inner-input {
237
+ display: flex;
238
+ position: sticky;
239
+ z-index: 50;
240
+ top: 0;
241
+ flex-direction: column;
242
+ gap: var(--spacing-4);
243
+ background-color: var(--color-bg-white-0);
244
+
245
+ &.sr-only {
246
+ position: absolute;
247
+ width: 1px;
248
+ height: 1px;
249
+ margin: -1px;
250
+ padding: 0;
251
+ overflow: hidden;
252
+ clip: rect(0, 0, 0, 0);
253
+ border: 0;
254
+ white-space: nowrap;
255
+ }
256
+ }
257
+ </style>
@@ -0,0 +1,29 @@
1
+ export * from './combobox-anchor.vue';
2
+ export { default as ComboboxAnchor } from './combobox-anchor.vue';
3
+
4
+ export * from './combobox-empty.vue';
5
+ export { default as ComboboxEmpty } from './combobox-empty.vue';
6
+
7
+ export * from './combobox-group.vue';
8
+ export { default as ComboboxGroup } from './combobox-group.vue';
9
+
10
+ export * from './combobox-input.vue';
11
+ export { default as ComboboxInput } from './combobox-input.vue';
12
+
13
+ export * from './combobox-item.vue';
14
+ export { default as ComboboxItem } from './combobox-item.vue';
15
+
16
+ export * from './combobox-list.vue';
17
+ export { default as ComboboxList } from './combobox-list.vue';
18
+
19
+ export * from './combobox-root.vue';
20
+ export { default as ComboboxRoot } from './combobox-root.vue';
21
+
22
+ export * from './combobox-separator.vue';
23
+ export { default as ComboboxSeparator } from './combobox-separator.vue';
24
+
25
+ export * from './combobox-trigger.vue';
26
+ export { default as ComboboxTrigger } from './combobox-trigger.vue';
27
+
28
+ export * from './combobox.vue';
29
+ export { default as Combobox } from './combobox.vue';
@@ -0,0 +1,99 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import Combobox from '../combobox.vue';
3
+
4
+ const meta: Meta<typeof Combobox> = {
5
+ title: 'Components/Combobox',
6
+ component: Combobox,
7
+ };
8
+
9
+ export default meta;
10
+
11
+ type Story = StoryObj<typeof Combobox>;
12
+
13
+ const OPTIONS = [
14
+ { value: { id: 1 }, label: 'Durward Reynolds', sublabel: 'Teacher' },
15
+ { value: { id: 2 }, label: 'Kenton Towne', icon: 'i-celeste-globe-line' },
16
+ { value: { id: 3 }, label: 'Therese Wunsch', sublabel: 'Engineer' },
17
+ { value: { id: 4 }, label: 'Benedict Kessler', image: 'https://cdn3.pixelcut.app/7/20/uncrop_hero_bdf08a8ca6.jpg' },
18
+ { value: { id: 5 }, label: 'Katelyn Rohan' },
19
+ { value: { id: 6 }, label: 'Lily Marissa', icon: 'i-celeste-globe-line' },
20
+ { value: { id: 7 }, label: 'Maya San Eleine', sublabel: 'Writer', icon: 'i-celeste-bookmark-fill' },
21
+ { value: { id: 8 }, label: 'Rami Kent' },
22
+ { value: { id: 9 }, label: 'Goerge Dan', icon: 'i-celeste-sticky-note-line' },
23
+ { value: { id: 10 }, label: 'Remus Down' },
24
+ ];
25
+
26
+ export const Default: Story = {
27
+ args: {
28
+ options: OPTIONS,
29
+ valueBy: 'id',
30
+ },
31
+
32
+ render: args => ({
33
+ components: { Combobox },
34
+ setup() {
35
+ return { args };
36
+ },
37
+ template: `
38
+ <Combobox v-bind="args">
39
+ </Combobox>
40
+ `,
41
+ }),
42
+ };
43
+
44
+ export const Inline: Story = {
45
+ args: {
46
+ options: OPTIONS,
47
+ valueBy: 'id',
48
+ type: 'inline',
49
+ },
50
+
51
+ render: args => ({
52
+ components: { Combobox },
53
+ setup() {
54
+ return { args };
55
+ },
56
+ template: `
57
+ <Combobox v-bind="args">
58
+ </Combobox>
59
+ `,
60
+ }),
61
+ };
62
+
63
+ export const Compact: Story = {
64
+ args: {
65
+ options: OPTIONS,
66
+ valueBy: 'id',
67
+ type: 'compact',
68
+ },
69
+
70
+ render: args => ({
71
+ components: { Combobox },
72
+ setup() {
73
+ return { args };
74
+ },
75
+ template: `
76
+ <Combobox v-bind="args">
77
+ </Combobox>
78
+ `,
79
+ }),
80
+ };
81
+
82
+ export const CompactInput: Story = {
83
+ args: {
84
+ options: OPTIONS,
85
+ valueBy: 'id',
86
+ type: 'compact-input',
87
+ },
88
+
89
+ render: args => ({
90
+ components: { Combobox },
91
+ setup() {
92
+ return { args };
93
+ },
94
+ template: `
95
+ <Combobox v-bind="args">
96
+ </Combobox>
97
+ `,
98
+ }),
99
+ };
@@ -15,6 +15,8 @@ const props = withDefaults(defineProps<Dropdown>(), {
15
15
  multiple: false,
16
16
  searchable: false,
17
17
  emptyLabel: 'No options available',
18
+ position: 'bottom',
19
+ align: 'start',
18
20
  });
19
21
 
20
22
  const model = defineModel<SelectedType | SelectedType[] | undefined>();
@@ -121,6 +123,8 @@ export interface Dropdown {
121
123
  error?: boolean;
122
124
  size?: 'xs' | 'sm' | 'md';
123
125
  type?: 'normal' | 'compact' | 'inline' | 'compact-input';
126
+ position?: 'top' | 'bottom';
127
+ align?: 'start' | 'end' | 'center';
124
128
  multiple?: boolean;
125
129
  emptyLabel?: string;
126
130
  searchable?: boolean;
@@ -209,7 +213,13 @@ export interface SelectedType {
209
213
  v-if="!disabled"
210
214
  force-mount
211
215
  dismissable
216
+ :avoid-collisions="true"
212
217
  class="celeste-dropdown-content"
218
+ :class="clsx(
219
+ 'celeste-dropdown-content',
220
+ `celeste-dropdown-content-size-${position}`,
221
+ `celeste-dropdown-content-align-${align}`,
222
+ )"
213
223
  >
214
224
  <ComboboxViewport class="celeste-dropdown-items-viewport">
215
225
  <div v-if="searchable" class="celeste-dropdown-search">
@@ -501,8 +511,6 @@ export interface SelectedType {
501
511
  display: flex;
502
512
  position: absolute;
503
513
  z-index: 9999;
504
- top: calc(var(--dropdown-width) + var(--spacing-4));
505
- left: 0;
506
514
  flex-direction: column;
507
515
  width: var(--dropdown-width);
508
516
  min-width: 300px;
@@ -528,6 +536,29 @@ export interface SelectedType {
528
536
  }
529
537
  }
530
538
 
539
+ .celeste-dropdown-content-size-top {
540
+ bottom: calc(var(--dropdown-width) + var(--spacing-4));
541
+ }
542
+
543
+ .celeste-dropdown-content-size-bottom {
544
+ top: calc(var(--dropdown-width) + var(--spacing-4));
545
+ }
546
+
547
+ .celeste-dropdown-content-align-start {
548
+ left: 0;
549
+ }
550
+
551
+ .celeste-dropdown-content-align-end {
552
+ right: 0;
553
+ }
554
+
555
+ :not(:has(.celeste-dropdown-anchor-trigger-type-normal)) {
556
+ .celeste-dropdown-content-align-center {
557
+ left: 50%;
558
+ transform: translateX(-50%);
559
+ }
560
+ }
561
+
531
562
  &:deep(.celeste-input-wrapper .celeste-input-prefix) {
532
563
  @include icon-size;
533
564
 
@@ -2,6 +2,7 @@ export * from './alert';
2
2
  export * from './badge';
3
3
  export * from './button';
4
4
  export * from './checkbox';
5
+ export * from './combobox';
5
6
  export * from './content-divider';
6
7
  export * from './dropdown';
7
8
  export * from './dropdown-menu';