@koide-labs/ui 0.0.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 (170) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.storybook/main.ts +25 -0
  3. package/.storybook/preview-head.html +6 -0
  4. package/.storybook/preview.tsx +48 -0
  5. package/.storybook/vitest.setup.ts +8 -0
  6. package/README.md +11 -0
  7. package/eslint.config.mjs +29 -0
  8. package/lint-staged.config.js +15 -0
  9. package/package.json +95 -0
  10. package/pnpm-workspace.yaml +2 -0
  11. package/postcss.config.mjs +7 -0
  12. package/prettier.config.mjs +24 -0
  13. package/scripts/build-icon-types.ts +38 -0
  14. package/src/-types.ts +8 -0
  15. package/src/-utils.tsx +64 -0
  16. package/src/components/accordion/accordion.module.css +44 -0
  17. package/src/components/accordion/accordion.stories.tsx +36 -0
  18. package/src/components/accordion/index.tsx +67 -0
  19. package/src/components/alert-dialog/alert-dialog.module.css +5 -0
  20. package/src/components/alert-dialog/alert-dialog.stories.tsx +53 -0
  21. package/src/components/alert-dialog/index.tsx +138 -0
  22. package/src/components/anchor/anchor.module.css +18 -0
  23. package/src/components/anchor/anchor.stories.tsx +28 -0
  24. package/src/components/anchor/index.tsx +45 -0
  25. package/src/components/avatar/avatar.module.css +56 -0
  26. package/src/components/avatar/avatar.stories.tsx +61 -0
  27. package/src/components/avatar/index.tsx +82 -0
  28. package/src/components/badge/badge.module.css +35 -0
  29. package/src/components/badge/badge.stories.tsx +60 -0
  30. package/src/components/badge/index.tsx +71 -0
  31. package/src/components/button/button.module.css +42 -0
  32. package/src/components/button/button.stories.tsx +108 -0
  33. package/src/components/button/index.tsx +63 -0
  34. package/src/components/checkbox/checkbox.module.css +36 -0
  35. package/src/components/checkbox/checkbox.stories.tsx +21 -0
  36. package/src/components/checkbox/index.tsx +41 -0
  37. package/src/components/code/code.module.css +20 -0
  38. package/src/components/code/code.stories.tsx +42 -0
  39. package/src/components/code/index.tsx +73 -0
  40. package/src/components/collapse/collapse.module.css +27 -0
  41. package/src/components/collapse/collapse.stories.tsx +27 -0
  42. package/src/components/collapse/index.tsx +59 -0
  43. package/src/components/command/command.module.css +95 -0
  44. package/src/components/command/command.stories.tsx +38 -0
  45. package/src/components/command/index.tsx +108 -0
  46. package/src/components/context-menu/context-menu.module.css +36 -0
  47. package/src/components/context-menu/context-menu.stories.tsx +99 -0
  48. package/src/components/context-menu/index.tsx +242 -0
  49. package/src/components/dialog/dialog.module.css +71 -0
  50. package/src/components/dialog/dialog.stories.tsx +29 -0
  51. package/src/components/dialog/index.tsx +148 -0
  52. package/src/components/heading/heading.module.css +3 -0
  53. package/src/components/heading/heading.stories.tsx +52 -0
  54. package/src/components/heading/index.tsx +112 -0
  55. package/src/components/icon/icon-names.ts +3189 -0
  56. package/src/components/icon/icon.module.css +36 -0
  57. package/src/components/icon/icon.stories.tsx +40 -0
  58. package/src/components/icon/index.tsx +60 -0
  59. package/src/components/icon-button/icon-button.module.css +33 -0
  60. package/src/components/icon-button/icon-button.stories.tsx +59 -0
  61. package/src/components/icon-button/index.tsx +48 -0
  62. package/src/components/inline-code/index.tsx +29 -0
  63. package/src/components/inline-code/inline-code.module.css +13 -0
  64. package/src/components/inline-code/inline-code.stories.tsx +31 -0
  65. package/src/components/input/index.tsx +22 -0
  66. package/src/components/input/input.module.css +23 -0
  67. package/src/components/input/input.stories.tsx +52 -0
  68. package/src/components/meter/index.tsx +55 -0
  69. package/src/components/meter/meter.module.css +23 -0
  70. package/src/components/meter/meter.stories.tsx +31 -0
  71. package/src/components/multiline-input/index.tsx +58 -0
  72. package/src/components/multiline-input/multiline-input.stories.tsx +26 -0
  73. package/src/components/number-input/index.tsx +74 -0
  74. package/src/components/number-input/number-input.module.css +41 -0
  75. package/src/components/number-input/number-input.stories.tsx +24 -0
  76. package/src/components/password-input/index.tsx +24 -0
  77. package/src/components/password-input/password-input.module.css +10 -0
  78. package/src/components/password-input/password-input.stories.tsx +24 -0
  79. package/src/components/pill/index.tsx +45 -0
  80. package/src/components/pill/pill.module.css +22 -0
  81. package/src/components/pill/pill.stories.tsx +83 -0
  82. package/src/components/popover/index.tsx +94 -0
  83. package/src/components/popover/popover.module.css +8 -0
  84. package/src/components/popover/popover.stories.tsx +53 -0
  85. package/src/components/preview-card/index.tsx +68 -0
  86. package/src/components/preview-card/preview-card.module.css +5 -0
  87. package/src/components/preview-card/preview-card.stories.tsx +58 -0
  88. package/src/components/radio/index.tsx +67 -0
  89. package/src/components/radio/radio-group.module.css +5 -0
  90. package/src/components/radio/radio.module.css +36 -0
  91. package/src/components/radio/radio.stories.tsx +27 -0
  92. package/src/components/search-bar/index.tsx +60 -0
  93. package/src/components/search-bar/search-bar.module.css +29 -0
  94. package/src/components/search-bar/search-bar.stories.tsx +37 -0
  95. package/src/components/select/index.tsx +132 -0
  96. package/src/components/select/select.module.css +63 -0
  97. package/src/components/select/select.stories.tsx +49 -0
  98. package/src/components/separator/index.tsx +28 -0
  99. package/src/components/separator/separator.module.css +24 -0
  100. package/src/components/separator/separator.stories.tsx +40 -0
  101. package/src/components/slider/index.tsx +28 -0
  102. package/src/components/slider/slider.module.css +52 -0
  103. package/src/components/slider/slider.stories.tsx +53 -0
  104. package/src/components/spinner/index.tsx +14 -0
  105. package/src/components/spinner/spinner.module.css +13 -0
  106. package/src/components/spinner/spinner.stories.tsx +17 -0
  107. package/src/components/stacked-avatars/index.tsx +88 -0
  108. package/src/components/stacked-avatars/stacked-avatars.module.css +79 -0
  109. package/src/components/stacked-avatars/stacked-avatars.stories.tsx +48 -0
  110. package/src/components/status-banner/index.tsx +96 -0
  111. package/src/components/status-banner/status-banner.module.css +52 -0
  112. package/src/components/status-banner/status-banner.stories.tsx +44 -0
  113. package/src/components/surface/index.tsx +83 -0
  114. package/src/components/surface/surface.module.css +35 -0
  115. package/src/components/surface/surface.stories.tsx +84 -0
  116. package/src/components/switch/index.tsx +23 -0
  117. package/src/components/switch/switch.module.css +45 -0
  118. package/src/components/switch/switch.stories.tsx +48 -0
  119. package/src/components/tabs/index.tsx +126 -0
  120. package/src/components/tabs/tabs.module.css +134 -0
  121. package/src/components/tabs/tabs.stories.tsx +88 -0
  122. package/src/components/text/index.tsx +69 -0
  123. package/src/components/text/text.module.css +76 -0
  124. package/src/components/text/text.stories.tsx +107 -0
  125. package/src/components/theme-provider/index.ts +2 -0
  126. package/src/components/theme-provider/theme-context.tsx +18 -0
  127. package/src/components/theme-provider/theme-provider.stories.tsx +47 -0
  128. package/src/components/theme-provider/theme-provider.tsx +77 -0
  129. package/src/components/timestamp/index.tsx +131 -0
  130. package/src/components/timestamp/timestamp.module.css +8 -0
  131. package/src/components/timestamp/timestamp.stories.tsx +37 -0
  132. package/src/components/toast/index.ts +2 -0
  133. package/src/components/toast/toast.module.css +163 -0
  134. package/src/components/toast/toast.stories.tsx +53 -0
  135. package/src/components/toast/toast.tsx +104 -0
  136. package/src/components/toast/use-toast-manager.ts +63 -0
  137. package/src/components/tooltip/index.tsx +61 -0
  138. package/src/components/tooltip/tooltip-arrow.tsx +17 -0
  139. package/src/components/tooltip/tooltip.module.css +44 -0
  140. package/src/components/tooltip/tooltip.stories.tsx +76 -0
  141. package/src/components/view/index.tsx +137 -0
  142. package/src/components/view/view.module.css +11 -0
  143. package/src/components/view/view.stories.tsx +131 -0
  144. package/src/components/view/view_colorway.module.css +280 -0
  145. package/src/components/view/view_interactive.module.css +127 -0
  146. package/src/components/view/view_loading.module.css +58 -0
  147. package/src/components/visually-hidden/index.ts +1 -0
  148. package/src/index.ts +49 -0
  149. package/src/integrations/react-markdown/index.tsx +134 -0
  150. package/src/integrations/react-markdown/react-markdown.module.css +62 -0
  151. package/src/integrations/react-markdown/react-markdown.stories.tsx +31 -0
  152. package/src/integrations/remix.ts +12 -0
  153. package/src/integrations/tailwind.css +173 -0
  154. package/src/integrations/twemoij/index.tsx +13 -0
  155. package/src/integrations/twemoij/twemoji.module.css +7 -0
  156. package/src/integrations/twemoij/twemoji.stories.tsx +40 -0
  157. package/src/stories/components/all-variants.tsx +40 -0
  158. package/src/stories/data.ts +72 -0
  159. package/src/stories/utils.ts +20 -0
  160. package/src/styles/core.css +153 -0
  161. package/src/styles/themes/dark.css +86 -0
  162. package/src/styles/themes/light.css +86 -0
  163. package/src/styles/tokens.ts +282 -0
  164. package/src/styles/transitions.module.css +31 -0
  165. package/stylelint.config.mjs +29 -0
  166. package/tsconfig.app.json +35 -0
  167. package/tsconfig.json +7 -0
  168. package/tsconfig.node.json +26 -0
  169. package/vite.config.ts +103 -0
  170. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,84 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { type ReactNode } from "react";
