@youcan/celeste 0.6.23 → 0.6.24

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.
package/dist/celeste.css CHANGED
@@ -1912,44 +1912,62 @@ to {
1912
1912
  }
1913
1913
  .celeste-dropdown-item .celeste-dropdown-item-header-lg .celeste-dropdown-item-label[data-v-3c2bd0ae] {
1914
1914
  font-weight: var(--font-weights-display-0);
1915
- }.celeste-dropdown-content[data-v-8e15332e],
1916
- div[data-v-8e15332e] .celeste-dropdown-content {
1917
- --dropdown-width: 100%;
1915
+ }.celeste-dropdown-content[data-v-dc6bae8b],
1916
+ div[data-v-dc6bae8b] .celeste-dropdown-content {
1917
+ --dropdown-width: fit-content;
1918
1918
  --dropdown-height: 300px;
1919
+ --dropdown-min-width: var(--radix-combobox-trigger-width, 300px);
1919
1920
  display: flex;
1920
1921
  z-index: 50;
1921
1922
  box-sizing: border-box;
1922
1923
  flex-direction: column;
1923
1924
  width: var(--dropdown-width);
1924
- min-width: 300px;
1925
+ min-width: var(--dropdown-min-width);
1925
1926
  max-height: var(--dropdown-height);
1926
1927
  margin-top: var(--spacing-10);
1927
1928
  padding: var(--spacing-8);
1928
- overflow: hidden;
1929
- transition-property: background-color, transform, opacity;
1930
- transition-duration: var(--animation-fast);
1931
- transition-timing-function: ease-out;
1929
+ overflow: visible;
1930
+ transform-origin: var(--radix-combobox-content-transform-origin);
1931
+ animation-duration: var(--animation-fast);
1932
+ animation-timing-function: ease-out;
1932
1933
  border: 1px solid var(--color-stroke-soft-200);
1933
1934
  border-radius: var(--radius-16);
1934
1935
  background-color: var(--color-bg-white-0);
1935
1936
  box-shadow: var(--shadow-regular-md);
1936
1937
  gap: var(--spacing-4);
1937
1938
  }
