@papernote/ui 1.0.0 → 1.2.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 (84) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +455 -445
  3. package/dist/components/CurrencyInput.d.ts +52 -0
  4. package/dist/components/CurrencyInput.d.ts.map +1 -0
  5. package/dist/components/DataTable.d.ts +3 -1
  6. package/dist/components/DataTable.d.ts.map +1 -1
  7. package/dist/components/Modal.d.ts.map +1 -1
  8. package/dist/components/Page.d.ts +2 -0
  9. package/dist/components/Page.d.ts.map +1 -1
  10. package/dist/components/PageLayout.d.ts +5 -1
  11. package/dist/components/PageLayout.d.ts.map +1 -1
  12. package/dist/components/Spreadsheet.d.ts +129 -0
  13. package/dist/components/Spreadsheet.d.ts.map +1 -0
  14. package/dist/components/Tabs.d.ts +5 -1
  15. package/dist/components/Tabs.d.ts.map +1 -1
  16. package/dist/components/index.d.ts +6 -0
  17. package/dist/components/index.d.ts.map +1 -1
  18. package/dist/index.d.ts +336 -5
  19. package/dist/index.esm.js +51152 -174
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +51145 -143
  22. package/dist/index.js.map +1 -1
  23. package/dist/styles.css +1187 -11
  24. package/dist/utils/excelExport.d.ts +143 -0
  25. package/dist/utils/excelExport.d.ts.map +1 -0
  26. package/dist/utils/index.d.ts +2 -0
  27. package/dist/utils/index.d.ts.map +1 -1
  28. package/package.json +13 -3
  29. package/src/components/AdminModal.css +49 -49
  30. package/src/components/CurrencyInput.stories.tsx +290 -0
  31. package/src/components/CurrencyInput.tsx +193 -0
  32. package/src/components/DataTable.stories.tsx +87 -0
  33. package/src/components/DataTable.tsx +149 -37
  34. package/src/components/Modal.stories.tsx +64 -0
  35. package/src/components/Modal.tsx +15 -2
  36. package/src/components/Page.stories.tsx +76 -0
  37. package/src/components/Page.tsx +35 -3
  38. package/src/components/PageLayout.stories.tsx +75 -0
  39. package/src/components/PageLayout.tsx +28 -9
  40. package/src/components/RoleManager.css +10 -10
  41. package/src/components/Spreadsheet.css +216 -0
  42. package/src/components/Spreadsheet.stories.tsx +362 -0
  43. package/src/components/Spreadsheet.tsx +351 -0
  44. package/src/components/SpreadsheetSimple.stories.tsx +27 -0
  45. package/src/components/Tabs.stories.tsx +31 -0
  46. package/src/components/Tabs.tsx +28 -4
  47. package/src/components/TimePicker.tsx +1 -1
  48. package/src/components/Toast.tsx +9 -9
  49. package/src/components/__tests__/Input.test.tsx +22 -26
  50. package/src/components/index.ts +11 -2
  51. package/src/styles/index.css +44 -6
  52. package/src/utils/excelExport.stories.tsx +535 -0
  53. package/src/utils/excelExport.ts +225 -0
  54. package/src/utils/index.ts +3 -0
  55. package/src/utils/sqlToNaturalLanguage.ts +1 -1
  56. package/tailwind.config.js +253 -253
  57. package/dist/components/Button.stories.d.ts +0 -51
  58. package/dist/components/Button.stories.d.ts.map +0 -1
  59. package/dist/components/ChartVisualizationUI.d.ts +0 -21
  60. package/dist/components/ChartVisualizationUI.d.ts.map +0 -1
  61. package/dist/components/ChatUI.d.ts +0 -23
  62. package/dist/components/ChatUI.d.ts.map +0 -1
  63. package/dist/components/CommissionDashboardUI.d.ts +0 -25
  64. package/dist/components/CommissionDashboardUI.d.ts.map +0 -1
  65. package/dist/components/DataTable.stories.d.ts +0 -23
  66. package/dist/components/DataTable.stories.d.ts.map +0 -1
  67. package/dist/components/FormField.d.ts +0 -35
  68. package/dist/components/FormField.d.ts.map +0 -1
  69. package/dist/components/Input.stories.d.ts +0 -366
  70. package/dist/components/Input.stories.d.ts.map +0 -1
  71. package/dist/components/InsightsPanelUI.d.ts +0 -21
  72. package/dist/components/InsightsPanelUI.d.ts.map +0 -1
  73. package/dist/components/PaymentHistoryTimeline.d.ts +0 -34
  74. package/dist/components/PaymentHistoryTimeline.d.ts.map +0 -1
  75. package/dist/components/RelationshipManagerUI.d.ts +0 -60
  76. package/dist/components/RelationshipManagerUI.d.ts.map +0 -1
  77. package/dist/components/RoleManager.d.ts +0 -19
  78. package/dist/components/RoleManager.d.ts.map +0 -1
  79. package/dist/components/SplitCommissionBadge.d.ts +0 -18
  80. package/dist/components/SplitCommissionBadge.d.ts.map +0 -1
  81. package/dist/components/__tests__/Button.test.d.ts +0 -2
  82. package/dist/components/__tests__/Button.test.d.ts.map +0 -1
  83. package/dist/components/__tests__/Input.test.d.ts +0 -2
  84. package/dist/components/__tests__/Input.test.d.ts.map +0 -1