3
+
4
+ import { surfaceBackgrounds } from "~/stories/data";
5
+
6
+ import { Surface, type Background, type SurfaceProps } from ".";
7
+ import { tokens } from "../../styles/tokens";
8
+ import { Text } from "../text";
9
+
10
+ const meta = {
11
+ title: "Surface",
12
+ component: Surface,
13
+ parameters: { layout: "centered" },
14
+ argTypes: {
15
+ background: {
16
+ control: "select",
17
+ options: surfaceBackgrounds,
18
+ },
19
+ elevated: {
20
+ control: "boolean",
21
+ },
22
+ },
23
+ } satisfies Meta<typeof Surface>;
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ background: "default",
32
+ elevated: false,
33
+ },
34
+ render: (args) => (
35
+ <PaddedSurface {...args}>
36
+ <Text>Hello world</Text>
37
+ </PaddedSurface>
38
+ ),
39
+ };
40
+
41
+ export const Stacking: Story = {
42
+ argTypes: {
43
+ background: {
44
+ table: {
45
+ disable: true,
46
+ },
47
+ },
48
+ elevated: {
49
+ table: {
50
+ disable: true,
51
+ },
52
+ },
53
+ },
54
+ render: () => (
55
+ <PaddedSurface label="root">
56
+ <PaddedSurface label="default" elevated>
57
+ <PaddedSurface label="higher" elevated>
58
+ <PaddedSurface label="highest" elevated />
59
+ </PaddedSurface>
60
+ </PaddedSurface>
61
+ </PaddedSurface>
62
+ ),
63
+ };
64
+
65
+ function PaddedSurface({
66
+ label,
67
+ children,
68
+ ...props
69
+ }: SurfaceProps & { label?: Background; children?: ReactNode }) {
70
+ return (
71
+ <Surface
72
+ style={{ padding: tokens.space24, minWidth: tokens.space256 }}
73
+ background={label}
74
+ {...props}
75
+ >
76
+ {label ? (
77
+ <Text style={{ marginBottom: children ? tokens.space16 : 0 }}>
78
+ {label}
79
+ </Text>
80
+ ) : null}
81
+ {children}
82
+ </Surface>
83
+ );
84
+ }
@@ -0,0 +1,23 @@
1
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
2
+
3
+ import { View, type Color } from "../view";
4
+
5
+ import styles from "./switch.module.css";
6
+
7
+ export function Switch({
8
+ color = "primary",
9
+ ...props
10
+ }: SwitchPrimitive.Root.Props & {
11
+ color?: Color;
12
+ }) {
13
+ return (
14
+ <View
15
+ render={<SwitchPrimitive.Root {...props} />}
16
+ className={styles["switch"]}
17
+ interactive="fill-outline"
18
+ color={color}
19
+ >
20
+ <SwitchPrimitive.Thumb className={styles["switch__thumb"]} />
21
+ </View>
22
+ );
23
+ }
@@ -0,0 +1,45 @@
1
+ .switch {
2
+ position: relative;
3
+ border-width: 1px;
4
+ border-style: solid;
5
+ border-color: var(--surface-interactive-border);
6
+ border-radius: var(--border-radius-round);
7
+ background: var(--surface-interactive-border);
8
+ width: 40px;
9
+ height: 24px;
10
+
11
+ &[data-checked] {
12
+ background: var(--view-colorway-dimmer);
13
+ }
14
+
15
+ &[data-checked]:not([disabled], [data-disabled]) {
16
+ background: var(--view-colorway-default);
17
+
18
+ &:hover {
19
+ border-color: var(--view-colorway-stronger);
20
+ }
21
+ }
22
+ }
23
+
24
+ .switch__thumb {
25
+ --switch-size: 16px;
26
+ --switch-padding: 4px;
27
+
28
+ display: block;
29
+ position: absolute;
30
+ top: 50%;
31
+ left: var(--switch-padding);
32
+ transform: translateY(-50%);
33
+ transition:
34
+ left var(--transition-duration-snappy) ease-out,
35
+ right var(--transition-duration-snappy) ease-out;
36
+ border-radius: var(--border-radius-round);
37
+ background-color: var(--white);
38
+ aspect-ratio: 1/1;
39
+ width: var(--switch-size);
40
+ height: var(--switch-size);
41
+
42
+ &[data-checked] {
43
+ left: calc(100% - var(--switch-size) - var(--switch-padding));
44
+ }
45
+ }
@@ -0,0 +1,48 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { AllVariants } from "~/stories/components/all-variants";
4
+ import { surfaceBackgrounds } from "~/stories/data";
5
+ import { tokens } from "~/styles/tokens";
6
+
7
+ import { Switch } from ".";
8
+ import { Surface } from "../surface";
9
+
10
+ const meta = {
11
+ title: "Input/Switch",
12
+ component: Switch,
13
+ parameters: { layout: "centered" },
14
+ } satisfies Meta<typeof Switch>;
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Default: Story = {
21
+ args: {
22
+ color: "primary",
23
+ disabled: false,
24
+ },
25
+ render: (args) => <Switch {...args} />,
26
+ };
27
+
28
+ export const AllInteractiveStyles: Story = {
29
+ render: (args) => (
30
+ <AllVariants
31
+ variantName="background"
32
+ variants={surfaceBackgrounds}
33
+ style={{ backgroundColor: "transparent", flexDirection: "row" }}
34
+ element={
35
+ <Surface
36
+ style={{
37
+ padding: tokens.space16,
38
+ gap: tokens.space8,
39
+ borderWidth: "1px",
40
+ borderColor: tokens.outlineDimmest,
41
+ }}
42
+ >
43
+ <Switch {...args} />
44
+ </Surface>
45
+ }
46
+ />
47
+ ),
48
+ };
@@ -0,0 +1,126 @@
1
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
2
+ import clsx from "clsx";
3
+ import { Fragment } from "react";
4
+
5
+ import { tokens } from "~/styles/tokens";
6
+
7
+ import { Button, type ButtonProps } from "../button";
8
+ import { Icon } from "../icon";
9
+ import { Separator, type SeparatorProps } from "../separator";
10
+ import { View } from "../view";
11
+
12
+ import styles from "./tabs.module.css";
13
+
14
+ type TabsTriggerProps = {
15
+ value: TabsPrimitive.Tab.Props["value"];
16
+ /** Show indicator that tab is complete. */
17
+ complete?: boolean;
18
+ } & ButtonProps;
19
+
20
+ type TabsVariant = "default" | "progress" | "button-group";
21
+
22
+ export interface TabsProps extends TabsPrimitive.Root.Props {
23
+ /** Tabs styling that we should use. Defaults to "default". */
24
+ variant?: TabsVariant;
25
+
26
+ /** Disable all triggers */
27
+ disabled?: boolean;
28
+
29
+ tabs: Array<TabsTriggerProps>;
30
+ }
31
+
32
+ const VARIANT_SEPARATOR: Set<TabsVariant> = new Set(["default", "progress"]);
33
+
34
+ export function Tabs({
35
+ variant = "default",
36
+ disabled = false,
37
+ tabs,
38
+ className,
39
+ children,
40
+ ...props
41
+ }: TabsProps) {
42
+ return (
43
+ <View
44
+ render={
45
+ <TabsPrimitive.Root
46
+ className={clsx(
47
+ styles["tabs"],
48
+ styles[`tabs_variant_${variant}`],
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ }
54
+ >
55
+ <TabsList>
56
+ {tabs.map((tabProps, i) => (
57
+ <Fragment key={tabProps.value}>
58
+ <TabsTrigger disabled={disabled} {...tabProps} />
59
+ {i + 1 < tabs.length && VARIANT_SEPARATOR.has(variant) ? (
60
+ <TabsSeparator
61
+ orientation={variant === "progress" ? "horizontal" : "vertical"}
62
+ />
63
+ ) : null}
64
+ </Fragment>
65
+ ))}
66
+ </TabsList>
67
+ {children}
68
+ </View>
69
+ );
70
+ }
71
+
72
+ function TabsTrigger({
73
+ value,
74
+ className,
75
+ complete,
76
+ children,
77
+ ...props
78
+ }: TabsTriggerProps) {
79
+ return (
80
+ <Button
81
+ interactive="list-item"
82
+ className={clsx(styles["tabs__trigger"], className)}
83
+ render={<TabsPrimitive.Tab value={value} />}
84
+ {...props}
85
+ >
86
+ {complete ? (
87
+ <Icon
88
+ color={tokens.positiveDefault}
89
+ name="check-line"
90
+ className={styles["tabs__trigger-complete"]}
91
+ />
92
+ ) : null}
93
+ {children}
94
+ </Button>
95
+ );
96
+ }
97
+
98
+ function TabsList({ className, ...props }: TabsPrimitive.List.Props) {
99
+ return (
100
+ <View
101
+ render={
102
+ <TabsPrimitive.List
103
+ className={clsx(styles["tabs__list"], className)}
104
+ {...props}
105
+ />
106
+ }
107
+ />
108
+ );
109
+ }
110
+
111
+ function TabsSeparator({ className, ...props }: SeparatorProps) {
112
+ return (
113
+ <Separator
114
+ className={clsx(styles["tabs__separator"], className)}
115
+ {...props}
116
+ />
117
+ );
118
+ }
119
+
120
+ export function TabsPanel({ children, ...props }: TabsPrimitive.Panel.Props) {
121
+ return (
122
+ <TabsPrimitive.Panel {...props}>
123
+ <View>{children}</View>
124
+ </TabsPrimitive.Panel>
125
+ );
126
+ }
@@ -0,0 +1,134 @@
1
+ /* stylelint-disable no-descending-specificity, max-nesting-depth */
2
+
3
+ /* TODO make this not so bad... ignoring for now because variants are easiest with nesting */
4
+
5
+ .tabs {
6
+ gap: var(--space-8);
7
+ }
8
+
9
+ .tabs__list {
10
+ display: flex;
11
+ flex-direction: row;
12
+ align-items: center;
13
+ }
14
+
15
+ /* Default variant */
16
+ .tabs_variant_default {
17
+ .tabs__list {
18
+ box-shadow: inset 0 -1px 0 var(--surface-interactive-border);
19
+ }
20
+
21
+ .tabs__separator {
22
+ height: var(--space-16);
23
+ }
24
+
25
+ .tabs__trigger {
26
+ display: flex;
27
+ justify-content: center;
28
+ align-items: center;
29
+ border-width: 0;
30
+ border-bottom-width: 1px;
31
+ border-color: var(--surface-interactive-border);
32
+ border-radius: var(--border-radius-default) var(--border-radius-default) 0 0;
33
+ height: calc(var(--space-32) + var(--space-4));
34
+ }
35
+
36
+ .tabs__trigger[data-active] {
37
+ border-color: var(--foreground-dimmer);
38
+ }
39
+
40
+ .tabs__trigger:not([disabled], [data-disabled]):active {
41
+ border-color: var(--primary-default);
42
+ }
43
+ }
44
+
45
+ /* Progress variant */
46
+ .tabs_variant_progress {
47
+ .tabs__list {
48
+ justify-content: space-between;
49
+ gap: var(--space-8);
50
+ }
51
+
52
+ .tabs__separator {
53
+ display: inline-block;
54
+ flex-shrink: 1;
55
+ width: var(--space-16);
56
+ }
57
+
58
+ .tabs__trigger {
59
+ position: relative;
60
+ flex-wrap: nowrap;
61
+ gap: var(--space-4);
62
+ transition-property: none;
63
+ user-select: none;
64
+ }
65
+
66
+ .tabs__trigger[data-active] {
67
+ background-color: var(--surface-interactive-background);
68
+ }
69
+
70
+ .tabs__trigger-complete {
71
+ position: absolute;
72
+ top: 0;
73
+ right: 0;
74
+ transform: translate(var(--space-6), calc(-1 * var(--space-6)));
75
+ border-radius: var(--border-radius-default);
76
+ background-color: var(--positive-dimmest);
77
+ aspect-ratio: 1 / 1;
78
+ }
79
+ }
80
+
81
+ /* Button group variant */
82
+ .tabs_variant_button-group {
83
+ .tabs__trigger {
84
+ transition-property: background-color, box-shadow;
85
+ border-radius: 0;
86
+ }
87
+
88
+ .tabs__trigger:first-child {
89
+ border-top-left-radius: var(--border-radius-default);
90
+ border-bottom-left-radius: var(--border-radius-default);
91
+ }
92
+
93
+ .tabs__trigger:last-child {
94
+ border-top-right-radius: var(--border-radius-default);
95
+ border-bottom-right-radius: var(--border-radius-default);
96
+ }
97
+
98
+ .tabs__trigger:not([disabled], [data-disabled]) {
99
+ background-color: var(--surface-interactive-background);
100
+
101
+ @media (hover: hover) {
102
+ &:hover {
103
+ background-color: var(--surface-interactive-background-active);
104
+ }
105
+ }
106
+ }
107
+
108
+ /* TODO: make this not repetitive */
109
+
110
+ /* Copied from view colorway. */
111
+ .tabs__trigger[data-active] {
112
+ background-color: var(--view-colorway-dimmest);
113
+ color: var(--view-colorway-default);
114
+
115
+ &:not([disabled], [data-disabled]) {
116
+ background-color: var(--view-colorway-dimmer);
117
+ color: var(--view-colorway-strongest);
118
+
119
+ &:focus {
120
+ box-shadow: 0 0 0 2px var(--view-colorway-stronger);
121
+
122
+ &:not(:focus-visible) {
123
+ box-shadow: none;
124
+ }
125
+ }
126
+
127
+ @media (hover: hover) {
128
+ &:hover {
129
+ background-color: var(--view-colorway-default);
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { Tabs, TabsPanel } from ".";
4
+
5
+ const meta = {
6
+ title: "Tabs",
7
+ component: Tabs,
8
+ parameters: { layout: "centered" },
9
+ } satisfies Meta<typeof Tabs>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ variant: "default",
17
+ defaultValue: "tab1",
18
+ tabs: [
19
+ { value: "tab1", leftIcon: "code-view", children: "Tab 1" },
20
+ { value: "tab2", leftIcon: "bar-chart-line", children: "Tab 2" },
21
+ { value: "tab3", leftIcon: "send-plane-line", children: "Tab 3" },
22
+ ],
23
+ },
24
+ render: (args) => (
25
+ <Tabs {...args}>
26
+ <TabsPanel value="tab1">Tab 1</TabsPanel>
27
+ <TabsPanel value="tab2">Tab 2</TabsPanel>
28
+ <TabsPanel value="tab3">Tab 3</TabsPanel>
29
+ </Tabs>
30
+ ),
31
+ };
32
+
33
+ export const Progress: Story = {
34
+ args: {
35
+ variant: "progress",
36
+ tabs: [
37
+ {
38
+ value: "tab1",
39
+ leftIcon: "code-view",
40
+ children: "Tab 1",
41
+ complete: true,
42
+ },
43
+ { value: "tab2", leftIcon: "bar-chart-line", children: "Tab 2" },
44
+ {
45
+ value: "tab3",
46
+ leftIcon: "send-plane-line",
47
+ children: "Tab 3",
48
+ },
49
+ ],
50
+ },
51
+ render: (args) => (
52
+ <Tabs defaultValue="tab1" {...args}>
53
+ <TabsPanel value="tab1">Tab 1</TabsPanel>
54
+ <TabsPanel value="tab2">Tab 2</TabsPanel>
55
+ <TabsPanel value="tab3">Tab 3</TabsPanel>
56
+ </Tabs>
57
+ ),
58
+ };
59
+
60
+ export const ButtonGroup: Story = {
61
+ args: {
62
+ variant: "button-group",
63
+ tabs: [
64
+ {
65
+ value: "tab1",
66
+ leftIcon: "code-view",
67
+ children: "Tab 1",
68
+ },
69
+ {
70
+ value: "tab2",
71
+ leftIcon: "bar-chart-line",
72
+ children: "Tab 2",
73
+ },
74
+ {
75
+ value: "tab3",
76
+ leftIcon: "send-plane-line",
77
+ children: "Tab 3",
78
+ },
79
+ ],
80
+ },
81
+ render: (args) => (
82
+ <Tabs defaultValue="tab3" {...args}>
83
+ <TabsPanel value="tab1">Tab 1</TabsPanel>
84
+ <TabsPanel value="tab2">Tab 2</TabsPanel>
85
+ <TabsPanel value="tab3">Tab 3</TabsPanel>
86
+ </Tabs>
87
+ ),
88
+ };
@@ -0,0 +1,69 @@
1
+ import { mergeProps, useRender } from "@base-ui/react";
2
+ import clsx from "clsx";
3
+
4
+ import type { Size } from "~/styles/tokens";
5
+
6
+ import viewStyles from "../view/view.module.css";
7
+ import styles from "./text.module.css";
8
+
9
+ type ColorVariants = "inherit" | "default" | "dimmer" | "dimmest";
10
+
11
+ export interface TextProps extends Omit<
12
+ useRender.ComponentProps<"span">,
13
+ "color"
14
+ > {
15
+ /**
16
+ * The color variant of the text.
17
+ * Defaults to "default".
18
+ */
19
+ color?: ColorVariants;
20
+
21
+ /**
22
+ * The size variant of the text.
23
+ * Defaults to "md".
24
+ */
25
+ size?: Size;
26
+
27
+ /**
28
+ * Maximum number of lines that should be displayed.
29
+ * Defaults to 1 (the minimum).
30
+ */
31
+ maxLines?: number;
32
+
33
+ /**
34
+ * A convenient shorthand that has the same effect as setting maxLines to Infinity
35
+ */
36
+ multiline?: boolean;
37
+ }
38
+
39
+ export function Text({
40
+ color = "default",
41
+ size = "md",
42
+ maxLines = 1,
43
+ multiline,
44
+ render,
45
+ ...props
46
+ }: TextProps) {
47
+ const element = useRender({
48
+ defaultTagName: "span",
49
+ render,
50
+ props: mergeProps(
51
+ {
52
+ className: clsx(
53
+ viewStyles["view"],
54
+ styles["text"],
55
+ styles[`text_color_${color}`],
56
+ styles[`text_size_${size}`],
57
+ !multiline && styles["text_clip-line"],
58
+ !multiline && maxLines > 1 && styles["text_clamp-lines"],
59
+ ),
60
+ style: {
61
+ "--text-max-lines": Math.max(maxLines, 1),
62
+ },
63
+ },
64
+ props,
65
+ ),
66
+ });
67
+
68
+ return element;
69
+ }
@@ -0,0 +1,76 @@
1
+ .text {
2
+ display: inline;
3
+ vertical-align: middle;
4
+ overflow: hidden;
5
+ overflow-wrap: break-word;
6
+ }
7
+
8
+ .text_clip-line {
9
+ display: inline-block;
10
+ max-width: 100%;
11
+ text-overflow: ellipsis;
12
+ white-space: nowrap;
13
+ }
14
+
15
+ .text_clamp-lines {
16
+ display: -webkit-box;
17
+ line-clamp: var(--ui-text-max-lines);
18
+ -webkit-line-clamp: var(--ui-text-max-lines);
19
+ -webkit-box-orient: vertical;
20
+ visibility: visible;
21
+ white-space: break-spaces;
22
+ }
23
+
24
+ /* Text color variants */
25
+ .text_color_inherit {
26
+ color: inherit;
27
+ }
28
+
29
+ .text_color_default {
30
+ color: var(--foreground-default);
31
+ }
32
+
33
+ .text_color_dimmer {
34
+ color: var(--foreground-dimmer);
35
+ }
36
+
37
+ .text_color_dimmest {
38
+ color: var(--foreground-dimmest);
39
+ }
40
+
41
+ /* Text size variants */
42
+ .text_size_sm {
43
+ font-weight: var(--font-weight-regular);
44
+ font-size: var(--font-size-small);
45
+ line-height: var(--line-height-small);
46
+ }
47
+
48
+ .text_size_md {
49
+ font-weight: var(--font-weight-regular);
50
+ font-size: var(--font-size-default);
51
+ line-height: var(--line-height-default);
52
+ }
53
+
54
+ .text_size_lg {
55
+ font-weight: var(--font-weight-medium);
56
+ font-size: var(--font-size-subhead-default);
57
+ line-height: var(--line-height-subhead-default);
58
+ }
59
+
60
+ .text_size_xl {
61
+ font-weight: var(--font-weight-medium);
62
+ font-size: var(--font-size-subhead-big);
63
+ line-height: var(--line-height-subhead-big);
64
+ }
65
+
66
+ .text_size_2xl {
67
+ font-weight: var(--font-weight-medium);
68
+ font-size: var(--font-size-header-default);
69
+ line-height: var(--line-height-header-default);
70
+ }
71
+
72
+ .text_size_3xl {
73
+ font-weight: var(--font-weight-medium);
74
+ font-size: var(--font-size-header-big);
75
+ line-height: var(--line-height-header-big);
76
+ }