@owlmeans/client-panel 0.1.2 → 0.1.3
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/README.md +46 -750
- package/build/components/layout/helper.d.ts +3 -3
- package/build/components/layout/helper.d.ts.map +1 -1
- package/build/components/layout/helper.js +1 -1
- package/build/components/layout/helper.js.map +1 -1
- package/package.json +15 -14
- package/src/components/layout/helper.ts +4 -4
- package/tsconfig.json +6 -11
package/README.md
CHANGED
|
@@ -1,791 +1,87 @@
|
|
|
1
1
|
# @owlmeans/client-panel
|
|
2
2
|
|
|
3
|
-
React
|
|
3
|
+
Schema-driven React form components with react-hook-form integration, i18n, and action buttons.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
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
|
|
7
|
+
- `ClientForm` — form wrapper with react-hook-form, AJV schema validation, and submit handling
|
|
8
|
+
- `ActionCtrl` — i18n-aware button component for form actions (submit, cancel, etc.)
|
|
9
|
+
- `InputCtrl` — labeled input field component backed by react-hook-form
|
|
10
|
+
- `useFormRef()` — hook to get a ref for programmatic form operations
|
|
11
|
+
- `FormOnSubmit` — type for form submission handler functions
|
|
12
|
+
- Platform-agnostic: used by React web and React Native frontends
|
|
22
13
|
|
|
23
14
|
## Installation
|
|
24
15
|
|
|
25
16
|
```bash
|
|
26
|
-
|
|
17
|
+
bun add @owlmeans/client-panel
|
|
27
18
|
```
|
|
28
19
|
|
|
29
|
-
##
|
|
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
|
-
}
|
|
20
|
+
## Usage
|
|
66
21
|
|
|
67
|
-
|
|
68
|
-
```
|
|
22
|
+
A complete form with submit and cancel actions:
|
|
69
23
|
|
|
70
|
-
**Usage:**
|
|
71
24
|
```typescript
|
|
72
|
-
import {
|
|
25
|
+
import { ClientForm, InputCtrl, ActionCtrl, useFormRef } from '@owlmeans/client-panel'
|
|
26
|
+
import type { FormOnSubmit } from '@owlmeans/client-panel'
|
|
73
27
|
|
|
74
|
-
function
|
|
28
|
+
function CreateProjectForm() {
|
|
75
29
|
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
30
|
|
|
111
|
-
|
|
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()
|
|
31
|
+
const onSubmit: FormOnSubmit<CreateProject> = async (data) => {
|
|
32
|
+
await ctx.module<ClientModule<Project>>('project-create').call({ body: data })
|
|
128
33
|
}
|
|
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
34
|
|
|
172
|
-
function FormField({ name }) {
|
|
173
|
-
const { errors } = useFormState()
|
|
174
|
-
const errorMessage = useFormError(name, errors[name])
|
|
175
|
-
|
|
176
35
|
return (
|
|
177
|
-
<
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
36
|
+
<ClientForm schema={createProjectSchema} onSubmit={onSubmit} ref={formRef}>
|
|
37
|
+
<InputCtrl name="title" />
|
|
38
|
+
<InputCtrl name="description" />
|
|
39
|
+
<ActionCtrl i18nKey="project.create.submit" type="submit" />
|
|
40
|
+
<ActionCtrl i18nKey="project.create.cancel" onClick={() => navigate.go('project-list')} />
|
|
41
|
+
</ClientForm>
|
|
181
42
|
)
|
|
182
43
|
}
|
|
183
44
|
```
|
|
184
45
|
|
|
185
|
-
|
|
46
|
+
## API
|
|
186
47
|
|
|
187
|
-
|
|
48
|
+
### `ClientForm`
|
|
188
49
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
```
|
|
50
|
+
React component. Props:
|
|
51
|
+
- `schema: AJVSchema` — AJV schema used for validation and default values
|
|
52
|
+
- `onSubmit: FormOnSubmit<T>` — called with validated form data
|
|
53
|
+
- `ref?` — `useFormRef()` ref for programmatic reset/submit
|
|
217
54
|
|
|
218
|
-
|
|
55
|
+
### `ActionCtrl`
|
|
219
56
|
|
|
220
|
-
|
|
57
|
+
React component for buttons. Props:
|
|
58
|
+
- `i18nKey: string` — translation key for button label
|
|
59
|
+
- `type?: 'submit' | 'button' | 'reset'`
|
|
60
|
+
- `onClick?: () => void`
|
|
61
|
+
- `disabled?: boolean`
|
|
221
62
|
|
|
222
|
-
|
|
223
|
-
interface FormOnSubmit<T> {
|
|
224
|
-
(data: T, update?: (data: T) => void): Promise<void> | void
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
#### `TFormContext`
|
|
63
|
+
### `InputCtrl`
|
|
229
64
|
|
|
230
|
-
|
|
65
|
+
React component for labeled inputs. Props:
|
|
66
|
+
- `name: string` — field name from the schema
|
|
67
|
+
- `type?: string` — input type (text, email, password, etc.)
|
|
68
|
+
- `i18nKey?: string` — translation key for label (defaults to field name)
|
|
231
69
|
|
|
232
|
-
|
|
233
|
-
interface TFormContext extends Omit<FormProps, 'defaults' | 'children'> {
|
|
234
|
-
loader: Toggleable // Loading state management
|
|
235
|
-
}
|
|
236
|
-
```
|
|
70
|
+
### `useFormRef(): FormRef`
|
|
237
71
|
|
|
238
|
-
|
|
72
|
+
Returns a ref object for programmatic form control.
|
|
239
73
|
|
|
240
|
-
|
|
74
|
+
### `FormOnSubmit<T>`
|
|
241
75
|
|
|
242
76
|
```typescript
|
|
243
|
-
|
|
244
|
-
name: string // Field name
|
|
245
|
-
def?: any // Default value
|
|
246
|
-
}
|
|
77
|
+
type FormOnSubmit<T> = (data: T) => void | Promise<void>
|
|
247
78
|
```
|
|
248
79
|
|
|
249
|
-
###
|
|
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
|
|
80
|
+
### `schemaToFormDefault(schema): Record<string, any>`
|
|
776
81
|
|
|
777
|
-
|
|
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)
|
|
82
|
+
Derives default form values from an AJV schema's `default` fields.
|
|
784
83
|
|
|
785
84
|
## Related Packages
|
|
786
85
|
|
|
787
|
-
- [`@owlmeans/client`](../client)
|
|
788
|
-
- [`@owlmeans/
|
|
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
|
|
86
|
+
- [`@owlmeans/client`](../client) — `useContext`, `useNavigate` used within form components
|
|
87
|
+
- [`@owlmeans/client-i18n`](../client-i18n) — i18n provider required by `ActionCtrl`/`InputCtrl`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { AbstractRequest } from '@owlmeans/
|
|
3
|
-
export declare const usePanelLayout: <T = {}, R extends AbstractRequest = AbstractRequest>() =>
|
|
1
|
+
import type { ClientEntrypoint } from '@owlmeans/client-entrypoint';
|
|
2
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint';
|
|
3
|
+
export declare const usePanelLayout: <T = {}, R extends AbstractRequest = AbstractRequest>() => ClientEntrypoint<T, R>;
|
|
4
4
|
export declare const useLayoutTitle: (name?: string, alias?: string) => string;
|
|
5
5
|
export declare const prepareLayoutTitle: (title: string) => string;
|
|
6
6
|
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../src/components/layout/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../src/components/layout/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAO3D,eAAO,MAAM,cAAc,GAAI,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,OAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAK3G,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAM9D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,WAA0C,CAAA"}
|
|
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
|
|
4
4
|
export const usePanelLayout = () => {
|
|
5
5
|
const context = useContext();
|
|
6
6
|
const location = context.router().useLocation();
|
|
7
|
-
return context.
|
|
7
|
+
return context.entrypoint(location.state.alias);
|
|
8
8
|
};
|
|
9
9
|
export const useLayoutTitle = (name, alias) => {
|
|
10
10
|
const layout = usePanelLayout();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../../../src/components/layout/helper.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAG,
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../../../src/components/layout/helper.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAG,GAAgF,EAAE;IAC9G,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,QAAQ,GAA0B,OAAO,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAA;IAEtE,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AACjD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAa,EAAE,KAAc,EAAU,EAAE;IACtE,MAAM,MAAM,GAAG,cAAc,EAAE,CAAA;IAC/B,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAC5B,KAAK,GAAG,KAAK,IAAI,MAAM,CAAC,KAAK,CAAA;IAE7B,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AACxE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-panel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@hookform/resolvers": "^3.9.0",
|
|
39
|
-
"@owlmeans/client": "^0.1.
|
|
40
|
-
"@owlmeans/client-i18n": "^0.1.
|
|
41
|
-
"@owlmeans/client-
|
|
42
|
-
"@owlmeans/client-route": "^0.1.
|
|
43
|
-
"@owlmeans/error": "^0.1.
|
|
44
|
-
"@owlmeans/
|
|
39
|
+
"@owlmeans/client": "^0.1.3",
|
|
40
|
+
"@owlmeans/client-i18n": "^0.1.3",
|
|
41
|
+
"@owlmeans/client-entrypoint": "^0.1.3",
|
|
42
|
+
"@owlmeans/client-route": "^0.1.3",
|
|
43
|
+
"@owlmeans/error": "^0.1.3",
|
|
44
|
+
"@owlmeans/entrypoint": "^0.1.3"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@owlmeans/auth": "
|
|
48
|
-
"@owlmeans/client-auth": "
|
|
49
|
-
"@owlmeans/i18n": "
|
|
50
|
-
"@owlmeans/router": "
|
|
47
|
+
"@owlmeans/auth": "^0.1.3",
|
|
48
|
+
"@owlmeans/client-auth": "^0.1.3",
|
|
49
|
+
"@owlmeans/i18n": "^0.1.3",
|
|
50
|
+
"@owlmeans/router": "^0.1.3",
|
|
51
51
|
"ajv": "*",
|
|
52
52
|
"react": "*",
|
|
53
53
|
"react-hook-form": "*"
|
|
@@ -61,13 +61,14 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@owlmeans/
|
|
65
|
-
"@owlmeans/
|
|
64
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
65
|
+
"@owlmeans/auth": "^0.1.3",
|
|
66
|
+
"@owlmeans/client-auth": "^0.1.3",
|
|
66
67
|
"@types/react": "^19.2.7",
|
|
67
68
|
"nodemon": "^3.1.11",
|
|
68
69
|
"npm-check": "^6.0.1",
|
|
69
70
|
"react-hook-form": "^7.53.0",
|
|
70
|
-
"typescript": "^
|
|
71
|
+
"typescript": "^6.0.2"
|
|
71
72
|
},
|
|
72
73
|
"publishConfig": {
|
|
73
74
|
"access": "public"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { ClientRoute } from '@owlmeans/client-route'
|
|
2
|
-
import type {
|
|
3
|
-
import type { AbstractRequest } from '@owlmeans/
|
|
2
|
+
import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
3
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
4
4
|
import type { Location } from '@owlmeans/router'
|
|
5
5
|
|
|
6
6
|
import { useContext } from '@owlmeans/client'
|
|
7
7
|
import { usePanelI18n } from '../context.js'
|
|
8
8
|
import { useMemo } from 'react'
|
|
9
9
|
|
|
10
|
-
export const usePanelLayout = <T = {}, R extends AbstractRequest = AbstractRequest>():
|
|
10
|
+
export const usePanelLayout = <T = {}, R extends AbstractRequest = AbstractRequest>(): ClientEntrypoint<T, R> => {
|
|
11
11
|
const context = useContext()
|
|
12
12
|
const location: Location<ClientRoute> = context.router().useLocation()
|
|
13
13
|
|
|
14
|
-
return context.
|
|
14
|
+
return context.entrypoint(location.state.alias)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export const useLayoutTitle = (name?: string, alias?: string): string => {
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json",
|
|
4
|
+
"@owlmeans/dep-config/tsconfig.react.json"
|
|
5
5
|
],
|
|
6
6
|
"compilerOptions": {
|
|
7
|
-
"rootDir": "./src/",
|
|
8
|
-
"outDir": "./build/"
|
|
9
|
-
"moduleResolution": "Bundler"
|
|
7
|
+
"rootDir": "./src/",
|
|
8
|
+
"outDir": "./build/"
|
|
10
9
|
},
|
|
11
|
-
"exclude": [
|
|
12
|
-
|
|
13
|
-
"./build/**/*",
|
|
14
|
-
"./*.ts"
|
|
15
|
-
]
|
|
16
|
-
}
|
|
10
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
|
|
11
|
+
}
|