@papernote/ui 1.2.0 → 1.3.1
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.
- package/dist/components/Box.d.ts +2 -1
- package/dist/components/Box.d.ts.map +1 -1
- package/dist/components/Button.d.ts +10 -1
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Card.d.ts +11 -2
- package/dist/components/Card.d.ts.map +1 -1
- package/dist/components/DataTable.d.ts +17 -3
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/EmptyState.d.ts +3 -1
- package/dist/components/EmptyState.d.ts.map +1 -1
- package/dist/components/Grid.d.ts +4 -2
- package/dist/components/Grid.d.ts.map +1 -1
- package/dist/components/Input.d.ts +2 -0
- package/dist/components/Input.d.ts.map +1 -1
- package/dist/components/MultiSelect.d.ts +13 -1
- package/dist/components/MultiSelect.d.ts.map +1 -1
- package/dist/components/Spreadsheet.d.ts +5 -1
- package/dist/components/Spreadsheet.d.ts.map +1 -1
- package/dist/components/Stack.d.ts +25 -5
- package/dist/components/Stack.d.ts.map +1 -1
- package/dist/components/Text.d.ts +20 -4
- package/dist/components/Text.d.ts.map +1 -1
- package/dist/components/Textarea.d.ts +2 -0
- package/dist/components/Textarea.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +115 -49
- package/dist/index.esm.js +187 -8563
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +186 -8563
- package/dist/index.js.map +1 -1
- package/dist/styles.css +8 -51
- package/package.json +4 -4
- package/src/components/Box.stories.tsx +377 -0
- package/src/components/Box.tsx +8 -4
- package/src/components/Button.tsx +23 -10
- package/src/components/Card.tsx +20 -5
- package/src/components/DataTable.stories.tsx +36 -25
- package/src/components/DataTable.tsx +95 -5
- package/src/components/EmptyState.stories.tsx +124 -72
- package/src/components/EmptyState.tsx +10 -0
- package/src/components/Grid.stories.tsx +348 -0
- package/src/components/Grid.tsx +12 -5
- package/src/components/Input.tsx +12 -2
- package/src/components/MultiSelect.tsx +41 -10
- package/src/components/Spreadsheet.tsx +8 -57
- package/src/components/Stack.stories.tsx +24 -1
- package/src/components/Stack.tsx +40 -10
- package/src/components/Text.stories.tsx +273 -0
- package/src/components/Text.tsx +33 -8
- package/src/components/Textarea.tsx +32 -21
- package/src/components/index.ts +1 -4
- package/dist/components/Table.d.ts +0 -26
- package/dist/components/Table.d.ts.map +0 -1
- package/src/components/Table.tsx +0 -239
package/src/components/Stack.tsx
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
// Stack Component - Vertical or horizontal stacking layout
|
|
2
2
|
// Provides consistent spacing between child elements
|
|
3
3
|
|
|
4
|
-
import React from 'react';
|
|
4
|
+
import React, { forwardRef } from 'react';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type SpacingValue = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
7
|
+
|
|
8
|
+
export interface StackProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
9
|
/** Content to stack */
|
|
8
10
|
children: React.ReactNode;
|
|
9
11
|
/** Direction of stack */
|
|
10
12
|
direction?: 'vertical' | 'horizontal';
|
|
11
|
-
/** Spacing between items */
|
|
12
|
-
spacing?:
|
|
13
|
+
/** Spacing between items (alias: gap) */
|
|
14
|
+
spacing?: SpacingValue;
|
|
15
|
+
/** Spacing between items (alias for spacing - for developer convenience) */
|
|
16
|
+
gap?: SpacingValue;
|
|
13
17
|
/** Alignment of items */
|
|
14
18
|
align?: 'start' | 'center' | 'end' | 'stretch';
|
|
15
19
|
/** Justify content */
|
|
@@ -23,23 +27,45 @@ export interface StackProps {
|
|
|
23
27
|
/**
|
|
24
28
|
* Stack component for arranging children vertically or horizontally with consistent spacing.
|
|
25
29
|
*
|
|
26
|
-
*
|
|
30
|
+
* Supports ref forwarding for DOM access.
|
|
31
|
+
*
|
|
32
|
+
* Spacing scale (use either `spacing` or `gap` prop - they're aliases):
|
|
27
33
|
* - none: 0
|
|
28
34
|
* - xs: 0.5rem (2)
|
|
29
35
|
* - sm: 0.75rem (3)
|
|
30
36
|
* - md: 1.5rem (6)
|
|
31
37
|
* - lg: 2rem (8)
|
|
32
38
|
* - xl: 3rem (12)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* // Using spacing prop
|
|
43
|
+
* <Stack spacing="md">
|
|
44
|
+
* <Card>Item 1</Card>
|
|
45
|
+
* <Card>Item 2</Card>
|
|
46
|
+
* </Stack>
|
|
47
|
+
*
|
|
48
|
+
* // Using gap prop (alias)
|
|
49
|
+
* <Stack gap="md">
|
|
50
|
+
* <Card>Item 1</Card>
|
|
51
|
+
* <Card>Item 2</Card>
|
|
52
|
+
* </Stack>
|
|
53
|
+
* ```
|
|
33
54
|
*/
|
|
34
|
-
export const Stack
|
|
55
|
+
export const Stack = forwardRef<HTMLDivElement, StackProps>(({
|
|
35
56
|
children,
|
|
36
57
|
direction = 'vertical',
|
|
37
|
-
spacing
|
|
58
|
+
spacing,
|
|
59
|
+
gap,
|
|
38
60
|
align = 'stretch',
|
|
39
61
|
justify = 'start',
|
|
40
62
|
wrap = false,
|
|
41
63
|
className = '',
|
|
42
|
-
|
|
64
|
+
...htmlProps
|
|
65
|
+
}, ref) => {
|
|
66
|
+
// Use gap as alias for spacing (spacing takes precedence if both provided)
|
|
67
|
+
const effectiveSpacing = spacing ?? gap ?? 'md';
|
|
68
|
+
|
|
43
69
|
const spacingClasses = {
|
|
44
70
|
vertical: {
|
|
45
71
|
none: '',
|
|
@@ -76,11 +102,13 @@ export const Stack: React.FC<StackProps> = ({
|
|
|
76
102
|
|
|
77
103
|
return (
|
|
78
104
|
<div
|
|
105
|
+
ref={ref}
|
|
106
|
+
{...htmlProps}
|
|
79
107
|
className={`
|
|
80
108
|
flex
|
|
81
109
|
${direction === 'vertical' ? 'flex-col' : 'flex-row'}
|
|
82
110
|
${wrap ? 'flex-wrap' : ''}
|
|
83
|
-
${spacingClasses[direction][
|
|
111
|
+
${spacingClasses[direction][effectiveSpacing]}
|
|
84
112
|
${alignClasses[align]}
|
|
85
113
|
${justifyClasses[justify]}
|
|
86
114
|
${className}
|
|
@@ -89,6 +117,8 @@ export const Stack: React.FC<StackProps> = ({
|
|
|
89
117
|
{children}
|
|
90
118
|
</div>
|
|
91
119
|
);
|
|
92
|
-
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
Stack.displayName = 'Stack';
|
|
93
123
|
|
|
94
124
|
export default Stack;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Text } from './Text';
|
|
3
|
+
import Stack from './Stack';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Typography/Text',
|
|
7
|
+
component: Text,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
Text component for consistent typography across the application.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
- **Semantic elements**: Render as p, span, div, h1-h6, or label
|
|
17
|
+
- **Size scale**: xs, sm, base, lg, xl, 2xl
|
|
18
|
+
- **Weight options**: normal, medium, semibold, bold
|
|
19
|
+
- **Color variants**: primary, secondary, muted, accent, error, success, warning
|
|
20
|
+
- **Text alignment**: left, center, right
|
|
21
|
+
- **Truncation**: Single line truncate or multi-line clamp (1-6 lines)
|
|
22
|
+
- **Transform**: uppercase, lowercase, capitalize
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
\`\`\`tsx
|
|
27
|
+
import { Text } from 'notebook-ui';
|
|
28
|
+
|
|
29
|
+
<Text size="lg" weight="semibold" color="primary">
|
|
30
|
+
Hello World
|
|
31
|
+
</Text>
|
|
32
|
+
|
|
33
|
+
<Text color="warning">Warning message</Text>
|
|
34
|
+
|
|
35
|
+
<Text truncate>This text will be truncated...</Text>
|
|
36
|
+
|
|
37
|
+
<Text lineClamp={2}>
|
|
38
|
+
This text will be clamped to 2 lines with ellipsis...
|
|
39
|
+
</Text>
|
|
40
|
+
\`\`\`
|
|
41
|
+
`,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
tags: ['autodocs'],
|
|
46
|
+
argTypes: {
|
|
47
|
+
as: {
|
|
48
|
+
control: 'select',
|
|
49
|
+
options: ['p', 'span', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'label'],
|
|
50
|
+
description: 'HTML element to render',
|
|
51
|
+
},
|
|
52
|
+
size: {
|
|
53
|
+
control: 'select',
|
|
54
|
+
options: ['xs', 'sm', 'base', 'lg', 'xl', '2xl'],
|
|
55
|
+
description: 'Font size',
|
|
56
|
+
},
|
|
57
|
+
weight: {
|
|
58
|
+
control: 'select',
|
|
59
|
+
options: ['normal', 'medium', 'semibold', 'bold'],
|
|
60
|
+
description: 'Font weight',
|
|
61
|
+
},
|
|
62
|
+
color: {
|
|
63
|
+
control: 'select',
|
|
64
|
+
options: ['primary', 'secondary', 'muted', 'accent', 'error', 'success', 'warning'],
|
|
65
|
+
description: 'Text color',
|
|
66
|
+
},
|
|
67
|
+
align: {
|
|
68
|
+
control: 'select',
|
|
69
|
+
options: ['left', 'center', 'right'],
|
|
70
|
+
description: 'Text alignment',
|
|
71
|
+
},
|
|
72
|
+
truncate: {
|
|
73
|
+
control: 'boolean',
|
|
74
|
+
description: 'Truncate text with ellipsis (single line)',
|
|
75
|
+
},
|
|
76
|
+
lineClamp: {
|
|
77
|
+
control: 'select',
|
|
78
|
+
options: [undefined, 1, 2, 3, 4, 5, 6],
|
|
79
|
+
description: 'Clamp text to specific number of lines',
|
|
80
|
+
},
|
|
81
|
+
transform: {
|
|
82
|
+
control: 'select',
|
|
83
|
+
options: [undefined, 'uppercase', 'lowercase', 'capitalize', 'normal'],
|
|
84
|
+
description: 'Text transform',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
} satisfies Meta<typeof Text>;
|
|
88
|
+
|
|
89
|
+
export default meta;
|
|
90
|
+
type Story = StoryObj<typeof meta>;
|
|
91
|
+
|
|
92
|
+
export const Default: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
children: 'The quick brown fox jumps over the lazy dog.',
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const Sizes: Story = {
|
|
99
|
+
render: () => (
|
|
100
|
+
<Stack spacing="sm">
|
|
101
|
+
<Text size="xs">Extra Small (xs) - 12px</Text>
|
|
102
|
+
<Text size="sm">Small (sm) - 14px</Text>
|
|
103
|
+
<Text size="base">Base (base) - 16px</Text>
|
|
104
|
+
<Text size="lg">Large (lg) - 18px</Text>
|
|
105
|
+
<Text size="xl">Extra Large (xl) - 20px</Text>
|
|
106
|
+
<Text size="2xl">2X Large (2xl) - 24px</Text>
|
|
107
|
+
</Stack>
|
|
108
|
+
),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const Weights: Story = {
|
|
112
|
+
render: () => (
|
|
113
|
+
<Stack spacing="sm">
|
|
114
|
+
<Text weight="normal">Normal weight - 400</Text>
|
|
115
|
+
<Text weight="medium">Medium weight - 500</Text>
|
|
116
|
+
<Text weight="semibold">Semibold weight - 600</Text>
|
|
117
|
+
<Text weight="bold">Bold weight - 700</Text>
|
|
118
|
+
</Stack>
|
|
119
|
+
),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* All available color variants including the new `warning` color.
|
|
124
|
+
*/
|
|
125
|
+
export const Colors: Story = {
|
|
126
|
+
render: () => (
|
|
127
|
+
<Stack spacing="sm">
|
|
128
|
+
<Text color="primary">Primary - Default text color</Text>
|
|
129
|
+
<Text color="secondary">Secondary - Slightly muted</Text>
|
|
130
|
+
<Text color="muted">Muted - Subdued text</Text>
|
|
131
|
+
<Text color="accent">Accent - Branded color</Text>
|
|
132
|
+
<Text color="success">Success - Positive feedback</Text>
|
|
133
|
+
<Text color="warning">Warning - Caution messages</Text>
|
|
134
|
+
<Text color="error">Error - Error messages</Text>
|
|
135
|
+
</Stack>
|
|
136
|
+
),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The warning color is useful for displaying caution messages,
|
|
141
|
+
* threshold alerts, or status indicators that need attention.
|
|
142
|
+
*/
|
|
143
|
+
export const WarningColor: Story = {
|
|
144
|
+
render: () => (
|
|
145
|
+
<Stack spacing="md">
|
|
146
|
+
<Text color="warning" size="lg" weight="semibold">
|
|
147
|
+
Warning: Your subscription expires in 3 days
|
|
148
|
+
</Text>
|
|
149
|
+
<Text color="warning">
|
|
150
|
+
Storage usage is at 85% capacity
|
|
151
|
+
</Text>
|
|
152
|
+
<Text color="warning" size="sm">
|
|
153
|
+
This action cannot be undone
|
|
154
|
+
</Text>
|
|
155
|
+
</Stack>
|
|
156
|
+
),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const Alignment: Story = {
|
|
160
|
+
render: () => (
|
|
161
|
+
<Stack spacing="sm" style={{ width: '300px' }}>
|
|
162
|
+
<Text align="left">Left aligned text</Text>
|
|
163
|
+
<Text align="center">Center aligned text</Text>
|
|
164
|
+
<Text align="right">Right aligned text</Text>
|
|
165
|
+
</Stack>
|
|
166
|
+
),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const Truncate: Story = {
|
|
170
|
+
render: () => (
|
|
171
|
+
<div style={{ width: '200px' }}>
|
|
172
|
+
<Text truncate>
|
|
173
|
+
This is a very long text that will be truncated with an ellipsis when it overflows the container.
|
|
174
|
+
</Text>
|
|
175
|
+
</div>
|
|
176
|
+
),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const LineClamp: Story = {
|
|
180
|
+
render: () => (
|
|
181
|
+
<Stack spacing="lg">
|
|
182
|
+
<div style={{ width: '300px' }}>
|
|
183
|
+
<Text size="sm" color="muted" weight="medium">1 Line Clamp:</Text>
|
|
184
|
+
<Text lineClamp={1}>
|
|
185
|
+
This is a very long text that will be clamped to one line with an ellipsis at the end.
|
|
186
|
+
</Text>
|
|
187
|
+
</div>
|
|
188
|
+
<div style={{ width: '300px' }}>
|
|
189
|
+
<Text size="sm" color="muted" weight="medium">2 Line Clamp:</Text>
|
|
190
|
+
<Text lineClamp={2}>
|
|
191
|
+
This is a very long text that will be clamped to two lines with an ellipsis at the end.
|
|
192
|
+
It demonstrates how multi-line truncation works with the lineClamp prop.
|
|
193
|
+
</Text>
|
|
194
|
+
</div>
|
|
195
|
+
<div style={{ width: '300px' }}>
|
|
196
|
+
<Text size="sm" color="muted" weight="medium">3 Line Clamp:</Text>
|
|
197
|
+
<Text lineClamp={3}>
|
|
198
|
+
This is a very long text that will be clamped to three lines with an ellipsis at the end.
|
|
199
|
+
It demonstrates how multi-line truncation works with the lineClamp prop. This is useful
|
|
200
|
+
for card descriptions or preview text that needs to fit a specific space.
|
|
201
|
+
</Text>
|
|
202
|
+
</div>
|
|
203
|
+
</Stack>
|
|
204
|
+
),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const Transform: Story = {
|
|
208
|
+
render: () => (
|
|
209
|
+
<Stack spacing="sm">
|
|
210
|
+
<Text transform="uppercase">uppercase text</Text>
|
|
211
|
+
<Text transform="lowercase">LOWERCASE TEXT</Text>
|
|
212
|
+
<Text transform="capitalize">capitalize each word</Text>
|
|
213
|
+
<Text transform="normal">Normal Text (no transform)</Text>
|
|
214
|
+
</Stack>
|
|
215
|
+
),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const SemanticElements: Story = {
|
|
219
|
+
render: () => (
|
|
220
|
+
<Stack spacing="sm">
|
|
221
|
+
<Text as="h1" size="2xl" weight="bold">Heading 1</Text>
|
|
222
|
+
<Text as="h2" size="xl" weight="semibold">Heading 2</Text>
|
|
223
|
+
<Text as="h3" size="lg" weight="semibold">Heading 3</Text>
|
|
224
|
+
<Text as="p">Paragraph text</Text>
|
|
225
|
+
<Text as="span" color="muted">Inline span text</Text>
|
|
226
|
+
<Text as="label" weight="medium">Label text</Text>
|
|
227
|
+
</Stack>
|
|
228
|
+
),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const CombinedStyles: Story = {
|
|
232
|
+
render: () => (
|
|
233
|
+
<Stack spacing="md">
|
|
234
|
+
<Text as="h1" size="2xl" weight="bold" color="primary">
|
|
235
|
+
Dashboard Overview
|
|
236
|
+
</Text>
|
|
237
|
+
<Text color="secondary">
|
|
238
|
+
Welcome back! Here's what's happening with your projects today.
|
|
239
|
+
</Text>
|
|
240
|
+
<Text size="sm" color="muted">
|
|
241
|
+
Last updated: 5 minutes ago
|
|
242
|
+
</Text>
|
|
243
|
+
<Text color="success" weight="medium">
|
|
244
|
+
All systems operational
|
|
245
|
+
</Text>
|
|
246
|
+
<Text color="warning" weight="medium">
|
|
247
|
+
2 items need attention
|
|
248
|
+
</Text>
|
|
249
|
+
<Text color="error" weight="medium">
|
|
250
|
+
1 critical error detected
|
|
251
|
+
</Text>
|
|
252
|
+
</Stack>
|
|
253
|
+
),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export const StatusMessages: Story = {
|
|
257
|
+
render: () => (
|
|
258
|
+
<Stack spacing="sm">
|
|
259
|
+
<Text color="success" size="sm">
|
|
260
|
+
✓ Changes saved successfully
|
|
261
|
+
</Text>
|
|
262
|
+
<Text color="warning" size="sm">
|
|
263
|
+
⚠ Your session will expire in 5 minutes
|
|
264
|
+
</Text>
|
|
265
|
+
<Text color="error" size="sm">
|
|
266
|
+
✗ Failed to connect to server
|
|
267
|
+
</Text>
|
|
268
|
+
<Text color="muted" size="sm">
|
|
269
|
+
Last sync: 2 hours ago
|
|
270
|
+
</Text>
|
|
271
|
+
</Stack>
|
|
272
|
+
),
|
|
273
|
+
};
|
package/src/components/Text.tsx
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// Text Component - Typography with consistent styling
|
|
2
2
|
// Provides semantic text elements with design system styling
|
|
3
3
|
|
|
4
|
-
import React from 'react';
|
|
4
|
+
import React, { forwardRef } from 'react';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type TextElement = 'p' | 'span' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label';
|
|
7
|
+
|
|
8
|
+
export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'> {
|
|
7
9
|
/** Text content */
|
|
8
10
|
children: React.ReactNode;
|
|
9
11
|
/** HTML element to render */
|
|
10
|
-
as?:
|
|
12
|
+
as?: TextElement;
|
|
11
13
|
/** Size variant */
|
|
12
14
|
size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
|
|
13
15
|
/** Weight variant */
|
|
14
16
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
15
17
|
/** Color variant */
|
|
16
|
-
color?: 'primary' | 'secondary' | 'muted' | 'accent' | 'error' | 'success';
|
|
18
|
+
color?: 'primary' | 'secondary' | 'muted' | 'accent' | 'error' | 'success' | 'warning';
|
|
17
19
|
/** Text alignment */
|
|
18
20
|
align?: 'left' | 'center' | 'right';
|
|
19
21
|
/** Truncate text with ellipsis (single line) */
|
|
@@ -29,6 +31,8 @@ export interface TextProps {
|
|
|
29
31
|
/**
|
|
30
32
|
* Text component for consistent typography across the application.
|
|
31
33
|
*
|
|
34
|
+
* Supports ref forwarding for DOM access.
|
|
35
|
+
*
|
|
32
36
|
* Size scale:
|
|
33
37
|
* - xs: 0.75rem (12px)
|
|
34
38
|
* - sm: 0.875rem (14px)
|
|
@@ -36,8 +40,21 @@ export interface TextProps {
|
|
|
36
40
|
* - lg: 1.125rem (18px)
|
|
37
41
|
* - xl: 1.25rem (20px)
|
|
38
42
|
* - 2xl: 1.5rem (24px)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <Text size="lg" weight="semibold" color="primary">
|
|
47
|
+
* Hello World
|
|
48
|
+
* </Text>
|
|
49
|
+
*
|
|
50
|
+
* <Text color="warning">Warning message</Text>
|
|
51
|
+
*
|
|
52
|
+
* // With ref
|
|
53
|
+
* const textRef = useRef<HTMLParagraphElement>(null);
|
|
54
|
+
* <Text ref={textRef}>Measurable text</Text>
|
|
55
|
+
* ```
|
|
39
56
|
*/
|
|
40
|
-
export const Text
|
|
57
|
+
export const Text = forwardRef<HTMLElement, TextProps>(({
|
|
41
58
|
children,
|
|
42
59
|
as: Component = 'p',
|
|
43
60
|
size = 'base',
|
|
@@ -48,7 +65,8 @@ export const Text: React.FC<TextProps> = ({
|
|
|
48
65
|
lineClamp,
|
|
49
66
|
transform,
|
|
50
67
|
className = '',
|
|
51
|
-
|
|
68
|
+
...htmlProps
|
|
69
|
+
}, ref) => {
|
|
52
70
|
const sizeClasses = {
|
|
53
71
|
xs: 'text-xs',
|
|
54
72
|
sm: 'text-sm',
|
|
@@ -72,6 +90,7 @@ export const Text: React.FC<TextProps> = ({
|
|
|
72
90
|
accent: 'text-primary-600',
|
|
73
91
|
error: 'text-error-600',
|
|
74
92
|
success: 'text-success-600',
|
|
93
|
+
warning: 'text-warning-600',
|
|
75
94
|
};
|
|
76
95
|
|
|
77
96
|
const alignClasses = {
|
|
@@ -109,10 +128,16 @@ export const Text: React.FC<TextProps> = ({
|
|
|
109
128
|
].filter(Boolean).join(' ');
|
|
110
129
|
|
|
111
130
|
return (
|
|
112
|
-
<Component
|
|
131
|
+
<Component
|
|
132
|
+
ref={ref as any}
|
|
133
|
+
className={classes}
|
|
134
|
+
{...htmlProps}
|
|
135
|
+
>
|
|
113
136
|
{children}
|
|
114
137
|
</Component>
|
|
115
138
|
);
|
|
116
|
-
};
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
Text.displayName = 'Text';
|
|
117
142
|
|
|
118
143
|
export default Text;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { forwardRef, useEffect, useRef } from 'react';
|
|
2
|
-
import { AlertCircle, CheckCircle, AlertTriangle } from 'lucide-react';
|
|
2
|
+
import { AlertCircle, CheckCircle, AlertTriangle, Loader2 } from 'lucide-react';
|
|
3
3
|
|
|
4
4
|
export type ValidationState = 'error' | 'success' | 'warning' | null;
|
|
5
5
|
|
|
@@ -18,6 +18,8 @@ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextArea
|
|
|
18
18
|
maxRows?: number;
|
|
19
19
|
/** Resize behavior (default: 'vertical') - overridden to 'none' when autoExpand is true */
|
|
20
20
|
resize?: 'none' | 'vertical' | 'horizontal' | 'both';
|
|
21
|
+
/** Show loading spinner (for async operations like auto-save) */
|
|
22
|
+
loading?: boolean;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
@@ -33,6 +35,7 @@ const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
33
35
|
minRows = 2,
|
|
34
36
|
maxRows = 10,
|
|
35
37
|
resize = 'vertical',
|
|
38
|
+
loading = false,
|
|
36
39
|
className = '',
|
|
37
40
|
id,
|
|
38
41
|
value,
|
|
@@ -136,26 +139,34 @@ const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
136
139
|
</label>
|
|
137
140
|
)}
|
|
138
141
|
|
|
139
|
-
{/* Textarea */}
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
{/* Textarea with loading indicator */}
|
|
143
|
+
<div className="relative">
|
|
144
|
+
<textarea
|
|
145
|
+
ref={textareaRef}
|
|
146
|
+
id={textareaId}
|
|
147
|
+
value={value}
|
|
148
|
+
maxLength={maxLength}
|
|
149
|
+
rows={autoExpand ? minRows : rows}
|
|
150
|
+
className={`
|
|
151
|
+
block w-full px-4 py-3 border rounded-lg text-sm text-ink-800 placeholder-ink-400
|
|
152
|
+
bg-white bg-subtle-grain transition-all duration-200
|
|
153
|
+
focus:outline-none focus:ring-2 ${getResizeClass()}
|
|
154
|
+
disabled:bg-paper-100 disabled:text-ink-400 disabled:cursor-not-allowed disabled:opacity-60
|
|
155
|
+
${getValidationClasses()}
|
|
156
|
+
${loading ? 'pr-10' : ''}
|
|
157
|
+
${className}
|
|
158
|
+
`}
|
|
159
|
+
aria-invalid={validationState === 'error'}
|
|
160
|
+
aria-describedby={validationMessage || helperText ? `${textareaId}-help` : undefined}
|
|
161
|
+
aria-required={props.required}
|
|
162
|
+
{...props}
|
|
163
|
+
/>
|
|
164
|
+
{loading && (
|
|
165
|
+
<div className="absolute top-3 right-3 pointer-events-none">
|
|
166
|
+
<Loader2 className="h-5 w-5 text-ink-400 animate-spin" />
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
159
170
|
|
|
160
171
|
{/* Helper Text / Validation Message / Character Count */}
|
|
161
172
|
{((helperText || validationMessage) || (showCharCount && maxLength)) && (
|
package/src/components/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { default as Select } from './Select';
|
|
|
13
13
|
export type { SelectProps, SelectOption, SelectHandle } from './Select';
|
|
14
14
|
|
|
15
15
|
export { default as MultiSelect } from './MultiSelect';
|
|
16
|
-
export type { MultiSelectProps, MultiSelectOption } from './MultiSelect';
|
|
16
|
+
export type { MultiSelectProps, MultiSelectOption, MultiSelectHandle } from './MultiSelect';
|
|
17
17
|
|
|
18
18
|
export { default as Switch } from './Switch';
|
|
19
19
|
export type { SwitchProps } from './Switch';
|
|
@@ -205,9 +205,6 @@ export { default as StepIndicator } from './StepIndicator';
|
|
|
205
205
|
export type { StepIndicatorProps, Step } from './StepIndicator';
|
|
206
206
|
|
|
207
207
|
// Data Display Components
|
|
208
|
-
export { default as Table } from './Table';
|
|
209
|
-
export type { TableProps, Column, SortDirection } from './Table';
|
|
210
|
-
|
|
211
208
|
export { default as Badge } from './Badge';
|
|
212
209
|
export type { BadgeProps } from './Badge';
|
|
213
210
|
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export type SortDirection = 'asc' | 'desc' | null;
|
|
3
|
-
export interface Column<T> {
|
|
4
|
-
key: string;
|
|
5
|
-
header: string;
|
|
6
|
-
accessor?: (row: T) => React.ReactNode;
|
|
7
|
-
sortable?: boolean;
|
|
8
|
-
filterable?: boolean;
|
|
9
|
-
width?: string;
|
|
10
|
-
}
|
|
11
|
-
export interface TableProps<T> {
|
|
12
|
-
data: T[];
|
|
13
|
-
columns: Column<T>[];
|
|
14
|
-
keyExtractor: (row: T) => string;
|
|
15
|
-
selectable?: boolean;
|
|
16
|
-
expandable?: boolean;
|
|
17
|
-
onRowSelect?: (selectedRows: string[]) => void;
|
|
18
|
-
renderExpandedRow?: (row: T) => React.ReactNode;
|
|
19
|
-
emptyState?: React.ReactNode;
|
|
20
|
-
className?: string;
|
|
21
|
-
onSort?: (columnKey: string, direction: SortDirection) => void;
|
|
22
|
-
sortColumn?: string | null;
|
|
23
|
-
sortDirection?: SortDirection;
|
|
24
|
-
}
|
|
25
|
-
export default function Table<T>({ data, columns, keyExtractor, selectable, expandable, onRowSelect, renderExpandedRow, emptyState, className, onSort, sortColumn: externalSortColumn, sortDirection: externalSortDirection, }: TableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
26
|
-
//# sourceMappingURL=Table.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/components/Table.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC/C,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAChD,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/D,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,CAAC,EAAE,EAC/B,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,SAAc,EACd,MAAM,EACN,UAAU,EAAE,kBAAkB,EAC9B,aAAa,EAAE,qBAAqB,GACrC,EAAE,UAAU,CAAC,CAAC,CAAC,2CAoMf"}
|