@sybilion/uilib 1.3.49 → 1.3.50

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.
@@ -15,8 +15,8 @@ function hasTabPanelContent(content) {
15
15
  return false;
16
16
  return true;
17
17
  }
18
- function PageTabs({ className, contentClassName, innerClassName, scrollbarClassName, items, tabsListProps, ...props }) {
19
- return (jsxs(Tabs, { className: cn(S.root, className), variant: "link", ...props, children: [jsx(TabsList, { ...tabsListProps, className: cn(S.list, tabsListProps?.withPaddings && S.withPaddings), children: jsx(PageXScroll, { size: "sm", scrollbarClassName: cn(S.scrollbar, scrollbarClassName), innerClassName: innerClassName, children: items.map(item => (jsx(TabsTrigger, { value: item.value, children: item.label }, item.value))) }) }), items.map(item => hasTabPanelContent(item.content) ? (jsx(TabsContent, { value: item.value, className: cn(S.content, contentClassName), children: item.content }, item.value)) : null)] }));
18
+ function PageTabs({ className, contentClassName, innerClassName, scrollbarClassName, items, tabsListProps, variant = 'link', ...props }) {
19
+ return (jsxs(Tabs, { className: cn(S.root, className), variant: variant, ...props, children: [jsx(TabsList, { ...tabsListProps, className: cn(S.list, tabsListProps?.withPaddings && S.withPaddings), children: jsx(PageXScroll, { size: "sm", scrollbarClassName: cn(S.scrollbar, scrollbarClassName), innerClassName: innerClassName, children: items.map((item, index) => (jsx(TabsTrigger, { value: item.value, children: item.label }, item.key ?? `${item.value}-${index}`))) }) }), items.map(item => hasTabPanelContent(item.content) ? (jsx(TabsContent, { value: item.value, className: cn(S.content, contentClassName), children: item.content }, item.value)) : null)] }));
20
20
  }
21
21
 
22
22
  export { PageTabs };
@@ -1,7 +1,7 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Tabs_root__cB9Au{display:flex;flex-direction:column;gap:.5rem}.Tabs_list__9Hs22{align-items:center;color:var(--muted-foreground);display:inline-flex;height:48px;justify-content:center;width:-moz-fit-content;width:fit-content}.Tabs_list__9Hs22.Tabs_fullWidth__Lj2Dd{width:100%}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22{background-color:transparent!important}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22{border-radius:1.5rem;box-shadow:none;gap:.25rem;height:auto}.Tabs_trigger__uzdqH{align-items:center;border:1px solid transparent;border-radius:.375rem;color:var(--muted-foreground);cursor:pointer;display:inline-flex;flex:1;font-size:var(--text-sm);font-weight:500;gap:.375rem;height:calc(100% - 1px);justify-content:center;padding:.25rem .5rem;transition:color .2s,box-shadow .2s;white-space:nowrap}.Tabs_trigger__uzdqH:focus-visible{border-color:var(--ring);box-shadow:0 0 0 3px var(--ring-50);outline:1px solid var(--ring);outline-offset:0}.Tabs_trigger__uzdqH:disabled{opacity:.5;pointer-events:none}.Tabs_trigger__uzdqH[data-state=active]{background-color:var(--background);box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.Tabs_trigger__uzdqH:hover,.Tabs_trigger__uzdqH[data-state=active]{color:var(--foreground)!important}.Tabs_darker__l-R0I .Tabs_trigger__uzdqH{background-color:var(--muted)}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{border:none;border-radius:var(--p-4);box-shadow:none;color:var(--muted-foreground);flex-shrink:0;font-weight:600;padding:var(--p-1) var(--p-3);transition:all .15s ease-in-out}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-6);box-shadow:0 1px 2px 0 rgba(0,0,0,.05);color:var(--foreground)}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{background-color:transparent!important;border:0!important;border-radius:0!important;box-shadow:0!important;font-size:16px;padding:12px 16px}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{box-shadow:inset 0 -3px 0 -1px var(--sb-cyan-400)!important}.Tabs_trigger__uzdqH svg{flex-shrink:0;pointer-events:none}.Tabs_trigger__uzdqH svg:not([class*=size-]){height:1rem;width:1rem}.dark .Tabs_trigger__uzdqH{color:var(--muted-foreground)}.dark .Tabs_trigger__uzdqH[data-state=active]{background-color:var(--input-30);border-color:var(--input);color:var(--foreground)}.Tabs_content__K27Vl{flex:1;outline:none}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:var(--text-xs);padding:var(--p-1) var(--p-2)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-4)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:var(--text-base);padding:var(--p-2) var(--p-4)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-8)}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeSm__gfpc6>.Tabs_list__9Hs22{height:auto;min-height:36px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeSm__gfpc6>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:14px;padding:8px 12px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeLg__CcRFZ>.Tabs_list__9Hs22{height:auto;min-height:56px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeLg__CcRFZ>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:18px;padding:16px 24px}";
4
- var S = {"root":"Tabs_root__cB9Au","list":"Tabs_list__9Hs22","fullWidth":"Tabs_fullWidth__Lj2Dd","variant-link":"Tabs_variant-link__xOMDg","variant-button":"Tabs_variant-button__wtc8k","trigger":"Tabs_trigger__uzdqH","darker":"Tabs_darker__l-R0I","content":"Tabs_content__K27Vl","sizeSm":"Tabs_sizeSm__gfpc6","sizeLg":"Tabs_sizeLg__CcRFZ"};
3
+ var css_248z = ".Tabs_root__cB9Au{display:flex;flex-direction:column;gap:.5rem}.Tabs_list__9Hs22{align-items:center;color:var(--muted-foreground);display:inline-flex;height:48px;justify-content:center;width:-moz-fit-content;width:fit-content}.Tabs_list__9Hs22.Tabs_fullWidth__Lj2Dd{width:100%}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22{background-color:transparent!important}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22,.Tabs_variant-outline__2CJJg .Tabs_list__9Hs22{border-radius:1.5rem;box-shadow:none;gap:.25rem;height:auto}.Tabs_variant-outline__2CJJg .Tabs_list__9Hs22{background-color:transparent!important;gap:var(--p-2)}.Tabs_trigger__uzdqH{align-items:center;border:1px solid transparent;border-radius:.375rem;color:var(--muted-foreground);cursor:pointer;display:inline-flex;flex:1;font-size:var(--text-sm);font-weight:500;gap:.375rem;height:calc(100% - 1px);justify-content:center;padding:.25rem .5rem;transition:color .2s,box-shadow .2s;white-space:nowrap}.Tabs_trigger__uzdqH:focus-visible{border-color:var(--ring);box-shadow:0 0 0 3px var(--ring-50);outline:1px solid var(--ring);outline-offset:0}.Tabs_trigger__uzdqH:disabled{opacity:.5;pointer-events:none}.Tabs_trigger__uzdqH[data-state=active]{background-color:var(--background);box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.Tabs_trigger__uzdqH:hover,.Tabs_trigger__uzdqH[data-state=active]{color:var(--foreground)!important}.Tabs_darker__l-R0I .Tabs_trigger__uzdqH{background-color:var(--muted)}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{border:none;border-radius:var(--p-4);box-shadow:none;color:var(--muted-foreground);flex-shrink:0;font-weight:600;padding:var(--p-1) var(--p-3);transition:all .15s ease-in-out}.Tabs_variant-button__wtc8k .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-6);box-shadow:0 1px 2px 0 rgba(0,0,0,.05);color:var(--foreground)}.Tabs_variant-outline__2CJJg .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{background-color:var(--background);border-radius:var(--p-6);color:var(--muted-foreground);flex-shrink:0;font-weight:600;padding:var(--p-1) var(--p-4);transition:box-shadow .15s ease-in-out,color .15s ease-in-out}.Tabs_variant-outline__2CJJg .Tabs_list__9Hs22 .Tabs_trigger__uzdqH:hover{color:var(--foreground)}.Tabs_variant-outline__2CJJg .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{background-color:var(--background);box-shadow:inset 0 0 0 2px var(--selected-tab-outline-color,var(--brand-color-400));color:var(--foreground)}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{background-color:transparent!important;border:0!important;border-radius:0!important;box-shadow:0!important;font-size:16px;padding:12px 16px}.Tabs_variant-link__xOMDg>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{box-shadow:inset 0 -3px 0 -1px var(--selected-tab-outline-color,var(--brand-color-400))!important}.Tabs_trigger__uzdqH svg{flex-shrink:0;pointer-events:none}.Tabs_trigger__uzdqH svg:not([class*=size-]){height:1rem;width:1rem}.dark .Tabs_trigger__uzdqH{color:var(--muted-foreground)}.dark .Tabs_trigger__uzdqH[data-state=active]{background-color:var(--input-30);border-color:var(--input);color:var(--foreground)}.dark .Tabs_variant-outline__2CJJg .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{background-color:var(--background);box-shadow:inset 0 0 0 2px var(--selected-tab-outline-color,var(--brand-color-400));color:var(--foreground)}.Tabs_content__K27Vl{flex:1;outline:none}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:var(--text-xs);padding:var(--p-1) var(--p-2)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-4)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:var(--text-base);padding:var(--p-2) var(--p-4)}.Tabs_root__cB9Au.Tabs_variant-button__wtc8k.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-8)}.Tabs_root__cB9Au.Tabs_variant-outline__2CJJg.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{border-radius:var(--p-4);font-size:var(--text-xs);padding:var(--p-1) var(--p-3)}.Tabs_root__cB9Au.Tabs_variant-outline__2CJJg.Tabs_sizeSm__gfpc6 .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-4)}.Tabs_root__cB9Au.Tabs_variant-outline__2CJJg.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH{border-radius:var(--p-8);font-size:var(--text-base);padding:var(--p-2) var(--p-5)}.Tabs_root__cB9Au.Tabs_variant-outline__2CJJg.Tabs_sizeLg__CcRFZ .Tabs_list__9Hs22 .Tabs_trigger__uzdqH[data-state=active]{border-radius:var(--p-8)}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeSm__gfpc6>.Tabs_list__9Hs22{height:auto;min-height:36px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeSm__gfpc6>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:14px;padding:8px 12px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeLg__CcRFZ>.Tabs_list__9Hs22{height:auto;min-height:56px}.Tabs_root__cB9Au.Tabs_variant-link__xOMDg.Tabs_sizeLg__CcRFZ>.Tabs_list__9Hs22 .Tabs_trigger__uzdqH{font-size:18px;padding:16px 24px}";
4
+ var S = {"root":"Tabs_root__cB9Au","list":"Tabs_list__9Hs22","fullWidth":"Tabs_fullWidth__Lj2Dd","variant-link":"Tabs_variant-link__xOMDg","variant-button":"Tabs_variant-button__wtc8k","variant-outline":"Tabs_variant-outline__2CJJg","trigger":"Tabs_trigger__uzdqH","darker":"Tabs_darker__l-R0I","content":"Tabs_content__K27Vl","sizeSm":"Tabs_sizeSm__gfpc6","sizeLg":"Tabs_sizeLg__CcRFZ"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { S as default };
@@ -1,14 +1,18 @@
1
- import { TabsListProps, TabsProps } from '#uilib/components/ui/Tabs';
2
- export declare function PageTabs({ className, contentClassName, innerClassName, scrollbarClassName, items, tabsListProps, ...props }: {
3
- items: {
4
- value: string;
5
- label: string;
6
- content?: React.ReactNode;
7
- }[];
1
+ import { TabsListProps, TabsProps, type TabsVariant } from '#uilib/components/ui/Tabs';
2
+ export type PageTabsItem = {
3
+ value: string;
4
+ label: React.ReactNode;
5
+ content?: React.ReactNode;
6
+ /** React list key when multiple items may share `value`. */
7
+ key?: string;
8
+ };
9
+ export declare function PageTabs({ className, contentClassName, innerClassName, scrollbarClassName, items, tabsListProps, variant, ...props }: {
10
+ items: PageTabsItem[];
8
11
  tabsListProps?: TabsListProps & {
9
12
  fullWidth?: boolean;
10
13
  withPaddings?: boolean;
11
14
  };
15
+ variant?: TabsVariant;
12
16
  } & TabsProps & {
13
17
  contentClassName?: string;
14
18
  innerClassName?: string;
@@ -7,7 +7,7 @@ export { PageXScroll } from './PageXScroll/PageXScroll';
7
7
  export { Breadcrumbs } from './Breadcrumbs/Breadcrumbs';
8
8
  export { PageFooter } from './PageFooter/PageFooter';
9
9
  export type { PageFooterLinkItem, PageFooterProps, } from './PageFooter/PageFooter';
10
- export { PageTabs } from './PageTabs/PageTabs';
10
+ export { PageTabs, type PageTabsItem } from './PageTabs/PageTabs';
11
11
  export { PageColumns } from './PageColumns/PageColumns';
12
12
  export { SectionHeader } from './SectionHeader';
13
13
  export type { SectionHeaderProps } from './SectionHeader';
@@ -1,6 +1,6 @@
1
- import { TabsContentProps, TabsListProps, TabsProps, TabsTriggerProps } from './Tabs.types';
1
+ import { TabsContentProps, TabsListProps, TabsProps, TabsTriggerProps, TabsVariant } from './Tabs.types';
2
2
  declare function Tabs({ className, variant, size, darker, ...props }: TabsProps & {
3
- variant?: 'button' | 'link';
3
+ variant?: TabsVariant;
4
4
  size?: 'sm' | 'md' | 'lg';
5
5
  darker?: boolean;
6
6
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { ComponentProps } from 'react';
2
2
  import * as TabsPrimitive from '@radix-ui/react-tabs';
3
+ export type TabsVariant = 'button' | 'link' | 'outline';
3
4
  export type TabsProps = ComponentProps<typeof TabsPrimitive.Root>;
4
5
  export type TabsListProps = ComponentProps<typeof TabsPrimitive.List>;
5
6
  export type TabsTriggerProps = ComponentProps<typeof TabsPrimitive.Trigger>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.49",
3
+ "version": "1.3.50",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -8,6 +8,7 @@ import {
8
8
  TabsListProps,
9
9
  TabsProps,
10
10
  TabsTrigger,
11
+ type TabsVariant,
11
12
  } from '#uilib/components/ui/Tabs';
12
13
 
13
14
  import S from './PageTabs.styl';
@@ -20,6 +21,14 @@ function hasTabPanelContent(content: React.ReactNode | undefined): boolean {
20
21
  return true;
21
22
  }
22
23
 
24
+ export type PageTabsItem = {
25
+ value: string;
26
+ label: React.ReactNode;
27
+ content?: React.ReactNode;
28
+ /** React list key when multiple items may share `value`. */
29
+ key?: string;
30
+ };
31
+
23
32
  export function PageTabs({
24
33
  className,
25
34
  contentClassName,
@@ -27,20 +36,22 @@ export function PageTabs({
27
36
  scrollbarClassName,
28
37
  items,
29
38
  tabsListProps,
39
+ variant = 'link',
30
40
  ...props
31
41
  }: {
32
- items: { value: string; label: string; content?: React.ReactNode }[];
42
+ items: PageTabsItem[];
33
43
  tabsListProps?: TabsListProps & {
34
44
  fullWidth?: boolean;
35
45
  withPaddings?: boolean;
36
46
  };
47
+ variant?: TabsVariant;
37
48
  } & TabsProps & {
38
49
  contentClassName?: string;
39
50
  innerClassName?: string;
40
51
  scrollbarClassName?: string;
41
52
  }) {
42
53
  return (
43
- <Tabs className={cn(S.root, className)} variant="link" {...props}>
54
+ <Tabs className={cn(S.root, className)} variant={variant} {...props}>
44
55
  <TabsList
45
56
  {...tabsListProps}
46
57
  className={cn(S.list, tabsListProps?.withPaddings && S.withPaddings)}
@@ -50,8 +61,11 @@ export function PageTabs({
50
61
  scrollbarClassName={cn(S.scrollbar, scrollbarClassName)}
51
62
  innerClassName={innerClassName}
52
63
  >
53
- {items.map(item => (
54
- <TabsTrigger value={item.value} key={item.value}>
64
+ {items.map((item, index) => (
65
+ <TabsTrigger
66
+ value={item.value}
67
+ key={item.key ?? `${item.value}-${index}`}
68
+ >
55
69
  {item.label}
56
70
  </TabsTrigger>
57
71
  ))}
@@ -15,7 +15,7 @@ export type {
15
15
  PageFooterLinkItem,
16
16
  PageFooterProps,
17
17
  } from './PageFooter/PageFooter';
18
- export { PageTabs } from './PageTabs/PageTabs';
18
+ export { PageTabs, type PageTabsItem } from './PageTabs/PageTabs';
19
19
  export { PageColumns } from './PageColumns/PageColumns';
20
20
  export { SectionHeader } from './SectionHeader';
21
21
  export type { SectionHeaderProps } from './SectionHeader';
@@ -19,6 +19,7 @@
19
19
  background-color transparent !important
20
20
 
21
21
  .variant-button &
22
+ .variant-outline &
22
23
  // background-color var(--muted-50)
23
24
  border-radius 1.5rem // 24px
24
25
  // padding 0.25rem // 4px
@@ -26,6 +27,10 @@
26
27
  box-shadow none
27
28
  height auto
28
29
 
30
+ .variant-outline &
31
+ gap var(--p-2)
32
+ background-color transparent !important
33
+
29
34
  .trigger
30
35
  display inline-flex
31
36
  flex 1
@@ -83,6 +88,23 @@
83
88
  color var(--foreground)
84
89
  box-shadow 0 1px 2px 0 rgba(0, 0, 0, 0.05)
85
90
 
91
+ .variant-outline .list &
92
+ flex-shrink 0
93
+ background-color var(--background)
94
+ padding var(--p-1) var(--p-4)
95
+ border-radius var(--p-6)
96
+ transition box-shadow 0.15s ease-in-out, color 0.15s ease-in-out
97
+ font-weight 600
98
+ color var(--muted-foreground)
99
+
100
+ &:hover
101
+ color var(--foreground)
102
+
103
+ &[data-state="active"]
104
+ box-shadow inset 0 0 0 2px var(--selected-tab-outline-color, var(--brand-color-400))
105
+ background-color var(--background)
106
+ color var(--foreground)
107
+
86
108
  .variant-link > .list &
87
109
  background-color transparent !important
88
110
  border 0 !important
@@ -92,7 +114,7 @@
92
114
  font-size 16px
93
115
 
94
116
  &[data-state="active"]
95
- box-shadow inset 0 -3px 0 -1px var(--sb-cyan-400) !important
117
+ box-shadow inset 0 -3px 0 -1px var(--selected-tab-outline-color, var(--brand-color-400)) !important
96
118
 
97
119
 
98
120
  & svg
@@ -111,6 +133,12 @@
111
133
  border-color var(--input)
112
134
  color var(--foreground)
113
135
 
136
+ :global(.dark) .variant-outline .list &
137
+ &[data-state="active"]
138
+ background-color var(--background)
139
+ box-shadow inset 0 0 0 2px var(--selected-tab-outline-color, var(--brand-color-400))
140
+ color var(--foreground)
141
+
114
142
  .content
115
143
  flex 1
116
144
  outline none
@@ -134,6 +162,25 @@
134
162
  &[data-state="active"]
135
163
  border-radius var(--p-8)
136
164
 
165
+ &.variant-outline
166
+ &.sizeSm
167
+ .list .trigger
168
+ padding var(--p-1) var(--p-3)
169
+ font-size var(--text-xs)
170
+ border-radius var(--p-4)
171
+
172
+ &[data-state="active"]
173
+ border-radius var(--p-4)
174
+
175
+ &.sizeLg
176
+ .list .trigger
177
+ padding var(--p-2) var(--p-5)
178
+ font-size var(--text-base)
179
+ border-radius var(--p-8)
180
+
181
+ &[data-state="active"]
182
+ border-radius var(--p-8)
183
+
137
184
  &.variant-link
138
185
  &.sizeSm
139
186
  > .list
@@ -11,6 +11,7 @@ interface CssExports {
11
11
  'trigger': string;
12
12
  'variant-button': string;
13
13
  'variant-link': string;
14
+ 'variant-outline': string;
14
15
  }
15
16
  export const cssExports: CssExports;
16
17
  export default cssExports;
@@ -8,6 +8,7 @@ import {
8
8
  TabsListProps,
9
9
  TabsProps,
10
10
  TabsTriggerProps,
11
+ TabsVariant,
11
12
  } from './Tabs.types';
12
13
 
13
14
  function Tabs({
@@ -17,7 +18,7 @@ function Tabs({
17
18
  darker = false,
18
19
  ...props
19
20
  }: TabsProps & {
20
- variant?: 'button' | 'link';
21
+ variant?: TabsVariant;
21
22
  size?: 'sm' | 'md' | 'lg';
22
23
  darker?: boolean;
23
24
  }) {
@@ -2,6 +2,8 @@ import { ComponentProps } from 'react';
2
2
 
3
3
  import * as TabsPrimitive from '@radix-ui/react-tabs';
4
4
 
5
+ export type TabsVariant = 'button' | 'link' | 'outline';
6
+
5
7
  export type TabsProps = ComponentProps<typeof TabsPrimitive.Root>;
6
8
  export type TabsListProps = ComponentProps<typeof TabsPrimitive.List>;
7
9
  export type TabsTriggerProps = ComponentProps<typeof TabsPrimitive.Trigger>;
@@ -3,6 +3,13 @@ import { PageContentSection, PageTabs } from '#uilib/components/ui/Page';
3
3
  import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
4
4
  import { DocsHeaderActions } from '../docsHeaderActions';
5
5
 
6
+ const OUTLINE_ITEMS = [
7
+ { value: 'outline-1', label: 'Crude Oil ↘ 15%' },
8
+ { value: 'outline-2', label: 'Consumer Staples ↘ 12%' },
9
+ { value: 'outline-3', label: 'Energy ↘ 8%' },
10
+ { value: 'outline-4', label: 'Metals ↘ 10%' },
11
+ ];
12
+
6
13
  const SCROLL_TEST_ITEMS = Array.from({ length: 10 }, (_, i) => {
7
14
  const n = i + 1;
8
15
  return {
@@ -54,6 +61,33 @@ export default function PageTabsPage() {
54
61
  />
55
62
  </div>
56
63
  </section>
64
+
65
+ <section>
66
+ <h3 style={{ marginBottom: '0.75rem' }}>Outline variant</h3>
67
+ <p
68
+ style={{
69
+ marginBottom: '1rem',
70
+ color: 'var(--muted-foreground)',
71
+ }}
72
+ >
73
+ <code>variant=&quot;outline&quot;</code> with horizontal scroll on a
74
+ muted surface.
75
+ </p>
76
+ <div
77
+ style={{
78
+ maxWidth: 500,
79
+ padding: '0.5rem 0',
80
+ borderRadius: 'var(--p-3)',
81
+ backgroundColor: 'var(--muted)',
82
+ }}
83
+ >
84
+ <PageTabs
85
+ variant="outline"
86
+ defaultValue={OUTLINE_ITEMS[0].value}
87
+ items={OUTLINE_ITEMS}
88
+ />
89
+ </div>
90
+ </section>
57
91
  </PageContentSection>
58
92
  </>
59
93
  );
@@ -1,22 +1,57 @@
1
+ import type { CSSProperties } from 'react';
2
+
1
3
  import { PageContentSection } from '#uilib/components/ui/Page';
2
4
  import {
3
5
  Tabs,
4
6
  TabsContent,
5
7
  TabsList,
6
8
  TabsTrigger,
9
+ type TabsVariant,
7
10
  } from '#uilib/components/ui/Tabs';
8
11
 
9
12
  import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
10
13
  import { DocsHeaderActions } from '../docsHeaderActions';
11
14
 
12
- const VARIANTS = ['button', 'link'] as const;
15
+ const VARIANTS: TabsVariant[] = ['button', 'link', 'outline'];
13
16
  const SIZES = ['sm', 'md', 'lg'] as const;
14
17
 
18
+ const OUTLINE_DEMO_TABS = [
19
+ { value: 'crude-oil', label: 'Crude Oil', pct: 15 },
20
+ { value: 'consumer-staples', label: 'Consumer Staples Euro', pct: 15 },
21
+ { value: 'energy', label: 'Energy', pct: 8 },
22
+ { value: 'metals', label: 'Metals', pct: 12 },
23
+ { value: 'agriculture', label: 'Agriculture', pct: 5 },
24
+ ] as const;
25
+
26
+ function OutlineTabLabel({ label, pct }: { label: string; pct: number }) {
27
+ return (
28
+ <span
29
+ style={{
30
+ display: 'inline-flex',
31
+ alignItems: 'center',
32
+ gap: 'var(--p-2)',
33
+ }}
34
+ >
35
+ <span>{label}</span>
36
+ <span
37
+ style={{
38
+ color: 'var(--destructive)',
39
+ fontSize: 'var(--text-xs)',
40
+ fontWeight: 600,
41
+ fontVariantNumeric: 'tabular-nums',
42
+ }}
43
+ >
44
+ ↘ {pct}%
45
+ </span>
46
+ </span>
47
+ );
48
+ }
49
+
15
50
  function TabsDemoCell({
16
51
  variant,
17
52
  size,
18
53
  }: {
19
- variant: (typeof VARIANTS)[number];
54
+ variant: TabsVariant;
20
55
  size: (typeof SIZES)[number];
21
56
  }) {
22
57
  const id = `${variant}-${size}`;
@@ -92,6 +127,55 @@ export default function TabsPage() {
92
127
  </div>
93
128
  </section>
94
129
 
130
+ <section>
131
+ <h3 style={{ marginBottom: '1rem' }}>Outline</h3>
132
+ <p
133
+ style={{
134
+ marginBottom: '0.75rem',
135
+ color: 'var(--muted-foreground)',
136
+ }}
137
+ >
138
+ White pill triggers; active tab uses a border via{' '}
139
+ <code>--selected-tab-outline-color</code> (defaults to{' '}
140
+ <code>--sb-cyan-400</code>).
141
+ </p>
142
+ <div
143
+ style={{
144
+ display: 'flex',
145
+ flexDirection: 'column',
146
+ gap: '1.5rem',
147
+ padding: '1rem',
148
+ borderRadius: 'var(--p-3)',
149
+ backgroundColor: 'var(--muted)',
150
+ }}
151
+ >
152
+ <Tabs variant="outline" defaultValue={OUTLINE_DEMO_TABS[0].value}>
153
+ <TabsList>
154
+ {OUTLINE_DEMO_TABS.map(tab => (
155
+ <TabsTrigger key={tab.value} value={tab.value}>
156
+ <OutlineTabLabel label={tab.label} pct={tab.pct} />
157
+ </TabsTrigger>
158
+ ))}
159
+ </TabsList>
160
+ </Tabs>
161
+
162
+ <Tabs
163
+ variant="outline"
164
+ defaultValue="custom-a"
165
+ style={
166
+ {
167
+ '--selected-tab-outline-color': 'oklch(0.55 0.2 250)',
168
+ } as CSSProperties
169
+ }
170
+ >
171
+ <TabsList>
172
+ <TabsTrigger value="custom-a">Custom outline</TabsTrigger>
173
+ <TabsTrigger value="custom-b">Second tab</TabsTrigger>
174
+ </TabsList>
175
+ </Tabs>
176
+ </div>
177
+ </section>
178
+
95
179
  <section>
96
180
  <h3 style={{ marginBottom: '1rem' }}>Darker</h3>
97
181
  <p