@youcan/celeste 0.13.0 → 0.14.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.
Files changed (65) hide show
  1. package/ai/component-registry.json +49 -2
  2. package/ai/component-registry.md +24 -2
  3. package/ai/icons.md +1 -1
  4. package/ai/tokens.md +1 -1
  5. package/dist/celeste.css +299 -86
  6. package/dist/cjs/chunks/calendar.cjs +71 -72
  7. package/dist/cjs/chunks/combobox-root.cjs +6 -6
  8. package/dist/cjs/chunks/components.cjs +2 -0
  9. package/dist/cjs/chunks/digit-input.cjs +13 -12
  10. package/dist/cjs/chunks/dropdown-menu.cjs +9 -6
  11. package/dist/cjs/chunks/mochi.cjs +168 -0
  12. package/dist/cjs/chunks/radio-group.cjs +4 -8
  13. package/dist/cjs/chunks/range-calendar.cjs +71 -72
  14. package/dist/cjs/chunks/scroll-area.cjs +16 -17
  15. package/dist/cjs/chunks/segmented-control.cjs +20 -21
  16. package/dist/cjs/chunks/select.cjs +8 -5
  17. package/dist/cjs/chunks/slider.cjs +14 -15
  18. package/dist/cjs/chunks/tab-menu.cjs +8 -9
  19. package/dist/cjs/command-root.cjs +8 -5
  20. package/dist/cjs/index.cjs +2 -0
  21. package/dist/cjs/mochi.cjs +2 -0
  22. package/dist/cjs/use-direction.cjs +4 -4
  23. package/dist/components/index.d.ts +1 -0
  24. package/dist/components/mochi/index.d.ts +2 -0
  25. package/dist/components/mochi/mochi.d.ts +22 -0
  26. package/dist/components/radio-group/radio-group.d.ts +2 -2
  27. package/dist/components/scroll-area/scroll-area.d.ts +2 -2
  28. package/dist/components/segmented-control/segmented-control.d.ts +2 -2
  29. package/dist/components/tab-menu/tab-menu.d.ts +2 -2
  30. package/dist/composables/use-direction.d.ts +2 -2
  31. package/dist/es/chunks/calendar.js +71 -72
  32. package/dist/es/chunks/combobox-root.js +6 -6
  33. package/dist/es/chunks/components.js +2 -0
  34. package/dist/es/chunks/digit-input.js +14 -13
  35. package/dist/es/chunks/dropdown-menu.js +9 -6
  36. package/dist/es/chunks/mochi.js +162 -0
  37. package/dist/es/chunks/radio-group.js +5 -9
  38. package/dist/es/chunks/range-calendar.js +71 -72
  39. package/dist/es/chunks/scroll-area.js +16 -17
  40. package/dist/es/chunks/segmented-control.js +21 -22
  41. package/dist/es/chunks/select.js +8 -5
  42. package/dist/es/chunks/slider.js +14 -15
  43. package/dist/es/chunks/tab-menu.js +9 -10
  44. package/dist/es/command-root.js +8 -5
  45. package/dist/es/index.js +2 -1
  46. package/dist/es/mochi.js +2 -0
  47. package/dist/es/use-direction.js +5 -5
  48. package/package.json +3 -3
  49. package/src/components/calendar/calendar.vue +62 -64
  50. package/src/components/combobox/combobox-root.vue +9 -5
  51. package/src/components/command/command-root.vue +10 -9
  52. package/src/components/digit-input/digit-input.vue +10 -11
  53. package/src/components/dropdown-menu/dropdown-menu.vue +9 -8
  54. package/src/components/index.ts +1 -0
  55. package/src/components/mochi/index.ts +2 -0
  56. package/src/components/mochi/mochi.vue +480 -0
  57. package/src/components/mochi/stories/mochi.stories.ts +105 -0
  58. package/src/components/radio-group/radio-group.vue +9 -10
  59. package/src/components/range-calendar/range-calendar.vue +63 -65
  60. package/src/components/scroll-area/scroll-area.vue +14 -15
  61. package/src/components/segmented-control/segmented-control.vue +20 -21
  62. package/src/components/select/select.vue +9 -8
  63. package/src/components/slider/slider.vue +16 -17
  64. package/src/components/tab-menu/tab-menu.vue +9 -10
  65. package/src/composables/use-direction.ts +6 -6
