@papernote/ui 1.3.1 → 1.6.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 (108) hide show
  1. package/dist/components/ActionBar.d.ts +112 -0
  2. package/dist/components/ActionBar.d.ts.map +1 -0
  3. package/dist/components/BottomNavigation.d.ts +98 -0
  4. package/dist/components/BottomNavigation.d.ts.map +1 -0
  5. package/dist/components/Checkbox.d.ts +2 -0
  6. package/dist/components/Checkbox.d.ts.map +1 -1
  7. package/dist/components/CheckboxList.d.ts +81 -0
  8. package/dist/components/CheckboxList.d.ts.map +1 -0
  9. package/dist/components/Chip.d.ts +92 -1
  10. package/dist/components/Chip.d.ts.map +1 -1
  11. package/dist/components/ConfirmDialog.d.ts +43 -1
  12. package/dist/components/ConfirmDialog.d.ts.map +1 -1
  13. package/dist/components/DataTable.d.ts +10 -1
  14. package/dist/components/DataTable.d.ts.map +1 -1
  15. package/dist/components/DataTableCardView.d.ts +99 -0
  16. package/dist/components/DataTableCardView.d.ts.map +1 -0
  17. package/dist/components/ExpandablePanel.d.ts +142 -0
  18. package/dist/components/ExpandablePanel.d.ts.map +1 -0
  19. package/dist/components/FloatingActionButton.d.ts +98 -0
  20. package/dist/components/FloatingActionButton.d.ts.map +1 -0
  21. package/dist/components/Input.d.ts +45 -1
  22. package/dist/components/Input.d.ts.map +1 -1
  23. package/dist/components/MobileHeader.d.ts +98 -0
  24. package/dist/components/MobileHeader.d.ts.map +1 -0
  25. package/dist/components/MobileLayout.d.ts +121 -0
  26. package/dist/components/MobileLayout.d.ts.map +1 -0
  27. package/dist/components/Modal.d.ts +78 -1
  28. package/dist/components/Modal.d.ts.map +1 -1
  29. package/dist/components/PageHeader.d.ts +86 -0
  30. package/dist/components/PageHeader.d.ts.map +1 -0
  31. package/dist/components/PullToRefresh.d.ts +87 -0
  32. package/dist/components/PullToRefresh.d.ts.map +1 -0
  33. package/dist/components/QueryTransparency.d.ts +1 -1
  34. package/dist/components/QueryTransparency.d.ts.map +1 -1
  35. package/dist/components/SearchableList.d.ts +83 -0
  36. package/dist/components/SearchableList.d.ts.map +1 -0
  37. package/dist/components/Select.d.ts +16 -2
  38. package/dist/components/Select.d.ts.map +1 -1
  39. package/dist/components/Sidebar.d.ts +40 -1
  40. package/dist/components/Sidebar.d.ts.map +1 -1
  41. package/dist/components/SwipeActions.d.ts +93 -0
  42. package/dist/components/SwipeActions.d.ts.map +1 -0
  43. package/dist/components/Switch.d.ts +1 -0
  44. package/dist/components/Switch.d.ts.map +1 -1
  45. package/dist/components/Textarea.d.ts +13 -0
  46. package/dist/components/Textarea.d.ts.map +1 -1
  47. package/dist/components/index.d.ts +31 -3
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/context/MobileContext.d.ts +168 -0
  50. package/dist/context/MobileContext.d.ts.map +1 -0
  51. package/dist/hooks/useResponsive.d.ts +158 -0
  52. package/dist/hooks/useResponsive.d.ts.map +1 -0
  53. package/dist/index.d.ts +1871 -51
  54. package/dist/index.esm.js +3025 -196
  55. package/dist/index.esm.js.map +1 -1
  56. package/dist/index.js +3063 -194
  57. package/dist/index.js.map +1 -1
  58. package/dist/styles.css +434 -1
  59. package/dist/types/index.d.ts +2 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/src/components/ActionBar.stories.tsx +246 -0
  63. package/src/components/ActionBar.tsx +242 -0
  64. package/src/components/BottomNavigation.stories.tsx +142 -0
  65. package/src/components/BottomNavigation.tsx +225 -0
  66. package/src/components/Checkbox.stories.tsx +162 -0
  67. package/src/components/Checkbox.tsx +22 -6
  68. package/src/components/CheckboxList.stories.tsx +311 -0
  69. package/src/components/CheckboxList.tsx +433 -0
  70. package/src/components/Chip.stories.tsx +389 -0
  71. package/src/components/Chip.tsx +182 -3
  72. package/src/components/ConfirmDialog.tsx +56 -4
  73. package/src/components/DataTable.tsx +60 -1
  74. package/src/components/DataTableCardView.stories.tsx +307 -0
  75. package/src/components/DataTableCardView.tsx +419 -0
  76. package/src/components/ExpandablePanel.stories.tsx +620 -0
  77. package/src/components/ExpandablePanel.tsx +383 -0
  78. package/src/components/FloatingActionButton.stories.tsx +197 -0
  79. package/src/components/FloatingActionButton.tsx +301 -0
  80. package/src/components/Grid.stories.tsx +16 -16
  81. package/src/components/Input.stories.tsx +214 -0
  82. package/src/components/Input.tsx +81 -4
  83. package/src/components/MobileHeader.stories.tsx +205 -0
  84. package/src/components/MobileHeader.tsx +233 -0
  85. package/src/components/MobileLayout.stories.tsx +338 -0
  86. package/src/components/MobileLayout.tsx +313 -0
  87. package/src/components/Modal.stories.tsx +388 -0
  88. package/src/components/Modal.tsx +122 -4
  89. package/src/components/PageHeader.stories.tsx +198 -0
  90. package/src/components/PageHeader.tsx +217 -0
  91. package/src/components/PullToRefresh.stories.tsx +321 -0
  92. package/src/components/PullToRefresh.tsx +294 -0
  93. package/src/components/QueryTransparency.tsx +1 -1
  94. package/src/components/SearchableList.stories.tsx +437 -0
  95. package/src/components/SearchableList.tsx +326 -0
  96. package/src/components/Select.stories.tsx +190 -0
  97. package/src/components/Select.tsx +353 -137
  98. package/src/components/Sidebar.tsx +193 -10
  99. package/src/components/SwipeActions.stories.tsx +327 -0
  100. package/src/components/SwipeActions.tsx +387 -0
  101. package/src/components/Switch.stories.tsx +158 -0
  102. package/src/components/Switch.tsx +12 -3
  103. package/src/components/Textarea.tsx +31 -1
  104. package/src/components/index.ts +69 -3
  105. package/src/context/MobileContext.tsx +296 -0
  106. package/src/hooks/useResponsive.ts +360 -0
  107. package/src/types/index.ts +4 -0
  108. package/tailwind.config.js +56 -1
