@owlmeans/client-panel 0.1.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 -0
- package/README.md +791 -0
- package/build/.gitkeep +0 -0
- package/build/auth/i18n/en.json +56 -0
- package/build/auth/i18n.d.ts +2 -0
- package/build/auth/i18n.d.ts.map +1 -0
- package/build/auth/i18n.js +4 -0
- package/build/auth/i18n.js.map +1 -0
- package/build/auth/index.d.ts +2 -0
- package/build/auth/index.d.ts.map +1 -0
- package/build/auth/index.js +2 -0
- package/build/auth/index.js.map +1 -0
- package/build/auth/plugins/consts.d.ts +4 -0
- package/build/auth/plugins/consts.d.ts.map +1 -0
- package/build/auth/plugins/consts.js +13 -0
- package/build/auth/plugins/consts.js.map +1 -0
- package/build/auth/plugins/exports.d.ts +3 -0
- package/build/auth/plugins/exports.d.ts.map +1 -0
- package/build/auth/plugins/exports.js +3 -0
- package/build/auth/plugins/exports.js.map +1 -0
- package/build/auth/plugins/types.d.ts +6 -0
- package/build/auth/plugins/types.d.ts.map +1 -0
- package/build/auth/plugins/types.js +2 -0
- package/build/auth/plugins/types.js.map +1 -0
- package/build/components/consts.d.ts +6 -0
- package/build/components/consts.d.ts.map +1 -0
- package/build/components/consts.js +7 -0
- package/build/components/consts.js.map +1 -0
- package/build/components/context.d.ts +9 -0
- package/build/components/context.d.ts.map +1 -0
- package/build/components/context.js +30 -0
- package/build/components/context.js.map +1 -0
- package/build/components/form/context.d.ts +8 -0
- package/build/components/form/context.d.ts.map +1 -0
- package/build/components/form/context.js +26 -0
- package/build/components/form/context.js.map +1 -0
- package/build/components/form/helper.d.ts +4 -0
- package/build/components/form/helper.d.ts.map +1 -0
- package/build/components/form/helper.js +5 -0
- package/build/components/form/helper.js.map +1 -0
- package/build/components/form/index.d.ts +4 -0
- package/build/components/form/index.d.ts.map +1 -0
- package/build/components/form/index.js +4 -0
- package/build/components/form/index.js.map +1 -0
- package/build/components/form/types.d.ts +33 -0
- package/build/components/form/types.d.ts.map +1 -0
- package/build/components/form/types.js +2 -0
- package/build/components/form/types.js.map +1 -0
- package/build/components/index.d.ts +6 -0
- package/build/components/index.d.ts.map +1 -0
- package/build/components/index.js +5 -0
- package/build/components/index.js.map +1 -0
- package/build/components/layout/helper.d.ts +6 -0
- package/build/components/layout/helper.d.ts.map +1 -0
- package/build/components/layout/helper.js +17 -0
- package/build/components/layout/helper.js.map +1 -0
- package/build/components/layout/index.d.ts +2 -0
- package/build/components/layout/index.d.ts.map +1 -0
- package/build/components/layout/index.js +2 -0
- package/build/components/layout/index.js.map +1 -0
- package/build/components/types.d.ts +7 -0
- package/build/components/types.d.ts.map +1 -0
- package/build/components/types.js +2 -0
- package/build/components/types.js.map +1 -0
- package/build/helper/form.d.ts +3 -0
- package/build/helper/form.d.ts.map +1 -0
- package/build/helper/form.js +5 -0
- package/build/helper/form.js.map +1 -0
- package/build/helper/index.d.ts +2 -0
- package/build/helper/index.d.ts.map +1 -0
- package/build/helper/index.js +2 -0
- package/build/helper/index.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/package.json +74 -0
- package/src/auth/i18n/en.json +56 -0
- package/src/auth/i18n.ts +5 -0
- package/src/auth/index.ts +2 -0
- package/src/auth/plugins/consts.ts +15 -0
- package/src/auth/plugins/exports.ts +3 -0
- package/src/auth/plugins/types.ts +6 -0
- package/src/components/consts.ts +6 -0
- package/src/components/context.tsx +44 -0
- package/src/components/form/context.tsx +39 -0
- package/src/components/form/helper.ts +7 -0
- package/src/components/form/index.ts +4 -0
- package/src/components/form/types.ts +37 -0
- package/src/components/index.ts +6 -0
- package/src/components/layout/helper.ts +26 -0
- package/src/components/layout/index.ts +2 -0
- package/src/components/types.ts +7 -0
- package/src/helper/form.ts +7 -0
- package/src/helper/index.ts +2 -0
- package/src/index.ts +3 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
# @owlmeans/client-panel
|
|
2
|
+
|
|
3
|
+
React client panel library for OwlMeans Common applications. This package provides a comprehensive set of UI components and utilities for building administrative panels, forms, and user interfaces with integrated internationalization, validation, and layout management.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@owlmeans/client-panel` package extends the OwlMeans client ecosystem with pre-built UI components designed for panel applications. It provides:
|
|
8
|
+
|
|
9
|
+
- **Form Components**: Advanced form handling with validation and internationalization
|
|
10
|
+
- **Layout Components**: Flexible layout system for panel interfaces
|
|
11
|
+
- **Authentication Components**: Pre-built authentication UI components
|
|
12
|
+
- **Helper Utilities**: Form helpers, context providers, and utility functions
|
|
13
|
+
- **Internationalization**: Full i18n support with contextual translations
|
|
14
|
+
- **Validation Integration**: AJV schema validation with React Hook Form
|
|
15
|
+
- **Error Handling**: Comprehensive error display and management
|
|
16
|
+
- **Responsive Design**: Mobile-friendly responsive layout components
|
|
17
|
+
|
|
18
|
+
This package is part of the OwlMeans panel UI ecosystem:
|
|
19
|
+
- **@owlmeans/client-panel**: React panel components *(this package)*
|
|
20
|
+
- **@owlmeans/web-panel**: Web-specific panel implementations
|
|
21
|
+
- **@owlmeans/native-panel**: React Native panel components
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @owlmeans/client-panel react react-hook-form
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Core Concepts
|
|
30
|
+
|
|
31
|
+
### Panel Architecture
|
|
32
|
+
The library follows a component-based architecture where panels are composed of reusable UI elements with consistent styling and behavior.
|
|
33
|
+
|
|
34
|
+
### Form Management
|
|
35
|
+
Built on React Hook Form with AJV schema validation, providing type-safe forms with automatic validation and error handling.
|
|
36
|
+
|
|
37
|
+
### Internationalization
|
|
38
|
+
Full integration with OwlMeans i18n system for translatable UI components with context-aware translations.
|
|
39
|
+
|
|
40
|
+
### Authentication Integration
|
|
41
|
+
Optional authentication components that integrate with the OwlMeans authentication system.
|
|
42
|
+
|
|
43
|
+
## API Reference
|
|
44
|
+
|
|
45
|
+
### Components
|
|
46
|
+
|
|
47
|
+
#### Form Components
|
|
48
|
+
|
|
49
|
+
The form system provides comprehensive form handling with validation, internationalization, and error management.
|
|
50
|
+
|
|
51
|
+
##### `<Form />`
|
|
52
|
+
|
|
53
|
+
Main form component with integrated validation and submission handling.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
interface FormProps extends PropsWithChildren<I18nProps> {
|
|
57
|
+
name?: string // Form identifier for i18n
|
|
58
|
+
formRef?: MutableRefObject<FormRef<any> | null> // Form reference
|
|
59
|
+
defaults?: Record<string, any> // Default form values
|
|
60
|
+
validation?: AnySchema // AJV validation schema
|
|
61
|
+
decorate?: boolean // Apply form decoration
|
|
62
|
+
horizontal?: BlockScaling // Horizontal layout scaling
|
|
63
|
+
vertical?: BlockScaling // Vertical layout scaling
|
|
64
|
+
onSubmit?: FormOnSubmit<any> // Submit handler
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const Form: FC<FormProps>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Usage:**
|
|
71
|
+
```typescript
|
|
72
|
+
import { Form, useFormRef } from '@owlmeans/client-panel'
|
|
73
|
+
|
|
74
|
+
function UserForm() {
|
|
75
|
+
const formRef = useFormRef()
|
|
76
|
+
|
|
77
|
+
const handleSubmit = async (data, update) => {
|
|
78
|
+
try {
|
|
79
|
+
await api.saveUser(data)
|
|
80
|
+
console.log('User saved successfully')
|
|
81
|
+
} catch (error) {
|
|
82
|
+
formRef.current?.error(error)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Form
|
|
88
|
+
name="user-form"
|
|
89
|
+
formRef={formRef}
|
|
90
|
+
defaults={{ name: '', email: '' }}
|
|
91
|
+
validation={userSchema}
|
|
92
|
+
onSubmit={handleSubmit}
|
|
93
|
+
>
|
|
94
|
+
<input name="name" placeholder="Name" />
|
|
95
|
+
<input name="email" placeholder="Email" />
|
|
96
|
+
<button type="submit">Save User</button>
|
|
97
|
+
</Form>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### Layout Components
|
|
103
|
+
|
|
104
|
+
Layout components provide structured panel layouts with responsive design.
|
|
105
|
+
|
|
106
|
+
##### Layout Helper
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { LayoutHelper } from '@owlmeans/client-panel'
|
|
110
|
+
|
|
111
|
+
// Layout utilities and components
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Hooks and Utilities
|
|
115
|
+
|
|
116
|
+
#### `useFormRef<T>(): MutableRefObject<FormRef<T> | null>`
|
|
117
|
+
|
|
118
|
+
Creates a reference for form component interaction.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { useFormRef } from '@owlmeans/client-panel'
|
|
122
|
+
|
|
123
|
+
function FormComponent() {
|
|
124
|
+
const formRef = useFormRef<UserData>()
|
|
125
|
+
|
|
126
|
+
const resetForm = () => {
|
|
127
|
+
formRef.current?.form.reset()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const updateForm = (data: UserData) => {
|
|
131
|
+
formRef.current?.update(data)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div>
|
|
136
|
+
<Form formRef={formRef}>
|
|
137
|
+
{/* form fields */}
|
|
138
|
+
</Form>
|
|
139
|
+
<button onClick={resetForm}>Reset</button>
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### `useFormI18n(): I18nFunction`
|
|
146
|
+
|
|
147
|
+
Provides internationalization for form components with contextual prefixes.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { useFormI18n } from '@owlmeans/client-panel'
|
|
151
|
+
|
|
152
|
+
function FormField() {
|
|
153
|
+
const t = useFormI18n()
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div>
|
|
157
|
+
<label>{t('name.label')}</label>
|
|
158
|
+
<input placeholder={t('name.placeholder')} />
|
|
159
|
+
</div>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `useFormError(name: string, error?: FieldError): string | undefined`
|
|
165
|
+
|
|
166
|
+
Provides internationalized error messages for form fields.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { useFormError } from '@owlmeans/client-panel'
|
|
170
|
+
import { useFormState } from 'react-hook-form'
|
|
171
|
+
|
|
172
|
+
function FormField({ name }) {
|
|
173
|
+
const { errors } = useFormState()
|
|
174
|
+
const errorMessage = useFormError(name, errors[name])
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div>
|
|
178
|
+
<input name={name} />
|
|
179
|
+
{errorMessage && <span className="error">{errorMessage}</span>}
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### `useClientFormContext(): TFormContext`
|
|
186
|
+
|
|
187
|
+
Accesses the current form context for configuration and state.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { useClientFormContext } from '@owlmeans/client-panel'
|
|
191
|
+
|
|
192
|
+
function CustomFormField() {
|
|
193
|
+
const formContext = useClientFormContext()
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<div className={formContext.horizontal ? 'horizontal' : 'vertical'}>
|
|
197
|
+
{/* field content */}
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Types and Interfaces
|
|
204
|
+
|
|
205
|
+
#### `FormRef<T>`
|
|
206
|
+
|
|
207
|
+
Reference interface for form component control.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
interface FormRef<T extends FieldValues = FieldValues> {
|
|
211
|
+
form: UseFormReturn<T> // React Hook Form instance
|
|
212
|
+
update: (data: T) => void // Update form data
|
|
213
|
+
loader: Toggleable // Loading state control
|
|
214
|
+
error: (error: unknown, target?: string) => void // Error handling
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### `FormOnSubmit<T>`
|
|
219
|
+
|
|
220
|
+
Submit handler interface for form components.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface FormOnSubmit<T> {
|
|
224
|
+
(data: T, update?: (data: T) => void): Promise<void> | void
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### `TFormContext`
|
|
229
|
+
|
|
230
|
+
Form context interface for component communication.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
interface TFormContext extends Omit<FormProps, 'defaults' | 'children'> {
|
|
234
|
+
loader: Toggleable // Loading state management
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### `FormFieldProps`
|
|
239
|
+
|
|
240
|
+
Base properties for form field components.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
interface FormFieldProps {
|
|
244
|
+
name: string // Field name
|
|
245
|
+
def?: any // Default value
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Helper Functions
|
|
250
|
+
|
|
251
|
+
#### `schemaToFormDefault(schema: AnySchema): Record<string, any>`
|
|
252
|
+
|
|
253
|
+
Extracts default values from AJV schema for form initialization.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { schemaToFormDefault } from '@owlmeans/client-panel'
|
|
257
|
+
|
|
258
|
+
const userSchema = {
|
|
259
|
+
type: 'object',
|
|
260
|
+
properties: {
|
|
261
|
+
name: { type: 'string', default: 'John Doe' },
|
|
262
|
+
email: { type: 'string', default: '' },
|
|
263
|
+
age: { type: 'number', default: 18 }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const defaults = schemaToFormDefault(userSchema)
|
|
268
|
+
// Result: { name: 'John Doe', email: '', age: 18 }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Constants
|
|
272
|
+
|
|
273
|
+
#### `BlockScaling`
|
|
274
|
+
|
|
275
|
+
Enumeration for layout scaling options.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
enum BlockScaling {
|
|
279
|
+
// Layout scaling values
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Authentication Components
|
|
284
|
+
|
|
285
|
+
The package includes optional authentication components accessible via the `/auth` export:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { AuthComponents } from '@owlmeans/client-panel/auth'
|
|
289
|
+
import { AuthPlugins } from '@owlmeans/client-panel/auth/plugins'
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
These components provide:
|
|
293
|
+
- Pre-built authentication forms
|
|
294
|
+
- Login and registration components
|
|
295
|
+
- Authentication plugins and extensions
|
|
296
|
+
- Integration with OwlMeans authentication system
|
|
297
|
+
|
|
298
|
+
## Usage Examples
|
|
299
|
+
|
|
300
|
+
### Basic Form with Validation
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import React from 'react'
|
|
304
|
+
import { Form, useFormRef, schemaToFormDefault } from '@owlmeans/client-panel'
|
|
305
|
+
import { useFormState } from 'react-hook-form'
|
|
306
|
+
|
|
307
|
+
interface UserData {
|
|
308
|
+
name: string
|
|
309
|
+
email: string
|
|
310
|
+
age: number
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const userSchema = {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {
|
|
316
|
+
name: { type: 'string', minLength: 2, default: '' },
|
|
317
|
+
email: { type: 'string', format: 'email', default: '' },
|
|
318
|
+
age: { type: 'number', minimum: 18, default: 18 }
|
|
319
|
+
},
|
|
320
|
+
required: ['name', 'email']
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function UserRegistrationForm() {
|
|
324
|
+
const formRef = useFormRef<UserData>()
|
|
325
|
+
const defaults = schemaToFormDefault(userSchema)
|
|
326
|
+
|
|
327
|
+
const handleSubmit = async (data: UserData, update) => {
|
|
328
|
+
try {
|
|
329
|
+
// Show loading state
|
|
330
|
+
formRef.current?.loader.on()
|
|
331
|
+
|
|
332
|
+
// Submit data
|
|
333
|
+
await api.createUser(data)
|
|
334
|
+
|
|
335
|
+
// Success - reset form
|
|
336
|
+
formRef.current?.form.reset()
|
|
337
|
+
|
|
338
|
+
console.log('User created successfully')
|
|
339
|
+
} catch (error) {
|
|
340
|
+
// Handle error
|
|
341
|
+
formRef.current?.error(error)
|
|
342
|
+
} finally {
|
|
343
|
+
// Hide loading state
|
|
344
|
+
formRef.current?.loader.off()
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<Form
|
|
350
|
+
name="user-registration"
|
|
351
|
+
formRef={formRef}
|
|
352
|
+
defaults={defaults}
|
|
353
|
+
validation={userSchema}
|
|
354
|
+
onSubmit={handleSubmit}
|
|
355
|
+
decorate={true}
|
|
356
|
+
>
|
|
357
|
+
<UserFormFields />
|
|
358
|
+
</Form>
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function UserFormFields() {
|
|
363
|
+
const { errors } = useFormState<UserData>()
|
|
364
|
+
const t = useFormI18n()
|
|
365
|
+
const nameError = useFormError('name', errors.name)
|
|
366
|
+
const emailError = useFormError('email', errors.email)
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<div>
|
|
370
|
+
<div>
|
|
371
|
+
<label>{t('name.label')}</label>
|
|
372
|
+
<input
|
|
373
|
+
name="name"
|
|
374
|
+
placeholder={t('name.placeholder')}
|
|
375
|
+
className={errors.name ? 'error' : ''}
|
|
376
|
+
/>
|
|
377
|
+
{nameError && <span className="error-text">{nameError}</span>}
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div>
|
|
381
|
+
<label>{t('email.label')}</label>
|
|
382
|
+
<input
|
|
383
|
+
name="email"
|
|
384
|
+
type="email"
|
|
385
|
+
placeholder={t('email.placeholder')}
|
|
386
|
+
className={errors.email ? 'error' : ''}
|
|
387
|
+
/>
|
|
388
|
+
{emailError && <span className="error-text">{emailError}</span>}
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<div>
|
|
392
|
+
<label>{t('age.label')}</label>
|
|
393
|
+
<input
|
|
394
|
+
name="age"
|
|
395
|
+
type="number"
|
|
396
|
+
min="18"
|
|
397
|
+
/>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<button type="submit">{t('submit')}</button>
|
|
401
|
+
</div>
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Form with Dynamic Validation
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { Form, useFormRef } from '@owlmeans/client-panel'
|
|
410
|
+
import { useState } from 'react'
|
|
411
|
+
|
|
412
|
+
function DynamicForm() {
|
|
413
|
+
const formRef = useFormRef()
|
|
414
|
+
const [formType, setFormType] = useState<'basic' | 'advanced'>('basic')
|
|
415
|
+
|
|
416
|
+
const basicSchema = {
|
|
417
|
+
type: 'object',
|
|
418
|
+
properties: {
|
|
419
|
+
name: { type: 'string', minLength: 2 },
|
|
420
|
+
email: { type: 'string', format: 'email' }
|
|
421
|
+
},
|
|
422
|
+
required: ['name', 'email']
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const advancedSchema = {
|
|
426
|
+
type: 'object',
|
|
427
|
+
properties: {
|
|
428
|
+
...basicSchema.properties,
|
|
429
|
+
phone: { type: 'string', pattern: '^\\+?[1-9]\\d{1,14}$' },
|
|
430
|
+
company: { type: 'string', minLength: 2 }
|
|
431
|
+
},
|
|
432
|
+
required: [...basicSchema.required, 'phone', 'company']
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const currentSchema = formType === 'basic' ? basicSchema : advancedSchema
|
|
436
|
+
|
|
437
|
+
const handleSubmit = async (data) => {
|
|
438
|
+
console.log('Form data:', data)
|
|
439
|
+
console.log('Form type:', formType)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<div>
|
|
444
|
+
<div>
|
|
445
|
+
<button onClick={() => setFormType('basic')}>Basic Form</button>
|
|
446
|
+
<button onClick={() => setFormType('advanced')}>Advanced Form</button>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<Form
|
|
450
|
+
name={`${formType}-form`}
|
|
451
|
+
formRef={formRef}
|
|
452
|
+
validation={currentSchema}
|
|
453
|
+
onSubmit={handleSubmit}
|
|
454
|
+
>
|
|
455
|
+
<input name="name" placeholder="Name" />
|
|
456
|
+
<input name="email" placeholder="Email" />
|
|
457
|
+
|
|
458
|
+
{formType === 'advanced' && (
|
|
459
|
+
<>
|
|
460
|
+
<input name="phone" placeholder="Phone" />
|
|
461
|
+
<input name="company" placeholder="Company" />
|
|
462
|
+
</>
|
|
463
|
+
)}
|
|
464
|
+
|
|
465
|
+
<button type="submit">Submit {formType} Form</button>
|
|
466
|
+
</Form>
|
|
467
|
+
</div>
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Internationalized Form
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
import { Form, useFormI18n, useFormError } from '@owlmeans/client-panel'
|
|
476
|
+
import { addI18nApp } from '@owlmeans/i18n'
|
|
477
|
+
|
|
478
|
+
// Add translations
|
|
479
|
+
addI18nApp('en', 'user-form', {
|
|
480
|
+
'name.label': 'Full Name',
|
|
481
|
+
'name.placeholder': 'Enter your full name',
|
|
482
|
+
'email.label': 'Email Address',
|
|
483
|
+
'email.placeholder': 'Enter your email',
|
|
484
|
+
'submit': 'Create Account',
|
|
485
|
+
'name.errors.minLength': 'Name must be at least 2 characters',
|
|
486
|
+
'email.errors.format': 'Please enter a valid email address'
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
addI18nApp('es', 'user-form', {
|
|
490
|
+
'name.label': 'Nombre Completo',
|
|
491
|
+
'name.placeholder': 'Ingrese su nombre completo',
|
|
492
|
+
'email.label': 'Dirección de Correo',
|
|
493
|
+
'email.placeholder': 'Ingrese su correo',
|
|
494
|
+
'submit': 'Crear Cuenta',
|
|
495
|
+
'name.errors.minLength': 'El nombre debe tener al menos 2 caracteres',
|
|
496
|
+
'email.errors.format': 'Por favor ingrese un correo válido'
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
function InternationalizedForm() {
|
|
500
|
+
const formRef = useFormRef()
|
|
501
|
+
|
|
502
|
+
const handleSubmit = async (data) => {
|
|
503
|
+
console.log('Localized form data:', data)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return (
|
|
507
|
+
<Form
|
|
508
|
+
name="user-form"
|
|
509
|
+
formRef={formRef}
|
|
510
|
+
validation={userSchema}
|
|
511
|
+
onSubmit={handleSubmit}
|
|
512
|
+
i18n={{
|
|
513
|
+
resource: 'user-form',
|
|
514
|
+
ns: 'user-form'
|
|
515
|
+
}}
|
|
516
|
+
>
|
|
517
|
+
<LocalizedFormFields />
|
|
518
|
+
</Form>
|
|
519
|
+
)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function LocalizedFormFields() {
|
|
523
|
+
const { errors } = useFormState()
|
|
524
|
+
const t = useFormI18n()
|
|
525
|
+
const nameError = useFormError('name', errors.name)
|
|
526
|
+
const emailError = useFormError('email', errors.email)
|
|
527
|
+
|
|
528
|
+
return (
|
|
529
|
+
<div>
|
|
530
|
+
<div>
|
|
531
|
+
<label>{t('name.label')}</label>
|
|
532
|
+
<input
|
|
533
|
+
name="name"
|
|
534
|
+
placeholder={t('name.placeholder')}
|
|
535
|
+
/>
|
|
536
|
+
{nameError && <div className="error">{nameError}</div>}
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
<div>
|
|
540
|
+
<label>{t('email.label')}</label>
|
|
541
|
+
<input
|
|
542
|
+
name="email"
|
|
543
|
+
placeholder={t('email.placeholder')}
|
|
544
|
+
/>
|
|
545
|
+
{emailError && <div className="error">{emailError}</div>}
|
|
546
|
+
</div>
|
|
547
|
+
|
|
548
|
+
<button type="submit">{t('submit')}</button>
|
|
549
|
+
</div>
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Form with Custom Error Handling
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
import { Form, useFormRef } from '@owlmeans/client-panel'
|
|
558
|
+
import { OwlMeansError } from '@owlmeans/error'
|
|
559
|
+
|
|
560
|
+
function FormWithErrorHandling() {
|
|
561
|
+
const formRef = useFormRef()
|
|
562
|
+
const [serverErrors, setServerErrors] = useState({})
|
|
563
|
+
|
|
564
|
+
const handleSubmit = async (data) => {
|
|
565
|
+
try {
|
|
566
|
+
setServerErrors({})
|
|
567
|
+
formRef.current?.loader.on()
|
|
568
|
+
|
|
569
|
+
await api.submitData(data)
|
|
570
|
+
|
|
571
|
+
// Success
|
|
572
|
+
formRef.current?.form.reset()
|
|
573
|
+
} catch (error) {
|
|
574
|
+
if (error instanceof OwlMeansError) {
|
|
575
|
+
// Handle specific OwlMeans errors
|
|
576
|
+
if (error.code === 'VALIDATION_ERROR') {
|
|
577
|
+
setServerErrors(error.details || {})
|
|
578
|
+
} else {
|
|
579
|
+
formRef.current?.error(error, 'general')
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
// Handle generic errors
|
|
583
|
+
formRef.current?.error(error)
|
|
584
|
+
}
|
|
585
|
+
} finally {
|
|
586
|
+
formRef.current?.loader.off()
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return (
|
|
591
|
+
<Form
|
|
592
|
+
name="error-handling-form"
|
|
593
|
+
formRef={formRef}
|
|
594
|
+
onSubmit={handleSubmit}
|
|
595
|
+
>
|
|
596
|
+
<div>
|
|
597
|
+
<input name="email" />
|
|
598
|
+
{serverErrors.email && (
|
|
599
|
+
<div className="server-error">{serverErrors.email}</div>
|
|
600
|
+
)}
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
<button type="submit">Submit</button>
|
|
604
|
+
</Form>
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Layout Components Usage
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
import { LayoutHelper } from '@owlmeans/client-panel'
|
|
613
|
+
|
|
614
|
+
function PanelLayout() {
|
|
615
|
+
return (
|
|
616
|
+
<LayoutHelper>
|
|
617
|
+
<div className="panel-container">
|
|
618
|
+
<header className="panel-header">
|
|
619
|
+
<h1>Admin Panel</h1>
|
|
620
|
+
</header>
|
|
621
|
+
|
|
622
|
+
<main className="panel-content">
|
|
623
|
+
<Form name="admin-form">
|
|
624
|
+
{/* form content */}
|
|
625
|
+
</Form>
|
|
626
|
+
</main>
|
|
627
|
+
|
|
628
|
+
<footer className="panel-footer">
|
|
629
|
+
<p>© 2024 My Company</p>
|
|
630
|
+
</footer>
|
|
631
|
+
</div>
|
|
632
|
+
</LayoutHelper>
|
|
633
|
+
)
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Authentication Panel Integration
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
import { AuthComponents } from '@owlmeans/client-panel/auth'
|
|
641
|
+
import { useContext } from '@owlmeans/client'
|
|
642
|
+
|
|
643
|
+
function AuthPanel() {
|
|
644
|
+
const context = useContext()
|
|
645
|
+
const authService = context.service('auth')
|
|
646
|
+
|
|
647
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
|
648
|
+
|
|
649
|
+
useEffect(() => {
|
|
650
|
+
const checkAuth = async () => {
|
|
651
|
+
const token = await authService.authenticated()
|
|
652
|
+
setIsAuthenticated(token != null)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
checkAuth()
|
|
656
|
+
}, [])
|
|
657
|
+
|
|
658
|
+
if (!isAuthenticated) {
|
|
659
|
+
return <AuthComponents.LoginForm />
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return (
|
|
663
|
+
<div>
|
|
664
|
+
<h1>Authenticated Panel</h1>
|
|
665
|
+
{/* panel content */}
|
|
666
|
+
</div>
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### Responsive Panel Design
|
|
672
|
+
|
|
673
|
+
```typescript
|
|
674
|
+
import { Form } from '@owlmeans/client-panel'
|
|
675
|
+
import { useState, useEffect } from 'react'
|
|
676
|
+
|
|
677
|
+
function ResponsivePanel() {
|
|
678
|
+
const [isMobile, setIsMobile] = useState(false)
|
|
679
|
+
|
|
680
|
+
useEffect(() => {
|
|
681
|
+
const checkMobile = () => {
|
|
682
|
+
setIsMobile(window.innerWidth < 768)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
checkMobile()
|
|
686
|
+
window.addEventListener('resize', checkMobile)
|
|
687
|
+
|
|
688
|
+
return () => window.removeEventListener('resize', checkMobile)
|
|
689
|
+
}, [])
|
|
690
|
+
|
|
691
|
+
return (
|
|
692
|
+
<Form
|
|
693
|
+
name="responsive-form"
|
|
694
|
+
horizontal={isMobile ? 'sm' : 'lg'}
|
|
695
|
+
vertical={isMobile ? 'sm' : 'md'}
|
|
696
|
+
>
|
|
697
|
+
<div className={isMobile ? 'mobile-layout' : 'desktop-layout'}>
|
|
698
|
+
{/* form fields */}
|
|
699
|
+
</div>
|
|
700
|
+
</Form>
|
|
701
|
+
)
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
## Integration with Other Packages
|
|
706
|
+
|
|
707
|
+
### Client Integration
|
|
708
|
+
```typescript
|
|
709
|
+
import { useContext } from '@owlmeans/client'
|
|
710
|
+
import { Form } from '@owlmeans/client-panel'
|
|
711
|
+
|
|
712
|
+
// Form components automatically integrate with client context
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Internationalization Integration
|
|
716
|
+
```typescript
|
|
717
|
+
import { useI18n } from '@owlmeans/client-i18n'
|
|
718
|
+
import { Form, useFormI18n } from '@owlmeans/client-panel'
|
|
719
|
+
|
|
720
|
+
// Automatic i18n integration for form components
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### Authentication Integration
|
|
724
|
+
```typescript
|
|
725
|
+
import { AuthComponents } from '@owlmeans/client-panel/auth'
|
|
726
|
+
import { useContext } from '@owlmeans/client'
|
|
727
|
+
|
|
728
|
+
// Authentication panel components
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### Module Integration
|
|
732
|
+
```typescript
|
|
733
|
+
import { modules } from '@owlmeans/client-module'
|
|
734
|
+
import { Form } from '@owlmeans/client-panel'
|
|
735
|
+
|
|
736
|
+
// Forms can be used within module-based routing
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
## Error Handling
|
|
740
|
+
|
|
741
|
+
The package integrates with the OwlMeans error system:
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
import { OwlMeansError } from '@owlmeans/error'
|
|
745
|
+
import { Form, useFormRef } from '@owlmeans/client-panel'
|
|
746
|
+
|
|
747
|
+
const handleFormError = (error: unknown) => {
|
|
748
|
+
if (error instanceof OwlMeansError) {
|
|
749
|
+
// Handle specific error types
|
|
750
|
+
switch (error.code) {
|
|
751
|
+
case 'VALIDATION_ERROR':
|
|
752
|
+
// Handle validation errors
|
|
753
|
+
break
|
|
754
|
+
case 'AUTHENTICATION_ERROR':
|
|
755
|
+
// Handle auth errors
|
|
756
|
+
break
|
|
757
|
+
default:
|
|
758
|
+
// Handle other errors
|
|
759
|
+
break
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
## Best Practices
|
|
766
|
+
|
|
767
|
+
1. **Form Validation**: Use AJV schemas for consistent validation
|
|
768
|
+
2. **Internationalization**: Provide translations for all user-facing text
|
|
769
|
+
3. **Error Handling**: Implement comprehensive error handling for forms
|
|
770
|
+
4. **Responsive Design**: Use layout components for responsive interfaces
|
|
771
|
+
5. **Accessibility**: Ensure forms are accessible with proper labels and ARIA attributes
|
|
772
|
+
6. **Performance**: Use form refs to avoid unnecessary re-renders
|
|
773
|
+
7. **Type Safety**: Use TypeScript interfaces for form data types
|
|
774
|
+
|
|
775
|
+
## Dependencies
|
|
776
|
+
|
|
777
|
+
This package depends on:
|
|
778
|
+
- `@owlmeans/client` - React client library
|
|
779
|
+
- `@owlmeans/client-i18n` - Client internationalization
|
|
780
|
+
- `@owlmeans/client-module` - Client module system
|
|
781
|
+
- `@owlmeans/error` - Error handling system
|
|
782
|
+
- `react` - React library (peer dependency)
|
|
783
|
+
- `react-hook-form` - Form management (peer dependency)
|
|
784
|
+
|
|
785
|
+
## Related Packages
|
|
786
|
+
|
|
787
|
+
- [`@owlmeans/client`](../client) - React client library
|
|
788
|
+
- [`@owlmeans/web-panel`](../web-panel) - Web-specific panel components
|
|
789
|
+
- [`@owlmeans/native-panel`](../native-panel) - React Native panel components
|
|
790
|
+
- [`@owlmeans/client-i18n`](../client-i18n) - Client internationalization
|
|
791
|
+
- [`@owlmeans/client-auth`](../client-auth) - Authentication integration
|