@kaspernj/api-maker 1.0.389 → 1.0.390

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaspernj/api-maker",
3
- "version": "1.0.389",
3
+ "version": "1.0.390",
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.45",
38
+ "set-state-compare": "^1.0.46",
39
39
  "spark-md5": "^3.0.2",
40
40
  "stacktrace-parser": "^0.1.10",
41
41
  "strftime": ">= 0.10.0",
package/src/form.jsx ADDED
@@ -0,0 +1,68 @@
1
+ import {createContext, memo, useContext, useMemo} from "react"
2
+ import BaseComponent from "./base-component"
3
+ import FormDataObjectizer from "form-data-objectizer"
4
+ import {Platform} from "react-native"
5
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
6
+
7
+ const FormContext = createContext(null)
8
+
9
+ class FormClass {
10
+ constructor() {
11
+ this.inputs = {}
12
+ }
13
+
14
+ asObject() {
15
+ const result = {}
16
+ const formDataObjectizer = new FormDataObjectizer()
17
+
18
+ for(const key in this.inputs) {
19
+ const value = this.inputs[key]
20
+
21
+ formDataObjectizer.treatInitial(key, value, result)
22
+ }
23
+
24
+ return result
25
+ }
26
+
27
+ setValue(name, value) {
28
+ console.log("Form", {name, value})
29
+
30
+ this.inputs[name] = value
31
+ }
32
+
33
+ setValueWithHidden(name, value) {
34
+ this.setValue(name, value)
35
+
36
+ if (Platform.OS == "web") {
37
+ return <input name={name} type="hidden" value={value !== null && value !== undefined ? value : ""} />
38
+ }
39
+ }
40
+ }
41
+
42
+ const Form = memo(shapeComponent(class Form extends BaseComponent {
43
+ render() {
44
+ const {children, setForm, ...restProps} = this.props
45
+ const form = useMemo(() => new FormClass(), [])
46
+
47
+ useMemo(() => {
48
+ if (setForm) {
49
+ setForm(form)
50
+ }
51
+ }, [setForm])
52
+
53
+ return (
54
+ <FormContext.Provider value={form}>
55
+ {Platform.OS == "web" &&
56
+ <form {...restProps}>
57
+ {children}
58
+ </form>
59
+ }
60
+ {Platform.OS != "web" && this.props.children}
61
+ </FormContext.Provider>
62
+ )
63
+ }
64
+ }))
65
+
66
+ const useForm = () => useContext(FormContext)
67
+
68
+ export {Form, FormContext, useForm}
@@ -1,7 +1,10 @@
1
+ import BaseComponent from "../base-component"
1
2
  import {Input as ApiMakerInput} from "@kaspernj/api-maker/src/inputs/input"
2
3
  import {Checkbox} from "./checkbox"
4
+ import {memo} from "react"
5
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
3
6
 
