@kaspernj/api-maker 1.0.340 → 1.0.342

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.340",
19
+ "version": "1.0.342",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -1,6 +1,6 @@
1
1
  import * as inflection from "inflection"
2
2
 
3
- export default function apiMakerIdForComponent (component) {
3
+ export default function apiMakerIdForComponent(component) {
4
4
  if ("id" in component.props) {
5
5
  return component.props.id
6
6
  } else if (component.props.attribute && component.props.model) {
@@ -1,184 +1,15 @@
1
- import {dig, digg, digs} from "diggerize"
2
- import EventListener from "../event-listener"
3
- import React from "react"
4
- import idForComponent from "./id-for-component.mjs"
5
- import nameForComponent from "./name-for-component.mjs"
6
- import strftime from "strftime"
7
-
8
- const inputWrapper = (WrapperComponentClass, wrapperOptions = {}) => {
9
- return class ApiMakerInputWrapper extends React.PureComponent {
10
- state = {
11
- errors: [],
12
- form: undefined
13
- }
14
-
15
- componentDidMount() {
16
- this.setForm()
17
- }
18
-
19
- componentDidUpdate() {
20
- this.setForm()
21
- }
22
-
23
- inputProps() {
24
- const givenInputProps = this.props.inputProps || {}
25
- const inputProps = Object.assign(
26
- {
27
- id: idForComponent(this),
28
- name: nameForComponent(this),
29
- ref: this.inputRef()
30
- },
31
- givenInputProps
32
- )
33
-
34
- if (this.handleAsCheckbox()) {
35
- if ("checked" in this.props) {
36
- inputProps.checked = this.props.checked
37
- }
38
-
39
- if ("defaultChecked" in this.props || (this.props.attribute && this.props.model)) {
40
- inputProps.defaultChecked = this.inputDefaultChecked()
41
- }
42
- } else {
43
- inputProps.defaultValue = this.inputDefaultValue()
44
- }
45
-
46
- return inputProps
47
- }
48
-
49
- render() {
50
- const {inputProps: oldInputProps, ...restProps} = this.props
51
- const {errors, form} = digs(this.state, "errors", "form")
52
- const type = this.inputType()
53
- const inputProps = this.inputProps()
54
-
55
- if (!inputProps.ref) throw new Error("No input ref?")
56
- if (!this.handleAsSelect()) inputProps.type = type
57
-
58
- const wrapperOpts = {
59
- errors,
60
- form,
61
- label: this.label()
62
- }
63
-
64
- return (
65
- <>
66
- {form &&
67
- <EventListener event="validation-errors" onCalled={digg(this, "onValidationErrors")} target={form} />
68
- }
69
- <WrapperComponentClass
70
- inputProps={inputProps}
71
- wrapperOpts={wrapperOpts}
72
- {...restProps}
73
- />
74
- </>
75
- )
76
- }
77
-
78
- formatValue (value) {
79
- const {formatValue} = this.props
80
-
81
- if (formatValue) {
82
- return formatValue(value)
83
- } else if (value instanceof Date && !isNaN(value.getTime())) {
84
- // We need to use a certain format for datetime-local
85
- if (this.inputType() == "datetime-local") {
86
- return strftime("%Y-%m-%dT%H:%M:%S", value)
87
- } else if (this.inputType() == "date") {
88
- return strftime("%Y-%m-%d", value)
89
- }
90
- }
91
-
92
- return value
93
- }
94
-
95
- handleAsCheckbox() {
96
- if (this.props.type == "checkbox") return true
97
- if (!("type" in this.props) && wrapperOptions.type == "checkbox") return true
98
-
99
- return false
100
- }
101
-
102
- handleAsSelect() {
103
- if (wrapperOptions.type == "select") return true
104
-
105
- return false
106
- }
107
-
108
- inputDefaultChecked () {
109
- if ("defaultChecked" in this.props) {
110
- return this.props.defaultChecked
111
- } else if (this.props.model) {
112
- if (!this.props.model[this.props.attribute])
113
- throw new Error(`No such attribute: ${this.props.attribute}`)
114
-
115
- return this.props.model[this.props.attribute]()
116
- }
117
- }
118
-
119
- inputDefaultValue() {
120
- if ("defaultValue" in this.props) {
121
- return this.formatValue(this.props.defaultValue)
122
- } else if (this.props.model) {
123
- if (!this.props.model[this.props.attribute]) {
124
- throw new Error(`No such attribute defined on resource: ${digg(this.props.model.modelClassData(), "name")}#${this.props.attribute}`)
125
- }
126
-
127
- return this.formatValue(this.props.model[this.props.attribute]())
128
- }
129
- }
130
-
131
- inputName() {
132
- if (this.state.blankInputName) return ""
133
-
134
- return nameForComponent(this)
135
- }
136
-
137
- inputRefBackup() {
138
- if (!this._inputRefBackup) this._inputRefBackup = React.createRef()
139
-
140
- return this._inputRefBackup
141
- }
142
-
143
- inputRef = () => this.props.inputRef || this.inputRefBackup()
144
-
145
- inputType() {
146
- if ("type" in this.props) {
147
- return this.props.type
148
- } else if (wrapperOptions.type == "checkbox") {
149
- return "checkbox"
150
- } else {
151
- return "text"
152
- }
153
- }
154
-
155
- label() {
156
- if ("label" in this.props) {
157
- return this.props.label
158
- } else if (this.props.attribute && this.props.model) {
159
- return this.props.model.modelClass().humanAttributeName(this.props.attribute)
160
- }
161
- }
162
-
163
- onValidationErrors = (event) => {
164
- const errors = event.detail.getValidationErrorsForInput({
165
- attribute: this.props.attribute,
166
- inputName: this.inputName(),
167
- onMatchValidationError: this.props.onMatchValidationError
168
- })
169
-
170
- this.setState({errors})
171
- }
172
-
173
- setForm () {
174
- const inputElement = this.inputRef().current
175
-
176
- let form
177
-
178
- if (inputElement) form = dig(inputElement, "form")
179
- if (form && form != this.state.form) this.setState({form})
180
- }
181
- }
1
+ import useInput from "../use-input.mjs"
2
+
3
+ const inputWrapper = (WrapperComponentClass, wrapperOptions = {}) => (props) => {
4
+ const {inputProps, restProps, wrapperOpts} = useInput({props, wrapperOptions})
5
+
6
+ return (
7
+ <WrapperComponentClass
8
+ inputProps={inputProps}
9
+ wrapperOpts={wrapperOpts}
10
+ {...restProps}
11
+ />
12
+ )
182
13
  }
183
14
 
184
15
  export default inputWrapper
@@ -6,10 +6,12 @@ const ApiMakerUseEventListener = (target, event, onCalled) => {
6
6
  }, [target, event, onCalled])
7
7
 
8
8
  useEffect(() => {
9
- target.addEventListener(event, onCalledCallback)
9
+ if (target) {
10
+ target.addEventListener(event, onCalledCallback)
10
11
 
11
- return () => {
12
- target.removeEventListener(event, onCalledCallback)
12
+ return () => {
13
+ target.removeEventListener(event, onCalledCallback)
14
+ }
13
15
  }
14
16
  }, [target, event, onCalled])
15
17
  }
@@ -0,0 +1,178 @@
1
+ import {dig, digg, digs} from "diggerize"
2
+ import {useCallback, useEffect, useMemo} from "react"
3
+ import idForComponent from "./inputs/id-for-component.mjs"
4
+ import nameForComponent from "./inputs/name-for-component.mjs"
5
+ import strftime from "strftime"
6
+ import useEventListener from "./use-event-listener.mjs"
7
+ import useShape from "set-state-compare/src/use-shape.js"
8
+
9
+ const useInput = ({props, wrapperOptions}) => {
10
+ const s = useShape(props)
11
+
12
+ s.useStates({
13
+ errors: [],
14
+ form: undefined
15
+ })
16
+
17
+ useEffect(() => {
18
+ setForm()
19
+ }, [s.props.inputRef?.current])
20
+
21
+ s.meta.fakeComponent = {props}
22
+
23
+ const formatValue = useCallback((value) => {
24
+ const {formatValue} = s.props
25
+
26
+ if (formatValue) {
27
+ return formatValue(value)
28
+ } else if (value instanceof Date && !isNaN(value.getTime())) {
29
+ // We need to use a certain format for datetime-local
30
+ if (inputType() == "datetime-local") {
31
+ return strftime("%Y-%m-%dT%H:%M:%S", value)
32
+ } else if (inputType() == "date") {
33
+ return strftime("%Y-%m-%d", value)
34
+ }
35
+ }
36
+
37
+ return value
38
+ }, [])
39
+
40
+ const handleAsCheckbox = useCallback(() => {
41
+ if (s.props.type == "checkbox") return true
42
+ if (!("type" in s.props) && wrapperOptions?.type == "checkbox") return true
43
+
44
+ return false
45
+ }, [])
46
+
47
+ const handleAsSelect = useCallback(() => {
48
+ if (wrapperOptions?.type == "select") return true
49
+
50
+ return false
51
+ }, [])
52
+
53
+ const inputDefaultChecked = useCallback(() => {
54
+ if ("defaultChecked" in s.props) {
55
+ return s.props.defaultChecked
56
+ } else if (s.props.model) {
57
+ if (!s.props.model[s.props.attribute])
58
+ throw new Error(`No such attribute: ${s.props.attribute}`)
59
+
60
+ return s.props.model[s.props.attribute]()
61
+ }
62
+ }, [])
63
+
64
+ const inputDefaultValue = useCallback(() => {
65
+ if ("defaultValue" in s.props) {
66
+ return formatValue(s.props.defaultValue)
67
+ } else if (s.props.model) {
68
+ if (!s.props.model[s.props.attribute]) {
69
+ throw new Error(`No such attribute defined on resource: ${digg(s.props.model.modelClassData(), "name")}#${s.props.attribute}`)
70
+ }
71
+
72
+ return formatValue(s.props.model[s.props.attribute]())
73
+ }
74
+ }, [])
75
+
76
+ const inputName = useCallback(() => {
77
+ if (s.state.blankInputName) return ""
78
+
79
+ return getName()
80
+ }, [])
81
+
82
+ const inputRefBackup = useCallback(() => {
83
+ if (!s.meta._inputRefBackup) s.meta._inputRefBackup = React.createRef()
84
+
85
+ return s.meta._inputRefBackup
86
+ }, [])
87
+
88
+ const inputRef = useCallback(() => s.props.inputRef || inputRefBackup())
89
+
90
+ const inputType = useCallback(() => {
91
+ if ("type" in s.props) {
92
+ return s.props.type
93
+ } else if (wrapperOptions?.type == "checkbox") {
94
+ return "checkbox"
95
+ } else {
96
+ return "text"
97
+ }
98
+ }, [])
99
+
100
+ const label = useCallback(() => {
101
+ if ("label" in s.props) {
102
+ return s.props.label
103
+ } else if (s.props.attribute && s.props.model) {
104
+ return s.props.model.modelClass().humanAttributeName(s.props.attribute)
105
+ }
106
+ }, [])
107
+
108
+ const onValidationErrors = useCallback((event) => {
109
+ const errors = event.detail.getValidationErrorsForInput({
110
+ attribute: s.props.attribute,
111
+ inputName: inputName(),
112
+ onMatchValidationError: s.props.onMatchValidationError
113
+ })
114
+
115
+ s.set({errors})
116
+ }, [])
117
+
118
+ const setForm = useCallback(() => {
119
+ const inputElement = inputRef().current
120
+
121
+ let form
122
+
123
+ if (inputElement) form = dig(inputElement, "form")
124
+ if (form && form != s.s.form) s.set({form})
125
+ }, [])
126
+
127
+ const getId = useCallback(() => idForComponent(s.m.fakeComponent), [])
128
+ const getName = useCallback(() => nameForComponent(s.m.fakeComponent), [])
129
+
130
+ const getInputProps = useCallback(() => {
131
+ const givenInputProps = s.props.inputProps || {}
132
+ const inputProps = Object.assign(
133
+ {
134
+ id: getId(),
135
+ name: getName(),
136
+ ref: inputRef()
137
+ },
138
+ givenInputProps
139
+ )
140
+
141
+ if (handleAsCheckbox()) {
142
+ if ("checked" in s.props) {
143
+ inputProps.checked = s.props.checked
144
+ }
145
+
146
+ if ("defaultChecked" in s.props || (s.props.attribute && s.props.model)) {
147
+ inputProps.defaultChecked = inputDefaultChecked()
148
+ }
149
+ } else {
150
+ inputProps.defaultValue = inputDefaultValue()
151
+ }
152
+
153
+ return inputProps
154
+ }, [])
155
+
156
+ const {inputProps: oldInputProps, ...restProps} = props
157
+ const type = inputType()
158
+ const inputProps = getInputProps()
159
+
160
+ if (!inputProps.ref) throw new Error("No input ref?")
161
+ if (!handleAsSelect()) inputProps.type = type
162
+
163
+ const wrapperOpts = {
164
+ errors: s.s.errors,
165
+ form: s.s.form,
166
+ label: label()
167
+ }
168
+
169
+ useEventListener(s.s.form, "validation-errors", onValidationErrors)
170
+
171
+ return {
172
+ inputProps,
173
+ wrapperOpts,
174
+ restProps
175
+ }
176
+ }
177
+
178
+ export default useInput