@kalink-ui/seedly 0.9.0 → 0.11.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 (108) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +14 -6
  3. package/src/components/box/box.css.ts +2 -2
  4. package/src/components/button/button.css.ts +52 -50
  5. package/src/components/button/button.tsx +15 -12
  6. package/src/components/button/index.ts +1 -1
  7. package/src/components/button-icon/button-icon.css.ts +90 -0
  8. package/src/components/button-icon/button-icon.tsx +23 -0
  9. package/src/components/button-icon/index.ts +1 -0
  10. package/src/components/card/card.tsx +7 -4
  11. package/src/components/card/index.ts +1 -1
  12. package/src/components/center/center.tsx +2 -2
  13. package/src/components/center/index.ts +1 -1
  14. package/src/components/cluster/cluster.css.ts +17 -0
  15. package/src/components/command/command-empty.tsx +14 -0
  16. package/src/components/command/command-group.css.ts +34 -0
  17. package/src/components/command/command-group.tsx +19 -0
  18. package/src/components/command/command-input.css.ts +31 -0
  19. package/src/components/command/command-input.tsx +44 -0
  20. package/src/components/command/command-item.css.ts +27 -0
  21. package/src/components/command/command-item.tsx +35 -0
  22. package/src/components/command/command-list.css.ts +14 -0
  23. package/src/components/command/command-list.tsx +19 -0
  24. package/src/components/command/command-separator.tsx +29 -0
  25. package/src/components/command/command.tsx +24 -0
  26. package/src/components/command/index.ts +7 -0
  27. package/src/components/cover/index.ts +1 -1
  28. package/src/components/divider/divider.css.ts +11 -0
  29. package/src/components/divider/divider.tsx +11 -0
  30. package/src/components/divider/index.ts +1 -0
  31. package/src/components/form-field/form-field-context.ts +18 -0
  32. package/src/components/form-field/form-field-control.tsx +34 -0
  33. package/src/components/form-field/form-field-description.tsx +16 -0
  34. package/src/components/form-field/form-field-error.tsx +22 -0
  35. package/src/components/form-field/form-field-item-context.ts +6 -0
  36. package/src/components/form-field/form-field-item.tsx +28 -0
  37. package/src/components/form-field/form-field-label.tsx +27 -0
  38. package/src/components/form-field/form-field-message.tsx +33 -0
  39. package/src/components/form-field/form-field.css.ts +97 -0
  40. package/src/components/form-field/form-field.tsx +56 -0
  41. package/src/components/form-field/index.ts +9 -0
  42. package/src/components/frame/frame.css.ts +8 -8
  43. package/src/components/frame/frame.tsx +2 -6
  44. package/src/components/frame/index.ts +1 -1
  45. package/src/components/heading/heading.tsx +43 -8
  46. package/src/components/index.ts +29 -15
  47. package/src/components/input/index.ts +2 -0
  48. package/src/components/input/input-wrapper.tsx +58 -0
  49. package/src/components/input/input.css.ts +250 -0
  50. package/src/components/input/input.tsx +56 -0
  51. package/src/components/label/index.ts +1 -0
  52. package/src/components/label/label.css.ts +37 -0
  53. package/src/components/label/label.tsx +23 -0
  54. package/src/components/loader/index.ts +1 -0
  55. package/src/components/loader/loader.css.ts +109 -0
  56. package/src/components/loader/moon-loader.tsx +43 -0
  57. package/src/components/loader-overlay/index.ts +1 -0
  58. package/src/components/loader-overlay/loader-overlay.css.ts +35 -0
  59. package/src/components/loader-overlay/loader-overlay.tsx +28 -0
  60. package/src/components/menu/index.ts +2 -0
  61. package/src/components/menu/menu-item.css.ts +79 -0
  62. package/src/components/menu/menu-separator.css.ts +53 -0
  63. package/src/components/popover/index.ts +3 -0
  64. package/src/components/popover/popover-content.css.ts +107 -0
  65. package/src/components/popover/popover-content.tsx +82 -0
  66. package/src/components/popover/popover.tsx +6 -0
  67. package/src/components/scroll-area/index.ts +1 -0
  68. package/src/components/scroll-area/scroll-area.css.ts +72 -0
  69. package/src/components/scroll-area/scroll-area.tsx +39 -0
  70. package/src/components/scroll-area/scroll-bar.tsx +37 -0
  71. package/src/components/seed/seed.tsx +4 -4
  72. package/src/components/select/index.ts +5 -0
  73. package/src/components/select/select-content.css.ts +22 -0
  74. package/src/components/select/select-content.tsx +51 -0
  75. package/src/components/select/select-item.css.ts +24 -0
  76. package/src/components/select/select-item.tsx +24 -0
  77. package/src/components/select/select-root.tsx +14 -0
  78. package/src/components/select/select-trigger.css.ts +75 -0
  79. package/src/components/select/select-trigger.tsx +47 -0
  80. package/src/components/select/select.tsx +85 -0
  81. package/src/components/sheet/index.ts +5 -0
  82. package/src/components/sheet/sheet-content.css.ts +143 -0
  83. package/src/components/sheet/sheet-content.tsx +43 -0
  84. package/src/components/sheet/sheet-description.tsx +21 -0
  85. package/src/components/sheet/sheet-footer.tsx +15 -0
  86. package/src/components/sheet/sheet-header.css.ts +35 -0
  87. package/src/components/sheet/sheet-header.tsx +32 -0
  88. package/src/components/sheet/sheet-overlay.css.ts +43 -0
  89. package/src/components/sheet/sheet-overlay.tsx +14 -0
  90. package/src/components/sheet/sheet-title.tsx +31 -0
  91. package/src/components/sheet/sheet.tsx +8 -0
  92. package/src/components/stack/index.ts +1 -1
  93. package/src/components/stack/stack.css.ts +5 -1
  94. package/src/components/stack/stack.tsx +2 -2
  95. package/src/components/text/index.ts +6 -0
  96. package/src/components/text/text.css.ts +31 -4
  97. package/src/components/text-field/index.ts +1 -0
  98. package/src/components/text-field/text-field.css.ts +3 -0
  99. package/src/components/text-field/text-field.tsx +64 -0
  100. package/src/components/textarea/index.ts +1 -0
  101. package/src/components/textarea/textarea-input.tsx +20 -0
  102. package/src/components/textarea/textarea.css.ts +10 -0
  103. package/src/components/textarea/textarea.tsx +69 -0
  104. package/src/styles/define-responsive-properties.ts +242 -0
  105. package/src/styles/extract-sprinkles-props.ts +29 -35
  106. package/src/styles/index.ts +9 -0
  107. package/src/styles/reset.css.ts +1 -0
  108. package/src/styles/visually-hidden.css.ts +21 -0
