@loword/loword-design-system 0.0.13 → 0.0.14

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.
@@ -0,0 +1,263 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { createContext, useContext, } from 'react';
4
+ import cn from '../../utils/cn.js';
5
+ import { renderIcon } from '../../utils/renderIcon.js';
6
+ import TabsBase from './TabsBase.js';
7
+ const LDTabsContext = createContext({
8
+ variant: 'line',
9
+ placement: 'top',
10
+ size: 'medium',
11
+ });
12
+ const useLDTabsContext = () => useContext(LDTabsContext);
13
+ // ── Root ──
14
+ // 세로 배치는 items-stretch — line의 기준선(List의 border)이 콘텐츠 높이까지 이어지려면 List가 늘어나야 한다.
15
+ // card는 패널(Content)이 자기 테두리를 직접 그리므로 stretch에 의존하지 않는다.
16
+ const rootLayoutClass = {
17
+ top: 'flex flex-col',
18
+ left: 'flex flex-row items-stretch',
19
+ right: 'flex flex-row items-stretch',
20
+ };
21
+ const Root = ({ variant = 'line', placement = 'top', size = 'medium', className, ...props }) => (_jsx(LDTabsContext, { value: { variant, placement, size }, children: _jsx(TabsBase.Root, { orientation: placement === 'top' ? 'horizontal' : 'vertical', className: cn(rootLayoutClass[placement],
22
+ // card 세로 배치는 트리거 border box와 콘텐츠 border가 맞닿아야 하므로 간격 없음
23
+ placement !== 'top' && variant !== 'card' && 'gap-6', className), ...props }) }));
24
+ // ── List ──
25
+ const listClass = {
26
+ line: {
27
+ top: 'flex flex-row border-b border-neutral200',
28
+ left: 'flex flex-col shrink-0 border-r border-neutral200',
29
+ right: 'flex flex-col shrink-0 border-l border-neutral200',
30
+ },
31
+ card: {
32
+ top: 'flex flex-row',
33
+ left: 'flex flex-col shrink-0',
34
+ right: 'flex flex-col shrink-0',
35
+ },
36
+ button: {
37
+ top: 'flex flex-row',
38
+ left: 'flex flex-col shrink-0',
39
+ right: 'flex flex-col shrink-0',
40
+ },
41
+ };
42
+ const listGapClass = {
43
+ line: {
44
+ top: { small: 'gap-3', medium: 'gap-3.5', large: 'gap-4' },
45
+ left: { small: 'gap-2', medium: 'gap-2.5', large: 'gap-3' },
46
+ right: { small: 'gap-2', medium: 'gap-2.5', large: 'gap-3' },
47
+ },
48
+ card: {
49
+ top: { small: 'gap-1', medium: 'gap-1', large: 'gap-1' },
50
+ left: { small: 'gap-1', medium: 'gap-1', large: 'gap-1' },
51
+ right: { small: 'gap-1', medium: 'gap-1', large: 'gap-1' },
52
+ },
53
+ button: {
54
+ top: { small: 'gap-1', medium: 'gap-1', large: 'gap-1' },
55
+ left: { small: 'gap-1', medium: 'gap-1.5', large: 'gap-2' },
56
+ right: { small: 'gap-1', medium: 'gap-1.5', large: 'gap-2' },
57
+ },
58
+ };
59
+ // card 전용: List 끝의 보이지 않는 스페이서.
60
+ // 라운딩 반지름(rounded-lg = 8px)만큼을 항상 비워둬 트리거가 패널의 자유 모서리 호를 침범하지 못하게 한다 —
61
+ // 트리거 열이 아무리 길어져도 마지막 8px은 이 요소 몫이라, 조건 분기 없이 어떤 크기 조합에서도 모서리가 맞는다.
62
+ const cardListSpacerClass = {
63
+ top: 'flex-1 min-w-2',
64
+ left: 'flex-1 min-h-2',
65
+ right: 'flex-1 min-h-2',
66
+ };
67
+ const List = ({ className, children, ...props }) => {
68
+ const { variant, placement, size } = useLDTabsContext();
69
+ return (_jsxs(TabsBase.List, { className: cn(listClass[variant][placement], listGapClass[variant][placement][size], placement === 'right' && 'order-last', className), ...props, children: [children, variant === 'card' && (_jsx("div", { "aria-hidden": true, className: cardListSpacerClass[placement] }))] }));
70
+ };
71
+ // ── Trigger ──
72
+ // line 공통: top 기준(배경 없이 텍스트 색상만 전환) — left/right도 동일, 테두리 색만 배치별로 갈림
73
+ // 활성/hover 텍스트 색과 인디케이터 보더 색은 color prop(colorTextClass/colorIndicatorClass)이 담당
74
+ const lineStateClass = cn('data-[state=inactive]:text-neutral600 data-[state=inactive]:not-data-[disabled]:hover:text-neutral900', 'data-[disabled]:opacity-40');
75
+ // card 공통: 비활성은 옅은 회색 배경, 활성은 패널과 동일한 흰 배경.
76
+ // 심(seam) 쪽 선은 패널(cardContentClass)이 혼자 그린다 — 비활성은 그 선이 그대로 보여 닫힌 박스가 되고,
77
+ // 활성만 같은 흰색으로 그 구간을 덮어 열린 형태가 된다(덮개는 cardActiveCoverClass).
78
+ // 흰색 위에 흰색을 덮는 구조라 배율이 어긋나 1~2px 밀려도 눈에 보이지 않는다.
79
+ // 활성 텍스트 색은 color prop(colorTextClass)이 담당, inactive hover는 color와 무관하게 그레이스케일
80
+ const cardStateClass = cn('data-[state=inactive]:bg-mono50 data-[state=inactive]:text-neutral600', // inactive
81
+ 'data-[state=inactive]:not-data-[disabled]:hover:bg-mono100 data-[state=inactive]:not-data-[disabled]:hover:text-neutral700', // inactive, hover
82
+ 'data-[state=active]:bg-white', // active
83
+ 'data-[state=active]:not-data-[disabled]:hover:bg-white', // active, hover
84
+ // disabled: active/inactive 상관없이 항상 동일하게 적용 — !important 대신 data-state와 결합해 specificity로 이기게 함
85
+ 'data-[state=active]:data-[disabled]:bg-mono50/40 data-[state=active]:data-[disabled]:text-neutral600/40', 'data-[state=inactive]:data-[disabled]:bg-mono50/40 data-[state=inactive]:data-[disabled]:text-neutral600/40');
86
+ // button 공통: 기본 배경 없음, 활성 시에만 배경 채움(LDButton 솔리드 스타일)
87
+ // 활성 텍스트 색·활성 배경은 color prop이 담당, inactive hover는 color와 무관하게 그레이스케일
88
+ const buttonStateClass = cn('data-[state=inactive]:text-neutral600', // inactive - 배경 없음
89
+ 'data-[state=inactive]:not-data-[disabled]:hover:bg-mono100 data-[state=inactive]:not-data-[disabled]:hover:text-neutral700', // inactive, hover
90
+ 'data-[disabled]:opacity-40');
91
+ const buttonVerticalClass = cn('rounded-[5px]', buttonStateClass);
92
+ // card 활성 트리거 전용: 심(seam) 쪽 패널 테두리를 덮어 그 구간만 선을 지우는 2px 스트립.
93
+ // 절대 위치의 컨테이닝 블록이 트리거의 padding box라 inset-0이 트리거 자신의 border 1px 행을 남긴다 —
94
+ // 그 행이 패널의 가로선·세로선과 T자로 만나 선이 이어진다.
95
+ // border box 전체를 덮는 box-shadow로 하면 첫 트리거 위치에서 패널 테두리의 시작 2px이 같이 지워져 선이 끊긴다.
96
+ // bg-white는 cardContentClass의 패널 배경과 같은 값이어야 한다(같이 바꿀 것).
97
+ const cardActiveCoverClass = {
98
+ top: "after:content-[''] after:absolute after:inset-x-0 after:top-full after:h-0.5 after:bg-transparent data-[state=active]:after:bg-white",
99
+ left: "after:content-[''] after:absolute after:inset-y-0 after:left-full after:w-0.5 after:bg-transparent data-[state=active]:after:bg-white",
100
+ right: "after:content-[''] after:absolute after:inset-y-0 after:right-full after:w-0.5 after:bg-transparent data-[state=active]:after:bg-white",
101
+ };
102
+ const triggerClass = {
103
+ line: {
104
+ top: cn('relative -mb-px border-b-2 border-transparent', lineStateClass),
105
+ left: cn(
106
+ // 메뉴가 왼쪽에 있으므로 콘텐츠와 맞닿는 오른쪽 변에 인디케이터.
107
+ // List의 회색 기준선과 겹치도록 -mr-px로 정렬(top의 -mb-px와 동일한 원리), 라운딩 없음.
108
+ 'relative -mr-px border-r-2 border-transparent', lineStateClass),
109
+ right: cn(
110
+ // 메뉴가 오른쪽에 있으므로 콘텐츠와 맞닿는 왼쪽 변에 인디케이터. 라운딩 없음.
111
+ 'relative -ml-px border-l-2 border-transparent', lineStateClass),
112
+ },
113
+ card: {
114
+ top: cn(
115
+ // 심 쪽(아래) 변은 0폭 — transparent로 두면 인접 변과 45° 마이터가 생겨 모서리가 대각선으로 잘린다.
116
+ // 그 변의 선은 패널이 그리므로 트리거는 패널에 겹치지 않고 flush로 붙는다(음수 마진 없음).
117
+ // 활성만 cardActiveCoverClass의 2px 스트립으로 그 구간의 패널 테두리를 덮어 열린 형태가 된다.
118
+ 'relative rounded-t-lg border border-b-0 border-neutral100', 'data-[state=active]:z-10', cardActiveCoverClass.top, cardStateClass),
119
+ left: cn('relative rounded-l-lg border border-r-0 border-neutral100', 'data-[state=active]:z-10', cardActiveCoverClass.left, cardStateClass),
120
+ right: cn('relative rounded-r-lg border border-l-0 border-neutral100', 'data-[state=active]:z-10', cardActiveCoverClass.right, cardStateClass),
121
+ },
122
+ button: {
123
+ top: cn('rounded-full bold', buttonStateClass),
124
+ left: buttonVerticalClass,
125
+ right: buttonVerticalClass,
126
+ },
127
+ };
128
+ // size는 트리거의 타이포와 여백만 바꿈 — variant/placement Record와 곱하지 않고 별도 Record로 분리
129
+ const triggerFontClass = {
130
+ small: 'display12',
131
+ medium: 'display14',
132
+ large: 'display16',
133
+ };
134
+ // line top
135
+ const lineTopPaddingClass = {
136
+ small: 'px-1 py-2',
137
+ medium: 'px-1.5 py-2.5',
138
+ large: 'px-2 py-3',
139
+ };
140
+ const cardTopPaddingClass = {
141
+ small: 'px-3 py-1.5',
142
+ medium: 'px-4 py-2',
143
+ large: 'px-5 py-2.5',
144
+ };
145
+ const buttonTopPaddingClass = {
146
+ small: 'px-3 py-1.5',
147
+ medium: 'px-4 py-2',
148
+ large: 'px-5 py-2.5',
149
+ };
150
+ // 아이콘–라벨 gap도 size 축 — 6/8/10px 등차(--control-gap-sm/md/lg와 같은 단계)
151
+ const triggerGapClass = {
152
+ small: 'gap-1.5',
153
+ medium: 'gap-2',
154
+ large: 'gap-2.5',
155
+ };
156
+ // 아이콘 크기는 size 축 — 12/12/16px. large만 LDButton 사다리(display16↔icon-md)와 일치한다.
157
+ // small은 사다리대로면 icon-xs(8px)지만 display12 옆에서 점처럼 보여 medium과 같은 icon-sm으로 올렸다.
158
+ const triggerIconClass = {
159
+ small: 'icon-sm',
160
+ medium: 'icon-md',
161
+ large: 'icon-md',
162
+ };
163
+ // ── color ──
164
+ // color는 Trigger 단위 prop이다 — 한 Tabs 안에서 트리거마다 다른 강조색을 지정할 수 있다.
165
+ // color는 활성(active) 상태만 담당한다. inactive hover는 variant와 무관하게 그레이스케일
166
+ // (bg-mono100·text-neutral700, line은 text-neutral900)이며 color와 무관.
167
+ // Tailwind JIT가 문자열 보간을 스캔하지 못하므로 클래스명을 전부 리터럴로 적는다.
168
+ // accent 단계는 소비 앱(loword-mono-frontend) 실사용을 참고해 정했다 —
169
+ // 그쪽 스케일(5~100)의 success100/warning100/danger100 이 여기 600 단계와 동일한 hex이고, 그쪽엔 700 이상이 없다.
170
+ // 다만 success/warning은 600이 텍스트로 너무 옅어(흰 배경 대비 1.93:1 / 1.66:1) 800을 쓴다 —
171
+ // 실사용값에서 의도적으로 벗어난 지점이다.
172
+ // 흰 배경 대비: lb500 3.00:1, danger600 3.29:1, success800 4.42:1, warning800 3.50:1.
173
+ // button 활성 시에는 accent 글자가 같은 계열 tint 배경 위에 놓인다. 그 대비: point 2.73:1, danger 3.05:1,
174
+ // success 4.21:1, warning 3.09:1 — point가 가장 약하다(기존 기본값).
175
+ /** 활성 텍스트 + 활성 hover 텍스트 (전 variant 공통) */
176
+ const colorTextClass = {
177
+ point: cn('data-[state=active]:text-lb500', 'data-[state=active]:not-data-[disabled]:hover:text-lb500'),
178
+ danger: cn('data-[state=active]:text-danger600', 'data-[state=active]:not-data-[disabled]:hover:text-danger600'),
179
+ success: cn('data-[state=active]:text-success800', 'data-[state=active]:not-data-[disabled]:hover:text-success800'),
180
+ warning: cn('data-[state=active]:text-warning800', 'data-[state=active]:not-data-[disabled]:hover:text-warning800'),
181
+ };
182
+ /** 활성 배경 tint — button 전용(card 활성 배경은 패널과 맞춘 bg-white로 cardStateClass가 담당) */
183
+ const colorActiveBgClass = {
184
+ point: cn('data-[state=active]:bg-tintBlue', 'data-[state=active]:not-data-[disabled]:hover:bg-tintBlue'),
185
+ danger: cn('data-[state=active]:bg-tintRed', 'data-[state=active]:not-data-[disabled]:hover:bg-tintRed'),
186
+ success: cn('data-[state=active]:bg-tintGreen', 'data-[state=active]:not-data-[disabled]:hover:bg-tintGreen'),
187
+ warning: cn('data-[state=active]:bg-tintYellow', 'data-[state=active]:not-data-[disabled]:hover:bg-tintYellow'),
188
+ };
189
+ /** line 인디케이터 보더 — 콘텐츠와 맞닿는 변이 배치별로 다름 */
190
+ const colorIndicatorClass = {
191
+ point: {
192
+ top: 'data-[state=active]:border-lb500',
193
+ left: 'data-[state=active]:border-r-lb500',
194
+ right: 'data-[state=active]:border-l-lb500',
195
+ },
196
+ danger: {
197
+ top: 'data-[state=active]:border-danger600',
198
+ left: 'data-[state=active]:border-r-danger600',
199
+ right: 'data-[state=active]:border-l-danger600',
200
+ },
201
+ success: {
202
+ top: 'data-[state=active]:border-success800',
203
+ left: 'data-[state=active]:border-r-success800',
204
+ right: 'data-[state=active]:border-l-success800',
205
+ },
206
+ warning: {
207
+ top: 'data-[state=active]:border-warning800',
208
+ left: 'data-[state=active]:border-r-warning800',
209
+ right: 'data-[state=active]:border-l-warning800',
210
+ },
211
+ };
212
+ // left/right는 variant와 무관하게 동일한 세로 여백을 씀
213
+ // 가로 8/10/12px, 높이는 leading(16/18/20px) + py×2 = 28/34/40px 등차
214
+ const verticalPaddingClass = {
215
+ small: 'px-2 py-1.5',
216
+ medium: 'px-2.5 py-2',
217
+ large: 'px-3 py-2.5',
218
+ };
219
+ const triggerPaddingClass = {
220
+ line: {
221
+ top: lineTopPaddingClass,
222
+ left: verticalPaddingClass,
223
+ right: verticalPaddingClass,
224
+ },
225
+ card: {
226
+ top: cardTopPaddingClass,
227
+ left: verticalPaddingClass,
228
+ right: verticalPaddingClass,
229
+ },
230
+ button: {
231
+ top: buttonTopPaddingClass,
232
+ left: verticalPaddingClass,
233
+ right: verticalPaddingClass,
234
+ },
235
+ };
236
+ const Trigger = ({ as, className, color = 'point', icon, children, ...props }) => {
237
+ const { variant, placement, size } = useLDTabsContext();
238
+ return (_jsxs(TabsBase.Trigger, { as: as, className: cn('flex items-center bold cursor-pointer ld-transition data-disabled:cursor-not-allowed', triggerFontClass[size], triggerClass[variant][placement], colorTextClass[color], variant === 'line' && colorIndicatorClass[color][placement], variant === 'button' && colorActiveBgClass[color], triggerGapClass[size], triggerPaddingClass[variant][placement][size], className), ...props, children: [renderIcon(icon, triggerIconClass[size]), children] }));
239
+ };
240
+ // ── Content ──
241
+ // 콘텐츠 영역은 placement에 따라 배치가 갈리고, card만 추가로 테두리를 두른다.
242
+ const contentClass = {
243
+ top: 'pt-4',
244
+ left: 'flex-1 min-w-0',
245
+ right: 'flex-1 min-w-0',
246
+ };
247
+ // card 전용 콘텐츠 패널 스타일
248
+ const cardContentClass = {
249
+ top: 'p-4 bg-white rounded-tr-lg rounded-b-lg border border-neutral100',
250
+ left: 'p-4 bg-white rounded-tr-lg rounded-b-lg border border-neutral100',
251
+ right: 'p-4 bg-white rounded-tl-lg rounded-b-lg border border-neutral100',
252
+ };
253
+ const Content = ({ className, children, ...props }) => {
254
+ const { variant, placement } = useLDTabsContext();
255
+ return (_jsx(TabsBase.Content, { className: cn(contentClass[placement], variant === 'card' && cardContentClass[placement], className), ...props, children: _jsx("div", { className: "size-full overflow-auto", children: children }) }));
256
+ };
257
+ const LDTabs = {
258
+ Root,
259
+ List,
260
+ Trigger,
261
+ Content,
262
+ };
263
+ export default LDTabs;
@@ -0,0 +1,38 @@
1
+ import { type CSSProperties, type ElementType, type ReactNode } from 'react';
2
+ import { type PolymorphicComponentProps } from '../../types/polymorphic.js';
3
+ interface RootProps {
4
+ value?: string;
5
+ defaultValue?: string;
6
+ onValueChange?: (value: string) => void;
7
+ orientation?: 'horizontal' | 'vertical';
8
+ className?: string;
9
+ style?: CSSProperties;
10
+ children?: ReactNode;
11
+ }
12
+ interface ListProps {
13
+ className?: string;
14
+ style?: CSSProperties;
15
+ children?: ReactNode;
16
+ }
17
+ interface TriggerOwnProps {
18
+ value: string;
19
+ disabled?: boolean;
20
+ className?: string;
21
+ style?: CSSProperties;
22
+ children?: ReactNode;
23
+ }
24
+ export type TriggerProps<T extends ElementType> = PolymorphicComponentProps<T, TriggerOwnProps>;
25
+ interface ContentProps {
26
+ value: string;
27
+ className?: string;
28
+ style?: CSSProperties;
29
+ children?: ReactNode;
30
+ }
31
+ export declare const TabsBase: {
32
+ Root: ({ className, children, ...props }: RootProps) => import("react/jsx-runtime").JSX.Element;
33
+ List: ({ className, style, children }: ListProps) => import("react/jsx-runtime").JSX.Element;
34
+ Trigger: ElementType;
35
+ Content: ({ value, className, style, children }: ContentProps) => import("react/jsx-runtime").JSX.Element;
36
+ };
37
+ export default TabsBase;
38
+ //# sourceMappingURL=TabsBase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabsBase.d.ts","sourceRoot":"","sources":["../../../src/components/LDTabs/TabsBase.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGzE,UAAU,SAAS;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAWD,UAAU,SAAS;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAWD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,WAAW,IAAI,yBAAyB,CACzE,CAAC,EACD,eAAe,CAChB,CAAC;AAqDF,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAUD,eAAO,MAAM,QAAQ;8CAvG4B,SAAS;2CAeZ,SAAS;aA2FjC,WAAW;qDAXuB,YAAY;CAanE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as RadixTabs from '@radix-ui/react-tabs';
3
+ const Root = ({ className, children, ...props }) => {
4
+ return (_jsx(RadixTabs.Root, { ...props, className: className, children: children }));
5
+ };
6
+ const List = ({ className, style, children }) => {
7
+ return (_jsx(RadixTabs.List, { className: className, style: style, children: children }));
8
+ };
9
+ // Radix Tabs.Trigger는 asChild 없이는 항상 <button>이라, 다른 엘리먼트로 바꾸려면 asChild가 유일한 경로다.
10
+ // asChild를 쓸 때는 자식 엘리먼트를 여기서 직접 만들어야 계산된 className이 정확히 그 엘리먼트에 붙는다.
11
+ // asChild는 외부에 노출하지 않는 내부 구현 디테일이다.
12
+ const Trigger = ({ as, value, disabled, className, style, children, href, ...props }) => {
13
+ if (!as || as === 'button') {
14
+ return (_jsx(RadixTabs.Trigger, { value: value, disabled: disabled, className: className, style: style, ...props, children: children }));
15
+ }
16
+ // as가 href를 가질 때 (a, NextLink 등) disabled=true일 경우 href를 제거하고 span으로 렌더링
17
+ const isDisabledLink = href && disabled;
18
+ const Component = isDisabledLink ? 'span' : as;
19
+ return (_jsx(RadixTabs.Trigger, { value: value, disabled: disabled, asChild: true, children: _jsx(Component, { className: className, style: style, "aria-disabled": disabled || undefined, ...(!isDisabledLink && { href }), ...props, children: children }) }));
20
+ };
21
+ const Content = ({ value, className, style, children }) => {
22
+ return (_jsx(RadixTabs.Content, { value: value, className: className, style: style, children: children }));
23
+ };
24
+ export const TabsBase = {
25
+ Root,
26
+ List,
27
+ Trigger: Trigger,
28
+ Content,
29
+ };
30
+ export default TabsBase;