@kaspernj/api-maker 1.0.339 → 1.0.341

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