1938
- .celeste-dropdown-content[data-state=closed][data-v-8e15332e],
1939
- div[data-v-8e15332e] .celeste-dropdown-content[data-state=closed] {
1940
- transform: translateY(10px);
1941
- opacity: 0;
1942
- pointer-events: none;
1939
+ .celeste-dropdown-content[data-state=open][data-v-dc6bae8b],
1940
+ div[data-v-dc6bae8b] .celeste-dropdown-content[data-state=open] {
1941
+ animation-name: slide-up-and-fade-dc6bae8b;
1943
1942
  }
1944
- .celeste-dropdown-content[data-state=open][data-v-8e15332e],
1945
- div[data-v-8e15332e] .celeste-dropdown-content[data-state=open] {
1946
- transform: translateY(0);
1947
- opacity: 1;
1943
+ .celeste-dropdown-content[data-state=closed][data-v-dc6bae8b],
1944
+ div[data-v-dc6bae8b] .celeste-dropdown-content[data-state=closed] {
1945
+ animation-name: slide-down-and-fade-dc6bae8b;
1948
1946
  }
1949
- div[data-v-8e15332e] .celeste-dropdown-items-viewport {
1947
+ div[data-v-dc6bae8b] .celeste-dropdown-items-viewport {
1950
1948
  display: flex;
1951
1949
  flex-direction: column;
1952
1950
  gap: var(--spacing-4);
1951
+ }
1952
+ @keyframes slide-up-and-fade-dc6bae8b {
1953
+ from {
1954
+ transform: translateY(10px);
1955
+ opacity: 0;
1956
+ }
1957
+ to {
1958
+ transform: translateY(0);
1959
+ opacity: 1;
1960
+ }
1961
+ }
1962
+ @keyframes slide-down-and-fade-dc6bae8b {
1963
+ from {
1964
+ transform: translateY(0);
1965
+ opacity: 1;
1966
+ }
1967
+ to {
1968
+ transform: translateY(10px);
1969
+ opacity: 0;
1970
+ }
1953
1971
  }.celeste-dropdown-separator[data-v-987226c9] {
1954
1972
  width: 100%;
1955
1973
  height: 1px;
@@ -2108,21 +2126,21 @@ div[data-v-8e15332e] .celeste-dropdown-items-viewport {
2108
2126
  height: 40px;
2109
2127
  padding: var(--spacing-10);
2110
2128
  border-radius: var(--radius-10);
2111
- }[data-v-268c7202] {
2129
+ }[data-v-cad50435] {
2112
2130
  box-sizing: border-box;
2113
2131
  }
2114
- .celeste-dropdown-anchor-trigger-prefix[data-v-268c7202] {
2132
+ .celeste-dropdown-anchor-trigger-prefix[data-v-cad50435] {
2115
2133
  display: flex;
2116
2134
  align-items: center;
2117
2135
  justify-content: center;
2118
2136
  }
2119
- .celeste-dropdown-anchor-trigger-prefix-selected[data-v-268c7202] {
2137
+ .celeste-dropdown-anchor-trigger-prefix-selected[data-v-cad50435] {
2120
2138
  display: flex;
2121
2139
  align-items: center;
2122
2140
  justify-content: center;
2123
2141
  }
2124
- .celeste-dropdown-item-image[data-v-268c7202],
2125
- .selected-dropdown-image[data-v-268c7202] {
2142
+ .celeste-dropdown-item-image[data-v-cad50435],
2143
+ .selected-dropdown-image[data-v-cad50435] {
2126
2144
  display: flex;
2127
2145
  align-items: center;
2128
2146
  justify-content: center;
@@ -2132,7 +2150,7 @@ div[data-v-8e15332e] .celeste-dropdown-items-viewport {
2132
2150
  object-fit: cover;
2133
2151
  border-radius: var(--radius-full);
2134
2152
  }
2135
- .celeste-dropdown-input[data-v-268c7202] {
2153
+ .celeste-dropdown-input[data-v-cad50435] {
2136
2154
  display: flex;
2137
2155
  flex: 1 0 0;
2138
2156
  align-items: center;
@@ -2143,7 +2161,7 @@ div[data-v-8e15332e] .celeste-dropdown-items-viewport {
2143
2161
  cursor: pointer;
2144
2162
  gap: var(--spacing-8);
2145
2163
  }
2146
- .celeste-dropdown-trigger[data-v-268c7202] {
2164
+ .celeste-dropdown-trigger[data-v-cad50435] {
2147
2165
  display: flex;
2148
2166
  place-items: center center;
2149
2167
  transition-property: transform;
@@ -2154,7 +2172,7 @@ div[data-v-8e15332e] .celeste-dropdown-items-viewport {
2154
2172
  color: var(--color-icon-soft-400);
2155
2173
  cursor: pointer;
2156
2174
  }
2157
- .celeste-dropdown-inner-input[data-v-268c7202] {
2175
+ .celeste-dropdown-inner-input[data-v-cad50435] {
2158
2176
  display: flex;
2159
2177
  position: sticky;
2160
2178
  z-index: 50;
@@ -2163,7 +2181,7 @@ div[data-v-8e15332e] .celeste-dropdown-items-viewport {
2163
2181
  gap: var(--spacing-4);
2164
2182
  background-color: var(--color-bg-white-0);
2165
2183
  }
2166
- .celeste-dropdown-inner-input.sr-only[data-v-268c7202] {
2184
+ .celeste-dropdown-inner-input.sr-only[data-v-cad50435] {
2167
2185
  position: absolute;
2168
2186
  width: 1px;
2169
2187
  height: 1px;
@@ -12,7 +12,7 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
12
12
  dismissable: { type: Boolean },
13
13
  side: {},
14
14
  sideOffset: { default: 4 },
15
- align: { default: "center" },
15
+ align: { default: "start" },
16
16
  alignOffset: {},
17
17
  avoidCollisions: { type: Boolean },
18
18
  collisionBoundary: {},
@@ -40,7 +40,6 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
40
40
  return Mo.openBlock(), Mo.createBlock(Mo.unref(index.nm), null, {
41
41
  default: Mo.withCtx(() => [
42
42
  Mo.createVNode(Mo.unref(index.Xv), Mo.mergeProps(Mo.unref(forwarded), {
43
- "force-mount": "",
44
43
  class: Mo.unref(clsx.clsx)("celeste-dropdown-content", props.class)
45
44
  }), {
46
45
  default: Mo.withCtx(() => [
@@ -59,5 +58,5 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
59
58
  };
60
59
  }
61
60
  });
62
- const ComboboxList = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-8e15332e"]]);
61
+ const ComboboxList = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-dc6bae8b"]]);
63
62
  module.exports = ComboboxList;
@@ -27,13 +27,11 @@ const _hoisted_6 = {
27
27
  key: 2,
28
28
  class: "celeste-dropdown-input"
29
29
  };
30
- const _hoisted_7 = { key: 0 };
31
- const _hoisted_8 = { key: 1 };
32
- const _hoisted_9 = {
33
- key: 2,
30
+ const _hoisted_7 = {
31
+ key: 0,
34
32
  class: "celeste-input-badge"
35
33
  };
36
- const _hoisted_10 = ["src"];
34
+ const _hoisted_8 = ["src"];
37
35
  const __default__ = {
38
36
  inheritAttrs: false
39
37
  };
@@ -43,6 +41,7 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
43
41
  props: {
44
42
  options: {},
45
43
  valueBy: {},
44
+ labelBy: {},
46
45
  placeholder: { default: "Select" },
47
46
  type: {},
48
47
  size: { default: "md" },
@@ -76,6 +75,7 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
76
75
  filterFunction: filterFunction2,
77
76
  options,
78
77
  valueBy,
78
+ labelBy,
79
79
  placeholder,
80
80
  type,
81
81
  size,
@@ -89,6 +89,16 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
89
89
  });
90
90
  const forwarded = index.xe(delegatedProps, emits);
91
91
  const model = Mo.computed(() => props.modelValue);
92
+ const label = Mo.computed(() => {
93
+ if (!model.value)
94
+ return props.placeholder;
95
+ if (Array.isArray(model.value)) {
96
+ if (model.value.length === 0)
97
+ return props.placeholder;
98
+ return model.value.map((item) => props.labelBy ? item[props.labelBy] : item.label).join(", ");
99
+ }
100
+ return props.labelBy ? model.value[props.labelBy] : model.value.label;
101
+ });
92
102
  function isSelected(value) {
93
103
  if (!Array.isArray(props.modelValue) || !props.multiple)
94
104
  return;
@@ -104,13 +114,13 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
104
114
  }
105
115
  function filterFunction(list, searchTerm) {
106
116
  return list.filter((option) => {
107
- const { value, label } = option;
108
- if (!value || !label)
117
+ const { value, label: label2 } = option;
118
+ if (!value || !label2)
109
119
  return false;
110
120
  if (props.valueBy && typeof value === "object" && props.valueBy in value) {
111
- return String(value[props.valueBy]).toLowerCase().includes(searchTerm.toLowerCase()) || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
121
+ return String(value[props.valueBy]).toLowerCase().includes(searchTerm.toLowerCase()) || label2.toString().toLowerCase().includes(searchTerm.toLowerCase());
112
122
  }
113
- return value.toString().toLowerCase().includes(searchTerm.toLowerCase()) || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
123
+ return value.toString().toLowerCase().includes(searchTerm.toLowerCase()) || label2.toString().toLowerCase().includes(searchTerm.toLowerCase());
114
124
  });
115
125
  }
116
126
  const mergedBadgeProps = Mo.computed(() => ({
@@ -118,7 +128,7 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
118
128
  disabled: props.disabled
119
129
  }));
120
130
  return (_ctx, _cache) => {
121
- return Mo.openBlock(), Mo.createBlock(comboboxRoot_vue_vue_type_script_setup_true_lang._sfc_main, Mo.mergeProps(Mo.unref(forwarded), { "filter-function": filterFunction }), {
131
+ return Mo.openBlock(), Mo.createBlock(comboboxRoot_vue_vue_type_script_setup_true_lang._sfc_main, Mo.mergeProps({ class: "shrink-0" }, Mo.unref(forwarded), { "filter-function": filterFunction }), {
122
132
  default: Mo.withCtx(() => [
123
133
  Mo.createVNode(comboboxAnchor_vue_vue_type_script_setup_true_lang._sfc_main, { "as-child": "" }, {
124
134
  default: Mo.withCtx(() => [
@@ -149,8 +159,8 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
149
159
  Mo.renderSlot(_ctx.$slots, "prefix", { class: "celeste-dropdown-anchor-trigger-prefix-default" }, void 0, true)
150
160
  ])) : Mo.createCommentVNode("", true),
151
161
  _ctx.type !== "inline" ? (Mo.openBlock(), Mo.createElementBlock("div", _hoisted_6, [
152
- _ctx.multiple ? (Mo.openBlock(), Mo.createElementBlock("span", _hoisted_7, Mo.toDisplayString(_ctx.placeholder), 1)) : (Mo.openBlock(), Mo.createElementBlock("span", _hoisted_8, Mo.toDisplayString(model.value && !Array.isArray(model.value) ? model.value.label : props.placeholder), 1)),
153
- mergedBadgeProps.value && _ctx.badgeProps ? (Mo.openBlock(), Mo.createElementBlock("span", _hoisted_9, [
162
+ Mo.createElementVNode("span", null, Mo.toDisplayString(label.value), 1),
163
+ mergedBadgeProps.value && _ctx.badgeProps ? (Mo.openBlock(), Mo.createElementBlock("span", _hoisted_7, [
154
164
  Mo.createVNode(badge, Mo.normalizeProps(Mo.guardReactiveProps(mergedBadgeProps.value)), null, 16)
155
165
  ])) : Mo.createCommentVNode("", true)
156
166
  ])) : Mo.createCommentVNode("", true),
@@ -202,7 +212,7 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
202
212
  src: option.image,
203
213
  alt: "option",
204
214
  class: "celeste-dropdown-item-image"
205
- }, null, 8, _hoisted_10)) : Mo.createCommentVNode("", true)
215
+ }, null, 8, _hoisted_8)) : Mo.createCommentVNode("", true)
206
216
  ]),
207
217
  _: 2
208
218
  }, 1032, ["label", "sublabel", "description", "checkbox", "value", "selected"]);
@@ -219,5 +229,5 @@ const _sfc_main = /* @__PURE__ */ Mo.defineComponent({
219
229
  };
220
230
  }
221
231
  });
222
- const combobox = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-268c7202"]]);
232
+ const combobox = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["__scopeId", "data-v-cad50435"]]);
223
233
  module.exports = combobox;
@@ -4,6 +4,7 @@ import { ComboboxItemPropsType } from './combobox-item';
4
4
  export interface ComboboxPropsType extends ComboboxRootProps {
5
5
  options: ComboboxItemPropsType[];
6
6
  valueBy?: string;
7
+ labelBy?: string;
7
8
  placeholder?: string;
8
9
  type?: 'normal' | 'compact' | 'inline' | 'compact-input';
9
10
  size?: 'xs' | 'sm' | 'md';
@@ -11,7 +11,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
11
11
  dismissable: { type: Boolean },
12
12
  side: {},
13
13
  sideOffset: { default: 4 },
14
- align: { default: "center" },
14
+ align: { default: "start" },
15
15
  alignOffset: {},
16
16
  avoidCollisions: { type: Boolean },
17
17
  collisionBoundary: {},
@@ -39,7 +39,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
39
39
  return openBlock(), createBlock(unref(nm), null, {
40
40
  default: withCtx(() => [
41
41
  createVNode(unref(Xv), mergeProps(unref(forwarded), {
42
- "force-mount": "",
43
42
  class: unref(clsx)("celeste-dropdown-content", props.class)
44
43
  }), {
45
44
  default: withCtx(() => [
@@ -58,7 +57,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
58
57
  };
59
58
  }
60
59
  });
61
- const ComboboxList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-8e15332e"]]);
60
+ const ComboboxList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-dc6bae8b"]]);
62
61
  export {
63
62
  ComboboxList as default
64
63
  };
@@ -26,13 +26,11 @@ const _hoisted_6 = {
26
26
  key: 2,
27
27
  class: "celeste-dropdown-input"
28
28
  };
29
- const _hoisted_7 = { key: 0 };
30
- const _hoisted_8 = { key: 1 };
31
- const _hoisted_9 = {
32
- key: 2,
29
+ const _hoisted_7 = {
30
+ key: 0,
33
31
  class: "celeste-input-badge"
34
32
  };
35
- const _hoisted_10 = ["src"];
33
+ const _hoisted_8 = ["src"];
36
34
  const __default__ = {
37
35
  inheritAttrs: false
38
36
  };
@@ -42,6 +40,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
42
40
  props: {
43
41
  options: {},
44
42
  valueBy: {},
43
+ labelBy: {},
45
44
  placeholder: { default: "Select" },
46
45
  type: {},
47
46
  size: { default: "md" },
@@ -75,6 +74,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
75
74
  filterFunction: filterFunction2,
76
75
  options,
77
76
  valueBy,
77
+ labelBy,
78
78
  placeholder,
79
79
  type,
80
80
  size,
@@ -88,6 +88,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
88
88
  });
89
89
  const forwarded = xe(delegatedProps, emits);
90
90
  const model = computed(() => props.modelValue);
91
+ const label = computed(() => {
92
+ if (!model.value)
93
+ return props.placeholder;
94
+ if (Array.isArray(model.value)) {
95
+ if (model.value.length === 0)
96
+ return props.placeholder;
97
+ return model.value.map((item) => props.labelBy ? item[props.labelBy] : item.label).join(", ");
98
+ }
99
+ return props.labelBy ? model.value[props.labelBy] : model.value.label;
100
+ });
91
101
  function isSelected(value) {
92
102
  if (!Array.isArray(props.modelValue) || !props.multiple)
93
103
  return;
@@ -103,13 +113,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
103
113
  }
104
114
  function filterFunction(list, searchTerm) {
105
115
  return list.filter((option) => {
106
- const { value, label } = option;
107
- if (!value || !label)
116
+ const { value, label: label2 } = option;
117
+ if (!value || !label2)
108
118
  return false;
109
119
  if (props.valueBy && typeof value === "object" && props.valueBy in value) {
110
- return String(value[props.valueBy]).toLowerCase().includes(searchTerm.toLowerCase()) || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
120
+ return String(value[props.valueBy]).toLowerCase().includes(searchTerm.toLowerCase()) || label2.toString().toLowerCase().includes(searchTerm.toLowerCase());
111
121
  }
112
- return value.toString().toLowerCase().includes(searchTerm.toLowerCase()) || label.toString().toLowerCase().includes(searchTerm.toLowerCase());
122
+ return value.toString().toLowerCase().includes(searchTerm.toLowerCase()) || label2.toString().toLowerCase().includes(searchTerm.toLowerCase());
113
123
  });
114
124
  }
115
125
  const mergedBadgeProps = computed(() => ({
@@ -117,7 +127,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
117
127
  disabled: props.disabled
118
128
  }));
119
129
  return (_ctx, _cache) => {
120
- return openBlock(), createBlock(_sfc_main$1, mergeProps(unref(forwarded), { "filter-function": filterFunction }), {
130
+ return openBlock(), createBlock(_sfc_main$1, mergeProps({ class: "shrink-0" }, unref(forwarded), { "filter-function": filterFunction }), {
121
131
  default: withCtx(() => [
122
132
  createVNode(_sfc_main$2, { "as-child": "" }, {
123
133
  default: withCtx(() => [
@@ -148,8 +158,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
148
158
  renderSlot(_ctx.$slots, "prefix", { class: "celeste-dropdown-anchor-trigger-prefix-default" }, void 0, true)
149
159
  ])) : createCommentVNode("", true),
150
160
  _ctx.type !== "inline" ? (openBlock(), createElementBlock("div", _hoisted_6, [
151
- _ctx.multiple ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(_ctx.placeholder), 1)) : (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(model.value && !Array.isArray(model.value) ? model.value.label : props.placeholder), 1)),
152
- mergedBadgeProps.value && _ctx.badgeProps ? (openBlock(), createElementBlock("span", _hoisted_9, [
161
+ createElementVNode("span", null, toDisplayString(label.value), 1),
162
+ mergedBadgeProps.value && _ctx.badgeProps ? (openBlock(), createElementBlock("span", _hoisted_7, [
153
163
  createVNode(Badge, normalizeProps(guardReactiveProps(mergedBadgeProps.value)), null, 16)
154
164
  ])) : createCommentVNode("", true)
155
165
  ])) : createCommentVNode("", true),
@@ -201,7 +211,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
201
211
  src: option.image,
202
212
  alt: "option",
203
213
  class: "celeste-dropdown-item-image"
204
- }, null, 8, _hoisted_10)) : createCommentVNode("", true)
214
+ }, null, 8, _hoisted_8)) : createCommentVNode("", true)
205
215
  ]),
206
216
  _: 2
207
217
  }, 1032, ["label", "sublabel", "description", "checkbox", "value", "selected"]);
@@ -218,7 +228,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
218
228
  };
219
229
  }
220
230
  });
221
- const combobox = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-268c7202"]]);
231
+ const combobox = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cad50435"]]);
222
232
  export {
223
233
  combobox as default
224
234
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@youcan/celeste",
3
3
  "type": "module",
4
- "version": "0.6.23",
4
+ "version": "0.6.24",
5
5
  "author": "YouCan <tech@youcan.shop>",
6
6
  "keywords": [
7
7
  "youcan",
@@ -32,8 +32,8 @@
32
32
  "clsx": "^2.1.1",
33
33
  "radix-vue": "^1.9.12",
34
34
  "tinycolor2": "^1.6.0",
35
- "@youcan/celeste-icons": "0.6.23",
36
- "@youcan/celeste-tokens": "0.6.23"
35
+ "@youcan/celeste-icons": "0.6.24",
36
+ "@youcan/celeste-tokens": "0.6.24"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@chromatic-com/storybook": "^4.0.0",
@@ -6,10 +6,9 @@ import { computed, type HTMLAttributes } from 'vue';
6
6
 
7
7
  const props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {
8
8
  position: 'popper',
9
- align: 'center',
9
+ align: 'start',
10
10
  sideOffset: 4,
11
11
  });
12
-
13
12
  const emits = defineEmits<ComboboxContentEmits>();
14
13
 
15
14
  const delegatedProps = computed(() => {
@@ -25,7 +24,6 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
25
24
  <ComboboxPortal>
26
25
  <ComboboxContent
27
26
  v-bind="forwarded"
28
- force-mount
29
27
  :class="clsx('celeste-dropdown-content', props.class)"
30
28
  >
31
29
  <ComboboxViewport class="celeste-dropdown-items-viewport">
@@ -38,43 +36,65 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
38
36
  <style lang="scss" scoped>
39
37
  .celeste-dropdown-content,
40
38
  div:deep(.celeste-dropdown-content) {
41
- --dropdown-width: 100%;
39
+ --dropdown-width: fit-content;
42
40
  --dropdown-height: 300px;
41
+ --dropdown-min-width: var(--radix-combobox-trigger-width, 300px);
43
42
 
44
43
  display: flex;
45
44
  z-index: 50;
46
45
  box-sizing: border-box;
47
46
  flex-direction: column;
48
47
  width: var(--dropdown-width);
49
- min-width: 300px;
48
+ min-width: var(--dropdown-min-width);
50
49
  max-height: var(--dropdown-height);
51
50
  margin-top: var(--spacing-10);
52
51
  padding: var(--spacing-8);
53
- overflow: hidden;
54
- transition-property: background-color, transform, opacity;
55
- transition-duration: var(--animation-fast);
56
- transition-timing-function: ease-out;
52
+ overflow: visible;
53
+ transform-origin: var(--radix-combobox-content-transform-origin);
54
+ animation-duration: var(--animation-fast);
55
+ animation-timing-function: ease-out;
57
56
  border: 1px solid var(--color-stroke-soft-200);
58
57
  border-radius: var(--radius-16);
59
58
  background-color: var(--color-bg-white-0);
60
59
  box-shadow: var(--shadow-regular-md);
61
60
  gap: var(--spacing-4);
62
61
 
62
+ &[data-state='open'] {
63
+ animation-name: slide-up-and-fade;
64
+ }
65
+
63
66
  &[data-state='closed'] {
67
+ animation-name: slide-down-and-fade;
68
+ }
69
+ }
70
+
71
+ div:deep(.celeste-dropdown-items-viewport) {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: var(--spacing-4);
75
+ }
76
+
77
+ @keyframes slide-up-and-fade {
78
+ from {
64
79
  transform: translateY(10px);
65
80
  opacity: 0;
66
- pointer-events: none;
67
81
  }
68
82
 
69
- &[data-state='open'] {
83
+ to {
70
84
  transform: translateY(0);
71
85
  opacity: 1;
72
86
  }
73
87
  }
74
88
 
75
- div:deep(.celeste-dropdown-items-viewport) {
76
- display: flex;
77
- flex-direction: column;
78
- gap: var(--spacing-4);
89
+ @keyframes slide-down-and-fade {
90
+ from {
91
+ transform: translateY(0);
92
+ opacity: 1;
93
+ }
94
+
95
+ to {
96
+ transform: translateY(10px);
97
+ opacity: 0;
98
+ }
79
99
  }
80
100
  </style>
@@ -28,6 +28,7 @@ const delegatedProps = computed(() => {
28
28
  filterFunction,
29
29
  options,
30
30
  valueBy,
31
+ labelBy,
31
32
  placeholder,
32
33
  type,
33
34
  size,
@@ -45,6 +46,24 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
45
46
 
46
47
  const model = computed(() => props.modelValue as ComboboxItemPropsType[] | ComboboxItemPropsType | undefined);
47
48
 
49
+ const label = computed(() => {
50
+ if (!model.value)
51
+ return props.placeholder;
52
+
53
+ if (Array.isArray(model.value)) {
54
+ if (model.value.length === 0)
55
+ return props.placeholder;
56
+
57
+ return model.value
58
+ .map(item => props.labelBy
59
+ ? item[props.labelBy as keyof ComboboxItemPropsType]
60
+ : item.label)
61
+ .join(', ');
62
+ }
63
+
64
+ return props.labelBy ? model.value[props.labelBy as keyof ComboboxItemPropsType] : model.value.label;
65
+ });
66
+
48
67
  function isSelected(value: ComboboxItemPropsType['value']): boolean | undefined {
49
68
  if (!Array.isArray(props.modelValue) || !props.multiple)
50
69
  return;
@@ -96,6 +115,7 @@ const mergedBadgeProps = computed(() => ({
96
115
  export interface ComboboxPropsType extends ComboboxRootProps {
97
116
  options: ComboboxItemPropsType[];
98
117
  valueBy?: string;
118
+ labelBy?: string;
99
119
  placeholder?: string;
100
120
  type?: 'normal' | 'compact' | 'inline' | 'compact-input';
101
121
  size?: 'xs' | 'sm' | 'md';
@@ -112,6 +132,7 @@ export default {
112
132
 
113
133
  <template>
114
134
  <ComboboxRoot
135
+ class="shrink-0"
115
136
  v-bind="forwarded"
116
137
  :filter-function="filterFunction"
117
138
  >
@@ -148,9 +169,8 @@ export default {
148
169
  v-if="type !== 'inline'"
149
170
  class="celeste-dropdown-input"
150
171
  >
151
- <span v-if="multiple">{{ placeholder }}</span>
152
- <span v-else>
153
- {{ model && !Array.isArray(model) ? model.label : props.placeholder }}
172
+ <span>
173
+ {{ label }}
154
174
  </span>
155
175
  <span v-if="mergedBadgeProps && badgeProps" class="celeste-input-badge">
156
176
  <Badge v-bind="mergedBadgeProps" />