@jmruthers/pace-core 0.5.140 → 0.5.141

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 (156) hide show
  1. package/README.md +2 -2
  2. package/dist/{DataTable-JXFCA2BJ.js → DataTable-EGIN2NKK.js} +3 -3
  3. package/dist/{EventLogo-rFL_kRjk.d.ts → EventLogo-B3V3otev.d.ts} +307 -1
  4. package/dist/{chunk-BOOI7GK2.js → chunk-3R472UXR.js} +117 -1
  5. package/dist/chunk-3R472UXR.js.map +1 -0
  6. package/dist/{chunk-5JMOHWDI.js → chunk-ALUN6O3G.js} +492 -324
  7. package/dist/chunk-ALUN6O3G.js.map +1 -0
  8. package/dist/{chunk-6DXZ6V5Q.js → chunk-PZV3XZKJ.js} +2 -2
  9. package/dist/{chunk-TLT2ZR3L.js → chunk-WKTQM2IC.js} +2 -2
  10. package/dist/components.d.ts +3 -1
  11. package/dist/components.js +15 -3
  12. package/dist/components.js.map +1 -1
  13. package/dist/index.d.ts +4 -2
  14. package/dist/index.js +18 -4
  15. package/dist/index.js.map +1 -1
  16. package/dist/rbac/index.d.ts +94 -1
  17. package/dist/rbac/index.js +4 -2
  18. package/dist/utils.js +1 -1
  19. package/docs/api/README.md +2 -2
  20. package/docs/api/classes/ColumnFactory.md +1 -1
  21. package/docs/api/classes/ErrorBoundary.md +1 -1
  22. package/docs/api/classes/InvalidScopeError.md +1 -1
  23. package/docs/api/classes/MissingUserContextError.md +1 -1
  24. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  25. package/docs/api/classes/PermissionDeniedError.md +1 -1
  26. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  27. package/docs/api/classes/RBACAuditManager.md +1 -1
  28. package/docs/api/classes/RBACCache.md +1 -1
  29. package/docs/api/classes/RBACEngine.md +1 -1
  30. package/docs/api/classes/RBACError.md +1 -1
  31. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  32. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  33. package/docs/api/classes/StorageUtils.md +1 -1
  34. package/docs/api/enums/FileCategory.md +1 -1
  35. package/docs/api/interfaces/AggregateConfig.md +1 -1
  36. package/docs/api/interfaces/BadgeProps.md +1 -1
  37. package/docs/api/interfaces/ButtonProps.md +1 -1
  38. package/docs/api/interfaces/CalendarProps.md +40 -0
  39. package/docs/api/interfaces/CardProps.md +1 -1
  40. package/docs/api/interfaces/ColorPalette.md +1 -1
  41. package/docs/api/interfaces/ColorShade.md +1 -1
  42. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  43. package/docs/api/interfaces/DataRecord.md +1 -1
  44. package/docs/api/interfaces/DataTableAction.md +1 -1
  45. package/docs/api/interfaces/DataTableColumn.md +1 -1
  46. package/docs/api/interfaces/DataTableProps.md +1 -1
  47. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  48. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  49. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  50. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  51. package/docs/api/interfaces/EventLogoProps.md +1 -1
  52. package/docs/api/interfaces/ExportColumn.md +1 -1
  53. package/docs/api/interfaces/ExportOptions.md +1 -1
  54. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  55. package/docs/api/interfaces/FileMetadata.md +1 -1
  56. package/docs/api/interfaces/FileReference.md +1 -1
  57. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  58. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  59. package/docs/api/interfaces/FileUploadProps.md +1 -1
  60. package/docs/api/interfaces/FooterProps.md +1 -1
  61. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  62. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  63. package/docs/api/interfaces/InputProps.md +1 -1
  64. package/docs/api/interfaces/LabelProps.md +1 -1
  65. package/docs/api/interfaces/LoginFormProps.md +1 -1
  66. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  67. package/docs/api/interfaces/NavigationContextType.md +1 -1
  68. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  69. package/docs/api/interfaces/NavigationItem.md +1 -1
  70. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  71. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  72. package/docs/api/interfaces/Organisation.md +1 -1
  73. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  74. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  75. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  76. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  77. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  78. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  79. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  80. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  81. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  82. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  83. package/docs/api/interfaces/PaletteData.md +1 -1
  84. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  85. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  86. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  87. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  88. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  89. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  90. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  91. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  92. package/docs/api/interfaces/RBACConfig.md +1 -1
  93. package/docs/api/interfaces/RBACLogger.md +1 -1
  94. package/docs/api/interfaces/ResourcePermissions.md +155 -0
  95. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  96. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  97. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  98. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  99. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  100. package/docs/api/interfaces/RouteConfig.md +1 -1
  101. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  102. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  103. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  104. package/docs/api/interfaces/StorageConfig.md +1 -1
  105. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  106. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  107. package/docs/api/interfaces/StorageListOptions.md +1 -1
  108. package/docs/api/interfaces/StorageListResult.md +1 -1
  109. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  110. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  111. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  112. package/docs/api/interfaces/StyleImport.md +1 -1
  113. package/docs/api/interfaces/SwitchProps.md +1 -1
  114. package/docs/api/interfaces/TabsContentProps.md +9 -0
  115. package/docs/api/interfaces/TabsListProps.md +9 -0
  116. package/docs/api/interfaces/TabsProps.md +9 -0
  117. package/docs/api/interfaces/TabsTriggerProps.md +9 -0
  118. package/docs/api/interfaces/TextareaProps.md +53 -0
  119. package/docs/api/interfaces/ToastActionElement.md +1 -1
  120. package/docs/api/interfaces/ToastProps.md +1 -1
  121. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  122. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  123. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  124. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  125. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  126. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  128. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  129. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  130. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  131. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  132. package/docs/api/interfaces/UseResourcePermissionsOptions.md +34 -0
  133. package/docs/api/interfaces/UserEventAccess.md +1 -1
  134. package/docs/api/interfaces/UserMenuProps.md +1 -1
  135. package/docs/api/interfaces/UserProfile.md +1 -1
  136. package/docs/api/modules.md +289 -2
  137. package/package.json +3 -1
  138. package/src/components/Calendar/Calendar.test.tsx +338 -0
  139. package/src/components/Calendar/Calendar.tsx +192 -0
  140. package/src/components/Calendar/index.ts +10 -0
  141. package/src/components/Tabs/Tabs.test.tsx +439 -0
  142. package/src/components/Tabs/Tabs.tsx +202 -0
  143. package/src/components/Tabs/index.ts +10 -0
  144. package/src/components/Textarea/Textarea.test.tsx +269 -0
  145. package/src/components/Textarea/Textarea.tsx +133 -0
  146. package/src/components/Textarea/index.ts +10 -0
  147. package/src/components/index.ts +11 -0
  148. package/src/index.ts +11 -0
  149. package/src/rbac/hooks/index.ts +2 -0
  150. package/src/rbac/hooks/useResourcePermissions.test.ts +633 -0
  151. package/src/rbac/hooks/useResourcePermissions.ts +235 -0
  152. package/dist/chunk-5JMOHWDI.js.map +0 -1
  153. package/dist/chunk-BOOI7GK2.js.map +0 -1
  154. /package/dist/{DataTable-JXFCA2BJ.js.map → DataTable-EGIN2NKK.js.map} +0 -0
  155. /package/dist/{chunk-6DXZ6V5Q.js.map → chunk-PZV3XZKJ.js.map} +0 -0
  156. /package/dist/{chunk-TLT2ZR3L.js.map → chunk-WKTQM2IC.js.map} +0 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * @file Textarea Component Tests
