@meeovi/directus-client 1.0.1 → 1.0.2

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.
@@ -1,6 +1,7 @@
1
- import { createDirectus, readItem, readItems, createItem, updateItem, deleteItem, uploadFiles, readSingleton, readFieldsByCollection } from '@directus/sdk';
1
+ import { readItem, readItems, createItem, updateItem, deleteItem, uploadFiles, readSingleton, readFieldsByCollection, type DirectusClient } from '@directus/sdk';
2
2
  export interface MeeoviDirectusClient<Schema> {
3
- client: ReturnType<typeof createDirectus<Schema>>;
3
+ client: DirectusClient<Schema>;
4
+ request: any;
4
5
  readItem: typeof readItem;
5
6
  readItems: typeof readItems;
6
7
  createItem: typeof createItem;
@@ -1,4 +1,3 @@
1
- // src/client/createClient.ts
2
1
  import { createDirectus, rest, authentication, readItem, readItems, createItem, updateItem, deleteItem, uploadFiles, readSingleton, readFieldsByCollection } from '@directus/sdk';
3
2
  export function createMeeoviDirectusClient(url) {
4
3
  const client = createDirectus(url)
@@ -6,6 +5,7 @@ export function createMeeoviDirectusClient(url) {
6
5
  .with(authentication());
7
6
  return {
8
7
  client,
8
+ request: client.request,
9
9
  readItem,
10
10
  readItems,
11
11
  createItem,
@@ -1,15 +1,13 @@
1
1
  import type { DirectusField } from '../schema/types';
2
- export interface FormEngineOptions {
3
- clearOnSuccess?: boolean;
4
- onSuccess?: () => void;
5
- onError?: (msg: string) => void;
2
+ export interface GeneratedFormField {
3
+ key: string;
4
+ widget: string;
5
+ type: string;
6
+ options?: Record<string, any>;
7
+ fields?: GeneratedFormField[];
8
+ isRepeatable?: boolean;
9
+ isFile?: boolean;
10
+ isRelational?: boolean;
6
11
  }
7
- export declare function createFormEngine(collectionName: string, fields: DirectusField[], directusClient: any, opts?: FormEngineOptions): {
8
- form: Record<string, any>;
9
- submit: () => Promise<{
10
- error: string | null;
11
- success: string | null;
12
- }>;
13
- readonly error: string | null;
14
- readonly success: string | null;
15
- };
12
+ export declare function generateFormField(field: DirectusField): GeneratedFormField;
13
+ export declare function generateFormSchema(fields: DirectusField[]): GeneratedFormField[];
@@ -1,63 +1,22 @@
1
- export function createFormEngine(collectionName, fields, directusClient, opts) {
2
- const form = {};
3
- let error = null;
4
- let success = null;
5
- const validate = () => {
6
- error = null;
7
- for (const field of fields) {
8
- const meta = field;
9
- if (!meta?.validation)
10
- continue;
11
- try {
12
- const validation = meta.validation;
13
- if (validation._and) {
14
- for (const rule of validation._and) {
15
- const fieldName = Object.keys(rule)[0];
16
- if (!fieldName)
17
- continue;
18
- const ruleDef = rule[fieldName];
19
- if (ruleDef?._regex) {
20
- const regex = new RegExp(ruleDef._regex);
21
- const value = String(form[field.field] ?? '');
22
- if (!regex.test(value)) {
23
- error = meta.validation_message || `${field.field} failed validation`;
24
- return false;
25
- }
26
- }
27
- }
28
- }
29
- }
30
- catch {
31
- error = `Validation error for ${field.field}`;
32
- return false;
33
- }
34
- }
35
- return true;
36
- };
37
- const submit = async () => {
38
- if (!validate())
39
- return { error, success };
40
- const result = await directusClient.request(directusClient.createItem(collectionName, form));
41
- if (result?.error) {
42
- error = result.error.message;
43
- return { error, success };
44
- }
45
- success = `${collectionName} created successfully`;
46
- if (opts?.clearOnSuccess) {
47
- for (const key of Object.keys(form))
48
- delete form[key];
49
- }
50
- opts?.onSuccess?.();
51
- return { error, success };
52
- };
53
- return {
54
- form,
55
- submit,
56
- get error() {
57
- return error;
58
- },
59
- get success() {
60
- return success;
61
- }
1
+ import { widgetRegistry } from './widget-registry';
2
+ export function generateFormField(field) {
3
+ const widget = widgetRegistry[field.interface || 'input'];
4
+ const base = {
5
+ key: field.field,
6
+ widget: widget.component,
7
+ type: field.type,
8
+ options: field.options || {},
9
+ isRepeatable: widget.isRepeatable,
10
+ isFile: widget.isFile,
11
+ isRelational: widget.isRelational
62
12
  };
13
+ if ((field.interface === 'repeater' || field.interface === 'group') && field.options?.fields) {
14
+ base.fields = field.options.fields.map((sub) => generateFormField(sub));
15
+ }
16
+ return base;
17
+ }
18
+ export function generateFormSchema(fields) {
19
+ return fields
20
+ .filter(f => f.interface !== 'presentation' && f.interface !== 'divider')
21
+ .map(generateFormField);
63
22
  }
@@ -1,17 +1,9 @@
1
- export declare const widgetRegistry: {
2
- text: {
3
- component: string;
4
- };
5
- number: {
6
- component: string;
7
- };
8
- checkbox: {
9
- component: string;
10
- };
11
- date: {
12
- component: string;
13
- };
14
- json: {
15
- component: string;
16
- };
17
- };
1
+ export interface WidgetDefinition {
2
+ component: string;
3
+ props?: Record<string, any>;
4
+ isRepeatable?: boolean;
5
+ isRelational?: boolean;
6
+ isFile?: boolean;
7
+ }
8
+ export declare const widgetRegistry: Record<string, WidgetDefinition>;
9
+ export declare function extendWidgetRegistryFromDirectus(client: any): Promise<void>;
@@ -1,7 +1,54 @@
1
+ import { readItems } from "@directus/sdk";
1
2
  export const widgetRegistry = {
2
- text: { component: 'TextInput' },
3
- number: { component: 'NumberInput' },
3
+ // Basic inputs
4
+ input: { component: 'TextInput' },
5
+ textarea: { component: 'TextareaInput' },
6
+ boolean: { component: 'ToggleInput' },
7
+ slider: { component: 'SliderInput' },
8
+ color: { component: 'ColorPicker' },
9
+ rating: { component: 'RatingInput' },
10
+ // Selects
11
+ 'select-dropdown': { component: 'SelectInput' },
12
+ 'select-multiple-dropdown': { component: 'MultiSelectInput' },
13
+ tags: { component: 'TagInput' },
4
14
  checkbox: { component: 'CheckboxInput' },
15
+ radio: { component: 'RadioInput' },
16
+ // Date/time
17
+ datetime: { component: 'DateTimeInput' },
5
18
  date: { component: 'DateInput' },
6
- json: { component: 'JsonEditor' }
19
+ time: { component: 'TimeInput' },
20
+ // Files
21
+ file: { component: 'FileInput', isFile: true },
22
+ files: { component: 'FilesInput', isFile: true },
23
+ image: { component: 'ImageInput', isFile: true },
24
+ images: { component: 'ImagesInput', isFile: true },
25
+ // Complex
26
+ repeater: { component: 'RepeaterInput', isRepeatable: true },
27
+ group: { component: 'GroupInput' },
28
+ json: { component: 'JsonEditor' },
29
+ code: { component: 'CodeEditor' },
30
+ wysiwyg: { component: 'WysiwygEditor' },
31
+ markdown: { component: 'MarkdownEditor' },
32
+ // Directus-specific
33
+ icon: { component: 'IconPicker' },
34
+ user: { component: 'UserSelect' },
35
+ role: { component: 'RoleSelect' },
36
+ translation: { component: 'TranslationInput' },
37
+ // Presentation (ignored in forms)
38
+ presentation: { component: 'PresentationBlock' },
39
+ divider: { component: 'DividerBlock' }
7
40
  };
41
+ export async function extendWidgetRegistryFromDirectus(client) {
42
+ const extensions = await client.request(readItems('directus_extensions'));
43
+ for (const ext of extensions) {
44
+ if (ext.type !== 'interface')
45
+ continue;
46
+ const name = ext.name;
47
+ if (!widgetRegistry[name]) {
48
+ widgetRegistry[name] = {
49
+ component: 'CustomInterfaceRenderer',
50
+ props: { interfaceName: name }
51
+ };
52
+ }
53
+ }
54
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meeovi/directus-client",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Directus Client Library with auto generating forms and tables, Directus SDK and Types, and vue/react components for Directus Visual Editing.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -1,5 +1,3 @@
1
- // src/client/createClient.ts
2
-
3
1
  import {
4
2
  createDirectus,
5
3
  rest,
@@ -11,11 +9,13 @@ import {
11
9
  deleteItem,
12
10
  uploadFiles,
13
11
  readSingleton,
14
- readFieldsByCollection
12
+ readFieldsByCollection,
13
+ type DirectusClient
15
14
  } from '@directus/sdk';
16
15
 
17
16
  export interface MeeoviDirectusClient<Schema> {
18
- client: ReturnType<typeof createDirectus<Schema>>;
17
+ client: DirectusClient<Schema>;
18
+ request: any;
19
19
  readItem: typeof readItem;
20
20
  readItems: typeof readItems;
21
21
  createItem: typeof createItem;
@@ -33,6 +33,7 @@ export function createMeeoviDirectusClient<Schema>(url: string): MeeoviDirectusC
33
33
 
34
34
  return {
35
35
  client,
36
+ request: client.request,
36
37
  readItem,
37
38
  readItems,
38
39
  createItem,
@@ -6,7 +6,7 @@ export interface GeneratedFormField {
6
6
  widget: string;
7
7
  type: string;
8
8
  options?: Record<string, any>;
9
- fields?: GeneratedFormField[]; // for repeater/group
9
+ fields?: GeneratedFormField[];
10
10
  isRepeatable?: boolean;
11
11
  isFile?: boolean;
12
12
  isRelational?: boolean;
@@ -25,24 +25,14 @@ export function generateFormField(field: DirectusField): GeneratedFormField {
25
25
  isRelational: widget.isRelational
26
26
  };
27
27
 
28
- // Handle repeater (nested fields)
29
- if (field.interface === 'repeater' && field.options?.fields) {
30
- base.fields = field.options.fields.map((sub: any) =>
31
- generateFormField(sub)
32
- );
33
- }
34
-
35
- // Handle group
36
- if (field.interface === 'group' && field.options?.fields) {
37
- base.fields = field.options.fields.map((sub: any) =>
38
- generateFormField(sub)
39
- );
28
+ if ((field.interface === 'repeater' || field.interface === 'group') && field.options?.fields) {
29
+ base.fields = field.options.fields.map((sub: any) => generateFormField(sub));
40
30
  }
41
31
 
42
32
  return base;
43
33
  }
44
34
 
45
- export function generateFormSchema(fields: DirectusField[]) {
35
+ export function generateFormSchema(fields: DirectusField[]): GeneratedFormField[] {
46
36
  return fields
47
37
  .filter(f => f.interface !== 'presentation' && f.interface !== 'divider')
48
38
  .map(generateFormField);
package/tsconfig.json CHANGED
@@ -7,7 +7,9 @@
7
7
  "module": "ESNext",
8
8
  "target": "ESNext",
9
9
  "strict": true,
10
- "jsx": "react-jsx"
10
+ "jsx": "react-jsx",
11
+ "skipLibCheck": true,
12
+ "noEmitOnError": false
11
13
  },
12
14
  "include": ["src"]
13
15
  }