@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.
- package/LICENSE +21 -21
- package/README.md +455 -445
- package/dist/components/CurrencyInput.d.ts +52 -0
- package/dist/components/CurrencyInput.d.ts.map +1 -0
- package/dist/components/DataTable.d.ts +3 -1
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/Modal.d.ts.map +1 -1
- package/dist/components/Page.d.ts +2 -0
- package/dist/components/Page.d.ts.map +1 -1
- package/dist/components/PageLayout.d.ts +5 -1
- package/dist/components/PageLayout.d.ts.map +1 -1
- package/dist/components/Spreadsheet.d.ts +129 -0
- package/dist/components/Spreadsheet.d.ts.map +1 -0
- package/dist/components/Tabs.d.ts +5 -1
- package/dist/components/Tabs.d.ts.map +1 -1
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +336 -5
- package/dist/index.esm.js +51152 -174
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +51145 -143
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1187 -11
- package/dist/utils/excelExport.d.ts +143 -0
- package/dist/utils/excelExport.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +13 -3
- package/src/components/AdminModal.css +49 -49
- package/src/components/CurrencyInput.stories.tsx +290 -0
- package/src/components/CurrencyInput.tsx +193 -0
- package/src/components/DataTable.stories.tsx +87 -0
- package/src/components/DataTable.tsx +149 -37
- package/src/components/Modal.stories.tsx +64 -0
- package/src/components/Modal.tsx +15 -2
- package/src/components/Page.stories.tsx +76 -0
- package/src/components/Page.tsx +35 -3
- package/src/components/PageLayout.stories.tsx +75 -0
- package/src/components/PageLayout.tsx +28 -9
- package/src/components/RoleManager.css +10 -10
- package/src/components/Spreadsheet.css +216 -0
- package/src/components/Spreadsheet.stories.tsx +362 -0
- package/src/components/Spreadsheet.tsx +351 -0
- package/src/components/SpreadsheetSimple.stories.tsx +27 -0
- package/src/components/Tabs.stories.tsx +31 -0
- package/src/components/Tabs.tsx +28 -4
- package/src/components/TimePicker.tsx +1 -1
- package/src/components/Toast.tsx +9 -9
- package/src/components/__tests__/Input.test.tsx +22 -26
- package/src/components/index.ts +11 -2
- package/src/styles/index.css +44 -6
- package/src/utils/excelExport.stories.tsx +535 -0
- package/src/utils/excelExport.ts +225 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/sqlToNaturalLanguage.ts +1 -1
- package/tailwind.config.js +253 -253
- package/dist/components/Button.stories.d.ts +0 -51
- package/dist/components/Button.stories.d.ts.map +0 -1
- package/dist/components/ChartVisualizationUI.d.ts +0 -21
- package/dist/components/ChartVisualizationUI.d.ts.map +0 -1
- package/dist/components/ChatUI.d.ts +0 -23
- package/dist/components/ChatUI.d.ts.map +0 -1
- package/dist/components/CommissionDashboardUI.d.ts +0 -25
- package/dist/components/CommissionDashboardUI.d.ts.map +0 -1
- package/dist/components/DataTable.stories.d.ts +0 -23
- package/dist/components/DataTable.stories.d.ts.map +0 -1
- package/dist/components/FormField.d.ts +0 -35
- package/dist/components/FormField.d.ts.map +0 -1
- package/dist/components/Input.stories.d.ts +0 -366
- package/dist/components/Input.stories.d.ts.map +0 -1
- package/dist/components/InsightsPanelUI.d.ts +0 -21
- package/dist/components/InsightsPanelUI.d.ts.map +0 -1
- package/dist/components/PaymentHistoryTimeline.d.ts +0 -34
- package/dist/components/PaymentHistoryTimeline.d.ts.map +0 -1
- package/dist/components/RelationshipManagerUI.d.ts +0 -60
- package/dist/components/RelationshipManagerUI.d.ts.map +0 -1
- package/dist/components/RoleManager.d.ts +0 -19
- package/dist/components/RoleManager.d.ts.map +0 -1
- package/dist/components/SplitCommissionBadge.d.ts +0 -18
- package/dist/components/SplitCommissionBadge.d.ts.map +0 -1
- package/dist/components/__tests__/Button.test.d.ts +0 -2
- package/dist/components/__tests__/Button.test.d.ts.map +0 -1
- package/dist/components/__tests__/Input.test.d.ts +0 -2
- 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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -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.
|
|
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
|
+
};
|