3
+ * @description Comprehensive tests for Textarea component
4
+ */
5
+
6
+ import React from 'react';
7
+ import { screen } from '@testing-library/react';
8
+ import userEvent from '@testing-library/user-event';
9
+ import { describe, it, expect, vi } from 'vitest';
10
+ import { Textarea } from './Textarea';
11
+ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
12
+
13
+ describe('Textarea Component', () => {
14
+ // Basic rendering tests
15
+ describe('Rendering', () => {
16
+ it('renders with default props', () => {
17
+ renderWithProviders(<Textarea />);
18
+ const textarea = screen.getByRole('textbox');
19
+ expect(textarea).toBeInTheDocument();
20
+ expect(textarea.tagName).toBe('TEXTAREA');
21
+ });
22
+
23
+ it('renders with custom placeholder', () => {
24
+ renderWithProviders(<Textarea placeholder="Enter your message..." />);
25
+ expect(screen.getByPlaceholderText('Enter your message...')).toBeInTheDocument();
26
+ });
27
+
28
+ it('renders with custom className', () => {
29
+ renderWithProviders(<Textarea className="custom-class" />);
30
+ const textarea = screen.getByRole('textbox');
31
+ expect(textarea).toHaveClass('custom-class');
32
+ });
33
+
34
+ it('renders with value', () => {
35
+ renderWithProviders(<Textarea value="test value" />);
36
+ expect(screen.getByDisplayValue('test value')).toBeInTheDocument();
37
+ });
38
+
39
+ it('renders with defaultValue', () => {
40
+ renderWithProviders(<Textarea defaultValue="default value" />);
41
+ expect(screen.getByDisplayValue('default value')).toBeInTheDocument();
42
+ });
43
+
44
+ it('renders with rows attribute', () => {
45
+ renderWithProviders(<Textarea rows={5} />);
46
+ const textarea = screen.getByRole('textbox');
47
+ expect(textarea).toHaveAttribute('rows', '5');
48
+ });
49
+ });
50
+
51
+ // Variant tests
52
+ describe('Variants', () => {
53
+ it('renders with default variant', () => {
54
+ renderWithProviders(<Textarea variant="default" />);
55
+ const textarea = screen.getByRole('textbox');
56
+ expect(textarea).toHaveClass('border-input');
57
+ });
58
+
59
+ it('renders with destructive variant', () => {
60
+ renderWithProviders(<Textarea variant="destructive" />);
61
+ const textarea = screen.getByRole('textbox');
62
+ expect(textarea).toHaveClass('border-destructive', 'focus-visible:ring-destructive');
63
+ });
64
+
65
+ it('applies destructive styling when error is true', () => {
66
+ renderWithProviders(<Textarea error={true} />);
67
+ const textarea = screen.getByRole('textbox');
68
+ expect(textarea).toHaveClass('border-destructive', 'focus-visible:ring-destructive');
69
+ });
70
+ });
71
+
72
+ // Size tests
73
+ describe('Sizes', () => {
74
+ it('renders with small size', () => {
75
+ renderWithProviders(<Textarea size="sm" />);
76
+ const textarea = screen.getByRole('textbox');
77
+ expect(textarea).toHaveClass('min-h-[60px]', 'px-2', 'py-1', 'text-xs');
78
+ });
79
+
80
+ it('renders with medium size (default)', () => {
81
+ renderWithProviders(<Textarea size="md" />);
82
+ const textarea = screen.getByRole('textbox');
83
+ expect(textarea).toHaveClass('min-h-[80px]', 'px-3', 'py-2', 'text-sm');
84
+ });
85
+
86
+ it('renders with large size', () => {
87
+ renderWithProviders(<Textarea size="lg" />);
88
+ const textarea = screen.getByRole('textbox');
89
+ expect(textarea).toHaveClass('min-h-[100px]', 'px-4', 'py-3', 'text-base');
90
+ });
91
+ });
92
+
93
+ // Event handling tests
94
+ describe('Event Handling', () => {
95
+ it('handles onChange events', async () => {
96
+ const handleChange = vi.fn();
97
+ const user = userEvent.setup();
98
+
99
+ renderWithProviders(<Textarea onChange={handleChange} />);
100
+
101
+ const textarea = screen.getByRole('textbox');
102
+ await user.type(textarea, 'test input');
103
+
104
+ expect(handleChange).toHaveBeenCalled();
105
+ });
106
+
107
+ it('handles onFocus events', async () => {
108
+ const handleFocus = vi.fn();
109
+ const user = userEvent.setup();
110
+
111
+ renderWithProviders(<Textarea onFocus={handleFocus} />);
112
+
113
+ const textarea = screen.getByRole('textbox');
114
+ await user.click(textarea);
115
+
116
+ expect(handleFocus).toHaveBeenCalledTimes(1);
117
+ });
118
+
119
+ it('handles onBlur events', async () => {
120
+ const handleBlur = vi.fn();
121
+ const user = userEvent.setup();
122
+
123
+ renderWithProviders(<Textarea onBlur={handleBlur} />);
124
+
125
+ const textarea = screen.getByRole('textbox');
126
+ await user.click(textarea);
127
+ await user.tab();
128
+
129
+ expect(handleBlur).toHaveBeenCalledTimes(1);
130
+ });
131
+
132
+ it('handles onKeyDown events', async () => {
133
+ const handleKeyDown = vi.fn();
134
+ const user = userEvent.setup();
135
+
136
+ renderWithProviders(<Textarea onKeyDown={handleKeyDown} />);
137
+
138
+ const textarea = screen.getByRole('textbox');
139
+ await user.type(textarea, 'a');
140
+
141
+ expect(handleKeyDown).toHaveBeenCalled();
142
+ });
143
+ });
144
+
145
+ // State management tests
146
+ describe('State Management', () => {
147
+ it('handles controlled state', () => {
148
+ const { rerender } = renderWithProviders(
149
+ <Textarea value="initial" />
150
+ );
151
+
152
+ expect(screen.getByDisplayValue('initial')).toBeInTheDocument();
153
+
154
+ rerender(<Textarea value="updated" />);
155
+ expect(screen.getByDisplayValue('updated')).toBeInTheDocument();
156
+ });
157
+
158
+ it('handles uncontrolled state', () => {
159
+ renderWithProviders(<Textarea defaultValue="default" />);
160
+ expect(screen.getByDisplayValue('default')).toBeInTheDocument();
161
+ });
162
+
163
+ it('handles disabled state', () => {
164
+ renderWithProviders(<Textarea disabled />);
165
+ const textarea = screen.getByRole('textbox');
166
+ expect(textarea).toBeDisabled();
167
+ expect(textarea).toHaveClass('disabled:cursor-not-allowed', 'disabled:opacity-50');
168
+ });
169
+ });
170
+
171
+ // Accessibility tests
172
+ describe('Accessibility', () => {
173
+ it('has proper ARIA attributes', () => {
174
+ renderWithProviders(<Textarea aria-label="Test textarea" />);
175
+ const textarea = screen.getByRole('textbox');
176
+ expect(textarea).toHaveAttribute('aria-label', 'Test textarea');
177
+ });
178
+
179
+ it('supports aria-describedby', () => {
180
+ renderWithProviders(
181
+ <div>
182
+ <Textarea aria-describedby="help-text" />
183
+ <div id="help-text">This is help text</div>
184
+ </div>
185
+ );
186
+ const textarea = screen.getByRole('textbox');
187
+ expect(textarea).toHaveAttribute('aria-describedby', 'help-text');
188
+ });
189
+
190
+ it('supports aria-invalid for error states', () => {
191
+ renderWithProviders(<Textarea aria-invalid="true" />);
192
+ const textarea = screen.getByRole('textbox');
193
+ expect(textarea).toHaveAttribute('aria-invalid', 'true');
194
+ });
195
+
196
+ it('is keyboard accessible', () => {
197
+ renderWithProviders(<Textarea />);
198
+ const textarea = screen.getByRole('textbox');
199
+ expect(textarea).not.toHaveAttribute('tabindex', '-1');
200
+ });
201
+
202
+ it('supports screen readers with proper focus management', async () => {
203
+ const user = userEvent.setup();
204
+ renderWithProviders(<Textarea />);
205
+
206
+ const textarea = screen.getByRole('textbox');
207
+ await user.click(textarea);
208
+
209
+ expect(textarea).toHaveFocus();
210
+ });
211
+ });
212
+
213
+ // Error handling tests
214
+ describe('Error Handling', () => {
215
+ it('handles error state styling', () => {
216
+ renderWithProviders(<Textarea error={true} />);
217
+ const textarea = screen.getByRole('textbox');
218
+ expect(textarea).toHaveClass('border-destructive', 'focus-visible:ring-destructive');
219
+ });
220
+ });
221
+
222
+ // Integration tests
223
+ describe('Integration', () => {
224
+ it('works with forms', async () => {
225
+ const handleSubmit = vi.fn((e) => e.preventDefault());
226
+ const user = userEvent.setup();
227
+
228
+ renderWithProviders(
229
+ <form onSubmit={handleSubmit}>
230
+ <Textarea name="test-textarea" />
231
+ <button type="submit">Submit</button>
232
+ </form>
233
+ );
234
+
235
+ await user.click(screen.getByRole('button', { name: 'Submit' }));
236
+ expect(handleSubmit).toHaveBeenCalledTimes(1);
237
+ });
238
+
239
+ it('works with labels', () => {
240
+ renderWithProviders(
241
+ <div>
242
+ <label htmlFor="test-textarea">Test Label</label>
243
+ <Textarea id="test-textarea" />
244
+ </div>
245
+ );
246
+
247
+ const textarea = screen.getByLabelText('Test Label');
248
+ expect(textarea).toBeInTheDocument();
249
+ });
250
+
251
+ it('forwards ref correctly', () => {
252
+ const ref = React.createRef<HTMLTextAreaElement>();
253
+ renderWithProviders(<Textarea ref={ref} />);
254
+
255
+ expect(ref.current).toBeInstanceOf(HTMLTextAreaElement);
256
+ });
257
+
258
+ it('handles multi-line input', async () => {
259
+ const user = userEvent.setup();
260
+ renderWithProviders(<Textarea />);
261
+
262
+ const textarea = screen.getByRole('textbox');
263
+ await user.type(textarea, 'Line 1{Enter}Line 2');
264
+
265
+ expect(textarea).toHaveValue('Line 1\nLine 2');
266
+ });
267
+ });
268
+ });
269
+
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @file Textarea Component
3
+ * @package @jmruthers/pace-core
4
+ * @module Textarea
5
+ * @since 0.5.141
6
+ *
7
+ * A multi-line text input component with consistent styling matching the Input component.
8
+ * Provides flexible, accessible textarea component with consistent styling and behavior.
9
+ *
10
+ * Features:
11
+ * - Multiple textarea variants (default, destructive)
12
+ * - Multiple textarea sizes (sm, md, lg)
13
+ * - Error state styling
14
+ * - Forwarded ref support
15
+ * - Consistent styling with Input component
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Basic textarea
20
+ * <Textarea placeholder="Enter your message..." />
21
+ *
22
+ * // Textarea with variants and sizes
23
+ * <Textarea
24
+ * variant="destructive"
25
+ * size="lg"
26
+ * placeholder="Error textarea"
27
+ * />
28
+ *
29
+ * // Textarea with error state
30
+ * <Textarea
31
+ * placeholder="Comments"
32
+ * error={true}
33
+ * />
34
+ * ```
35
+ *
36
+ * @accessibility
37
+ * - Proper ARIA attributes and roles
38
+ * - Keyboard navigation support
39
+ * - Screen reader friendly
40
+ * - Focus management
41
+ */
42
+
43
+ import * as React from 'react';
44
+ import { cn } from '../../utils/core/cn';
45
+
46
+ // ============================================================================
47
+ // TEXTAREA COMPONENT
48
+ // ============================================================================
49
+
50
+ export interface TextareaProps
51
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
52
+ /**
53
+ * Textarea variant style
54
+ */
55
+ variant?: 'default' | 'destructive';
56
+
57
+ /**
58
+ * Textarea size
59
+ */
60
+ size?: 'sm' | 'md' | 'lg';
61
+
62
+ /**
63
+ * Error state for styling
64
+ */
65
+ error?: boolean;
66
+ }
67
+
68
+ /**
69
+ * Textarea component
70
+ * A flexible, accessible textarea component with multiple variants and sizes.
71
+ * Matches the Input component API and styling for consistency.
72
+ *
73
+ * @param props - Textarea configuration and styling
74
+ * @param ref - Forwarded ref to the textarea element
75
+ * @returns JSX.Element - The rendered textarea element
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * // Basic textarea
80
+ * <Textarea placeholder="Enter your message..." />
81
+ *
82
+ * // Textarea with error state
83
+ * <Textarea
84
+ * placeholder="Comments"
85
+ * error={true}
86
+ * />
87
+ *
88
+ * // Large textarea with destructive variant
89
+ * <Textarea
90
+ * variant="destructive"
91
+ * size="lg"
92
+ * placeholder="Error textarea"
93
+ * />
94
+ * ```
95
+ */
96
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
97
+ ({ className, variant = 'default', size = 'md', error, ...props }, ref) => {
98
+ return (
99
+ <textarea
100
+ className={cn(
101
+ // Base styles (matching Input component)
102
+ 'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
103
+
104
+ // Variant styles
105
+ {
106
+ 'border-input': variant === 'default' && !error,
107
+ 'border-destructive focus-visible:ring-destructive': variant === 'destructive' || error,
108
+ },
109
+
110
+ // Size styles
111
+ {
112
+ 'min-h-[60px] px-2 py-1 text-xs': size === 'sm',
113
+ 'min-h-[80px] px-3 py-2 text-sm': size === 'md',
114
+ 'min-h-[100px] px-4 py-3 text-base': size === 'lg',
115
+ },
116
+
117
+ className
118
+ )}
119
+ ref={ref}
120
+ {...props}
121
+ />
122
+ );
123
+ }
124
+ );
125
+
126
+ Textarea.displayName = 'Textarea';
127
+
128
+ // ============================================================================
129
+ // EXPORTS
130
+ // ============================================================================
131
+
132
+ export { Textarea };
133
+
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file Textarea component exports
3
+ * @package @jmruthers/pace-core
4
+ * @module Textarea
5
+ * @since 0.5.141
6
+ */
7
+
8
+ export { Textarea } from './Textarea';
9
+ export type { TextareaProps } from './Textarea';
10
+
@@ -46,6 +46,9 @@ export type { InputProps } from './Input';
46
46
  export { Label } from './Label';
