@papernote/ui 1.1.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 (75) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +455 -455
  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/index.d.ts +4 -0
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/index.d.ts +204 -4
  15. package/dist/index.esm.js +415 -88
  16. package/dist/index.esm.js.map +1 -1
  17. package/dist/index.js +413 -82
  18. package/dist/index.js.map +1 -1
  19. package/dist/styles.css +2877 -2675
  20. package/dist/utils/excelExport.d.ts +143 -0
  21. package/dist/utils/excelExport.d.ts.map +1 -0
  22. package/dist/utils/index.d.ts +2 -0
  23. package/dist/utils/index.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/src/components/AdminModal.css +49 -49
  26. package/src/components/CurrencyInput.stories.tsx +290 -0
  27. package/src/components/CurrencyInput.tsx +193 -0
  28. package/src/components/DataTable.tsx +78 -14
  29. package/src/components/Modal.stories.tsx +64 -0
  30. package/src/components/Modal.tsx +15 -2
  31. package/src/components/Page.stories.tsx +76 -0
  32. package/src/components/Page.tsx +35 -3
  33. package/src/components/PageLayout.stories.tsx +75 -0
  34. package/src/components/PageLayout.tsx +28 -9
  35. package/src/components/RoleManager.css +10 -10
  36. package/src/components/Spreadsheet.css +216 -216
  37. package/src/components/Spreadsheet.stories.tsx +362 -362
  38. package/src/components/Spreadsheet.tsx +351 -351
  39. package/src/components/SpreadsheetSimple.stories.tsx +27 -27
  40. package/src/components/Tabs.tsx +152 -152
  41. package/src/components/index.ts +5 -0
  42. package/src/styles/index.css +41 -4
  43. package/src/utils/excelExport.stories.tsx +535 -0
  44. package/src/utils/excelExport.ts +225 -0
  45. package/src/utils/index.ts +3 -0
  46. package/tailwind.config.js +253 -253
  47. package/dist/components/Button.stories.d.ts +0 -51
  48. package/dist/components/Button.stories.d.ts.map +0 -1
  49. package/dist/components/ChartVisualizationUI.d.ts +0 -21
  50. package/dist/components/ChartVisualizationUI.d.ts.map +0 -1
  51. package/dist/components/ChatUI.d.ts +0 -23
  52. package/dist/components/ChatUI.d.ts.map +0 -1
  53. package/dist/components/CommissionDashboardUI.d.ts +0 -25
  54. package/dist/components/CommissionDashboardUI.d.ts.map +0 -1
  55. package/dist/components/DataTable.stories.d.ts +0 -23
  56. package/dist/components/DataTable.stories.d.ts.map +0 -1
  57. package/dist/components/FormField.d.ts +0 -35
  58. package/dist/components/FormField.d.ts.map +0 -1
  59. package/dist/components/Input.stories.d.ts +0 -366
  60. package/dist/components/Input.stories.d.ts.map +0 -1
  61. package/dist/components/InsightsPanelUI.d.ts +0 -21
  62. package/dist/components/InsightsPanelUI.d.ts.map +0 -1
  63. package/dist/components/PaymentHistoryTimeline.d.ts +0 -34
  64. package/dist/components/PaymentHistoryTimeline.d.ts.map +0 -1
  65. package/dist/components/RelationshipManagerUI.d.ts +0 -60
  66. package/dist/components/RelationshipManagerUI.d.ts.map +0 -1
  67. package/dist/components/RoleManager.d.ts +0 -19
  68. package/dist/components/RoleManager.d.ts.map +0 -1
  69. package/dist/components/SplitCommissionBadge.d.ts +0 -18
  70. package/dist/components/SplitCommissionBadge.d.ts.map +0 -1
  71. package/dist/components/Spreadsheet.css +0 -216
  72. package/dist/components/__tests__/Button.test.d.ts +0 -2
  73. package/dist/components/__tests__/Button.test.d.ts.map +0 -1
  74. package/dist/components/__tests__/Input.test.d.ts +0 -2
  75. package/dist/components/__tests__/Input.test.d.ts.map +0 -1
