@ecomcx/nexus-admin-sdk 0.0.0-stage → 1.24.1

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/dist/host.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { CmsAdmin } from "./types.js";
2
+ /** Admin host đặt object này vào globalThis.__CMS_ADMIN__ trước khi nạp bundle của plugin. */
3
+ export declare function host(): CmsAdmin;
package/dist/host.js ADDED
@@ -0,0 +1,7 @@
1
+ /** Admin host đặt object này vào globalThis.__CMS_ADMIN__ trước khi nạp bundle của plugin. */
2
+ export function host() {
3
+ const h = globalThis.__CMS_ADMIN__;
4
+ if (!h)
5
+ throw new Error("@ecomcx/nexus-admin-sdk: chỉ chạy được bên trong trang quản trị CMS");
6
+ return h;
7
+ }
@@ -0,0 +1,406 @@
1
+ import type { AdminPage, AskOptions, DashboardWidget, Density, MetaBox, TableSettings } from "./types.js";
2
+ export * from "./types.js";
3
+ export declare const registerPage: (page: AdminPage) => void;
4
+ export declare const registerMetaBox: (box: MetaBox) => void;
5
+ /** Sidebar riêng trong trình soạn bài, kèm nút trên thanh hành động (CMS cũ chưa hỗ trợ thì bỏ qua). */
6
+ export declare const registerEditorSidebar: (s: import("./types.js").EditorSidebar) => void | undefined;
7
+ export declare const registerDashboardWidget: (w: DashboardWidget) => void;
8
+ /** Thêm cột vào danh sách bài viết/trang (vd điểm SEO). */
9
+ export declare const registerPostListColumn: (c: import("./types.js").PostListColumn) => void;
10
+ /** Thêm hộp vào form sửa chuyên mục/thẻ (dữ liệu lưu trong term.meta). */
11
+ export declare const registerTermMetaBox: (b: import("./types.js").TermMetaBox) => void;
12
+ /** Lấy các MetaBox đã đăng ký trong hệ thống (tùy chọn lọc theo postType). */
13
+ export declare const getMetaBoxes: (postType?: string) => MetaBox[];
14
+ /** Hook lấy toàn bộ state plugin UI trong host. */
15
+ export declare const usePluginUi: () => any;
16
+ export declare const useApi: <T = any>(path: string | null) => {
17
+ data: T | undefined;
18
+ loading: boolean;
19
+ error: Error | null;
20
+ reload(): void;
21
+ };
22
+ export declare const can: (capability: string) => boolean;
23
+ /**
24
+ * Khối cho trình soạn nội dung, khai báo bằng defineBlock (file dùng chung với phần website của plugin):
25
+ * trình soạn tự có mục slash menu, khung khối và form cài đặt sinh từ attributes. Nhãn "%khoá%" dịch theo
26
+ * locales của plugin khi tên khối có tiền tố "slug/" (vd "lien-he/callout").
27
+ */
28
+ export declare const registerBlock: (def: import("@ecomcx/nexus-content").BlockDefinition, options?: import("./types.js").RegisterBlockOptions) => void;
29
+ /**
30
+ * Thêm panel vào tab "Khối" của trình soạn cho một số loại khối (kể cả khối lõi như "image", "button") hoặc mọi khối:
31
+ * registerBlockPanel({ id: "seo/link-rel", title: "%link.title%", blocks: ["button"], attributes: { sponsored: { default: false } }, component: Panel })
32
+ * CMS cũ (chưa có tab "Khối") → bỏ qua, không lỗi.
33
+ */
34
+ export declare const registerBlockPanel: (panel: import("./types.js").BlockPanel) => void | undefined;
35
+ /**
36
+ * Thêm mẫu (đoạn dựng sẵn) vào tab "Mẫu" của trình chèn:
37
+ * registerPattern({ name: "shop/product-hero", title: "%pattern.hero%", category: "hero", content: { type: "doc", content: [...] } })
38
+ * CMS cũ (chưa có tab Mẫu) → bỏ qua, không lỗi.
39
+ */
40
+ export declare const registerPattern: (pattern: import("./types.js").EditorPattern) => void | undefined;
41
+ export { defineBlock, type BlockDefinition, type BlockDefinitionInput, type BlockAttrSpec } from "@ecomcx/nexus-content";
42
+ /** Thêm node/mark TipTap cho trình soạn (cấp thấp — khối thường dùng registerBlock). */
43
+ export declare const registerEditorExtension: (pack: import("./types.js").EditorExtensionPack) => void;
44
+ /** Lớp TipTap dùng chung với admin (không cần cài @tiptap/core trong plugin). */
45
+ export declare const tiptap: {
46
+ Node: any;
47
+ Mark: any;
48
+ Extension: any;
49
+ mergeAttributes: (...attrs: any[]) => Record<string, any>;
50
+ };
51
+ export declare const currentUser: () => {
52
+ id: number;
53
+ displayName: string;
54
+ capabilities: string[];
55
+ } | null;
56
+ /**
57
+ * Công bố hàm cho plugin khác dùng trong admin (gọi ở đầu bundle): exposeApi({ openCartDrawer, formatPrice })
58
+ * Bên dùng: const shop = await waitForPluginApi<ShopAdminApi>("shop"); shop?.formatPrice(1000)
59
+ */
60
+ export declare const exposeApi: (api: Record<string, unknown>) => void | undefined;
61
+ /** Hàm plugin khác đã công bố (undefined nếu plugin chưa bật hoặc bundle chưa nạp xong). */
62
+ export declare const getPluginApi: <T = Record<string, unknown>>(slug: string) => T | undefined;
63
+ /** Chờ plugin khác nạp xong rồi lấy hàm (undefined nếu plugin không bật). */
64
+ export declare const waitForPluginApi: <T = Record<string, unknown>>(slug: string, timeoutMs?: number) => Promise<T | undefined>;
65
+ /**
66
+ * Dịch chuỗi giao diện theo khoá (ICU MessageFormat). Bản dịch của plugin đặt trong gói: locales/vi.json, locales/en.json
67
+ * — CMS tự nạp với namespace = slug plugin:
68
+ * locales/en.json: { "form.title": "Contact", "form.count": "{count, plural, one {# message} other {# messages}}" }
69
+ * const t = createT("lien-he"); t("form.title") → "Contact"; t("form.count", { count: 3 }) → "3 messages"
70
+ * Khoá có ":" là khoá đầy đủ ("core:common.save" — nhãn chung của lõi, "plugin-khac:x").
71
+ */
72
+ export declare const t: (key: string, values?: Record<string, unknown>) => string;
73
+ /** Hàm dịch gắn sẵn namespace của plugin. */
74
+ export declare const createT: (namespace: string) => (key: string, values?: Record<string, unknown>) => string;
75
+ /** Thêm bản dịch bằng mã (ngoài locales/*.json), vd bản dịch tải từ API. */
76
+ export declare const registerMessages: (namespace: string, locale: string, dict: Record<string, string>) => void | undefined;
77
+ /** @deprecated Chuỗi gốc tiếng Việt làm khoá — dùng t()/createT() với locales/*.json. */
78
+ export declare const __: (text: string, ...args: unknown[]) => string;
79
+ /** @deprecated Dùng locales/*.json hoặc registerMessages(). */
80
+ export declare const registerTranslations: (locale: string, dict: Record<string, string>) => void;
81
+ export declare const adminLocale: () => string;
82
+ export declare const api: {
83
+ get: <T = any>(p: string) => Promise<T>;
84
+ post: <T = any>(p: string, b?: unknown) => Promise<T>;
85
+ put: <T = any>(p: string, b?: unknown) => Promise<T>;
86
+ del: <T = any>(p: string) => Promise<T>;
87
+ upload: <T = any>(p: string, f: File, field?: string) => Promise<T>;
88
+ };
89
+ export declare const notify: {
90
+ success: (m: string) => void;
91
+ error: (m: string) => void;
92
+ info: (m: string) => void;
93
+ };
94
+ export declare const registerSlot: (slot: import("./types.js").AdminSlotItem) => void;
95
+ export declare const getSlots: (slot: string, postType?: string) => import("./types.js").AdminSlotItem[];
96
+ export declare const registerPostRowAction: (a: import("./types.js").PostRowAction) => void;
97
+ /** Thao tác hàng loạt trên danh sách bài (CMS ≥ 1.2.0). */
98
+ export declare const registerPostBulkAction: (a: import("./types.js").PostBulkAction) => void | undefined;
99
+ /** Bộ lọc theo meta trên danh sách bài (CMS ≥ 1.2.0). */
100
+ export declare const registerPostListFilter: (f: import("./types.js").PostListFilter) => void | undefined;
101
+ /** Hộp trong trang sửa thành viên / hồ sơ, lưu vào user meta (CMS ≥ 1.2.0). */
102
+ export declare const registerUserProfileBox: (b: import("./types.js").UserProfileBox) => void | undefined;
103
+ /** Cột / nút trên dòng / thao tác hàng loạt cho danh sách người dùng, bình luận, media (list: "users" | "comments" | "media"). */
104
+ export declare const registerListColumn: (c: import("./types.js").ListColumn) => void | undefined;
105
+ export declare const registerListRowAction: (a: import("./types.js").ListRowAction) => void | undefined;
106
+ export declare const registerListBulkAction: (a: import("./types.js").ListBulkAction) => void | undefined;
107
+ export declare const getPostRowActions: (postType?: string) => import("./types.js").PostRowAction[];
108
+ export declare const hooks: import("./types.js").AdminHookRegistry;
109
+ export declare const addAction: (hook: string, namespace: string, cb: (...args: any[]) => void, priority?: number) => void;
110
+ export declare const doAction: (hook: string, ...args: any[]) => void;
111
+ export declare const addFilter: <T = any>(filter: string, namespace: string, cb: (v: T, ...args: any[]) => T, priority?: number) => void;
112
+ export declare const applyFilters: <T = any>(filter: string, value: T, ...args: any[]) => T;
113
+ export declare const removeAction: (hook: string, namespace: string) => void;
114
+ export declare const removeFilter: (filter: string, namespace: string) => void;
115
+ export declare const hasAction: (hook: string, namespace?: string) => boolean;
116
+ export declare const hasFilter: (filter: string, namespace?: string) => boolean;
117
+ export declare const Button: NonNullable<import("react").ComponentType<{
118
+ variant?: "primary" | "secondary" | "danger" | "ghost";
119
+ size?: "sm" | "md";
120
+ loading?: boolean;
121
+ icon?: import("react").ReactNode;
122
+ } & import("react").ButtonHTMLAttributes<HTMLButtonElement>>>;
123
+ export declare const Input: NonNullable<import("react").ComponentType<import("react").InputHTMLAttributes<HTMLInputElement> & {
124
+ ref?: React.Ref<HTMLInputElement>;
125
+ }>>;
126
+ export declare const Textarea: NonNullable<import("react").ComponentType<import("react").TextareaHTMLAttributes<HTMLTextAreaElement> & {
127
+ ref?: React.Ref<HTMLTextAreaElement>;
128
+ }>>;
129
+ export declare const Select: NonNullable<import("react").ComponentType<import("react").SelectHTMLAttributes<HTMLSelectElement> & {
130
+ options: Array<{
131
+ value: string;
132
+ label: string;
133
+ }>;
134
+ }>>;
135
+ export declare const Checkbox: NonNullable<import("react").ComponentType<{
136
+ checked: boolean;
137
+ onChange(checked: boolean): void;
138
+ label?: import("react").ReactNode;
139
+ disabled?: boolean;
140
+ description?: import("react").ReactNode;
141
+ ariaLabel?: string;
142
+ indeterminate?: boolean;
143
+ }>>;
144
+ export declare const Field: NonNullable<import("react").ComponentType<{
145
+ label: import("react").ReactNode;
146
+ help?: import("react").ReactNode;
147
+ error?: import("react").ReactNode;
148
+ children: import("react").ReactNode;
149
+ className?: string;
150
+ }>>;
151
+ export declare const Card: NonNullable<import("react").ComponentType<{
152
+ title?: import("react").ReactNode;
153
+ description?: import("react").ReactNode;
154
+ actions?: import("react").ReactNode;
155
+ children: import("react").ReactNode;
156
+ className?: string;
157
+ padded?: boolean;
158
+ }>>;
159
+ export declare const PageHeader: NonNullable<import("react").ComponentType<{
160
+ title: import("react").ReactNode;
161
+ description?: import("react").ReactNode;
162
+ actions?: import("react").ReactNode;
163
+ }>>;
164
+ export declare const Table: NonNullable<import("react").ComponentType<{
165
+ columns: import("./types.js").Column<any>[];
166
+ rows: any[];
167
+ rowKey?: (row: any) => string | number;
168
+ empty?: import("react").ReactNode;
169
+ loading?: boolean;
170
+ selectable?: boolean;
171
+ selected?: Set<string | number>;
172
+ onSelect?(s: Set<string | number>): void;
173
+ onRowClick?(row: any): void;
174
+ sort?: import("./types.js").SortState;
175
+ onSort?(s: import("./types.js").SortState): void;
176
+ hiddenColumns?: string[];
177
+ density?: Density;
178
+ ariaLabel?: string;
179
+ }>>;
180
+ export declare const Badge: NonNullable<import("react").ComponentType<{
181
+ tone?: "gray" | "green" | "yellow" | "red" | "blue" | "purple";
182
+ children: import("react").ReactNode;
183
+ dot?: boolean;
184
+ }>>;
185
+ export declare const Modal: NonNullable<import("react").ComponentType<{
186
+ open: boolean;
187
+ title: import("react").ReactNode;
188
+ onClose(): void;
189
+ children: import("react").ReactNode;
190
+ footer?: import("react").ReactNode;
191
+ width?: number;
192
+ description?: import("react").ReactNode;
193
+ dismissOnBackdrop?: boolean;
194
+ }>>;
195
+ export declare const Spinner: NonNullable<import("react").ComponentType<{
196
+ className?: string;
197
+ }>>;
198
+ export declare const EmptyState: NonNullable<import("react").ComponentType<{
199
+ title: import("react").ReactNode;
200
+ description?: import("react").ReactNode;
201
+ action?: import("react").ReactNode;
202
+ icon?: import("react").ReactNode;
203
+ }>>;
204
+ export declare const MediaPicker: NonNullable<import("react").ComponentType<{
205
+ value?: {
206
+ id: number;
207
+ url: string;
208
+ } | null;
209
+ onChange(value: {
210
+ id: number;
211
+ url: string;
212
+ } | null): void;
213
+ accept?: string;
214
+ }>>;
215
+ export declare const Switch: NonNullable<import("react").ComponentType<{
216
+ checked: boolean;
217
+ onChange(v: boolean): void;
218
+ disabled?: boolean;
219
+ label?: string;
220
+ children?: import("react").ReactNode;
221
+ description?: import("react").ReactNode;
222
+ }>>;
223
+ export declare const IconButton: NonNullable<import("react").ComponentType<{
224
+ label: string;
225
+ active?: boolean;
226
+ children: import("react").ReactNode;
227
+ } & import("react").ButtonHTMLAttributes<HTMLButtonElement>>>;
228
+ export declare const Section: NonNullable<import("react").ComponentType<{
229
+ title: import("react").ReactNode;
230
+ children: import("react").ReactNode;
231
+ defaultOpen?: boolean;
232
+ actions?: import("react").ReactNode;
233
+ }>>;
234
+ export declare const Tabs: NonNullable<import("react").ComponentType<{
235
+ value: string;
236
+ onChange(v: string): void;
237
+ items: Array<{
238
+ value: string;
239
+ label: import("react").ReactNode;
240
+ count?: number;
241
+ }>;
242
+ }>>;
243
+ export declare const MetaBoxes: NonNullable<import("react").ComponentType<import("./types.js").MetaBoxesProps>>;
244
+ export declare const Slot: NonNullable<import("react").ComponentType<{
245
+ name: string;
246
+ props?: any;
247
+ fallback?: import("react").ReactNode;
248
+ postType?: string;
249
+ className?: string;
250
+ }>>;
251
+ export declare const Fill: NonNullable<import("react").ComponentType<{
252
+ name: string;
253
+ priority?: number;
254
+ children: import("react").ReactNode;
255
+ }>>;
256
+ export declare const Dialog: NonNullable<import("react").ComponentType<{
257
+ open: boolean;
258
+ title: import("react").ReactNode;
259
+ onClose(): void;
260
+ children: import("react").ReactNode;
261
+ footer?: import("react").ReactNode;
262
+ width?: number;
263
+ description?: import("react").ReactNode;
264
+ dismissOnBackdrop?: boolean;
265
+ }> | undefined>;
266
+ export declare const Drawer: NonNullable<import("react").ComponentType<{
267
+ open: boolean;
268
+ title: import("react").ReactNode;
269
+ onClose(): void;
270
+ children: import("react").ReactNode;
271
+ footer?: import("react").ReactNode;
272
+ width?: number;
273
+ side?: "left" | "right";
274
+ description?: import("react").ReactNode;
275
+ }> | undefined>;
276
+ export declare const Popover: NonNullable<import("react").ComponentType<{
277
+ trigger: (p: {
278
+ open: boolean;
279
+ toggle(): void;
280
+ }) => import("react").ReactNode;
281
+ children: (close: () => void) => import("react").ReactNode;
282
+ align?: "start" | "end";
283
+ className?: string;
284
+ placement?: "left" | "right" | "top" | "bottom" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | "right-start" | "right-end";
285
+ }> | undefined>;
286
+ export declare const DropdownMenu: NonNullable<import("react").ComponentType<{
287
+ trigger: React.ReactElement;
288
+ children: import("react").ReactNode;
289
+ align?: "start" | "end";
290
+ className?: string;
291
+ placement?: "left" | "right" | "top" | "bottom" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | "right-start" | "right-end";
292
+ }> | undefined>;
293
+ export declare const MenuItem: NonNullable<import("react").ComponentType<{
294
+ icon?: import("react").ReactNode;
295
+ children: import("react").ReactNode;
296
+ onClick?(): void;
297
+ danger?: boolean;
298
+ shortcut?: import("react").ReactNode;
299
+ disabled?: boolean;
300
+ keepOpen?: boolean;
301
+ }> | undefined>;
302
+ export declare const MenuSeparator: NonNullable<import("react").ComponentType<{}> | undefined>;
303
+ export declare const MenuLabel: NonNullable<import("react").ComponentType<{
304
+ children: import("react").ReactNode;
305
+ }> | undefined>;
306
+ export declare const Tooltip: NonNullable<import("react").ComponentType<{
307
+ content: import("react").ReactNode;
308
+ children: React.ReactElement;
309
+ placement?: "left" | "right" | "top" | "bottom" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | "right-start" | "right-end";
310
+ delay?: number;
311
+ }> | undefined>;
312
+ export declare const Skeleton: NonNullable<import("react").ComponentType<{
313
+ className?: string;
314
+ }> | undefined>;
315
+ export declare const Combobox: NonNullable<import("react").ComponentType<{
316
+ value: string | null;
317
+ onChange(v: string | null): void;
318
+ options: import("./types.js").UiOption[];
319
+ placeholder?: string;
320
+ disabled?: boolean;
321
+ ariaLabel?: string;
322
+ onSearch?(q: string): void;
323
+ clearable?: boolean;
324
+ className?: string;
325
+ emptyText?: import("react").ReactNode;
326
+ }> | undefined>;
327
+ export declare const TagInput: NonNullable<import("react").ComponentType<{
328
+ value: string[];
329
+ onChange(v: string[]): void;
330
+ options?: import("./types.js").UiOption[];
331
+ creatable?: boolean;
332
+ placeholder?: string;
333
+ ariaLabel?: string;
334
+ disabled?: boolean;
335
+ max?: number;
336
+ onSearch?(q: string): void;
337
+ }> | undefined>;
338
+ export declare const DatePicker: NonNullable<import("react").ComponentType<{
339
+ value: string | null;
340
+ onChange(v: string | null): void;
341
+ withTime?: boolean;
342
+ placeholder?: string;
343
+ ariaLabel?: string;
344
+ disabled?: boolean;
345
+ min?: string;
346
+ max?: string;
347
+ clearable?: boolean;
348
+ }> | undefined>;
349
+ export declare const RadioGroup: NonNullable<import("react").ComponentType<{
350
+ value: string;
351
+ onChange(v: string): void;
352
+ options: Array<{
353
+ value: string;
354
+ label: import("react").ReactNode;
355
+ description?: import("react").ReactNode;
356
+ disabled?: boolean;
357
+ }>;
358
+ ariaLabel?: string;
359
+ variant?: "list" | "cards" | "segmented";
360
+ disabled?: boolean;
361
+ }> | undefined>;
362
+ export declare const TreeView: NonNullable<import("react").ComponentType<{
363
+ nodes: import("./types.js").TreeNode[];
364
+ ariaLabel: string;
365
+ checkable?: boolean;
366
+ checked?: Array<string | number>;
367
+ onCheck?(id: string | number, value: boolean): void;
368
+ selected?: string | number | null;
369
+ onSelect?(id: string | number): void;
370
+ defaultExpanded?: "all" | "none" | Array<string | number>;
371
+ className?: string;
372
+ }> | undefined>;
373
+ export declare const Pagination: NonNullable<import("react").ComponentType<{
374
+ page: number;
375
+ totalPages: number;
376
+ onPage(p: number): void;
377
+ total?: number;
378
+ pageSize?: number;
379
+ onPageSize?(n: number): void;
380
+ pageSizes?: number[];
381
+ }> | undefined>;
382
+ export declare const TableSettingsMenu: NonNullable<import("react").ComponentType<{
383
+ columns: Array<{
384
+ key: string;
385
+ title: import("react").ReactNode;
386
+ }>;
387
+ settings: TableSettings;
388
+ }> | undefined>;
389
+ /** Cột ẩn + mật độ của một bảng, nhớ theo `key` (vd "my-plugin:orders"). CMS cũ: không lưu, mọi cột hiện. */
390
+ export declare function useTableSettings(key: string, defaults?: {
391
+ hidden?: string[];
392
+ density?: Density;
393
+ }): TableSettings;
394
+ /** ISO (UTC) → giá trị cho `DatePicker withTime` (giờ địa phương). */
395
+ export declare const isoToLocal: (iso?: string | null) => string | null;
396
+ /** Giá trị của `DatePicker withTime` → ISO (UTC). */
397
+ export declare const localToIso: (v: string | null) => string | null;
398
+ /** Hỏi xác nhận bằng hộp thoại của admin (CMS cũ: window.confirm). `danger` = nút đỏ, focus mặc định ở "Huỷ". */
399
+ export declare const confirmDialog: (message: string, opts?: AskOptions) => Promise<boolean>;
400
+ /** Hỏi một chuỗi; null khi huỷ (CMS cũ: window.prompt). */
401
+ export declare const promptDialog: (message: string, opts?: AskOptions & {
402
+ defaultValue?: string;
403
+ placeholder?: string;
404
+ }) => Promise<string | null>;
405
+ /** Báo một thông điệp, chờ người dùng bấm OK (CMS cũ: window.alert). */
406
+ export declare const alertDialog: (message: string, opts?: AskOptions) => Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,193 @@
1
+ import { host } from "./host.js";
2
+ export * from "./types.js";
3
+ export const registerPage = (page) => host().registerPage(page);
4
+ export const registerMetaBox = (box) => host().registerMetaBox(box);
5
+ /** Sidebar riêng trong trình soạn bài, kèm nút trên thanh hành động (CMS cũ chưa hỗ trợ thì bỏ qua). */
6
+ export const registerEditorSidebar = (s) => host().registerEditorSidebar?.(s);
7
+ export const registerDashboardWidget = (w) => host().registerDashboardWidget(w);
8
+ /** Thêm cột vào danh sách bài viết/trang (vd điểm SEO). */
9
+ export const registerPostListColumn = (c) => host().registerPostListColumn(c);
10
+ /** Thêm hộp vào form sửa chuyên mục/thẻ (dữ liệu lưu trong term.meta). */
11
+ export const registerTermMetaBox = (b) => host().registerTermMetaBox(b);
12
+ /** Lấy các MetaBox đã đăng ký trong hệ thống (tùy chọn lọc theo postType). */
13
+ export const getMetaBoxes = (postType) => (host().getMetaBoxes ? host().getMetaBoxes(postType) : []);
14
+ /** Hook lấy toàn bộ state plugin UI trong host. */
15
+ export const usePluginUi = () => (host().usePluginUi ? host().usePluginUi() : { metaBoxes: [], pages: {}, dashboardWidgets: [], postListColumns: [], termMetaBoxes: [], loaded: true, errors: [] });
16
+ export const useApi = (path) => host().useApi(path);
17
+ export const can = (capability) => host().can(capability);
18
+ /**
19
+ * Khối cho trình soạn nội dung, khai báo bằng defineBlock (file dùng chung với phần website của plugin):
20
+ * trình soạn tự có mục slash menu, khung khối và form cài đặt sinh từ attributes. Nhãn "%khoá%" dịch theo
21
+ * locales của plugin khi tên khối có tiền tố "slug/" (vd "lien-he/callout").
22
+ */
23
+ export const registerBlock = (def, options) => {
24
+ const editor = host().editor;
25
+ if (!editor.registerBlock)
26
+ throw new Error("registerBlock cần CMS ≥ 1.8.0 (trình soạn hỗ trợ defineBlock)");
27
+ editor.registerBlock(def, options);
28
+ };
29
+ /**
30
+ * Thêm panel vào tab "Khối" của trình soạn cho một số loại khối (kể cả khối lõi như "image", "button") hoặc mọi khối:
31
+ * registerBlockPanel({ id: "seo/link-rel", title: "%link.title%", blocks: ["button"], attributes: { sponsored: { default: false } }, component: Panel })
32
+ * CMS cũ (chưa có tab "Khối") → bỏ qua, không lỗi.
33
+ */
34
+ export const registerBlockPanel = (panel) => host().editor.registerBlockPanel?.(panel);
35
+ /**
36
+ * Thêm mẫu (đoạn dựng sẵn) vào tab "Mẫu" của trình chèn:
37
+ * registerPattern({ name: "shop/product-hero", title: "%pattern.hero%", category: "hero", content: { type: "doc", content: [...] } })
38
+ * CMS cũ (chưa có tab Mẫu) → bỏ qua, không lỗi.
39
+ */
40
+ export const registerPattern = (pattern) => host().editor.registerPattern?.(pattern);
41
+ export { defineBlock } from "@ecomcx/nexus-content";
42
+ /** Thêm node/mark TipTap cho trình soạn (cấp thấp — khối thường dùng registerBlock). */
43
+ export const registerEditorExtension = (pack) => host().editor.register(pack);
44
+ /** Lớp TipTap dùng chung với admin (không cần cài @tiptap/core trong plugin). */
45
+ export const tiptap = new Proxy({}, { get: (_t, k) => host().editor.tiptap[k] });
46
+ export const currentUser = () => host().currentUser();
47
+ /**
48
+ * Công bố hàm cho plugin khác dùng trong admin (gọi ở đầu bundle): exposeApi({ openCartDrawer, formatPrice })
49
+ * Bên dùng: const shop = await waitForPluginApi<ShopAdminApi>("shop"); shop?.formatPrice(1000)
50
+ */
51
+ export const exposeApi = (api) => host().plugins?.expose("", api);
52
+ /** Hàm plugin khác đã công bố (undefined nếu plugin chưa bật hoặc bundle chưa nạp xong). */
53
+ export const getPluginApi = (slug) => host().plugins?.get(slug);
54
+ /** Chờ plugin khác nạp xong rồi lấy hàm (undefined nếu plugin không bật). */
55
+ export const waitForPluginApi = (slug, timeoutMs) => host().plugins ? host().plugins.wait(slug, timeoutMs) : Promise.resolve(undefined);
56
+ /**
57
+ * Dịch chuỗi giao diện theo khoá (ICU MessageFormat). Bản dịch của plugin đặt trong gói: locales/vi.json, locales/en.json
58
+ * — CMS tự nạp với namespace = slug plugin:
59
+ * locales/en.json: { "form.title": "Contact", "form.count": "{count, plural, one {# message} other {# messages}}" }
60
+ * const t = createT("lien-he"); t("form.title") → "Contact"; t("form.count", { count: 3 }) → "3 messages"
61
+ * Khoá có ":" là khoá đầy đủ ("core:common.save" — nhãn chung của lõi, "plugin-khac:x").
62
+ */
63
+ export const t = (key, values) => (host().i18n.t ? host().i18n.t(key, values) : key);
64
+ /** Hàm dịch gắn sẵn namespace của plugin. */
65
+ export const createT = (namespace) => (key, values) => t(key.includes(":") ? key : `${namespace}:${key}`, values);
66
+ /** Thêm bản dịch bằng mã (ngoài locales/*.json), vd bản dịch tải từ API. */
67
+ export const registerMessages = (namespace, locale, dict) => host().i18n.registerMessages?.(namespace, locale, dict);
68
+ /** @deprecated Chuỗi gốc tiếng Việt làm khoá — dùng t()/createT() với locales/*.json. */
69
+ export const __ = (text, ...args) => host().i18n.__(text, ...args);
70
+ /** @deprecated Dùng locales/*.json hoặc registerMessages(). */
71
+ export const registerTranslations = (locale, dict) => host().i18n.registerTranslations(locale, dict);
72
+ export const adminLocale = () => host().i18n.locale;
73
+ export const api = {
74
+ get: (p) => host().api.get(p),
75
+ post: (p, b) => host().api.post(p, b),
76
+ put: (p, b) => host().api.put(p, b),
77
+ del: (p) => host().api.del(p),
78
+ upload: (p, f, field) => host().api.upload(p, f, field),
79
+ };
80
+ export const notify = {
81
+ success: (m) => host().notify.success(m),
82
+ error: (m) => host().notify.error(m),
83
+ info: (m) => host().notify.info(m),
84
+ };
85
+ // Component UI: lấy lười từ host để module plugin có thể được import trước khi render.
86
+ // `fallback`: dùng khi CMS cũ chưa có component đó (UI kit v2) — plugin vẫn chạy, chỉ giản dị hơn.
87
+ const ui = (name, fallback) => ((props) => {
88
+ const C = host().ui[name] ?? fallback;
89
+ if (!C) {
90
+ console.warn(`[admin-sdk] CMS chưa có ui.${String(name)} — cần bản CMS mới hơn`);
91
+ return null;
92
+ }
93
+ return host().React.createElement(C, props);
94
+ });
95
+ const h = (...a) => host().React.createElement(...a);
96
+ export const registerSlot = (slot) => host().registerSlot(slot);
97
+ export const getSlots = (slot, postType) => (host().getSlots ? host().getSlots(slot, postType) : []);
98
+ export const registerPostRowAction = (a) => host().registerPostRowAction(a);
99
+ /** Thao tác hàng loạt trên danh sách bài (CMS ≥ 1.2.0). */
100
+ export const registerPostBulkAction = (a) => host().registerPostBulkAction?.(a);
101
+ /** Bộ lọc theo meta trên danh sách bài (CMS ≥ 1.2.0). */
102
+ export const registerPostListFilter = (f) => host().registerPostListFilter?.(f);
103
+ /** Hộp trong trang sửa thành viên / hồ sơ, lưu vào user meta (CMS ≥ 1.2.0). */
104
+ export const registerUserProfileBox = (b) => host().registerUserProfileBox?.(b);
105
+ /** Cột / nút trên dòng / thao tác hàng loạt cho danh sách người dùng, bình luận, media (list: "users" | "comments" | "media"). */
106
+ export const registerListColumn = (c) => host().registerListColumn?.(c);
107
+ export const registerListRowAction = (a) => host().registerListRowAction?.(a);
108
+ export const registerListBulkAction = (a) => host().registerListBulkAction?.(a);
109
+ export const getPostRowActions = (postType) => (host().getPostRowActions ? host().getPostRowActions(postType) : []);
110
+ export const hooks = new Proxy({}, {
111
+ get: (_t, k) => host().hooks?.[k] ?? (() => { }),
112
+ });
113
+ export const addAction = (hook, namespace, cb, priority) => host().hooks.addAction(hook, namespace, cb, priority);
114
+ export const doAction = (hook, ...args) => host().hooks.doAction(hook, ...args);
115
+ export const addFilter = (filter, namespace, cb, priority) => host().hooks.addFilter(filter, namespace, cb, priority);
116
+ export const applyFilters = (filter, value, ...args) => host().hooks.applyFilters(filter, value, ...args);
117
+ export const removeAction = (hook, namespace) => host().hooks.removeAction(hook, namespace);
118
+ export const removeFilter = (filter, namespace) => host().hooks.removeFilter(filter, namespace);
119
+ export const hasAction = (hook, namespace) => host().hooks.hasAction(hook, namespace);
120
+ export const hasFilter = (filter, namespace) => host().hooks.hasFilter(filter, namespace);
121
+ export const Button = ui("Button");
122
+ export const Input = ui("Input");
123
+ export const Textarea = ui("Textarea");
124
+ export const Select = ui("Select");
125
+ export const Checkbox = ui("Checkbox");
126
+ export const Field = ui("Field");
127
+ export const Card = ui("Card");
128
+ export const PageHeader = ui("PageHeader");
129
+ export const Table = ui("Table");
130
+ export const Badge = ui("Badge");
131
+ export const Modal = ui("Modal");
132
+ export const Spinner = ui("Spinner");
133
+ export const EmptyState = ui("EmptyState");
134
+ export const MediaPicker = ui("MediaPicker");
135
+ export const Switch = ui("Switch");
136
+ export const IconButton = ui("IconButton");
137
+ export const Section = ui("Section");
138
+ export const Tabs = ui("Tabs");
139
+ export const MetaBoxes = ui("MetaBoxes");
140
+ export const Slot = ui("Slot");
141
+ export const Fill = ui("Fill");
142
+ // ----- UI kit v2 (xem trang /ui-kit trong admin) -----
143
+ export const Dialog = ui("Dialog", (p) => h(host().ui.Modal, p));
144
+ export const Drawer = ui("Drawer", (p) => h(host().ui.Modal, p));
145
+ export const Popover = ui("Popover");
146
+ export const DropdownMenu = ui("DropdownMenu");
147
+ export const MenuItem = ui("MenuItem");
148
+ export const MenuSeparator = ui("MenuSeparator", () => null);
149
+ export const MenuLabel = ui("MenuLabel");
150
+ export const Tooltip = ui("Tooltip", (p) => p.children);
151
+ export const Skeleton = ui("Skeleton", (p) => h("div", { className: p.className }));
152
+ export const Combobox = ui("Combobox", (p) => h(host().ui.Select, {
153
+ value: p.value ?? "", disabled: p.disabled, "aria-label": p.ariaLabel, onChange: (e) => p.onChange(e.target.value || null),
154
+ options: [{ value: "", label: p.placeholder ?? "—" }, ...p.options],
155
+ }));
156
+ export const TagInput = ui("TagInput", (p) => h(host().ui.Input, {
157
+ defaultValue: p.value.join(", "), placeholder: p.placeholder, "aria-label": p.ariaLabel, disabled: p.disabled,
158
+ onBlur: (e) => p.onChange(String(e.target.value).split(",").map((x) => x.trim()).filter(Boolean)),
159
+ }));
160
+ export const DatePicker = ui("DatePicker", (p) => h(host().ui.Input, {
161
+ type: p.withTime ? "datetime-local" : "date", value: p.value ?? "", min: p.min, max: p.max, disabled: p.disabled, "aria-label": p.ariaLabel,
162
+ onChange: (e) => p.onChange(e.target.value || null),
163
+ }));
164
+ export const RadioGroup = ui("RadioGroup", (p) => h(host().ui.Select, {
165
+ value: p.value, disabled: p.disabled, "aria-label": p.ariaLabel, onChange: (e) => p.onChange(e.target.value),
166
+ options: p.options.map((o) => ({ value: o.value, label: typeof o.label === "string" ? o.label : o.value })),
167
+ }));
168
+ export const TreeView = ui("TreeView");
169
+ export const Pagination = ui("Pagination");
170
+ export const TableSettingsMenu = ui("TableSettingsMenu", () => null);
171
+ /** Cột ẩn + mật độ của một bảng, nhớ theo `key` (vd "my-plugin:orders"). CMS cũ: không lưu, mọi cột hiện. */
172
+ export function useTableSettings(key, defaults) {
173
+ const fn = host().ui.useTableSettings;
174
+ if (fn)
175
+ return fn(key, defaults);
176
+ return { hidden: defaults?.hidden ?? [], density: defaults?.density ?? "comfortable", toggleColumn: () => { }, setDensity: () => { } };
177
+ }
178
+ const pad = (n) => String(n).padStart(2, "0");
179
+ /** ISO (UTC) → giá trị cho `DatePicker withTime` (giờ địa phương). */
180
+ export const isoToLocal = (iso) => {
181
+ if (!iso)
182
+ return null;
183
+ const d = new Date(iso);
184
+ return isNaN(d.getTime()) ? null : `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
185
+ };
186
+ /** Giá trị của `DatePicker withTime` → ISO (UTC). */
187
+ export const localToIso = (v) => (v ? new Date(v).toISOString() : null);
188
+ /** Hỏi xác nhận bằng hộp thoại của admin (CMS cũ: window.confirm). `danger` = nút đỏ, focus mặc định ở "Huỷ". */
189
+ export const confirmDialog = (message, opts) => host().ui.confirmDialog ? host().ui.confirmDialog(message, opts) : Promise.resolve(window.confirm(message));
190
+ /** Hỏi một chuỗi; null khi huỷ (CMS cũ: window.prompt). */
191
+ export const promptDialog = (message, opts) => host().ui.promptDialog ? host().ui.promptDialog(message, opts) : Promise.resolve(window.prompt(message, opts?.defaultValue ?? ""));
192
+ /** Báo một thông điệp, chờ người dùng bấm OK (CMS cũ: window.alert). */
193
+ export const alertDialog = (message, opts) => host().ui.alertDialog ? host().ui.alertDialog(message, opts) : Promise.resolve(window.alert(message));
@@ -0,0 +1,4 @@
1
+ export declare const jsx: typeof import("react/jsx-runtime").jsx;
2
+ export declare const jsxs: typeof import("react/jsx-runtime").jsxs;
3
+ export declare const Fragment: import("react").ExoticComponent<import("react").FragmentProps>;
4
+ export declare const jsxDEV: typeof import("react/jsx-runtime").jsx;
@@ -0,0 +1,6 @@
1
+ import { host } from "./host.js";
2
+ const rt = host().jsxRuntime;
3
+ export const jsx = rt.jsx;
4
+ export const jsxs = rt.jsxs;
5
+ export const Fragment = rt.Fragment;
6
+ export const jsxDEV = rt.jsx;
@@ -0,0 +1,11 @@
1
+ declare const React: typeof import("react");
2
+ export default React;
3
+ export declare const Children: {
4
+ map<T, C>(children: C | readonly C[], fn: (child: C, index: number) => T): C extends null | undefined ? C : Array<Exclude<T, boolean | null | undefined>>;
5
+ forEach<C>(children: C | readonly C[], fn: (child: C, index: number) => void): void;
6
+ count(children: any): number;
7
+ only<C>(children: C): C extends any[] ? never : C;
8
+ toArray(children: import("react").ReactNode | import("react").ReactNode[]): Array<Exclude<import("react").ReactNode, boolean | null | undefined>>;
9
+ }, Component: typeof import("react").Component, Fragment: import("react").ExoticComponent<import("react").FragmentProps>, PureComponent: typeof import("react").PureComponent, StrictMode: import("react").ExoticComponent<{
10
+ children?: import("react").ReactNode | undefined;
11
+ }>, Suspense: import("react").ExoticComponent<import("react").SuspenseProps>, cloneElement: typeof import("react").cloneElement, createContext: typeof import("react").createContext, createElement: typeof import("react").createElement, createRef: typeof import("react").createRef, forwardRef: typeof import("react").forwardRef, isValidElement: typeof import("react").isValidElement, lazy: typeof import("react").lazy, memo: typeof import("react").memo, startTransition: typeof import("react").startTransition, use: typeof import("react").use, useActionState: typeof import("react").useActionState, useCallback: typeof import("react").useCallback, useContext: typeof import("react").useContext, useDebugValue: typeof import("react").useDebugValue, useDeferredValue: typeof import("react").useDeferredValue, useEffect: typeof import("react").useEffect, useId: typeof import("react").useId, useImperativeHandle: typeof import("react").useImperativeHandle, useInsertionEffect: typeof import("react").useInsertionEffect, useLayoutEffect: typeof import("react").useLayoutEffect, useMemo: typeof import("react").useMemo, useOptimistic: typeof import("react").useOptimistic, useReducer: typeof import("react").useReducer, useRef: typeof import("react").useRef, useState: typeof import("react").useState, useSyncExternalStore: typeof import("react").useSyncExternalStore, useTransition: typeof import("react").useTransition;
package/dist/react.js ADDED
@@ -0,0 +1,5 @@
1
+ // Shim: plugin import "react" → dùng chung React của admin (không bundle React thứ hai).
2
+ import { host } from "./host.js";
3
+ const React = host().React;
4
+ export default React;
5
+ export const { Children, Component, Fragment, PureComponent, StrictMode, Suspense, cloneElement, createContext, createElement, createRef, forwardRef, isValidElement, lazy, memo, startTransition, use, useActionState, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect, useLayoutEffect, useMemo, useOptimistic, useReducer, useRef, useState, useSyncExternalStore, useTransition, } = React;