@@ -0,0 +1,143 @@
1
+ import { WorkBook } from 'xlsx';
2
+ /**
3
+ * Column definition for Excel export
4
+ */
5
+ export interface ExcelColumn {
6
+ /** Key in the data object */
7
+ key: string;
8
+ /** Column header label */
9
+ label: string;
10
+ /** Optional formatter function */
11
+ format?: (value: any) => string | number | boolean;
12
+ }
13
+ /**
14
+ * Options for exporting data to Excel
15
+ */
16
+ export interface ExportToExcelOptions {
17
+ /** Array of data objects to export */
18
+ data: any[];
19
+ /** Output filename (default: 'export.xlsx') */
20
+ filename?: string;
21
+ /** Sheet name (default: 'Sheet1') */
22
+ sheetName?: string;
23
+ /** Column definitions for custom headers and ordering */
24
+ columns?: ExcelColumn[];
25
+ /** Include headers row (default: true) */
26
+ includeHeaders?: boolean;
27
+ /** Custom workbook for multi-sheet exports */
28
+ workbook?: WorkBook;
29
+ }
30
+ /**
31
+ * Export data to Excel file
32
+ *
33
+ * A standalone utility for exporting any data array to Excel format.
34
+ * Works independently of the Spreadsheet component.
35
+ *
36
+ * **Features:**
37
+ * - Export arrays of objects to Excel
38
+ * - Custom column headers and ordering
39
+ * - Value formatting with custom functions
40
+ * - Multi-sheet support
41
+ * - Automatic type handling
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // Simple export - uses object keys as headers
46
+ * const data = [
47
+ * { id: 1, name: 'Product A', price: 29.99 },
48
+ * { id: 2, name: 'Product B', price: 49.99 },
49
+ * ];
50
+ * exportToExcel({ data, filename: 'products.xlsx' });
51
+ *
52
+ * // Custom columns with formatting
53
+ * exportToExcel({
54
+ * data: users,
55
+ * filename: 'users.xlsx',
56
+ * columns: [
57
+ * { key: 'id', label: 'ID' },
58
+ * { key: 'name', label: 'Full Name' },
59
+ * { key: 'email', label: 'Email Address' },
60
+ * { key: 'createdAt', label: 'Joined', format: (date) => new Date(date).toLocaleDateString() },
61
+ * { key: 'isActive', label: 'Status', format: (active) => active ? 'Active' : 'Inactive' },
62
+ * ],
63
+ * });
64
+ *
65
+ * // Multi-sheet export
66
+ * const wb = utils.book_new();
67
+ * exportToExcel({ data: products, sheetName: 'Products', workbook: wb });
68
+ * exportToExcel({ data: orders, sheetName: 'Orders', workbook: wb });
69
+ * writeFile(wb, 'multi-sheet.xlsx');
70
+ * ```
71
+ *
72
+ * @param options - Export configuration options
73
+ * @returns WorkBook if workbook option provided, otherwise void (auto-downloads)
74
+ */
75
+ export declare function exportToExcel({ data, filename, sheetName, columns, includeHeaders, workbook, }: ExportToExcelOptions): WorkBook | void;
76
+ /**
77
+ * Export DataTable-compatible data to Excel
78
+ *
79
+ * Helper function specifically designed for exporting DataTable data.
80
+ * Automatically handles common DataTable column configurations.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * import { exportDataTableToExcel } from 'notebook-ui';
85
+ *
86
+ * const columns = [
87
+ * { key: 'id', header: 'ID' },
88
+ * { key: 'name', header: 'Name' },
89
+ * { key: 'price', header: 'Price' },
90
+ * ];
91
+ *
92
+ * exportDataTableToExcel({
93
+ * data: products,
94
+ * columns: columns,
95
+ * filename: 'products.xlsx',
96
+ * });
97
+ * ```
98
+ */
99
+ export interface DataTableExportOptions {
100
+ /** Array of data objects */
101
+ data: any[];
102
+ /** DataTable column definitions */
103
+ columns: Array<{
104
+ key: string;
105
+ header: string;
106
+ }>;
107
+ /** Output filename */
108
+ filename?: string;
109
+ /** Sheet name */
110
+ sheetName?: string;
111
+ }
112
+ export declare function exportDataTableToExcel({ data, columns, filename, sheetName, }: DataTableExportOptions): void;
113
+ /**
114
+ * Create a multi-sheet Excel workbook
115
+ *
116
+ * Utility for creating Excel files with multiple sheets.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * import { createMultiSheetExcel } from 'notebook-ui';
121
+ *
122
+ * createMultiSheetExcel({
123
+ * filename: 'report.xlsx',
124
+ * sheets: [
125
+ * { name: 'Products', data: products },
126
+ * { name: 'Orders', data: orders },
127
+ * { name: 'Customers', data: customers, columns: customerColumns },
128
+ * ],
129
+ * });
130
+ * ```
131
+ */
132
+ export interface MultiSheetExcelOptions {
133
+ /** Output filename */
134
+ filename: string;
135
+ /** Array of sheet configurations */
136
+ sheets: Array<{
137
+ name: string;
138
+ data: any[];
139
+ columns?: ExcelColumn[];
140
+ }>;
141
+ }
142
+ export declare function createMultiSheetExcel({ filename, sheets }: MultiSheetExcelOptions): void;
143
+ //# sourceMappingURL=excelExport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excelExport.d.ts","sourceRoot":"","sources":["../../src/utils/excelExport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,QAAQ,EAAE,MAAM,MAAM,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACpD;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sCAAsC;IACtC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAwB,EACxB,SAAoB,EACpB,OAAO,EACP,cAAqB,EACrB,QAAQ,GACT,EAAE,oBAAoB,GAAG,QAAQ,GAAG,IAAI,CA0CxC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,mCAAmC;IACnC,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,OAAO,EACP,QAAwB,EACxB,SAAoB,GACrB,EAAE,sBAAsB,GAAG,IAAI,CAY/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,EAAE,CAAC;QACZ,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;KACzB,CAAC,CAAC;CACJ;AAED,wBAAgB,qBAAqB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,sBAAsB,GAAG,IAAI,CAaxF"}
@@ -2,4 +2,6 @@ export { translateSqlToNaturalLanguage } from './sqlToNaturalLanguage';
2
2
  export type { QueryDescription, FriendlyNameConfig } from './sqlToNaturalLanguage';