47
47
  export type { LabelProps } from './Label';
48
48
 
49
+ export { Textarea } from './Textarea';
50
+ export type { TextareaProps } from './Textarea';
51
+
49
52
  export { Alert, AlertTitle, AlertDescription } from './Alert';
50
53
  export { Avatar, AvatarImage, AvatarFallback } from './Avatar';
51
54
 
@@ -114,6 +117,14 @@ export {
114
117
  SelectSeparator,
115
118
  } from './Select';
116
119
 
120
+ // Tabs exports
121
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs';
122
+ export type { TabsProps, TabsListProps, TabsTriggerProps, TabsContentProps } from './Tabs';
123
+
124
+ // Calendar exports
125
+ export { Calendar } from './Calendar';
126
+ export type { CalendarProps } from './Calendar';
127
+
117
128
  // Toast exports
118
129
  export {
119
130
  Toast,
package/src/index.ts CHANGED
@@ -76,6 +76,9 @@ export type { InputProps } from './components/Input/Input';
76
76
  export { Label } from './components/Label/Label';
77
77
  export type { LabelProps } from './components/Label/Label';
78
78
 
79
+ export { Textarea } from './components/Textarea/Textarea';
80
+ export type { TextareaProps } from './components/Textarea/Textarea';
81
+
79
82
  export { Alert, AlertTitle, AlertDescription } from './components/Alert/Alert';
80
83
  export { Avatar, AvatarImage, AvatarFallback } from './components/Avatar/Avatar';
81
84
 
@@ -117,6 +120,14 @@ export {
117
120
  SelectSeparator,
118
121
  } from './components/Select';
119
122
 
123
+ // Tabs exports
124
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './components/Tabs/Tabs';
125
+ export type { TabsProps, TabsListProps, TabsTriggerProps, TabsContentProps } from './components/Tabs/Tabs';
126
+
127
+ // Calendar exports
128
+ export { Calendar } from './components/Calendar/Calendar';
129
+ export type { CalendarProps } from './components/Calendar/Calendar';
130
+
120
131
  // Modal functionality is provided by Dialog components
121
132
 
122
133
  export {
@@ -10,6 +10,8 @@
10
10
  export { useRBAC } from './useRBAC';
11
11
  export { useResolvedScope } from './useResolvedScope';
12
12
  export type { UseResolvedScopeOptions, UseResolvedScopeReturn } from './useResolvedScope';
13
+ export { useResourcePermissions } from './useResourcePermissions';
14
+ export type { UseResourcePermissionsOptions, ResourcePermissions } from './useResourcePermissions';
13
15
 
14
16
  // Export all permission hooks
15
17
  export {