@kaspernj/api-maker 1.0.395 → 1.0.397
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 +2 -2
- package/src/inputs/input.jsx +6 -3
- package/src/router/route.jsx +224 -0
- package/src/router/switch.jsx +78 -0
- package/src/router.jsx +37 -29
- package/src/super-admin/edit-page.jsx +0 -2
- package/src/use-updated-event.mjs +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaspernj/api-maker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.397",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"on-location-changed": ">= 1.0.13",
|
|
36
36
|
"qs": ">= 6.9.3",
|
|
37
37
|
"replaceall": ">= 0.1.6",
|
|
38
|
-
"set-state-compare": "^1.0.
|
|
38
|
+
"set-state-compare": "^1.0.49",
|
|
39
39
|
"spark-md5": "^3.0.2",
|
|
40
40
|
"stacktrace-parser": "^0.1.10",
|
|
41
41
|
"strftime": ">= 0.10.0",
|
package/src/inputs/input.jsx
CHANGED
|
@@ -34,15 +34,18 @@ const ApiMakerInputsInput = memo(shapeComponent(class ApiMakerInputsInput extend
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
setup() {
|
|
37
|
+
const {inputProps} = this.p
|
|
38
|
+
const {defaultValue, name} = inputProps
|
|
39
|
+
|
|
37
40
|
this.form = useForm()
|
|
38
41
|
this.visibleInputRef = useRef()
|
|
39
42
|
|
|
40
43
|
this.useStates({
|
|
41
|
-
blankInputName: digg(
|
|
44
|
+
blankInputName: digg(inputProps, "type") == "file"
|
|
42
45
|
})
|
|
43
46
|
useMemo(() => {
|
|
44
|
-
if (
|
|
45
|
-
this.tt.form?.setValue(
|
|
47
|
+
if (name) {
|
|
48
|
+
this.tt.form?.setValue(name, defaultValue)
|
|
46
49
|
}
|
|
47
50
|
}, [])
|
|
48
51
|
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import BaseComponent from "../base-component"
|
|
2
|
+
import {createContext, useContext, useMemo} from "react"
|
|
3
|
+
import memo from "set-state-compare/src/memo"
|
|
4
|
+
import PropTypes from "prop-types"
|
|
5
|
+
import propTypesExact from "prop-types-exact"
|
|
6
|
+
import {shapeComponent} from "set-state-compare/src/shape-component.js"
|
|
7
|
+
import Switch, {CurrentSwitchContext} from "./switch"
|
|
8
|
+
|
|
9
|
+
const CurrentPathContext = createContext([])
|
|
10
|
+
const ParamsContext = createContext({})
|
|
11
|
+
const RequireComponentContext = createContext(null)
|
|
12
|
+
const RouteContext = createContext(null)
|
|
13
|
+
const useParams = () => useContext(ParamsContext)
|
|
14
|
+
|
|
15
|
+
const Route = memo(shapeComponent(class Route extends BaseComponent {
|
|
16
|
+
static defaultProps = {
|
|
17
|
+
exact: false,
|
|
18
|
+
fallback: false,
|
|
19
|
+
includeInPath: true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static propTypes = propTypesExact({
|
|
23
|
+
component: PropTypes.string,
|
|
24
|
+
componentPath: PropTypes.string,
|
|
25
|
+
exact: PropTypes.bool.isRequired,
|
|
26
|
+
fallback: PropTypes.bool.isRequired,
|
|
27
|
+
includeInPath: PropTypes.bool.isRequired,
|
|
28
|
+
onMatch: PropTypes.func,
|
|
29
|
+
path: PropTypes.string
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
match = null
|
|
33
|
+
newParams = null
|
|
34
|
+
pathParts = null
|
|
35
|
+
|
|
36
|
+
setup() {
|
|
37
|
+
const {path} = this.props
|
|
38
|
+
const {pathsMatched, switchGroup} = useContext(CurrentSwitchContext)
|
|
39
|
+
const givenRoute = useContext(RouteContext)
|
|
40
|
+
const {pathShown} = switchGroup.s
|
|
41
|
+
|
|
42
|
+
this.requireComponent = useContext(RequireComponentContext)
|
|
43
|
+
this.currentParams = useContext(ParamsContext)
|
|
44
|
+
this.currentPath = useContext(CurrentPathContext)
|
|
45
|
+
this.switchGroup = switchGroup
|
|
46
|
+
|
|
47
|
+
this.routeParts = useMemo(() => {
|
|
48
|
+
let routeParts = givenRoute?.split("/")
|
|
49
|
+
|
|
50
|
+
return routeParts
|
|
51
|
+
}, [path, givenRoute])
|
|
52
|
+
|
|
53
|
+
this.pathParts = useMemo(() => path?.split("/"), [path])
|
|
54
|
+
|
|
55
|
+
this.newRouteParts = useMemo(
|
|
56
|
+
() => {
|
|
57
|
+
if (!path) {
|
|
58
|
+
if (givenRoute == "") {
|
|
59
|
+
return []
|
|
60
|
+
} else {
|
|
61
|
+
return this.routeParts
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this.routeParts.slice(this.pathParts.length, this.routeParts.length)
|
|
66
|
+
},
|
|
67
|
+
[givenRoute].concat(this.pathParts)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
this.useStates({Component: null, componentNotFound: null, matches: false})
|
|
71
|
+
|
|
72
|
+
useMemo(() => {
|
|
73
|
+
this.loadMatches()
|
|
74
|
+
}, [givenRoute, path, pathsMatched])
|
|
75
|
+
|
|
76
|
+
useMemo(() => {
|
|
77
|
+
if (this.hasSwitchMatch() && !this.s.Component && this.s.matches) {
|
|
78
|
+
if (this.props.onMatch) {
|
|
79
|
+
this.props.onMatch()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.loadComponent()
|
|
83
|
+
}
|
|
84
|
+
}, [path, pathShown, this.s.matches])
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
hasSwitchMatch = () => this.switchGroup.s.pathShown && this.switchGroup.s.pathShown == this.pathId()
|
|
88
|
+
|
|
89
|
+
pathId() {
|
|
90
|
+
const {fallback} = this.p
|
|
91
|
+
const {path} = this.props
|
|
92
|
+
let pathId
|
|
93
|
+
|
|
94
|
+
if (fallback) {
|
|
95
|
+
pathId = "[FALLBACK]"
|
|
96
|
+
} else if (!path) {
|
|
97
|
+
pathId = "[PATH-EMPTY]"
|
|
98
|
+
} else {
|
|
99
|
+
pathId = path
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return pathId
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
loadMatches() {
|
|
106
|
+
const {newRouteParts} = this.tt
|
|
107
|
+
const {component, path} = this.props
|
|
108
|
+
const {exact, includeInPath, fallback} = this.p
|
|
109
|
+
|
|
110
|
+
let matches = true
|
|
111
|
+
const params = {}
|
|
112
|
+
const componentPathParts = [...this.currentPath]
|
|
113
|
+
|
|
114
|
+
for (const pathPartIndex in this.pathParts) {
|
|
115
|
+
const pathPart = this.pathParts[pathPartIndex]
|
|
116
|
+
const translatedPathPart = I18n.t(`routes.${pathPart}`, {defaultValue: pathPart})
|
|
117
|
+
|
|
118
|
+
if (!(pathPartIndex in this.routeParts)) {
|
|
119
|
+
matches = false
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const routePart = this.routeParts[pathPartIndex]
|
|
124
|
+
|
|
125
|
+
if (pathPart.startsWith(":") && routePart) {
|
|
126
|
+
const paramName = pathPart.slice(1, pathPart.length)
|
|
127
|
+
|
|
128
|
+
params[paramName] = routePart
|
|
129
|
+
} else if (translatedPathPart != routePart) {
|
|
130
|
+
matches = false
|
|
131
|
+
break
|
|
132
|
+
} else if (!component && includeInPath) {
|
|
133
|
+
componentPathParts.push(pathPart)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (exact && newRouteParts.length > 0) {
|
|
138
|
+
matches = false
|
|
139
|
+
} else if (matches && path) {
|
|
140
|
+
matches = true
|
|
141
|
+
} else if (this.routeParts.length == 0) {
|
|
142
|
+
matches = true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const matchId = this.pathId()
|
|
146
|
+
|
|
147
|
+
if (!matches && fallback) {
|
|
148
|
+
matches = true
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (matches) {
|
|
152
|
+
if (component && includeInPath) {
|
|
153
|
+
componentPathParts.push(component)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const newParams = Object.assign(this.currentParams, params)
|
|
157
|
+
|
|
158
|
+
this.setInstance({componentPathParts, match: {params}, newParams})
|
|
159
|
+
this.setState({matches})
|
|
160
|
+
this.switchGroup?.setPathMatched(matchId, true)
|
|
161
|
+
} else {
|
|
162
|
+
this.setInstance({componentPathParts: null, match: null, newParams: null})
|
|
163
|
+
this.setState({matches})
|
|
164
|
+
this.switchGroup?.setPathMatched(matchId, false)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async loadComponent() {
|
|
169
|
+
const actualComponentPath = this.props.componentPath || this.tt.componentPathParts.join("/")
|
|
170
|
+
let Component
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const componentImport = await this.tt.requireComponent({routeDefinition: {component: actualComponentPath}})
|
|
174
|
+
|
|
175
|
+
Component = componentImport.default
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`Couldn't find component: ${actualComponentPath}`)
|
|
178
|
+
|
|
179
|
+
throw error
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.setState({Component, componentNotFound: !Component})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
render() {
|
|
186
|
+
const {componentPathParts, match, newParams, newRouteParts} = this.tt
|
|
187
|
+
const {component, path} = this.props
|
|
188
|
+
const {Component, componentNotFound, matches} = this.s
|
|
189
|
+
|
|
190
|
+
if (!matches || !this.hasSwitchMatch()) {
|
|
191
|
+
// Route isn't matching and shouldn't be rendered at all.
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!Component && !componentNotFound) {
|
|
196
|
+
// Route is matching but hasn't been loaded yet.
|
|
197
|
+
return (
|
|
198
|
+
<div>
|
|
199
|
+
Loading {component || this.props.componentPath || componentPathParts.join("/")}
|
|
200
|
+
</div>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!Component && componentNotFound) {
|
|
205
|
+
// Don't render anything if the component couldn't be found.
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<CurrentPathContext.Provider value={componentPathParts}>
|
|
211
|
+
<RouteContext.Provider value={newRouteParts.join("/")}>
|
|
212
|
+
<ParamsContext.Provider value={newParams}>
|
|
213
|
+
<Switch name={`route-group-${path}`} single={false}>
|
|
214
|
+
<Component match={match} />
|
|
215
|
+
</Switch>
|
|
216
|
+
</ParamsContext.Provider>
|
|
217
|
+
</RouteContext.Provider>
|
|
218
|
+
</CurrentPathContext.Provider>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
}))
|
|
222
|
+
|
|
223
|
+
export {RequireComponentContext, RouteContext, Switch, useParams}
|
|
224
|
+
export default Route
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import BaseComponent from "../base-component"
|
|
2
|
+
import {createContext} from "react"
|
|
3
|
+
import memo from "set-state-compare/src/memo"
|
|
4
|
+
import PropTypes from "prop-types"
|
|
5
|
+
import propTypesExact from "prop-types-exact"
|
|
6
|
+
import {shapeComponent} from "set-state-compare/src/shape-component.js"
|
|
7
|
+
|
|
8
|
+
const CurrentSwitchContext = createContext([])
|
|
9
|
+
|
|
10
|
+
const Switch = memo(shapeComponent(class Switch extends BaseComponent {
|
|
11
|
+
static defaultProps = {
|
|
12
|
+
name: "[no name]",
|
|
13
|
+
single: true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static propTypes = propTypesExact({
|
|
17
|
+
children: PropTypes.node,
|
|
18
|
+
name: PropTypes.string,
|
|
19
|
+
single: PropTypes.bool
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
pathsMatchedKeys = []
|
|
23
|
+
|
|
24
|
+
setup() {
|
|
25
|
+
this.useStates({
|
|
26
|
+
lastUpdate: new Date(),
|
|
27
|
+
pathShown: undefined,
|
|
28
|
+
pathsMatched: {}
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render() {
|
|
33
|
+
const {pathShown, pathsMatched} = this.s
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<CurrentSwitchContext.Provider value={{pathShown, pathsMatched, switchGroup: this}}>
|
|
37
|
+
{this.props.children}
|
|
38
|
+
</CurrentSwitchContext.Provider>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pathShown(pathsMatched) {
|
|
43
|
+
for (const pathMatched of this.tt.pathsMatchedKeys) {
|
|
44
|
+
const isPathMatched = pathsMatched[pathMatched]
|
|
45
|
+
|
|
46
|
+
if (isPathMatched) {
|
|
47
|
+
return pathMatched
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
setPathMatched(path, matched) {
|
|
53
|
+
const {pathsMatchedKeys} = this.tt
|
|
54
|
+
const {pathsMatched} = this.s
|
|
55
|
+
|
|
56
|
+
if (!path) throw new Error("No 'path' given")
|
|
57
|
+
if (pathsMatched[path] == matched) return
|
|
58
|
+
|
|
59
|
+
if (!pathsMatchedKeys.includes(path)) {
|
|
60
|
+
pathsMatchedKeys.push(path)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const newPathsMatched = {...this.s.pathsMatched}
|
|
64
|
+
|
|
65
|
+
newPathsMatched[path] = matched
|
|
66
|
+
|
|
67
|
+
const pathShown = this.pathShown(newPathsMatched)
|
|
68
|
+
|
|
69
|
+
this.setState({
|
|
70
|
+
lastUpdate: Math.random() + new Date().getTime(),
|
|
71
|
+
pathShown,
|
|
72
|
+
pathsMatched: newPathsMatched
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}))
|
|
76
|
+
|
|
77
|
+
export {CurrentSwitchContext}
|
|
78
|
+
export default Switch
|
package/src/router.jsx
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
|
+
import BaseComponent from "./base-component"
|
|
1
2
|
import PropTypes from "prop-types"
|
|
3
|
+
import propTypesExact from "prop-types-exact"
|
|
2
4
|
import React, {memo} from "react"
|
|
5
|
+
import {shapeComponent} from "set-state-compare/src/shape-component.js"
|
|
3
6
|
import {Suspense} from "react"
|
|
4
|
-
import
|
|
7
|
+
import useRouter from "./use-router"
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
export default memo(shapeComponent(class ApiMakerRouter extends BaseComponent {
|
|
10
|
+
static propTypes = propTypesExact({
|
|
11
|
+
history: PropTypes.object,
|
|
12
|
+
notFoundComponent: PropTypes.elementType,
|
|
13
|
+
path: PropTypes.string,
|
|
14
|
+
requireComponent: PropTypes.func.isRequired,
|
|
15
|
+
routeDefinitions: PropTypes.object,
|
|
16
|
+
routes: PropTypes.object
|
|
17
|
+
})
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
render() {
|
|
20
|
+
const {notFoundComponent, path, requireComponent, routeDefinitions, routes} = this.props
|
|
21
|
+
const {match} = useRouter({path, routes, routeDefinitions})
|
|
22
|
+
const {matchingRoute} = match
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</Suspense>
|
|
18
|
-
)
|
|
19
|
-
} else {
|
|
20
|
-
return null
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const Component = props.requireComponent({routeDefinition: matchingRoute.parsedRouteDefinition.routeDefinition})
|
|
24
|
+
if (!matchingRoute) {
|
|
25
|
+
if (notFoundComponent) {
|
|
26
|
+
const NotFoundComponent = notFoundComponent
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
28
|
+
return (
|
|
29
|
+
<Suspense fallback={<div />}>
|
|
30
|
+
<NotFoundComponent match={match} />
|
|
31
|
+
</Suspense>
|
|
32
|
+
)
|
|
33
|
+
} else {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
notFoundComponent: PropTypes.elementType,
|
|
35
|
-
requireComponent: PropTypes.func.isRequired
|
|
36
|
-
}
|
|
38
|
+
const Component = requireComponent({routeDefinition: matchingRoute.parsedRouteDefinition.routeDefinition})
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
return (
|
|
41
|
+
<Suspense fallback={<div />}>
|
|
42
|
+
<Component match={match} />
|
|
43
|
+
</Suspense>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
}))
|
|
@@ -103,8 +103,6 @@ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends Base
|
|
|
103
103
|
const {model} = this.tt
|
|
104
104
|
const formObject = this.s.form.asObject()
|
|
105
105
|
|
|
106
|
-
console.log({formObject})
|
|
107
|
-
|
|
108
106
|
model.assignAttributes(formObject)
|
|
109
107
|
await model.save()
|
|
110
108
|
Params.changeParams({mode: undefined, model_id: model.id()})
|
|
@@ -3,7 +3,9 @@ import debounceFunction from "debounce"
|
|
|
3
3
|
import ModelEvents from "./model-events.mjs"
|
|
4
4
|
import useShape from "set-state-compare/src/use-shape.js"
|
|
5
5
|
|
|
6
|
-
const apiMakerUseUpdatedEvent = (model, onUpdated,
|
|
6
|
+
const apiMakerUseUpdatedEvent = (model, onUpdated, props = {}) => {
|
|
7
|
+
const {active = true, debounce, onConnected, ...restProps} = props
|
|
8
|
+
|
|
7
9
|
if (Object.keys(restProps).length > 0) {
|
|
8
10
|
throw new Error(`Unknown props given to useUpdatedEvent: ${Object.keys(restProps).join(", ")}`)
|
|
9
11
|
}
|