@youcan/celeste 0.11.0 → 0.13.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 (116) hide show
  1. package/ai/component-registry.json +30 -2
  2. package/ai/component-registry.md +5 -1
  3. package/ai/icons.md +1 -1
  4. package/ai/tokens.md +1 -1
  5. package/dist/celeste.css +311 -218
  6. package/dist/cjs/chunks/ConfigProvider.cjs +58 -1
  7. package/dist/cjs/chunks/calendar.cjs +73 -67
  8. package/dist/cjs/chunks/combobox-root.cjs +12 -5
  9. package/dist/cjs/chunks/digit-input.cjs +13 -7
  10. package/dist/cjs/chunks/dist2.cjs +6 -0
  11. package/dist/cjs/chunks/dropdown-menu.cjs +10 -4
  12. package/dist/cjs/chunks/fancy-button.cjs +1 -1
  13. package/dist/cjs/chunks/radio-group.cjs +9 -3
  14. package/dist/cjs/chunks/range-calendar.cjs +73 -67
  15. package/dist/cjs/chunks/scroll-area.cjs +18 -12
  16. package/dist/cjs/chunks/scroll-bar.cjs +2 -0
  17. package/dist/cjs/chunks/segmented-control-item.cjs +1 -1
  18. package/dist/cjs/chunks/segmented-control.cjs +25 -4
  19. package/dist/cjs/chunks/select.cjs +9 -3
  20. package/dist/cjs/chunks/slider.cjs +16 -10
  21. package/dist/cjs/chunks/tab-menu-item.cjs +6 -2
  22. package/dist/cjs/chunks/tab-menu-list.cjs +53 -9
  23. package/dist/cjs/chunks/tab-menu.cjs +12 -5
  24. package/dist/cjs/chunks/text-input.cjs +1 -1
  25. package/dist/cjs/command-root.cjs +9 -3
  26. package/dist/cjs/index.cjs +4 -0
  27. package/dist/cjs/use-direction.cjs +31 -0
  28. package/dist/cjs/use-tab-observer.cjs +42 -0
  29. package/dist/components/combobox/combobox-root.d.ts +2 -2
  30. package/dist/components/dropdown-menu/dropdown-menu.d.ts +2 -2
  31. package/dist/components/radio-group/radio-group.d.ts +2 -2
  32. package/dist/components/scroll-area/scroll-area.d.ts +2 -2
  33. package/dist/components/segmented-control/segmented-control.d.ts +2 -2
  34. package/dist/components/select/select.d.ts +2 -2
  35. package/dist/components/tab-menu/tab-menu-list.d.ts +14 -5
  36. package/dist/components/tab-menu/tab-menu.d.ts +2 -2
  37. package/dist/composables/index.d.ts +2 -0
  38. package/dist/composables/use-direction.d.ts +3 -0
  39. package/dist/composables/use-tab-observer.d.ts +14 -0
  40. package/dist/es/chunks/BubbleSelect.js +1 -1
  41. package/dist/es/chunks/CollapsibleRoot.js +1 -1
  42. package/dist/es/chunks/ComboboxInput.js +1 -1
  43. package/dist/es/chunks/ComboboxRoot.js +1 -1
  44. package/dist/es/chunks/ConfigProvider.js +53 -2
  45. package/dist/es/chunks/DialogRoot.js +1 -1
  46. package/dist/es/chunks/DropdownMenuRoot.js +1 -1
  47. package/dist/es/chunks/FocusScope.js +1 -1
  48. package/dist/es/chunks/MenuRoot.js +1 -1
  49. package/dist/es/chunks/MenuSub.js +1 -1
  50. package/dist/es/chunks/PaginationRoot.js +1 -1
  51. package/dist/es/chunks/PinInputRoot.js +1 -1
  52. package/dist/es/chunks/PopoverRoot.js +1 -1
  53. package/dist/es/chunks/ProgressIndicator.js +1 -1
  54. package/dist/es/chunks/RadioGroupRoot.js +1 -1
  55. package/dist/es/chunks/RovingFocusGroup.js +1 -1
  56. package/dist/es/chunks/SelectItemAlignedPosition.js +1 -1
  57. package/dist/es/chunks/TabsRoot.js +1 -1
  58. package/dist/es/chunks/Teleport.js +1 -1
  59. package/dist/es/chunks/ToggleGroupRoot.js +1 -1
  60. package/dist/es/chunks/TooltipRoot.js +1 -1
  61. package/dist/es/chunks/bar-chart.js +1 -1
  62. package/dist/es/chunks/calendar.js +74 -68
  63. package/dist/es/chunks/checkbox.js +1 -1
  64. package/dist/es/chunks/combobox-root.js +13 -6
  65. package/dist/es/chunks/counter-input.js +1 -1
  66. package/dist/es/chunks/digit-input.js +14 -8
  67. package/dist/es/chunks/dist2.js +1 -1
  68. package/dist/es/chunks/dropdown-menu-sub.js +1 -1
  69. package/dist/es/chunks/dropdown-menu.js +11 -5
  70. package/dist/es/chunks/fancy-button.js +1 -1
  71. package/dist/es/chunks/radio-group-item.js +1 -1
  72. package/dist/es/chunks/radio-group.js +10 -4
  73. package/dist/es/chunks/range-calendar.js +74 -68
  74. package/dist/es/chunks/scroll-area.js +19 -13
  75. package/dist/es/chunks/scroll-bar.js +3 -1
  76. package/dist/es/chunks/segmented-control-item.js +2 -2
  77. package/dist/es/chunks/segmented-control.js +27 -5
  78. package/dist/es/chunks/select.js +10 -4
  79. package/dist/es/chunks/sidebar-provider.js +1 -1
  80. package/dist/es/chunks/slider.js +17 -11
  81. package/dist/es/chunks/switch.js +1 -1
  82. package/dist/es/chunks/tab-menu-item.js +6 -2
  83. package/dist/es/chunks/tab-menu-list.js +56 -10
  84. package/dist/es/chunks/tab-menu.js +13 -6
  85. package/dist/es/chunks/text-input.js +1 -1
  86. package/dist/es/chunks/useDirection.js +1 -1
  87. package/dist/es/chunks/useId.js +1 -1
  88. package/dist/es/chunks/useLocale.js +1 -1
  89. package/dist/es/chunks/useNonce.js +1 -1
  90. package/dist/es/command-root.js +10 -4
  91. package/dist/es/index.js +3 -1
  92. package/dist/es/use-direction.js +30 -0
  93. package/dist/es/use-tab-observer.js +41 -0
  94. package/package.json +3 -3
  95. package/src/components/button/fancy-button.vue +14 -4
  96. package/src/components/calendar/calendar.vue +66 -60
  97. package/src/components/combobox/combobox-root.vue +9 -4
  98. package/src/components/command/command-root.vue +13 -8
  99. package/src/components/digit-input/digit-input.vue +13 -8
  100. package/src/components/dropdown-menu/dropdown-menu.vue +12 -7
  101. package/src/components/radio-group/radio-group.vue +12 -7
  102. package/src/components/range-calendar/range-calendar.vue +67 -61
  103. package/src/components/scroll-area/scroll-area.vue +17 -18
  104. package/src/components/segmented-control/segmented-control-item.vue +3 -3
  105. package/src/components/segmented-control/segmented-control.vue +48 -8
  106. package/src/components/select/select.vue +12 -7
  107. package/src/components/slider/slider.vue +19 -14
  108. package/src/components/switch/switch.vue +19 -18
  109. package/src/components/tab-menu/stories/tab-menu.stories.ts +39 -0
  110. package/src/components/tab-menu/tab-menu-item.vue +27 -28
  111. package/src/components/tab-menu/tab-menu-list.vue +85 -3
  112. package/src/components/tab-menu/tab-menu.vue +12 -4
  113. package/src/components/text-input/text-input.vue +11 -29
  114. package/src/composables/index.ts +2 -0
  115. package/src/composables/use-direction.ts +40 -0
  116. package/src/composables/use-tab-observer.ts +50 -0
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from 'vue';
4
4
  import { DateFormatter, getLocalTimeZone, isEqualDay } from '@internationalized/date';