@@ -1,27 +1,27 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { Spreadsheet } from './Spreadsheet';
3
-
4
- const meta: Meta<typeof Spreadsheet> = {
5
- title: 'Components/Spreadsheet/Simple Test',
6
- component: Spreadsheet,
7
- parameters: {
8
- docs: {
9
- description: {
10
- component: 'Simple test story for Spreadsheet component debugging.',
11
- },
12
- },
13
- },
14
- };
15
-
16
- export default meta;
17
- type Story = StoryObj<typeof Spreadsheet>;
18
-
19
- /**
20
- * Minimal test - just render with defaults
21
- */
22
- export const MinimalTest: Story = {
23
- args: {
24
- rows: 5,
25
- columns: 3,
26
- },
27
- };
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Spreadsheet } from './Spreadsheet';
3
+
4
+ const meta: Meta<typeof Spreadsheet> = {
5
+ title: 'Components/Spreadsheet/Simple Test',
6
+ component: Spreadsheet,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: 'Simple test story for Spreadsheet component debugging.',
11
+ },
12
+ },
13
+ },
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof Spreadsheet>;
18
+
19
+ /**
20
+ * Minimal test - just render with defaults
21
+ */
22
+ export const MinimalTest: Story = {
23
+ args: {
24
+ rows: 5,
25
+ columns: 3,
26
+ },
27
+ };
@@ -1,152 +1,152 @@
1
- import React, { useState, useEffect } from 'react';
2
-
3
- export interface Tab {
4
- id: string;
5
- label: string;
6
- icon?: React.ReactNode;
7
- content: React.ReactNode;
8
- disabled?: boolean;
9
- }
10
-
11
- export interface TabsProps {
12
- tabs: Tab[];
13
- /** Controlled mode: Currently active tab ID */
14
- activeTab?: string;
15
- /** Uncontrolled mode: Initial tab ID (ignored if activeTab is provided) */
16
- defaultTab?: string;
17
- variant?: 'underline' | 'pill';
18
- /** Orientation of tabs (default: 'horizontal') */
19
- orientation?: 'horizontal' | 'vertical';
20
- /** Size of tabs (default: 'md') */
21
- size?: 'sm' | 'md' | 'lg';
22
- /** Called when tab changes (required for controlled mode) */
23
- onChange?: (tabId: string) => void;
24
- }
25
-
26
- export default function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'underline', orientation = 'horizontal', size = 'md', onChange }: TabsProps) {
27
- const [internalActiveTab, setInternalActiveTab] = useState(defaultTab || tabs[0]?.id);
28
-
29
- // Controlled mode: use activeTab prop, Uncontrolled mode: use internal state
30
- const isControlled = controlledActiveTab !== undefined;
31
- const activeTab = isControlled ? controlledActiveTab : internalActiveTab;
32
-
33
- // Ensure the activeTab exists in the current tabs array
34
- // This handles the case where tabs array reference changes at the same time as activeTab
35
- useEffect(() => {
36
- const tabExists = tabs.some(tab => tab.id === activeTab);
37
- if (!tabExists && tabs.length > 0) {
38
- // If the activeTab doesn't exist in the new tabs array, use the first tab
39
- if (isControlled) {
40
- onChange?.(tabs[0].id);
41
- } else {
42
- setInternalActiveTab(tabs[0].id);
43
- }
44
- }
45
- }, [tabs, activeTab, isControlled, onChange]);
46
-
47
- const handleTabChange = (tabId: string) => {
48
- if (!isControlled) {
49
- setInternalActiveTab(tabId);
50
- }
51
- onChange?.(tabId);
52
- };
53
-
54
- // Size-specific classes
55
- const sizeClasses = {
56
- sm: {
57
- padding: 'px-3 py-1.5',
58
- text: 'text-xs',
59
- icon: 'h-3.5 w-3.5',
60
- gap: orientation === 'vertical' ? 'gap-1.5' : 'gap-4',
61
- minWidth: orientation === 'vertical' ? 'min-w-[150px]' : '',
62
- spacing: orientation === 'vertical' ? 'mt-4' : 'mt-4',
63
- },
64
- md: {
65
- padding: 'px-4 py-2.5',
66
- text: 'text-sm',
67
- icon: 'h-4 w-4',
68
- gap: orientation === 'vertical' ? 'gap-2' : 'gap-6',
69
- minWidth: orientation === 'vertical' ? 'min-w-[200px]' : '',
70
- spacing: orientation === 'vertical' ? 'mt-6' : 'mt-6',
71
- },
72
- lg: {
73
- padding: 'px-5 py-3',
74
- text: 'text-base',
75
- icon: 'h-5 w-5',
76
- gap: orientation === 'vertical' ? 'gap-3' : 'gap-8',
77
- minWidth: orientation === 'vertical' ? 'min-w-[250px]' : '',
78
- spacing: orientation === 'vertical' ? 'mt-8' : 'mt-8',
79
- },
80
- };
81
-
82
- return (
83
- <div className={`w-full ${orientation === 'vertical' ? `flex ${sizeClasses[size].gap}` : ''}`}>
84
- {/* Tab Headers */}
85
- <div
86
- className={`
87
- flex ${orientation === 'vertical' ? 'flex-col' : ''}
88
- ${variant === 'underline'
89
- ? orientation === 'vertical'
90
- ? `border-r border-paper-200 ${sizeClasses[size].gap} pr-6`
91
- : `border-b border-paper-200 ${sizeClasses[size].gap}`
92
- : `${sizeClasses[size].gap} p-1 bg-paper-50 rounded-lg`
93
- }
94
- ${sizeClasses[size].minWidth}
95
- `}
96
- role="tablist"
97
- >
98
- {tabs.map((tab) => {
99
- const isActive = activeTab === tab.id;
100
-
101
- return (
102
- <button
103
- key={tab.id}
104
- role="tab"
105
- aria-selected={isActive}
106
- aria-controls={`panel-${tab.id}`}
107
- disabled={tab.disabled}
108
- onClick={() => !tab.disabled && handleTabChange(tab.id)}
109
- className={`
110
- flex items-center gap-2 ${sizeClasses[size].padding} ${sizeClasses[size].text} font-medium transition-all duration-200
111
- ${orientation === 'vertical' ? 'w-full justify-start' : ''}
112
- ${
113
- variant === 'underline'
114
- ? isActive
115
- ? orientation === 'vertical'
116
- ? 'text-accent-900 border-r-2 border-accent-500 -mr-[1px]'
117
- : 'text-accent-900 border-b-2 border-accent-500 -mb-[1px]'
118
- : orientation === 'vertical'
119
- ? 'text-ink-600 hover:text-ink-900 border-r-2 border-transparent'
120
- : 'text-ink-600 hover:text-ink-900 border-b-2 border-transparent'
121
- : isActive
122
- ? 'bg-white text-accent-900 rounded-md shadow-xs'
123
- : 'text-ink-600 hover:text-ink-900 hover:bg-white/50 rounded-md'
124
- }
125
- ${tab.disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
126
- `}
127
- >
128
- {tab.icon && <span className={`flex-shrink-0 ${sizeClasses[size].icon}`}>{tab.icon}</span>}
129
- <span>{tab.label}</span>
130
- </button>
131
- );
132
- })}
133
- </div>
134
-
135
- {/* Tab Content */}
136
- <div className={`${orientation === 'vertical' ? 'flex-1' : sizeClasses[size].spacing}`}>
137
- {tabs.map((tab) => (
138
- <div
139
- key={tab.id}
140
- id={`panel-${tab.id}`}
141
- role="tabpanel"
142
- aria-labelledby={tab.id}
143
- hidden={activeTab !== tab.id}
144
- className={activeTab === tab.id ? 'animate-fade-in' : ''}
145
- >
146
- {activeTab === tab.id && tab.content}
147
- </div>
148
- ))}
149
- </div>
150
- </div>
151
- );
152
- }
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ export interface Tab {
4
+ id: string;
5
+ label: string;
6
+ icon?: React.ReactNode;
7
+ content: React.ReactNode;
8
+ disabled?: boolean;
9
+ }
10
+
11
+ export interface TabsProps {
12
+ tabs: Tab[];
13
+ /** Controlled mode: Currently active tab ID */
14
+ activeTab?: string;
15
+ /** Uncontrolled mode: Initial tab ID (ignored if activeTab is provided) */
16
+ defaultTab?: string;
17
+ variant?: 'underline' | 'pill';
18
+ /** Orientation of tabs (default: 'horizontal') */
19
+ orientation?: 'horizontal' | 'vertical';
20
+ /** Size of tabs (default: 'md') */
21
+ size?: 'sm' | 'md' | 'lg';
22
+ /** Called when tab changes (required for controlled mode) */
23
+ onChange?: (tabId: string) => void;
24
+ }
25
+
26
+ export default function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'underline', orientation = 'horizontal', size = 'md', onChange }: TabsProps) {
27
+ const [internalActiveTab, setInternalActiveTab] = useState(defaultTab || tabs[0]?.id);
28
+
29
+ // Controlled mode: use activeTab prop, Uncontrolled mode: use internal state
30
+ const isControlled = controlledActiveTab !== undefined;
31
+ const activeTab = isControlled ? controlledActiveTab : internalActiveTab;
32
+
33
+ // Ensure the activeTab exists in the current tabs array
34
+ // This handles the case where tabs array reference changes at the same time as activeTab
35
+ useEffect(() => {
36
+ const tabExists = tabs.some(tab => tab.id === activeTab);
37
+ if (!tabExists && tabs.length > 0) {
38
+ // If the activeTab doesn't exist in the new tabs array, use the first tab
39
+ if (isControlled) {
40
+ onChange?.(tabs[0].id);
41
+ } else {
42
+ setInternalActiveTab(tabs[0].id);
43
+ }
44
+ }
45
+ }, [tabs, activeTab, isControlled, onChange]);
46
+
47
+ const handleTabChange = (tabId: string) => {
48
+ if (!isControlled) {
49
+ setInternalActiveTab(tabId);
50
+ }
51
+ onChange?.(tabId);
52
+ };
53
+
54
+ // Size-specific classes
55
+ const sizeClasses = {
56
+ sm: {
57
+ padding: 'px-3 py-1.5',
58
+ text: 'text-xs',
59
+ icon: 'h-3.5 w-3.5',
60
+ gap: orientation === 'vertical' ? 'gap-1.5' : 'gap-4',
61
+ minWidth: orientation === 'vertical' ? 'min-w-[150px]' : '',
62
+ spacing: orientation === 'vertical' ? 'mt-4' : 'mt-4',
63
+ },
64
+ md: {
65
+ padding: 'px-4 py-2.5',
66
+ text: 'text-sm',
67
+ icon: 'h-4 w-4',
68
+ gap: orientation === 'vertical' ? 'gap-2' : 'gap-6',
69
+ minWidth: orientation === 'vertical' ? 'min-w-[200px]' : '',
70
+ spacing: orientation === 'vertical' ? 'mt-6' : 'mt-6',
71
+ },
72
+ lg: {
73
+ padding: 'px-5 py-3',
74
+ text: 'text-base',
75
+ icon: 'h-5 w-5',
76
+ gap: orientation === 'vertical' ? 'gap-3' : 'gap-8',
77
+ minWidth: orientation === 'vertical' ? 'min-w-[250px]' : '',
78
+ spacing: orientation === 'vertical' ? 'mt-8' : 'mt-8',
79
+ },
80
+ };
81
+
82
+ return (
83
+ <div className={`w-full ${orientation === 'vertical' ? `flex ${sizeClasses[size].gap}` : ''}`}>
84
+ {/* Tab Headers */}
85
+ <div
86
+ className={`
87
+ flex ${orientation === 'vertical' ? 'flex-col' : ''}
88
+ ${variant === 'underline'
89
+ ? orientation === 'vertical'
90
+ ? `border-r border-paper-200 ${sizeClasses[size].gap} pr-6`
91
+ : `border-b border-paper-200 ${sizeClasses[size].gap}`
92
+ : `${sizeClasses[size].gap} p-1 bg-paper-50 rounded-lg`
93
+ }
94
+ ${sizeClasses[size].minWidth}
95
+ `}
96
+ role="tablist"
97
+ >
98
+ {tabs.map((tab) => {
99
+ const isActive = activeTab === tab.id;
100
+
101
+ return (
102
+ <button
103
+ key={tab.id}
104
+ role="tab"
105
+ aria-selected={isActive}
106
+ aria-controls={`panel-${tab.id}`}
107
+ disabled={tab.disabled}
108
+ onClick={() => !tab.disabled && handleTabChange(tab.id)}
109
+ className={`
110
+ flex items-center gap-2 ${sizeClasses[size].padding} ${sizeClasses[size].text} font-medium transition-all duration-200
111
+ ${orientation === 'vertical' ? 'w-full justify-start' : ''}
112
+ ${
113
+ variant === 'underline'
114
+ ? isActive
115
+ ? orientation === 'vertical'
116
+ ? 'text-accent-900 border-r-2 border-accent-500 -mr-[1px]'
117
+ : 'text-accent-900 border-b-2 border-accent-500 -mb-[1px]'
118
+ : orientation === 'vertical'
119
+ ? 'text-ink-600 hover:text-ink-900 border-r-2 border-transparent'
120
+ : 'text-ink-600 hover:text-ink-900 border-b-2 border-transparent'
121
+ : isActive
122
+ ? 'bg-white text-accent-900 rounded-md shadow-xs'
123
+ : 'text-ink-600 hover:text-ink-900 hover:bg-white/50 rounded-md'
124
+ }
125
+ ${tab.disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
126
+ `}
127
+ >
128
+ {tab.icon && <span className={`flex-shrink-0 ${sizeClasses[size].icon}`}>{tab.icon}</span>}
129
+ <span>{tab.label}</span>
130
+ </button>
131
+ );
132
+ })}
133
+ </div>
134
+
135
+ {/* Tab Content */}
136
+ <div className={`${orientation === 'vertical' ? 'flex-1' : sizeClasses[size].spacing}`}>
137
+ {tabs.map((tab) => (
138
+ <div
139
+ key={tab.id}
140
+ id={`panel-${tab.id}`}
141
+ role="tabpanel"
142
+ aria-labelledby={tab.id}
143
+ hidden={activeTab !== tab.id}
144
+ className={activeTab === tab.id ? 'animate-fade-in' : ''}
145
+ >
146
+ {activeTab === tab.id && tab.content}
147
+ </div>
148
+ ))}
149
+ </div>
150
+ </div>
151
+ );
152
+ }
@@ -309,6 +309,8 @@ export type {
309
309
  // Display Components
310
310
  export { default as CurrencyDisplay } from './CurrencyDisplay';
311
311
  export type { CurrencyDisplayProps } from './CurrencyDisplay';
312
+ export { default as CurrencyInput } from './CurrencyInput';
313
+ export type { CurrencyInputProps } from './CurrencyInput';
312
314
 
313
315
  export { default as DateDisplay } from './DateDisplay';
314
316
  export type { DateDisplayProps } from './DateDisplay';
@@ -409,6 +411,9 @@ export {
409
411
  } from '../utils/tableEnhancements';
410
412
  export type { ColumnResize, ColumnOrder } from '../utils/tableEnhancements';
411
413
 
414
+ export { exportToExcel, exportDataTableToExcel, createMultiSheetExcel } from '../utils/excelExport';
415
+ export type { ExcelColumn, ExportToExcelOptions, DataTableExportOptions, MultiSheetExcelOptions } from '../utils/excelExport';
416
+
412
417
  // Hooks
413
418
  export { useColumnResize, useColumnReorder } from '../hooks/useTableEnhancements';
414
419
  export type { UseColumnResizeOptions, UseColumnReorderOptions } from '../hooks/useTableEnhancements';
@@ -172,16 +172,53 @@
172
172
  @apply text-error-600;
173
173
  }
