@owlmeans/client-panel 0.1.2 → 0.1.4

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 CHANGED
@@ -1,791 +1,87 @@
1
1
  # @owlmeans/client-panel
2
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.
3
+ Schema-driven React form components with react-hook-form integration, i18n, and action buttons.
4
4
 
5
5
  ## Overview
6
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
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
- npm install @owlmeans/client-panel react react-hook-form
17
+ bun add @owlmeans/client-panel
27
18
  ```
28
19
 
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
- }
20
+ ## Usage
66
21
 
67
- const Form: FC<FormProps>
68
- ```
22
+ A complete form with submit and cancel actions:
69
23
 
70
- **Usage:**
71
24
  ```typescript
72
- import { Form, useFormRef } from '@owlmeans/client-panel'
25
+ import { ClientForm, InputCtrl, ActionCtrl, useFormRef } from '@owlmeans/client-panel'
26
+ import type { FormOnSubmit } from '@owlmeans/client-panel'
73
27
 
74
- function UserForm() {
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
- // 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()
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
- <div>
178
- <input name={name} />
179
- {errorMessage && <span className="error">{errorMessage}</span>}
180
- </div>
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
- #### `useClientFormContext(): TFormContext`
46
+ ## API
186
47
 
187
- Accesses the current form context for configuration and state.
48
+ ### `ClientForm`
188
49
 
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
- ```
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
- #### `FormOnSubmit<T>`
55
+ ### `ActionCtrl`
219
56
 
220
- Submit handler interface for form components.
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
- ```typescript
223
- interface FormOnSubmit<T> {
224
- (data: T, update?: (data: T) => void): Promise<void> | void
225
- }
226
- ```
227
-
228
- #### `TFormContext`
63
+ ### `InputCtrl`
229
64
 
230
- Form context interface for component communication.
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
- ```typescript
233
- interface TFormContext extends Omit<FormProps, 'defaults' | 'children'> {
234
- loader: Toggleable // Loading state management
235
- }
236
- ```
70
+ ### `useFormRef(): FormRef`
237
71
 
238
- #### `FormFieldProps`
72
+ Returns a ref object for programmatic form control.
239
73
 
240
- Base properties for form field components.
74
+ ### `FormOnSubmit<T>`
241
75
 
242
76
  ```typescript
243
- interface FormFieldProps {
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
- ### 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
80
+ ### `schemaToFormDefault(schema): Record<string, any>`
776
81
 
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)
82
+ Derives default form values from an AJV schema's `default` fields.
784
83
 
785
84
  ## Related Packages
786
85
 
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
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 { ClientModule } from '@owlmeans/client-module';
2
- import type { AbstractRequest } from '@owlmeans/module';
3
- export declare const usePanelLayout: <T = {}, R extends AbstractRequest = AbstractRequest>() => ClientModule<T, R>;
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,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,cAAc,GAAI,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,OAAK,YAAY,CAAC,CAAC,EAAE,CAAC,CAKvG,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAM9D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,WAA0C,CAAA"}
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.module(location.state.alias);
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,GAA4E,EAAE;IAC1G,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,QAAQ,GAA0B,OAAO,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAA;IAEtE,OAAO,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAC7C,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"}
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.2",
3
+ "version": "0.1.4",
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.2",
40
- "@owlmeans/client-i18n": "^0.1.2",
41
- "@owlmeans/client-module": "^0.1.2",
42
- "@owlmeans/client-route": "^0.1.2",
43
- "@owlmeans/error": "^0.1.2",
44
- "@owlmeans/module": "^0.1.2"
39
+ "@owlmeans/client": "^0.1.4",
40
+ "@owlmeans/client-i18n": "^0.1.4",
41
+ "@owlmeans/client-entrypoint": "^0.1.4",
42
+ "@owlmeans/client-route": "^0.1.4",
43
+ "@owlmeans/error": "^0.1.4",
44
+ "@owlmeans/entrypoint": "^0.1.4"
45
45
  },
46
46
  "peerDependencies": {
47
- "@owlmeans/auth": "*",
48
- "@owlmeans/client-auth": "*",
49
- "@owlmeans/i18n": "*",
50
- "@owlmeans/router": "*",
47
+ "@owlmeans/auth": "^0.1.4",
48
+ "@owlmeans/client-auth": "^0.1.4",
49
+ "@owlmeans/i18n": "^0.1.4",
50
+ "@owlmeans/router": "^0.1.4",
51
51
  "ajv": "*",
52
52
  "react": "*",
53
53
  "react-hook-form": "*"
@@ -61,13 +61,14 @@
61
61
  }
62
62
  },
63
63
  "devDependencies": {
64
- "@owlmeans/auth": "^0.1.2",
65
- "@owlmeans/client-auth": "^0.1.2",
64
+ "@owlmeans/dep-config": "workspace:*",
65
+ "@owlmeans/auth": "^0.1.4",
66
+ "@owlmeans/client-auth": "^0.1.4",
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": "^5.8.3"
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 { ClientModule } from '@owlmeans/client-module'
3
- import type { AbstractRequest } from '@owlmeans/module'
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>(): ClientModule<T, R> => {
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.module(location.state.alias)
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
- "../tsconfig.default.json",
4
- "../tsconfig.react.json",
3
+ "@owlmeans/dep-config/tsconfig.base.json",
4
+ "@owlmeans/dep-config/tsconfig.react.json"
5
5
  ],
6
6
  "compilerOptions": {
7
- "rootDir": "./src/", /* Specify the root folder within your source files. */
8
- "outDir": "./build/", /* Specify an output folder for all emitted files. */
9
- "moduleResolution": "Bundler"
7
+ "rootDir": "./src/",
8
+ "outDir": "./build/"
10
9
  },
11
- "exclude": [
12
- "./dist/**/*",
13
- "./build/**/*",
14
- "./*.ts"
15
- ]
16
- }
10
+ "exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
11
+ }