@stonecrop/nuxt 0.8.11 → 0.8.12
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 +74 -21
- package/dist/module.d.mts +72 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +60 -52
- package/dist/types.d.mts +1 -1
- package/package.json +10 -10
- package/dist/runtime/pages/StonecropPage.d.vue.ts +0 -3
- package/dist/runtime/pages/StonecropPage.vue +0 -139
- package/dist/runtime/pages/StonecropPage.vue.d.ts +0 -3
package/README.md
CHANGED
|
@@ -19,12 +19,11 @@ Stonecrop is a **schema-driven UI framework** that generates forms, tables, and
|
|
|
19
19
|
|
|
20
20
|
## Module Features
|
|
21
21
|
|
|
22
|
-
- **
|
|
23
|
-
- **Form & Table Components**: Pre-configured AForm and ATable components with HST integration
|
|
22
|
+
- **Route Generation**: Scans your `/doctypes` folder and registers routes using your own page components
|
|
24
23
|
- **Plugin System**: Auto-registers Stonecrop composables and utilities
|
|
25
24
|
- **Theme Support**: Import and customize Stonecrop themes
|
|
26
25
|
- **TypeScript First**: Full type safety and IntelliSense support
|
|
27
|
-
- **
|
|
26
|
+
- **Thin Wrapper**: The module is intentionally opinion-free — page rendering, queries, and navigation stay in your application
|
|
28
27
|
|
|
29
28
|
## Quick Setup
|
|
30
29
|
|
|
@@ -99,7 +98,7 @@ Create a JSON schema in `/doctypes/task.json`:
|
|
|
99
98
|
}
|
|
100
99
|
```
|
|
101
100
|
|
|
102
|
-
The module
|
|
101
|
+
The module picks up this file and, if `pageComponent` is configured, registers a route at the doctype's `slug` value (or `task` if no slug is set), passing the parsed schema into `route.meta`.
|
|
103
102
|
|
|
104
103
|
### Use the Stonecrop Composable
|
|
105
104
|
|
|
@@ -184,13 +183,14 @@ export default defineNuxtConfig({
|
|
|
184
183
|
modules: ['@stonecrop/nuxt'],
|
|
185
184
|
|
|
186
185
|
stonecrop: {
|
|
187
|
-
//
|
|
188
|
-
|
|
186
|
+
// Point to your own page component for slug-based routing (one route per doctype)
|
|
187
|
+
pageComponent: 'pages/StonecropPage.vue',
|
|
189
188
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
// Or supply a custom strategy for full control
|
|
190
|
+
// routeStrategy: (doctypes) => [...],
|
|
191
|
+
|
|
192
|
+
// Enable DocBuilder for visual schema editing
|
|
193
|
+
docbuilder: false,
|
|
194
194
|
},
|
|
195
195
|
|
|
196
196
|
// Import Stonecrop theme
|
|
@@ -201,24 +201,77 @@ export default defineNuxtConfig({
|
|
|
201
201
|
})
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
### Module Options
|
|
205
|
+
|
|
206
|
+
| Option | Type | Description |
|
|
207
|
+
|--------|------|-------------|
|
|
208
|
+
| `pageComponent` | `string` | Path (relative to `srcDir`) to your page component. The module registers one route per doctype at `/<slug>`, passing `schema` and `doctype` in `route.meta`. |
|
|
209
|
+
| `routeStrategy` | `RouteStrategyFn` | Custom function receiving all parsed doctypes; returns a `NuxtPage[]`. Takes priority over `pageComponent`. |
|
|
210
|
+
| `docbuilder` | `boolean` | Enable the DocBuilder feature at `/docbuilder`. Defaults to `false`. |
|
|
211
|
+
| `doctypesDir` | `string` | Override the doctypes directory path. Defaults to `doctypes/` inside `srcDir`. |
|
|
212
|
+
|
|
213
|
+
If neither `pageComponent` nor `routeStrategy` is configured the module logs a warning and skips doctype route registration.
|
|
205
214
|
|
|
206
|
-
|
|
215
|
+
## Route Generation
|
|
207
216
|
|
|
208
|
-
|
|
217
|
+
### Default: slug-based routing
|
|
218
|
+
|
|
219
|
+
The module scans your `doctypes/` folder and registers one route per JSON file:
|
|
209
220
|
|
|
210
221
|
```
|
|
211
222
|
doctypes/
|
|
212
|
-
├── task.json
|
|
213
|
-
├── user.json
|
|
214
|
-
└── project.json
|
|
223
|
+
├── task.json → /task (if no slug field)
|
|
224
|
+
├── user.json → /user/:id (if slug is "user/:id")
|
|
225
|
+
└── project.json → /project
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Each route's `meta` contains the parsed doctype:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
route.meta.schema // ParsedDoctype['fields']
|
|
232
|
+
route.meta.doctype // ParsedDoctype['data']
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Your page component receives these via `useRoute()`:
|
|
236
|
+
|
|
237
|
+
```vue
|
|
238
|
+
<script setup lang="ts">
|
|
239
|
+
const route = useRoute()
|
|
240
|
+
const schema = route.meta.schema // array of field definitions
|
|
241
|
+
const doctype = route.meta.doctype // full doctype JSON object
|
|
242
|
+
</script>
|
|
215
243
|
```
|
|
216
244
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
245
|
+
### Custom route strategy
|
|
246
|
+
|
|
247
|
+
For full control — multiple routes per doctype, conditional skipping, custom meta — provide a `RouteStrategyFn`:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import type { RouteStrategyFn } from '@stonecrop/nuxt'
|
|
251
|
+
import { resolve } from 'path'
|
|
252
|
+
|
|
253
|
+
const myStrategy: RouteStrategyFn = (doctypes) =>
|
|
254
|
+
doctypes
|
|
255
|
+
.filter(({ data }) => !data.parentDoctype) // skip child tables
|
|
256
|
+
.flatMap(({ fileName, data, fields }) => [
|
|
257
|
+
{
|
|
258
|
+
name: `${fileName}-list`,
|
|
259
|
+
path: `/${data.slug ?? fileName.toLowerCase()}`,
|
|
260
|
+
file: resolve('./pages/ListPage.vue'),
|
|
261
|
+
meta: { schema: fields, doctype: data, viewMode: 'list' },
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: `${fileName}-detail`,
|
|
265
|
+
path: `/${data.slug ?? fileName.toLowerCase()}/:id`,
|
|
266
|
+
file: resolve('./pages/DetailPage.vue'),
|
|
267
|
+
meta: { schema: fields, doctype: data, viewMode: 'detail' },
|
|
268
|
+
},
|
|
269
|
+
])
|
|
270
|
+
|
|
271
|
+
export default defineNuxtConfig({
|
|
272
|
+
stonecrop: { routeStrategy: myStrategy },
|
|
273
|
+
})
|
|
274
|
+
```
|
|
222
275
|
|
|
223
276
|
### Plugin Registration
|
|
224
277
|
|
package/dist/module.d.mts
CHANGED
|
@@ -1,4 +1,49 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { NuxtPage } from '@nuxt/schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parsed doctype data read from a JSON file in the doctypes directory.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
interface ParsedDoctype {
|
|
9
|
+
/** Original filename without extension (e.g., 'user-table', 'User') */
|
|
10
|
+
fileName: string;
|
|
11
|
+
/** Parsed JSON content of the doctype file */
|
|
12
|
+
data: Record<string, unknown>;
|
|
13
|
+
/** Schema fields array (from `schema` or `fields` property) */
|
|
14
|
+
fields: Record<string, unknown>[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Route strategy function signature.
|
|
18
|
+
*
|
|
19
|
+
* Receives all parsed doctypes from the `doctypes/` directory and returns
|
|
20
|
+
* an array of NuxtPage definitions to register. The user is responsible
|
|
21
|
+
* for providing the `file` property on each page (pointing to their own
|
|
22
|
+
* page component).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const myStrategy: RouteStrategyFn = (doctypes) => {
|
|
27
|
+
* return doctypes.flatMap(({ fileName, data, fields }) => [
|
|
28
|
+
* {
|
|
29
|
+
* name: `${fileName}-list`,
|
|
30
|
+
* path: `/${data.slug || fileName.toLowerCase()}`,
|
|
31
|
+
* file: resolve('./pages/MyListPage.vue'),
|
|
32
|
+
* meta: { schema: fields, doctype: data, viewMode: 'list' },
|
|
33
|
+
* },
|
|
34
|
+
* {
|
|
35
|
+
* name: `${fileName}-detail`,
|
|
36
|
+
* path: `/${data.slug || fileName.toLowerCase()}/:id`,
|
|
37
|
+
* file: resolve('./pages/MyDetailPage.vue'),
|
|
38
|
+
* meta: { schema: fields, doctype: data, viewMode: 'detail' },
|
|
39
|
+
* },
|
|
40
|
+
* ])
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
type RouteStrategyFn = (doctypes: ParsedDoctype[]) => NuxtPage[];
|
|
2
47
|
|
|
3
48
|
interface ModuleOptions {
|
|
4
49
|
router?: Record<string, unknown>;
|
|
@@ -6,8 +51,34 @@ interface ModuleOptions {
|
|
|
6
51
|
docbuilder?: boolean;
|
|
7
52
|
/** Path to doctypes folder (defaults to 'doctypes' in srcDir) */
|
|
8
53
|
doctypesDir?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Path to the page component used for default slug-based routing.
|
|
56
|
+
* When `routeStrategy` is not set, one route per doctype is registered
|
|
57
|
+
* at `/<slug>` (or `/<fileName>` if no slug) using this component.
|
|
58
|
+
*
|
|
59
|
+
* The path is resolved relative to the application's `srcDir`.
|
|
60
|
+
*
|
|
61
|
+
* @example `'pages/StonecropPage.vue'`
|
|
62
|
+
*/
|
|
63
|
+
pageComponent?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Custom route strategy function for full control over route generation.
|
|
66
|
+
* When provided, `pageComponent` is ignored and this function is called
|
|
67
|
+
* with all parsed doctypes to produce the NuxtPage array.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* routeStrategy: (doctypes) => doctypes.map(({ fileName, data, fields }) => ({
|
|
72
|
+
* name: `stonecrop-${fileName}`,
|
|
73
|
+
* path: `/${data.slug || fileName.toLowerCase()}`,
|
|
74
|
+
* file: resolve('./pages/MyPage.vue'),
|
|
75
|
+
* meta: { schema: fields, doctype: data },
|
|
76
|
+
* }))
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
routeStrategy?: RouteStrategyFn;
|
|
9
80
|
}
|
|
10
81
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
11
82
|
|
|
12
83
|
export { _default as default };
|
|
13
|
-
export type { ModuleOptions };
|
|
84
|
+
export type { ModuleOptions, ParsedDoctype, RouteStrategyFn };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -106,63 +106,71 @@ const module$1 = defineNuxtModule({
|
|
|
106
106
|
try {
|
|
107
107
|
const dirContents = await readdir(doctypesDir);
|
|
108
108
|
const schemas = dirContents.filter((file) => extname(file) === ".json");
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
extendPages(async (pages) => {
|
|
109
|
+
const doctypes = [];
|
|
110
|
+
for (const schema of schemas) {
|
|
112
111
|
try {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
} else {
|
|
122
|
-
logger.log("Skipping Stonecrop home page: root page already exists");
|
|
112
|
+
const schemaPath = resolve(doctypesDir, schema);
|
|
113
|
+
const fileContents = await readFile(schemaPath, "utf-8");
|
|
114
|
+
let schemaData;
|
|
115
|
+
try {
|
|
116
|
+
schemaData = JSON.parse(fileContents);
|
|
117
|
+
} catch (parseError) {
|
|
118
|
+
logger.error(`Failed to parse schema file '${schema}':`, parseError);
|
|
119
|
+
continue;
|
|
123
120
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const fileContents = await readFile(schemaPath, "utf-8");
|
|
129
|
-
let schemaData;
|
|
130
|
-
try {
|
|
131
|
-
schemaData = JSON.parse(fileContents);
|
|
132
|
-
} catch (parseError) {
|
|
133
|
-
logger.error(`Failed to parse schema file '${schema}':`, parseError);
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const schemaFields = schemaData.schema || schemaData.fields;
|
|
137
|
-
if (!schemaFields) {
|
|
138
|
-
logger.warn(`Schema file '${schema}' missing 'schema' or 'fields' property, skipping`);
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const routePath = schemaData.slug || schemaName.toLowerCase();
|
|
142
|
-
if (!pagePaths.includes(`/${routePath}`)) {
|
|
143
|
-
pages.unshift({
|
|
144
|
-
name: `stonecrop-${schemaName}`,
|
|
145
|
-
path: `/${routePath}`,
|
|
146
|
-
file: stonecropPage,
|
|
147
|
-
meta: {
|
|
148
|
-
schema: schemaFields,
|
|
149
|
-
doctype: schemaData
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
logger.log(`Added route: /${routePath} (${schemaName})`);
|
|
153
|
-
} else {
|
|
154
|
-
logger.warn(`Route /${routePath} already exists, skipping ${schemaName}`);
|
|
155
|
-
}
|
|
156
|
-
} catch (schemaError) {
|
|
157
|
-
logger.error(`Error processing schema '${schema}':`, schemaError);
|
|
158
|
-
}
|
|
121
|
+
const schemaFields = schemaData.schema || schemaData.fields;
|
|
122
|
+
if (!schemaFields) {
|
|
123
|
+
logger.warn(`Schema file '${schema}' missing 'schema' or 'fields' property, skipping`);
|
|
124
|
+
continue;
|
|
159
125
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
doctypes.push({
|
|
127
|
+
fileName: schema.replace(".json", ""),
|
|
128
|
+
data: schemaData,
|
|
129
|
+
fields: schemaFields
|
|
130
|
+
});
|
|
131
|
+
} catch (schemaError) {
|
|
132
|
+
logger.error(`Error processing schema '${schema}':`, schemaError);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
extendPages((pages) => {
|
|
136
|
+
const pagePaths = pages.map((page) => page.path);
|
|
137
|
+
if (!pagePaths.includes("/")) {
|
|
138
|
+
pages.unshift({
|
|
139
|
+
name: "stonecrop-home",
|
|
140
|
+
path: "/",
|
|
141
|
+
file: homepage
|
|
142
|
+
});
|
|
143
|
+
logger.log("Added Stonecrop home page at /");
|
|
144
|
+
} else {
|
|
145
|
+
logger.log("Skipping Stonecrop home page: root page already exists");
|
|
146
|
+
}
|
|
147
|
+
let generatedPages = [];
|
|
148
|
+
if (options.routeStrategy) {
|
|
149
|
+
generatedPages = options.routeStrategy(doctypes);
|
|
150
|
+
} else if (options.pageComponent) {
|
|
151
|
+
const componentPath = resolve(appDir, options.pageComponent);
|
|
152
|
+
generatedPages = doctypes.map(({ fileName, data, fields }) => {
|
|
153
|
+
const slug = data.slug || fileName.toLowerCase();
|
|
154
|
+
return {
|
|
155
|
+
name: `stonecrop-${fileName}`,
|
|
156
|
+
path: `/${slug}`,
|
|
157
|
+
file: componentPath,
|
|
158
|
+
meta: { schema: fields, doctype: data }
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
logger.warn(
|
|
163
|
+
"No routeStrategy or pageComponent configured \u2014 doctype routes will not be registered. Set pageComponent to a page path or provide a routeStrategy function."
|
|
164
164
|
);
|
|
165
165
|
}
|
|
166
|
+
for (const page of generatedPages) {
|
|
167
|
+
if (!pagePaths.includes(page.path)) {
|
|
168
|
+
pages.unshift(page);
|
|
169
|
+
logger.log(`Added route: ${page.path} (${page.name})`);
|
|
170
|
+
} else {
|
|
171
|
+
logger.warn(`Route ${page.path} already exists, skipping ${page.name}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
166
174
|
});
|
|
167
175
|
} catch (doctypeError) {
|
|
168
176
|
logger.error("Error setting up doctype pages:", doctypeError);
|
package/dist/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/nuxt",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.12",
|
|
4
4
|
"description": "Nuxt module for Stonecrop",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"jiti": "^2.4.2",
|
|
45
45
|
"pathe": "^2.0.3",
|
|
46
46
|
"prompts": "^2.4.2",
|
|
47
|
-
"@stonecrop/aform": "0.8.
|
|
48
|
-
"@stonecrop/
|
|
49
|
-
"@stonecrop/
|
|
50
|
-
"@stonecrop/
|
|
51
|
-
"@stonecrop/
|
|
52
|
-
"@stonecrop/
|
|
53
|
-
"@stonecrop/
|
|
54
|
-
"@stonecrop/
|
|
47
|
+
"@stonecrop/aform": "0.8.12",
|
|
48
|
+
"@stonecrop/atable": "0.8.12",
|
|
49
|
+
"@stonecrop/casl-middleware": "0.8.12",
|
|
50
|
+
"@stonecrop/node-editor": "0.8.12",
|
|
51
|
+
"@stonecrop/graphql-middleware": "0.8.12",
|
|
52
|
+
"@stonecrop/nuxt-grafserv": "0.8.12",
|
|
53
|
+
"@stonecrop/stonecrop": "0.8.12",
|
|
54
|
+
"@stonecrop/schema": "0.8.12"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@eslint/js": "^9.39.2",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"lint": "eslint .",
|
|
97
97
|
"test": "vitest run",
|
|
98
98
|
"test:ui": "vitest --ui",
|
|
99
|
-
"test:coverage": "vitest run --coverage",
|
|
99
|
+
"test:coverage": "vitest run --coverage.enabled --coverage.provider=istanbul",
|
|
100
100
|
"test:watch": "vitest watch",
|
|
101
101
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
102
102
|
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
-
declare const _default: typeof __VLS_export;
|
|
3
|
-
export default _default;
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="stonecrop-page">
|
|
3
|
-
<ClientOnly>
|
|
4
|
-
<component v-if="!loading" :is="rootComponent" v-bind="componentProps" @row-click="handleRowClick" />
|
|
5
|
-
<div v-else class="loading-state">Loading...</div>
|
|
6
|
-
<template #fallback>
|
|
7
|
-
<div class="loading-state">Loading...</div>
|
|
8
|
-
</template>
|
|
9
|
-
</ClientOnly>
|
|
10
|
-
</div>
|
|
11
|
-
</template>
|
|
12
|
-
|
|
13
|
-
<script setup>
|
|
14
|
-
import { useRoute, useRouter } from "nuxt/app";
|
|
15
|
-
import { onMounted, ref, computed, watch, markRaw } from "vue";
|
|
16
|
-
import { getDefaultComponent } from "@stonecrop/schema";
|
|
17
|
-
const route = useRoute();
|
|
18
|
-
const router = useRouter();
|
|
19
|
-
const loading = ref(true);
|
|
20
|
-
const data = ref(null);
|
|
21
|
-
const doctype = computed(() => route.meta.doctype);
|
|
22
|
-
const schemaFields = computed(() => route.meta.schema);
|
|
23
|
-
const rootComponent = computed(() => {
|
|
24
|
-
const fields = schemaFields.value || [];
|
|
25
|
-
const rootField = fields.find((f) => f.fieldtype === "Doctype" && f.component === "ATable");
|
|
26
|
-
if (rootField?.component) {
|
|
27
|
-
return rootField.component;
|
|
28
|
-
}
|
|
29
|
-
return "AForm";
|
|
30
|
-
});
|
|
31
|
-
const componentProps = computed(() => {
|
|
32
|
-
const fields = schemaFields.value || [];
|
|
33
|
-
const rootField = fields.find((f) => f.fieldtype === "Doctype" && f.component === "ATable");
|
|
34
|
-
if (rootField?.component === "ATable") {
|
|
35
|
-
return {
|
|
36
|
-
columns: rootField.columns || buildColumnsFromFields(fields),
|
|
37
|
-
rows: data.value || [],
|
|
38
|
-
config: rootField.config || { view: "list" }
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
modelValue: buildFormSchema(fields),
|
|
43
|
-
data: data.value || {}
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
function buildColumnsFromFields(fields) {
|
|
47
|
-
const excludeTypes = ["Text", "Attach", "JSON", "Table", "Doctype", "Link"];
|
|
48
|
-
return fields.filter((f) => !excludeTypes.includes(f.fieldtype)).slice(0, 8).map((f) => ({
|
|
49
|
-
name: f.fieldname,
|
|
50
|
-
label: f.label || f.fieldname,
|
|
51
|
-
fieldtype: f.fieldtype,
|
|
52
|
-
width: f.width || "15ch"
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
function buildFormSchema(fields) {
|
|
56
|
-
return fields.filter((f) => f.fieldtype !== "Doctype").map((f) => ({
|
|
57
|
-
fieldname: f.fieldname,
|
|
58
|
-
label: f.label || f.fieldname,
|
|
59
|
-
component: f.component || getDefaultComponent(f.fieldtype),
|
|
60
|
-
fieldtype: f.fieldtype,
|
|
61
|
-
required: f.required,
|
|
62
|
-
readOnly: f.readOnly,
|
|
63
|
-
options: f.options,
|
|
64
|
-
default: f.default
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
const isListView = computed(() => {
|
|
68
|
-
const fields = schemaFields.value || [];
|
|
69
|
-
const rootField = fields.find((f) => f.fieldtype === "Doctype" && f.component === "ATable");
|
|
70
|
-
return rootField?.component === "ATable";
|
|
71
|
-
});
|
|
72
|
-
async function fetchData() {
|
|
73
|
-
loading.value = true;
|
|
74
|
-
const doctypeName = doctype.value?.name;
|
|
75
|
-
if (!doctypeName) {
|
|
76
|
-
loading.value = false;
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
if (isListView.value) {
|
|
81
|
-
const query = `
|
|
82
|
-
query GetRecords($doctype: String!) {
|
|
83
|
-
stonecropRecords(doctype: $doctype) {
|
|
84
|
-
data
|
|
85
|
-
count
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
`;
|
|
89
|
-
const response = await $fetch("/graphql/", {
|
|
90
|
-
method: "POST",
|
|
91
|
-
body: {
|
|
92
|
-
query,
|
|
93
|
-
variables: { doctype: doctypeName }
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
data.value = response.data?.stonecropRecords?.data || [];
|
|
97
|
-
} else {
|
|
98
|
-
const recordId = route.params.id;
|
|
99
|
-
if (!recordId || recordId === "new") {
|
|
100
|
-
data.value = {};
|
|
101
|
-
loading.value = false;
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const query = `
|
|
105
|
-
query GetRecord($doctype: String!, $id: String!) {
|
|
106
|
-
stonecropRecord(doctype: $doctype, id: $id) {
|
|
107
|
-
data
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
`;
|
|
111
|
-
const response = await $fetch("/graphql/", {
|
|
112
|
-
method: "POST",
|
|
113
|
-
body: {
|
|
114
|
-
query,
|
|
115
|
-
variables: { doctype: doctypeName, id: recordId }
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
data.value = response.data?.stonecropRecord?.data || {};
|
|
119
|
-
}
|
|
120
|
-
} catch (e) {
|
|
121
|
-
console.warn("[@stonecrop/nuxt] Could not fetch data:", e);
|
|
122
|
-
data.value = isListView.value ? [] : {};
|
|
123
|
-
}
|
|
124
|
-
loading.value = false;
|
|
125
|
-
}
|
|
126
|
-
function handleRowClick(row) {
|
|
127
|
-
const id = row.id || row.name || row.slug;
|
|
128
|
-
if (id && isListView.value) {
|
|
129
|
-
const basePath = route.path.replace(/\/$/, "");
|
|
130
|
-
router.push(`${basePath}/${id}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
onMounted(fetchData);
|
|
134
|
-
watch(() => route.params, fetchData, { deep: true });
|
|
135
|
-
</script>
|
|
136
|
-
|
|
137
|
-
<style scoped>
|
|
138
|
-
.stonecrop-page{width:100%}.loading-state{color:var(--sc-gray-50);padding:2rem;text-align:center}
|
|
139
|
-
</style>
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
-
declare const _default: typeof __VLS_export;
|
|
3
|
-
export default _default;
|