@stonecrop/nuxt 0.6.0 → 0.6.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 +271 -29
- package/dist/module.json +1 -1
- package/dist/module.mjs +9 -8
- package/dist/runtime/plugin.js +4 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -3,57 +3,299 @@
|
|
|
3
3
|
[![npm version][npm-version-src]][npm-version-href]
|
|
4
4
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
5
|
|
|
6
|
-
Nuxt module for Stonecrop.
|
|
6
|
+
The official Nuxt module for Stonecrop - a schema-driven UI framework with event-driven workflows and hierarchical state management.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## What is Stonecrop?
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
10
|
+
Stonecrop is a **schema-driven UI framework** that generates forms, tables, and workflows from JSON schemas. Instead of manually creating CRUD interfaces for every data model, you define your data structure once and Stonecrop handles the UI generation, state management, and validation automatically.
|
|
11
|
+
|
|
12
|
+
**Key Benefits:**
|
|
13
|
+
- **Schema-Driven**: Define data models in JSON, get full CRUD interfaces automatically
|
|
14
|
+
- **HST State Management**: Hierarchical State Tree for complex, nested application state
|
|
15
|
+
- **FSM Workflows**: XState-powered finite state machines for predictable business logic
|
|
16
|
+
- **Nuxt Native**: First-class integration with Nuxt 4's architecture
|
|
17
|
+
- **Live Validation**: Real-time form validation based on schema rules
|
|
18
|
+
- **Excel-like Tables**: Rich table component with keyboard navigation and inline editing
|
|
19
|
+
|
|
20
|
+
## Module Features
|
|
21
|
+
|
|
22
|
+
- **Automatic Page Generation**: Creates routes from DocType schemas in your `/doctypes` folder
|
|
23
|
+
- **Form & Table Components**: Pre-configured AForm and ATable components with HST integration
|
|
24
|
+
- **Plugin System**: Auto-registers Stonecrop composables and utilities
|
|
25
|
+
- **Theme Support**: Import and customize Stonecrop themes
|
|
26
|
+
- **TypeScript First**: Full type safety and IntelliSense support
|
|
27
|
+
- **Zero Config**: Works out of the box with sensible defaults
|
|
14
28
|
|
|
15
29
|
## Quick Setup
|
|
16
30
|
|
|
17
|
-
Install the module to your Nuxt application
|
|
31
|
+
Install the module to your Nuxt application:
|
|
18
32
|
|
|
19
33
|
```bash
|
|
20
34
|
npx nuxi module add @stonecrop/nuxt
|
|
21
35
|
```
|
|
22
36
|
|
|
23
|
-
That's it! You can now use
|
|
37
|
+
That's it! You can now use Stonecrop in your Nuxt app.
|
|
24
38
|
|
|
39
|
+
## Basic Usage
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
### Define a DocType Schema
|
|
42
|
+
|
|
43
|
+
Create a JSON schema in `/doctypes/task.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"name": "task",
|
|
48
|
+
"label": "Task",
|
|
49
|
+
"schema": [
|
|
50
|
+
{
|
|
51
|
+
"fieldname": "title",
|
|
52
|
+
"label": "Title",
|
|
53
|
+
"fieldtype": "Data",
|
|
54
|
+
"required": true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"fieldname": "description",
|
|
58
|
+
"label": "Description",
|
|
59
|
+
"fieldtype": "Text"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"fieldname": "completed",
|
|
63
|
+
"label": "Completed",
|
|
64
|
+
"fieldtype": "Check"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The module automatically generates routes at `/task` for this DocType.
|
|
71
|
+
|
|
72
|
+
### Use the Stonecrop Composable
|
|
73
|
+
|
|
74
|
+
In your page or component:
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<script setup lang="ts">
|
|
78
|
+
import taskDoctype from '~/doctypes/task.json'
|
|
79
|
+
|
|
80
|
+
// HST-reactive form setup
|
|
81
|
+
const { stonecrop, provideHSTPath, handleHSTChange, formData } = useStonecrop({
|
|
82
|
+
doctype: taskDoctype,
|
|
83
|
+
recordId: 'task-123' // or undefined for new records
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Access the hierarchical state tree
|
|
87
|
+
const taskTitle = stonecrop.getStore().get('task.task-123.title')
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<AForm
|
|
92
|
+
:schema="formData.schema"
|
|
93
|
+
:data="formData"
|
|
94
|
+
@update="handleHSTChange"
|
|
95
|
+
/>
|
|
96
|
+
</template>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Understanding Schema-Driven Development
|
|
100
|
+
|
|
101
|
+
**Traditional Approach:**
|
|
102
|
+
```vue
|
|
103
|
+
<!-- Manual form creation -->
|
|
104
|
+
<template>
|
|
105
|
+
<form>
|
|
106
|
+
<input v-model="task.title" required />
|
|
107
|
+
<textarea v-model="task.description" />
|
|
108
|
+
<input type="checkbox" v-model="task.completed" />
|
|
109
|
+
<button @click="validate">Save</button>
|
|
110
|
+
</form>
|
|
111
|
+
</template>
|
|
112
|
+
|
|
113
|
+
<script setup>
|
|
114
|
+
// Manual validation logic
|
|
115
|
+
const validate = () => {
|
|
116
|
+
if (!task.title) {
|
|
117
|
+
errors.title = 'Required'
|
|
118
|
+
}
|
|
119
|
+
// ... more validation
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Stonecrop Approach:**
|
|
125
|
+
```vue
|
|
126
|
+
<!-- Schema generates form automatically -->
|
|
127
|
+
<template>
|
|
128
|
+
<AForm :schema="taskSchema" :data="formData" />
|
|
129
|
+
</template>
|
|
130
|
+
|
|
131
|
+
<script setup>
|
|
132
|
+
// Validation is automatic from schema
|
|
133
|
+
const { formData } = useStonecrop({
|
|
134
|
+
doctype: taskDoctype,
|
|
135
|
+
recordId: taskId
|
|
136
|
+
})
|
|
137
|
+
</script>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The schema defines:
|
|
141
|
+
- Field types (text input, checkbox, select, etc.)
|
|
142
|
+
- Validation rules (required, patterns, min/max)
|
|
143
|
+
- Labels and help text
|
|
144
|
+
- Relationships between data models
|
|
145
|
+
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
Add options to your `nuxt.config.ts`:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
export default defineNuxtConfig({
|
|
152
|
+
modules: ['@stonecrop/nuxt'],
|
|
153
|
+
|
|
154
|
+
stonecrop: {
|
|
155
|
+
// Enable DocBuilder for visual schema editing
|
|
156
|
+
docbuilder: true,
|
|
157
|
+
|
|
158
|
+
// Custom router configuration
|
|
159
|
+
router: {
|
|
160
|
+
// Router options
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// Import Stonecrop theme
|
|
165
|
+
css: [
|
|
166
|
+
'@stonecrop/themes/default/default.css',
|
|
167
|
+
// or your custom theme
|
|
168
|
+
]
|
|
169
|
+
})
|
|
170
|
+
```
|
|
27
171
|
|
|
28
|
-
|
|
29
|
-
<summary>Local development</summary>
|
|
172
|
+
## Module Behavior
|
|
30
173
|
|
|
31
|
-
|
|
32
|
-
# Install dependencies
|
|
33
|
-
npm install
|
|
174
|
+
### Automatic Page Generation
|
|
34
175
|
|
|
35
|
-
|
|
36
|
-
npm run dev:prepare
|
|
176
|
+
The module scans your `/doctypes` folder and creates routes automatically:
|
|
37
177
|
|
|
38
|
-
|
|
39
|
-
|
|
178
|
+
```
|
|
179
|
+
doctypes/
|
|
180
|
+
├── task.json → /task
|
|
181
|
+
├── user.json → /user
|
|
182
|
+
└── project.json → /project
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Each route uses the `StonecropPage.vue` layout that provides:
|
|
186
|
+
- List view with ATable component
|
|
187
|
+
- Detail view with AForm component
|
|
188
|
+
- HST state management
|
|
189
|
+
- Router integration for navigation
|
|
190
|
+
|
|
191
|
+
### Plugin Registration
|
|
192
|
+
|
|
193
|
+
The module auto-registers:
|
|
194
|
+
- `useStonecrop()` - Main composable for HST integration
|
|
195
|
+
- `useTableNavigation()` - Helper for table-to-detail navigation
|
|
196
|
+
- Pinia store configuration
|
|
197
|
+
- Component auto-imports (AForm, ATable, etc.)
|
|
198
|
+
|
|
199
|
+
## Why Schema-Driven?
|
|
200
|
+
|
|
201
|
+
**Problem:** Building CRUD applications is repetitive. Every data model needs:
|
|
202
|
+
- Forms for creating/editing
|
|
203
|
+
- Tables for listing
|
|
204
|
+
- Validation logic
|
|
205
|
+
- State management
|
|
206
|
+
- API integration
|
|
207
|
+
|
|
208
|
+
**Solution:** Define the structure once, generate everything automatically.
|
|
209
|
+
|
|
210
|
+
**Benefits:**
|
|
211
|
+
- **Faster Development**: Write less boilerplate code
|
|
212
|
+
- **Consistency**: All forms/tables follow the same patterns
|
|
213
|
+
- **Fewer Bugs**: Validation and state management are centralized
|
|
214
|
+
- **Self-Documenting**: Schemas serve as data model documentation
|
|
215
|
+
- **Easy Updates**: Change schema, UI updates automatically
|
|
216
|
+
|
|
217
|
+
## Advanced Features
|
|
218
|
+
|
|
219
|
+
### Hierarchical State Tree (HST)
|
|
220
|
+
|
|
221
|
+
HST provides path-based state addressing:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const store = stonecrop.getStore()
|
|
225
|
+
|
|
226
|
+
// Set nested values with dot notation
|
|
227
|
+
store.set('project.proj-1.tasks.task-1.completed', true)
|
|
228
|
+
|
|
229
|
+
// Get values anywhere in the tree
|
|
230
|
+
const completed = store.get('project.proj-1.tasks.task-1.completed')
|
|
40
231
|
|
|
41
|
-
|
|
42
|
-
|
|
232
|
+
// Navigate the tree hierarchy
|
|
233
|
+
const taskNode = store.getNode('project.proj-1.tasks.task-1')
|
|
234
|
+
const parent = taskNode.getParent() // Returns project node
|
|
235
|
+
const breadcrumbs = taskNode.getBreadcrumbs()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### XState Integration
|
|
239
|
+
|
|
240
|
+
Define workflows as finite state machines:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { createMachine } from 'xstate'
|
|
43
244
|
|
|
44
|
-
|
|
45
|
-
|
|
245
|
+
const taskMachine = createMachine({
|
|
246
|
+
id: 'task',
|
|
247
|
+
initial: 'draft',
|
|
248
|
+
states: {
|
|
249
|
+
draft: {
|
|
250
|
+
on: { SUBMIT: 'pending' }
|
|
251
|
+
},
|
|
252
|
+
pending: {
|
|
253
|
+
on: {
|
|
254
|
+
APPROVE: 'completed',
|
|
255
|
+
REJECT: 'draft'
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
completed: {
|
|
259
|
+
type: 'final'
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
})
|
|
46
263
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
264
|
+
// Stonecrop persists state machine data in HST
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Examples
|
|
50
268
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
269
|
+
Check out the [playground](./playground) for an example featuring:
|
|
270
|
+
- Permission management system (RBAC)
|
|
271
|
+
- DocType builder with visual state machine editor
|
|
272
|
+
- Complex nested forms with relationships
|
|
273
|
+
- Table views with inline editing
|
|
274
|
+
- HST state visualization
|
|
54
275
|
|
|
55
|
-
</details>
|
|
56
276
|
|
|
277
|
+
## Contribution
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# Install dependencies
|
|
281
|
+
npm install
|
|
282
|
+
|
|
283
|
+
# Generate type stubs
|
|
284
|
+
npm run dev:prepare
|
|
285
|
+
|
|
286
|
+
# Develop with the playground
|
|
287
|
+
npm run dev
|
|
288
|
+
|
|
289
|
+
# Build the playground
|
|
290
|
+
npm run dev:build
|
|
291
|
+
|
|
292
|
+
# Run ESLint
|
|
293
|
+
npm run lint
|
|
294
|
+
|
|
295
|
+
# Run Vitest
|
|
296
|
+
npm run test
|
|
297
|
+
npm run test:watch
|
|
298
|
+
```
|
|
57
299
|
|
|
58
300
|
<!-- Badges -->
|
|
59
301
|
[npm-version-src]: https://img.shields.io/npm/v/@stonecrop/nuxt/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -31,15 +31,16 @@ const module = defineNuxtModule({
|
|
|
31
31
|
extendPages(async (pages) => {
|
|
32
32
|
try {
|
|
33
33
|
const pagePaths = pages.map((page) => page.path);
|
|
34
|
-
if (pagePaths.includes("/")) {
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (!pagePaths.includes("/")) {
|
|
35
|
+
pages.unshift({
|
|
36
|
+
name: "stonecrop-home",
|
|
37
|
+
path: "/",
|
|
38
|
+
file: homepage
|
|
39
|
+
});
|
|
40
|
+
logger.log("Added Stonecrop home page at /");
|
|
41
|
+
} else {
|
|
42
|
+
logger.log("Skipping Stonecrop home page: root page already exists");
|
|
37
43
|
}
|
|
38
|
-
pages.unshift({
|
|
39
|
-
name: "stonecrop-home",
|
|
40
|
-
path: "/",
|
|
41
|
-
file: homepage
|
|
42
|
-
});
|
|
43
44
|
for (const schema of schemas) {
|
|
44
45
|
try {
|
|
45
46
|
const schemaName = schema.replace(".json", "");
|
package/dist/runtime/plugin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { install as AForm } from "@stonecrop/aform";
|
|
2
2
|
import { install as ATable } from "@stonecrop/atable";
|
|
3
|
-
import {
|
|
3
|
+
import { install as NodeEditor } from "@stonecrop/node-editor";
|
|
4
|
+
import StonecropPlugin from "@stonecrop/stonecrop";
|
|
4
5
|
import { createPinia } from "pinia";
|
|
5
6
|
import { defineNuxtPlugin, useRouter } from "nuxt/app";
|
|
6
7
|
export default defineNuxtPlugin((nuxt) => {
|
|
@@ -10,5 +11,6 @@ export default defineNuxtPlugin((nuxt) => {
|
|
|
10
11
|
app.use(pinia);
|
|
11
12
|
app.use(AForm);
|
|
12
13
|
app.use(ATable);
|
|
13
|
-
app.use(
|
|
14
|
+
app.use(NodeEditor);
|
|
15
|
+
app.use(StonecropPlugin, { router });
|
|
14
16
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/nuxt",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Nuxt module for Stonecrop",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@nuxt/kit": "^4.2.0",
|
|
35
35
|
"pinia": "^3.0.3",
|
|
36
|
-
"@stonecrop/aform": "0.6.
|
|
37
|
-
"@stonecrop/
|
|
38
|
-
"@stonecrop/
|
|
36
|
+
"@stonecrop/aform": "0.6.2",
|
|
37
|
+
"@stonecrop/node-editor": "0.6.2",
|
|
38
|
+
"@stonecrop/atable": "0.6.2",
|
|
39
|
+
"@stonecrop/stonecrop": "0.6.2"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@eslint/js": "^9.38.0",
|