5
5
  import clsx from 'clsx';
6
6
  import {
7
+ ConfigProvider,
7
8
  RangeCalendarCell,
8
9
  RangeCalendarCellTrigger,
9
10
  RangeCalendarGrid,
@@ -18,6 +19,7 @@ import {
18
19
  } from 'reka-ui';
19
20
  import { computed } from 'vue';
20
21
  import { useDelegatedProps } from '@/composables/use-delegated-props';
22
+ import { useDirection } from '@/composables/use-direction';
21
23
 
22
24
  export interface RangeCalendarProps extends RangeCalendarRootProps {
23
25
  class?: HTMLAttributes['class'];
@@ -36,6 +38,8 @@ const emits = defineEmits<RangeCalendarRootEmits>();
36
38
  const delegatedProps = useDelegatedProps(props, ['class', 'markedDates']);
37
39
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
38
40
 
41
+ const direction = useDirection();
42
+
39
43
  const monthFormatter = computed(
40
44
  () => new DateFormatter(props.locale ?? 'en-US', { month: 'long', year: 'numeric' }),
41
45
  );
@@ -54,70 +58,72 @@ function weekdayLabel(day: string): string {
54
58
  </script>
55
59
 
56
60
  <template>
57
- <RangeCalendarRoot
58
- v-slot="{ weekDays, grid }"
59
- v-bind="forwarded"
60
- :class="clsx('celeste-range-calendar', props.class)"
61
- >
62
- <div class="celeste-range-calendar-months">
63
- <div
64
- v-for="month in grid"
65
- :key="month.value.toString()"
66
- class="celeste-range-calendar-month"
67
- >
68
- <div class="celeste-range-calendar-header">
69
- <RangeCalendarPrev as-child>
70
- <button type="button" class="celeste-range-calendar-nav">
71
- <i class="i-celeste-arrow-left-s-line" />
72
- </button>
73
- </RangeCalendarPrev>
74
- <span class="celeste-range-calendar-heading">{{ formatMonth(month.value) }}</span>
75
- <RangeCalendarNext as-child>
76
- <button type="button" class="celeste-range-calendar-nav">
77
- <i class="i-celeste-arrow-right-s-line" />
78
- </button>
79
- </RangeCalendarNext>
80
- </div>
81
-
82
- <RangeCalendarGrid class="celeste-range-calendar-grid">
83
- <RangeCalendarGridHead>
84
- <RangeCalendarGridRow class="celeste-range-calendar-week">
85
- <RangeCalendarHeadCell
86
- v-for="day in weekDays"
87
- :key="day"
88
- class="celeste-range-calendar-weekday"
89
- >
90
- {{ weekdayLabel(day) }}
91
- </RangeCalendarHeadCell>
92
- </RangeCalendarGridRow>
93
- </RangeCalendarGridHead>
94
- <RangeCalendarGridBody class="celeste-range-calendar-body">
95
- <RangeCalendarGridRow
96
- v-for="(weekDates, index) in month.rows"
97
- :key="`week-${index}`"
98
- class="celeste-range-calendar-week"
99
- >
100
- <RangeCalendarCell
101
- v-for="weekDate in weekDates"
102
- :key="weekDate.toString()"
103
- :date="weekDate"
104
- class="celeste-range-calendar-cell"
61
+ <ConfigProvider :dir="props.dir ?? direction">
62
+ <RangeCalendarRoot
63
+ v-slot="{ weekDays, grid }"
64
+ v-bind="forwarded"
65
+ :class="clsx('celeste-range-calendar', props.class)"
66
+ >
67
+ <div class="celeste-range-calendar-months">
68
+ <div
69
+ v-for="month in grid"
70
+ :key="month.value.toString()"
71
+ class="celeste-range-calendar-month"
72
+ >
73
+ <div class="celeste-range-calendar-header">
74
+ <RangeCalendarPrev as-child>
75
+ <button type="button" class="celeste-range-calendar-nav">
76
+ <i class="i-celeste-arrow-left-s-line" />
77
+ </button>
78
+ </RangeCalendarPrev>
79
+ <span class="celeste-range-calendar-heading">{{ formatMonth(month.value) }}</span>
80
+ <RangeCalendarNext as-child>
81
+ <button type="button" class="celeste-range-calendar-nav">
82
+ <i class="i-celeste-arrow-right-s-line" />
83
+ </button>
84
+ </RangeCalendarNext>
85
+ </div>
86
+
87
+ <RangeCalendarGrid class="celeste-range-calendar-grid">
88
+ <RangeCalendarGridHead>
89
+ <RangeCalendarGridRow class="celeste-range-calendar-week">
90
+ <RangeCalendarHeadCell
91
+ v-for="day in weekDays"
92
+ :key="day"
93
+ class="celeste-range-calendar-weekday"
94
+ >
95
+ {{ weekdayLabel(day) }}
96
+ </RangeCalendarHeadCell>
97
+ </RangeCalendarGridRow>
98
+ </RangeCalendarGridHead>
99
+ <RangeCalendarGridBody class="celeste-range-calendar-body">
100
+ <RangeCalendarGridRow
101
+ v-for="(weekDates, index) in month.rows"
102
+ :key="`week-${index}`"
103
+ class="celeste-range-calendar-week"
105
104
  >
106
- <RangeCalendarCellTrigger
107
- :day="weekDate"
108
- :month="month.value"
109
- class="celeste-range-calendar-day"
110
- :data-marked="isMarked(weekDate) ? '' : undefined"
105
+ <RangeCalendarCell
106
+ v-for="weekDate in weekDates"
107
+ :key="weekDate.toString()"
108
+ :date="weekDate"
109
+ class="celeste-range-calendar-cell"
111
110
  >
112
- <span class="celeste-range-calendar-day-number">{{ weekDate.day }}</span>
113
- </RangeCalendarCellTrigger>
114
- </RangeCalendarCell>
115
- </RangeCalendarGridRow>
116
- </RangeCalendarGridBody>
117
- </RangeCalendarGrid>
111
+ <RangeCalendarCellTrigger
112
+ :day="weekDate"
113
+ :month="month.value"
114
+ class="celeste-range-calendar-day"
115
+ :data-marked="isMarked(weekDate) ? '' : undefined"
116
+ >
117
+ <span class="celeste-range-calendar-day-number">{{ weekDate.day }}</span>
118
+ </RangeCalendarCellTrigger>
119
+ </RangeCalendarCell>
120
+ </RangeCalendarGridRow>
121
+ </RangeCalendarGridBody>
122
+ </RangeCalendarGrid>
123
+ </div>
118
124
  </div>
119
- </div>
120
- </RangeCalendarRoot>
125
+ </RangeCalendarRoot>
126
+ </ConfigProvider>
121
127
  </template>
122
128
 
123
129
  <style scoped lang="scss">
@@ -2,32 +2,31 @@
2
2
  import type { ScrollAreaRootProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import clsx from 'clsx';
5
- import {
6
- ScrollAreaCorner,
7
- ScrollAreaRoot,
8
-
9
- ScrollAreaViewport,
10
- } from 'reka-ui';
11
- import { useDelegatedProps } from '@/composables';
5
+ import { ConfigProvider, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaViewport } from 'reka-ui';
6
+ import { useDelegatedProps, useDirection } from '@/composables';
12
7
  import ScrollBar from './scroll-bar.vue';
13
8
 
14
9
  const props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes['class'] }>();
15
10
 
16
11
  const delegatedProps = useDelegatedProps(props, 'class');
12
+
13
+ const direction = useDirection();
17
14
  </script>
18
15
 
19
16
  <template>
20
- <ScrollAreaRoot
21
- v-bind="delegatedProps"
22
- :class="clsx('celeste-scroll-area', props.class)"
23
- >
24
- <ScrollAreaViewport class="celeste-scroll-area-viewport">
25
- <slot />
26
- </ScrollAreaViewport>
27
- <ScrollBar />
28
- <ScrollBar orientation="horizontal" />
29
- <ScrollAreaCorner />
30
- </ScrollAreaRoot>
17
+ <ConfigProvider :dir="props.dir ?? direction">
18
+ <ScrollAreaRoot
19
+ v-bind="delegatedProps"
20
+ :class="clsx('celeste-scroll-area', props.class)"
21
+ >
22
+ <ScrollAreaViewport class="celeste-scroll-area-viewport">
23
+ <slot />
24
+ </ScrollAreaViewport>
25
+ <ScrollBar />
26
+ <ScrollBar orientation="horizontal" />
27
+ <ScrollAreaCorner />
28
+ </ScrollAreaRoot>
29
+ </ConfigProvider>
31
30
  </template>
32
31
 
33
32
  <style scoped lang="scss">
@@ -30,12 +30,14 @@ function onKeydown(e: KeyboardEvent) {
30
30
  <style scoped>
31
31
  .celeste-segmented-control-item {
32
32
  display: flex;
33
+ position: relative;
34
+ z-index: 1;
33
35
  align-items: center;
34
36
  gap: var(--spacing-6);
35
37
  justify-content: center;
36
38
  width: 100%;
37
39
  padding: var(--spacing-4);
38
- transition: all var(--animation-fast) ease-out;
40
+ transition: color var(--animation-normal) ease-out;
39
41
  border: none;
40
42
  border-radius: var(--radius-6);
41
43
  background-color: transparent;
@@ -44,8 +46,6 @@ function onKeydown(e: KeyboardEvent) {
44
46
  cursor: pointer;
45
47
 
46
48
  &[data-state='on'] {
47
- background-color: var(--color-bg-white-0);
48
- box-shadow: var(--shadow-toggle-switch);
49
49
  color: var(--color-text-strong-950);
50
50
  pointer-events: none;
51
51
  }
@@ -2,30 +2,50 @@
2
2
  import type { ToggleGroupRootEmits, ToggleGroupRootProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import clsx from 'clsx';
5
- import { ToggleGroupRoot as SegmentedControlRoot, useForwardPropsEmits } from 'reka-ui';
5
+ import { ConfigProvider, ToggleGroupRoot as SegmentedControlRoot, useForwardPropsEmits } from 'reka-ui';
6
6
  import { useDelegatedProps } from '@/composables/use-delegated-props';
7
+ import { useDirection } from '@/composables/use-direction';
8
+ import { useTabObserver } from '@/composables/use-tab-observer';
7
9
 
8
10
  const props = defineProps<Omit<ToggleGroupRootProps, 'type'> & { class?: HTMLAttributes['class'] }>();
9
11
  const emits = defineEmits<ToggleGroupRootEmits>();
10
12
 
11
13
  const delegatedProps = useDelegatedProps(props, 'class');
12
14
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
15
+
16
+ const direction = useDirection();
17
+
18
+ const { listRef, indicator, ready } = useTabObserver('[data-state="on"]');
13
19
  </script>
14
20
 
15
21
  <template>
16
- <SegmentedControlRoot
17
- :class="clsx('celeste-segmented-control', props.class)"
18
- v-bind="forwarded"
19
- type="single"
20
- >
21
- <slot />
22
- </SegmentedControlRoot>
22
+ <ConfigProvider :dir="props.dir ?? direction">
23
+ <SegmentedControlRoot
24
+ ref="listRef"
25
+ :class="clsx('celeste-segmented-control', props.class)"
26
+ v-bind="forwarded"
27
+ type="single"
28
+ >
29
+ <slot />
30
+ <div
31
+ :hidden="!ready"
32
+ class="celeste-segmented-control-indicator"
33
+ :style="{
34
+ width: `${indicator.width}px`,
35
+ height: `${indicator.height}px`,
36
+ transform: `translate3d(${indicator.left}px, ${indicator.top}px, 0)`,
37
+ }"
38
+ />
39
+ </SegmentedControlRoot>
40
+ </ConfigProvider>
23
41
  </template>
24
42
 
25
43
  <style scoped>
26
44
  .celeste-segmented-control {
27
45
  display: flex;
46
+ position: relative;
28
47
  gap: var(--spacing-4);
48
+ isolation: isolate;
29
49
  align-items: center;
30
50
  width: 100%;
31
51
  padding: var(--spacing-4);
@@ -36,4 +56,24 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
36
56
  flex-direction: column;
37
57
  }
38
58
  }
59
+
60
+ .celeste-segmented-control-indicator {
61
+ position: absolute;
62
+ z-index: -1;
63
+ top: 0;
64
+ left: 0;
65
+ transition-property: transform, width, height;
66
+ transition-duration: var(--animation-normal);
67
+ transition-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
68
+ border-radius: var(--radius-6);
69
+ background-color: var(--color-bg-white-0);
70
+ box-shadow: var(--shadow-toggle-switch);
71
+ pointer-events: none;
72
+ }
73
+
74
+ @media (prefers-reduced-motion: reduce) {
75
+ .celeste-segmented-control-indicator {
76
+ transition-duration: 0s;
77
+ }
78
+ }
39
79
  </style>
@@ -2,21 +2,26 @@
2
2
  import type { SelectRootEmits, SelectRootProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import clsx from 'clsx';
5
- import { SelectRoot, useForwardPropsEmits } from 'reka-ui';
5
+ import { ConfigProvider, SelectRoot, useForwardPropsEmits } from 'reka-ui';
6
6
  import { useDelegatedProps } from '@/composables/use-delegated-props';
7
+ import { useDirection } from '@/composables/use-direction';
7
8
 
8
9
  const props = defineProps<SelectRootProps & { class?: HTMLAttributes['class'] }>();
9
10
  const emits = defineEmits<SelectRootEmits>();
10
11
 
11
12
  const delegatedProps = useDelegatedProps(props, 'class');
12
13
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
14
+
15
+ const direction = useDirection();
13
16
  </script>
14
17
 
15
18
  <template>
16
- <SelectRoot
17
- v-bind="forwarded"
18
- :class="clsx('celeste-select', props.class)"
19
- >
20
- <slot />
21
- </SelectRoot>
19
+ <ConfigProvider :dir="props.dir ?? direction">
20
+ <SelectRoot
21
+ v-bind="forwarded"
22
+ :class="clsx('celeste-select', props.class)"
23
+ >
24
+ <slot />
25
+ </SelectRoot>
26
+ </ConfigProvider>
22
27
  </template>
@@ -2,31 +2,36 @@
2
2
  import type { SliderRootEmits, SliderRootProps } from 'reka-ui';
3
3
  import type { HTMLAttributes } from 'vue';
4
4
  import clsx from 'clsx';
5
- import { SliderRange, SliderRoot, SliderThumb, SliderTrack, useForwardPropsEmits } from 'reka-ui';
5
+ import { ConfigProvider, SliderRange, SliderRoot, SliderThumb, SliderTrack, useForwardPropsEmits } from 'reka-ui';
6
6
  import { useDelegatedProps } from '@/composables/use-delegated-props';
7
+ import { useDirection } from '@/composables/use-direction';
7
8
 
8
9
  const props = defineProps<Omit<SliderRootProps, 'orientation'> & { class?: HTMLAttributes['class'] }>();
9
10
  const emits = defineEmits<SliderRootEmits>();
10
11
 
11
12
  const delegatedProps = useDelegatedProps(props, 'class');
12
13
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
14
+
15
+ const direction = useDirection();
13
16
  </script>
14
17
 
15
18
  <template>
16
- <SliderRoot
17
- v-bind="forwarded"
18
- :class="clsx('celeste-slider', props.class)"
19
- >
20
- <SliderTrack class="celeste-slider-track">
21
- <SliderRange class="celeste-slider-range" />
22
- </SliderTrack>
19
+ <ConfigProvider :dir="props.dir ?? direction">
20
+ <SliderRoot
21
+ v-bind="forwarded"
22
+ :class="clsx('celeste-slider', props.class)"
23
+ >
24
+ <SliderTrack class="celeste-slider-track">
25
+ <SliderRange class="celeste-slider-range" />
26
+ </SliderTrack>
23
27
 
24
- <SliderThumb
25
- v-for="(_, key) in modelValue"
26
- :key="key"
27
- class="celeste-slider-thumb"
28
- />
29
- </SliderRoot>
28
+ <SliderThumb
29
+ v-for="(_, key) in modelValue"
30
+ :key="key"
31
+ class="celeste-slider-thumb"
32
+ />
33
+ </SliderRoot>
34
+ </ConfigProvider>
30
35
  </template>
31
36
 
32
37
  <style lang="scss">
@@ -25,39 +25,39 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
25
25
  .celeste-switch {
26
26
  --width: 26px;
27
27
  --height: 16px;
28
+ --thumb-size: 12px;
29
+ --thumb-inset: 1px;
30
+ --travel: calc(var(--width) - var(--thumb-size) - var(--thumb-inset) * 2 - 2px);
28
31
  --bg-color: var(--color-bg-soft-200);
29
- --animation-function: cubic-bezier(0.18, 0.89, 0.43, 1.19);
30
32
 
31
33
  position: relative;
34
+ box-sizing: border-box;
32
35
  width: var(--width);
33
36
  height: var(--height);
34
- transition: background var(--animation-fast) ease-out;
37
+ transition: background-color var(--animation-fast) ease-out;
35
38
  border: 1px solid transparent;
36
39
  border-radius: var(--radius-full);
37
40
  background-color: var(--bg-color);
38
41
  cursor: pointer;
39
42
 
40
43
  .celeste-switch-thumb {
41
- --thumb-size: 12px;
42
44
  --thumb-hole: 4px;
45
+ --direction: 1;
43
46
 
44
47
  position: absolute;
45
48
  box-sizing: border-box;
46
49
  width: var(--thumb-size);
47
50
  height: var(--thumb-size);
48
- transform: translateY(-50%);
49
- transition-property: inset-inline-start, width, height;
50
- transition-duration: var(--animation-fast);
51
- transition-timing-function: var(--animation-function), ease-out, ease-out;
51
+ transform: translateY(-50%) scale(var(--thumb-scale, 1));
52
+ transition: transform var(--animation-fast) ease-out;
52
53
  border-radius: var(--radius-full);
53
54
  background-color: var(--color-static-white);
54
55
  box-shadow: var(--shadow-toggle-switch);
55
56
  inset-block-start: 50%;
56
- inset-inline-start: 1px;
57
- will-change: transform;
57
+ inset-inline-start: var(--thumb-inset);
58
58
 
59
59
  &[data-state='checked'] {
60
- inset-inline-start: calc(100% - var(--thumb-size) - 1px);
60
+ transform: translateY(-50%) translateX(calc(var(--travel) * var(--direction))) scale(var(--thumb-scale, 1));
61
61
  }
62
62
 
63
63
  &::before {
@@ -66,11 +66,15 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
66
66
  width: var(--thumb-hole);
67
67
  height: var(--thumb-hole);
68
68
  margin: auto;
69
- transition: background var(--animation-fast) ease-out;
69
+ transition: background-color var(--animation-fast) ease-out;
70
70
  border-radius: var(--radius-full);
71
71
  background-color: var(--bg-color);
72
72
  inset: var(--spacing-0);
73
73
  }
74
+
75
+ :dir(rtl) & {
76
+ --direction: -1;
77
+ }
74
78
  }
75
79
 
76
80
  &[aria-checked='true'] {
@@ -79,25 +83,22 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
79
83
 
80
84
  &:disabled {
81
85
  --bg-color: var(--color-bg-white-0);
86
+ --thumb-inset: var(--spacing-2);
87
+ --travel: calc(var(--width) - var(--thumb-size) - var(--thumb-inset) - 2px);
82
88
 
83
89
  border-color: var(--color-stroke-soft-200);
84
90
  background-color: var(--bg-color);
85
91
  pointer-events: none;
86
92
 
87
93
  .celeste-switch-thumb {
88
- inset-inline-start: var(--spacing-2);
89
94
  border-color: var(--color-bg-soft-200);
90
95
  background-color: var(--color-bg-soft-200);
91
96
  box-shadow: none;
92
-
93
- &[data-state='checked'] {
94
- inset-inline-start: calc(100% - var(--thumb-size));
95
- }
96
97
  }
97
98
  }
98
99
 
99
- &:active:not(:disabled) .celeste-switch-thumb {
100
- --thumb-size: 10px;
100
+ &:active:not(:disabled) {
101
+ --thumb-scale: 0.833;
101
102
  }
102
103
 
103
104
  &:hover:not(:active, :disabled) {
@@ -237,3 +237,42 @@ export const IconsOnly: Story = {
237
237
  `,
238
238
  }),
239
239
  };
240
+
241
+ export const FilledAndScrollable: Story = {
242
+ args: {
243
+ orientation: 'horizontal',
244
+ defaultValue: 'orders',
245
+ },
246
+ render: args => ({
247
+ components: { TabMenu, TabMenuList, TabMenuItem, TabMenuContent, HintText },
248
+ setup() {
249
+ return { args };
250
+ },
251
+ template: `
252
+ <div style="display: flex; flex-direction: column; gap: 32px; width: 480px; margin: 0 auto;">
253
+ <TabMenu v-bind="args">
254
+ <TabMenuList fill align="center">
255
+ <TabMenuItem value="orders">Orders</TabMenuItem>
256
+ <TabMenuItem value="products">Products</TabMenuItem>
257
+ <TabMenuItem value="customers">Customers</TabMenuItem>
258
+ </TabMenuList>
259
+ <TabMenuContent value="orders"><div style="padding: 16px;"><HintText text="fill + align center" /></div></TabMenuContent>
260
+ <TabMenuContent value="products"><div style="padding: 16px;"><HintText text="fill + align center" /></div></TabMenuContent>
261
+ <TabMenuContent value="customers"><div style="padding: 16px;"><HintText text="fill + align center" /></div></TabMenuContent>
262
+ </TabMenu>
263
+
264
+ <TabMenu v-bind="args">
265
+ <TabMenuList scrollable :bordered="false">
266
+ <TabMenuItem value="orders">Orders</TabMenuItem>
267
+ <TabMenuItem value="products">Products</TabMenuItem>
268
+ <TabMenuItem value="customers">Customers</TabMenuItem>
269
+ <TabMenuItem value="discounts">Discounts</TabMenuItem>
270
+ <TabMenuItem value="analytics">Analytics</TabMenuItem>
271
+ <TabMenuItem value="marketing">Marketing</TabMenuItem>
272
+ </TabMenuList>
273
+ <TabMenuContent value="orders"><div style="padding: 16px;"><HintText text="scrollable, no border" /></div></TabMenuContent>
274
+ </TabMenu>
275
+ </div>
276
+ `,
277
+ }),
278
+ };
@@ -23,7 +23,9 @@ const forwardedProps = useForwardProps(delegatedProps);
23
23
  <span v-if="$slots.default" class="celeste-tab-menu-item-label">
24
24
  <slot />
25
25
  </span>
26
- <slot name="postfix" />
26
+ <span v-if="$slots.postfix" class="celeste-tab-menu-item-postfix">
27
+ <slot name="postfix" />
28
+ </span>
27
29
  </TabsTrigger>
28
30
  </template>
29
31
 
@@ -36,17 +38,20 @@ const forwardedProps = useForwardProps(delegatedProps);
36
38
  justify-content: flex-start;
37
39
  width: 100%;
38
40
  padding: var(--spacing-8);
39
- transition: all var(--animation-fast) var(--animation-function);
41
+ transition:
42
+ background-color var(--animation-fast) var(--animation-function),
43
+ color var(--animation-fast) var(--animation-function);
40
44
  border: none;
41
45
  border-radius: var(--radius-8);
42
46
  color: var(--color-text-sub-600);
43
- font-size: var(--font-size-10);
47
+ font: var(--label-sm);
44
48
  cursor: pointer;
45
49
  gap: var(--spacing-6);
46
50
 
47
51
  &:deep(i) {
48
52
  width: 20px;
49
53
  height: 20px;
54
+ transition: color var(--animation-fast) var(--animation-function);
50
55
  }
51
56
 
52
57
  &:focus {
@@ -54,33 +59,32 @@ const forwardedProps = useForwardProps(delegatedProps);
54
59
  }
55
60
 
56
61
  &[data-orientation='horizontal'] {
57
- position: relative;
58
62
  width: fit-content;
59
63
  padding-block: var(--spacing-14);
60
64
  padding-inline: var(--spacing-0);
61
-
62
- &::after {
63
- content: '';
64
- display: block;
65
- position: absolute;
66
- inset-inline: 0;
67
- inset-block-end: 0;
68
- width: 100%;
69
- height: 2px;
70
- transition: all var(--animation-fast) var(--animation-function);
71
- background-color: transparent;
72
- }
73
- }
74
-
75
- &[data-orientation='vertical'][data-state='active'] {
76
- background-color: var(--color-bg-weak-50);
65
+ background-color: var(--color-bg-white-0);
77
66
  }
78
67
 
79
- &[data-orientation='horizontal'][data-state='active'] {
68
+ &[data-orientation='vertical'] {
80
69
  background-color: var(--color-bg-white-0);
81
70
 
82
- &::after {
83
- background-color: var(--color-primary-base);
71
+ &:hover,
72
+ &[data-state='active'] {
73
+ background-color: var(--color-bg-weak-50);
74
+ }
75
+
76
+ .celeste-tab-menu-item-postfix {
77
+ display: inline-flex;
78
+ transform: scale(0.75);
79
+ transition:
80
+ transform var(--animation-fast) var(--animation-function),
81
+ opacity var(--animation-fast) var(--animation-function);
82
+ opacity: 0;
83
+ }
84
+
85
+ &[data-state='active'] .celeste-tab-menu-item-postfix {
86
+ transform: scale(1);
87
+ opacity: 1;
84
88
  }
85
89
  }
86
90
 
@@ -94,14 +98,9 @@ const forwardedProps = useForwardProps(delegatedProps);
94
98
  }
95
99
  }
96
100
 
97
- &[data-state='inactive'] {
98
- background-color: var(--color-bg-white-0);
99
- }
100
-
101
101
  .celeste-tab-menu-item-label {
102
102
  flex: 1;
103
103
  overflow: hidden;
104
- font-size: var(--font-size-2);
105
104
  text-align: start;
106
105
  text-overflow: ellipsis;
107
106
  white-space: nowrap;