denwa-react-shared 1.0.88 → 1.0.90

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/package.json CHANGED
@@ -1,13 +1,25 @@
1
1
  {
2
2
  "name": "denwa-react-shared",
3
3
  "private": false,
4
- "version": "1.0.88",
4
+ "version": "1.0.90",
5
5
  "type": "module",
6
6
  "author": "Denwa",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Denwa799/react-shared.git"
10
+ },
11
+ "keywords": [
12
+ "react",
13
+ "shared",
14
+ "admin",
15
+ "tanstack-intent"
16
+ ],
7
17
  "main": "dist/denwa-react-shared.umd.js",
8
18
  "module": "dist/denwa-react-shared.es.js",
9
19
  "files": [
10
- "dist"
20
+ "dist",
21
+ "skills",
22
+ "!skills/_artifacts"
11
23
  ],
12
24
  "types": "dist/index.d.ts",
13
25
  "exports": {
@@ -63,6 +75,7 @@
63
75
  "@types/react": "^19.1.6",
64
76
  "@types/react-dom": "^19.1.6",
65
77
  "@types/validator": "^13.15.1",
78
+ "@tanstack/intent": "latest",
66
79
  "@vitejs/plugin-react-swc": "^3.10.1",
67
80
  "autoprefixer": "^10.4.21",
68
81
  "eslint": "^9.28.0",
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: admin-forms
3
+ description: >
4
+ Implement entity editor forms using DrawerForm and specialized inputs.
5
+ Covers form submission, language switching, and integration with
6
+ AdminTable actions.
7
+ type: framework
8
+ library: denwa-react-shared
9
+ framework: react
10
+ library_version: "1.0.88"
11
+ requires:
12
+ - session-auth
13
+ ---
14
+
15
+ # Drawer Forms & Inputs
16
+
17
+ Most entity management in the admin panel happens within `Drawer` panels using `BaseDrawerForm`. This component standardizes the appearance of save buttons and multi-language controls.
18
+
19
+ ## Setup
20
+
21
+ ```tsx
22
+ import { BaseDrawerForm, AdminDrawerFormProps } from 'denwa-react-shared';
23
+
24
+ const UserForm = ({ id, action, onClose, onRefetch }: AdminDrawerFormProps<'create' | 'update' | 'read'>) => {
25
+ const { control, handleSubmit } = useForm();
26
+
27
+ const isReadOnly = action === 'read';
28
+
29
+ return (
30
+ <BaseDrawerForm
31
+ saveText="Сохранить"
32
+ submitButtonText={action === 'create' ? 'Создать' : 'Обновить'}
33
+ isVisibleSubmit={!isReadOnly}
34
+ onSubmitClick={handleSubmit(onSave)}
35
+ >
36
+ <Controller
37
+ name="name"
38
+ control={control}
39
+ render={({ field }) => <Input {...field} disabled={isReadOnly} />}
40
+ />
41
+ {/* ... form content */}
42
+ </BaseDrawerForm>
43
+ );
44
+ };
45
+ ```
46
+
47
+ ## Core Patterns
48
+
49
+ ### Language Switching
50
+ If the entity supports multiple languages, enable `isVisibleLanguage` and provide `languagesData`.
51
+
52
+ ```tsx
53
+ <BaseDrawerForm
54
+ isVisibleLanguage
55
+ language={currentLang}
56
+ languagesData={[
57
+ { label: 'Русский', value: 'ru' },
58
+ { label: 'English', value: 'en' },
59
+ ]}
60
+ onChangeLang={(lang) => setLang(lang)}
61
+ // Optional: Bulk translation button
62
+ isVisibleLanguageButton
63
+ translateAllText="Перевести все"
64
+ onTranslateAllClick={handleTranslate}
65
+ >
66
+ {/* ... fields */}
67
+ </BaseDrawerForm>
68
+ ```
69
+
70
+ ### Integration with AdminTable
71
+ The `AdminTable` orchestration passes `id`, `action`, `onClose`, and `onRefetch` to the `drawerContent` component.
72
+
73
+ ```tsx
74
+ // In AdminTable:
75
+ <AdminTable
76
+ drawerContent={UserForm}
77
+ readAction="read"
78
+ createAction="create"
79
+ updateAction="update"
80
+ // ...
81
+ />
82
+ ```
83
+
84
+ ## Common Mistakes
85
+
86
+ ### MEDIUM Using raw antd Form
87
+ Wrong:
88
+ ```tsx
89
+ import { Form } from 'antd';
90
+ return <Form>{children}</Form>;
91
+ ```
92
+ Correct:
93
+ ```tsx
94
+ import { BaseDrawerForm } from 'denwa-react-shared';
95
+ return <BaseDrawerForm>{children}</BaseDrawerForm>;
96
+ ```
97
+ `BaseDrawerForm` wraps `antd` Form but adds standardized footer buttons and integrated logic for language switching and submission loading states.
98
+
99
+ Source: maintainer interview
100
+
101
+ ### HIGH Mismanaging "read" action state
102
+ Wrong:
103
+ ```tsx
104
+ const UserForm = ({ action }) => {
105
+ return <BaseDrawerForm>{/* inputs are always enabled */}</BaseDrawerForm>;
106
+ };
107
+ ```
108
+ Correct:
109
+ ```tsx
110
+ const UserForm = ({ action }) => {
111
+ const isReadOnly = action === 'read';
112
+ return (
113
+ <BaseDrawerForm isVisibleSubmit={!isReadOnly}>
114
+ <Input disabled={isReadOnly} />
115
+ </BaseDrawerForm>
116
+ );
117
+ };
118
+ ```
119
+ When `action` is 'read', the form should visually hide the submit button and disable all input fields.
120
+
121
+ Source: src/shared/ui/admin-table/index.tsx
@@ -0,0 +1,153 @@
1
+ ---
2
+ name: admin-table
3
+ description: >
4
+ Implement complex data tables using AdminTable and useFetchTableData.
5
+ Covers columns configuration, server-side filtering, sorting,
6
+ and search state mapping.
7
+ type: framework
8
+ library: denwa-react-shared
9
+ framework: react
10
+ library_version: "1.0.88"
11
+ requires:
12
+ - session-auth
13
+ sources:
14
+ - "Denwa799/react-shared:src/shared/ui/admin-table/index.tsx"
15
+ - "Denwa799/react-shared:src/shared/lib/hooks/use-fetch-table-data.ts"
16
+ ---
17
+
18
+ # Admin Table & Data Fetching
19
+
20
+ The `AdminTable` component is the centerpiece for building resource lists. It works in conjunction with `useFetchTableData` to map UI states (search, sort, page) to API filters.
21
+
22
+ ## Setup
23
+
24
+ ```tsx
25
+ import { AdminTable, useFetchTableData } from 'denwa-react-shared';
26
+ import { ColumnsType } from 'antd/es/table';
27
+
28
+ const UsersPage = () => {
29
+ // 1. Setup state setters (usually from nuqs or local state)
30
+ const [order, setOrder] = useState('createdAtDesc');
31
+ const [search, setSearch] = useState('');
32
+
33
+ // 2. Use hook to get mapped filters/sort for API
34
+ const { filter, sort, onChangeOrder, ...searchHandlers } = useFetchTableData({
35
+ order,
36
+ search,
37
+ onSetOrder: setOrder,
38
+ onSetSearch: setSearch,
39
+ // ... other setters for pagination, radio, date search
40
+ sortFields: [{ key: 'createdAtDesc', field: 'createdAt', order: 'desc' }],
41
+ searchFields: ['name', 'email'],
42
+ // ... field configuration
43
+ });
44
+
45
+ // 3. Render table
46
+ return (
47
+ <AdminTable
48
+ tableData={data}
49
+ columns={columns}
50
+ order={order}
51
+ onChangeOrder={onChangeOrder}
52
+ searchProps={{
53
+ ...searchHandlers,
54
+ // ... text labels
55
+ }}
56
+ // ... actions and text props
57
+ />
58
+ );
59
+ };
60
+ ```
61
+
62
+ ## Core Patterns
63
+
64
+ ### Configuring Search and Filters
65
+ `searchProps` expects a `TableSearchProps` object. Ensure you provide the `datePickerComponent` and all required handlers from `useFetchTableData`.
66
+
67
+ ```tsx
68
+ <AdminTable
69
+ searchProps={{
70
+ datePickerComponent: BaseDatePicker,
71
+ searchText: 'Поиск',
72
+ noDateText: 'Нет даты',
73
+ emptyText: 'Ничего не найдено',
74
+ searchSelect: currentSearchField,
75
+ radioOptions: [{ label: 'Все', value: 'all' }],
76
+ searchOptions: [{ label: 'Имя', value: 'name' }],
77
+ searchType: 'text',
78
+ onChangeSearch: handleFieldChange,
79
+ onChangeTextSearch: handleTextChange,
80
+ }}
81
+ // ...
82
+ />
83
+ ```
84
+
85
+ ### Server-Side Pagination
86
+ The table expects `serverPagination` prop of type `IPaginate`.
87
+
88
+ ```tsx
89
+ <AdminTable
90
+ serverPagination={{
91
+ total: totalItems,
92
+ page: currentPage,
93
+ limit: pageSize,
94
+ }}
95
+ onSetPaginate={(page, limit) => {
96
+ setPage(page);
97
+ setLimit(limit);
98
+ }}
99
+ />
100
+ ```
101
+
102
+ ## Common Mistakes
103
+
104
+ ### HIGH Hallucinating internal state management
105
+ Wrong:
106
+ ```tsx
107
+ // Trying to use useFetchTableData without passing external setters
108
+ const tableInfo = useFetchTableData({
109
+ order: 'desc', // Static value
110
+ // Missing onSetOrder, onSetSearch, etc.
111
+ });
112
+ ```
113
+ Correct:
114
+ ```tsx
115
+ const [order, setOrder] = useState('desc');
116
+ const tableInfo = useFetchTableData({
117
+ order,
118
+ onSetOrder: setOrder,
119
+ // Must provide setters to allow the hook to update external state
120
+ });
121
+ ```
122
+ The hook acts as a logic mapper and relies on external state (like `nuqs` or `useState`) to persist changes.
123
+
124
+ Source: maintainer interview
125
+
126
+ ### HIGH Passings wrong pagination format
127
+ Wrong:
128
+ ```tsx
129
+ // Using antd internal pagination object
130
+ <AdminTable serverPagination={{ current: 1, pageSize: 10 }} />
131
+ ```
132
+ Correct:
133
+ ```tsx
134
+ <AdminTable serverPagination={{ page: 1, limit: 10, total: 100 }} />
135
+ ```
136
+ `AdminTable` expects a custom `IPaginate` shape, not the standard Ant Design pagination object.
137
+
138
+ Source: src/shared/ui/admin-table/types.ts
139
+
140
+ ### MEDIUM Using raw antd Table
141
+ Wrong:
142
+ ```tsx
143
+ import { Table } from 'antd';
144
+ return <Table dataSource={data} columns={columns} />;
145
+ ```
146
+ Correct:
147
+ ```tsx
148
+ import { AdminTable } from 'denwa-react-shared';
149
+ return <AdminTable tableData={data} columns={columns} ... />;
150
+ ```
151
+ Using raw `Table` loses integrated search UI, standardized skeletons, and the "Drawer Form" orchestration.
152
+
153
+ Source: maintainer interview
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: material-map
3
+ description: >
4
+ Integrate Yandex Maps for coordinate selection. Covers map initialization,
5
+ setting markers, and coordinate synchronization.
6
+ type: framework
7
+ library: denwa-react-shared
8
+ framework: react
9
+ library_version: "1.0.88"
10
+ requires:
11
+ - session-auth
12
+ sources:
13
+ - "Denwa799/react-shared:src/shared/ui/material-map/index.tsx"
14
+ ---
15
+
16
+ # Yandex Maps Integration
17
+
18
+ `BaseMaterialMap` uses Yandex Maps to allow users to select geographic coordinates. It requires a Yandex Maps API key and provides a simple interface for single-point selection.
19
+
20
+ ## Setup
21
+
22
+ ```tsx
23
+ import { BaseMaterialMap } from 'denwa-react-shared';
24
+
25
+ const LocationPicker = () => {
26
+ return (
27
+ <BaseMaterialMap
28
+ apiKey="your-yandex-api-key"
29
+ value={{ lat: 55.751244, lng: 37.618423 }}
30
+ onChange={(coords) => setLocation(coords)}
31
+ height={400}
32
+ zoom={10}
33
+ noDataText="Координаты не выбраны"
34
+ />
35
+ );
36
+ };
37
+ ```
38
+
39
+ ## Core Patterns
40
+
41
+ ### Manual Coordinate Entry
42
+ The component displays the current coordinates in inputs. Users can either click on the map or type values manually. Always ensure `onChange` is provided to capture changes from both sources.
43
+
44
+ ### Custom Height and Styling
45
+ The map container should be constrained by the `height` prop. It defaults to `100%` width of the parent container.
46
+
47
+ ## Common Mistakes
48
+
49
+ ### CRITICAL Missing API Key
50
+ Wrong:
51
+ ```tsx
52
+ <BaseMaterialMap value={coords} />
53
+ ```
54
+ Correct:
55
+ ```tsx
56
+ <BaseMaterialMap apiKey={import.meta.env.VITE_YAMAP_KEY} value={coords} />
57
+ ```
58
+ The map uses `@iminside/react-yandex-maps` internally. If the `apiKey` is missing or invalid, the map will fail to load or will show a "Demo mode" watermark.
59
+
60
+ Source: src/shared/ui/material-map/index.tsx
61
+
62
+ ### HIGH Forgetting default center for empty values
63
+ If the `value` is undefined, the map might center on a default (often 0,0) or fail. Always provide a fallback `defaultCenter` or check for existence.
64
+
65
+ Source: maintainer interview
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: media-management
3
+ description: >
4
+ Handle single/multiple image uploads and file attachments.
5
+ Covers drag-and-drop sorting, temporary upload patterns,
6
+ and media deletion.
7
+ type: framework
8
+ library: denwa-react-shared
9
+ framework: react
10
+ library_version: "1.0.88"
11
+ requires:
12
+ - session-auth
13
+ sources:
14
+ - "Denwa799/react-shared:src/shared/ui/image-upload/base-image-upload.tsx"
15
+ - "Denwa799/react-shared:src/shared/ui/file-upload/file-upload.tsx"
16
+ ---
17
+
18
+ # Media & File Management
19
+
20
+ The library provides `BaseImageUpload` for image galleries and `FileUpload` for general documents. Both rely on a two-step process: uploading to a temporary location first, then associating the path with the entity.
21
+
22
+ ## Setup
23
+
24
+ ### Image Upload with Sorting
25
+ ```tsx
26
+ import { BaseImageUpload, TAdminImage } from 'denwa-react-shared';
27
+
28
+ const Gallery = () => {
29
+ return (
30
+ <BaseImageUpload
31
+ isMultiple
32
+ label="Галерея"
33
+ images={images}
34
+ onUpdateTempImage={handleTempUpload}
35
+ onUpdateItems={handleReorder}
36
+ onUpdateDeleteItems={handleDelete}
37
+ />
38
+ );
39
+ };
40
+ ```
41
+
42
+ ## Core Patterns
43
+
44
+ ### Two-Step Upload Process
45
+ Components emit native File objects. You must upload these to a temporary storage API and return the resulting path to the component's state.
46
+
47
+ ```tsx
48
+ const handleTempUpload = async (file: File) => {
49
+ const formData = new FormData();
50
+ formData.append('file', file);
51
+
52
+ const response = await api.upload(formData); // Your API
53
+ return response.path; // e.g. "/temp/random-id.png"
54
+ };
55
+ ```
56
+
57
+ ### File Attachments
58
+ For non-image files, use `FileUpload`. It supports single or multiple files with size/extension limits.
59
+
60
+ ```tsx
61
+ import { FileUpload } from 'denwa-react-shared';
62
+
63
+ <FileUpload
64
+ label="Документ"
65
+ onUpload={handleFileUpload}
66
+ onRemove={handleFileRemove}
67
+ items={data.files}
68
+ isMultiple={false}
69
+ />
70
+ ```
71
+
72
+ ## Common Mistakes
73
+
74
+ ### CRITICAL Forgetting to handle reordering
75
+ Wrong:
76
+ ```tsx
77
+ // Only handling uploads
78
+ <BaseImageUpload onUpdateTempImage={upload} />
79
+ ```
80
+ Correct:
81
+ ```tsx
82
+ <BaseImageUpload
83
+ onUpdateTempImage={upload}
84
+ onUpdateItems={(newItems) => setImages(newItems)}
85
+ />
86
+ ```
87
+ If `onUpdateItems` is missing, drag-and-drop reordering will visually work but won't persist to state.
88
+
89
+ Source: src/shared/ui/image-upload/base-image-upload.tsx
90
+
91
+ ### HIGH Passing only URLs instead of Image objects
92
+ Wrong:
93
+ ```tsx
94
+ <BaseImageUpload images={["/image1.png"]} />
95
+ ```
96
+ Correct:
97
+ ```tsx
98
+ <BaseImageUpload images={[{ id: 1, path: "/image1.png" }]} />
99
+ ```
100
+ `BaseImageUpload` expects an array of objects containing `id` and `path`, not raw strings.
101
+
102
+ Source: src/shared/ui/image-upload/types.ts
103
+
104
+ ### MEDIUM Missing upload button due to limits
105
+ If the `limit` prop is reached, the "Upload" button disappears. Ensure you communicate this UI behavior or provide a way to delete items.
106
+
107
+ Source: src/shared/ui/image-upload/base-image-upload.tsx:55
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: session-auth
3
+ description: >
4
+ Manage authentication sessions, parse server responses, and validate identity
5
+ cookies using Zod schemas. Use for handling login flow results and
6
+ session integrity checks.
7
+ type: core
8
+ library: denwa-react-shared
9
+ library_version: "1.0.88"
10
+ sources:
11
+ - "Denwa799/react-shared:src/shared/schemas/index.ts"
12
+ ---
13
+
14
+ # Session & Auth Management
15
+
16
+ This skill covers the standardization of server responses and session state validation using Zod schemas. All API interactions should be validated against these schemas to ensure data consistency.
17
+
18
+ ## Setup
19
+
20
+ ```typescript
21
+ import { responseSchema, sessionCookieSchema } from 'denwa-react-shared';
22
+
23
+ // Example: Validating an API response
24
+ const handleApiResponse = (data: unknown) => {
25
+ const result = responseSchema.safeParse(data);
26
+ if (!result.success) {
27
+ console.error('API format mismatch:', result.error);
28
+ return;
29
+ }
30
+ return result.data;
31
+ };
32
+ ```
33
+
34
+ ## Core Patterns
35
+
36
+ ### Parsing Session Cookies
37
+ Use `sessionCookieSchema` to validate the structure of the authentication cookie before using it in the application.
38
+
39
+ ```typescript
40
+ import { sessionCookieSchema } from 'denwa-react-shared';
41
+
42
+ const validateSession = (cookieData: unknown) => {
43
+ const session = sessionCookieSchema.parse(cookieData);
44
+ return {
45
+ isLoggedIn: !!session.tokens.accessToken.token,
46
+ userRoles: session.roles,
47
+ isAdmin: session.roles.includes('admin'),
48
+ };
49
+ };
50
+ ```
51
+
52
+ ### Handling Standard Error Envelopes
53
+ The `responseSchema` includes a structured `error` field. Always check for both the top-level `error` object and `statusCode`.
54
+
55
+ ```typescript
56
+ import { responseSchema } from 'denwa-react-shared';
57
+
58
+ async function fetchData(url: string) {
59
+ const response = await fetch(url);
60
+ const data = await response.json();
61
+
62
+ const parsed = responseSchema.parse(data);
63
+ if (parsed.error) {
64
+ throw new Error(parsed.error.message || 'Unknown error');
65
+ }
66
+
67
+ return parsed.data;
68
+ }
69
+ ```
70
+
71
+ ## Common Mistakes
72
+
73
+ ### MEDIUM Manually parsing cookies without schema
74
+ Wrong:
75
+ ```typescript
76
+ const id = JSON.parse(cookies.get('session')).id;
77
+ ```
78
+ Correct:
79
+ ```typescript
80
+ const session = sessionCookieSchema.parse(cookies.get('session'));
81
+ const id = session.id;
82
+ ```
83
+ Manually accessing properties bypasses validation and creates runtime risks if the session structure changes.
84
+
85
+ Source: maintainer interview
86
+
87
+ ### HIGH Ignoring the data/response duality
88
+ Wrong:
89
+ ```typescript
90
+ // Expecting data to always be the result
91
+ const users = response.data;
92
+ ```
93
+ Correct:
94
+ ```typescript
95
+ // Checking both data and any legacy response fields
96
+ const users = response.data ?? response.response;
97
+ ```
98
+ The `responseSchema` allows for flexibility in return fields (`data` vs `response`). Agents should account for both.
99
+
100
+ Source: src/shared/schemas/index.ts
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: text-editor
3
+ description: >
4
+ Implement rich text editing using BaseTextEditor (Slate.js).
5
+ Covers content synchronization (JSON and HTML) and toolbar configuration.
6
+ type: framework
7
+ library: denwa-react-shared
8
+ framework: react
9
+ library_version: "1.0.88"
10
+ requires:
11
+ - session-auth
12
+ sources:
13
+ - "Denwa799/react-shared:src/shared/ui/text-editor/text-editor.tsx"
14
+ ---
15
+
16
+ # Slate Rich Text Editor
17
+
18
+ `BaseTextEditor` provides a rich text editing experience based on Slate.js. It handles serialization to HTML and synchronization with external form state through debounced handlers.
19
+
20
+ ## Setup
21
+
22
+ ```tsx
23
+ import { BaseTextEditor, BaseTextEditorRefMethods } from 'denwa-react-shared';
24
+
25
+ const MyForm = () => {
26
+ const editorRef = useRef<BaseTextEditorRefMethods>(null);
27
+
28
+ return (
29
+ <BaseTextEditor
30
+ boldText="Жирный"
31
+ italicText="Курсив"
32
+ underlineText="Подчеркнутый"
33
+ // ... more labels
34
+ onSetContent={(jsonString, lang) => setFieldValue('content', jsonString)}
35
+ onSetHtml={(htmlString, lang) => setFieldValue('contentHtml', htmlString)}
36
+ onErrorMessage={(msg) => alert(msg)}
37
+ />
38
+ );
39
+ };
40
+ ```
41
+
42
+ ## Core Patterns
43
+
44
+ ### Content Initialization
45
+ To set the editor value (e.g. when loading an existing entity), use the `setValue` method on the ref. It expects the serialized JSON string found in the database.
46
+
47
+ ```tsx
48
+ useEffect(() => {
49
+ if (data?.content) {
50
+ editorRef.current?.setValue(data.content);
51
+ }
52
+ }, [data]);
53
+ ```
54
+
55
+ ### Syncing JSON and HTML
56
+ The editor emits events for both the raw Slate JSON structure (for editing) and the rendered HTML (for display/SEO). Always provide both `onSetContent` and `onSetHtml`.
57
+
58
+ ```tsx
59
+ <BaseTextEditor
60
+ onSetContent={(json) => updateJson(json)}
61
+ onSetHtml={(html) => updateHtml(html)}
62
+ />
63
+ ```
64
+
65
+ ## Common Mistakes
66
+
67
+ ### HIGH Passing raw HTML to setValue
68
+ Wrong:
69
+ ```tsx
70
+ editorRef.current?.setValue("<p>Hello world</p>");
71
+ ```
72
+ Correct:
73
+ ```tsx
74
+ editorRef.current?.setValue(JSON.stringify([{ type: 'paragraph', children: [{ text: 'Hello world' }] }]));
75
+ ```
76
+ `setValue` expects a serialized JSON string representing the Slate document structure. Passing raw HTML will cause parsing errors or an empty editor.
77
+
78
+ Source: src/shared/ui/text-editor/text-editor.tsx:113
79
+
80
+ ### MEDIUM Missing synchronization handlers
81
+ Wrong:
82
+ ```tsx
83
+ <BaseTextEditor onSetHtml={(html) => setHtml(html)} />
84
+ // Missing onSetContent
85
+ ```
86
+ Correct:
87
+ ```tsx
88
+ <BaseTextEditor
89
+ onSetContent={(json) => setJson(json)}
90
+ onSetHtml={(html) => setHtml(html)}
91
+ />
92
+ ```
93
+ Failing to provide `onSetContent` prevents the application from saving the internal editor state, making future edits impossible even if the HTML is saved.
94
+
95
+ Source: src/shared/ui/text-editor/text-editor.tsx:83
96
+
97
+ ### MEDIUM Ignoring the debounce
98
+ The editor uses a 1-second debounce (`TIME.seconds.seconds1`) before firing sync events. Do not expect immediate state updates on every keystroke.
99
+
100
+ Source: src/shared/ui/text-editor/text-editor.tsx:86
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: utility-hooks
3
+ description: >
4
+ Essential helper hooks for responsive design, provider composition,
5
+ and application lifecycle management.
6
+ type: framework
7
+ library: denwa-react-shared
8
+ framework: react
9
+ library_version: "1.0.88"
10
+ requires:
11
+ - session-auth
12
+ ---
13
+
14
+ # Shared Utilities & Hooks
15
+
16
+ Standardized helpers to reduce boilerplate in provider setup and responsive logic.
17
+
18
+ ## Setup
19
+
20
+ ```tsx
21
+ import { ProviderComposer, useViewPort } from 'denwa-react-shared';
22
+
23
+ const App = ({ children }) => {
24
+ return (
25
+ <ProviderComposer
26
+ providers={[
27
+ <AuthProvider key="auth" />,
28
+ <ConfigProvider key="config" />,
29
+ <ReactQueryProvider key="query" />,
30
+ ]}
31
+ >
32
+ {children}
33
+ </ProviderComposer>
34
+ );
35
+ };
36
+ ```
37
+
38
+ ## Core Patterns
39
+
40
+ ### Responsive Logic with useViewPort
41
+ Standardizes breakpoints across the admin panel.
42
+
43
+ ```tsx
44
+ const { isMobile, isTablet, isLaptop } = useViewPort();
45
+
46
+ return (
47
+ <div>
48
+ {isMobile ? <MobileNav /> : <DesktopNav />}
49
+ </div>
50
+ );
51
+ ```
52
+
53
+ ### Local Storage Wrapper
54
+ Safe wrappers for browser storage with type-safety.
55
+
56
+ ```tsx
57
+ import { getStorageItem, setStorageItem } from 'denwa-react-shared';
58
+
59
+ const theme = getStorageItem('admin_theme', 'light');
60
+ setStorageItem('admin_theme', 'dark');
61
+ ```
62
+
63
+ ## Common Mistakes
64
+
65
+ ### MEDIUM Pyramid of doom in Providers
66
+ Wrong:
67
+ ```tsx
68
+ <Auth>
69
+ <Config>
70
+ <Query>
71
+ <Layout>{children}</Layout>
72
+ </Query>
73
+ </Config>
74
+ </Auth>
75
+ ```
76
+ Correct:
77
+ ```tsx
78
+ <ProviderComposer providers={[<Auth/>, <Config/>, <Query/>]}>
79
+ <Layout>{children}</Layout>
80
+ </ProviderComposer>
81
+ ```
82
+ `ProviderComposer` simplifies deep nesting of React providers, making the root file more readable.
83
+
84
+ Source: src/shared/lib/provider-composer/index.tsx
85
+
86
+ ### MEDIUM Hardcoding breakpoints
87
+ Wrong:
88
+ ```tsx
89
+ const isMobile = useMediaQuery('(max-width: 768px)');
90
+ ```
91
+ Correct:
92
+ ```tsx
93
+ const { isMobile } = useViewPort();
94
+ ```
95
+ Internal library components (like `AdminTable`) use breakpoints from `useViewPort`. Custom components should use the same hook to ensure consistent responsive behavior.
96
+
97
+ Source: src/shared/lib/hooks/use-view-port.ts