3
3
  export { formatStatisticValue, formatStatistics } from './statisticsFormatter';
4
4
  export type { StatisticFormat, StatisticConfig, FormattedStatistic } from './statisticsFormatter';
5
+ export { exportToExcel, exportDataTableToExcel, createMultiSheetExcel } from './excelExport';
6
+ export type { ExcelColumn, ExportToExcelOptions, DataTableExportOptions, MultiSheetExcelOptions } from './excelExport';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAElG,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC7F,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papernote/ui",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "A modern React component library with a paper notebook aesthetic - minimal, professional, and expressive",
6
6
  "main": "dist/index.js",
@@ -31,8 +31,12 @@
31
31
  "test:watch": "jest --watch",
32
32
  "test:coverage": "jest --coverage",
33
33
  "prepublishOnly": "npm run build",
34
+ "prerelease": "bash scripts/pre-publish-check.sh",
34
35
  "storybook": "storybook dev -p 6006",
35
- "build-storybook": "storybook build"
36
+ "build-storybook": "storybook build",
37
+ "docs:dev": "vitepress dev docs-site",
38
+ "docs:build": "vitepress build docs-site",
39
+ "docs:preview": "vitepress preview docs-site"
36
40
  },
37
41
  "peerDependencies": {
38
42
  "lucide-react": "^0.553.0",
@@ -64,6 +68,7 @@
64
68
  "jest-environment-jsdom": "^30.2.0",
65
69
  "lucide-react": "^0.554.0",
66
70
  "postcss": "^8.4.0",
71
+ "postcss-import": "^16.1.1",
67
72
  "react": "^19.2.0",
68
73
  "react-dom": "^19.2.0",
69
74
  "react-router-dom": "^7.9.6",
@@ -76,7 +81,8 @@
76
81
  "ts-jest": "^29.4.5",
77
82
  "tslib": "^2.6.0",
78
83
  "typescript": "^5.0.0",
79
- "vite": "^7.2.4"
84
+ "vite": "^7.2.4",
85
+ "vitepress": "^1.6.4"
80
86
  },
