@duke333/builder-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Winu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # @duke333/builder-ui
2
+
3
+ Frontend талын builder package. Энэ package нь dynamic form/list/view runtime logic,
4
+ builder admin factory components, API client factory, React Query hook factory-уудыг
5
+ эзэмшинэ.
6
+
7
+ ## Workspace Development
8
+
9
+ This repository uses the package as a workspace dependency:
10
+
11
+ ```json
12
+ {
13
+ "dependencies": {
14
+ "@duke333/builder-ui": "workspace:*"
15
+ }
16
+ }
17
+ ```
18
+
19
+ Ажиллуулах:
20
+
21
+ ```bash
22
+ pnpm install
23
+ pnpm build
24
+ pnpm typecheck
25
+ pnpm test:run
26
+ ```
27
+
28
+ ## Хэрэглэх
29
+
30
+ Host app дээр package factory-уудыг host wrapper component болгон угсарна:
31
+
32
+ ```text
33
+ src/features/builder/api.ts
34
+ src/features/builder/hooks/useBuilder.ts
35
+ src/features/builder/components/DynamicForm.tsx
36
+ src/features/builder/components/DynamicList.tsx
37
+ src/features/builder/components/DynamicView.tsx
38
+ ```
39
+
40
+ App page дээр wrapper-уудыг ашиглана:
41
+
42
+ ```tsx
43
+ import { DynamicView } from "../features/builder/components/DynamicView";
44
+
45
+ export default function RuntimePage() {
46
+ return <DynamicView viewId="..." />;
47
+ }
48
+ ```
49
+
50
+ Дангаар нь дуудах contract:
51
+
52
+ ```tsx
53
+ <DynamicForm formId="..." />
54
+ <DynamicList listId="..." />
55
+ <DynamicView viewId="..." />
56
+ ```
57
+
58
+ `DynamicView` нь form + list нийлсэн CRUD screen. Харин зөвхөн form эсвэл зөвхөн
59
+ grid/list хэрэгтэй бол `DynamicForm`, `DynamicList`-ийг шууд ашиглана.
60
+
61
+ ## Host UI Adapter
62
+
63
+ Package нь shadcn/ui-г шууд эзэмшихгүй. Host app өөрийн Button, Dialog, DataGrid,
64
+ form renderer зэрэг design-system component-уудаа adapter-аар өгнө. Ингэснээр portal
65
+ энгийн shadcn app хэвээр, builder нь нэмэлт reusable layer хэвээр үлдэнэ.
66
+
67
+ ## Publish / Update
68
+
69
+ Publish after `@duke333/builder-contracts` and `@duke333/builder-backend` are available in the
70
+ target registry. The package is configured for public npm publishing.
71
+
72
+ External portal project дээр:
73
+
74
+ ```bash
75
+ pnpm add @duke333/builder-ui
76
+ ```
77
+
78
+ Update:
79
+
80
+ ```bash
81
+ pnpm update @duke333/builder-ui
82
+ ```
83
+
84
+ Package нь `dist` output publish хийнэ. `prepack` нь publish/pack хийхээс өмнө
85
+ `pnpm build` ажиллуулна.
@@ -0,0 +1,282 @@
1
+ import * as react from 'react';
2
+ import { ComponentType, ReactNode } from 'react';
3
+ import { BuilderApiContracts } from './api/index.js';
4
+ import { BuilderHooks } from './hooks/index.js';
5
+ import { BuilderRuntimeFormSchemaLike, BuilderRuntimeFormValues, BuilderRuntimeEntryLike, BuilderRuntimeViewLike, BuilderRuntimeEntryValues } from './runtime/index.js';
6
+
7
+ type DynamicFormContracts = BuilderApiContracts & {
8
+ BuilderForm: {
9
+ id: string;
10
+ title: string;
11
+ schema: BuilderRuntimeFormSchemaLike;
12
+ };
13
+ CreateBuilderViewEntryInput: BuilderRuntimeFormValues;
14
+ };
15
+ type DynamicFormProps = {
16
+ formId: string;
17
+ };
18
+ type DynamicFormShellProps = {
19
+ title: string;
20
+ children: ReactNode;
21
+ };
22
+ type DynamicFormFieldsProps<TSchema> = {
23
+ formId: string;
24
+ schema: TSchema;
25
+ namePrefix: "values";
26
+ };
27
+ type DynamicFormSubmitProps = {
28
+ isSaving: boolean;
29
+ };
30
+ type DynamicFormErrorStateProps = {
31
+ title: string;
32
+ description: string;
33
+ };
34
+ type DynamicFormNotifications = {
35
+ success(message: string): void;
36
+ error(message: string): void;
37
+ errorMessage(error: unknown, fallback: string): string;
38
+ };
39
+ type DynamicFormDependencies<C extends DynamicFormContracts> = {
40
+ hooks: Pick<BuilderHooks<C>, "useRuntimeBuilderForm" | "useSaveRuntimeBuilderFormEntry">;
41
+ FormShell: ComponentType<DynamicFormShellProps>;
42
+ FormFields: ComponentType<DynamicFormFieldsProps<C["BuilderForm"]["schema"]>>;
43
+ SubmitButton: ComponentType<DynamicFormSubmitProps>;
44
+ ErrorState: ComponentType<DynamicFormErrorStateProps>;
45
+ notifications: DynamicFormNotifications;
46
+ };
47
+ declare function createDynamicFormComponent<C extends DynamicFormContracts>(deps: DynamicFormDependencies<C>): ({ formId }: DynamicFormProps) => react.JSX.Element | null;
48
+
49
+ type DynamicListContracts = BuilderApiContracts & {
50
+ BuilderList: {
51
+ id: string;
52
+ title: string;
53
+ description?: string | null;
54
+ };
55
+ BuilderViewEntry: {
56
+ id: string;
57
+ };
58
+ ListBuilderViewEntriesResponse: {
59
+ items: Array<BuilderApiContracts["BuilderViewEntry"]>;
60
+ total: number;
61
+ };
62
+ };
63
+ type DynamicListProps = {
64
+ listId: string;
65
+ };
66
+ type DynamicListDataGridProps<TController> = {
67
+ title?: string;
68
+ description?: string;
69
+ controller: TController;
70
+ isLoading?: boolean;
71
+ searchPlaceholder?: string;
72
+ emptyTitle?: string;
73
+ emptyDescription?: string;
74
+ };
75
+ type DynamicListErrorStateProps = {
76
+ title: string;
77
+ description: string;
78
+ };
79
+ type DynamicListDependencies<C extends DynamicListContracts, TColumn, TState extends {
80
+ query: C["ListBuilderViewEntriesQuery"];
81
+ }, TController> = {
82
+ hooks: Pick<BuilderHooks<C>, "useRuntimeBuilderList" | "useRuntimeBuilderListEntries">;
83
+ buildColumns(list: C["BuilderList"]): TColumn[];
84
+ useDataTableState(args?: {
85
+ pageSize?: number;
86
+ }): TState;
87
+ useDataTable(args: {
88
+ columns: TColumn[];
89
+ data: C["BuilderViewEntry"][];
90
+ rowCount: number;
91
+ state: TState;
92
+ getRowId(row: C["BuilderViewEntry"]): string;
93
+ }): TController;
94
+ DataGrid: ComponentType<DynamicListDataGridProps<TController>>;
95
+ ErrorState: ComponentType<DynamicListErrorStateProps>;
96
+ };
97
+ declare function createDynamicListComponent<C extends DynamicListContracts, TColumn, TState extends {
98
+ query: C["ListBuilderViewEntriesQuery"];
99
+ }, TController>(deps: DynamicListDependencies<C, TColumn, TState, TController>): ({ listId }: DynamicListProps) => react.JSX.Element;
100
+
101
+ type DynamicViewContracts = BuilderApiContracts & {
102
+ BuilderView: {
103
+ id: string;
104
+ title: string;
105
+ description?: string | null;
106
+ form?: unknown | null;
107
+ };
108
+ BuilderViewEntry: BuilderRuntimeEntryLike;
109
+ ListBuilderViewEntriesResponse: {
110
+ items: Array<BuilderApiContracts["BuilderViewEntry"]>;
111
+ total: number;
112
+ };
113
+ };
114
+ type DynamicViewProps = {
115
+ viewId: string;
116
+ };
117
+ type DynamicViewColumnActions<TEntry> = {
118
+ onEdit(entry: TEntry): void;
119
+ onDelete(entry: TEntry): void;
120
+ };
121
+ type DynamicViewDataGridProps<TController> = {
122
+ title?: string;
123
+ description?: string;
124
+ controller: TController;
125
+ isLoading?: boolean;
126
+ searchPlaceholder?: string;
127
+ emptyTitle?: string;
128
+ emptyDescription?: string;
129
+ onRowClick?: (row: unknown) => void;
130
+ toolbarExtra?: ReactNode;
131
+ };
132
+ type DynamicViewAddButtonProps = {
133
+ disabled: boolean;
134
+ onAdd(): void;
135
+ };
136
+ type DynamicViewEntryDialogProps<TView, TEntry, TInput> = {
137
+ open: boolean;
138
+ view: TView;
139
+ editing: TEntry | null;
140
+ onClose(): void;
141
+ onSave(args: {
142
+ id?: string;
143
+ input: TInput;
144
+ }): Promise<unknown>;
145
+ isSaving: boolean;
146
+ };
147
+ type DynamicViewDeleteDialogProps<TEntry> = {
148
+ entry: TEntry | null;
149
+ isDeleting: boolean;
150
+ onClose(): void;
151
+ onConfirm(): Promise<void>;
152
+ };
153
+ type DynamicViewErrorStateProps = {
154
+ title: string;
155
+ description: string;
156
+ };
157
+ type DynamicViewDependencies<C extends DynamicViewContracts, TColumn, TState extends {
158
+ query: C["ListBuilderViewEntriesQuery"];
159
+ }, TController> = {
160
+ hooks: Pick<BuilderHooks<C>, "useBuilderViewEntries" | "useDeleteBuilderViewEntry" | "useRuntimeBuilderView" | "useSaveBuilderViewEntry">;
161
+ buildColumns(view: C["BuilderView"], actions: DynamicViewColumnActions<C["BuilderViewEntry"]>): TColumn[];
162
+ useDataTableState(args?: {
163
+ pageSize?: number;
164
+ }): TState;
165
+ useDataTable(args: {
166
+ columns: TColumn[];
167
+ data: C["BuilderViewEntry"][];
168
+ rowCount: number;
169
+ state: TState;
170
+ getRowId(row: C["BuilderViewEntry"]): string;
171
+ }): TController;
172
+ DataGrid: ComponentType<DynamicViewDataGridProps<TController>>;
173
+ AddButton: ComponentType<DynamicViewAddButtonProps>;
174
+ EntryDialog: ComponentType<DynamicViewEntryDialogProps<C["BuilderView"], C["BuilderViewEntry"], C["UpdateBuilderViewEntryInput"]>>;
175
+ DeleteDialog: ComponentType<DynamicViewDeleteDialogProps<C["BuilderViewEntry"]>>;
176
+ ErrorState: ComponentType<DynamicViewErrorStateProps>;
177
+ };
178
+ declare function createDynamicViewComponent<C extends DynamicViewContracts, TColumn, TState extends {
179
+ query: C["ListBuilderViewEntriesQuery"];
180
+ }, TController>(deps: DynamicViewDependencies<C, TColumn, TState, TController>): ({ viewId }: DynamicViewProps) => react.JSX.Element;
181
+
182
+ type BuilderRuntimeDatasourceLike = {
183
+ type: "dbTable";
184
+ tableName: string;
185
+ } | {
186
+ type: string;
187
+ } | null;
188
+ type RuntimeEntryDialogSubformLike = {
189
+ key: string;
190
+ };
191
+ type RuntimeEntryDialogViewLike = BuilderRuntimeViewLike & {
192
+ id: string;
193
+ title: string;
194
+ list?: {
195
+ source?: BuilderRuntimeDatasourceLike;
196
+ } | null;
197
+ form?: {
198
+ id: string;
199
+ source?: BuilderRuntimeDatasourceLike;
200
+ schema: BuilderRuntimeFormSchemaLike & {
201
+ subforms?: readonly RuntimeEntryDialogSubformLike[];
202
+ };
203
+ } | null;
204
+ };
205
+ type RuntimeEntryDialogSaveArgs<TInput> = {
206
+ id?: string;
207
+ input: TInput;
208
+ };
209
+ type RuntimeEntryDialogProps<TView, TEntry, TInput> = {
210
+ open: boolean;
211
+ view: TView;
212
+ editing: TEntry | null;
213
+ onClose: () => void;
214
+ onSave: (args: RuntimeEntryDialogSaveArgs<TInput>) => Promise<unknown>;
215
+ isSaving: boolean;
216
+ };
217
+ type RuntimeEntryDialogShellProps = {
218
+ open: boolean;
219
+ title: string;
220
+ description: string;
221
+ onClose: () => void;
222
+ children: ReactNode;
223
+ };
224
+ type RuntimeEntryDialogExtraItem = {
225
+ key: string;
226
+ render: () => ReactNode;
227
+ };
228
+ type RuntimeEntryDialogFieldsProps<TSchema> = {
229
+ formId: string;
230
+ viewId: string;
231
+ entry?: BuilderRuntimeEntryLike | null;
232
+ schema: TSchema;
233
+ namePrefix: "values";
234
+ extraItems?: RuntimeEntryDialogExtraItem[];
235
+ };
236
+ type RuntimeEntryDialogActionsProps = {
237
+ isSaving: boolean;
238
+ onClose: () => void;
239
+ };
240
+ type RuntimeEntryDialogNotifications = {
241
+ success(message: string): void;
242
+ error(message: string): void;
243
+ errorMessage(error: unknown, fallback: string): string;
244
+ };
245
+ type RuntimeEntryDialogDraftRowsChange<TEntry> = {
246
+ subformKey: string;
247
+ dataKey: string;
248
+ rows: TEntry[];
249
+ };
250
+ type RuntimeEntryDialogSubformFieldProps<TSubform, TEntry> = {
251
+ subform: TSubform;
252
+ parentEntry?: TEntry | null;
253
+ draftRows?: TEntry[];
254
+ onDraftRowsChange?(args: RuntimeEntryDialogDraftRowsChange<TEntry>): void;
255
+ };
256
+ type RuntimeCompositeData = Record<string, Record<string, unknown> | Array<Record<string, unknown>>>;
257
+ type RuntimeCompositeCreateInput = {
258
+ data: RuntimeCompositeData;
259
+ };
260
+ type RuntimeCompositeUpdateInput = {
261
+ data: RuntimeCompositeData;
262
+ };
263
+ type RuntimeMutation<TInput> = {
264
+ mutateAsync(input: TInput): Promise<unknown>;
265
+ isPending: boolean;
266
+ };
267
+ type RuntimeEntryDialogDependencies<TView extends RuntimeEntryDialogViewLike, TEntry extends BuilderRuntimeEntryLike = BuilderRuntimeEntryLike> = {
268
+ DialogShell: ComponentType<RuntimeEntryDialogShellProps>;
269
+ FormFields: ComponentType<RuntimeEntryDialogFieldsProps<NonNullable<TView["form"]>["schema"]>>;
270
+ Actions: ComponentType<RuntimeEntryDialogActionsProps>;
271
+ formClassName?: string;
272
+ SubformField?: ComponentType<RuntimeEntryDialogSubformFieldProps<NonNullable<NonNullable<TView["form"]>["schema"]["subforms"]>[number], TEntry>>;
273
+ useCreateCompositeEntry?(viewId: string): RuntimeMutation<RuntimeCompositeCreateInput>;
274
+ useUpdateCompositeEntry?(viewId: string): RuntimeMutation<{
275
+ id: string;
276
+ input: RuntimeCompositeUpdateInput;
277
+ }>;
278
+ notifications: RuntimeEntryDialogNotifications;
279
+ };
280
+ declare function createRuntimeEntryDialogComponent<TView extends RuntimeEntryDialogViewLike, TEntry extends BuilderRuntimeEntryLike, TInput extends BuilderRuntimeEntryValues>(deps: RuntimeEntryDialogDependencies<TView, TEntry>): ({ open, view, editing, onClose, onSave, isSaving, }: RuntimeEntryDialogProps<TView, TEntry, TInput>) => react.JSX.Element;
281
+
282
+ export { type RuntimeEntryDialogSaveArgs as A, type RuntimeEntryDialogShellProps as B, type RuntimeEntryDialogViewLike as C, type DynamicFormContracts as D, createDynamicFormComponent as E, createDynamicListComponent as F, createDynamicViewComponent as G, createRuntimeEntryDialogComponent as H, type RuntimeEntryDialogDraftRowsChange as R, type DynamicFormDependencies as a, type DynamicFormErrorStateProps as b, type DynamicFormFieldsProps as c, type DynamicFormNotifications as d, type DynamicFormProps as e, type DynamicFormShellProps as f, type DynamicFormSubmitProps as g, type DynamicListContracts as h, type DynamicListDataGridProps as i, type DynamicListDependencies as j, type DynamicListErrorStateProps as k, type DynamicListProps as l, type DynamicViewAddButtonProps as m, type DynamicViewColumnActions as n, type DynamicViewContracts as o, type DynamicViewDataGridProps as p, type DynamicViewDeleteDialogProps as q, type DynamicViewDependencies as r, type DynamicViewEntryDialogProps as s, type DynamicViewErrorStateProps as t, type DynamicViewProps as u, type RuntimeEntryDialogActionsProps as v, type RuntimeEntryDialogDependencies as w, type RuntimeEntryDialogFieldsProps as x, type RuntimeEntryDialogNotifications as y, type RuntimeEntryDialogProps as z };