@stonecrop/stonecrop 0.2.5 → 0.2.7
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/package.json +26 -11
- package/src/composable.ts +49 -0
- package/src/doctype.ts +39 -0
- package/src/exceptions.ts +13 -0
- package/src/index.ts +6 -0
- package/src/plugins/index.ts +23 -0
- package/src/registry.ts +35 -0
- package/src/router.ts +8 -0
- package/src/shims-vue.d.ts +5 -0
- package/src/stonecrop.ts +243 -0
- package/src/stores/data.ts +8 -0
- package/src/stores/index.ts +16 -0
- package/src/stores/xstate.ts +35 -0
- package/dist/stonecrop.js +0 -5319
- package/dist/stonecrop.umd.cjs +0 -5322
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/stonecrop",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "schema helper",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Tyler Matteson",
|
|
9
|
+
"email": "tyler@agritheory.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/agritheory/stonecrop",
|
|
14
|
+
"directory": "stonecrop"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/agritheory/stonecrop/issues"
|
|
18
|
+
},
|
|
7
19
|
"exports": {
|
|
8
20
|
".": {
|
|
9
21
|
"import": "./dist/stonecrop.js",
|
|
@@ -12,10 +24,11 @@
|
|
|
12
24
|
},
|
|
13
25
|
"main": "dist/stonecrop.js",
|
|
14
26
|
"module": "dist/stonecrop.js",
|
|
27
|
+
"umd": "dist/stonecrop.umd.cjs",
|
|
15
28
|
"types": "src/index",
|
|
16
29
|
"files": [
|
|
17
30
|
"dist/*",
|
|
18
|
-
"src
|
|
31
|
+
"src/*"
|
|
19
32
|
],
|
|
20
33
|
"dependencies": {
|
|
21
34
|
"@vueuse/core": "^9.13.0",
|
|
@@ -24,26 +37,28 @@
|
|
|
24
37
|
"pinia-shared-state": "^0.3.0",
|
|
25
38
|
"pinia-undo": "^0.1.9",
|
|
26
39
|
"pinia-xstate": "^1.0.9",
|
|
27
|
-
"vue": "^3.
|
|
40
|
+
"vue": "^3.4.23",
|
|
28
41
|
"vue-router": "^4",
|
|
29
42
|
"xstate": "~4.37.2"
|
|
30
43
|
},
|
|
31
44
|
"devDependencies": {
|
|
32
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
33
|
-
"@typescript-eslint/parser": "^
|
|
34
|
-
"@vitejs/plugin-vue": "^
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
|
46
|
+
"@typescript-eslint/parser": "^7.6.0",
|
|
47
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
35
48
|
"eslint": "^8.40.0",
|
|
36
49
|
"eslint-config-prettier": "^8.8.0",
|
|
37
50
|
"eslint-plugin-vue": "^9.11.1",
|
|
38
|
-
"typescript": "^5.
|
|
39
|
-
"vite": "^
|
|
40
|
-
"@stonecrop/aform": "0.2.
|
|
41
|
-
"@stonecrop/atable": "0.2.
|
|
51
|
+
"typescript": "^5.4.5",
|
|
52
|
+
"vite": "^5.2.9",
|
|
53
|
+
"@stonecrop/aform": "0.2.7",
|
|
54
|
+
"@stonecrop/atable": "0.2.7"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
42
58
|
},
|
|
43
59
|
"engines": {
|
|
44
60
|
"node": ">=20.11.0"
|
|
45
61
|
},
|
|
46
|
-
"umd": "dist/stonecrop.umd.cjs",
|
|
47
62
|
"scripts": {
|
|
48
63
|
"build": "vite build",
|
|
49
64
|
"dev": "vite serve stories/ -c vite.config.ts",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { inject, onBeforeMount, Ref, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
import Registry from './registry'
|
|
4
|
+
import { Stonecrop } from './stonecrop'
|
|
5
|
+
import { useDataStore } from './stores/data'
|
|
6
|
+
|
|
7
|
+
type StonecropReturn = {
|
|
8
|
+
stonecrop: Ref<Stonecrop>
|
|
9
|
+
isReady: Ref<boolean>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useStonecrop(registry?: Registry): StonecropReturn {
|
|
13
|
+
if (!registry) {
|
|
14
|
+
registry = inject<Registry>('$registry')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const store = useDataStore()
|
|
18
|
+
const stonecrop = ref(new Stonecrop(registry, store))
|
|
19
|
+
const isReady = ref(false)
|
|
20
|
+
|
|
21
|
+
onBeforeMount(async () => {
|
|
22
|
+
const route = registry.router.currentRoute.value
|
|
23
|
+
const doctypeSlug = route.params.records?.toString().toLowerCase()
|
|
24
|
+
const recordId = route.params.record?.toString().toLowerCase()
|
|
25
|
+
|
|
26
|
+
// TODO: handle views other than list and form views?
|
|
27
|
+
if (!doctypeSlug && !recordId) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// setup doctype via registry
|
|
32
|
+
const doctype = await registry.getMeta(doctypeSlug)
|
|
33
|
+
registry.addDoctype(doctype)
|
|
34
|
+
stonecrop.value.setup(doctype)
|
|
35
|
+
|
|
36
|
+
if (doctypeSlug) {
|
|
37
|
+
if (recordId) {
|
|
38
|
+
await stonecrop.value.getRecord(doctype, recordId)
|
|
39
|
+
} else {
|
|
40
|
+
await stonecrop.value.getRecords(doctype)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
stonecrop.value.runAction(doctype, 'LOAD', recordId ? [recordId] : undefined)
|
|
45
|
+
isReady.value = true
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return { stonecrop, isReady }
|
|
49
|
+
}
|
package/src/doctype.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Component } from 'vue'
|
|
2
|
+
|
|
3
|
+
import type { ImmutableDoctype } from 'types/index'
|
|
4
|
+
|
|
5
|
+
export default class DoctypeMeta {
|
|
6
|
+
readonly doctype: string
|
|
7
|
+
readonly schema: ImmutableDoctype['schema']
|
|
8
|
+
readonly workflow: ImmutableDoctype['workflow']
|
|
9
|
+
readonly actions: ImmutableDoctype['actions']
|
|
10
|
+
// TODO: allow different components for different views; probably
|
|
11
|
+
// should be defined in the schema instead?
|
|
12
|
+
readonly component?: Component
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
doctype: string,
|
|
16
|
+
schema: ImmutableDoctype['schema'],
|
|
17
|
+
workflow: ImmutableDoctype['workflow'],
|
|
18
|
+
actions: ImmutableDoctype['actions'],
|
|
19
|
+
component?: Component
|
|
20
|
+
) {
|
|
21
|
+
this.doctype = doctype
|
|
22
|
+
this.schema = schema
|
|
23
|
+
this.workflow = workflow
|
|
24
|
+
this.actions = actions
|
|
25
|
+
this.component = component
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get slug() {
|
|
29
|
+
// kebab case
|
|
30
|
+
return this.doctype
|
|
31
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
32
|
+
.replace(/[\s_]+/g, '-')
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get __typename() {
|
|
37
|
+
return this.doctype
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function NotImplementedError(message: string) {
|
|
2
|
+
this.message = message || ''
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
NotImplementedError.prototype = Object.create(Error.prototype, {
|
|
6
|
+
constructor: { value: NotImplementedError },
|
|
7
|
+
name: { value: 'NotImplemented' },
|
|
8
|
+
stack: {
|
|
9
|
+
get: function () {
|
|
10
|
+
return new Error().stack
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
})
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { App } from 'vue'
|
|
2
|
+
|
|
3
|
+
import type { InstallOptions } from 'types/index'
|
|
4
|
+
import Registry from '../registry'
|
|
5
|
+
import router from '../router'
|
|
6
|
+
import { pinia } from '../stores'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
install: (app: App, options?: InstallOptions) => {
|
|
10
|
+
const appRouter = options?.router || router
|
|
11
|
+
const registry = new Registry(appRouter, options?.getMeta)
|
|
12
|
+
|
|
13
|
+
app.use(appRouter)
|
|
14
|
+
app.use(pinia)
|
|
15
|
+
app.provide('$registry', registry)
|
|
16
|
+
|
|
17
|
+
if (options?.components) {
|
|
18
|
+
for (const [tag, component] of Object.entries(options.components)) {
|
|
19
|
+
app.component(tag, component)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
}
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Router } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
import DoctypeMeta from '@/doctype'
|
|
4
|
+
|
|
5
|
+
export default class Registry {
|
|
6
|
+
static _root: Registry
|
|
7
|
+
name: string
|
|
8
|
+
router: Router
|
|
9
|
+
registry: Record<string, DoctypeMeta>
|
|
10
|
+
getMeta?: (doctype: string) => DoctypeMeta | Promise<DoctypeMeta>
|
|
11
|
+
|
|
12
|
+
constructor(router: Router, getMeta?: (doctype: string) => DoctypeMeta | Promise<DoctypeMeta>) {
|
|
13
|
+
if (Registry._root) {
|
|
14
|
+
return Registry._root
|
|
15
|
+
}
|
|
16
|
+
Registry._root = this
|
|
17
|
+
this.name = 'Registry'
|
|
18
|
+
this.router = router
|
|
19
|
+
this.registry = {}
|
|
20
|
+
this.getMeta = getMeta
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addDoctype(doctype: DoctypeMeta) {
|
|
24
|
+
if (!(doctype.doctype in Object.keys(this.registry))) {
|
|
25
|
+
this.registry[doctype.slug] = doctype
|
|
26
|
+
}
|
|
27
|
+
if (!this.router.hasRoute(doctype.doctype)) {
|
|
28
|
+
this.router.addRoute({
|
|
29
|
+
path: `/${doctype.slug}`,
|
|
30
|
+
name: doctype.slug,
|
|
31
|
+
component: doctype.component,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/router.ts
ADDED
package/src/stonecrop.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { ImmutableDoctype, Schema } from 'types/index'
|
|
2
|
+
import DoctypeMeta from './doctype'
|
|
3
|
+
import { NotImplementedError } from './exceptions'
|
|
4
|
+
import Registry from './registry'
|
|
5
|
+
import { useDataStore } from './stores/data'
|
|
6
|
+
|
|
7
|
+
export class Stonecrop {
|
|
8
|
+
/**
|
|
9
|
+
* @property {Stonecrop} _root
|
|
10
|
+
* @description The root Stonecrop instance
|
|
11
|
+
*/
|
|
12
|
+
static _root: Stonecrop
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @property {string} name
|
|
16
|
+
* @description The name of the Stonecrop instance
|
|
17
|
+
* @example
|
|
18
|
+
* 'Stonecrop'
|
|
19
|
+
*/
|
|
20
|
+
readonly name = 'Stonecrop'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @property {Registry} registry
|
|
24
|
+
* @description The registry is an immutable collection of doctypes
|
|
25
|
+
* @example
|
|
26
|
+
* {
|
|
27
|
+
* 'task': {
|
|
28
|
+
* doctype: 'Task',
|
|
29
|
+
* schema: {
|
|
30
|
+
* title: 'string',
|
|
31
|
+
* description: 'string',
|
|
32
|
+
* ...
|
|
33
|
+
* }
|
|
34
|
+
* },
|
|
35
|
+
* ...
|
|
36
|
+
* }
|
|
37
|
+
* @see {@link Registry}
|
|
38
|
+
* @see {@link DoctypeMeta}
|
|
39
|
+
*/
|
|
40
|
+
readonly registry: Registry
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @property {Schema} schema - The Stonecrop schema
|
|
44
|
+
* @description The schema is a subset of the registry
|
|
45
|
+
* @example
|
|
46
|
+
* {
|
|
47
|
+
* doctype: 'Task',
|
|
48
|
+
* schema: {
|
|
49
|
+
* title: 'string',
|
|
50
|
+
* description: 'string',
|
|
51
|
+
* ...
|
|
52
|
+
* }
|
|
53
|
+
* }
|
|
54
|
+
* @see {@link Registry}
|
|
55
|
+
* @see {@link DoctypeMeta}
|
|
56
|
+
* @see {@link DoctypeMeta.schema}
|
|
57
|
+
*/
|
|
58
|
+
schema: Schema
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @property {ImmutableDoctype['workflow']} workflow
|
|
62
|
+
* @description The workflow is a subset of the registry
|
|
63
|
+
*/
|
|
64
|
+
workflow: ImmutableDoctype['workflow']
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @property {ImmutableDoctype['actions']} actions
|
|
68
|
+
* @description The actions are a subset of the registry
|
|
69
|
+
*/
|
|
70
|
+
actions: ImmutableDoctype['actions']
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @property {ReturnType<typeof useDataStore>} store
|
|
74
|
+
* @description The Pinia store that manages the mutable records
|
|
75
|
+
*/
|
|
76
|
+
store: ReturnType<typeof useDataStore>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @constructor
|
|
80
|
+
* @param {Registry} registry - The immutable registry
|
|
81
|
+
* @param {ReturnType<typeof useDataStore>} store - The mutable Pinia store
|
|
82
|
+
* @param {Schema} [schema] - (optional) The Stonecrop schema
|
|
83
|
+
* @param {ImmutableDoctype['workflow']} [workflow] - (optional) The Stonecrop workflow
|
|
84
|
+
* @param {ImmutableDoctype['actions']} [actions] - (optional) The Stonecrop actions
|
|
85
|
+
* @returns {Stonecrop} The Stonecrop instance
|
|
86
|
+
* @description The Stonecrop constructor initializes a new Stonecrop instance with the given registry, store, schema, workflow, and actions. If a Stonecrop instance has already been created, it returns the existing instance instead of creating a new one.
|
|
87
|
+
* @example
|
|
88
|
+
* const registry = new Registry()
|
|
89
|
+
* const store = useDataStore()
|
|
90
|
+
* const stonecrop = new Stonecrop(registry, store, schema, workflow, actions)
|
|
91
|
+
*/
|
|
92
|
+
constructor(
|
|
93
|
+
registry: Registry,
|
|
94
|
+
store: ReturnType<typeof useDataStore>,
|
|
95
|
+
schema?: Schema,
|
|
96
|
+
workflow?: ImmutableDoctype['workflow'],
|
|
97
|
+
actions?: ImmutableDoctype['actions']
|
|
98
|
+
) {
|
|
99
|
+
if (Stonecrop._root) {
|
|
100
|
+
return Stonecrop._root
|
|
101
|
+
}
|
|
102
|
+
Stonecrop._root = this
|
|
103
|
+
this.registry = registry
|
|
104
|
+
this.store = store
|
|
105
|
+
this.schema = schema // new Registry(schema)
|
|
106
|
+
this.workflow = workflow
|
|
107
|
+
this.actions = actions
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @method setup
|
|
112
|
+
* @param {DoctypeMeta} doctype - The doctype to setup
|
|
113
|
+
* @returns {void}
|
|
114
|
+
* @description Sets up the Stonecrop instance with the given doctype
|
|
115
|
+
* @example
|
|
116
|
+
* const doctype = await registry.getMeta('Task')
|
|
117
|
+
* stonecrop.setup(doctype)
|
|
118
|
+
*/
|
|
119
|
+
setup(doctype: DoctypeMeta): void {
|
|
120
|
+
this.getMeta(doctype)
|
|
121
|
+
this.getWorkflow(doctype)
|
|
122
|
+
this.getActions(doctype)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @method getMeta
|
|
127
|
+
* @param {DoctypeMeta} doctype - The doctype to get meta for
|
|
128
|
+
* @returns {DoctypeMeta}
|
|
129
|
+
* @see {@link DoctypeMeta}
|
|
130
|
+
* @throws NotImplementedError
|
|
131
|
+
* @description Gets the meta for the given doctype
|
|
132
|
+
* @example
|
|
133
|
+
* const doctype = await registry.getMeta('Task')
|
|
134
|
+
* const meta = stonecrop.getMeta(doctype)
|
|
135
|
+
*/
|
|
136
|
+
getMeta(doctype: DoctypeMeta): DoctypeMeta | Promise<DoctypeMeta> | never {
|
|
137
|
+
return this.registry.getMeta ? this.registry.getMeta(doctype.doctype) : new NotImplementedError(doctype.doctype)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @method getWorkflow
|
|
142
|
+
* @param {DoctypeMeta} doctype - The doctype to get workflow for
|
|
143
|
+
* @returns {void}
|
|
144
|
+
* @description Gets the workflow for the given doctype
|
|
145
|
+
* @example
|
|
146
|
+
* const doctype = await registry.getMeta('Task')
|
|
147
|
+
* stonecrop.getWorkflow(doctype)
|
|
148
|
+
*/
|
|
149
|
+
getWorkflow(doctype: DoctypeMeta): void {
|
|
150
|
+
const doctypeRegistry = this.registry.registry[doctype.slug]
|
|
151
|
+
this.workflow = doctypeRegistry.workflow
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @method getActions
|
|
156
|
+
* @param {DoctypeMeta} doctype - The doctype to get actions for
|
|
157
|
+
* @returns {void}
|
|
158
|
+
* @description Gets the actions for the given doctype
|
|
159
|
+
* @example
|
|
160
|
+
* const doctype = await registry.getMeta('Task')
|
|
161
|
+
* stonecrop.getActions(doctype)
|
|
162
|
+
*/
|
|
163
|
+
getActions(doctype: DoctypeMeta): void {
|
|
164
|
+
const doctypeRegistry = this.registry.registry[doctype.slug]
|
|
165
|
+
this.actions = doctypeRegistry.actions
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @method getRecords
|
|
170
|
+
* @param {DoctypeMeta} doctype - The doctype to get records for
|
|
171
|
+
* @param {RequestInit} [filters] - The filters to apply to the records
|
|
172
|
+
* @returns {Promise<void>}
|
|
173
|
+
* @description Gets the records for the given doctype
|
|
174
|
+
* @example
|
|
175
|
+
* const doctype = await registry.getMeta('Task')
|
|
176
|
+
* await stonecrop.getRecords(doctype)
|
|
177
|
+
* @example
|
|
178
|
+
* const doctype = await registry.getMeta('Task')
|
|
179
|
+
* const filters = JSON.stringify({ status: 'Open' })
|
|
180
|
+
* await stonecrop.getRecords(doctype, { body: filters })
|
|
181
|
+
*/
|
|
182
|
+
async getRecords(doctype: DoctypeMeta, filters?: RequestInit): Promise<void> {
|
|
183
|
+
this.store.$patch({ records: [] })
|
|
184
|
+
const records = await fetch(`/${doctype.slug}`, filters)
|
|
185
|
+
const data: Record<string, any>[] = await records.json()
|
|
186
|
+
this.store.$patch({ records: data })
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @method getRecord
|
|
191
|
+
* @param {DoctypeMeta} doctype - The doctype to get record for
|
|
192
|
+
* @param {string} id - The id of the record to get
|
|
193
|
+
* @returns {Promise<void>}
|
|
194
|
+
* @description Gets the record for the given doctype and id
|
|
195
|
+
* @example
|
|
196
|
+
* const doctype = await registry.getMeta('Task')
|
|
197
|
+
* await stonecrop.getRecord(doctype, 'TASK-00001')
|
|
198
|
+
*/
|
|
199
|
+
async getRecord(doctype: DoctypeMeta, id: string): Promise<void> {
|
|
200
|
+
this.store.$patch({ record: {} })
|
|
201
|
+
const record = await fetch(`/${doctype.slug}/${id}`)
|
|
202
|
+
const data: Record<string, any> = await record.json()
|
|
203
|
+
this.store.$patch({ record: data })
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @method runAction
|
|
208
|
+
* @param {DoctypeMeta} doctype - The doctype to run action for
|
|
209
|
+
* @param {string} action - The action to run
|
|
210
|
+
* @param {string[]} [id] - The id(s) of the record(s) to run action on
|
|
211
|
+
* @returns {void}
|
|
212
|
+
* @description Runs the action for the given doctype and id
|
|
213
|
+
* @example
|
|
214
|
+
* const doctype = await registry.getMeta('Task')
|
|
215
|
+
* stonecrop.runAction(doctype, 'CREATE')
|
|
216
|
+
* @example
|
|
217
|
+
* const doctype = await registry.getMeta('Task')
|
|
218
|
+
* stonecrop.runAction(doctype, 'UPDATE', ['TASK-00001'])
|
|
219
|
+
* @example
|
|
220
|
+
* const doctype = await registry.getMeta('Task')
|
|
221
|
+
* stonecrop.runAction(doctype, 'DELETE', ['TASK-00001'])
|
|
222
|
+
* @example
|
|
223
|
+
* const doctype = await registry.getMeta('Task')
|
|
224
|
+
* stonecrop.runAction(doctype, 'TRANSITION', ['TASK-00001', 'TASK-00002'])
|
|
225
|
+
*/
|
|
226
|
+
runAction(doctype: DoctypeMeta, action: string, id?: string[]): void {
|
|
227
|
+
const doctypeRegistry = this.registry.registry[doctype.slug]
|
|
228
|
+
const actions = doctypeRegistry.actions.get(action)
|
|
229
|
+
|
|
230
|
+
// trigger the action on the state machine
|
|
231
|
+
const { initialState } = this.workflow
|
|
232
|
+
this.workflow.transition(initialState, { type: action })
|
|
233
|
+
|
|
234
|
+
// run actions after state machine transition
|
|
235
|
+
if (actions.length > 0) {
|
|
236
|
+
actions.forEach(action => {
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
238
|
+
const actionFn = new Function(action)
|
|
239
|
+
actionFn(id)
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createPinia } from 'pinia'
|
|
2
|
+
import { PiniaSharedState } from 'pinia-shared-state'
|
|
3
|
+
import { PiniaUndo } from 'pinia-undo'
|
|
4
|
+
|
|
5
|
+
const pinia = createPinia()
|
|
6
|
+
|
|
7
|
+
// Pass the plugin to your application's pinia plugin
|
|
8
|
+
pinia.use(
|
|
9
|
+
PiniaSharedState({
|
|
10
|
+
enable: true,
|
|
11
|
+
initialize: true,
|
|
12
|
+
})
|
|
13
|
+
)
|
|
14
|
+
// pinia.use(PiniaUndo)
|
|
15
|
+
|
|
16
|
+
export { pinia }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import xstate from 'pinia-xstate'
|
|
3
|
+
import { createMachine } from 'xstate'
|
|
4
|
+
|
|
5
|
+
export const counterMachine = createMachine(
|
|
6
|
+
{
|
|
7
|
+
id: 'counter',
|
|
8
|
+
initial: 'active',
|
|
9
|
+
context: {
|
|
10
|
+
count: 0,
|
|
11
|
+
},
|
|
12
|
+
tsTypes: {} as import('./xstate.typegen').Typegen0,
|
|
13
|
+
states: {
|
|
14
|
+
active: {
|
|
15
|
+
on: {
|
|
16
|
+
INC: { actions: 'increment' },
|
|
17
|
+
DEC: { actions: 'decrement' },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
actions: {
|
|
24
|
+
increment: context => {
|
|
25
|
+
context.count = context.count + 1
|
|
26
|
+
},
|
|
27
|
+
decrement: context => {
|
|
28
|
+
context.count = context.count - 1
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// create a store using the xstate middleware
|
|
35
|
+
export const useCounterStore = defineStore(counterMachine.id, xstate(counterMachine))
|