81
87
  "keywords": [
82
88
  "react",
@@ -99,5 +105,9 @@
99
105
  "homepage": "https://github.com/kwhittenberger/papernote-ui#readme",
100
106
  "bugs": {
101
107
  "url": "https://github.com/kwhittenberger/papernote-ui/issues"
108
+ },
109
+ "dependencies": {
110
+ "react-spreadsheet": "^0.10.1",
111
+ "xlsx": "^0.18.5"
102
112
  }
103
113
  }
@@ -1,49 +1,49 @@
1
- /* AdminModal.css - Styles for AdminModal component */
2
-
3
- .admin-modal-overlay {
4
- left: 240px;
5
- }
6
-
7
- .admin-modal-content {
8
- max-height: 90vh;
9
- }
10
-
11
- .admin-modal-large {
12
- max-width: 1200px;
13
- }
14
-
15
- .admin-modal-medium {
16
- max-width: 800px;
17
- }
18
-
19
- .admin-modal-sidebar-placeholder {
20
- left: 0;
21
- width: 240px;
22
- z-index: -1;
23
- }
24
-
25
- .admin-modal-tabs {
26
- gap: 2rem;
27
- }
28
-
29
- .admin-modal-tab-indicator {
30
- height: 2px;
31
- }
32
-
33
- .admin-modal-tab-separator {
34
- width: 1px;
35
- height: 20px;
36
- background-color: #d1d5db;
37
- margin: 0 16px;
38
- }
39
-
40
- .admin-modal-form,
41
- .admin-modal-content-area {
42
- height: 0px;
43
- min-height: 0px;
44
- max-height: none;
45
- }
46
-
47
- .admin-modal-footer {
48
- gap: 0.75rem;
49
- }
1
+ /* AdminModal.css - Styles for AdminModal component */
2
+
3
+ .admin-modal-overlay {
4
+ left: 240px;
5
+ }
6
+
7
+ .admin-modal-content {
8
+ max-height: 90vh;
9
+ }
10
+
11
+ .admin-modal-large {
12
+ max-width: 1200px;
13
+ }
14
+
15
+ .admin-modal-medium {
16
+ max-width: 800px;
17
+ }
18
+
19
+ .admin-modal-sidebar-placeholder {
20
+ left: 0;
21
+ width: 240px;
22
+ z-index: -1;
23
+ }
24
+
25
+ .admin-modal-tabs {
26
+ gap: 2rem;
27
+ }
28
+
29
+ .admin-modal-tab-indicator {
30
+ height: 2px;
31
+ }
32
+
33
+ .admin-modal-tab-separator {
34
+ width: 1px;
35
+ height: 20px;
36
+ background-color: #d1d5db;
37
+ margin: 0 16px;
38
+ }
39
+
40
+ .admin-modal-form,
41
+ .admin-modal-content-area {
42
+ height: 0px;
43
+ min-height: 0px;
44
+ max-height: none;
45
+ }
46
+
47
+ .admin-modal-footer {
48
+ gap: 0.75rem;
49
+ }
@@ -0,0 +1,290 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import CurrencyInput from './CurrencyInput';
4
+
5
+ const meta = {
6
+ title: 'Forms/CurrencyInput',
7
+ component: CurrencyInput,
8
+ parameters: {
9
+ layout: 'centered',
10
+ docs: {
11
+ description: {
12
+ component: `
13
+ Specialized input component for monetary values with automatic currency formatting.
14
+
15
+ ## Features
16
+ - **Auto-formatting**: Formats currency with proper symbols and thousands separators
17
+ - **Smart parsing**: Handles user input gracefully, removing invalid characters
18
+ - **Multiple currencies**: Supports any currency code (USD, EUR, GBP, etc.)
19
+ - **Localization**: Respects locale for number formatting
20
+ - **Validation**: Built-in min/max value constraints
21
+ - **Negative values**: Optional support for negative amounts
22
+ - **Precision control**: Configurable decimal places
23
+
24
+ ## Usage
25
+
26
+ \`\`\`tsx
27
+ import { CurrencyInput } from 'notebook-ui';
28
+
29
+ const [price, setPrice] = useState<number | null>(1234.56);
30
+
31
+ <CurrencyInput
32
+ label="Price"
33
+ value={price}
34
+ onChange={setPrice}
35
+ currency="USD"
36
+ precision={2}
37
+ />
38
+ \`\`\`
39
+ `,
40
+ },
41
+ },
42
+ },
43
+ tags: ['autodocs'],
44
+ argTypes: {
45
+ value: {
46
+ control: 'number',
47
+ description: 'Numeric value (unformatted)',
48
+ table: {
49
+ type: { summary: 'number | string' },
50
+ },
51
+ },
52
+ onChange: {
53
+ description: 'Callback when value changes (receives numeric value)',
54
+ table: {
55
+ type: { summary: '(value: number | null) => void' },
56
+ },
57
+ },
58
+ currency: {
59
+ control: 'text',
60
+ description: 'Currency code (ISO 4217)',
61
+ table: {
62
+ type: { summary: 'string' },
63
+ defaultValue: { summary: 'USD' },
64
+ },
65
+ },
66
+ locale: {
67
+ control: 'text',
68
+ description: 'Locale for formatting',
69
+ table: {
70
+ type: { summary: 'string' },
71
+ defaultValue: { summary: 'en-US' },
72
+ },
73
+ },
74
+ precision: {
75
+ control: 'number',
76
+ description: 'Number of decimal places',
77
+ table: {
78
+ type: { summary: 'number' },
79
+ defaultValue: { summary: '2' },
80
+ },
81
+ },
82
+ allowNegative: {
83
+ control: 'boolean',
84
+ description: 'Allow negative values',
85
+ table: {
86
+ type: { summary: 'boolean' },
87
+ defaultValue: { summary: 'false' },
88
+ },
89
+ },
90
+ min: {
91
+ control: 'number',
92
+ description: 'Minimum allowed value',
93
+ table: {
94
+ type: { summary: 'number' },
95
+ },
96
+ },
97
+ max: {
98
+ control: 'number',
99
+ description: 'Maximum allowed value',
100
+ table: {
101
+ type: { summary: 'number' },
102
+ },
103
+ },
104
+ },
105
+ } satisfies Meta<typeof CurrencyInput>;
106
+
107
+ export default meta;
108
+ type Story = StoryObj<typeof meta>;
109
+
110
+ export const Default: Story = {
111
+ render: () => {
112
+ const [value, setValue] = useState<number | null>(1234.56);
113
+ return (
114
+ <div style={{ width: '300px' }}>
115
+ <CurrencyInput
116
+ label="Price"
117
+ value={value}
118
+ onChange={setValue}
119
+ helperText="Enter the product price"
120
+ />
121
+ <div style={{ marginTop: '1rem', fontSize: '0.875rem', color: '#64748b' }}>
122
+ Current value: {value !== null ? `$${value.toFixed(2)}` : 'null'}
123
+ </div>
124
+ </div>
125
+ );
126
+ },
127
+ };
128
+
129
+ export const WithValidation: Story = {
130
+ render: () => {
131
+ const [value, setValue] = useState<number | null>(150);
132
+ const max = 100;
133
+ const hasError = value !== null && value > max;
134
+
135
+ return (
136
+ <div style={{ width: '300px' }}>
137
+ <CurrencyInput
138
+ label="Budget"
139
+ value={value}
140
+ onChange={setValue}
141
+ max={max}
142
+ validationState={hasError ? 'error' : value !== null && value > 0 ? 'success' : null}
143
+ validationMessage={hasError ? `Exceeds maximum budget of $${max}` : value !== null && value > 0 ? 'Within budget' : ''}
144
+ />
145
+ </div>
146
+ );
147
+ },
148
+ };
149
+
150
+ export const DifferentCurrencies: Story = {
151
+ render: () => {
152
+ const [usd, setUsd] = useState<number | null>(1234.56);
153
+ const [eur, setEur] = useState<number | null>(1234.56);
154
+ const [gbp, setGbp] = useState<number | null>(1234.56);
155
+ const [jpy, setJpy] = useState<number | null>(123456);
156
+
157
+ return (
158
+ <div style={{ width: '300px', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
159
+ <CurrencyInput
160
+ label="US Dollars"
161
+ value={usd}
162
+ onChange={setUsd}
163
+ currency="USD"
164
+ locale="en-US"
165
+ />
166
+ <CurrencyInput
167
+ label="Euros"
168
+ value={eur}
169
+ onChange={setEur}
170
+ currency="EUR"
171
+ locale="de-DE"
172
+ />
173
+ <CurrencyInput
174
+ label="British Pounds"
175
+ value={gbp}
176
+ onChange={setGbp}
177
+ currency="GBP"
178
+ locale="en-GB"
179
+ />
180
+ <CurrencyInput
181
+ label="Japanese Yen"
182
+ value={jpy}
183
+ onChange={setJpy}
184
+ currency="JPY"
185
+ locale="ja-JP"
186
+ precision={0}
187
+ />
188
+ </div>
189
+ );
190
+ },
191
+ };
192
+
193
+ export const WithNegativeValues: Story = {
194
+ render: () => {
195
+ const [value, setValue] = useState<number | null>(-500.75);
196
+ return (
197
+ <div style={{ width: '300px' }}>
198
+ <CurrencyInput
199
+ label="Profit/Loss"
200
+ value={value}
201
+ onChange={setValue}
202
+ allowNegative
203
+ helperText="Negative values allowed for losses"
204
+ />
205
+ <div style={{ marginTop: '1rem', fontSize: '0.875rem', color: '#64748b' }}>
206
+ Current value: {value !== null ? `$${value.toFixed(2)}` : 'null'}
207
+ </div>
208
+ </div>
209
+ );
210
+ },
211
+ };
212
+
213
+ export const WithMinMax: Story = {
214
+ render: () => {
215
+ const [value, setValue] = useState<number | null>(50);
216
+ const min = 10;
217
+ const max = 100;
218
+
219
+ return (
220
+ <div style={{ width: '300px' }}>
221
+ <CurrencyInput
222
+ label="Amount"
223
+ value={value}
224
+ onChange={setValue}
225
+ min={min}
226
+ max={max}
227
+ helperText={`Min: $${min}, Max: $${max}`}
228
+ />
229
+ <div style={{ marginTop: '1rem', fontSize: '0.875rem', color: '#64748b' }}>
230
+ Current value: {value !== null ? `$${value.toFixed(2)}` : 'null'}
231
+ </div>
232
+ </div>
233
+ );
234
+ },
235
+ };
236
+
237
+ export const Required: Story = {
238
+ render: () => {
239
+ const [value, setValue] = useState<number | null>(null);
240
+ const hasError = value === null;
241
+
242
+ return (
243
+ <div style={{ width: '300px' }}>
244
+ <CurrencyInput
245
+ label="Donation Amount"
246
+ value={value}
247
+ onChange={setValue}
248
+ required
249
+ validationState={hasError ? 'error' : 'success'}
250
+ validationMessage={hasError ? 'Amount is required' : 'Thank you!'}
251
+ />
252
+ </div>
253
+ );
254
+ },
255
+ };
256
+
257
+ export const Clearable: Story = {
258
+ render: () => {
259
+ const [value, setValue] = useState<number | null>(1234.56);
260
+ return (
261
+ <div style={{ width: '300px' }}>
262
+ <CurrencyInput
263
+ label="Price"
264
+ value={value}
265
+ onChange={setValue}
266
+ clearable
267
+ onClear={() => setValue(null)}
268
+ helperText="Click X to clear the value"
269
+ />
270
+ <div style={{ marginTop: '1rem', fontSize: '0.875rem', color: '#64748b' }}>
271
+ Current value: {value !== null ? `$${value.toFixed(2)}` : 'null'}
272
+ </div>
273
+ </div>
274
+ );
275
+ },
276
+ };
277
+
278
+ export const Disabled: Story = {
279
+ render: () => (
280
+ <div style={{ width: '300px' }}>
281
+ <CurrencyInput
282
+ label="Price"
283
+ value={1234.56}
284
+ onChange={() => {}}
285
+ disabled
286
+ helperText="This field is disabled"
287
+ />
288
+ </div>
289
+ ),
290
+ };