@papernote/ui 1.10.19 → 1.10.21

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/styles.css CHANGED
@@ -3782,6 +3782,11 @@ input:checked + .slider:before{
3782
3782
  background-color: rgb(217 119 6 / var(--tw-bg-opacity, 1));
3783
3783
  }
3784
3784
 
3785
+ .bg-warning-700{
3786
+ --tw-bg-opacity: 1;
3787
+ background-color: rgb(180 83 9 / var(--tw-bg-opacity, 1));
3788
+ }
3789
+
3785
3790
  .bg-white{
3786
3791
  --tw-bg-opacity: 1;
3787
3792
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
@@ -4418,6 +4423,11 @@ input:checked + .slider:before{
4418
4423
  font-style: italic;
4419
4424
  }
4420
4425
 
4426
+ .tabular-nums{
4427
+ --tw-numeric-spacing: tabular-nums;
4428
+ font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction);
4429
+ }
4430
+
4421
4431
  .leading-5{
4422
4432
  line-height: 1.25rem;
4423
4433
  }
@@ -5687,6 +5697,11 @@ input:checked + .slider:before{
5687
5697
  background-color: rgb(180 83 9 / var(--tw-bg-opacity, 1));
5688
5698
  }
5689
5699
 
5700
+ .hover\:bg-warning-800:hover{
5701
+ --tw-bg-opacity: 1;
5702
+ background-color: rgb(146 64 14 / var(--tw-bg-opacity, 1));
5703
+ }
5704
+
5690
5705
  .hover\:bg-white:hover{
5691
5706
  --tw-bg-opacity: 1;
5692
5707
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papernote/ui",
3
- "version": "1.10.19",
3
+ "version": "1.10.21",
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",
@@ -13,7 +13,7 @@ const meta = {
13
13
  Alert banner for displaying important messages with contextual styling and actions.
14
14
 
15
15
  ## Features
16
- - **Variants**: info, success, warning, error with semantic colors
16
+ - **Variants**: info, success, warning, caution, error with semantic colors
17
17
  - **Icons**: Automatic variant-specific icons
18
18
  - **Title & Message**: Optional title with message content
19
19
  - **Dismissible**: Optional close button
@@ -45,10 +45,10 @@ import { Alert } from 'notebook-ui';
45
45
  argTypes: {
46
46
  variant: {
47
47
  control: 'select',
48
- options: ['info', 'success', 'warning', 'error'],
48
+ options: ['info', 'success', 'warning', 'caution', 'error'],
49
49
  description: 'Alert variant with semantic colors and icons',
50
50
  table: {
51
- type: { summary: 'info | success | warning | error' },
51
+ type: { summary: 'info | success | warning | caution | error' },
52
52
  defaultValue: { summary: 'info' },
53
53
  },
54
54
  },
@@ -114,6 +114,18 @@ export const Warning: Story = {
114
114
  },
115
115
  };
116
116
 
117
+ /**
118
+ * Caution variant for informational states that need context but aren't alarming.
119
+ * Use for demo/sandbox modes, wash sale notices, or exploratory features.
120
+ */
121
+ export const Caution: Story = {
122
+ args: {
123
+ variant: 'caution',
124
+ title: 'Demo Mode',
125
+ children: 'You are in demo mode. Changes will not be saved to the database.',
126
+ },
127
+ };
128
+
117
129
  export const Error: Story = {
118
130
  args: {
119
131
  variant: 'error',
@@ -170,10 +182,11 @@ export const LongMessage: Story = {
170
182
  export const AllVariants: Story = {
171
183
  render: () => (
172
184
  <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
173
- <Alert variant="info" title="Information" message="This is an informational message." />
174
- <Alert variant="success" title="Success" message="Operation completed successfully." />
175
- <Alert variant="warning" title="Warning" message="Please review before proceeding." />
176
- <Alert variant="error" title="Error" message="Something went wrong." />
185
+ <Alert variant="info" title="Information">This is an informational message.</Alert>
186
+ <Alert variant="success" title="Success">Operation completed successfully.</Alert>
187
+ <Alert variant="warning" title="Warning">Please review before proceeding.</Alert>
188
+ <Alert variant="caution" title="Demo Mode">You are exploring in sandbox mode.</Alert>
189
+ <Alert variant="error" title="Error">Something went wrong.</Alert>
177
190
  </div>
178
191
  ),
179
192
  };
@@ -8,7 +8,7 @@ export interface AlertAction {
8
8
  }
9
9
 
10
10
  export interface AlertProps {
11
- variant?: 'success' | 'error' | 'warning' | 'info';
11
+ variant?: 'success' | 'error' | 'warning' | 'caution' | 'info';
12
12
  title?: string;
13
13
  children: React.ReactNode;
14
14
  onClose?: () => void;
@@ -38,6 +38,10 @@ export default function Alert({
38
38
  container: 'bg-warning-50 border-warning-200 text-warning-900',
39
39
  icon: <AlertTriangle className="h-5 w-5 text-warning-600" />,
40
40
  },
41
+ caution: {
42
+ container: 'bg-warning-100 border-warning-200 text-warning-800',
43
+ icon: <Info className="h-5 w-5 text-warning-700" />,
44
+ },
41
45
  info: {
42
46
  container: 'bg-primary-50 border-primary-200 text-primary-900',
43
47
  icon: <Info className="h-5 w-5 text-primary-600" />,
@@ -54,6 +58,7 @@ export default function Alert({
54
58
  success: 'bg-success-600 text-white hover:bg-success-700',
55
59
  error: 'bg-error-600 text-white hover:bg-error-700',
56
60
  warning: 'bg-warning-600 text-white hover:bg-warning-700',
61
+ caution: 'bg-warning-700 text-white hover:bg-warning-800',
57
62
  info: 'bg-primary-600 text-white hover:bg-primary-700',
58
63
  };
59
64
  return `${base} ${variantClasses[variant]}`;
@@ -64,6 +69,7 @@ export default function Alert({
64
69
  success: 'bg-white border border-success-300 text-success-700 hover:bg-success-50',
65
70
  error: 'bg-white border border-error-300 text-error-700 hover:bg-error-50',
66
71
  warning: 'bg-white border border-warning-300 text-warning-700 hover:bg-warning-50',
72
+ caution: 'bg-white border border-warning-300 text-warning-800 hover:bg-warning-50',
67
73
  info: 'bg-white border border-primary-300 text-primary-700 hover:bg-primary-50',
68
74
  };
69
75
  return `${base} ${variantClasses[variant]}`;
@@ -14,7 +14,7 @@ const meta = {
14
14
  Badge component for status indicators, labels, and tags with optional icons and removal.
15
15
 
16
16
  ## Features
17
- - **Variants**: success, warning, error, info, neutral with semantic colors
17
+ - **Variants**: success, warning, caution, error, info, neutral with semantic colors
18
18
  - **Sizes**: sm, md, lg
19
19
  - **Icons**: Display icons alongside text
20
20
  - **Removable**: Optional X button for dismissible badges
@@ -44,10 +44,10 @@ import { Check } from 'lucide-react';
44
44
  argTypes: {
45
45
  variant: {
46
46
  control: 'select',
47
- options: ['success', 'warning', 'error', 'info', 'neutral'],
47
+ options: ['success', 'warning', 'caution', 'error', 'info', 'neutral'],
48
48
  description: 'Badge variant with semantic colors',
49
49
  table: {
50
- type: { summary: 'success | warning | error | info | neutral' },
50
+ type: { summary: 'success | warning | caution | error | info | neutral' },
51
51
  defaultValue: { summary: 'neutral' },
52
52
  },
53
53
  },
@@ -113,6 +113,17 @@ export const Warning: Story = {
113
113
  },
114
114
  };
115
115
 
116
+ /**
117
+ * Caution variant for informational states that need context but aren't alarming.
118
+ * Use for demo/sandbox modes, wash sale notices, or exploratory features.
119
+ */
120
+ export const Caution: Story = {
121
+ args: {
122
+ variant: 'caution',
123
+ children: 'Demo Mode',
124
+ },
125
+ };
126
+
116
127
  export const Error: Story = {
117
128
  args: {
118
129
  variant: 'error',
@@ -199,10 +210,10 @@ export const DotWithText: Story = {
199
210
  export const AllVariants: Story = {
200
211
  render: () => (
201
212
  <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
202
- <Badge variant="default">Default</Badge>
203
- <Badge variant="primary">Primary</Badge>
213
+ <Badge variant="neutral">Neutral</Badge>
204
214
  <Badge variant="success">Success</Badge>
205
215
  <Badge variant="warning">Warning</Badge>
216
+ <Badge variant="caution">Caution</Badge>
206
217
  <Badge variant="error">Error</Badge>
207
218
  <Badge variant="info">Info</Badge>
208
219
  </div>
@@ -224,10 +235,10 @@ export const StatusBadges: Story = {
224
235
  <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
225
236
  <Badge variant="success">Active</Badge>
226
237
  <Badge variant="warning">Pending</Badge>
238
+ <Badge variant="caution">Demo</Badge>
227
239
  <Badge variant="error">Inactive</Badge>
228
240
  <Badge variant="info">Draft</Badge>
229
- <Badge variant="primary">Published</Badge>
230
- <Badge variant="default">Archived</Badge>
241
+ <Badge variant="neutral">Archived</Badge>
231
242
  </div>
232
243
  ),
233
244
  };
@@ -3,7 +3,7 @@ import { X } from 'lucide-react';
3
3
 
4
4
  export interface BadgeProps {
5
5
  children?: React.ReactNode;
6
- variant?: 'success' | 'warning' | 'error' | 'info' | 'neutral';
6
+ variant?: 'success' | 'warning' | 'error' | 'caution' | 'info' | 'neutral';
7
7
  size?: 'sm' | 'md' | 'lg';
8
8
  icon?: React.ReactNode;
9
9
  onRemove?: () => void;
@@ -37,6 +37,7 @@ export default function Badge({
37
37
  success: 'bg-success-50 text-success-700 border-success-200',
38
38
  warning: 'bg-warning-50 text-warning-700 border-warning-200',
39
39
  error: 'bg-error-50 text-error-700 border-error-200',
40
+ caution: 'bg-warning-100 text-warning-800 border-warning-200',
40
41
  info: 'bg-primary-50 text-primary-700 border-primary-200',
41
42
  neutral: 'bg-paper-100 text-ink-700 border-paper-300',
42
43
  };
@@ -45,6 +46,7 @@ export default function Badge({
45
46
  success: 'bg-success-500',
46
47
  warning: 'bg-warning-500',
47
48
  error: 'bg-error-500',
49
+ caution: 'bg-warning-700',
48
50
  info: 'bg-primary-500',
49
51
  neutral: 'bg-ink-400',
50
52
  };
@@ -0,0 +1,188 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { PivotTable } from './PivotTable';
3
+
4
+ const meta: Meta<typeof PivotTable> = {
5
+ title: 'Data Display/PivotTable',
6
+ component: PivotTable,
7
+ parameters: {
8
+ layout: 'padded',
9
+ docs: {
10
+ description: {
11
+ component:
12
+ 'PivotTable transforms flat data into a cross-tabulation table with row labels, column headers, and aggregated values. Ideal for financial reports, sales summaries, and time-based data analysis.',
13
+ },
14
+ },
15
+ },
16
+ tags: ['autodocs'],
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof PivotTable>;
21
+
22
+ // Sample commission data by principal and month
23
+ const commissionData = [
24
+ { PrincipalName: 'Allwire', CommissionFiscalMonth: 1, MonthlyCommission: 1250.5 },
25
+ { PrincipalName: 'Allwire', CommissionFiscalMonth: 2, MonthlyCommission: 1875.25 },
26
+ { PrincipalName: 'Allwire', CommissionFiscalMonth: 3, MonthlyCommission: 2100.0 },
27
+ { PrincipalName: 'Allwire', CommissionFiscalMonth: 4, MonthlyCommission: 1950.75 },
28
+ { PrincipalName: 'AMSC', CommissionFiscalMonth: 1, MonthlyCommission: 3200.0 },
29
+ { PrincipalName: 'AMSC', CommissionFiscalMonth: 2, MonthlyCommission: 2850.5 },
30
+ { PrincipalName: 'AMSC', CommissionFiscalMonth: 3, MonthlyCommission: 3100.25 },
31
+ { PrincipalName: 'AMSC', CommissionFiscalMonth: 4, MonthlyCommission: 3450.0 },
32
+ { PrincipalName: 'Buckingham', CommissionFiscalMonth: 1, MonthlyCommission: 850.0 },
33
+ { PrincipalName: 'Buckingham', CommissionFiscalMonth: 2, MonthlyCommission: 925.75 },
34
+ { PrincipalName: 'Buckingham', CommissionFiscalMonth: 3, MonthlyCommission: 1100.5 },
35
+ { PrincipalName: 'Buckingham', CommissionFiscalMonth: 4, MonthlyCommission: 975.25 },
36
+ { PrincipalName: 'Delta Corp', CommissionFiscalMonth: 1, MonthlyCommission: 5500.0 },
37
+ { PrincipalName: 'Delta Corp', CommissionFiscalMonth: 2, MonthlyCommission: 4800.25 },
38
+ { PrincipalName: 'Delta Corp', CommissionFiscalMonth: 3, MonthlyCommission: 5200.5 },
39
+ { PrincipalName: 'Delta Corp', CommissionFiscalMonth: 4, MonthlyCommission: 5750.75 },
40
+ ];
41
+
42
+ const monthLabels: Record<number, string> = {
43
+ 1: 'January',
44
+ 2: 'February',
45
+ 3: 'March',
46
+ 4: 'April',
47
+ 5: 'May',
48
+ 6: 'June',
49
+ 7: 'July',
50
+ 8: 'August',
51
+ 9: 'September',
52
+ 10: 'October',
53
+ 11: 'November',
54
+ 12: 'December',
55
+ };
56
+
57
+ /**
58
+ * Default pivot table showing commission by principal and month
59
+ */
60
+ export const Default: Story = {
61
+ args: {
62
+ data: commissionData,
63
+ rowField: 'PrincipalName',
64
+ columnField: 'CommissionFiscalMonth',
65
+ valueField: 'MonthlyCommission',
66
+ columnLabels: monthLabels,
67
+ rowLabel: 'Principal',
68
+ showRowTotals: true,
69
+ showRowAverages: true,
70
+ showColumnTotals: true,
71
+ },
72
+ };
73
+
74
+ /**
75
+ * Without averages column
76
+ */
77
+ export const WithoutAverages: Story = {
78
+ args: {
79
+ ...Default.args,
80
+ showRowAverages: false,
81
+ },
82
+ };
83
+
84
+ /**
85
+ * Without totals row
86
+ */
87
+ export const WithoutColumnTotals: Story = {
88
+ args: {
89
+ ...Default.args,
90
+ showColumnTotals: false,
91
+ },
92
+ };
93
+
94
+ /**
95
+ * Minimal - just the pivot data
96
+ */
97
+ export const Minimal: Story = {
98
+ args: {
99
+ ...Default.args,
100
+ showRowTotals: false,
101
+ showRowAverages: false,
102
+ showColumnTotals: false,
103
+ },
104
+ };
105
+
106
+ // Sales data by region and quarter
107
+ const salesData = [
108
+ { Region: 'North', Quarter: 'Q1', Sales: 125000 },
109
+ { Region: 'North', Quarter: 'Q2', Sales: 145000 },
110
+ { Region: 'North', Quarter: 'Q3', Sales: 132000 },
111
+ { Region: 'North', Quarter: 'Q4', Sales: 178000 },
112
+ { Region: 'South', Quarter: 'Q1', Sales: 98000 },
113
+ { Region: 'South', Quarter: 'Q2', Sales: 112000 },
114
+ { Region: 'South', Quarter: 'Q3', Sales: 105000 },
115
+ { Region: 'South', Quarter: 'Q4', Sales: 142000 },
116
+ { Region: 'East', Quarter: 'Q1', Sales: 210000 },
117
+ { Region: 'East', Quarter: 'Q2', Sales: 195000 },
118
+ { Region: 'East', Quarter: 'Q3', Sales: 225000 },
119
+ { Region: 'East', Quarter: 'Q4', Sales: 248000 },
120
+ { Region: 'West', Quarter: 'Q1', Sales: 175000 },
121
+ { Region: 'West', Quarter: 'Q2', Sales: 168000 },
122
+ { Region: 'West', Quarter: 'Q3', Sales: 192000 },
123
+ { Region: 'West', Quarter: 'Q4', Sales: 215000 },
124
+ ];
125
+
126
+ /**
127
+ * Sales by region and quarter
128
+ */
129
+ export const SalesByRegion: Story = {
130
+ args: {
131
+ data: salesData,
132
+ rowField: 'Region',
133
+ columnField: 'Quarter',
134
+ valueField: 'Sales',
135
+ rowLabel: 'Region',
136
+ showRowTotals: true,
137
+ showRowAverages: true,
138
+ showColumnTotals: true,
139
+ },
140
+ };
141
+
142
+ /**
143
+ * Custom number formatter (no currency symbol)
144
+ */
145
+ export const CustomFormatter: Story = {
146
+ args: {
147
+ ...Default.args,
148
+ formatValue: (value: number | null) => {
149
+ if (value === null) return '-';
150
+ return new Intl.NumberFormat('en-US', {
151
+ minimumFractionDigits: 0,
152
+ maximumFractionDigits: 0,
153
+ }).format(value);
154
+ },
155
+ },
156
+ };
157
+
158
+ // Sparse data with missing values
159
+ const sparseData = [
160
+ { Product: 'Widget A', Month: 1, Units: 150 },
161
+ { Product: 'Widget A', Month: 3, Units: 200 },
162
+ { Product: 'Widget A', Month: 4, Units: 175 },
163
+ { Product: 'Widget B', Month: 1, Units: 300 },
164
+ { Product: 'Widget B', Month: 2, Units: 280 },
165
+ { Product: 'Widget B', Month: 4, Units: 320 },
166
+ { Product: 'Widget C', Month: 2, Units: 90 },
167
+ { Product: 'Widget C', Month: 3, Units: 110 },
168
+ ];
169
+
170
+ /**
171
+ * Sparse data with empty cells
172
+ */
173
+ export const SparseData: Story = {
174
+ args: {
175
+ data: sparseData,
176
+ rowField: 'Product',
177
+ columnField: 'Month',
178
+ valueField: 'Units',
179
+ columnLabels: monthLabels,
180
+ rowLabel: 'Product',
181
+ showRowTotals: true,
182
+ showColumnTotals: true,
183
+ formatValue: (value: number | null) => {
184
+ if (value === null) return '-';
185
+ return value.toLocaleString();
186
+ },
187
+ },
188
+ };