4
- export default class ApiMakerInputsAttachment extends BaseComponent {
7
+ export default memo(shapeComponent(class ApiMakerInputsAttachment extends BaseComponent {
5
8
  static propTypes = {
6
9
  className: PropTypes.string,
7
10
  model: PropTypes.object.isRequired,
@@ -9,8 +12,10 @@ export default class ApiMakerInputsAttachment extends BaseComponent {
9
12
  purgeName: PropTypes.string
10
13
  }
11
14
 
12
- state = {
13
- purgeChecked: false
15
+ setup() {
16
+ this.useStates({
17
+ purgeChecked: false
18
+ })
14
19
  }
15
20
 
16
21
  render() {
@@ -91,4 +96,4 @@ export default class ApiMakerInputsAttachment extends BaseComponent {
91
96
 
92
97
  if (this.props.onPurgeChanged) this.props.onPurgeChanged(e)
93
98
  }
94
- }
99
+ }))
@@ -1,11 +1,14 @@
1
1
  import AutoSubmit from "./auto-submit.mjs"
2
+ import BaseComponent from "../base-component"
2
3
  import {digg, digs} from "diggerize"
3
4
  import EventUpdated from "../event-updated"
4
5
  import inputWrapper from "./input-wrapper"
5
6
  import PropTypes from "prop-types"
6
- import React from "react"
7
+ import {memo} from "react"
8
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
9
+ import {useForm} from "../form"
7
10
 
8
- class ApiMakerInputsCheckbox extends React.PureComponent {
11
+ const ApiMakerInputsCheckbox = memo(shapeComponent(class ApiMakerInputsCheckbox extends BaseComponent {
9
12
  static defaultProps = {
10
13
  autoRefresh: false,
11
14
  autoSubmit: false,
@@ -28,6 +31,10 @@ class ApiMakerInputsCheckbox extends React.PureComponent {
28
31
  zeroInput: PropTypes.bool
29
32
  }
30
33
 
34
+ setup() {
35
+ this.form = useForm()
36
+ }
37
+
31
38
  render () {
32
39
  const {
33
40
  attribute,
@@ -69,9 +76,11 @@ class ApiMakerInputsCheckbox extends React.PureComponent {
69
76
  }
70
77
 
71
78
  onChanged = (...args) => {
72
- const {attribute, autoSubmit, model, onChange} = this.props
79
+ const {form} = this.tt
80
+ const {attribute, autoSubmit, model, name, onChange} = this.props
73
81
 
74
82
  if (attribute && autoSubmit && model) new AutoSubmit({component: this}).autoSubmit()
83
+ if (form && name) form.setValue(name, args[0].target.checked)
75
84
  if (onChange) onChange(...args)
76
85
  }
77
86
 
@@ -92,7 +101,7 @@ class ApiMakerInputsCheckbox extends React.PureComponent {
92
101
  inputRef.current.checked = newValue
93
102
  }
94
103
  }
95
- }
104
+ }))
96
105
 
97
106
  export {ApiMakerInputsCheckbox as Checkbox}
98
107
  export default inputWrapper(ApiMakerInputsCheckbox, {type: "checkbox"})
@@ -1,3 +1,4 @@
1
+ import BaseComponent from "../base-component"
1
2
  import classNames from "classnames"
2
3
  import {digs} from "diggerize"
3
4
  import inputWrapper from "./input-wrapper"
@@ -5,9 +6,10 @@ import * as inflection from "inflection"
5
6
  import InvalidFeedback from "../bootstrap/invalid-feedback"
6
7
  import PropTypes from "prop-types"
7
8
  import propTypesExact from "prop-types-exact"
8
- import React from "react"
9
+ import {memo} from "react"
10
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
9
11
 
10
- class ApiMakerInputsCheckboxes extends React.PureComponent {
12
+ const ApiMakerInputsCheckboxes = memo(shapeComponent(class ApiMakerInputsCheckboxes extends BaseComponent {
11
13
  static propTypes = propTypesExact({
12
14
  attribute: PropTypes.string,
13
15
  defaultValue: PropTypes.array,
@@ -108,6 +110,6 @@ class ApiMakerInputsCheckboxes extends React.PureComponent {
108
110
  </div>
109
111
  )
110
112
  }
111
- }
113
+ }))
112
114
 
113
115
  export default inputWrapper(ApiMakerInputsCheckboxes)
@@ -1,4 +1,5 @@
1
1
  import AutoSubmit from "./auto-submit.mjs"
2
+ import BaseComponent from "../base-component"
2
3
  import {dig, digg, digs} from "diggerize"
3
4
  import EventUpdated from "../event-updated"
4
5
  import inputWrapper from "./input-wrapper"
@@ -6,10 +7,11 @@ import Money from "./money"
6
7
  import PropTypes from "prop-types"
7
8
  import {memo, useRef} from "react"
8
9
  import replaceall from "replaceall"
9
- import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component.js"
10
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
10
11
  import strftime from "strftime"
12
+ import {useForm} from "../form"
11
13
 
12
- const ApiMakerInputsInput = memo(shapeComponent(class ApiMakerInputsInput extends ShapeComponent {
14
+ const ApiMakerInputsInput = memo(shapeComponent(class ApiMakerInputsInput extends BaseComponent {
13
15
  static defaultProps = {
14
16
  autoRefresh: false,
15
17
  autoSubmit: false,
@@ -32,6 +34,7 @@ const ApiMakerInputsInput = memo(shapeComponent(class ApiMakerInputsInput extend
32
34
  }
33
35
 
34
36
  setup() {
37
+ this.form = useForm()
35
38
  this.visibleInputRef = useRef()
36
39
 
37
40
  this.useStates({
@@ -204,13 +207,15 @@ const ApiMakerInputsInput = memo(shapeComponent(class ApiMakerInputsInput extend
204
207
  }
205
208
 
206
209
  onInputChanged = (e) => {
207
- const {attribute, autoSubmit, inputProps, model, onChange} = this.props
210
+ const {form} = this.tt
211
+ const {attribute, autoSubmit, inputProps, model, name, onChange} = this.props
208
212
  const {localizedNumber} = digs(this.props, "localizedNumber")
209
213
 
210
214
  if (localizedNumber) this.inputReference().current.value = this.actualValue(digg(e, "target"))
211
215
 
212
216
  if (attribute && autoSubmit && model) this.delayAutoSubmit()
213
217
  if (digg(inputProps, "type") == "file") this.setState({blankInputName: this.getBlankInputName()})
218
+ if (form && name) form.setValue(name, e.target.value)
214
219
  if (onChange) onChange(e)
215
220
  }
216
221
 
@@ -1,9 +1,11 @@
1
- import {dig} from "diggerize"
1
+ import BaseComponent from "../base-component"
2
2
  import inputWrapper from "./input-wrapper"
3
3
  import PropTypes from "prop-types"
4
- import React from "react"
4
+ import {memo} from "react"
5
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
6
+ import {useForm} from "../form"
5
7
 
6
- class ApiMakerInputsSelect extends React.PureComponent {
8
+ const ApiMakerInputsSelect = memo(shapeComponent(class ApiMakerInputsSelect extends BaseComponent {
7
9
  static propTypes = {
8
10
  attribute: PropTypes.string,
9
11
  children: PropTypes.node,
@@ -13,10 +15,15 @@ class ApiMakerInputsSelect extends React.PureComponent {
13
15
  inputProps: PropTypes.object.isRequired,
14
16
  model: PropTypes.object,
15
17
  name: PropTypes.string,
18
+ onChange: PropTypes.func,
16
19
  options: PropTypes.array,
17
20
  wrapperOpts: PropTypes.object.isRequired
18
21
  }
19
22
 
23
+ setup() {
24
+ this.form = useForm()
25
+ }
26
+
20
27
  render () {
21
28
  const {
22
29
  attribute,
@@ -28,13 +35,14 @@ class ApiMakerInputsSelect extends React.PureComponent {
28
35
  inputRef,
29
36
  model,
30
37
  name,
38
+ onChange,
31
39
  options,
32
40
  wrapperOpts,
33
41
  ...restProps
34
42
  } = this.props
35
43
 
36
44
  return (
37
- <select {...inputProps} {...restProps}>
45
+ <select onChange={this.tt.onChange} {...inputProps} {...restProps}>
38
46
  {this.includeBlank() &&
39
47
  <option data-include-blank="true">
40
48
  {typeof includeBlank != "boolean" ? includeBlank : null}
@@ -50,6 +58,14 @@ class ApiMakerInputsSelect extends React.PureComponent {
50
58
  )
51
59
  }
52
60
 
61
+ onChange = (e) => {
62
+ const {form} = this.tt
63
+ const {name, onChange} = this.props
64
+
65
+ if (form && name) form.setValue(name, e.target.value)
66
+ if (onChange) onChange(e)
67
+ }
68
+
53
69
  optionKey (option) {
54
70
  if (Array.isArray(option)) {
55
71
  return `select-option-${option[1]}`
@@ -81,7 +97,7 @@ class ApiMakerInputsSelect extends React.PureComponent {
81
97
  return false
82
98
  }
83
99
  }
84
- }
100
+ }))
85
101
 
86
102
  export {ApiMakerInputsSelect as Select}
87
103
  export default inputWrapper(ApiMakerInputsSelect)
@@ -1,35 +1,57 @@
1
1
  import BaseComponent from "../../base-component"
2
+ import {FormContext} from "../../form"
2
3
  import {memo} from "react"
3
4
  import {shapeComponent} from "set-state-compare/src/shape-component.js"
4
5
 
5
6
  export default memo(shapeComponent(class EditAttributeContent extends BaseComponent {
7
+ setup() {
8
+ const {inputs, name} = this.p
9
+
10
+ this.form = useContext(FormContext)
11
+ this.useStates({
12
+ value: () => this.defaultValue()
13
+ })
14
+
15
+ useMemo(() => {
16
+ inputs[name] = this.s.value
17
+
18
+ if (this.form) {
19
+ this.form.setValue(name, this.s.value)
20
+ }
21
+ }, [])
22
+ }
23
+
6
24
  render() {
7
- const {attribute, id, inputs, model, name} = this.props
25
+ const {attribute, id, model} = this.props
8
26
 
9
27
  if (!(attribute.attribute in model)) {
10
28
  throw new Error(`${attribute.attribute} isn't set on the resource ${model.modelClassData().name}`)
11
29
  }
12
30
 
13
- const defaultValue = useCallback(() => model[attribute.attribute]() || "")
14
- const [value, setValue] = useState(() => defaultValue())
15
- const onChangeValue = useCallback((newValue) => {
16
- inputs[name] = newValue
17
- setValue(newValue)
18
- })
19
- useMemo(() => {
20
- inputs[name] = value
21
- }, [])
22
-
23
- const contentArgs = () => ({
31
+ const contentArgs = useMemo(() => ({
24
32
  inputProps: {
25
33
  attribute: attribute.attribute,
26
- defaultValue: defaultValue(),
34
+ defaultValue: this.defaultValue(),
27
35
  id,
28
36
  model
29
37
  },
30
- onChangeValue
31
- })
38
+ onChangeValue: this.tt.onChangeValue
39
+ }), [attribute.attribute, id, model])
40
+
41
+ return attribute.content(contentArgs)
42
+ }
43
+
44
+ defaultValue = () => this.p.model[this.p.attribute.attribute]() || ""
45
+
46
+ onChangeValue = (newValue) => {
47
+ const {inputs, name} = this.p
48
+
49
+ inputs[name] = newValue
50
+
51
+ if (this.form) {
52
+ this.form.setValue(name, newValue)
53
+ }
32
54
 
33
- return attribute.content(contentArgs())
55
+ this.setState({value: newValue})
34
56
  }
35
57
  }))
@@ -1,4 +1,5 @@
1
1
  import BaseComponent from "../../base-component"
2
+ import {useForm} from "../../form"
2
3
  import PropTypes from "prop-types"
3
4
  import propTypesExact from "prop-types-exact"
4
5
  import {shapeComponent} from "set-state-compare/src/shape-component.js"
@@ -14,12 +15,17 @@ export default memo(shapeComponent(class EditAttributeInput extends BaseComponen
14
15
  setup() {
15
16
  const {inputs, name} = this.p
16
17
 
18
+ this.form = useForm()
17
19
  this.useStates({
18
20
  value: this.defaultValue()
19
21
  })
20
22
 
21
23
  useMemo(() => {
22
24
  inputs[name] = this.s.value
25
+
26
+ if (this.form) {
27
+ this.form.setValue(name, this.s.value)
28
+ }
23
29
  }, [])
24
30
  }
25
31
 
@@ -56,6 +62,11 @@ export default memo(shapeComponent(class EditAttributeInput extends BaseComponen
56
62
  const {inputs, name} = this.p
57
63
 
58
64
  inputs[name] = newValue
65
+
66
+ if (this.form) {
67
+ this.form.setValue(name, newValue)
68
+ }
69
+
59
70
  this.setState({value: newValue})
60
71
  }
61
72
  }))
@@ -1,9 +1,10 @@
1
+ import {Pressable, Text, View} from "react-native"
1
2
  import BaseComponent from "../base-component"
2
3
  import ConfigReader from "./config-reader.jsx"
3
4
  import {digg} from "diggerize"
4
5
  import EditAttribute from "./edit-page/edit-attribute"
6
+ import {Form} from "../form"
5
7
  import * as inflection from "inflection"
6
- import {Pressable, Text, View} from "react-native"
7
8
  import Locales from "shared/locales"
8
9
  import {memo} from "react"
9
10
  import PropTypes from "prop-types"
@@ -50,7 +51,8 @@ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends Base
50
51
  const useModelResult = useModel(modelClass, {
51
52
  cacheArgs: [currentUser?.id()],
52
53
  loadByQueryParam: (props) => props.queryParams.model_id,
53
- newIfNoId: true,
54
+ newIfNoId: this.configReader.modelConfig?.edit?.newIfNoId || true,
55
+ preload: this.configReader.modelConfig?.edit?.preload,
54
56
  select: selectedAttributes
55
57
  })
56
58
 
@@ -61,6 +63,7 @@ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends Base
61
63
  this.modelId = queryParams.model_id
62
64
  this.modelArgs = {}
63
65
  this.modelArgs[modelIdVarName] = this.modelId
66
+ this.useStates({form: null})
64
67
  }
65
68
 
66
69
  render() {
@@ -70,27 +73,29 @@ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends Base
70
73
 
71
74
  return (
72
75
  <View dataSet={{class: "super-admin--edit-page"}}>
73
- {model && attributes?.map((attribute) =>
74
- <EditAttribute attribute={attribute} inputs={this.inputs} key={attribute.attribute} model={model} modelClass={modelClass} />
75
- )}
76
- {extraContent && extraContent(modelArgs)}
77
- <Pressable
78
- dataSet={{class: "submit-button"}}
79
- onPress={this.tt.onSubmit}
80
- style={{
81
- paddingTop: 18,
82
- paddingRight: 24,
83
- paddingBottom: 18,
84
- paddingLeft: 24,
85
- borderRadius: 10,
86
- backgroundColor: "#4c93ff",
87
- marginTop: 10
88
- }}
89
- >
90
- <Text style={{color: "#fff"}}>
91
- Submit
92
- </Text>
93
- </Pressable>
76
+ <Form setForm={this.setStates.form}>
77
+ {model && attributes?.map((attribute) =>
78
+ <EditAttribute attribute={attribute} inputs={this.inputs} key={attribute.attribute} model={model} modelClass={modelClass} />
79
+ )}
80
+ {extraContent && extraContent(modelArgs)}
81
+ <Pressable
82
+ dataSet={{class: "submit-button"}}
83
+ onPress={this.tt.onSubmit}
84
+ style={{
85
+ paddingTop: 18,
86
+ paddingRight: 24,
87
+ paddingBottom: 18,
88
+ paddingLeft: 24,
89
+ borderRadius: 10,
90
+ backgroundColor: "#4c93ff",
91
+ marginTop: 10
92
+ }}
93
+ >
94
+ <Text style={{color: "#fff"}}>
95
+ Submit
96
+ </Text>
97
+ </Pressable>
98
+ </Form>
94
99
  </View>
95
100
  )
96
101
  }
@@ -98,8 +103,14 @@ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends Base
98
103
  onSubmit = async () => {
99
104
  try {
100
105
  const {inputs, model} = this.tt
106
+ const {form} = this.s
107
+
108
+ const formObject = form.asObject()
109
+ const allInputs = Object.assign({}, inputs, formObject)
110
+
111
+ console.log({allInputs})
101
112
 
102
- model.assignAttributes(inputs)
113
+ model.assignAttributes(allInputs)
103
114
  await model.save(this.inputs)
104
115
  Params.changeParams({mode: undefined, model_id: model.id()})
105
116
  } catch (error) {