@kaspernj/api-maker 1.0.373 → 1.0.375
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/base-model/attribute.mjs +14 -5
- package/src/base-model/column.mjs +13 -0
- package/src/bootstrap/attribute-row/index.jsx +17 -4
- package/src/cable-connection-pool.mjs +3 -7
- package/src/commands-pool.mjs +5 -31
- package/src/run-last.mjs +39 -0
- package/src/table/filters/attribute-element.jsx +42 -0
- package/src/table/filters/filter-form.jsx +36 -131
- package/src/table/filters/filter.jsx +55 -0
- package/src/table/filters/index.jsx +2 -50
- package/src/table/filters/reflection-element.jsx +35 -0
- package/src/table/filters/scope-element.jsx +39 -0
- package/src/table/model-row.jsx +19 -11
- package/src/table/table.jsx +8 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaspernj/api-maker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.375",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"on-location-changed": ">= 1.0.10",
|
|
36
36
|
"qs": ">= 6.9.3",
|
|
37
37
|
"replaceall": ">= 0.1.6",
|
|
38
|
-
"set-state-compare": "^1.0.
|
|
38
|
+
"set-state-compare": "^1.0.45",
|
|
39
39
|
"spark-md5": "^3.0.2",
|
|
40
40
|
"stacktrace-parser": "^0.1.10",
|
|
41
41
|
"strftime": ">= 0.10.0",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Column from "./column.mjs"
|
|
1
2
|
import {digg} from "diggerize"
|
|
2
3
|
|
|
3
4
|
export default class ApiMakerBaseModelAttribute {
|
|
@@ -5,10 +6,20 @@ export default class ApiMakerBaseModelAttribute {
|
|
|
5
6
|
this.attributeData = attributeData
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
getColumn() {
|
|
10
|
+
if (!this.column) {
|
|
11
|
+
const columnData = digg(this, "attributeData", "column")
|
|
12
|
+
|
|
13
|
+
if (columnData) {
|
|
14
|
+
this.column = new Column(columnData)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return this.column
|
|
10
19
|
}
|
|
11
20
|
|
|
21
|
+
isColumn = () => Boolean(digg(this, "attributeData", "column"))
|
|
22
|
+
|
|
12
23
|
isSelectedByDefault() {
|
|
13
24
|
const isSelectedByDefault = digg(this, "attributeData", "selected_by_default")
|
|
14
25
|
|
|
@@ -17,7 +28,5 @@ export default class ApiMakerBaseModelAttribute {
|
|
|
17
28
|
return false
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
name()
|
|
21
|
-
return digg(this, "attributeData", "name")
|
|
22
|
-
}
|
|
31
|
+
name = () => digg(this, "attributeData", "name")
|
|
23
32
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {digg} from "diggerize"
|
|
2
|
+
|
|
3
|
+
export default class ApiMakerBaseModelColumn {
|
|
4
|
+
constructor(columnData) {
|
|
5
|
+
if (!columnData) {
|
|
6
|
+
throw new Error("No column data was given")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
this.columnData = columnData
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getType = () => digg(this, "columnData", "type")
|
|
13
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import classNames from "classnames"
|
|
2
2
|
import {digg} from "diggerize"
|
|
3
|
+
import * as inflection from "inflection"
|
|
3
4
|
import MoneyFormatter from "../../money-formatter"
|
|
4
5
|
import PropTypes from "prop-types"
|
|
5
|
-
import
|
|
6
|
+
import {memo, useMemo} from "react"
|
|
7
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component.js"
|
|
6
8
|
import strftime from "strftime"
|
|
7
9
|
|
|
8
|
-
export default class ApiMakerBootstrapAttributeRow extends
|
|
10
|
+
export default memo(shapeComponent(class ApiMakerBootstrapAttributeRow extends ShapeComponent {
|
|
9
11
|
static defaultProps = {
|
|
10
12
|
checkIfAttributeLoaded: false
|
|
11
13
|
}
|
|
@@ -20,6 +22,13 @@ export default class ApiMakerBootstrapAttributeRow extends React.PureComponent {
|
|
|
20
22
|
value: PropTypes.node
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
setup() {
|
|
26
|
+
this.attribute = useMemo(
|
|
27
|
+
() => this.props.model?.constructor?.attributes()?.find((attribute) => attribute.name() == inflection.underscore(this.props.attribute)),
|
|
28
|
+
[this.props.attribute, this.props.model]
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
render () {
|
|
24
33
|
const {attribute, checkIfAttributeLoaded, children, className, identifier, label, model, value, ...restProps} = this.props
|
|
25
34
|
|
|
@@ -68,7 +77,11 @@ export default class ApiMakerBootstrapAttributeRow extends React.PureComponent {
|
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
valueContent(value) {
|
|
71
|
-
|
|
80
|
+
const columnType = this.attribute?.getColumn()?.getType()
|
|
81
|
+
|
|
82
|
+
if (columnType == "date") {
|
|
83
|
+
return I18n.l("date.formats.default", value)
|
|
84
|
+
} else if (value instanceof Date) {
|
|
72
85
|
return strftime("%Y-%m-%d %H:%M", value)
|
|
73
86
|
} else if (typeof value === "boolean") {
|
|
74
87
|
if (value) return I18n.t("js.shared.yes", {defaultValue: "Yes"})
|
|
@@ -80,4 +93,4 @@ export default class ApiMakerBootstrapAttributeRow extends React.PureComponent {
|
|
|
80
93
|
return value
|
|
81
94
|
}
|
|
82
95
|
}
|
|
83
|
-
}
|
|
96
|
+
}))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import CableSubscriptionPool from "./cable-subscription-pool.mjs"
|
|
2
2
|
import CableSubscription from "./cable-subscription.mjs"
|
|
3
3
|
import {dig} from "diggerize"
|
|
4
|
+
import RunLast from "./run-last.mjs"
|
|
4
5
|
|
|
5
6
|
const shared = {}
|
|
6
7
|
|
|
@@ -96,7 +97,7 @@ export default class ApiMakerCableConnectionPool {
|
|
|
96
97
|
currentSubscription[value].push(subscription)
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
this.
|
|
100
|
+
this.scheduleConnectUpcomingRunLast.queue()
|
|
100
101
|
|
|
101
102
|
return subscription
|
|
102
103
|
}
|
|
@@ -122,10 +123,5 @@ export default class ApiMakerCableConnectionPool {
|
|
|
122
123
|
this.cableSubscriptionPools.push(cableSubscriptionPool)
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
if (this.scheduleConnectUpcomingTimeout)
|
|
127
|
-
clearTimeout(this.scheduleConnectUpcomingTimeout)
|
|
128
|
-
|
|
129
|
-
this.scheduleConnectUpcomingTimeout = setTimeout(this.connectUpcoming, 0)
|
|
130
|
-
}
|
|
126
|
+
scheduleConnectUpcomingRunLast = new RunLast(this.connectUpcoming)
|
|
131
127
|
}
|
package/src/commands-pool.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import Deserializer from "./deserializer.mjs"
|
|
|
6
6
|
import {dig, digg} from "diggerize"
|
|
7
7
|
import events from "./events.mjs"
|
|
8
8
|
import FormDataObjectizer from "form-data-objectizer"
|
|
9
|
+
import RunLast from "./run-last.mjs"
|
|
9
10
|
import Serializer from "./serializer.mjs"
|
|
10
11
|
import SessionStatusUpdater from "./session-status-updater.mjs"
|
|
11
12
|
import ValidationError from "./validation-error.mjs"
|
|
@@ -26,9 +27,9 @@ export default class ApiMakerCommandsPool {
|
|
|
26
27
|
const promiseResult = pool.addCommand(data)
|
|
27
28
|
|
|
28
29
|
if (args.instant) {
|
|
29
|
-
pool.
|
|
30
|
+
pool.flushRunLast.run()
|
|
30
31
|
} else {
|
|
31
|
-
pool.
|
|
32
|
+
pool.flushRunLast.queue()
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
return promiseResult
|
|
@@ -115,13 +116,11 @@ export default class ApiMakerCommandsPool {
|
|
|
115
116
|
throw new Error("Couldnt successfully execute request")
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
async
|
|
119
|
+
flush = async () => {
|
|
119
120
|
if (this.commandsCount() == 0) {
|
|
120
121
|
return
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
this.clearTimeout()
|
|
124
|
-
|
|
125
124
|
const currentPool = this.pool
|
|
126
125
|
const currentPoolData = this.poolData
|
|
127
126
|
|
|
@@ -197,12 +196,6 @@ export default class ApiMakerCommandsPool {
|
|
|
197
196
|
commandData.reject(error)
|
|
198
197
|
}
|
|
199
198
|
|
|
200
|
-
clearTimeout() {
|
|
201
|
-
if (this.flushTimeout) {
|
|
202
|
-
clearTimeout(this.flushTimeout)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
199
|
isActive() {
|
|
207
200
|
if (this.commandsCount() > 0) {
|
|
208
201
|
return true
|
|
@@ -215,24 +208,5 @@ export default class ApiMakerCommandsPool {
|
|
|
215
208
|
return false
|
|
216
209
|
}
|
|
217
210
|
|
|
218
|
-
|
|
219
|
-
// If only waiting a single time, then other event-queue-jobs might be before us and queue other jobs that might queue calls to the backend
|
|
220
|
-
setFlushTimeout() {
|
|
221
|
-
this.flushTriggerCount = 0
|
|
222
|
-
this.clearTimeout()
|
|
223
|
-
this.flushTrigger()
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
flushTrigger = () => {
|
|
227
|
-
if (this.flushTriggerCount >= 10) {
|
|
228
|
-
this.flush()
|
|
229
|
-
} else {
|
|
230
|
-
this.flushTriggerCount++
|
|
231
|
-
this.flushTriggerQueue()
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
flushTriggerQueue() {
|
|
236
|
-
this.flushTimeout = setTimeout(this.flushTrigger, 0)
|
|
237
|
-
}
|
|
211
|
+
flushRunLast = new RunLast(this.flush)
|
|
238
212
|
}
|
package/src/run-last.mjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export default class RunLast {
|
|
2
|
+
constructor(callback) {
|
|
3
|
+
if (!callback) throw new Error("Empty callback given")
|
|
4
|
+
|
|
5
|
+
this.callback = callback
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Try to batch calls to backend while waiting for the event-queue-call to clear any other jobs before the request and reset on every flush call
|
|
9
|
+
// If only waiting a single time, then other event-queue-jobs might be before us and queue other jobs that might queue calls to the backend
|
|
10
|
+
queue() {
|
|
11
|
+
this.flushTriggerCount = 0
|
|
12
|
+
this.clearTimeout()
|
|
13
|
+
this.flushTrigger()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
flushTrigger = () => {
|
|
17
|
+
if (this.flushTriggerCount >= 10) {
|
|
18
|
+
this.run()
|
|
19
|
+
} else {
|
|
20
|
+
this.flushTriggerCount++
|
|
21
|
+
this.flushTriggerQueue()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
flushTriggerQueue() {
|
|
26
|
+
this.flushTimeout = setTimeout(this.flushTrigger, 0)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clearTimeout() {
|
|
30
|
+
if (this.flushTimeout) {
|
|
31
|
+
clearTimeout(this.flushTimeout)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
run() {
|
|
36
|
+
clearTimeout(this.flushTimeout)
|
|
37
|
+
this.callback()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Attribute from "../../base-model/attribute"
|
|
2
|
+
import {digg, digs} from "diggerize"
|
|
3
|
+
import * as inflection from "inflection"
|
|
4
|
+
import PropTypes from "prop-types"
|
|
5
|
+
import PropTypesExact from "prop-types-exact"
|
|
6
|
+
import {memo} from "react"
|
|
7
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component"
|
|
8
|
+
|
|
9
|
+
export default memo(shapeComponent(class AttributeElement extends ShapeComponent {
|
|
10
|
+
static propTypes = PropTypesExact({
|
|
11
|
+
active: PropTypes.bool.isRequired,
|
|
12
|
+
attribute: PropTypes.instanceOf(Attribute).isRequired,
|
|
13
|
+
currentModelClass: PropTypes.func.isRequired,
|
|
14
|
+
fikter: PropTypes.object,
|
|
15
|
+
onClick: PropTypes.func.isRequired
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
render() {
|
|
19
|
+
const {active, attribute, currentModelClass} = digs(this.props, "active", "attribute", "currentModelClass")
|
|
20
|
+
const style = {}
|
|
21
|
+
|
|
22
|
+
if (active) style.fontWeight = "bold"
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
className="attribute-element"
|
|
27
|
+
data-attribute-name={attribute.name()}
|
|
28
|
+
data-model-class={currentModelClass.modelClassData().name}
|
|
29
|
+
onClick={digg(this, "onAttributeClicked")}
|
|
30
|
+
style={style}
|
|
31
|
+
>
|
|
32
|
+
{currentModelClass.humanAttributeName(inflection.camelize(attribute.name(), true))}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onAttributeClicked = (e) => {
|
|
38
|
+
e.preventDefault()
|
|
39
|
+
|
|
40
|
+
this.props.onClick({attribute: digg(this, "props", "attribute")})
|
|
41
|
+
}
|
|
42
|
+
}))
|
|
@@ -1,116 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AttributeElement from "./attribute-element"
|
|
2
2
|
import {digg, digs} from "diggerize"
|
|
3
3
|
import * as inflection from "inflection"
|
|
4
4
|
import Input from "../../inputs/input"
|
|
5
5
|
import PropTypes from "prop-types"
|
|
6
6
|
import PropTypesExact from "prop-types-exact"
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import {memo, useMemo, useRef} from "react"
|
|
8
|
+
import ReflectionElement from "./reflection-element"
|
|
9
|
+
import ScopeElement from "./scope-element"
|
|
9
10
|
import Select from "../../inputs/select"
|
|
10
11
|
import Services from "../../services.mjs"
|
|
11
|
-
import
|
|
12
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component"
|
|
12
13
|
|
|
13
|
-
class
|
|
14
|
-
static propTypes = PropTypesExact({
|
|
15
|
-
active: PropTypes.bool.isRequired,
|
|
16
|
-
attribute: PropTypes.instanceOf(Attribute).isRequired,
|
|
17
|
-
currentModelClass: PropTypes.func.isRequired,
|
|
18
|
-
fikter: PropTypes.object,
|
|
19
|
-
onClick: PropTypes.func.isRequired
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
render() {
|
|
23
|
-
const {active, attribute, currentModelClass} = digs(this.props, "active", "attribute", "currentModelClass")
|
|
24
|
-
const style = {}
|
|
25
|
-
|
|
26
|
-
if (active) style.fontWeight = "bold"
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div
|
|
30
|
-
className="attribute-element"
|
|
31
|
-
data-attribute-name={attribute.name()}
|
|
32
|
-
data-model-class={currentModelClass.modelClassData().name}
|
|
33
|
-
onClick={digg(this, "onAttributeClicked")}
|
|
34
|
-
style={style}
|
|
35
|
-
>
|
|
36
|
-
{currentModelClass.humanAttributeName(inflection.camelize(attribute.name(), true))}
|
|
37
|
-
</div>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
onAttributeClicked = (e) => {
|
|
42
|
-
e.preventDefault()
|
|
43
|
-
|
|
44
|
-
this.props.onClick({attribute: digg(this, "props", "attribute")})
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
class ReflectionElement extends React.PureComponent {
|
|
49
|
-
static propTypes = PropTypesExact({
|
|
50
|
-
currentModelClass: PropTypes.func.isRequired,
|
|
51
|
-
onClick: PropTypes.func.isRequired,
|
|
52
|
-
reflection: PropTypes.instanceOf(Reflection).isRequired
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
render() {
|
|
56
|
-
const {currentModelClass, reflection} = digs(this.props, "currentModelClass", "reflection")
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<div
|
|
60
|
-
className="reflection-element"
|
|
61
|
-
data-model-class={currentModelClass.modelClassData().name}
|
|
62
|
-
data-reflection-name={reflection.name()}
|
|
63
|
-
onClick={digg(this, "onReflectionClicked")}
|
|
64
|
-
>
|
|
65
|
-
{currentModelClass.humanAttributeName(reflection.name())}
|
|
66
|
-
</div>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
onReflectionClicked = (e) => {
|
|
71
|
-
e.preventDefault()
|
|
72
|
-
|
|
73
|
-
this.props.onClick({reflection: digg(this, "props", "reflection")})
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
class ScopeElement extends React.PureComponent {
|
|
78
|
-
static defaultProps = {
|
|
79
|
-
active: false
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
static propTypes = {
|
|
83
|
-
active: PropTypes.bool.isRequired,
|
|
84
|
-
scope: PropTypes.object.isRequired
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
render() {
|
|
88
|
-
const {active, scope} = this.props
|
|
89
|
-
const style = {}
|
|
90
|
-
|
|
91
|
-
if (active) style.fontWeight = "bold"
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<div
|
|
95
|
-
className="scope-element"
|
|
96
|
-
key={scope.name()}
|
|
97
|
-
onClick={digg(this, "onScopeClicked")}
|
|
98
|
-
style={style}
|
|
99
|
-
>
|
|
100
|
-
{scope.name()}
|
|
101
|
-
</div>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
onScopeClicked = (e) => {
|
|
106
|
-
e.preventDefault()
|
|
107
|
-
|
|
108
|
-
this.props.onScopeClicked({scope: this.props.scope})
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
export default class ApiMakerTableFiltersFilterForm extends React.PureComponent {
|
|
14
|
+
export default memo(shapeComponent(class ApiMakerTableFiltersFilterForm extends ShapeComponent {
|
|
114
15
|
static propTypes = PropTypesExact({
|
|
115
16
|
filter: PropTypes.object,
|
|
116
17
|
modelClass: PropTypes.func.isRequired,
|
|
@@ -118,18 +19,22 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
118
19
|
querySearchName: PropTypes.string.isRequired
|
|
119
20
|
})
|
|
120
21
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
22
|
+
setup() {
|
|
23
|
+
this.useStates({
|
|
24
|
+
attribute: () => this.currentModelClassFromPath(this.props.filter.p || [])
|
|
25
|
+
.ransackableAttributes()
|
|
26
|
+
.find((attribute) => attribute.name() == this.props.filter.a),
|
|
27
|
+
path: this.props.filter.p || [],
|
|
28
|
+
predicate: undefined,
|
|
29
|
+
predicates: undefined,
|
|
30
|
+
scope: this.props.filter.sc,
|
|
31
|
+
value: this.props.filter.v
|
|
32
|
+
})
|
|
33
|
+
this.valueInputRef = useRef()
|
|
130
34
|
|
|
131
|
-
|
|
132
|
-
|
|
35
|
+
useMemo(() => {
|
|
36
|
+
this.loadRansackPredicates()
|
|
37
|
+
}, [])
|
|
133
38
|
}
|
|
134
39
|
|
|
135
40
|
async loadRansackPredicates() {
|
|
@@ -141,7 +46,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
141
46
|
currentPredicate = predicates.find((predicate) => predicate.name == this.props.filter.pre)
|
|
142
47
|
}
|
|
143
48
|
|
|
144
|
-
this.
|
|
49
|
+
this.setState({
|
|
145
50
|
predicate: currentPredicate,
|
|
146
51
|
predicates
|
|
147
52
|
})
|
|
@@ -150,7 +55,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
150
55
|
render() {
|
|
151
56
|
const {valueInputRef} = digs(this, "valueInputRef")
|
|
152
57
|
const currentModelClass = this.currentModelClass()
|
|
153
|
-
const {attribute, predicate, predicates, scope, value} = digs(this.
|
|
58
|
+
const {attribute, predicate, predicates, scope, value} = digs(this.state, "attribute", "predicate", "scope", "predicates", "value")
|
|
154
59
|
let submitEnabled = false
|
|
155
60
|
|
|
156
61
|
if (attribute && predicate) {
|
|
@@ -188,7 +93,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
188
93
|
<div>
|
|
189
94
|
{this.sortedByName(currentModelClass.ransackableAttributes(), currentModelClass).map((attribute) =>
|
|
190
95
|
<AttributeElement
|
|
191
|
-
active={attribute.name() == this.
|
|
96
|
+
active={attribute.name() == this.state.attribute?.name()}
|
|
192
97
|
attribute={attribute}
|
|
193
98
|
currentModelClass={currentModelClass}
|
|
194
99
|
key={attribute.name()}
|
|
@@ -197,7 +102,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
197
102
|
)}
|
|
198
103
|
{currentModelClass.ransackableScopes().map((scope) =>
|
|
199
104
|
<ScopeElement
|
|
200
|
-
active={scope.name() == this.
|
|
105
|
+
active={scope.name() == this.state.scope?.name()}
|
|
201
106
|
key={scope.name()}
|
|
202
107
|
scope={scope}
|
|
203
108
|
onScopeClicked={digg(this, "onScopeClicked")}
|
|
@@ -205,7 +110,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
205
110
|
)}
|
|
206
111
|
</div>
|
|
207
112
|
<div>
|
|
208
|
-
{predicates && !this.
|
|
113
|
+
{predicates && !this.state.scope &&
|
|
209
114
|
<Select
|
|
210
115
|
className="predicate-select"
|
|
211
116
|
defaultValue={predicate?.name}
|
|
@@ -232,7 +137,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
232
137
|
}
|
|
233
138
|
|
|
234
139
|
currentModelClass() {
|
|
235
|
-
const {path} = digs(this.
|
|
140
|
+
const {path} = digs(this.state, "path")
|
|
236
141
|
|
|
237
142
|
return this.currentModelClassFromPath(path)
|
|
238
143
|
}
|
|
@@ -259,7 +164,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
259
164
|
|
|
260
165
|
currentPathParts() {
|
|
261
166
|
const {modelClass} = digs(this.props, "modelClass")
|
|
262
|
-
const {path} = digs(this.
|
|
167
|
+
const {path} = digs(this.state, "path")
|
|
263
168
|
const result = []
|
|
264
169
|
let currentModelClass = modelClass
|
|
265
170
|
|
|
@@ -284,7 +189,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
284
189
|
}
|
|
285
190
|
|
|
286
191
|
onAttributeClicked = ({attribute}) => {
|
|
287
|
-
this.
|
|
192
|
+
this.setState({
|
|
288
193
|
attribute,
|
|
289
194
|
predicate: undefined,
|
|
290
195
|
scope: undefined
|
|
@@ -293,15 +198,15 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
293
198
|
|
|
294
199
|
onPredicateChanged = (e) => {
|
|
295
200
|
const chosenPredicateName = digg(e, "target", "value")
|
|
296
|
-
const predicate = this.
|
|
201
|
+
const predicate = this.state.predicates.find((predicate) => predicate.name == chosenPredicateName)
|
|
297
202
|
|
|
298
|
-
this.
|
|
203
|
+
this.setState({predicate})
|
|
299
204
|
}
|
|
300
205
|
|
|
301
206
|
onReflectionClicked = ({reflection}) => {
|
|
302
|
-
const newPath = this.
|
|
207
|
+
const newPath = this.state.path.concat([inflection.underscore(reflection.name())])
|
|
303
208
|
|
|
304
|
-
this.
|
|
209
|
+
this.setState({
|
|
305
210
|
attribute: undefined,
|
|
306
211
|
path: newPath,
|
|
307
212
|
predicate: undefined
|
|
@@ -311,7 +216,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
311
216
|
}
|
|
312
217
|
|
|
313
218
|
onScopeClicked = ({scope}) => {
|
|
314
|
-
this.
|
|
219
|
+
this.setState({
|
|
315
220
|
attribute: undefined,
|
|
316
221
|
scope
|
|
317
222
|
})
|
|
@@ -321,7 +226,7 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
321
226
|
e.preventDefault()
|
|
322
227
|
|
|
323
228
|
const {filter, querySearchName} = digs(this.props, "filter", "querySearchName")
|
|
324
|
-
const {attribute, path, predicate, scope} = digs(this.
|
|
229
|
+
const {attribute, path, predicate, scope} = digs(this.state, "attribute", "path", "predicate", "scope")
|
|
325
230
|
const {filterIndex} = digs(filter, "filterIndex")
|
|
326
231
|
const searchParams = Params.parse()[querySearchName] || {}
|
|
327
232
|
const value = digg(this, "valueInputRef", "current", "value")
|
|
@@ -367,4 +272,4 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
|
|
|
367
272
|
currentModelClass.humanAttributeName(a.name()).toLowerCase().localeCompare(currentModelClass.humanAttributeName(b.name()).toLowerCase())
|
|
368
273
|
)
|
|
369
274
|
}
|
|
370
|
-
}
|
|
275
|
+
}))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {digg, digs} from "diggerize"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
import PropTypesExact from "prop-types-exact"
|
|
4
|
+
import {memo} from "react"
|
|
5
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component"
|
|
6
|
+
|
|
7
|
+
export default memo(shapeComponent(class ApiMakerTableFilter extends ShapeComponent {
|
|
8
|
+
static propTypes = PropTypesExact({
|
|
9
|
+
a: PropTypes.string,
|
|
10
|
+
filterIndex: PropTypes.number.isRequired,
|
|
11
|
+
onClick: PropTypes.func.isRequired,
|
|
12
|
+
onRemoveClicked: PropTypes.func.isRequired,
|
|
13
|
+
p: PropTypes.array.isRequired,
|
|
14
|
+
pre: PropTypes.string,
|
|
15
|
+
sc: PropTypes.string,
|
|
16
|
+
v: PropTypes.string.isRequired
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
const {p, v} = digs(this.props, "p", "v")
|
|
21
|
+
const {a, pre, sc} = this.props
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div style={{display: "inline-block", backgroundColor: "grey", padding: "10px 6px"}}>
|
|
25
|
+
<span className="filter-label" onClick={digg(this, "onFilterClicked")} style={{cursor: "pointer"}}>
|
|
26
|
+
{p.length > 0 &&
|
|
27
|
+
`${p.join(".")}.`
|
|
28
|
+
}
|
|
29
|
+
{a} {sc} {pre} {v}
|
|
30
|
+
</span>
|
|
31
|
+
<span>
|
|
32
|
+
<a className="remove-filter-button" href="#" onClick={digg(this, "onRemoveFilterClicked")}>
|
|
33
|
+
<i className="fa fa-remove la la-remove" />
|
|
34
|
+
</a>
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
onFilterClicked = (e) => {
|
|
41
|
+
e.preventDefault()
|
|
42
|
+
|
|
43
|
+
const {a, filterIndex, p, pre, v} = digs(this.props, "a", "filterIndex", "p", "pre", "v")
|
|
44
|
+
|
|
45
|
+
this.props.onClick({a, filterIndex, p, pre, v})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onRemoveFilterClicked = (e) => {
|
|
49
|
+
e.preventDefault()
|
|
50
|
+
|
|
51
|
+
const {filterIndex} = digs(this.props, "filterIndex")
|
|
52
|
+
|
|
53
|
+
this.props.onRemoveClicked({filterIndex})
|
|
54
|
+
}
|
|
55
|
+
}))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {digg, digs} from "diggerize"
|
|
2
|
+
import Filter from "./filter"
|
|
2
3
|
import FilterForm from "./filter-form"
|
|
3
4
|
import LoadSearchModal from "./load-search-modal"
|
|
4
5
|
import SaveSearchModal from "./save-search-modal"
|
|
@@ -8,55 +9,6 @@ import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-compon
|
|
|
8
9
|
import useI18n from "i18n-on-steroids/src/use-i18n.mjs"
|
|
9
10
|
import useQueryParams from "on-location-changed/src/use-query-params"
|
|
10
11
|
|
|
11
|
-
const ApiMakerTableFilter = memo(shapeComponent(class ApiMakerTableFilter extends ShapeComponent {
|
|
12
|
-
static propTypes = {
|
|
13
|
-
a: PropTypes.string.isRequired,
|
|
14
|
-
filterIndex: PropTypes.number.isRequired,
|
|
15
|
-
onClick: PropTypes.func.isRequired,
|
|
16
|
-
onRemoveClicked: PropTypes.func.isRequired,
|
|
17
|
-
p: PropTypes.array.isRequired,
|
|
18
|
-
pre: PropTypes.string.isRequired,
|
|
19
|
-
v: PropTypes.string.isRequired
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
render() {
|
|
23
|
-
const {p, v} = digs(this.props, "p", "v")
|
|
24
|
-
const {a, pre, sc} = this.props
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div style={{display: "inline-block", backgroundColor: "grey", padding: "10px 6px"}}>
|
|
28
|
-
<span className="filter-label" onClick={digg(this, "onFilterClicked")} style={{cursor: "pointer"}}>
|
|
29
|
-
{p.length > 0 &&
|
|
30
|
-
`${p.join(".")}.`
|
|
31
|
-
}
|
|
32
|
-
{a} {sc} {pre} {v}
|
|
33
|
-
</span>
|
|
34
|
-
<span>
|
|
35
|
-
<a className="remove-filter-button" href="#" onClick={digg(this, "onRemoveFilterClicked")}>
|
|
36
|
-
<i className="fa fa-remove la la-remove" />
|
|
37
|
-
</a>
|
|
38
|
-
</span>
|
|
39
|
-
</div>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
onFilterClicked = (e) => {
|
|
44
|
-
e.preventDefault()
|
|
45
|
-
|
|
46
|
-
const {a, filterIndex, p, pre, v} = digs(this.props, "a", "filterIndex", "p", "pre", "v")
|
|
47
|
-
|
|
48
|
-
this.props.onClick({a, filterIndex, p, pre, v})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
onRemoveFilterClicked = (e) => {
|
|
52
|
-
e.preventDefault()
|
|
53
|
-
|
|
54
|
-
const {filterIndex} = digs(this.props, "filterIndex")
|
|
55
|
-
|
|
56
|
-
this.props.onRemoveClicked({filterIndex})
|
|
57
|
-
}
|
|
58
|
-
}))
|
|
59
|
-
|
|
60
12
|
export default memo(shapeComponent(class ApiMakerTableFilters extends ShapeComponent {
|
|
61
13
|
static propTypes = {
|
|
62
14
|
currentUser: PropTypes.object,
|
|
@@ -110,7 +62,7 @@ export default memo(shapeComponent(class ApiMakerTableFilters extends ShapeCompo
|
|
|
110
62
|
/>
|
|
111
63
|
}
|
|
112
64
|
{currentFilters?.map((filterData, filterIndex) =>
|
|
113
|
-
<
|
|
65
|
+
<Filter
|
|
114
66
|
key={filterIndex}
|
|
115
67
|
filterIndex={filterIndex}
|
|
116
68
|
onClick={digg(this, "onFilterClicked")}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {digg, digs} from "diggerize"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
import PropTypesExact from "prop-types-exact"
|
|
4
|
+
import {memo} from "react"
|
|
5
|
+
import Reflection from "../../base-model/reflection"
|
|
6
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component"
|
|
7
|
+
|
|
8
|
+
export default memo(shapeComponent(class ReflectionElement extends ShapeComponent {
|
|
9
|
+
static propTypes = PropTypesExact({
|
|
10
|
+
currentModelClass: PropTypes.func.isRequired,
|
|
11
|
+
onClick: PropTypes.func.isRequired,
|
|
12
|
+
reflection: PropTypes.instanceOf(Reflection).isRequired
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
const {currentModelClass, reflection} = digs(this.props, "currentModelClass", "reflection")
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className="reflection-element"
|
|
21
|
+
data-model-class={currentModelClass.modelClassData().name}
|
|
22
|
+
data-reflection-name={reflection.name()}
|
|
23
|
+
onClick={digg(this, "onReflectionClicked")}
|
|
24
|
+
>
|
|
25
|
+
{currentModelClass.humanAttributeName(reflection.name())}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onReflectionClicked = (e) => {
|
|
31
|
+
e.preventDefault()
|
|
32
|
+
|
|
33
|
+
this.props.onClick({reflection: digg(this, "props", "reflection")})
|
|
34
|
+
}
|
|
35
|
+
}))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {digg} from "diggerize"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
import {memo} from "react"
|
|
4
|
+
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component"
|
|
5
|
+
|
|
6
|
+
export default memo(shapeComponent(class ScopeElement extends ShapeComponent {
|
|
7
|
+
static defaultProps = {
|
|
8
|
+
active: false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static propTypes = {
|
|
12
|
+
active: PropTypes.bool.isRequired,
|
|
13
|
+
scope: PropTypes.object.isRequired
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
const {active, scope} = this.props
|
|
18
|
+
const style = {}
|
|
19
|
+
|
|
20
|
+
if (active) style.fontWeight = "bold"
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
className="scope-element"
|
|
25
|
+
key={scope.name()}
|
|
26
|
+
onClick={digg(this, "onScopeClicked")}
|
|
27
|
+
style={style}
|
|
28
|
+
>
|
|
29
|
+
{scope.name()}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
onScopeClicked = (e) => {
|
|
35
|
+
e.preventDefault()
|
|
36
|
+
|
|
37
|
+
this.props.onScopeClicked({scope: this.props.scope})
|
|
38
|
+
}
|
|
39
|
+
}))
|
package/src/table/model-row.jsx
CHANGED
|
@@ -105,8 +105,8 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
columnsContentFromAttributeAndPath (column, model) {
|
|
108
|
-
const {attribute} = digs(column, "attribute")
|
|
109
|
-
const
|
|
108
|
+
const {attribute: attributeName} = digs(column, "attribute")
|
|
109
|
+
const attributeNameUnderscore = inflection.underscore(attributeName)
|
|
110
110
|
const path = column.path || []
|
|
111
111
|
let value
|
|
112
112
|
let currentModel = model
|
|
@@ -118,8 +118,18 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
if (!(
|
|
122
|
-
|
|
121
|
+
if (!(attributeName in currentModel)) {
|
|
122
|
+
throw new Error(`${currentModel.constructor.modelName().human()} doesn't respond to ${attributeName}`)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (currentModel.isAttributeLoaded(attributeName)) value = currentModel[attributeName]()
|
|
126
|
+
|
|
127
|
+
const attribute = currentModel.constructor.attributes().find((attribute) => attribute.name() == attributeNameUnderscore)
|
|
128
|
+
const modelColumn = attribute?.getColumn()
|
|
129
|
+
|
|
130
|
+
if (modelColumn?.getType() == "date") {
|
|
131
|
+
return this.presentDateTime({apiMakerType: "date", value})
|
|
132
|
+
}
|
|
123
133
|
|
|
124
134
|
return this.presentColumnValue(value)
|
|
125
135
|
}
|
|
@@ -155,9 +165,9 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
|
|
158
|
-
presentColumnValue
|
|
168
|
+
presentColumnValue(value) {
|
|
159
169
|
if (value instanceof Date) {
|
|
160
|
-
return this.presentDateTime(value)
|
|
170
|
+
return this.presentDateTime({value})
|
|
161
171
|
} else if (MoneyFormatter.isMoney(value)) {
|
|
162
172
|
return MoneyFormatter.format(value)
|
|
163
173
|
} else if (typeof value == "boolean") {
|
|
@@ -174,15 +184,13 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
174
184
|
return value
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
presentDateTime(value) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (apiMakerType == "time") {
|
|
187
|
+
presentDateTime({apiMakerType, value}) {
|
|
188
|
+
if (!apiMakerType || apiMakerType == "time") {
|
|
181
189
|
const dateTimeFormatName = this.props.liveTable.props.defaultDateTimeFormatName || "time.formats.default"
|
|
182
190
|
|
|
183
191
|
return I18n.l(dateTimeFormatName, value)
|
|
184
192
|
} else if (apiMakerType == "date") {
|
|
185
|
-
const dateFormatName = this.props.liveTable.props.
|
|
193
|
+
const dateFormatName = this.props.liveTable.props.defaultDateFormatName || "date.formats.default"
|
|
186
194
|
|
|
187
195
|
return I18n.l(dateFormatName, value)
|
|
188
196
|
} else {
|
package/src/table/table.jsx
CHANGED
|
@@ -22,6 +22,7 @@ import TableSettings from "./table-settings"
|
|
|
22
22
|
import uniqunize from "uniqunize"
|
|
23
23
|
import useBreakpoint from "./use-breakpoint"
|
|
24
24
|
import useCollection from "../use-collection"
|
|
25
|
+
import useQueryParams from "on-location-changed/src/use-query-params.js"
|
|
25
26
|
|
|
26
27
|
const paginationOptions = [30, 60, 90, ["All", "all"]]
|
|
27
28
|
const WorkerPluginsCheckAllCheckbox = React.lazy(() => import("./worker-plugins-check-all-checkbox"))
|
|
@@ -79,6 +80,7 @@ export default memo(shapeComponent(class ApiMakerTable extends ShapeComponent {
|
|
|
79
80
|
|
|
80
81
|
setup() {
|
|
81
82
|
const {breakpoint} = useBreakpoint()
|
|
83
|
+
const queryParams = useQueryParams()
|
|
82
84
|
|
|
83
85
|
this.setInstance({
|
|
84
86
|
breakpoint,
|
|
@@ -91,18 +93,19 @@ export default memo(shapeComponent(class ApiMakerTable extends ShapeComponent {
|
|
|
91
93
|
if (!queryName) queryName = collectionKey
|
|
92
94
|
|
|
93
95
|
const columnsAsArray = this.columnsAsArray()
|
|
96
|
+
const querySName = `${queryName}_s`
|
|
94
97
|
|
|
95
98
|
this.useStates({
|
|
96
99
|
columns: columnsAsArray,
|
|
97
100
|
currentWorkplace: undefined,
|
|
98
|
-
identifier: this.props.identifier || `${collectionKey}-default`,
|
|
101
|
+
identifier: () => this.props.identifier || `${collectionKey}-default`,
|
|
99
102
|
preload: undefined,
|
|
100
103
|
preparedColumns: undefined,
|
|
101
104
|
queryName,
|
|
102
|
-
queryQName: `${queryName}_q`,
|
|
103
|
-
queryPageName: `${queryName}_page`,
|
|
104
|
-
querySName
|
|
105
|
-
showFilters:
|
|
105
|
+
queryQName: () => `${queryName}_q`,
|
|
106
|
+
queryPageName: () => `${queryName}_page`,
|
|
107
|
+
querySName,
|
|
108
|
+
showFilters: () => Boolean(queryParams[querySName]),
|
|
106
109
|
showSettings: false,
|
|
107
110
|
tableSetting: undefined,
|
|
108
111
|
tableSettingFullCacheKey: undefined
|