@meeovi/directus-client 1.0.0 → 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.
- package/README.md +238 -0
- package/dist/client/createClient.d.ts +14 -0
- package/dist/client/createClient.js +18 -0
- package/dist/generators/form-engine.d.ts +13 -0
- package/dist/generators/form-engine.js +22 -0
- package/dist/generators/table-engine.d.ts +6 -0
- package/dist/generators/table-engine.js +9 -0
- package/dist/generators/validation-engine.d.ts +2 -0
- package/dist/generators/validation-engine.js +25 -0
- package/dist/generators/widget-registry.d.ts +9 -0
- package/dist/generators/widget-registry.js +54 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +17 -0
- package/dist/react/DirectusProvider.d.ts +7 -0
- package/dist/react/DirectusProvider.js +6 -0
- package/dist/react/useDirectus.d.ts +2 -0
- package/dist/react/useDirectus.js +9 -0
- package/dist/schema/introspect.d.ts +5 -0
- package/dist/schema/introspect.js +30 -0
- package/dist/schema/types.d.ts +47 -0
- package/dist/schema/types.js +1 -0
- package/dist/utils/collections.d.ts +5 -0
- package/dist/utils/collections.js +13 -0
- package/dist/utils/fields.d.ts +4 -0
- package/dist/utils/fields.js +40 -0
- package/dist/utils/livePreview.d.ts +11 -0
- package/dist/utils/livePreview.js +11 -0
- package/dist/utils/useDirectusField.d.ts +10 -0
- package/dist/utils/useDirectusField.js +119 -0
- package/dist/utils/useDirectusRequest.d.ts +7 -0
- package/dist/utils/useDirectusRequest.js +33 -0
- package/dist/utils/useLivePreview.d.ts +1 -0
- package/dist/utils/useLivePreview.js +16 -0
- package/dist/utils/useVisualEditing.d.ts +18 -0
- package/dist/utils/useVisualEditing.js +27 -0
- package/dist/utils/visualEditing.d.ts +21 -0
- package/dist/utils/visualEditing.js +25 -0
- package/dist/vue/DirectusProvider.d.ts +15 -0
- package/dist/vue/DirectusProvider.js +12 -0
- package/dist/vue/useDirectus.d.ts +2 -0
- package/dist/vue/useDirectus.js +9 -0
- package/package.json +16 -5
- package/src/client/createClient.ts +5 -4
- package/src/generators/form-engine.ts +31 -81
- package/src/generators/widget-registry.ts +71 -4
- package/src/react/useDirectus.ts +1 -1
- package/src/utils/livePreview.ts +24 -0
- package/src/utils/visualEditing.ts +46 -0
- package/src/vue/useDirectus.ts +1 -1
- package/tsconfig.json +15 -0
- package/src/utils/useDirectusField.ts +0 -144
- package/src/utils/useDirectusRequest.ts +0 -32
- package/src/utils/useDirectusSchema.js +0 -9
- package/src/utils/useLivePreview.ts +0 -17
- package/src/utils/useVisualEditing.ts +0 -38
package/README.md
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# @meeovi/directus-client
|
|
2
|
+
|
|
3
|
+
A framework-agnostic, type-safe Directus client with optional bindings for Vue 3, Nuxt 3, and React. Includes schema introspection, auto-form and auto-table generators, validation utilities, and visual-editing / live-preview helpers.
|
|
4
|
+
|
|
5
|
+
Designed for modular, provider-agnostic platforms like Meeovi, but usable in any Directus-powered project.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Framework-agnostic Directus client
|
|
10
|
+
- Vue 3 bindings (`useDirectus`, `DirectusVueProvider`)
|
|
11
|
+
- React bindings (`useDirectus`, `DirectusReactProvider`)
|
|
12
|
+
- Schema introspection
|
|
13
|
+
- Auto-form engine
|
|
14
|
+
- Auto-table engine
|
|
15
|
+
- Validation engine
|
|
16
|
+
- Widget registry
|
|
17
|
+
- Visual editing utilities
|
|
18
|
+
- Live preview utilities
|
|
19
|
+
- Fully typed and tree-shakeable
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @meeovi/directus-client
|
|
25
|
+
# or
|
|
26
|
+
pnpm add @meeovi/directus-client
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Package structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
src/
|
|
33
|
+
client/
|
|
34
|
+
createClient.ts
|
|
35
|
+
vue/
|
|
36
|
+
DirectusProvider.ts
|
|
37
|
+
useDirectus.ts
|
|
38
|
+
react/
|
|
39
|
+
DirectusProvider.tsx
|
|
40
|
+
useDirectus.ts
|
|
41
|
+
schema/
|
|
42
|
+
types.ts
|
|
43
|
+
introspect.ts
|
|
44
|
+
utils/
|
|
45
|
+
collections.ts
|
|
46
|
+
fields.ts
|
|
47
|
+
generators/
|
|
48
|
+
form-engine.ts
|
|
49
|
+
table-engine.ts
|
|
50
|
+
validation-engine.ts
|
|
51
|
+
widget-registry.ts
|
|
52
|
+
visual-editing.ts
|
|
53
|
+
live-preview.ts
|
|
54
|
+
index.ts
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### 1) Framework-agnostic
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { createMeeoviDirectusClient } from '@meeovi/directus-client';
|
|
63
|
+
|
|
64
|
+
const directus = createMeeoviDirectusClient('https://your-directus-url.com');
|
|
65
|
+
|
|
66
|
+
// Example request
|
|
67
|
+
const products = await directus.client.request(directus.readItems('products'));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2) Vue 3
|
|
71
|
+
|
|
72
|
+
Wrap your app:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { DirectusVueProvider, createMeeoviDirectusClient } from '@meeovi/directus-client';
|
|
76
|
+
|
|
77
|
+
const directus = createMeeoviDirectusClient('https://cms.example.com');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```vue
|
|
81
|
+
<template>
|
|
82
|
+
<DirectusVueProvider :client="directus">
|
|
83
|
+
<App />
|
|
84
|
+
</DirectusVueProvider>
|
|
85
|
+
</template>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use inside components:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { useDirectus } from '@meeovi/directus-client';
|
|
92
|
+
|
|
93
|
+
const directus = useDirectus();
|
|
94
|
+
const items = await directus.client.request(directus.readItems('articles'));
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3) Nuxt 3
|
|
98
|
+
|
|
99
|
+
Create a plugin:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// plugins/directus.client.ts
|
|
103
|
+
import { defineNuxtPlugin, useRuntimeConfig } from '#imports';
|
|
104
|
+
import { createMeeoviDirectusClient, DirectusVueProvider } from '@meeovi/directus-client';
|
|
105
|
+
|
|
106
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
107
|
+
const config = useRuntimeConfig();
|
|
108
|
+
const directus = createMeeoviDirectusClient(config.public.directus.url);
|
|
109
|
+
|
|
110
|
+
nuxtApp.provide('directus', directus);
|
|
111
|
+
nuxtApp.vueApp.component('DirectusVueProvider', DirectusVueProvider);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Use it:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const { $directus } = useNuxtApp();
|
|
119
|
+
const posts = await $directus.client.request($directus.readItems('posts'));
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4) React
|
|
123
|
+
|
|
124
|
+
Wrap your app:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import { DirectusReactProvider, createMeeoviDirectusClient } from '@meeovi/directus-client';
|
|
128
|
+
|
|
129
|
+
const directus = createMeeoviDirectusClient('https://cms.example.com');
|
|
130
|
+
|
|
131
|
+
export function App() {
|
|
132
|
+
return (
|
|
133
|
+
<DirectusReactProvider client={directus}>
|
|
134
|
+
<YourRoutes />
|
|
135
|
+
</DirectusReactProvider>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use inside components:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { useDirectus } from '@meeovi/directus-client';
|
|
144
|
+
const directus = useDirectus();
|
|
145
|
+
const items = await directus.client.request(directus.readItems('products'));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Schema introspection
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { introspectSchema } from '@meeovi/directus-client';
|
|
152
|
+
|
|
153
|
+
const schema = await introspectSchema(directus.client);
|
|
154
|
+
console.log(schema.directus_fields);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Auto-Form Engine
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { createFormEngine } from '@meeovi/directus-client';
|
|
161
|
+
|
|
162
|
+
const engine = createFormEngine('products', fields, directus);
|
|
163
|
+
engine.form.title = 'New Product';
|
|
164
|
+
const result = await engine.submit();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Auto-Table Engine
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { generateTableSchema } from '@meeovi/directus-client';
|
|
171
|
+
const table = generateTableSchema(fields);
|
|
172
|
+
console.log(table);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Validation Engine
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { validateField } from '@meeovi/directus-client';
|
|
179
|
+
const error = validateField(field, value);
|
|
180
|
+
if (error) console.error(error);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Widget Registry
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { widgetRegistry } from '@meeovi/directus-client';
|
|
187
|
+
console.log(widgetRegistry.text.component);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Visual Editing (framework-agnostic)
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { createVisualEditing } from '@meeovi/directus-client';
|
|
194
|
+
|
|
195
|
+
const visual = createVisualEditing({
|
|
196
|
+
enableVisualEditing: true,
|
|
197
|
+
directusUrl: 'https://cms.example.com',
|
|
198
|
+
query: { 'visual-editing': 'true' },
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
visual.apply({ elements: document.querySelectorAll('[data-editable]') });
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Live Preview (framework-agnostic)
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import { createLivePreview } from '@meeovi/directus-client';
|
|
208
|
+
|
|
209
|
+
const preview = createLivePreview({
|
|
210
|
+
query: Object.fromEntries(new URLSearchParams(location.search)),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (preview.enabled) {
|
|
214
|
+
console.log('Preview token:', preview.state.token);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## TypeScript support
|
|
219
|
+
|
|
220
|
+
Everything is fully typed: Directus schema, client methods, form/table generators, visual editing, live preview, and framework bindings.
|
|
221
|
+
|
|
222
|
+
## Build
|
|
223
|
+
|
|
224
|
+
If you’re working inside a monorepo:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm --workspace @meeovi/directus-client run build
|
|
228
|
+
# or
|
|
229
|
+
pnpm --filter @meeovi/directus-client build
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Contributing
|
|
233
|
+
|
|
234
|
+
PRs are welcome. This package is designed to be modular, extensible, and framework-agnostic — contributions that improve developer experience or expand framework bindings are encouraged.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT © Meeovi
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readItem, readItems, createItem, updateItem, deleteItem, uploadFiles, readSingleton, readFieldsByCollection, type DirectusClient } from '@directus/sdk';
|
|
2
|
+
export interface MeeoviDirectusClient<Schema> {
|
|
3
|
+
client: DirectusClient<Schema>;
|
|
4
|
+
request: any;
|
|
5
|
+
readItem: typeof readItem;
|
|
6
|
+
readItems: typeof readItems;
|
|
7
|
+
createItem: typeof createItem;
|
|
8
|
+
updateItem: typeof updateItem;
|
|
9
|
+
deleteItem: typeof deleteItem;
|
|
10
|
+
uploadFiles: typeof uploadFiles;
|
|
11
|
+
readSingleton: typeof readSingleton;
|
|
12
|
+
readFieldsByCollection: typeof readFieldsByCollection;
|
|
13
|
+
}
|
|
14
|
+
export declare function createMeeoviDirectusClient<Schema>(url: string): MeeoviDirectusClient<Schema>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createDirectus, rest, authentication, readItem, readItems, createItem, updateItem, deleteItem, uploadFiles, readSingleton, readFieldsByCollection } from '@directus/sdk';
|
|
2
|
+
export function createMeeoviDirectusClient(url) {
|
|
3
|
+
const client = createDirectus(url)
|
|
4
|
+
.with(rest())
|
|
5
|
+
.with(authentication());
|
|
6
|
+
return {
|
|
7
|
+
client,
|
|
8
|
+
request: client.request,
|
|
9
|
+
readItem,
|
|
10
|
+
readItems,
|
|
11
|
+
createItem,
|
|
12
|
+
updateItem,
|
|
13
|
+
deleteItem,
|
|
14
|
+
uploadFiles,
|
|
15
|
+
readSingleton,
|
|
16
|
+
readFieldsByCollection
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DirectusField } from '../schema/types';
|
|
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;
|
|
11
|
+
}
|
|
12
|
+
export declare function generateFormField(field: DirectusField): GeneratedFormField;
|
|
13
|
+
export declare function generateFormSchema(fields: DirectusField[]): GeneratedFormField[];
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
|
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);
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function validateField(field, value) {
|
|
2
|
+
if (!field.validation)
|
|
3
|
+
return null;
|
|
4
|
+
try {
|
|
5
|
+
const validation = field.validation;
|
|
6
|
+
if (validation._and) {
|
|
7
|
+
for (const rule of validation._and) {
|
|
8
|
+
const fieldName = Object.keys(rule)[0];
|
|
9
|
+
if (!fieldName)
|
|
10
|
+
continue;
|
|
11
|
+
const ruleDef = rule[fieldName];
|
|
12
|
+
if (ruleDef?._regex) {
|
|
13
|
+
const regex = new RegExp(ruleDef._regex);
|
|
14
|
+
if (!regex.test(String(value ?? ''))) {
|
|
15
|
+
return field.validation_message || `${field.field} failed validation`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return `Validation error for ${field.field}`;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readItems } from "@directus/sdk";
|
|
2
|
+
export const widgetRegistry = {
|
|
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' },
|
|
14
|
+
checkbox: { component: 'CheckboxInput' },
|
|
15
|
+
radio: { component: 'RadioInput' },
|
|
16
|
+
// Date/time
|
|
17
|
+
datetime: { component: 'DateTimeInput' },
|
|
18
|
+
date: { component: 'DateInput' },
|
|
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' }
|
|
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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './client/createClient';
|
|
2
|
+
export { default as DirectusVueProvider } from './vue/DirectusProvider';
|
|
3
|
+
export * from './vue/useDirectus';
|
|
4
|
+
export { DirectusProvider as DirectusReactProvider } from './react/DirectusProvider';
|
|
5
|
+
export * from './react/useDirectus';
|
|
6
|
+
export * from './schema/types';
|
|
7
|
+
export * from './schema/introspect';
|
|
8
|
+
export * from './utils/collections';
|
|
9
|
+
export * from './utils/fields';
|
|
10
|
+
export * from './generators/form-engine';
|
|
11
|
+
export * from './generators/table-engine';
|
|
12
|
+
export * from './generators/validation-engine';
|
|
13
|
+
export * from './generators/widget-registry';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from './client/createClient';
|
|
3
|
+
// Vue bindings
|
|
4
|
+
export { default as DirectusVueProvider } from './vue/DirectusProvider';
|
|
5
|
+
export * from './vue/useDirectus';
|
|
6
|
+
// React bindings
|
|
7
|
+
export { DirectusProvider as DirectusReactProvider } from './react/DirectusProvider';
|
|
8
|
+
export * from './react/useDirectus';
|
|
9
|
+
// Schema + generators + utils
|
|
10
|
+
export * from './schema/types';
|
|
11
|
+
export * from './schema/introspect';
|
|
12
|
+
export * from './utils/collections';
|
|
13
|
+
export * from './utils/fields';
|
|
14
|
+
export * from './generators/form-engine';
|
|
15
|
+
export * from './generators/table-engine';
|
|
16
|
+
export * from './generators/validation-engine';
|
|
17
|
+
export * from './generators/widget-registry';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MeeoviDirectusClient } from '../client/createClient';
|
|
3
|
+
export declare const DirectusContext: React.Context<MeeoviDirectusClient<any> | null>;
|
|
4
|
+
export declare function DirectusProvider({ client, children }: {
|
|
5
|
+
client: MeeoviDirectusClient<any>;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext } from 'react';
|
|
3
|
+
export const DirectusContext = createContext(null);
|
|
4
|
+
export function DirectusProvider({ client, children }) {
|
|
5
|
+
return (_jsx(DirectusContext.Provider, { value: client, children: children }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { DirectusContext } from './DirectusProvider';
|
|
3
|
+
export function useReactDirectus() {
|
|
4
|
+
const client = useContext(DirectusContext);
|
|
5
|
+
if (!client) {
|
|
6
|
+
throw new Error('Directus client not provided. Wrap your app in <DirectusProvider>.');
|
|
7
|
+
}
|
|
8
|
+
return client;
|
|
9
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DirectusSchema, DirectusField, DirectusCollection, DirectusRelation } from './types';
|
|
2
|
+
export declare function introspectCollections(client: any): Promise<DirectusCollection[]>;
|
|
3
|
+
export declare function introspectFields(client: any, collection: string): Promise<DirectusField[]>;
|
|
4
|
+
export declare function introspectRelations(client: any): Promise<DirectusRelation[]>;
|
|
5
|
+
export declare function introspectSchema(client: any): Promise<DirectusSchema>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// schema/introspect.ts
|
|
2
|
+
import { readFieldsByCollection, readItems } from '@directus/sdk';
|
|
3
|
+
export async function introspectCollections(client) {
|
|
4
|
+
return await client.request(readItems('directus_collection'));
|
|
5
|
+
}
|
|
6
|
+
export async function introspectFields(client, collection) {
|
|
7
|
+
return await client.request(readFieldsByCollection(collection));
|
|
8
|
+
}
|
|
9
|
+
export async function introspectRelations(client) {
|
|
10
|
+
return await client.request(readItems('directus_relations'));
|
|
11
|
+
}
|
|
12
|
+
export async function introspectSchema(client) {
|
|
13
|
+
const [collections, relations] = await Promise.all([
|
|
14
|
+
introspectCollections(client),
|
|
15
|
+
introspectRelations(client),
|
|
16
|
+
]);
|
|
17
|
+
const fields = [];
|
|
18
|
+
for (const col of collections) {
|
|
19
|
+
const colFields = await introspectFields(client, col.collection);
|
|
20
|
+
fields.push(...colFields);
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
collections,
|
|
24
|
+
fields,
|
|
25
|
+
relations,
|
|
26
|
+
directus_collections: collections,
|
|
27
|
+
directus_fields: fields,
|
|
28
|
+
directus_relations: relations,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface DirectusField {
|
|
2
|
+
collection: string;
|
|
3
|
+
field: string;
|
|
4
|
+
type: string;
|
|
5
|
+
interface?: string;
|
|
6
|
+
options?: Record<string, any>;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
readonly?: boolean;
|
|
9
|
+
hidden?: boolean;
|
|
10
|
+
sort?: number;
|
|
11
|
+
special?: string[];
|
|
12
|
+
validation?: Record<string, any>;
|
|
13
|
+
validation_message?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface DirectusRelation {
|
|
16
|
+
collection: string;
|
|
17
|
+
field: string;
|
|
18
|
+
related_collection: string | null;
|
|
19
|
+
meta?: Record<string, any>;
|
|
20
|
+
}
|
|
21
|
+
export interface DirectusCollection {
|
|
22
|
+
collection: string;
|
|
23
|
+
meta?: Record<string, any>;
|
|
24
|
+
schema?: Record<string, any>;
|
|
25
|
+
}
|
|
26
|
+
export interface DirectusSchema {
|
|
27
|
+
directus_collections: DirectusCollection[];
|
|
28
|
+
directus_relations: DirectusRelation[];
|
|
29
|
+
directus_fields: DirectusField[];
|
|
30
|
+
collections: DirectusCollection[];
|
|
31
|
+
fields: DirectusField[];
|
|
32
|
+
relations: DirectusRelation[];
|
|
33
|
+
}
|
|
34
|
+
export interface GeneratedFieldSchema {
|
|
35
|
+
key: string;
|
|
36
|
+
type: string;
|
|
37
|
+
widget: string;
|
|
38
|
+
required: boolean;
|
|
39
|
+
readonly: boolean;
|
|
40
|
+
hidden: boolean;
|
|
41
|
+
options?: Record<string, any>;
|
|
42
|
+
validation?: Record<string, any>;
|
|
43
|
+
}
|
|
44
|
+
export interface GeneratedCollectionSchema {
|
|
45
|
+
collection: string;
|
|
46
|
+
fields: GeneratedFieldSchema[];
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DirectusSchema, DirectusCollection, DirectusField } from '../schema/types';
|
|
2
|
+
export declare function getCollection(schema: DirectusSchema, name: string): DirectusCollection | undefined;
|
|
3
|
+
export declare function getCollectionFields(schema: DirectusSchema, name: string): DirectusField[];
|
|
4
|
+
export declare function listCollections(schema: DirectusSchema): string[];
|
|
5
|
+
export declare function isSingleton(schema: DirectusSchema, name: string): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function getCollection(schema, name) {
|
|
2
|
+
return schema.collections.find(c => c.collection === name);
|
|
3
|
+
}
|
|
4
|
+
export function getCollectionFields(schema, name) {
|
|
5
|
+
return schema.fields.filter(f => f.collection === name);
|
|
6
|
+
}
|
|
7
|
+
export function listCollections(schema) {
|
|
8
|
+
return schema.collections.map(c => c.collection);
|
|
9
|
+
}
|
|
10
|
+
export function isSingleton(schema, name) {
|
|
11
|
+
const col = getCollection(schema, name);
|
|
12
|
+
return col?.meta?.singleton === true;
|
|
13
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DirectusField, GeneratedFieldSchema } from '../schema/types';
|
|
2
|
+
export declare function mapFieldToWidget(field: DirectusField): string;
|
|
3
|
+
export declare function generateFieldSchema(field: DirectusField): GeneratedFieldSchema;
|
|
4
|
+
export declare function generateFieldsSchema(fields: DirectusField[]): GeneratedFieldSchema[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function mapFieldToWidget(field) {
|
|
2
|
+
if (field.interface)
|
|
3
|
+
return field.interface;
|
|
4
|
+
switch (field.type) {
|
|
5
|
+
case 'string':
|
|
6
|
+
case 'text':
|
|
7
|
+
return 'text';
|
|
8
|
+
case 'integer':
|
|
9
|
+
case 'bigInteger':
|
|
10
|
+
case 'float':
|
|
11
|
+
case 'decimal':
|
|
12
|
+
return 'number';
|
|
13
|
+
case 'boolean':
|
|
14
|
+
return 'checkbox';
|
|
15
|
+
case 'date':
|
|
16
|
+
case 'dateTime':
|
|
17
|
+
return 'date';
|
|
18
|
+
case 'json':
|
|
19
|
+
return 'json';
|
|
20
|
+
default:
|
|
21
|
+
return 'text';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function generateFieldSchema(field) {
|
|
25
|
+
return {
|
|
26
|
+
key: field.field,
|
|
27
|
+
type: field.type,
|
|
28
|
+
widget: mapFieldToWidget(field),
|
|
29
|
+
required: field.required ?? false,
|
|
30
|
+
readonly: field.readonly ?? false,
|
|
31
|
+
hidden: field.hidden ?? false,
|
|
32
|
+
options: field.options ?? {},
|
|
33
|
+
validation: field.validation ?? {},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function generateFieldsSchema(fields) {
|
|
37
|
+
return fields
|
|
38
|
+
.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0))
|
|
39
|
+
.map(generateFieldSchema);
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LivePreviewState {
|
|
2
|
+
token?: string;
|
|
3
|
+
}
|
|
4
|
+
export interface LivePreviewOptions {
|
|
5
|
+
query?: Record<string, string | undefined>;
|
|
6
|
+
initialState?: LivePreviewState;
|
|
7
|
+
}
|
|
8
|
+
export declare function createLivePreview(options: LivePreviewOptions): {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
state: LivePreviewState;
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function createLivePreview(options) {
|
|
2
|
+
const { query = {}, initialState = {} } = options;
|
|
3
|
+
const shouldEnable = Boolean(query.preview) && Boolean(query.token);
|
|
4
|
+
const state = {
|
|
5
|
+
token: query.token || initialState.token,
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
enabled: shouldEnable,
|
|
9
|
+
state,
|
|
10
|
+
};
|
|
11
|
+
}
|