@shohojdhara/atomix 0.3.6 → 0.3.8
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/README.md +3 -3
- package/dist/atomix.css +77 -0
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +77 -0
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +50 -142
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/core.js +179 -274
- package/dist/core.js.map +1 -1
- package/dist/forms.js +50 -142
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +179 -274
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +1255 -1226
- package/dist/index.esm.js +2806 -2958
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3113 -3269
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +313 -667
- package/dist/theme.js +1818 -2589
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/Button/Button.tsx +85 -167
- package/src/components/DataTable/DataTable.stories.tsx +238 -0
- package/src/components/DataTable/DataTable.test.tsx +450 -0
- package/src/components/DataTable/DataTable.tsx +384 -61
- package/src/components/DatePicker/DatePicker.tsx +29 -38
- package/src/components/Upload/Upload.tsx +539 -40
- package/src/lib/composables/useAtomixGlass.ts +7 -7
- package/src/lib/composables/useDataTable.ts +355 -15
- package/src/lib/composables/useDatePicker.ts +19 -0
- package/src/lib/config/loader.ts +2 -3
- package/src/lib/constants/components.ts +17 -0
- package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
- package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
- package/src/lib/theme/adapters/index.ts +1 -4
- package/src/lib/theme/config/configLoader.ts +82 -223
- package/src/lib/theme/config/loader.ts +15 -21
- package/src/lib/theme/constants/constants.ts +1 -1
- package/src/lib/theme/core/ThemeRegistry.ts +75 -279
- package/src/lib/theme/core/composeTheme.ts +30 -88
- package/src/lib/theme/core/createTheme.ts +88 -51
- package/src/lib/theme/core/createThemeObject.ts +2 -2
- package/src/lib/theme/core/index.ts +15 -2
- package/src/lib/theme/errors/errors.ts +1 -1
- package/src/lib/theme/generators/generateCSSNested.ts +131 -0
- package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
- package/src/lib/theme/generators/index.ts +6 -0
- package/src/lib/theme/index.ts +45 -27
- package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +393 -544
- package/src/lib/theme/runtime/index.ts +1 -0
- package/src/lib/theme/runtime/useTheme.ts +1 -1
- package/src/lib/theme/runtime/useThemeTokens.ts +122 -0
- package/src/lib/theme/test/testTheme.ts +2 -1
- package/src/lib/theme/types.ts +14 -14
- package/src/lib/theme/utils/componentTheming.ts +140 -0
- package/src/lib/theme/utils/domUtils.ts +57 -15
- package/src/lib/theme/utils/injectCSS.ts +0 -1
- package/src/lib/theme/utils/naming.ts +100 -0
- package/src/lib/theme/utils/themeHelpers.ts +1 -39
- package/src/lib/theme/utils/themeUtils.ts +1 -170
- package/src/lib/types/components.ts +145 -0
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/dataTableExport.ts +143 -0
- package/src/lib/utils/memoryMonitor.ts +3 -3
- package/src/lib/utils/themeNaming.ts +135 -0
- package/src/styles/06-components/_components.data-table.scss +95 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { ElementType, forwardRef, useCallback
|
|
1
|
+
import React, { ElementType, forwardRef, useCallback } from 'react';
|
|
2
2
|
import { useButton } from '../../lib/composables/useButton';
|
|
3
3
|
import { ButtonProps } from '../../lib/types/components';
|
|
4
4
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
5
|
import { Spinner } from '../Spinner/Spinner';
|
|
6
6
|
import { Icon, type PhosphorIconsType } from '../Icon/Icon';
|
|
7
|
-
import { BUTTON } from '../../lib/constants/components';
|
|
7
|
+
import { BUTTON, THEME_NAMING } from '../../lib/constants/components';
|
|
8
|
+
import { ThemeNaming } from '../../lib/utils/themeNaming';
|
|
8
9
|
|
|
9
10
|
export type ButtonAsProp = {
|
|
10
11
|
as?: ElementType;
|
|
@@ -62,13 +63,7 @@ export const Button = React.memo(
|
|
|
62
63
|
const shouldRenderAsLink = Boolean(href && !isDisabled);
|
|
63
64
|
|
|
64
65
|
// Resolve icon element - support both icon (ReactNode) and iconName (string)
|
|
65
|
-
const iconElement =
|
|
66
|
-
if (loading) return null;
|
|
67
|
-
if (iconName) {
|
|
68
|
-
return <Icon name={iconName as PhosphorIconsType} size={iconSize} />;
|
|
69
|
-
}
|
|
70
|
-
return icon;
|
|
71
|
-
}, [icon, iconName, iconSize, loading]);
|
|
66
|
+
const iconElement = iconName ? <Icon name={iconName as PhosphorIconsType} size={iconSize} /> : icon;
|
|
72
67
|
|
|
73
68
|
const { generateButtonClass, handleClick } = useButton({
|
|
74
69
|
variant,
|
|
@@ -83,24 +78,23 @@ export const Button = React.memo(
|
|
|
83
78
|
selected,
|
|
84
79
|
});
|
|
85
80
|
|
|
86
|
-
const buttonClass =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
81
|
+
const buttonClass = [
|
|
82
|
+
BUTTON.BASE_CLASS,
|
|
83
|
+
ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
|
|
84
|
+
size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
|
|
85
|
+
iconOnly ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT) : '',
|
|
86
|
+
rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
|
|
87
|
+
isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
|
|
88
|
+
glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
|
|
89
|
+
loading ? BUTTON.CLASSES.LOADING : '',
|
|
90
|
+
fullWidth ? BUTTON.CLASSES.FULL_WIDTH : '',
|
|
91
|
+
block ? BUTTON.CLASSES.BLOCK : '',
|
|
92
|
+
active ? BUTTON.CLASSES.ACTIVE : '',
|
|
93
|
+
selected ? BUTTON.CLASSES.SELECTED : '',
|
|
94
|
+
className,
|
|
95
|
+
]
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join(' ');
|
|
104
98
|
|
|
105
99
|
// Handle click with loading check
|
|
106
100
|
const handleClickEvent = useCallback(
|
|
@@ -145,117 +139,76 @@ export const Button = React.memo(
|
|
|
145
139
|
);
|
|
146
140
|
|
|
147
141
|
// Determine button text
|
|
148
|
-
const buttonText =
|
|
149
|
-
if (loading && loadingText) return loadingText;
|
|
150
|
-
if (loading && !loadingText) return label || children;
|
|
151
|
-
return label || children;
|
|
152
|
-
}, [loading, loadingText, label, children]);
|
|
142
|
+
const buttonText = loading && loadingText ? loadingText : label || children;
|
|
153
143
|
|
|
154
144
|
// Determine spinner size based on button size
|
|
155
|
-
const spinnerSize =
|
|
156
|
-
if (size === 'sm') return 'sm';
|
|
157
|
-
if (size === 'lg') return 'md';
|
|
158
|
-
return 'sm';
|
|
159
|
-
}, [size]);
|
|
145
|
+
const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
|
|
160
146
|
|
|
161
147
|
// Button content with icon positioning
|
|
162
|
-
const buttonContent =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (iconPosition === 'end') {
|
|
187
|
-
return (
|
|
188
|
-
<>
|
|
189
|
-
{labelElement}
|
|
190
|
-
{spinnerElement}
|
|
191
|
-
{iconSpan}
|
|
192
|
-
</>
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<>
|
|
198
|
-
{spinnerElement}
|
|
199
|
-
{iconSpan}
|
|
200
|
-
{labelElement}
|
|
201
|
-
</>
|
|
202
|
-
);
|
|
203
|
-
}, [iconElement, iconPosition, iconOnly, buttonText, loading, spinnerSize, variant]);
|
|
148
|
+
const buttonContent = (
|
|
149
|
+
<>
|
|
150
|
+
{loading && (
|
|
151
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.SPINNER_ELEMENT)} aria-hidden="true">
|
|
152
|
+
<Spinner
|
|
153
|
+
size={spinnerSize}
|
|
154
|
+
variant={
|
|
155
|
+
variant === 'link' || (typeof variant === 'string' && variant.startsWith('outline-'))
|
|
156
|
+
? 'primary'
|
|
157
|
+
: (variant === 'danger' ? 'error' : (variant as any))
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
</span>
|
|
161
|
+
)}
|
|
162
|
+
{iconElement && !loading && (
|
|
163
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)} aria-hidden="true">
|
|
164
|
+
{iconElement}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
{!iconOnly && buttonText && (
|
|
168
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.LABEL_ELEMENT)}>{buttonText}</span>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
204
172
|
|
|
205
173
|
// Button props
|
|
206
|
-
const buttonProps =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
handleBlurEvent,
|
|
235
|
-
isDisabled,
|
|
236
|
-
loading,
|
|
237
|
-
ariaLabel,
|
|
238
|
-
iconOnly,
|
|
239
|
-
label,
|
|
240
|
-
children,
|
|
241
|
-
ariaDescribedBy,
|
|
242
|
-
ariaExpanded,
|
|
243
|
-
ariaControls,
|
|
244
|
-
tabIndex,
|
|
245
|
-
style,
|
|
246
|
-
props,
|
|
247
|
-
]
|
|
248
|
-
);
|
|
174
|
+
const buttonProps = {
|
|
175
|
+
ref,
|
|
176
|
+
className: buttonClass,
|
|
177
|
+
type: Component === 'button' && !shouldRenderAsLink ? type : undefined,
|
|
178
|
+
onClick: handleClickEvent,
|
|
179
|
+
onMouseEnter: onHover ? handleMouseEnter : undefined,
|
|
180
|
+
onFocus: onFocus ? handleFocusEvent : undefined,
|
|
181
|
+
onBlur: onBlur ? handleBlurEvent : undefined,
|
|
182
|
+
disabled: isDisabled && Component === 'button' && !shouldRenderAsLink,
|
|
183
|
+
'aria-disabled': isDisabled,
|
|
184
|
+
'aria-busy': loading,
|
|
185
|
+
'aria-label': ariaLabel || (iconOnly ? label || children : undefined),
|
|
186
|
+
'aria-describedby': ariaDescribedBy,
|
|
187
|
+
'aria-expanded': ariaExpanded,
|
|
188
|
+
'aria-controls': ariaControls,
|
|
189
|
+
tabIndex: tabIndex !== undefined ? tabIndex : (isDisabled ? -1 : 0),
|
|
190
|
+
style,
|
|
191
|
+
...props,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Default glass props
|
|
195
|
+
const defaultGlassProps = {
|
|
196
|
+
displacementScale: 20,
|
|
197
|
+
blurAmount: 0,
|
|
198
|
+
saturation: 200,
|
|
199
|
+
elasticity: 0,
|
|
200
|
+
};
|
|
201
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
249
202
|
|
|
250
203
|
// Render as anchor if href is provided
|
|
251
204
|
if (shouldRenderAsLink) {
|
|
252
205
|
const { ref: _, ...buttonPropsWithoutRef } = buttonProps;
|
|
253
206
|
const anchorButtonProps = {
|
|
254
207
|
...buttonPropsWithoutRef,
|
|
255
|
-
type: undefined,
|
|
208
|
+
type: undefined,
|
|
256
209
|
disabled: undefined,
|
|
257
210
|
};
|
|
258
|
-
|
|
211
|
+
|
|
259
212
|
// Use custom LinkComponent if provided (e.g., Next.js Link)
|
|
260
213
|
if (LinkComponent) {
|
|
261
214
|
const LinkComp = LinkComponent as React.ComponentType<any>;
|
|
@@ -266,25 +219,14 @@ export const Button = React.memo(
|
|
|
266
219
|
target,
|
|
267
220
|
rel: target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
268
221
|
};
|
|
269
|
-
|
|
222
|
+
|
|
270
223
|
const linkElement = (
|
|
271
224
|
<LinkComp {...linkProps}>
|
|
272
225
|
{buttonContent}
|
|
273
226
|
</LinkComp>
|
|
274
227
|
);
|
|
275
228
|
|
|
276
|
-
|
|
277
|
-
const defaultGlassProps = {
|
|
278
|
-
displacementScale: 20,
|
|
279
|
-
blurAmount: 0,
|
|
280
|
-
saturation: 200,
|
|
281
|
-
elasticity: 0,
|
|
282
|
-
};
|
|
283
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
284
|
-
return <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass>;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return linkElement;
|
|
229
|
+
return glass ? <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass> : linkElement;
|
|
288
230
|
}
|
|
289
231
|
|
|
290
232
|
// Fallback to regular anchor tag
|
|
@@ -294,39 +236,15 @@ export const Button = React.memo(
|
|
|
294
236
|
</a>
|
|
295
237
|
);
|
|
296
238
|
|
|
297
|
-
|
|
298
|
-
const defaultGlassProps = {
|
|
299
|
-
displacementScale: 20,
|
|
300
|
-
blurAmount: 0,
|
|
301
|
-
saturation: 200,
|
|
302
|
-
elasticity: 0,
|
|
303
|
-
};
|
|
304
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
305
|
-
return <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass>;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return anchorElement;
|
|
239
|
+
return glass ? <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass> : anchorElement;
|
|
309
240
|
}
|
|
310
241
|
|
|
311
242
|
// Default button rendering
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
blurAmount: 0,
|
|
316
|
-
saturation: 200,
|
|
317
|
-
elasticity: 0,
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
321
|
-
|
|
322
|
-
return (
|
|
323
|
-
<AtomixGlass {...glassProps}>
|
|
324
|
-
<Component {...buttonProps}>{buttonContent}</Component>
|
|
325
|
-
</AtomixGlass>
|
|
326
|
-
);
|
|
327
|
-
}
|
|
243
|
+
const buttonElement = (
|
|
244
|
+
<Component {...buttonProps}>{buttonContent}</Component>
|
|
245
|
+
);
|
|
328
246
|
|
|
329
|
-
return <
|
|
247
|
+
return glass ? <AtomixGlass {...glassProps}>{buttonElement}</AtomixGlass> : buttonElement;
|
|
330
248
|
}
|
|
331
249
|
)
|
|
332
250
|
);
|
|
@@ -335,4 +253,4 @@ Button.displayName = 'Button';
|
|
|
335
253
|
|
|
336
254
|
export type { ButtonProps };
|
|
337
255
|
|
|
338
|
-
export default Button;
|
|
256
|
+
export default Button;
|
|
@@ -253,3 +253,241 @@ export const Interactive: Story = {
|
|
|
253
253
|
sortable: true,
|
|
254
254
|
},
|
|
255
255
|
};
|
|
256
|
+
|
|
257
|
+
// Row selection - multiple
|
|
258
|
+
export const RowSelectionMultiple: Story = {
|
|
259
|
+
render: args => {
|
|
260
|
+
const [selectedRows, setSelectedRows] = useState<any[]>([]);
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<div>
|
|
264
|
+
<DataTable
|
|
265
|
+
{...args}
|
|
266
|
+
selectionMode="multiple"
|
|
267
|
+
onSelectionChange={(rows, ids) => setSelectedRows(rows)}
|
|
268
|
+
/>
|
|
269
|
+
{selectedRows.length > 0 && (
|
|
270
|
+
<div style={{ marginTop: '1rem', padding: '1rem', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
271
|
+
<strong>Selected: {selectedRows.length} row(s)</strong>
|
|
272
|
+
<pre style={{ marginTop: '0.5rem', fontSize: '0.875rem' }}>
|
|
273
|
+
{JSON.stringify(selectedRows.map(r => r.name), null, 2)}
|
|
274
|
+
</pre>
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
},
|
|
280
|
+
args: {
|
|
281
|
+
data: users,
|
|
282
|
+
columns,
|
|
283
|
+
sortable: true,
|
|
284
|
+
},
|
|
285
|
+
parameters: {
|
|
286
|
+
docs: {
|
|
287
|
+
description: {
|
|
288
|
+
story: 'DataTable with multiple row selection enabled. Select rows using checkboxes.',
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Row selection - single
|
|
295
|
+
export const RowSelectionSingle: Story = {
|
|
296
|
+
render: args => {
|
|
297
|
+
const [selectedRow, setSelectedRow] = useState<any>(null);
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<div>
|
|
301
|
+
<DataTable
|
|
302
|
+
{...args}
|
|
303
|
+
selectionMode="single"
|
|
304
|
+
onSelectionChange={(rows) => setSelectedRow(rows[0] || null)}
|
|
305
|
+
/>
|
|
306
|
+
{selectedRow && (
|
|
307
|
+
<div style={{ marginTop: '1rem', padding: '1rem', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
308
|
+
<strong>Selected:</strong>
|
|
309
|
+
<pre style={{ marginTop: '0.5rem', fontSize: '0.875rem' }}>
|
|
310
|
+
{JSON.stringify(selectedRow, null, 2)}
|
|
311
|
+
</pre>
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
},
|
|
317
|
+
args: {
|
|
318
|
+
data: users,
|
|
319
|
+
columns,
|
|
320
|
+
sortable: true,
|
|
321
|
+
},
|
|
322
|
+
parameters: {
|
|
323
|
+
docs: {
|
|
324
|
+
description: {
|
|
325
|
+
story: 'DataTable with single row selection enabled. Select a row using radio buttons.',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// Column-specific filtering
|
|
332
|
+
export const ColumnFilters: Story = {
|
|
333
|
+
args: {
|
|
334
|
+
data: users,
|
|
335
|
+
columns: columns.map(col => ({
|
|
336
|
+
...col,
|
|
337
|
+
filterable: ['name', 'role', 'email'].includes(col.key),
|
|
338
|
+
})),
|
|
339
|
+
columnFilters: true,
|
|
340
|
+
sortable: true,
|
|
341
|
+
},
|
|
342
|
+
parameters: {
|
|
343
|
+
docs: {
|
|
344
|
+
description: {
|
|
345
|
+
story: 'DataTable with column-specific filters. Each filterable column has its own filter input.',
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Column resizing
|
|
352
|
+
export const ResizableColumns: Story = {
|
|
353
|
+
args: {
|
|
354
|
+
data: users,
|
|
355
|
+
columns: columns.map(col => ({
|
|
356
|
+
...col,
|
|
357
|
+
resizable: true,
|
|
358
|
+
minWidth: '100px',
|
|
359
|
+
})),
|
|
360
|
+
resizable: true,
|
|
361
|
+
sortable: true,
|
|
362
|
+
},
|
|
363
|
+
parameters: {
|
|
364
|
+
docs: {
|
|
365
|
+
description: {
|
|
366
|
+
story: 'DataTable with resizable columns. Drag the right edge of column headers to resize.',
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// Column reordering
|
|
373
|
+
export const ReorderableColumns: Story = {
|
|
374
|
+
args: {
|
|
375
|
+
data: users,
|
|
376
|
+
columns,
|
|
377
|
+
reorderable: true,
|
|
378
|
+
sortable: true,
|
|
379
|
+
},
|
|
380
|
+
parameters: {
|
|
381
|
+
docs: {
|
|
382
|
+
description: {
|
|
383
|
+
story: 'DataTable with reorderable columns. Drag column headers to reorder them.',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// Column visibility toggle
|
|
390
|
+
export const ColumnVisibility: Story = {
|
|
391
|
+
args: {
|
|
392
|
+
data: users,
|
|
393
|
+
columns,
|
|
394
|
+
showColumnVisibility: true,
|
|
395
|
+
sortable: true,
|
|
396
|
+
},
|
|
397
|
+
parameters: {
|
|
398
|
+
docs: {
|
|
399
|
+
description: {
|
|
400
|
+
story: 'DataTable with column visibility toggle. Use the Columns button to show/hide columns.',
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// Export functionality
|
|
407
|
+
export const Exportable: Story = {
|
|
408
|
+
args: {
|
|
409
|
+
data: users,
|
|
410
|
+
columns,
|
|
411
|
+
exportable: true,
|
|
412
|
+
exportFormats: ['csv', 'excel', 'json'],
|
|
413
|
+
exportFilename: 'users',
|
|
414
|
+
sortable: true,
|
|
415
|
+
},
|
|
416
|
+
parameters: {
|
|
417
|
+
docs: {
|
|
418
|
+
description: {
|
|
419
|
+
story: 'DataTable with export functionality. Export data as CSV, Excel, or JSON.',
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Sticky headers
|
|
426
|
+
export const StickyHeaders: Story = {
|
|
427
|
+
args: {
|
|
428
|
+
data: largeDataSet,
|
|
429
|
+
columns,
|
|
430
|
+
stickyHeader: true,
|
|
431
|
+
stickyHeaderOffset: '0px',
|
|
432
|
+
sortable: true,
|
|
433
|
+
paginated: true,
|
|
434
|
+
pageSize: 20,
|
|
435
|
+
},
|
|
436
|
+
parameters: {
|
|
437
|
+
docs: {
|
|
438
|
+
description: {
|
|
439
|
+
story: 'DataTable with sticky headers. Headers remain visible when scrolling.',
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// All advanced features
|
|
446
|
+
export const AllAdvancedFeatures: Story = {
|
|
447
|
+
render: args => {
|
|
448
|
+
const [selectedRows, setSelectedRows] = useState<any[]>([]);
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<div>
|
|
452
|
+
<DataTable
|
|
453
|
+
{...args}
|
|
454
|
+
selectionMode="multiple"
|
|
455
|
+
onSelectionChange={(rows) => setSelectedRows(rows)}
|
|
456
|
+
/>
|
|
457
|
+
{selectedRows.length > 0 && (
|
|
458
|
+
<div style={{ marginTop: '1rem', padding: '1rem', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
459
|
+
<strong>Selected: {selectedRows.length} row(s)</strong>
|
|
460
|
+
</div>
|
|
461
|
+
)}
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
464
|
+
},
|
|
465
|
+
args: {
|
|
466
|
+
data: largeDataSet,
|
|
467
|
+
columns: columns.map(col => ({
|
|
468
|
+
...col,
|
|
469
|
+
filterable: ['name', 'role'].includes(col.key),
|
|
470
|
+
resizable: true,
|
|
471
|
+
})),
|
|
472
|
+
sortable: true,
|
|
473
|
+
filterable: true,
|
|
474
|
+
columnFilters: true,
|
|
475
|
+
paginated: true,
|
|
476
|
+
pageSize: 10,
|
|
477
|
+
striped: true,
|
|
478
|
+
bordered: true,
|
|
479
|
+
resizable: true,
|
|
480
|
+
reorderable: true,
|
|
481
|
+
showColumnVisibility: true,
|
|
482
|
+
exportable: true,
|
|
483
|
+
exportFormats: ['csv', 'excel', 'json'],
|
|
484
|
+
stickyHeader: true,
|
|
485
|
+
},
|
|
486
|
+
parameters: {
|
|
487
|
+
docs: {
|
|
488
|
+
description: {
|
|
489
|
+
story: 'DataTable with all advanced features enabled: selection, filtering, resizing, reordering, visibility toggle, export, and sticky headers.',
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
};
|