@@ -0,0 +1,480 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import clsx from 'clsx';
4
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
5
+
6
+ type Animation = 'blink-twice' | 'blink-once' | 'look-blink-twice' | 'look-blink-once';
7
+ type PeekSide = 'bottom' | 'right' | 'top' | 'left';
8
+ type Peek = '' | 'drop' | 'hidden' | 'out' | 'home';
9
+
10
+ const props = withDefaults(
11
+ defineProps<{
12
+ class?: HTMLAttributes['class'];
13
+ size?: number;
14
+ animate?: boolean;
15
+ thinking?: boolean;
16
+ }>(),
17
+ {
18
+ size: 40,
19
+ animate: false,
20
+ thinking: false,
21
+ },
22
+ );
23
+
24
+ const idle = defineModel<boolean>('idle', { default: false });
25
+
26
+ const DROP_DURATION = 2010;
27
+ const HIDE_DURATION = 420;
28
+ const SETTLE_DURATION = 500;
29
+
30
+ const durations: Record<Animation, number> = { 'blink-twice': 440, 'blink-once': 160, 'look-blink-twice': 3000, 'look-blink-once': 2800 };
31
+ const weights: [Animation, number][] = [['blink-twice', 35], ['blink-once', 35], ['look-blink-twice', 15], ['look-blink-once', 15]];
32
+ const rotation: Record<PeekSide, string> = { bottom: '0deg', right: '-90deg', top: '180deg', left: '90deg' };
33
+ const order: PeekSide[] = ['bottom', 'right', 'top', 'left'];
34
+
35
+ const anim = ref<Animation | ''>('');
36
+ const peek = ref<Peek>('');
37
+ const peekSide = ref<PeekSide>('bottom');
38
+
39
+ const sizePx = computed(() => `${props.size}px`);
40
+ const rotate = computed(() => rotation[peekSide.value]);
41
+
42
+ let retired = false;
43
+ let frame: number | undefined;
44
+ let timer: ReturnType<typeof setTimeout> | undefined;
45
+ let peekTimer: ReturnType<typeof setTimeout> | undefined;
46
+
47
+ function pick(): Animation {
48
+ let r = Math.random() * 100;
49
+ for (const [name, weight] of weights) {
50
+ r -= weight;
51
+ if (r <= 0) {
52
+ return name;
53
+ }
54
+ }
55
+
56
+ return 'blink-twice';
57
+ }
58
+
59
+ function cycle() {
60
+ clearTimeout(timer);
61
+ cancelAnimationFrame(frame ?? 0);
62
+ anim.value = '';
63
+ if (!props.animate || peek.value) {
64
+ return;
65
+ }
66
+ frame = requestAnimationFrame(() => {
67
+ const next = pick();
68
+ anim.value = next;
69
+ timer = setTimeout(cycle, durations[next] + 800 + Math.random() * 1600);
70
+ });
71
+ }
72
+
73
+ function later(fn: () => void, ms: number) {
74
+ clearTimeout(peekTimer);
75
+ peekTimer = setTimeout(fn, ms);
76
+ }
77
+
78
+ function drop() {
79
+ peekSide.value = 'bottom';
80
+ peek.value = 'drop';
81
+ cycle();
82
+ later(() => {
83
+ peek.value = 'out';
84
+ }, DROP_DURATION);
85
+ }
86
+
87
+ function settle() {
88
+ peekSide.value = 'bottom';
89
+ peek.value = 'home';
90
+ later(() => {
91
+ peek.value = '';
92
+ cycle();
93
+ }, SETTLE_DURATION);
94
+ }
95
+
96
+ function hop() {
97
+ peek.value = 'hidden';
98
+ later(() => {
99
+ const next = order[(order.indexOf(peekSide.value) + 1) % order.length];
100
+ if (next === 'bottom') {
101
+ retired = true;
102
+ settle();
103
+ idle.value = false;
104
+
105
+ return;
106
+ }
107
+ peekSide.value = next;
108
+ peek.value = 'out';
109
+ }, HIDE_DURATION);
110
+ }
111
+
112
+ function poke() {
113
+ if (peek.value === 'out') {
114
+ hop();
115
+ }
116
+ else if (!peek.value && props.animate && !retired) {
117
+ idle.value = true;
118
+ }
119
+ }
120
+
121
+ watch(() => idle.value && props.animate, (active) => {
122
+ if (active && !retired && !peek.value) {
123
+ drop();
124
+ }
125
+ else if (!active && peek.value && peek.value !== 'home') {
126
+ peek.value = 'hidden';
127
+ later(settle, HIDE_DURATION);
128
+ }
129
+ }, { immediate: true });
130
+
131
+ watch(() => props.animate, cycle);
132
+ onMounted(cycle);
133
+ onBeforeUnmount(() => {
134
+ cancelAnimationFrame(frame ?? 0);
135
+ clearTimeout(timer);
136
+ clearTimeout(peekTimer);
137
+ });
138
+ </script>
139
+
140
+ <template>
141
+ <div
142
+ :class="clsx(
143
+ 'celeste-mochi',
144
+ peek && `celeste-mochi-peek-${peek}`,
145
+ props.thinking && 'celeste-mochi-thinking',
146
+ props.class,
147
+ )"
148
+ :data-anim="anim || undefined"
149
+ aria-hidden="true"
150
+ >
151
+ <div class="celeste-mochi-stage">
152
+ <div class="celeste-mochi-chan">
153
+ <div class="celeste-mochi-body" @click="poke" />
154
+ <div class="celeste-mochi-eyes">
155
+ <div class="celeste-mochi-eye celeste-mochi-eye-l" />
156
+ <div class="celeste-mochi-eye celeste-mochi-eye-r" />
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </template>
162
+
163
+ <style scoped lang="scss">
164
+ .celeste-mochi {
165
+ --mochi-size: v-bind('sizePx');
166
+ --mochi-rotate: v-bind('rotate');
167
+
168
+ position: relative;
169
+ flex: none;
170
+ width: var(--mochi-size);
171
+ height: var(--mochi-size);
172
+ overflow: hidden;
173
+ border-radius: calc(var(--mochi-size) * 0.2222);
174
+ background: linear-gradient(180deg, var(--color-brand-300) 0%, #f90171 100%);
175
+ isolation: isolate;
176
+ }
177
+
178
+ .celeste-mochi::after {
179
+ content: '';
180
+ position: absolute;
181
+ inset: 0;
182
+ border-radius: inherit;
183
+ box-shadow: inset 0 0 0 max(1px, calc(var(--mochi-size) * 0.0278)) var(--color-brand-200);
184
+ pointer-events: none;
185
+ }
186
+
187
+ .celeste-mochi > * {
188
+ position: absolute;
189
+ }
190
+
191
+ .celeste-mochi-stage {
192
+ inset: 0;
193
+ transform: rotate(var(--mochi-rotate));
194
+ }
195
+
196
+ .celeste-mochi-chan {
197
+ position: absolute;
198
+ top: 24%;
199
+ left: -15%;
200
+ width: 129.4%;
201
+ height: 140.9%;
202
+ transition: transform 0.38s cubic-bezier(0.3, -0.4, 0.7, 0.2);
203
+ }
204
+
205
+ .celeste-mochi-body {
206
+ position: absolute;
207
+ inset: 0;
208
+ border-radius: 50%;
209
+ background: var(--color-brand-400);
210
+ box-shadow:
211
+ inset 0 calc(var(--mochi-size) * 0.242) calc(var(--mochi-size) * 0.4015) rgb(255 255 255 / 80%),
212
+ inset 0 calc(var(--mochi-size) * 0.363) calc(var(--mochi-size) * 0.2826) var(--color-brand-300);
213
+ }
214
+
215
+ .celeste-mochi-eyes {
216
+ --eye-top: 12.8%;
217
+ --eye-h: 19.4%;
218
+
219
+ position: absolute;
220
+ transform-origin: 50% calc(var(--eye-top) + var(--eye-h) / 2);
221
+ transition: transform 0.7s ease-in-out;
222
+ pointer-events: none;
223
+ inset: 0;
224
+ }
225
+
226
+ .celeste-mochi-eye {
227
+ position: absolute;
228
+ top: var(--eye-top);
229
+ width: 13.6%;
230
+ height: var(--eye-h);
231
+ overflow: hidden;
232
+ border-radius: var(--radius-full);
233
+ background: linear-gradient(180deg, var(--color-static-white) 0%, rgb(255 255 255 / 60%) 100%);
234
+ filter: drop-shadow(0 calc(var(--mochi-size) * 0.0305) calc(var(--mochi-size) * 0.0153) var(--color-pink-500));
235
+ }
236
+
237
+ .celeste-mochi-eye-l {
238
+ left: 31.5%;
239
+ }
240
+
241
+ .celeste-mochi-eye-r {
242
+ right: 31.5%;
243
+ }
244
+
245
+ .celeste-mochi-peek-drop .celeste-mochi-eye,
246
+ .celeste-mochi-peek-hidden .celeste-mochi-eye,
247
+ .celeste-mochi-peek-out .celeste-mochi-eye {
248
+ height: calc(var(--eye-h) * 80 / 112);
249
+ border-bottom-right-radius: 0;
250
+ border-bottom-left-radius: 0;
251
+ }
252
+
253
+ .celeste-mochi-peek-drop .celeste-mochi-eye {
254
+ animation: celeste-mochi-squint 0.25s ease-in-out 1s both;
255
+ }
256
+
257
+ .celeste-mochi[data-anim='blink-twice'] .celeste-mochi-eye,
258
+ .celeste-mochi[data-anim='look-blink-twice'] .celeste-mochi-eye {
259
+ animation: celeste-mochi-blink-twice 3.5s linear both;
260
+ }
261
+
262
+ .celeste-mochi[data-anim='blink-once'] .celeste-mochi-eye,
263
+ .celeste-mochi[data-anim='look-blink-once'] .celeste-mochi-eye {
264
+ animation: celeste-mochi-blink-once 3.5s linear both;
265
+ }
266
+
267
+ .celeste-mochi[data-anim='look-blink-twice'] .celeste-mochi-eyes {
268
+ animation: celeste-mochi-look-after-blink-twice 3.5s linear both;
269
+ }
270
+
271
+ .celeste-mochi[data-anim='look-blink-once'] .celeste-mochi-eyes {
272
+ animation: celeste-mochi-look-after-blink-once 3.5s linear both;
273
+ }
274
+
275
+ .celeste-mochi-thinking .celeste-mochi-eyes {
276
+ transform: translate(3.77%, -3.118%) rotate(-7deg);
277
+ transition: transform 0.6s ease-out;
278
+ }
279
+
280
+ .celeste-mochi-thinking .celeste-mochi-eye {
281
+ animation: celeste-mochi-blink-twice 3.5s linear 1s infinite;
282
+ }
283
+
284
+ .celeste-mochi-peek-drop .celeste-mochi-chan {
285
+ animation: celeste-mochi-drop 2.01s linear both;
286
+ }
287
+
288
+ .celeste-mochi-peek-hidden .celeste-mochi-chan {
289
+ transform: translateY(calc(var(--mochi-size) * 0.754));
290
+ }
291
+
292
+ .celeste-mochi-peek-out .celeste-mochi-chan {
293
+ transform: translateY(calc(var(--mochi-size) * 0.295));
294
+ transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
295
+ }
296
+
297
+ .celeste-mochi-peek-home .celeste-mochi-chan {
298
+ transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
299
+ }
300
+
301
+ @media (prefers-reduced-motion: reduce) {
302
+ .celeste-mochi * {
303
+ transition-duration: 1ms !important;
304
+ animation-duration: 1ms !important;
305
+ animation-delay: 0s !important;
306
+ animation-iteration-count: 1 !important;
307
+ }
308
+ }
309
+
310
+ @keyframes celeste-mochi-blink-twice {
311
+ 0%,
312
+ 4.57%,
313
+ 8% {
314
+ top: var(--eye-top);
315
+ height: var(--eye-h);
316
+ animation-timing-function: ease-in;
317
+ }
318
+
319
+ 2.29%,
320
+ 10.29% {
321
+ top: calc(var(--eye-top) + var(--eye-h) / 2);
322
+ height: 0;
323
+ animation-timing-function: ease-out;
324
+ }
325
+
326
+ 12.57%,
327
+ 100% {
328
+ top: var(--eye-top);
329
+ height: var(--eye-h);
330
+ }
331
+ }
332
+
333
+ @keyframes celeste-mochi-blink-once {
334
+ 0% {
335
+ top: var(--eye-top);
336
+ height: var(--eye-h);
337
+ animation-timing-function: ease-in;
338
+ }
339
+
340
+ 2.29% {
341
+ top: calc(var(--eye-top) + var(--eye-h) / 2);
342
+ height: 0;
343
+ animation-timing-function: ease-out;
344
+ }
345
+
346
+ 4.57%,
347
+ 100% {
348
+ top: var(--eye-top);
349
+ height: var(--eye-h);
350
+ }
351
+ }
352
+
353
+ @keyframes celeste-mochi-look-after-blink-twice {
354
+ 0%,
355
+ 14.29% {
356
+ transform: translate(0, 0);
357
+ animation-timing-function: ease-out;
358
+ }
359
+
360
+ 22.86%,
361
+ 42.86% {
362
+ transform: translate(3.393%, 1.386%);
363
+ animation-timing-function: ease-in-out;
364
+ }
365
+
366
+ 54.29%,
367
+ 74.29% {
368
+ transform: translate(-3.393%, 1.386%);
369
+ animation-timing-function: ease-in-out;
370
+ }
371
+
372
+ 85.71%,
373
+ 100% {
374
+ transform: translate(0, 0);
375
+ }
376
+ }
377
+
378
+ @keyframes celeste-mochi-look-after-blink-once {
379
+ 0%,
380
+ 8.57% {
381
+ transform: translate(0, 0);
382
+ animation-timing-function: ease-out;
383
+ }
384
+
385
+ 17.14%,
386
+ 37.14% {
387
+ transform: translate(3.393%, 1.386%);
388
+ animation-timing-function: ease-in-out;
389
+ }
390
+
391
+ 48.57%,
392
+ 68.57% {
393
+ transform: translate(-3.393%, 1.386%);
394
+ animation-timing-function: ease-in-out;
395
+ }
396
+
397
+ 80%,
398
+ 100% {
399
+ transform: translate(0, 0);
400
+ }
401
+ }
402
+
403
+ @keyframes celeste-mochi-squint {
404
+ from {
405
+ height: var(--eye-h);
406
+ border-bottom-right-radius: var(--radius-full);
407
+ border-bottom-left-radius: var(--radius-full);
408
+ }
409
+
410
+ to {
411
+ height: calc(var(--eye-h) * 80 / 112);
412
+ border-bottom-right-radius: 0;
413
+ border-bottom-left-radius: 0;
414
+ }
415
+ }
416
+
417
+ @keyframes celeste-mochi-drop {
418
+ 0% {
419
+ transform: translateY(0);
420
+ animation-timing-function: cubic-bezier(0.43, -0.3, 0.74, 0.33);
421
+ }
422
+
423
+ 21.4% {
424
+ transform: translateY(calc(var(--mochi-size) * -0.0082));
425
+ animation-timing-function: cubic-bezier(0.3, -0.05, 0.7, -0.5);
426
+ }
427
+
428
+ 44.28%,
429
+ 68.15% {
430
+ transform: translateY(calc(var(--mochi-size) * 0.754));
431
+ animation-timing-function: linear(
432
+ 0,
433
+ 0.048,
434
+ 0.168,
435
+ 0.326,
436
+ 0.497,
437
+ 0.66,
438
+ 0.804,
439
+ 0.921,
440
+ 1.009,
441
+ 1.068,
442
+ 1.103,
443
+ 1.118,
444
+ 1.118,
445
+ 1.107,
446
+ 1.089,
447
+ 1.069,
448
+ 1.049,
449
+ 1.031,
450
+ 1.015,
451
+ 1.003,
452
+ 0.995,
453
+ 0.989,
454
+ 0.986,
455
+ 0.986,
456
+ 0.987,
457
+ 0.988,
458
+ 0.991,
459
+ 0.993,
460
+ 0.995,
461
+ 0.997,
462
+ 0.999,
463
+ 1,
464
+ 1.001,
465
+ 1.002,
466
+ 1.002,
467
+ 1.002,
468
+ 1.001,
469
+ 1.001,
470
+ 1.001,
471
+ 1.001,
472
+ 1
473
+ );
474
+ }
475
+
476
+ 100% {
477
+ transform: translateY(calc(var(--mochi-size) * 0.295));
478
+ }
479
+ }
480
+ </style>
@@ -0,0 +1,105 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import { ref } from 'vue';
3
+ import Mochi from '../mochi.vue';
4
+
5
+ const meta: Meta<typeof Mochi> = {
6
+ title: 'Components/Mochi',
7
+ component: Mochi,
8
+ argTypes: {
9
+ size: { control: { type: 'number', min: 16, max: 240, step: 4 } },
10
+ },
11
+ };
12
+
13
+ export default meta;
14
+
15
+ type Story = StoryObj<typeof Mochi>;
16
+
17
+ export const Default: Story = {
18
+ args: {
19
+ size: 80,
20
+ animate: true,
21
+ },
22
+ render: args => ({
23
+ components: { Mochi },
24
+ setup() {
25
+ return { args };
26
+ },
27
+ template: `<Mochi v-bind="args" />`,
28
+ }),
29
+ };
30
+
31
+ export const Static: Story = {
32
+ name: 'Static (Motionless)',
33
+ args: {
34
+ size: 80,
35
+ animate: false,
36
+ },
37
+ render: args => ({
38
+ components: { Mochi },
39
+ setup() {
40
+ return { args };
41
+ },
42
+ template: `<Mochi v-bind="args" />`,
43
+ }),
44
+ };
45
+
46
+ export const Sizes: Story = {
47
+ args: {
48
+ animate: true,
49
+ },
50
+ render: args => ({
51
+ components: { Mochi },
52
+ setup() {
53
+ return { args };
54
+ },
55
+ template: `
56
+ <div style="display: flex; align-items: center; gap: 16px;">
57
+ <Mochi v-bind="args" :size="28" />
58
+ <Mochi v-bind="args" :size="40" />
59
+ <Mochi v-bind="args" :size="80" />
60
+ <Mochi v-bind="args" :size="120" />
61
+ </div>
62
+ `,
63
+ }),
64
+ };
65
+
66
+ export const Thinking: Story = {
67
+ args: {
68
+ size: 80,
69
+ thinking: true,
70
+ },
71
+ render: args => ({
72
+ components: { Mochi },
73
+ setup() {
74
+ return { args };
75
+ },
76
+ template: `<Mochi v-bind="args" />`,
77
+ }),
78
+ };
79
+
80
+ export const Idle: Story = {
81
+ name: 'Idle (Peek-a-boo)',
82
+ args: {
83
+ size: 80,
84
+ animate: true,
85
+ },
86
+ render: args => ({
87
+ components: { Mochi },
88
+ setup() {
89
+ const idle = ref(false);
90
+
91
+ return { args, idle };
92
+ },
93
+ template: `
94
+ <div style="display: flex; flex-direction: column; align-items: flex-start; gap: 12px;">
95
+ <Mochi v-bind="args" v-model:idle="idle" />
96
+ <p style="font: var(--paragraph-sm); color: var(--color-text-sub-600);">
97
+ idle: {{ idle }} — click Mochi to start peeking, then click again to make it hop.
98
+ </p>
99
+ <button type="button" style="font: var(--label-sm);" @click="idle = !idle">
100
+ Toggle idle
101
+ </button>
102
+ </div>
103
+ `,
104
+ }),
105
+ };
@@ -2,7 +2,7 @@
2
2
  import type { RadioGroupRootEmits, RadioGroupRootProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import clsx from 'clsx';
5
- import { ConfigProvider, RadioGroupRoot, useForwardPropsEmits } from 'reka-ui';
5
+ import { RadioGroupRoot, useForwardPropsEmits } from 'reka-ui';
6
6
  import { useDelegatedProps } from '@/composables/use-delegated-props';
7
7
  import { useDirection } from '@/composables/use-direction';
8
8
 
@@ -12,18 +12,17 @@ const emits = defineEmits<RadioGroupRootEmits>();
12
12
  const delegatedProps = useDelegatedProps(props, 'class');
13
13
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
14
14
 
15
- const direction = useDirection();
15
+ const dir = useDirection(() => props.dir);
16
16
  </script>
17
17
 
18
18
  <template>
19
- <ConfigProvider :dir="props.dir ?? direction">
20
- <RadioGroupRoot
21
- :class="clsx('celeste-radio-wrapper', props.class)"
22
- v-bind="forwarded"
23
- >
24
- <slot />
25
- </RadioGroupRoot>
26
- </ConfigProvider>
19
+ <RadioGroupRoot
20
+ :class="clsx('celeste-radio-wrapper', props.class)"
21
+ v-bind="forwarded"
22
+ :dir="dir"
23
+ >
24
+ <slot />
25
+ </RadioGroupRoot>
27
26
  </template>
28
27
 
29
28
  <style lang="scss">