174
174
 
175
- /* Notebook Page Container - Creates bounded page effect */
175
+ /* Notebook Page Container - Creates bounded page effect
176
+ NOTE: This class is deprecated - Page component now handles responsive layout via props
177
+ Keeping for backward compatibility only */
176
178
  .notebook-page {
177
179
  @apply bg-white bg-subtle-grain rounded-sm shadow-lg border-l-4 border-paper-300;
178
180
  max-width: 1400px;
179
- margin: 1rem auto 1rem 1rem; /* Top margin, auto right for anchoring, bottom margin, small left margin */
180
- padding: 3rem 4rem 3rem 5rem; /* Extra left padding for binding margin */
181
- min-height: calc(100vh - 12rem);
181
+ /* Responsive margins - fixed left/top, responsive right/bottom */
182
+ margin-top: 1rem;
183
+ margin-left: 1rem;
184
+ margin-right: 1rem;
185
+ margin-bottom: 1rem;
186
+ /* Responsive padding - fixed left/top, responsive right/bottom */
187
+ padding-top: 3rem;
188
+ padding-left: 5rem; /* Extra left padding for binding margin */
189
+ padding-right: 1rem;
190
+ padding-bottom: 1rem;
191
+ min-height: calc(100vh - 2rem);
182
192
  position: relative;
183
193
  }
184
194
 
195
+ /* Responsive padding/margin increases on larger screens */
196
+ @media (min-width: 640px) {
197
+ .notebook-page {
198
+ margin-right: 1.5rem;
199
+ margin-bottom: 2rem;
200
+ padding-right: 2rem;
201
+ padding-bottom: 2rem;
202
+ }
203
+ }
204
+
205
+ @media (min-width: 768px) {
206
+ .notebook-page {
207
+ margin-right: 2rem;
208
+ margin-bottom: 2rem;
209
+ padding-right: 3rem;
210
+ padding-bottom: 3rem;
211
+ }
212
+ }
213
+
214
+ @media (min-width: 1024px) {
215
+ .notebook-page {
216
+ margin-right: auto; /* Center on large screens */
217
+ padding-right: 4rem;
218
+ padding-bottom: 4rem;
219
+ }
220
+ }
221
+
185
222
  /* Notebook binding effect on sidebar */
186
223
  .notebook-binding {
187
224
  position: relative;