@kaspernj/api-maker 1.0.311 → 1.0.313

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 CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.311",
19
+ "version": "1.0.313",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -52,7 +52,7 @@
52
52
  "on-location-changed": ">= 1.0.8",
53
53
  "qs": ">= 6.9.3",
54
54
  "replaceall": ">= 0.1.6",
55
- "set-state-compare": ">= 1.0.25",
55
+ "set-state-compare": ">= 1.0.26",
56
56
  "spark-md5": "^3.0.2",
57
57
  "strftime": ">= 0.10.0",
58
58
  "uniqunize": "^1.0.1",
@@ -1,305 +1,39 @@
1
- import Collection from "./collection"
2
- import debounce from "debounce"
3
- import {digg, digs} from "diggerize"
4
- import EventCreated from "./event-created"
5
- import EventDestroyed from "./event-destroyed"
6
- import EventUpdated from "./event-updated"
7
- import PropTypes from "prop-types"
8
- import PropTypesExact from "prop-types-exact"
9
- import React from "react"
10
- import withQueryParams from "on-location-changed/src/with-query-params"
11
-
12
- class CollectionLoader extends React.PureComponent {
13
- static defaultProps = {
14
- destroyEnabled: true,
15
- groupBy: ["id"],
16
- noRecordsAvailableContent: undefined,
17
- noRecordsFoundContent: undefined,
18
- pagination: false,
19
- preloads: [],
20
- select: {}
21
- }
22
-
23
- static propTypes = PropTypesExact({
24
- abilities: PropTypes.object,
25
- appHistory: PropTypes.object,
26
- className: PropTypes.string,
27
- collection: PropTypes.instanceOf(Collection),
28
- component: PropTypes.object,
29
- defaultParams: PropTypes.object,
30
- destroyEnabled: PropTypes.bool.isRequired,
31
- groupBy: PropTypes.array,
32
- modelClass: PropTypes.func.isRequired,
33
- noRecordsAvailableContent: PropTypes.func,
34
- noRecordsFoundContent: PropTypes.func,
35
- onModelsLoaded: PropTypes.func,
36
- pagination: PropTypes.bool.isRequired,
37
- paginateContent: PropTypes.func,
38
- preloads: PropTypes.array.isRequired,
39
- queryMethod: PropTypes.func,
40
- queryName: PropTypes.string,
41
- queryParams: PropTypes.object,
42
- select: PropTypes.object,
43
- selectColumns: PropTypes.object
44
- })
45
-
46
- shape = digg(this, "props", "component", "shape")
47
-
48
- constructor (props) {
49
- super(props)
50
-
51
- let queryName = props.queryName
52
-
53
- if (!queryName) queryName = digg(props.modelClass.modelClassData(), "collectionKey")
54
-
55
- this.shape.set({
56
- models: undefined,
57
- overallCount: undefined,
58
- query: undefined,
59
- queryName,
60
- queryPerKey: `${queryName}_per`,
61
- queryQName: `${queryName}_q`,
62
- querySName: `${queryName}_s`,
63
- queryPageName: `${queryName}_page`,
64
- qParams: undefined,
65
- result: undefined,
66
- searchParams: undefined,
67
- showNoRecordsAvailableContent: false,
68
- showNoRecordsFoundContent: false
69
- })
70
- }
71
-
72
- componentDidMount () {
73
- this.loadQParams()
74
- this.loadModels()
75
-
76
- const {noRecordsAvailableContent} = digs(this.props, "noRecordsAvailableContent")
77
-
78
- if (noRecordsAvailableContent) this.loadOverallCount()
79
- }
80
-
81
- componentDidUpdate(prevProps) {
82
- const {queryPageName, queryPerKey, queryQName, querySName} = digs(this.shape, "queryPageName", "queryPerKey", "queryQName", "querySName")
83
- let changed = false
84
-
85
- // Only load models again if certain things in the URL changes
86
- if (prevProps.queryParams[queryQName] != this.props.queryParams[queryQName]) {
87
- changed = true
88
- } else if (prevProps.queryParams[queryPageName] != this.props.queryParams[queryPageName]) {
89
- changed = true
90
- } else if (prevProps.queryParams[queryPerKey] != this.props.queryParams[queryPerKey]) {
91
- changed = true
92
- } else if (prevProps.queryParams[querySName] != this.props.queryParams[querySName]) {
93
- changed = true
94
- } else if (prevProps.collection != this.props.collection) {
95
- changed = true
96
- }
97
-
98
- if (changed) {
99
- this.loadQParams()
100
- this.loadModels()
101
- }
102
- }
103
-
104
- async loadOverallCount () {
105
- const baseQuery = this.props.collection || this.props.modelClass.all()
106
- const overallCount = await baseQuery.count()
107
-
108
- this.shape.set({
109
- overallCount,
110
- showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({overallCount}),
111
- showNoRecordsFoundContent: this.showNoRecordsFoundContent({overallCount})
112
- })
113
- }
114
-
115
- hasQParams() {
116
- const {queryParams} = digs(this.props, "queryParams")
117
- const {queryQName} = digs(this.shape, "queryQName")
118
-
119
- if (queryQName in queryParams) return true
120
-
121
- return false
122
- }
123
-
124
- qParams() {
125
- const {queryParams} = digs(this.props, "queryParams")
126
- const {queryQName} = digs(this.shape, "queryQName")
127
-
128
- if (this.hasQParams()) return JSON.parse(digg(queryParams, queryQName))
129
-
130
- return {}
131
- }
132
-
133
- loadQParams () {
134
- const {queryParams} = digs(this.props, "queryParams")
135
- const {querySName} = digs(this.shape, "querySName")
136
- const qParams = this.hasQParams() ? this.qParams() : Object.assign({}, this.props.defaultParams)
137
- const searchParams = []
138
-
139
- if (queryParams[querySName]) {
140
- for (const rawSearchParam of queryParams[querySName]) {
141
- const parsedSearchParam = JSON.parse(rawSearchParam)
142
-
143
- searchParams.push(parsedSearchParam)
144
- }
145
- }
146
-
147
- this.shape.set({qParams, searchParams})
148
- }
149
-
150
- loadModels = async () => {
151
- const {pagination, queryParams} = digs(this.props, "pagination", "queryParams")
152
- const {abilities, collection, groupBy, modelClass, onModelsLoaded, preloads, queryMethod, select, selectColumns} = this.props
153
- const {
154
- qParams,
155
- queryPageName,
156
- queryPerKey,
157
- queryQName,
158
- searchParams
159
- } = digs(
160
- this.shape,
161
- "qParams",
162
- "queryPageName",
163
- "queryPerKey",
164
- "queryQName",
165
- "searchParams"
166
- )
167
-
168
- let query = collection?.clone() || modelClass.ransack()
169
-
170
- if (pagination) {
171
- const page = queryParams[queryPageName] || 1
172
- let per = queryParams[queryPerKey] || 30
173
-
174
- if (per == "all") {
175
- per = 999_999_999
176
- } else {
177
- per = Number(per)
178
- }
179
-
180
- query.page(page).per(per)
181
- }
182
-
183
- if (groupBy) query = query.groupBy(...groupBy)
184
-
185
- query = query
186
- .ransack(qParams)
187
- .search(searchParams)
188
- .searchKey(queryQName)
189
- .pageKey(queryPageName)
190
- .perKey(queryPerKey)
191
- .preload(preloads)
192
- .select(select)
193
-
194
- if (abilities) query = query.abilities(abilities)
195
- if (selectColumns) query = query.selectColumns(selectColumns)
196
-
197
- let result
198
-
199
- if (queryMethod) {
200
- result = await queryMethod({query})
201
- } else {
202
- result = await query.result()
203
- }
204
-
205
- const models = result.models()
206
-
207
- if (onModelsLoaded) {
208
- onModelsLoaded({
209
- models,
210
- qParams,
211
- query,
212
- result
213
- })
214
- }
215
-
216
- this.shape.set({
217
- query,
218
- result,
219
- models: result.models(),
220
- showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({models}),
221
- showNoRecordsFoundContent: this.showNoRecordsFoundContent({models})
222
- })
223
- }
224
-
225
- loadModelsDebounce = debounce(digg(this, "loadModels"))
226
- onModelCreated = digg(this, "loadModels")
227
-
228
- onModelDestroyed = (args) => {
229
- const {models} = digs(this.shape, "models")
230
-
231
- this.shape.set({
232
- models: models.filter((model) => model.id() != args.model.id())
233
- })
234
- }
235
-
236
- onModelUpdated = (args) => {
237
- const {models} = digs(this.shape, "models")
238
- const updatedModel = digg(args, "model")
239
- const foundModel = models.find((model) => model.id() == updatedModel.id())
240
-
241
- if (foundModel) this.loadModelsDebounce()
242
- }
243
-
244
- showNoRecordsAvailableContent (args) {
245
- const {noRecordsAvailableContent} = digs(this.props, "noRecordsAvailableContent")
246
- let models, overallCount
247
-
248
- if (args.models !== undefined) {
249
- models = args.models
250
- } else if (this.shape.models !== undefined) {
251
- models = this.shape.models
252
- }
253
-
254
- if (args.overallCount !== undefined) {
255
- overallCount = args.overallCount
256
- } else if (this.shape.overallCount !== undefined) {
257
- overallCount = this.shape.overallCount
258
- }
259
-
260
- if (models === undefined || overallCount === undefined || noRecordsAvailableContent === undefined) return false
261
- if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return true
262
- }
263
-
264
- showNoRecordsFoundContent (args) {
265
- const {noRecordsAvailableContent, noRecordsFoundContent} = digs(this.props, "noRecordsAvailableContent", "noRecordsFoundContent")
266
- let models, overallCount
267
-
268
- if (args.models !== undefined) {
269
- models = args.models
270
- } else if (this.shape.models !== undefined) {
271
- models = this.shape.models
272
- }
273
-
274
- if (args.overallCount !== undefined) {
275
- overallCount = args.overallCount
276
- } else if (this.shape.overallCount !== undefined) {
277
- overallCount = this.shape.overallCount
278
- }
279
-
280
- if (models === undefined || noRecordsFoundContent === undefined) return false
281
-
282
- // Dont show noRecordsAvailableContent together with noRecordsAvailableContent
283
- if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return false
284
- if (models.length === 0 && noRecordsFoundContent) return true
285
- }
286
-
287
- render() {
288
- const {modelClass} = digs(this.props, "modelClass")
289
- const {models} = digs(this.shape, "models")
290
-
291
- return (
292
- <>
293
- <EventCreated modelClass={modelClass} onCreated={digg(this, "onModelCreated")} />
294
- {models && models.map((model) =>
295
- <React.Fragment key={model.id()}>
296
- <EventDestroyed model={model} onDestroyed={digg(this, "onModelDestroyed")} />
297
- <EventUpdated model={model} onUpdated={digg(this, "onModelUpdated")} />
298
- </React.Fragment>
299
- )}
300
- </>
301
- )
302
- }
1
+ import {digg} from "diggerize"
2
+ import {memo, useEffect} from "react"
3
+ import useCollection from "./use-collection"
4
+ import useShape from "set-state-compare/src/use-shape.js"
5
+
6
+ const CollectionLoader = (props) => {
7
+ const s = useShape(props)
8
+ const useCollectionResult = useCollection(props)
9
+ const cachePartsKeys = [
10
+ "modelIdsCacheString",
11
+ "overallCount",
12
+ "qParams",
13
+ "queryName",
14
+ "queryPerKey",
15
+ "queryQName",
16
+ "querySName",
17
+ "queryPageName",
18
+ "searchParams",
19
+ "showNoRecordsAvailableContent",
20
+ "showNoRecordsFoundContent"
21
+ ]
22
+ const cacheParts = []
23
+
24
+ for(const cachePartsKey of cachePartsKeys) {
25
+ cacheParts.push(digg(useCollectionResult, cachePartsKey))
26
+ }
27
+
28
+ s.updateMeta({useCollectionResult})
29
+
30
+ useEffect(() => {
31
+ const componentShape = digg(s.p.component, "shape")
32
+
33
+ componentShape.set(s.m.useCollectionResult)
34
+ }, cacheParts)
35
+
36
+ return null
303
37
  }
304
38
 
305
- export default withQueryParams(CollectionLoader)
39
+ export default memo(CollectionLoader)
@@ -3,7 +3,7 @@ import EventListener from "../../../event-listener"
3
3
  import PropTypes from "prop-types"
4
4
  import PropTypesExact from "prop-types-exact"
5
5
  import {memo, useCallback, useRef, useState} from "react"
6
- import useShape from "set-state-compare/src/use-shape"
6
+ import useShape from "set-state-compare/src/use-shape.js"
7
7
 
8
8
  const ApiMakerSuperAdminLayoutHeader = ({actions, onTriggerMenu, title}) => {
9
9
  const shape = useShape()
@@ -8,7 +8,7 @@ import PropTypes from "prop-types"
8
8
  import PropTypesExact from "prop-types-exact"
9
9
  import {memo, useCallback, useEffect, useState} from "react"
10
10
  import useCurrentUser from "../../use-current-user"
11
- import useShape from "set-state-compare/src/use-shape"
11
+ import useShape from "set-state-compare/src/use-shape.js"
12
12
 
13
13
  const NoAccess = React.lazy(() => import("./no-access"))
14
14
 
@@ -0,0 +1,38 @@
1
+ import CanCan from "./can-can.mjs"
2
+ import {useCallback, useEffect, useState} from "react"
3
+ import useShape from "set-state-compare/src/use-shape.js"
4
+
5
+ const useCanCan = (abilitiesCallback, dependencies) => {
6
+ const shape = useShape({abilitiesCallback})
7
+ const [canCan, setCanCan] = useState()
8
+
9
+ useEffect(() => {
10
+ loadAbilities()
11
+ }, dependencies)
12
+
13
+ const loadAbilities = useCallback(async () => {
14
+ const canCan = CanCan.current()
15
+ const abilities = shape.props.abilitiesCallback()
16
+
17
+ await canCan.loadAbilities(abilities)
18
+
19
+ setCanCan(canCan)
20
+ }, [])
21
+
22
+ const onResetAbilities = useCallback(() => {
23
+ setCanCan(undefined)
24
+ loadAbilities()
25
+ }, [])
26
+
27
+ useEffect(() => {
28
+ CanCan.current().events.addListener("onResetAbilities", onResetAbilities)
29
+
30
+ return () => {
31
+ CanCan.current().events.removeListener("onResetAbilities", onResetAbilities)
32
+ }
33
+ }, [])
34
+
35
+ return canCan
36
+ }
37
+
38
+ export default useCanCan
@@ -0,0 +1,277 @@
1
+ import Collection from "./collection.mjs"
2
+ import debounce from "debounce"
3
+ import {digg} from "diggerize"
4
+ import ModelEvents from "./model-events.mjs"
5
+ import PropTypes from "prop-types"
6
+ import PropTypesExact from "prop-types-exact"
7
+ import {useCallback, useEffect} from "react"
8
+ import useShape from "set-state-compare/src/use-shape.js"
9
+ import useQueryParams from "on-location-changed/src/use-query-params.js"
10
+
11
+ const useCollection = ({
12
+ abilities,
13
+ collection,
14
+ defaultParams,
15
+ groupBy = ["id"],
16
+ modelClass,
17
+ noRecordsAvailableContent = undefined,
18
+ noRecordsFoundContent = undefined,
19
+ onModelsLoaded,
20
+ pagination = false,
21
+ preloads = [],
22
+ queryMethod,
23
+ queryName,
24
+ select = {},
25
+ selectColumns
26
+ }) => {
27
+ const s = useShape({
28
+ abilities,
29
+ collection,
30
+ defaultParams,
31
+ groupBy,
32
+ modelClass,
33
+ noRecordsAvailableContent,
34
+ noRecordsFoundContent,
35
+ onModelsLoaded,
36
+ pagination,
37
+ preloads,
38
+ queryMethod,
39
+ select,
40
+ selectColumns
41
+ })
42
+
43
+ if (!queryName) queryName = digg(modelClass.modelClassData(), "collectionKey")
44
+
45
+ const setModels = s.useState("models")
46
+ const setOverallCount = s.useState("overallCount")
47
+ const setQuery = s.useState("query")
48
+ const setQueryName = s.useState("queryName", queryName)
49
+ const setQueryPerKey = s.useState("queryPerKey", `${s.s.queryName}_per`)
50
+ const setQueryQName = s.useState("queryQName", `${s.s.queryName}_q`)
51
+ const setQuerySName = s.useState("querySName", `${s.s.queryName}_s`)
52
+ const setQueryPageName = s.useState("queryPageName", `${s.s.queryName}_page`)
53
+ const setQParams = s.useState("qParams")
54
+ const setResult = s.useState("result")
55
+ const setSearchParams = s.useState("searchParams")
56
+ const setShowNoRecordsAvailableContent = s.useState("showNoRecordsAvailableContent", false)
57
+ const setShowNoRecordsFoundContent = s.useState("showNoRecordsFoundContent", false)
58
+ const queryParams = useQueryParams()
59
+ const modelIds = s.s.models?.map((model) => model.id())
60
+
61
+ let modelIdsCacheString
62
+
63
+ if (s.s.models === undefined) {
64
+ modelIdsCacheString = "models-undefined"
65
+ } else if (s.s.models.length === 0) {
66
+ modelIdsCacheString = "no-models"
67
+ } else {
68
+ modelIdsCacheString = modelIds?.join("---")
69
+ }
70
+
71
+ s.updateMeta({queryParams})
72
+
73
+ const loadOverallCount = useCallback(async () => {
74
+ const baseQuery = s.p.collection || s.p.modelClass.all()
75
+ const overallCount = await baseQuery.count()
76
+
77
+ setOverallCount(overallCount)
78
+ setShowNoRecordsAvailableContent(showNoRecordsAvailableContent({overallCount}))
79
+ setShowNoRecordsFoundContent(showNoRecordsFoundContent({overallCount}))
80
+ }, [])
81
+
82
+ const hasQParams = useCallback(() => {
83
+ if (s.s.queryQName in s.m.queryParams) return true
84
+
85
+ return false
86
+ }, [])
87
+
88
+ const qParams = useCallback(() => {
89
+ if (hasQParams()) return JSON.parse(digg(s.m.queryParams, s.s.queryQName))
90
+
91
+ return {}
92
+ }, [])
93
+
94
+ const loadQParams = useCallback(() => {
95
+ const qParamsToSet = hasQParams() ? qParams() : Object.assign({}, s.p.defaultParams)
96
+ const searchParams = []
97
+
98
+ if (s.m.queryParams[s.s.querySName]) {
99
+ for (const rawSearchParam of s.m.queryParams[s.s.querySName]) {
100
+ const parsedSearchParam = JSON.parse(rawSearchParam)
101
+
102
+ searchParams.push(parsedSearchParam)
103
+ }
104
+ }
105
+
106
+ setQParams(qParamsToSet)
107
+ setSearchParams(searchParams)
108
+ }, [])
109
+
110
+ const loadModels = useCallback(async () => {
111
+ let query = s.p.collection?.clone() || s.p.modelClass.ransack()
112
+
113
+ if (s.p.pagination) {
114
+ const page = s.m.queryParams[s.s.queryPageName] || 1
115
+ let per = s.m.queryParams[s.s.queryPerKey] || 30
116
+
117
+ if (per == "all") {
118
+ per = 999_999_999
119
+ } else {
120
+ per = Number(per)
121
+ }
122
+
123
+ query.page(page).per(per)
124
+ }
125
+
126
+ if (s.p.groupBy) query = query.groupBy(...s.p.groupBy)
127
+
128
+ query = query
129
+ .ransack(s.s.qParams)
130
+ .search(s.s.searchParams)
131
+ .searchKey(s.s.queryQName)
132
+ .pageKey(s.s.queryPageName)
133
+ .perKey(s.s.queryPerKey)
134
+ .preload(s.p.preloads)
135
+ .select(s.p.select)
136
+
137
+ if (s.p.abilities) query = query.abilities(s.p.abilities)
138
+ if (s.p.selectColumns) query = query.selectColumns(s.p.selectColumns)
139
+
140
+ let result
141
+
142
+ if (s.p.queryMethod) {
143
+ result = await s.p.queryMethod({query})
144
+ } else {
145
+ result = await query.result()
146
+ }
147
+
148
+ const models = result.models()
149
+
150
+ if (s.p.onModelsLoaded) {
151
+ s.p.onModelsLoaded({
152
+ models,
153
+ qParams: s.s.qParams,
154
+ query,
155
+ result
156
+ })
157
+ }
158
+
159
+ setQuery(query)
160
+ setResult(result)
161
+ setModels(result.models())
162
+ setShowNoRecordsAvailableContent(showNoRecordsAvailableContent({models}))
163
+ setShowNoRecordsFoundContent(showNoRecordsFoundContent({models}))
164
+ }, [])
165
+
166
+ const loadModelsDebounce = useCallback(debounce(loadModels), [])
167
+ const onModelDestroyed = useCallback((args) => {
168
+ setModels(s.s.models.filter((model) => model.id() != args.model.id()))
169
+ }, [])
170
+
171
+ const onModelUpdated = useCallback((args) => {
172
+ const updatedModel = digg(args, "model")
173
+ const foundModel = s.s.models.find((model) => model.id() == updatedModel.id())
174
+
175
+ if (foundModel) loadModelsDebounce()
176
+ }, [])
177
+
178
+ const showNoRecordsAvailableContent = useCallback((args) => {
179
+ let models, overallCount
180
+
181
+ if (args.models !== undefined) {
182
+ models = args.models
183
+ } else if (s.s.models !== undefined) {
184
+ models = s.s.models
185
+ }
186
+
187
+ if (args.overallCount !== undefined) {
188
+ overallCount = args.overallCount
189
+ } else if (s.s.overallCount !== undefined) {
190
+ overallCount = s.s.overallCount
191
+ }
192
+
193
+ if (models === undefined || overallCount === undefined || s.p.noRecordsAvailableContent === undefined) return false
194
+ if (models.length === 0 && overallCount === 0 && s.p.noRecordsAvailableContent) return true
195
+ }, [])
196
+
197
+ const showNoRecordsFoundContent = useCallback((args) => {
198
+ let models, overallCount
199
+
200
+ if (args.models !== undefined) {
201
+ models = args.models
202
+ } else if (s.s.models !== undefined) {
203
+ models = s.s.models
204
+ }
205
+
206
+ if (args.overallCount !== undefined) {
207
+ overallCount = args.overallCount
208
+ } else if (s.s.overallCount !== undefined) {
209
+ overallCount = s.s.overallCount
210
+ }
211
+
212
+ if (models === undefined || s.p.noRecordsFoundContent === undefined) return false
213
+
214
+ // Dont show noRecordsAvailableContent together with noRecordsAvailableContent
215
+ if (models.length === 0 && overallCount === 0 && s.p.noRecordsAvailableContent) return false
216
+ if (models.length === 0 && s.p.noRecordsFoundContent) return true
217
+ }, [])
218
+
219
+ useEffect(() => {
220
+ loadQParams()
221
+ loadModels()
222
+ }, [queryParams[s.s.queryQName], queryParams[s.s.queryPageName], queryParams[s.s.queryPerKey], queryParams[s.s.querySName], collection])
223
+
224
+ useEffect(() => {
225
+ if (s.p.noRecordsAvailableContent) loadOverallCount()
226
+ }, [])
227
+
228
+ useEffect(() => {
229
+ const connectCreated = ModelEvents.connectCreated(s.p.modelClass, loadModels)
230
+
231
+ return () => {
232
+ connectCreated.unsubscribe()
233
+ }
234
+ }, [])
235
+
236
+ useEffect(() => {
237
+ const connections = []
238
+
239
+ if (s.s.models) {
240
+ for(const model of s.s.models) {
241
+ connections.push(ModelEvents.connectUpdated(model, onModelUpdated))
242
+ connections.push(ModelEvents.connectDestroyed(model, onModelDestroyed))
243
+ }
244
+ }
245
+
246
+ return () => {
247
+ for(const connection of connections) {
248
+ connection.unsubscribe()
249
+ }
250
+ }
251
+ }, [modelIdsCacheString])
252
+
253
+ const result = Object.assign({}, s.state)
254
+
255
+ result.modelIdsCacheString = modelIdsCacheString
256
+
257
+ return result
258
+ }
259
+
260
+ useCollection.propTypes = PropTypesExact({
261
+ abilities: PropTypes.object,
262
+ collection: PropTypes.instanceOf(Collection),
263
+ defaultParams: PropTypes.object,
264
+ groupBy: PropTypes.array,
265
+ modelClass: PropTypes.func.isRequired,
266
+ noRecordsAvailableContent: PropTypes.func,
267
+ noRecordsFoundContent: PropTypes.func,
268
+ onModelsLoaded: PropTypes.func,
269
+ pagination: PropTypes.bool.isRequired,
270
+ preloads: PropTypes.array.isRequired,
271
+ queryMethod: PropTypes.func,
272
+ queryName: PropTypes.string,
273
+ select: PropTypes.object,
274
+ selectColumns: PropTypes.object
275
+ })
276
+
277
+ export default useCollection
@@ -1,16 +1,12 @@
1
- import CanCanLoader from "./can-can-loader"
1
+ import useCanCan from "./use-can-can.mjs"
2
+ import {memo} from "react"
2
3
 
3
- export default (WrappedComponent, abilities) => class WithCanCan extends React.PureComponent {
4
- state = {
5
- canCan: undefined
6
- }
4
+ export default (WrappedComponent, abilities) => {
5
+ const WithCanCan = (props) => {
6
+ const canCan = useCanCan(() => abilities)
7
7
 
8
- render() {
9
- return (
10
- <>
11
- <CanCanLoader abilities={abilities} component={this} />
12
- <WrappedComponent canCan={this.state.canCan} {...this.props} />
13
- </>
14
- )
8
+ return <WrappedComponent canCan={canCan} {...props} />
15
9
  }
10
+
11
+ return memo(WithCanCan)
16
12
  }
@@ -1,259 +1,16 @@
1
- import debounce from "debounce"
2
- import {digg, digs} from "diggerize"
3
- import EventCreated from "./event-created"
4
- import EventDestroyed from "./event-destroyed"
5
- import EventUpdated from "./event-updated"
6
- import React from "react"
7
- import Shape from "set-state-compare/src/shape"
8
- import withQueryParams from "on-location-changed/src/with-query-params"
1
+ import {digg} from "diggerize"
2
+ import {memo} from "react"
3
+ import useCollection from "./use-collection"
9
4
 
10
- export default (WrappedComponent, withCollectionArgs) => withQueryParams(class ApiMakerWithCollection extends React.PureComponent {
11
- constructor (props) {
12
- super(props)
5
+ export default (WrappedComponent, withCollectionArgs) => memo(() => {
6
+ const useCollectionResult = useCollection(withCollectionArgs)
7
+ const models = digg(useCollectionResult, "models")
8
+ const modelsArgName = inflection.camelize(digg(withCollectionArgs.modelClass.modelClassData(), "pluralName"), true)
9
+ const forwardArgs = {}
13
10
 
14
- let queryName = withCollectionArgs.queryName
11
+ forwardArgs[modelsArgName] = models
15
12
 
16
- if (!queryName) queryName = digg(withCollectionArgs.modelClass.modelClassData(), "collectionKey")
17
-
18
- this.shape = new Shape(this, {
19
- models: undefined,
20
- overallCount: undefined,
21
- query: undefined,
22
- queryName,
23
- queryPerKey: `${queryName}_per`,
24
- queryQName: `${queryName}_q`,
25
- querySName: `${queryName}_s`,
26
- queryPageName: `${queryName}_page`,
27
- qParams: undefined,
28
- result: undefined,
29
- searchParams: undefined,
30
- showNoRecordsAvailableContent: false,
31
- showNoRecordsFoundContent: false
32
- })
33
- }
34
-
35
- componentDidMount () {
36
- this.loadQParams()
37
- this.loadModels()
38
-
39
- const {noRecordsAvailableContent} = digs(withCollectionArgs)
40
-
41
- if (noRecordsAvailableContent) this.loadOverallCount()
42
- }
43
-
44
- componentDidUpdate(prevProps) {
45
- const {queryPageName, queryPerKey, queryQName, querySName} = digs(this.shape, "queryPageName", "queryPerKey", "queryQName", "querySName")
46
- let changed = false
47
-
48
- // Only load models again if certain things in the URL changes
49
- if (prevProps.queryParams[queryQName] != this.props.queryParams[queryQName]) {
50
- changed = true
51
- } else if (prevProps.queryParams[queryPageName] != this.props.queryParams[queryPageName]) {
52
- changed = true
53
- } else if (prevProps.queryParams[queryPerKey] != this.props.queryParams[queryPerKey]) {
54
- changed = true
55
- } else if (prevProps.queryParams[querySName] != this.props.queryParams[querySName]) {
56
- changed = true
57
- }
58
-
59
- if (changed) {
60
- this.loadQParams()
61
- this.loadModels()
62
- }
63
- }
64
-
65
- async loadOverallCount () {
66
- const baseQuery = withCollectionArgs.collection || withCollectionArgs.modelClass.all()
67
- const overallCount = await baseQuery.count()
68
-
69
- this.shape.set({
70
- overallCount,
71
- showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({overallCount}),
72
- showNoRecordsFoundContent: this.showNoRecordsFoundContent({overallCount})
73
- })
74
- }
75
-
76
- hasQParams() {
77
- const {queryParams} = digs(this.props, "queryParams")
78
- const {queryQName} = digs(this.shape, "queryQName")
79
-
80
- if (queryQName in queryParams) return true
81
-
82
- return false
83
- }
84
-
85
- qParams() {
86
- const {queryParams} = digs(this.props, "queryParams")
87
- const {queryQName} = digs(this.shape, "queryQName")
88
-
89
- if (this.hasQParams()) return JSON.parse(digg(queryParams, queryQName))
90
-
91
- return {}
92
- }
93
-
94
- loadQParams () {
95
- const {queryParams} = digs(this.props, "queryParams")
96
- const {querySName} = digs(this.shape, "querySName")
97
- const qParams = this.hasQParams() ? this.qParams() : Object.assign({}, withCollectionArgs.defaultParams)
98
- const searchParams = []
99
-
100
- if (queryParams[querySName]) {
101
- for (const rawSearchParam of queryParams[querySName]) {
102
- const parsedSearchParam = JSON.parse(rawSearchParam)
103
-
104
- searchParams.push(parsedSearchParam)
105
- }
106
- }
107
-
108
- this.shape.set({qParams, searchParams})
109
- }
110
-
111
- loadModels = async () => {
112
- const {queryParams} = digs(this.props, "queryParams")
113
- const {abilities, collection, groupBy, modelClass, onModelsLoaded, preloads, select, selectColumns} = withCollectionArgs
114
- const {
115
- qParams,
116
- queryPageName,
117
- queryPerKey,
118
- queryQName,
119
- searchParams
120
- } = digs(
121
- this.shape,
122
- "qParams",
123
- "queryPageName",
124
- "queryPerKey",
125
- "queryQName",
126
- "searchParams"
127
- )
128
- const page = queryParams[queryPageName] || 1
129
- let per = queryParams[queryPerKey] || 30
130
-
131
- if (per == "all") {
132
- per = 999_999_999
133
- } else {
134
- per = Number(per)
135
- }
136
-
137
- let query = collection?.clone() || modelClass.ransack()
138
-
139
- if (groupBy) query = query.groupBy(...groupBy)
140
-
141
- query = query
142
- .ransack(qParams)
143
- .search(searchParams)
144
- .searchKey(queryQName)
145
- .page(page)
146
- .pageKey(queryPageName)
147
- .per(per)
148
- .perKey(queryPerKey)
149
- .preload(preloads)
150
- .select(select)
151
-
152
- if (abilities) query = query.abilities(abilities)
153
- if (selectColumns) query = query.selectColumns(selectColumns)
154
-
155
- const result = await query.result()
156
- const models = result.models()
157
-
158
- if (onModelsLoaded) {
159
- onModelsLoaded({
160
- models,
161
- qParams,
162
- query,
163
- result
164
- })
165
- }
166
-
167
- this.shape.set({
168
- query,
169
- result,
170
- models: result.models(),
171
- showNoRecordsAvailableContent: this.showNoRecordsAvailableContent({models}),
172
- showNoRecordsFoundContent: this.showNoRecordsFoundContent({models})
173
- })
174
- }
175
-
176
- loadModelsDebounce = debounce(digg(this, "loadModels"))
177
- onModelCreated = digg(this, "loadModels")
178
-
179
- onModelDestroyed = ({destroyedModel}) => {
180
- const {models} = digs(this.shape, "models")
181
-
182
- this.shape.set({
183
- models: models.filter((model) => model.id() != destroyedModel.id())
184
- })
185
- }
186
-
187
- onModelUpdated = ({model: updatedModel}) => {
188
- const {models} = digs(this.shape, "models")
189
- const foundModel = models.find((model) => model.id() == updatedModel.id())
190
-
191
- if (foundModel) this.loadModelsDebounce()
192
- }
193
-
194
- showNoRecordsAvailableContent (args) {
195
- const {noRecordsAvailableContent} = withCollectionArgs
196
- let models, overallCount
197
-
198
- if (args.models !== undefined) {
199
- models = args.models
200
- } else if (this.shape.models !== undefined) {
201
- models = this.shape.models
202
- }
203
-
204
- if (args.overallCount !== undefined) {
205
- overallCount = args.overallCount
206
- } else if (this.shape.overallCount !== undefined) {
207
- overallCount = this.shape.overallCount
208
- }
209
-
210
- if (models === undefined || overallCount === undefined || noRecordsAvailableContent === undefined) return false
211
- if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return true
212
- }
213
-
214
- showNoRecordsFoundContent (args) {
215
- const {noRecordsAvailableContent, noRecordsFoundContent} = withCollectionArgs
216
- let models, overallCount
217
-
218
- if (args.models !== undefined) {
219
- models = args.models
220
- } else if (this.shape.models !== undefined) {
221
- models = this.shape.models
222
- }
223
-
224
- if (args.overallCount !== undefined) {
225
- overallCount = args.overallCount
226
- } else if (this.shape.overallCount !== undefined) {
227
- overallCount = this.shape.overallCount
228
- }
229
-
230
- if (models === undefined || noRecordsFoundContent === undefined) return false
231
-
232
- // Dont show noRecordsAvailableContent together with noRecordsAvailableContent
233
- if (models.length === 0 && overallCount === 0 && noRecordsAvailableContent) return false
234
- if (models.length === 0 && noRecordsFoundContent) return true
235
- }
236
-
237
- render() {
238
- const {modelClass} = digs(withCollectionArgs, "modelClass")
239
- const {onModelCreated, onModelDestroyed, onModelUpdated} = digs(this, "onModelCreated", "onModelDestroyed", "onModelUpdated")
240
- const {models} = digs(this.shape, "models")
241
- const modelsArgName = inflection.camelize(digg(withCollectionArgs.modelClass.modelClassData(), "pluralName"), true)
242
- const forwardArgs = {}
243
-
244
- forwardArgs[modelsArgName] = models
245
-
246
- return (
247
- <>
248
- <EventCreated modelClass={modelClass} onCreated={onModelCreated} />
249
- {models && models.map((model) =>
250
- <React.Fragment key={model.id()}>
251
- <EventDestroyed model={model} onDestroyed={onModelDestroyed} />
252
- <EventUpdated model={model} onUpdated={onModelUpdated} />
253
- </React.Fragment>
254
- )}
255
- <WrappedComponent {...forwardArgs} {...this.props} />
256
- </>
257
- )
258
- }
13
+ return (
14
+ <WrappedComponent {...forwardArgs} {...this.props} />
15
+ )
259
16
  })
@@ -1,54 +0,0 @@
1
- import ApiMakerEventEmitterListener from "./event-emitter-listener"
2
- import {digg, digs} from "diggerize"
3
- import CanCan from "./can-can.mjs"
4
- import PropTypes from "prop-types"
5
- import propTypesExact from "prop-types-exact"
6
- import React from "react"
7
-
8
- export default class ApiMakerCanCanLoader extends React.PureComponent {
9
- static propTypes = propTypesExact({
10
- abilities: PropTypes.array.isRequired,
11
- component: PropTypes.object.isRequired
12
- })
13
-
14
- componentDidMount () {
15
- this.loadAbilities()
16
- }
17
-
18
- async loadAbilities () {
19
- const canCan = CanCan.current()
20
- const {abilities} = digs(this.props, "abilities")
21
-
22
- await canCan.loadAbilities(abilities)
23
-
24
- this.updateComponent({canCan})
25
- }
26
-
27
- render () {
28
- const canCan = CanCan.current()
29
- const events = digg(canCan, "events")
30
-
31
- return (
32
- <ApiMakerEventEmitterListener
33
- event="onResetAbilities"
34
- events={events}
35
- onCalled={this.onResetAbilities}
36
- />
37
- )
38
- }
39
-
40
- onResetAbilities = () => {
41
- this.updateComponent({canCan: undefined})
42
- this.loadAbilities()
43
- }
44
-
45
- updateComponent (updatedState) {
46
- const {component} = digs(this.props, "component")
47
-
48
- if (component.shape) {
49
- component.shape.set(updatedState)
50
- } else {
51
- component.setState(updatedState)
52
- }
53
- }
54
- }