@papernote/ui 1.2.0 → 1.3.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.
- 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/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 +110 -48
- package/dist/index.esm.js +144 -138
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +143 -138
- package/dist/index.js.map +1 -1
- package/dist/styles.css +8 -51
- package/package.json +1 -1
- 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/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/dist/styles.css
CHANGED
|
@@ -969,49 +969,6 @@ h4{
|
|
|
969
969
|
border-color: rgb(231 229 228 / var(--tw-divide-opacity, 1));
|
|
970
970
|
}
|
|
971
971
|
|
|
972
|
-
.table-header{
|
|
973
|
-
--tw-bg-opacity: 1;
|
|
974
|
-
background-color: rgb(250 250 249 / var(--tw-bg-opacity, 1));
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
.table-header-cell{
|
|
978
|
-
padding-left: 1.5rem;
|
|
979
|
-
padding-right: 1.5rem;
|
|
980
|
-
padding-top: 0.75rem;
|
|
981
|
-
padding-bottom: 0.75rem;
|
|
982
|
-
text-align: left;
|
|
983
|
-
font-size: 0.75rem;
|
|
984
|
-
line-height: 1.125rem;
|
|
985
|
-
font-weight: 500;
|
|
986
|
-
text-transform: uppercase;
|
|
987
|
-
letter-spacing: 0.05em;
|
|
988
|
-
--tw-text-opacity: 1;
|
|
989
|
-
color: rgb(120 113 108 / var(--tw-text-opacity, 1));
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
.table-cell{
|
|
993
|
-
white-space: nowrap;
|
|
994
|
-
padding-left: 1.5rem;
|
|
995
|
-
padding-right: 1.5rem;
|
|
996
|
-
padding-top: 1rem;
|
|
997
|
-
padding-bottom: 1rem;
|
|
998
|
-
font-size: 0.875rem;
|
|
999
|
-
line-height: 1.375rem;
|
|
1000
|
-
--tw-text-opacity: 1;
|
|
1001
|
-
color: rgb(68 64 60 / var(--tw-text-opacity, 1));
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
.table-row{
|
|
1005
|
-
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
|
|
1006
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
1007
|
-
transition-duration: 150ms;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
.table-row:hover{
|
|
1011
|
-
--tw-bg-opacity: 1;
|
|
1012
|
-
background-color: rgb(250 250 249 / var(--tw-bg-opacity, 1));
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
972
|
/* Navigation Styles - Notebook-like */
|
|
1016
973
|
|
|
1017
974
|
/* Switch Toggle Styles - Refined */
|
|
@@ -1396,6 +1353,10 @@ input:checked + .slider:before{
|
|
|
1396
1353
|
top: 5rem;
|
|
1397
1354
|
}
|
|
1398
1355
|
|
|
1356
|
+
.top-3{
|
|
1357
|
+
top: 0.75rem;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1399
1360
|
.top-4{
|
|
1400
1361
|
top: 1rem;
|
|
1401
1362
|
}
|
|
@@ -1739,14 +1700,6 @@ input:checked + .slider:before{
|
|
|
1739
1700
|
display: table;
|
|
1740
1701
|
}
|
|
1741
1702
|
|
|
1742
|
-
.table-cell{
|
|
1743
|
-
display: table-cell;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
.table-row{
|
|
1747
|
-
display: table-row;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
1703
|
.grid{
|
|
1751
1704
|
display: grid;
|
|
1752
1705
|
}
|
|
@@ -1755,6 +1708,10 @@ input:checked + .slider:before{
|
|
|
1755
1708
|
display: none;
|
|
1756
1709
|
}
|
|
1757
1710
|
|
|
1711
|
+
.aspect-square{
|
|
1712
|
+
aspect-ratio: 1 / 1;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1758
1715
|
.h-0{
|
|
1759
1716
|
height: 0px;
|
|
1760
1717
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useRef, useEffect, useState } from 'react';
|
|
3
|
+
import { Box } from './Box';
|
|
4
|
+
import Stack from './Stack';
|
|
5
|
+
import Text from './Text';
|
|
6
|
+
import Button from './Button';
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: 'Layout/Box',
|
|
10
|
+
component: Box,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered',
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: `
|
|
16
|
+
Box is a generic container component with design system spacing and borders.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
- **Padding**: none, xs, sm, md, lg, xl (with directional variants)
|
|
20
|
+
- **Margin**: none, xs, sm, md, lg, xl, auto (with directional variants)
|
|
21
|
+
- **Border**: none, top, bottom, left, right, all
|
|
22
|
+
- **Border color**: default, primary, accent
|
|
23
|
+
- **Border radius**: none, sm, md, lg, xl, full
|
|
24
|
+
- **Width/Height**: auto, full, fit, screen
|
|
25
|
+
- **Ref forwarding**: Supports refs for DOM access
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
\`\`\`tsx
|
|
30
|
+
import { Box } from 'notebook-ui';
|
|
31
|
+
|
|
32
|
+
// Basic container with padding
|
|
33
|
+
<Box padding="md">
|
|
34
|
+
Content here
|
|
35
|
+
</Box>
|
|
36
|
+
|
|
37
|
+
// With border and rounded corners
|
|
38
|
+
<Box padding="lg" border="all" rounded="md">
|
|
39
|
+
Card-like container
|
|
40
|
+
</Box>
|
|
41
|
+
|
|
42
|
+
// With ref for DOM access
|
|
43
|
+
const boxRef = useRef<HTMLDivElement>(null);
|
|
44
|
+
<Box ref={boxRef} padding="md">
|
|
45
|
+
Measurable content
|
|
46
|
+
</Box>
|
|
47
|
+
\`\`\`
|
|
48
|
+
`,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
tags: ['autodocs'],
|
|
53
|
+
argTypes: {
|
|
54
|
+
padding: {
|
|
55
|
+
control: 'select',
|
|
56
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl'],
|
|
57
|
+
description: 'Padding on all sides',
|
|
58
|
+
},
|
|
59
|
+
paddingTop: {
|
|
60
|
+
control: 'select',
|
|
61
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl'],
|
|
62
|
+
},
|
|
63
|
+
paddingBottom: {
|
|
64
|
+
control: 'select',
|
|
65
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl'],
|
|
66
|
+
},
|
|
67
|
+
paddingLeft: {
|
|
68
|
+
control: 'select',
|
|
69
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl'],
|
|
70
|
+
},
|
|
71
|
+
paddingRight: {
|
|
72
|
+
control: 'select',
|
|
73
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl'],
|
|
74
|
+
},
|
|
75
|
+
margin: {
|
|
76
|
+
control: 'select',
|
|
77
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'auto'],
|
|
78
|
+
description: 'Margin on all sides',
|
|
79
|
+
},
|
|
80
|
+
marginTop: {
|
|
81
|
+
control: 'select',
|
|
82
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'auto'],
|
|
83
|
+
},
|
|
84
|
+
marginBottom: {
|
|
85
|
+
control: 'select',
|
|
86
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'auto'],
|
|
87
|
+
},
|
|
88
|
+
marginLeft: {
|
|
89
|
+
control: 'select',
|
|
90
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'auto'],
|
|
91
|
+
},
|
|
92
|
+
marginRight: {
|
|
93
|
+
control: 'select',
|
|
94
|
+
options: ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'auto'],
|
|
95
|
+
},
|
|
96
|
+
border: {
|
|
97
|
+
control: 'select',
|
|
98
|
+
options: ['none', 'top', 'bottom', 'left', 'right', 'all'],
|
|
99
|
+
description: 'Border style',
|
|
100
|
+
},
|
|
101
|
+
borderColor: {
|
|
102
|
+
control: 'select',
|
|
103
|
+
options: ['default', 'primary', 'accent'],
|
|
104
|
+
description: 'Border color',
|
|
105
|
+
},
|
|
106
|
+
rounded: {
|
|
107
|
+
control: 'select',
|
|
108
|
+
options: ['none', 'sm', 'md', 'lg', 'xl', 'full'],
|
|
109
|
+
description: 'Border radius',
|
|
110
|
+
},
|
|
111
|
+
width: {
|
|
112
|
+
control: 'select',
|
|
113
|
+
options: [undefined, 'auto', 'full', 'fit', 'screen'],
|
|
114
|
+
description: 'Width',
|
|
115
|
+
},
|
|
116
|
+
height: {
|
|
117
|
+
control: 'select',
|
|
118
|
+
options: [undefined, 'auto', 'full', 'screen'],
|
|
119
|
+
description: 'Height',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
} satisfies Meta<typeof Box>;
|
|
123
|
+
|
|
124
|
+
export default meta;
|
|
125
|
+
type Story = StoryObj<typeof meta>;
|
|
126
|
+
|
|
127
|
+
export const Default: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
padding: 'md',
|
|
130
|
+
children: 'Basic box with medium padding',
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const Padding: Story = {
|
|
135
|
+
render: () => (
|
|
136
|
+
<Stack spacing="md">
|
|
137
|
+
<Box padding="xs" className="bg-paper-100">
|
|
138
|
+
<Text size="sm">padding="xs"</Text>
|
|
139
|
+
</Box>
|
|
140
|
+
<Box padding="sm" className="bg-paper-100">
|
|
141
|
+
<Text size="sm">padding="sm"</Text>
|
|
142
|
+
</Box>
|
|
143
|
+
<Box padding="md" className="bg-paper-100">
|
|
144
|
+
<Text size="sm">padding="md"</Text>
|
|
145
|
+
</Box>
|
|
146
|
+
<Box padding="lg" className="bg-paper-100">
|
|
147
|
+
<Text size="sm">padding="lg"</Text>
|
|
148
|
+
</Box>
|
|
149
|
+
<Box padding="xl" className="bg-paper-100">
|
|
150
|
+
<Text size="sm">padding="xl"</Text>
|
|
151
|
+
</Box>
|
|
152
|
+
</Stack>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const DirectionalPadding: Story = {
|
|
157
|
+
render: () => (
|
|
158
|
+
<Stack spacing="md">
|
|
159
|
+
<Box paddingTop="lg" className="bg-paper-100">
|
|
160
|
+
<Text size="sm">paddingTop="lg"</Text>
|
|
161
|
+
</Box>
|
|
162
|
+
<Box paddingBottom="lg" className="bg-paper-100">
|
|
163
|
+
<Text size="sm">paddingBottom="lg"</Text>
|
|
164
|
+
</Box>
|
|
165
|
+
<Box paddingLeft="lg" className="bg-paper-100">
|
|
166
|
+
<Text size="sm">paddingLeft="lg"</Text>
|
|
167
|
+
</Box>
|
|
168
|
+
<Box paddingRight="lg" className="bg-paper-100">
|
|
169
|
+
<Text size="sm">paddingRight="lg"</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
</Stack>
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const WithBorder: Story = {
|
|
176
|
+
render: () => (
|
|
177
|
+
<Stack spacing="md">
|
|
178
|
+
<Box padding="md" border="all">
|
|
179
|
+
<Text size="sm">border="all"</Text>
|
|
180
|
+
</Box>
|
|
181
|
+
<Box padding="md" border="top">
|
|
182
|
+
<Text size="sm">border="top"</Text>
|
|
183
|
+
</Box>
|
|
184
|
+
<Box padding="md" border="bottom">
|
|
185
|
+
<Text size="sm">border="bottom"</Text>
|
|
186
|
+
</Box>
|
|
187
|
+
<Box padding="md" border="left">
|
|
188
|
+
<Text size="sm">border="left"</Text>
|
|
189
|
+
</Box>
|
|
190
|
+
<Box padding="md" border="right">
|
|
191
|
+
<Text size="sm">border="right"</Text>
|
|
192
|
+
</Box>
|
|
193
|
+
</Stack>
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const BorderColors: Story = {
|
|
198
|
+
render: () => (
|
|
199
|
+
<Stack spacing="md">
|
|
200
|
+
<Box padding="md" border="all" borderColor="default">
|
|
201
|
+
<Text size="sm">borderColor="default"</Text>
|
|
202
|
+
</Box>
|
|
203
|
+
<Box padding="md" border="all" borderColor="primary">
|
|
204
|
+
<Text size="sm">borderColor="primary"</Text>
|
|
205
|
+
</Box>
|
|
206
|
+
<Box padding="md" border="all" borderColor="accent">
|
|
207
|
+
<Text size="sm">borderColor="accent"</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
</Stack>
|
|
210
|
+
),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export const Rounded: Story = {
|
|
214
|
+
render: () => (
|
|
215
|
+
<Stack spacing="md">
|
|
216
|
+
<Box padding="md" border="all" rounded="none">
|
|
217
|
+
<Text size="sm">rounded="none"</Text>
|
|
218
|
+
</Box>
|
|
219
|
+
<Box padding="md" border="all" rounded="sm">
|
|
220
|
+
<Text size="sm">rounded="sm"</Text>
|
|
221
|
+
</Box>
|
|
222
|
+
<Box padding="md" border="all" rounded="md">
|
|
223
|
+
<Text size="sm">rounded="md"</Text>
|
|
224
|
+
</Box>
|
|
225
|
+
<Box padding="md" border="all" rounded="lg">
|
|
226
|
+
<Text size="sm">rounded="lg"</Text>
|
|
227
|
+
</Box>
|
|
228
|
+
<Box padding="md" border="all" rounded="xl">
|
|
229
|
+
<Text size="sm">rounded="xl"</Text>
|
|
230
|
+
</Box>
|
|
231
|
+
<Box padding="md" border="all" rounded="full" style={{ width: '150px', textAlign: 'center' }}>
|
|
232
|
+
<Text size="sm">rounded="full"</Text>
|
|
233
|
+
</Box>
|
|
234
|
+
</Stack>
|
|
235
|
+
),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export const CardLike: Story = {
|
|
239
|
+
render: () => (
|
|
240
|
+
<Box padding="lg" border="all" rounded="lg" className="bg-white shadow-sm" style={{ width: '300px' }}>
|
|
241
|
+
<Stack spacing="sm">
|
|
242
|
+
<Text as="h3" size="lg" weight="semibold">Card Title</Text>
|
|
243
|
+
<Text color="secondary">
|
|
244
|
+
This box is styled to look like a card with padding, border, and rounded corners.
|
|
245
|
+
</Text>
|
|
246
|
+
<Box marginTop="sm">
|
|
247
|
+
<Button variant="primary" size="sm">Action</Button>
|
|
248
|
+
</Box>
|
|
249
|
+
</Stack>
|
|
250
|
+
</Box>
|
|
251
|
+
),
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export const CenteredWithMargin: Story = {
|
|
255
|
+
render: () => (
|
|
256
|
+
<div style={{ width: '400px', border: '1px dashed #ccc' }}>
|
|
257
|
+
<Box
|
|
258
|
+
padding="md"
|
|
259
|
+
margin="auto"
|
|
260
|
+
border="all"
|
|
261
|
+
rounded="md"
|
|
262
|
+
style={{ width: 'fit-content' }}
|
|
263
|
+
>
|
|
264
|
+
<Text>Centered with margin="auto"</Text>
|
|
265
|
+
</Box>
|
|
266
|
+
</div>
|
|
267
|
+
),
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Box supports ref forwarding, allowing you to access the underlying DOM element.
|
|
272
|
+
* This is useful for measuring dimensions, handling scroll, or integrating with
|
|
273
|
+
* third-party libraries that need DOM access.
|
|
274
|
+
*/
|
|
275
|
+
export const WithRef: Story = {
|
|
276
|
+
render: function RefExample() {
|
|
277
|
+
const boxRef = useRef<HTMLDivElement>(null);
|
|
278
|
+
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
279
|
+
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
if (boxRef.current) {
|
|
282
|
+
const { offsetWidth, offsetHeight } = boxRef.current;
|
|
283
|
+
setDimensions({ width: offsetWidth, height: offsetHeight });
|
|
284
|
+
}
|
|
285
|
+
}, []);
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<Stack spacing="md">
|
|
289
|
+
<Box
|
|
290
|
+
ref={boxRef}
|
|
291
|
+
padding="lg"
|
|
292
|
+
border="all"
|
|
293
|
+
rounded="md"
|
|
294
|
+
className="bg-paper-50"
|
|
295
|
+
>
|
|
296
|
+
<Text>This box's dimensions are measured using a ref</Text>
|
|
297
|
+
</Box>
|
|
298
|
+
<Box padding="sm" border="all" rounded="sm" className="bg-success-50">
|
|
299
|
+
<Text size="sm" color="success">
|
|
300
|
+
Measured: {dimensions.width}px x {dimensions.height}px
|
|
301
|
+
</Text>
|
|
302
|
+
</Box>
|
|
303
|
+
</Stack>
|
|
304
|
+
);
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Another ref example showing focus management.
|
|
310
|
+
*/
|
|
311
|
+
export const RefForFocus: Story = {
|
|
312
|
+
render: function FocusExample() {
|
|
313
|
+
const boxRef = useRef<HTMLDivElement>(null);
|
|
314
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
315
|
+
|
|
316
|
+
const handleFocus = () => {
|
|
317
|
+
if (boxRef.current) {
|
|
318
|
+
boxRef.current.focus();
|
|
319
|
+
setIsFocused(true);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<Stack spacing="md">
|
|
325
|
+
<Box
|
|
326
|
+
ref={boxRef}
|
|
327
|
+
tabIndex={0}
|
|
328
|
+
padding="lg"
|
|
329
|
+
border="all"
|
|
330
|
+
borderColor={isFocused ? 'accent' : 'default'}
|
|
331
|
+
rounded="md"
|
|
332
|
+
onFocus={() => setIsFocused(true)}
|
|
333
|
+
onBlur={() => setIsFocused(false)}
|
|
334
|
+
className="transition-colors outline-none focus:ring-2 focus:ring-accent-400"
|
|
335
|
+
>
|
|
336
|
+
<Text>Click the button or press Tab to focus this box</Text>
|
|
337
|
+
</Box>
|
|
338
|
+
<Button variant="secondary" onClick={handleFocus}>
|
|
339
|
+
Focus the Box
|
|
340
|
+
</Button>
|
|
341
|
+
<Text size="sm" color="muted">
|
|
342
|
+
Status: {isFocused ? 'Focused' : 'Not focused'}
|
|
343
|
+
</Text>
|
|
344
|
+
</Stack>
|
|
345
|
+
);
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export const NestedBoxes: Story = {
|
|
350
|
+
render: () => (
|
|
351
|
+
<Box padding="lg" border="all" rounded="lg" className="bg-paper-50">
|
|
352
|
+
<Text weight="semibold" size="lg">Outer Box</Text>
|
|
353
|
+
<Box padding="md" marginTop="md" border="all" rounded="md" className="bg-white">
|
|
354
|
+
<Text>Inner Box 1</Text>
|
|
355
|
+
</Box>
|
|
356
|
+
<Box padding="md" marginTop="md" border="all" rounded="md" className="bg-white">
|
|
357
|
+
<Text>Inner Box 2</Text>
|
|
358
|
+
</Box>
|
|
359
|
+
</Box>
|
|
360
|
+
),
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
export const WidthOptions: Story = {
|
|
364
|
+
render: () => (
|
|
365
|
+
<Stack spacing="md" style={{ width: '400px' }}>
|
|
366
|
+
<Box padding="sm" border="all" width="auto">
|
|
367
|
+
<Text size="sm">width="auto" (shrinks to content)</Text>
|
|
368
|
+
</Box>
|
|
369
|
+
<Box padding="sm" border="all" width="full">
|
|
370
|
+
<Text size="sm">width="full" (100% of parent)</Text>
|
|
371
|
+
</Box>
|
|
372
|
+
<Box padding="sm" border="all" width="fit">
|
|
373
|
+
<Text size="sm">width="fit"</Text>
|
|
374
|
+
</Box>
|
|
375
|
+
</Stack>
|
|
376
|
+
),
|
|
377
|
+
};
|
package/src/components/Box.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Box Component - Generic container with design system styling
|
|
2
2
|
// Provides consistent padding, borders, and other container styles
|
|
3
3
|
|
|
4
|
-
import React from 'react';
|
|
4
|
+
import React, { forwardRef } from 'react';
|
|
5
5
|
|
|
6
6
|
export interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
7
|
/** Content */
|
|
@@ -42,8 +42,9 @@ export interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Box component for generic containers with design system spacing and borders.
|
|
45
|
+
* Supports ref forwarding for DOM access.
|
|
45
46
|
*/
|
|
46
|
-
export const Box
|
|
47
|
+
export const Box = forwardRef<HTMLDivElement, BoxProps>(({
|
|
47
48
|
children,
|
|
48
49
|
padding,
|
|
49
50
|
paddingTop,
|
|
@@ -62,7 +63,7 @@ export const Box: React.FC<BoxProps> = ({
|
|
|
62
63
|
height,
|
|
63
64
|
className = '',
|
|
64
65
|
...htmlProps
|
|
65
|
-
}) => {
|
|
66
|
+
}, ref) => {
|
|
66
67
|
const spacingMap = {
|
|
67
68
|
none: '0',
|
|
68
69
|
xs: '2',
|
|
@@ -148,6 +149,7 @@ export const Box: React.FC<BoxProps> = ({
|
|
|
148
149
|
|
|
149
150
|
return (
|
|
150
151
|
<div
|
|
152
|
+
ref={ref}
|
|
151
153
|
{...htmlProps}
|
|
152
154
|
className={`
|
|
153
155
|
${getPaddingClass()}
|
|
@@ -163,6 +165,8 @@ export const Box: React.FC<BoxProps> = ({
|
|
|
163
165
|
{children}
|
|
164
166
|
</div>
|
|
165
167
|
);
|
|
166
|
-
};
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
Box.displayName = 'Box';
|
|
167
171
|
|
|
168
172
|
export default Box;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
2
|
import { Loader2 } from 'lucide-react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -31,6 +31,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
31
31
|
* A versatile button component that supports multiple visual styles, sizes, icons,
|
|
32
32
|
* loading states, and notification badges.
|
|
33
33
|
*
|
|
34
|
+
* Supports ref forwarding for DOM access.
|
|
35
|
+
*
|
|
34
36
|
* @example Basic usage
|
|
35
37
|
* ```tsx
|
|
36
38
|
* <Button variant="primary">Click me</Button>
|
|
@@ -38,9 +40,9 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
38
40
|
*
|
|
39
41
|
* @example With icon and loading
|
|
40
42
|
* ```tsx
|
|
41
|
-
* <Button
|
|
42
|
-
* variant="secondary"
|
|
43
|
-
* icon={<Save />}
|
|
43
|
+
* <Button
|
|
44
|
+
* variant="secondary"
|
|
45
|
+
* icon={<Save />}
|
|
44
46
|
* loading={isSaving}
|
|
45
47
|
* >
|
|
46
48
|
* Save Changes
|
|
@@ -49,16 +51,22 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
49
51
|
*
|
|
50
52
|
* @example Icon-only with badge
|
|
51
53
|
* ```tsx
|
|
52
|
-
* <Button
|
|
53
|
-
* iconOnly
|
|
54
|
-
* badge={5}
|
|
54
|
+
* <Button
|
|
55
|
+
* iconOnly
|
|
56
|
+
* badge={5}
|
|
55
57
|
* badgeVariant="error"
|
|
56
58
|
* >
|
|
57
59
|
* <Bell />
|
|
58
60
|
* </Button>
|
|
59
61
|
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example With ref
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
66
|
+
* <Button ref={buttonRef}>Focusable</Button>
|
|
67
|
+
* ```
|
|
60
68
|
*/
|
|
61
|
-
|
|
69
|
+
const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
|
62
70
|
variant = 'primary',
|
|
63
71
|
size = 'md',
|
|
64
72
|
loading = false,
|
|
@@ -72,7 +80,7 @@ export default function Button({
|
|
|
72
80
|
disabled,
|
|
73
81
|
className = '',
|
|
74
82
|
...props
|
|
75
|
-
}
|
|
83
|
+
}, ref) => {
|
|
76
84
|
const baseStyles = 'inline-flex items-center justify-center font-medium rounded-lg border transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent-400 disabled:opacity-40 disabled:cursor-not-allowed';
|
|
77
85
|
|
|
78
86
|
const variantStyles = {
|
|
@@ -111,6 +119,7 @@ export default function Button({
|
|
|
111
119
|
|
|
112
120
|
const buttonElement = (
|
|
113
121
|
<button
|
|
122
|
+
ref={ref}
|
|
114
123
|
className={`
|
|
115
124
|
${baseStyles}
|
|
116
125
|
${variantStyles[variant]}
|
|
@@ -163,4 +172,8 @@ export default function Button({
|
|
|
163
172
|
</span>
|
|
164
173
|
</div>
|
|
165
174
|
);
|
|
166
|
-
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
Button.displayName = 'Button';
|
|
178
|
+
|
|
179
|
+
export default Button;
|
package/src/components/Card.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
2
|
import { Skeleton } from './Loading';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Card component props
|
|
6
6
|
*/
|
|
7
|
-
export interface CardProps {
|
|
7
|
+
export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onClick'> {
|
|
8
8
|
/** Card content */
|
|
9
9
|
children: React.ReactNode;
|
|
10
10
|
/** Visual style variant affecting padding and shadow */
|
|
@@ -24,6 +24,8 @@ export interface CardProps {
|
|
|
24
24
|
/**
|
|
25
25
|
* Card - Container component with paper aesthetic and subtle shadow
|
|
26
26
|
*
|
|
27
|
+
* Supports ref forwarding for DOM access.
|
|
28
|
+
*
|
|
27
29
|
* A content container with paper texture, border, and shadow effects. Supports
|
|
28
30
|
* different sizes, variants (padding/shadow levels), and loading states.
|
|
29
31
|
*
|
|
@@ -53,8 +55,14 @@ export interface CardProps {
|
|
|
53
55
|
* <p>Content</p>
|
|
54
56
|
* </Card>
|
|
55
57
|
* ```
|
|
58
|
+
*
|
|
59
|
+
* @example With ref
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const cardRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
* <Card ref={cardRef}>Content</Card>
|
|
63
|
+
* ```
|
|
56
64
|
*/
|
|
57
|
-
|
|
65
|
+
const Card = forwardRef<HTMLDivElement, CardProps>(({
|
|
58
66
|
children,
|
|
59
67
|
variant = 'default',
|
|
60
68
|
width = 'auto',
|
|
@@ -62,7 +70,8 @@ export default function Card({
|
|
|
62
70
|
onClick,
|
|
63
71
|
hoverable = false,
|
|
64
72
|
loading = false,
|
|
65
|
-
|
|
73
|
+
...htmlProps
|
|
74
|
+
}, ref) => {
|
|
66
75
|
const baseStyles = 'bg-white bg-subtle-grain border-2 border-paper-300 transition-shadow duration-200';
|
|
67
76
|
|
|
68
77
|
const variantStyles = {
|
|
@@ -84,6 +93,8 @@ export default function Card({
|
|
|
84
93
|
|
|
85
94
|
return (
|
|
86
95
|
<div
|
|
96
|
+
ref={ref}
|
|
97
|
+
{...htmlProps}
|
|
87
98
|
className={`${baseStyles} ${variantStyles[variant]} ${widthStyles[width]} ${interactiveStyles} ${className}`}
|
|
88
99
|
onClick={!loading ? onClick : undefined}
|
|
89
100
|
role={onClick ? 'button' : undefined}
|
|
@@ -101,7 +112,11 @@ export default function Card({
|
|
|
101
112
|
)}
|
|
102
113
|
</div>
|
|
103
114
|
);
|
|
104
|
-
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
Card.displayName = 'Card';
|
|
118
|
+
|
|
119
|
+
export default Card;
|
|
105
120
|
|
|
106
121
|
/**
|
|
107
122
|
* CardHeader component props
|