@@ -0,0 +1,109 @@
1
+ import { createVar, keyframes, style } from '@vanilla-extract/css';
2
+ import { calc } from '@vanilla-extract/css-utils';
3
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
4
+
5
+ import { sys, transition } from '../../styles';
6
+
7
+ export const loader = recipe({
8
+ variants: {
9
+ active: {
10
+ true: {
11
+ opacity: 1,
12
+ visibility: 'visible',
13
+
14
+ animationPlayState: 'running',
15
+ },
16
+ false: {
17
+ opacity: 0,
18
+ visibility: 'hidden',
19
+
20
+ animationPlayState: 'paused',
21
+ },
22
+ },
23
+ },
24
+ });
25
+
26
+ const size = createVar();
27
+ const moonSize = createVar();
28
+
29
+ const loaderAnimation = keyframes({
30
+ '100%': {
31
+ transform: 'rotate(360deg)',
32
+ },
33
+ });
34
+
35
+ export const loaderWrapper = recipe({
36
+ base: {
37
+ overflow: 'hidden',
38
+ width: size,
39
+ height: size,
40
+
41
+ animationName: loaderAnimation,
42
+ animationDuration: '1s',
43
+ animationIterationCount: 'infinite',
44
+ animationTimingFunction: 'linear',
45
+ animationFillMode: 'forwards',
46
+
47
+ transition: transition(['opacity', 'visibility']),
48
+ pointerEvents: 'none',
49
+
50
+ vars: {
51
+ [moonSize]: calc.divide(size, 7),
52
+ },
53
+ },
54
+
55
+ variants: {
56
+ size: {
57
+ sm: {
58
+ vars: {
59
+ [size]: sys.spacing[4],
60
+ },
61
+ },
62
+ md: {
63
+ vars: {
64
+ [size]: sys.spacing[5],
65
+ },
66
+ },
67
+ lg: {
68
+ vars: {
69
+ [size]: sys.spacing[6],
70
+ },
71
+ },
72
+ },
73
+ },
74
+ });
75
+
76
+ export const ellipse = style({
77
+ width: size,
78
+ height: size,
79
+
80
+ position: 'absolute',
81
+ insetBlockStart: 0,
82
+ insetInlineStart: 0,
83
+
84
+ borderRadius: '100%',
85
+ borderWidth: moonSize,
86
+ borderStyle: 'solid',
87
+ borderColor: `color-mix(in srgb, ${sys.color.foreground} 30%, transparent)`,
88
+ });
89
+
90
+ export const moon = style({
91
+ width: moonSize,
92
+ height: moonSize,
93
+
94
+ position: 'absolute',
95
+ insetBlockStart: calc.subtract(
96
+ calc.divide(size, 2),
97
+ calc.divide(moonSize, 2),
98
+ ),
99
+ insetInlineStart: 0,
100
+
101
+ backgroundColor: sys.color.foreground,
102
+
103
+ borderRadius: '100%',
104
+ });
105
+
106
+ export type LoaderVariants = NonNullable<RecipeVariants<typeof loader>>;
107
+ export type MoonLoaderVariants = NonNullable<
108
+ RecipeVariants<typeof loaderWrapper>
109
+ >;
@@ -0,0 +1,43 @@
1
+ import { clsx } from 'clsx';
2
+ import { ElementType } from 'react';
3
+
4
+ import { Box, BoxProps } from '../box';
5
+
6
+ import {
7
+ ellipse,
8
+ moon,
9
+ loaderWrapper,
10
+ loader,
11
+ LoaderVariants,
12
+ MoonLoaderVariants,
13
+ } from './loader.css';
14
+
15
+ export type LoaderProps<TUse extends ElementType> = BoxProps<TUse> & {
16
+ forceMount?: boolean;
17
+ className?: string;
18
+ } & LoaderVariants;
19
+
20
+ /**
21
+ * Heavily inspired by https://github.com/davidhu2000/react-spinners/blob/main/src/MoonLoader.tsx
22
+ */
23
+ export function MoonLoader<TUse extends ElementType>({
24
+ active,
25
+ size = 'md',
26
+ forceMount = false,
27
+ className,
28
+ ...props
29
+ }: LoaderProps<TUse> & MoonLoaderVariants) {
30
+ if (!active && !forceMount) {
31
+ return null;
32
+ }
33
+
34
+ return (
35
+ <Box
36
+ className={clsx(loaderWrapper({ size }), loader({ active }), className)}
37
+ {...props}
38
+ >
39
+ <span className={ellipse} />
40
+ <span className={moon} />
41
+ </Box>
42
+ );
43
+ }
@@ -0,0 +1 @@
1
+ export { LoaderOverlay } from './loader-overlay';
@@ -0,0 +1,35 @@
1
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
2
+
3
+ import { sys } from '../../styles';
4
+
5
+ export const loaderOverlay = recipe({
6
+ base: {
7
+ display: 'flex',
8
+ justifyContent: 'center',
9
+ alignItems: 'center',
10
+
11
+ height: '100%',
12
+ width: '100%',
13
+
14
+ zIndex: 1000,
15
+
16
+ backgroundColor: `color-mix(in srgb, ${sys.color.foreground} 10%, transparent)`,
17
+ },
18
+
19
+ variants: {
20
+ position: {
21
+ absolute: {
22
+ position: 'absolute',
23
+ top: 0,
24
+ left: 0,
25
+ },
26
+ relative: {
27
+ position: 'relative',
28
+ },
29
+ },
30
+ },
31
+ });
32
+
33
+ export type LoaderOverlayVariants = NonNullable<
34
+ RecipeVariants<typeof loaderOverlay>
35
+ >;
@@ -0,0 +1,28 @@
1
+ import { clsx } from 'clsx';
2
+
3
+ import { Center } from '../center';
4
+ import { MoonLoader } from '../loader';
5
+ import { Stack } from '../stack';
6
+ import { Text } from '../text';
7
+
8
+ import { loaderOverlay, LoaderOverlayVariants } from './loader-overlay.css';
9
+
10
+ interface LoaderOverlayProps extends LoaderOverlayVariants {
11
+ text?: string;
12
+ className?: string;
13
+ }
14
+
15
+ export function LoaderOverlay({
16
+ className,
17
+ text,
18
+ position,
19
+ }: LoaderOverlayProps) {
20
+ return (
21
+ <div className={clsx(loaderOverlay({ position }), className)}>
22
+ <Stack use={Center}>
23
+ <MoonLoader active forceMount />
24
+ {text && <Text>{text}</Text>}
25
+ </Stack>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,2 @@
1
+ export { menuItem, menuItemIcon } from './menu-item.css';
2
+ export { menuSeparator } from './menu-separator.css';
@@ -0,0 +1,79 @@
1
+ import { style } from '@vanilla-extract/css';
2
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
3
+
4
+ import { sys, typography } from '../../styles';
5
+
6
+ export const menuItem = recipe({
7
+ base: [
8
+ typography.body.medium,
9
+ {
10
+ display: 'flex',
11
+ alignItems: 'center',
12
+ gap: sys.spacing[2],
13
+
14
+ paddingInline: sys.spacing[2],
15
+ paddingBlock: sys.spacing[1],
16
+ width: '100%',
17
+
18
+ position: 'relative',
19
+
20
+ textAlign: 'start',
21
+
22
+ borderRadius: sys.shape.corner.small,
23
+
24
+ cursor: 'pointer',
25
+ userSelect: 'none',
26
+
27
+ selectors: {
28
+ '&[data-disabled="true"], &[aria-disabled="true"]': {
29
+ pointerEvents: 'none',
30
+ opacity: 0.5,
31
+ },
32
+
33
+ '&:before': {
34
+ content: '""',
35
+
36
+ width: '100%',
37
+ height: '100%',
38
+
39
+ position: 'absolute',
40
+ top: 0,
41
+ left: 0,
42
+
43
+ backgroundColor: sys.color.foreground,
44
+ opacity: 0,
45
+ borderRadius: 'inherit',
46
+
47
+ pointerEvents: 'none',
48
+ },
49
+
50
+ '&:hover::before': {
51
+ opacity: sys.state.hovered.opacity,
52
+ },
53
+
54
+ '&:is(:focus, :focus-visible, [aria-selected="true"])': {
55
+ outline: 'none',
56
+ },
57
+
58
+ '&:focus::before, &:focus-visible::before, &[aria-selected="true"]::before':
59
+ {
60
+ opacity: sys.state.focused.opacity,
61
+ },
62
+ },
63
+ },
64
+ ],
65
+
66
+ variants: {
67
+ inset: {
68
+ true: {
69
+ paddingInlineStart: sys.spacing[4],
70
+ },
71
+ },
72
+ },
73
+ });
74
+
75
+ export const menuItemIcon = style({
76
+ color: sys.color.foreground,
77
+ });
78
+
79
+ export type MenuItemVariants = NonNullable<RecipeVariants<typeof menuItem>>;
@@ -0,0 +1,53 @@
1
+ import { createVar } from '@vanilla-extract/css';
2
+ import { calc } from '@vanilla-extract/css-utils';
3
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
4
+
5
+ import { mapContractVars, sys } from '../../styles';
6
+ import { components } from '../../styles/layers.css';
7
+
8
+ const spaceVar = createVar();
9
+
10
+ export const menuSeparator = recipe({
11
+ base: {
12
+ paddingBlock: spaceVar,
13
+ marginInline: sys.spacing[2],
14
+
15
+ position: 'relative',
16
+
17
+ '::after': {
18
+ content: '""',
19
+
20
+ height: '1px',
21
+ width: '100%',
22
+
23
+ position: 'absolute',
24
+ top: '50%',
25
+
26
+ backgroundColor: sys.color.foreground,
27
+
28
+ transform: 'translateY(-50%)',
29
+ },
30
+ },
31
+
32
+ variants: {
33
+ offset: {
34
+ true: {
35
+ marginInline: calc.negate(sys.spacing[2]),
36
+ },
37
+ },
38
+
39
+ spacing: mapContractVars(sys.spacing, (key) => ({
40
+ '@layer': {
41
+ [components]: {
42
+ vars: {
43
+ [spaceVar]: sys.spacing[key],
44
+ },
45
+ },
46
+ },
47
+ })),
48
+ },
49
+ });
50
+
51
+ export type MenuSeparatorVariants = NonNullable<
52
+ RecipeVariants<typeof menuSeparator>
53
+ >;
@@ -0,0 +1,3 @@
1
+ export { Popover, PopoverTrigger } from './popover';
2
+ export { PopoverContent } from './popover-content';
3
+ export { popoverContent } from './popover-content.css';
@@ -0,0 +1,107 @@
1
+ import { createVar, keyframes } from '@vanilla-extract/css';
2
+ import { calc } from '@vanilla-extract/css-utils';
3
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
4
+
5
+ import { mapContractVars, sys } from '../../styles';
6
+
7
+ const translateX = createVar();
8
+ const offsetX = createVar();
9
+ const translateY = createVar();
10
+ const offsetY = createVar();
11
+ const scale = createVar();
12
+
13
+ const enter = keyframes({
14
+ '100%': {
15
+ opacity: 1,
16
+ transform: `translateX(${offsetX}) translateY(${offsetY})`,
17
+ pointerEvents: 'auto',
18
+ },
19
+ });
20
+
21
+ const leave = keyframes({
22
+ '0%': {
23
+ opacity: 1,
24
+ transform: `translateX(${offsetX}) translateY(${offsetY})`,
25
+ pointerEvents: 'auto',
26
+ },
27
+ });
28
+
29
+ export const popoverContent = recipe({
30
+ base: {
31
+ animationDuration: sys.motion.duration.short[2],
32
+ animationTimingFunction: sys.motion.easing.standard,
33
+ animationFillMode: 'forwards',
34
+
35
+ opacity: 0,
36
+ transform: `translate3d(${translateX}, ${translateY}, 0) scale3d(${scale}, ${scale}, ${scale})`,
37
+
38
+ selectors: {
39
+ '&[data-state="open"]': {
40
+ animationName: enter,
41
+ },
42
+
43
+ '&[data-state="closed"]': {
44
+ animationName: leave,
45
+ },
46
+
47
+ '&[data-side*="top"]': {
48
+ vars: {
49
+ [translateY]: sys.spacing[2],
50
+ [offsetY]: calc.negate(sys.spacing[1]),
51
+ },
52
+ },
53
+
54
+ '&[data-side*="right"]': {
55
+ vars: {
56
+ [translateX]: calc.negate(sys.spacing[1]),
57
+ [offsetX]: sys.spacing[2],
58
+ },
59
+ },
60
+
61
+ '&[data-side*="bottom"]': {
62
+ vars: {
63
+ [translateY]: calc.negate(sys.spacing[1]),
64
+ [offsetY]: sys.spacing[2],
65
+ },
66
+ },
67
+
68
+ '&[data-side*="left"]': {
69
+ vars: {
70
+ [translateX]: sys.spacing[3],
71
+ [offsetY]: calc.negate(sys.spacing[1]),
72
+ },
73
+ },
74
+ },
75
+
76
+ vars: {
77
+ [translateX]: '0',
78
+ [translateY]: '0',
79
+ [offsetX]: '0',
80
+ [offsetY]: '0',
81
+ [scale]: '0.98',
82
+ '--popover-trigger-width': 'var(--radix-popover-trigger-width)',
83
+ },
84
+ },
85
+
86
+ variants: {
87
+ width: {
88
+ trigger: {
89
+ minWidth: 'var(--popover-trigger-width)',
90
+ },
91
+ },
92
+
93
+ scrollable: {
94
+ true: {
95
+ overflow: 'hidden',
96
+ },
97
+ },
98
+
99
+ elevation: mapContractVars(sys.elevation, (key) => ({
100
+ boxShadow: sys.elevation[key],
101
+ })),
102
+ },
103
+ });
104
+
105
+ export type PopoverContentVariants = NonNullable<
106
+ RecipeVariants<typeof popoverContent>
107
+ >;
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { Portal, Content } from '@radix-ui/react-popover';
4
+ import { clsx } from 'clsx';
5
+ import {
6
+ ComponentPropsWithoutRef,
7
+ ComponentPropsWithRef,
8
+ ReactNode,
9
+ } from 'react';
10
+
11
+ import { Box, BoxProps } from '../box';
12
+ import { ScrollArea } from '../scroll-area';
13
+
14
+ import { popoverContent, PopoverContentVariants } from './popover-content.css';
15
+
16
+ export type PopoverContentProps = {
17
+ portaled?: boolean;
18
+ maxHeight?: string;
19
+ scrollable?: boolean;
20
+ } & ComponentPropsWithRef<typeof Content> &
21
+ PopoverContentVariants &
22
+ ComponentPropsWithoutRef<typeof Portal> &
23
+ BoxProps<'div'>;
24
+
25
+ export interface PopoverScrollableProps {
26
+ scrollable?: boolean;
27
+ maxHeight?: string;
28
+ children?: ReactNode;
29
+ }
30
+
31
+ export function PopoverScrollable({
32
+ children,
33
+ scrollable = true,
34
+ maxHeight,
35
+ }: PopoverScrollableProps) {
36
+ if (scrollable === false) {
37
+ return children;
38
+ }
39
+
40
+ return <ScrollArea maxHeight={maxHeight}>{children}</ScrollArea>;
41
+ }
42
+
43
+ export function PopoverContent({
44
+ className,
45
+ align = 'center',
46
+ container,
47
+ children,
48
+ spacing = 4,
49
+ radius = 'medium',
50
+ width,
51
+ portaled = true,
52
+ scrollable = true,
53
+ maxHeight = 'var(--radix-popover-content-available-height)',
54
+ elevation,
55
+ ...props
56
+ }: PopoverContentProps) {
57
+ const content = (
58
+ <Content
59
+ align={align}
60
+ className={clsx(
61
+ popoverContent({ width, scrollable, elevation }),
62
+ className,
63
+ )}
64
+ sideOffset={0}
65
+ asChild
66
+ collisionPadding={16}
67
+ {...props}
68
+ >
69
+ <Box spacing={spacing} radius={radius}>
70
+ <PopoverScrollable scrollable={scrollable} maxHeight={maxHeight}>
71
+ {children}
72
+ </PopoverScrollable>
73
+ </Box>
74
+ </Content>
75
+ );
76
+
77
+ return portaled === true ? (
78
+ <Portal container={container}>{content}</Portal>
79
+ ) : (
80
+ content
81
+ );
82
+ }
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+
3
+ import { Root, Trigger } from '@radix-ui/react-popover';
4
+
5
+ export const Popover = Root;
6
+ export const PopoverTrigger = Trigger;
@@ -0,0 +1 @@
1
+ export { ScrollArea } from './scroll-area';
@@ -0,0 +1,72 @@
1
+ import { createVar, style } from '@vanilla-extract/css';
2
+ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
3
+
4
+ import { sys, transition } from '../../styles';
5
+
6
+ export const viewportMaxHeight = createVar();
7
+
8
+ export const scrollArea = style({
9
+ overflow: 'hidden',
10
+ height: '100%',
11
+
12
+ position: 'relative',
13
+ });
14
+
15
+ export const scrollAreaViewport = style({
16
+ height: '100%',
17
+ maxHeight: viewportMaxHeight,
18
+ width: '100%',
19
+
20
+ borderRadius: 'inherit',
21
+
22
+ vars: {
23
+ [viewportMaxHeight]: 'initial',
24
+ },
25
+ });
26
+
27
+ export const scrollAreaScrollbar = recipe({
28
+ base: {
29
+ display: 'flex',
30
+
31
+ padding: 1,
32
+
33
+ transition: transition(['color', 'background-color', 'border-color'], {
34
+ duration: 'medium.2',
35
+ }),
36
+ userSelect: 'none',
37
+ },
38
+
39
+ variants: {
40
+ orientation: {
41
+ vertical: {
42
+ height: '100%',
43
+ width: 10,
44
+
45
+ borderInlineStartWidth: 1,
46
+ borderInlineStartColor: 'transparent',
47
+ },
48
+
49
+ horizontal: {
50
+ height: 10,
51
+ width: '100%',
52
+
53
+ borderBlockStartWidth: 1,
54
+ borderBlockStartColor: 'transparent',
55
+ },
56
+ },
57
+ },
58
+ });
59
+
60
+ export const scrollAreaThumb = style({
61
+ flexGrow: 1,
62
+
63
+ position: 'relative',
64
+
65
+ borderRadius: sys.shape.corner.small,
66
+
67
+ backgroundColor: sys.color.foreground,
68
+ });
69
+
70
+ export type ScrollAreaScrollbarVariants = NonNullable<
71
+ RecipeVariants<typeof scrollAreaScrollbar>
72
+ >;
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+
3
+ import { Corner, Root, Viewport } from '@radix-ui/react-scroll-area';
4
+ import { assignInlineVars } from '@vanilla-extract/dynamic';
5
+ import { ComponentPropsWithRef } from 'react';
6
+
7
+ import {
8
+ scrollArea,
9
+ scrollAreaViewport,
10
+ viewportMaxHeight,
11
+ } from './scroll-area.css';
12
+ import { ScrollBar } from './scroll-bar';
13
+
14
+ export type ScrollAreaProps = ComponentPropsWithRef<typeof Root> & {
15
+ maxHeight?: string;
16
+ };
17
+
18
+ export function ScrollArea({
19
+ ref,
20
+ className,
21
+ children,
22
+ maxHeight = 'initial',
23
+ ...props
24
+ }: ScrollAreaProps) {
25
+ return (
26
+ <Root ref={ref} className={scrollArea} {...props}>
27
+ <Viewport
28
+ className={scrollAreaViewport}
29
+ style={assignInlineVars({
30
+ [viewportMaxHeight]: maxHeight,
31
+ })}
32
+ >
33
+ {children}
34
+ </Viewport>
35
+ <ScrollBar />
36
+ <Corner />
37
+ </Root>
38
+ );
39
+ }