@@ -0,0 +1,198 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { MemoryRouter } from 'react-router-dom';
3
+ import PageHeader from './PageHeader';
4
+ import { Plus, Download, Filter, Settings, Share2, Trash2, Edit } from 'lucide-react';
5
+
6
+ const meta: Meta<typeof PageHeader> = {
7
+ title: 'Layout/PageHeader',
8
+ component: PageHeader,
9
+ decorators: [
10
+ (Story) => (
11
+ <MemoryRouter>
12
+ <Story />
13
+ </MemoryRouter>
14
+ ),
15
+ ],
16
+ parameters: {
17
+ layout: 'fullscreen',
18
+ docs: {
19
+ description: {
20
+ component: 'A standard page header component with title, breadcrumbs, and action buttons. Use this at the top of pages to provide consistent navigation and actions.',
21
+ },
22
+ },
23
+ },
24
+ argTypes: {
25
+ // Disable controls for ReactNode props that can't be edited via text
26
+ rightContent: { control: false },
27
+ belowTitle: { control: false },
28
+ actions: { control: false },
29
+ breadcrumbs: { control: false },
30
+ backButton: { control: false },
31
+ },
32
+ tags: ['autodocs'],
33
+ };
34
+
35
+ export default meta;
36
+ type Story = StoryObj<typeof PageHeader>;
37
+
38
+ export const Default: Story = {
39
+ args: {
40
+ title: 'Products',
41
+ subtitle: 'Manage your product catalog',
42
+ },
43
+ };
44
+
45
+ export const WithBreadcrumbs: Story = {
46
+ args: {
47
+ title: 'Products',
48
+ subtitle: 'Manage your product catalog',
49
+ breadcrumbs: [
50
+ { label: 'Inventory' },
51
+ { label: 'Products' },
52
+ ],
53
+ },
54
+ };
55
+
56
+ export const WithActions: Story = {
57
+ args: {
58
+ title: 'Products',
59
+ subtitle: 'Manage your product catalog',
60
+ breadcrumbs: [
61
+ { label: 'Inventory' },
62
+ { label: 'Products' },
63
+ ],
64
+ actions: [
65
+ { id: 'filter', label: 'Filter', icon: <Filter className="h-4 w-4" />, onClick: () => alert('Filter clicked'), variant: 'ghost' },
66
+ { id: 'export', label: 'Export', icon: <Download className="h-4 w-4" />, onClick: () => alert('Export clicked'), variant: 'secondary' },
67
+ { id: 'add', label: 'Add Product', icon: <Plus className="h-4 w-4" />, onClick: () => alert('Add clicked'), variant: 'primary' },
68
+ ],
69
+ },
70
+ };
71
+
72
+ export const WithBackButton: Story = {
73
+ args: {
74
+ title: 'Edit Product',
75
+ subtitle: 'Update product details',
76
+ backButton: {
77
+ label: 'Back to Products',
78
+ onClick: () => alert('Back clicked'),
79
+ },
80
+ },
81
+ };
82
+
83
+ export const DetailPage: Story = {
84
+ args: {
85
+ title: 'Wireless Bluetooth Headphones',
86
+ subtitle: 'SKU: WBH-2024-001 • In Stock',
87
+ breadcrumbs: [
88
+ { label: 'Inventory' },
89
+ { label: 'Products', href: '/products' },
90
+ { label: 'Wireless Bluetooth Headphones' },
91
+ ],
92
+ actions: [
93
+ { id: 'share', label: 'Share', icon: <Share2 className="h-4 w-4" />, onClick: () => alert('Share'), variant: 'ghost' },
94
+ { id: 'edit', label: 'Edit', icon: <Edit className="h-4 w-4" />, onClick: () => alert('Edit'), variant: 'secondary' },
95
+ { id: 'delete', label: 'Delete', icon: <Trash2 className="h-4 w-4" />, onClick: () => alert('Delete'), variant: 'danger' },
96
+ ],
97
+ },
98
+ };
99
+
100
+ export const Sticky: Story = {
101
+ args: {
102
+ title: 'Products',
103
+ subtitle: 'Scroll down to see the sticky behavior',
104
+ breadcrumbs: [
105
+ { label: 'Inventory' },
106
+ { label: 'Products' },
107
+ ],
108
+ actions: [
109
+ { id: 'add', label: 'Add Product', icon: <Plus className="h-4 w-4" />, onClick: () => {}, variant: 'primary' },
110
+ ],
111
+ sticky: true,
112
+ },
113
+ decorators: [
114
+ (Story) => (
115
+ <div style={{ height: '400px', overflow: 'auto', border: '1px solid #e5e5e5', borderRadius: '8px' }}>
116
+ <Story />
117
+ <div className="p-6">
118
+ <p className="text-ink-600 font-medium">↓ Scroll down to see the sticky header behavior</p>
119
+ <div className="mt-4 space-y-4">
120
+ {Array.from({ length: 20 }).map((_, i) => (
121
+ <div key={i} className="p-4 bg-paper-100 rounded-lg">
122
+ Content block {i + 1}
123
+ </div>
124
+ ))}
125
+ </div>
126
+ </div>
127
+ </div>
128
+ ),
129
+ ],
130
+ };
131
+
132
+ export const WithLoadingAction: Story = {
133
+ args: {
134
+ title: 'Products',
135
+ actions: [
136
+ { id: 'export', label: 'Exporting...', icon: <Download className="h-4 w-4" />, onClick: () => {}, variant: 'secondary', loading: true },
137
+ { id: 'add', label: 'Add Product', icon: <Plus className="h-4 w-4" />, onClick: () => {}, variant: 'primary' },
138
+ ],
139
+ },
140
+ };
141
+
142
+ export const WithDisabledAction: Story = {
143
+ args: {
144
+ title: 'Products',
145
+ actions: [
146
+ { id: 'export', label: 'Export', icon: <Download className="h-4 w-4" />, onClick: () => {}, variant: 'secondary', disabled: true },
147
+ { id: 'add', label: 'Add Product', icon: <Plus className="h-4 w-4" />, onClick: () => {}, variant: 'primary' },
148
+ ],
149
+ },
150
+ };
151
+
152
+ export const SettingsPage: Story = {
153
+ args: {
154
+ title: 'Settings',
155
+ subtitle: 'Manage your application preferences',
156
+ breadcrumbs: [
157
+ { label: 'Settings' },
158
+ ],
159
+ actions: [
160
+ { id: 'reset', label: 'Reset to Defaults', onClick: () => alert('Reset'), variant: 'ghost' },
161
+ { id: 'save', label: 'Save Changes', onClick: () => alert('Save'), variant: 'primary' },
162
+ ],
163
+ },
164
+ };
165
+
166
+ export const DashboardPage: Story = {
167
+ args: {
168
+ title: 'Dashboard',
169
+ subtitle: 'Welcome back, John! Here\'s what\'s happening today.',
170
+ actions: [
171
+ { id: 'settings', label: 'Settings', icon: <Settings className="h-4 w-4" />, onClick: () => {}, variant: 'ghost' },
172
+ { id: 'export', label: 'Export Report', icon: <Download className="h-4 w-4" />, onClick: () => {}, variant: 'secondary' },
173
+ ],
174
+ },
175
+ };
176
+
177
+ export const TitleOnly: Story = {
178
+ args: {
179
+ title: 'Simple Page',
180
+ },
181
+ };
182
+
183
+ export const LongTitle: Story = {
184
+ args: {
185
+ title: 'This Is a Very Long Page Title That Might Need to Truncate on Smaller Screens',
186
+ subtitle: 'With an equally verbose subtitle that provides additional context about this page',
187
+ breadcrumbs: [
188
+ { label: 'Category' },
189
+ { label: 'Subcategory' },
190
+ { label: 'This Is a Very Long Page Title' },
191
+ ],
192
+ actions: [
193
+ { id: 'action1', label: 'Action 1', onClick: () => {}, variant: 'ghost' },
194
+ { id: 'action2', label: 'Action 2', onClick: () => {}, variant: 'secondary' },
195
+ { id: 'action3', label: 'Primary Action', onClick: () => {}, variant: 'primary' },
196
+ ],
197
+ },
198
+ };
@@ -0,0 +1,217 @@
1
+ import { ReactNode } from 'react';
2
+ import Breadcrumbs, { BreadcrumbItem } from './Breadcrumbs';
3
+
4
+ export interface PageHeaderAction {
5
+ /** Unique identifier for the action */
6
+ id: string;
7
+ /** Button label text */
8
+ label: string;
9
+ /** Icon to display (from lucide-react) */
10
+ icon?: ReactNode;
11
+ /** Click handler */
12
+ onClick: () => void;
13
+ /** Button variant */
14
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'outline';
15
+ /** Disabled state */
16
+ disabled?: boolean;
17
+ /** Loading state */
18
+ loading?: boolean;
19
+ /** Hide on mobile */
20
+ hideOnMobile?: boolean;
21
+ }
22
+
23
+ export interface PageHeaderProps {
24
+ /** Page title */
25
+ title: string;
26
+ /** Optional subtitle/description */
27
+ subtitle?: string;
28
+ /** Breadcrumb navigation items */
29
+ breadcrumbs?: BreadcrumbItem[];
30
+ /** Show home icon in breadcrumbs (default: true) */
31
+ showHomeBreadcrumb?: boolean;
32
+ /** Action buttons to display on the right */
33
+ actions?: PageHeaderAction[];
34
+ /** Custom content to render on the right (instead of actions) */
35
+ rightContent?: ReactNode;
36
+ /** Custom content to render below title */
37
+ belowTitle?: ReactNode;
38
+ /** Additional CSS classes */
39
+ className?: string;
40
+ /** Make header sticky at top */
41
+ sticky?: boolean;
42
+ /** Back button configuration */
43
+ backButton?: {
44
+ label?: string;
45
+ onClick: () => void;
46
+ };
47
+ }
48
+
49
+ /**
50
+ * PageHeader - Standard page header with title, breadcrumbs, and actions
51
+ *
52
+ * A consistent header component for pages that provides:
53
+ * - Page title and optional subtitle
54
+ * - Breadcrumb navigation
55
+ * - Action buttons (Create, Export, etc.)
56
+ * - Optional back button
57
+ * - Sticky positioning option
58
+ *
59
+ * @example Basic usage
60
+ * ```tsx
61
+ * <PageHeader
62
+ * title="Products"
63
+ * subtitle="Manage your product catalog"
64
+ * breadcrumbs={[{ label: 'Inventory' }, { label: 'Products' }]}
65
+ * actions={[
66
+ * { id: 'export', label: 'Export', icon: <Download />, onClick: handleExport, variant: 'ghost' },
67
+ * { id: 'add', label: 'Add Product', icon: <Plus />, onClick: handleAdd, variant: 'primary' },
68
+ * ]}
69
+ * />
70
+ * ```
71
+ *
72
+ * @example With back button
73
+ * ```tsx
74
+ * <PageHeader
75
+ * title="Edit Product"
76
+ * backButton={{ label: 'Back to Products', onClick: () => navigate('/products') }}
77
+ * />
78
+ * ```
79
+ *
80
+ * @example With custom right content
81
+ * ```tsx
82
+ * <PageHeader
83
+ * title="Dashboard"
84
+ * rightContent={<DateRangePicker value={range} onChange={setRange} />}
85
+ * />
86
+ * ```
87
+ */
88
+ export default function PageHeader({
89
+ title,
90
+ subtitle,
91
+ breadcrumbs,
92
+ showHomeBreadcrumb = true,
93
+ actions,
94
+ rightContent,
95
+ belowTitle,
96
+ className = '',
97
+ sticky = false,
98
+ backButton,
99
+ }: PageHeaderProps) {
100
+ const variantStyles: Record<string, string> = {
101
+ primary: 'bg-accent-500 text-white border-accent-500 hover:bg-accent-600 hover:shadow-sm',
102
+ secondary: 'bg-white text-ink-700 border-paper-300 hover:bg-paper-50 hover:border-paper-400 shadow-xs hover:shadow-sm',
103
+ ghost: 'bg-transparent text-ink-600 border-transparent hover:text-ink-800 hover:bg-paper-100',
104
+ danger: 'bg-error-500 text-white border-error-500 hover:bg-error-600 hover:shadow-sm',
105
+ outline: 'bg-transparent text-ink-700 border-paper-300 hover:bg-paper-50 hover:border-ink-400',
106
+ };
107
+
108
+ return (
109
+ <div
110
+ className={`
111
+ bg-white border-b border-paper-200
112
+ ${sticky ? 'sticky top-0 z-40' : ''}
113
+ ${className}
114
+ `}
115
+ >
116
+ <div className="px-6 py-4">
117
+ {/* Breadcrumbs */}
118
+ {breadcrumbs && breadcrumbs.length > 0 && (
119
+ <div className="mb-3">
120
+ <Breadcrumbs items={breadcrumbs} showHome={showHomeBreadcrumb} />
121
+ </div>
122
+ )}
123
+
124
+ {/* Back button */}
125
+ {backButton && (
126
+ <div className="mb-3">
127
+ <button
128
+ onClick={backButton.onClick}
129
+ className="inline-flex items-center gap-1.5 text-sm text-ink-500 hover:text-ink-700 transition-colors"
130
+ >
131
+ <svg
132
+ className="h-4 w-4"
133
+ fill="none"
134
+ stroke="currentColor"
135
+ viewBox="0 0 24 24"
136
+ >
137
+ <path
138
+ strokeLinecap="round"
139
+ strokeLinejoin="round"
140
+ strokeWidth={2}
141
+ d="M15 19l-7-7 7-7"
142
+ />
143
+ </svg>
144
+ <span>{backButton.label || 'Back'}</span>
145
+ </button>
146
+ </div>
147
+ )}
148
+
149
+ {/* Title row */}
150
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
151
+ {/* Title and subtitle */}
152
+ <div className="min-w-0 flex-1">
153
+ <h1 className="text-2xl font-bold text-ink-900 truncate">{title}</h1>
154
+ {subtitle && (
155
+ <p className="mt-1 text-sm text-ink-500">{subtitle}</p>
156
+ )}
157
+ </div>
158
+
159
+ {/* Actions or custom right content */}
160
+ {(actions || rightContent) && (
161
+ <div className="flex items-center gap-2 flex-shrink-0">
162
+ {rightContent}
163
+ {actions && actions.map((action) => (
164
+ <button
165
+ key={action.id}
166
+ onClick={action.onClick}
167
+ disabled={action.disabled || action.loading}
168
+ className={`
169
+ inline-flex items-center justify-center gap-2
170
+ px-4 py-2 text-sm font-medium rounded-lg border
171
+ transition-all duration-200
172
+ focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent-400
173
+ disabled:opacity-40 disabled:cursor-not-allowed
174
+ ${variantStyles[action.variant || 'secondary']}
175
+ ${action.hideOnMobile ? 'hidden sm:inline-flex' : ''}
176
+ `}
177
+ >
178
+ {action.loading ? (
179
+ <svg
180
+ className="h-4 w-4 animate-spin"
181
+ fill="none"
182
+ viewBox="0 0 24 24"
183
+ >
184
+ <circle
185
+ className="opacity-25"
186
+ cx="12"
187
+ cy="12"
188
+ r="10"
189
+ stroke="currentColor"
190
+ strokeWidth="4"
191
+ />
192
+ <path
193
+ className="opacity-75"
194
+ fill="currentColor"
195
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
196
+ />
197
+ </svg>
198
+ ) : action.icon ? (
199
+ <span className="h-4 w-4">{action.icon}</span>
200
+ ) : null}
201
+ <span>{action.label}</span>
202
+ </button>
203
+ ))}
204
+ </div>
205
+ )}
206
+ </div>
207
+
208
+ {/* Below title content */}
209
+ {belowTitle && (
210
+ <div className="mt-4">
211
+ {belowTitle}
212
+ </div>
213
+ )}
214
+ </div>
215
+ </div>
216
+ );
217
+ }