@kaspernj/api-maker 1.0.214 → 1.0.217
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 +5 -4
- package/src/base-model.mjs +1 -1
- package/src/bootstrap/attribute-row/basic-style.scss +9 -0
- package/src/bootstrap/attribute-row/index.jsx +84 -0
- package/src/bootstrap/attribute-rows.jsx +27 -0
- package/src/bootstrap/card.jsx +135 -0
- package/src/bootstrap/checkbox.jsx +79 -0
- package/src/bootstrap/checkboxes.jsx +122 -0
- package/src/bootstrap/index.js +0 -0
- package/src/bootstrap/input.jsx +160 -0
- package/src/bootstrap/invalid-feedback.jsx +31 -0
- package/src/bootstrap/live-table/model-row.jsx +150 -0
- package/src/bootstrap/live-table.jsx +399 -0
- package/src/bootstrap/paginate.jsx +153 -0
- package/src/bootstrap/radio-buttons.jsx +87 -0
- package/src/bootstrap/select.jsx +110 -0
- package/src/bootstrap/sort-link.jsx +102 -0
- package/src/collection-loader.jsx +7 -8
- package/src/inputs/auto-submit.mjs +37 -0
- package/src/inputs/checkbox.jsx +97 -0
- package/src/inputs/checkboxes.jsx +113 -0
- package/src/inputs/id-for-component.mjs +15 -0
- package/src/inputs/input-wrapper.jsx +170 -0
- package/src/inputs/input.jsx +235 -0
- package/src/inputs/money.jsx +177 -0
- package/src/inputs/name-for-component.mjs +15 -0
- package/src/inputs/select.jsx +87 -0
- package/src/model-class-require.mjs +1 -1
- package/src/model-name.mjs +1 -1
- package/src/super-admin/index.jsx +11 -0
- package/src/super-admin/layout/header/index.jsx +60 -0
- package/src/super-admin/layout/header/style.scss +124 -0
- package/src/super-admin/layout/index.jsx +156 -0
- package/src/super-admin/layout/menu/index.jsx +116 -0
- package/src/super-admin/layout/menu/menu-content.jsx +106 -0
- package/src/super-admin/layout/menu/menu-item/index.jsx +27 -0
- package/src/super-admin/layout/menu/menu-item/style.scss +30 -0
- package/src/super-admin/layout/menu/style.scss +103 -0
- package/src/super-admin/layout/style.scss +25 -0
- package/src/table/column-identifier.mjs +23 -0
- package/src/table/column-visible.mjs +7 -0
- package/src/table/model-row.jsx +182 -0
- package/src/table/select-calculator.mjs +48 -0
- package/src/table/style.scss +72 -0
- package/src/table/table-settings.js +175 -0
- package/src/table/table.jsx +498 -0
- package/src/table/variables.scss +11 -0
- package/src/table/with-breakpoint.jsx +48 -0
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
render() {
|
|
24
|
+
const {errors, form} = digs(this.state, "errors", "form")
|
|
25
|
+
const type = this.inputType()
|
|
26
|
+
const inputProps = {
|
|
27
|
+
id: idForComponent(this),
|
|
28
|
+
name: nameForComponent(this),
|
|
29
|
+
ref: this.inputRef()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!inputProps.ref) throw new Error("No input ref?")
|
|
33
|
+
|
|
34
|
+
if (this.handleAsCheckbox()) {
|
|
35
|
+
inputProps.defaultChecked = this.inputDefaultChecked()
|
|
36
|
+
} else {
|
|
37
|
+
inputProps.defaultValue = this.inputDefaultValue()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!this.handleAsSelect()) inputProps.type = type
|
|
41
|
+
|
|
42
|
+
const wrapperOpts = {
|
|
43
|
+
errors,
|
|
44
|
+
form,
|
|
45
|
+
label: this.label()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{form &&
|
|
51
|
+
<EventListener event="validation-errors" onCalled={digg(this, "onValidationErrors")} target={form} />
|
|
52
|
+
}
|
|
53
|
+
<WrapperComponentClass
|
|
54
|
+
inputProps={inputProps}
|
|
55
|
+
wrapperOpts={wrapperOpts}
|
|
56
|
+
{...this.props}
|
|
57
|
+
/>
|
|
58
|
+
</>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
formatValue (value) {
|
|
63
|
+
const {formatValue} = this.props
|
|
64
|
+
|
|
65
|
+
if (formatValue) {
|
|
66
|
+
return formatValue(value)
|
|
67
|
+
} else if (value instanceof Date && !isNaN(value.getTime())) {
|
|
68
|
+
// We need to use a certain format for datetime-local
|
|
69
|
+
if (this.inputType() == "datetime-local") {
|
|
70
|
+
return strftime("%Y-%m-%dT%H:%M:%S", value)
|
|
71
|
+
} else if (this.inputType() == "date") {
|
|
72
|
+
return strftime("%Y-%m-%d", value)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handleAsCheckbox() {
|
|
80
|
+
if (this.props.type == "checkbox") return true
|
|
81
|
+
if (!("type" in this.props) && wrapperOptions.type == "checkbox") return true
|
|
82
|
+
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
handleAsSelect() {
|
|
87
|
+
if (wrapperOptions.type == "select") return true
|
|
88
|
+
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
inputDefaultChecked () {
|
|
93
|
+
if ("defaultChecked" in this.props) {
|
|
94
|
+
return this.props.defaultChecked
|
|
95
|
+
} else if (this.props.model) {
|
|
96
|
+
if (!this.props.model[this.props.attribute])
|
|
97
|
+
throw new Error(`No such attribute: ${this.props.attribute}`)
|
|
98
|
+
|
|
99
|
+
return this.props.model[this.props.attribute]()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
inputDefaultValue() {
|
|
104
|
+
if ("defaultValue" in this.props) {
|
|
105
|
+
return this.formatValue(this.props.defaultValue)
|
|
106
|
+
} else if (this.props.model) {
|
|
107
|
+
if (!this.props.model[this.props.attribute]) {
|
|
108
|
+
throw new Error(`No such attribute: ${digg(this.props.model.modelClassData(), "name")}#${this.props.attribute}`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this.formatValue(this.props.model[this.props.attribute]())
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
inputName() {
|
|
116
|
+
if (this.state.blankInputName) return ""
|
|
117
|
+
|
|
118
|
+
return nameForComponent(this)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
inputRefBackup() {
|
|
122
|
+
if (!this._inputRefBackup) this._inputRefBackup = React.createRef()
|
|
123
|
+
|
|
124
|
+
return this._inputRefBackup
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
inputRef() {
|
|
128
|
+
return this.props.inputRef || this.inputRefBackup()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
inputType() {
|
|
132
|
+
if ("type" in this.props) {
|
|
133
|
+
return this.props.type
|
|
134
|
+
} else if (wrapperOptions.type == "checkbox") {
|
|
135
|
+
return "checkbox"
|
|
136
|
+
} else {
|
|
137
|
+
return "text"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
label() {
|
|
142
|
+
if ("label" in this.props) {
|
|
143
|
+
return this.props.label
|
|
144
|
+
} else if (this.props.model) {
|
|
145
|
+
return this.props.model.modelClass().humanAttributeName(this.props.attribute)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onValidationErrors = (event) => {
|
|
150
|
+
const errors = event.detail.getValidationErrorsForInput({
|
|
151
|
+
attribute: this.props.attribute,
|
|
152
|
+
inputName: this.inputName(),
|
|
153
|
+
onMatchValidationError: this.props.onMatchValidationError
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
this.setState({errors})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
setForm () {
|
|
160
|
+
const inputElement = this.inputRef().current
|
|
161
|
+
|
|
162
|
+
let form
|
|
163
|
+
|
|
164
|
+
if (inputElement) form = dig(inputElement, "form")
|
|
165
|
+
if (form && form != this.state.form) this.setState({form})
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default inputWrapper
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import AutoSubmit from "./auto-submit.mjs"
|
|
2
|
+
import {dig, digg, digs} from "diggerize"
|
|
3
|
+
import EventUpdated from "../event-updated"
|
|
4
|
+
import inputWrapper from "./input-wrapper"
|
|
5
|
+
import Money from "./money"
|
|
6
|
+
import PropTypes from "prop-types"
|
|
7
|
+
import React from "react"
|
|
8
|
+
import replaceall from "replaceall"
|
|
9
|
+
import strftime from "strftime"
|
|
10
|
+
|
|
11
|
+
class ApiMakerInputsInput extends React.PureComponent {
|
|
12
|
+
static defaultProps = {
|
|
13
|
+
autoRefresh: false,
|
|
14
|
+
autoSubmit: false,
|
|
15
|
+
localizedNumber: false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static propTypes = {
|
|
19
|
+
attribute: PropTypes.string,
|
|
20
|
+
autoRefresh: PropTypes.bool.isRequired,
|
|
21
|
+
autoSubmit: PropTypes.bool.isRequired,
|
|
22
|
+
className: PropTypes.string,
|
|
23
|
+
formatValue: PropTypes.func,
|
|
24
|
+
id: PropTypes.string,
|
|
25
|
+
localizedNumber: PropTypes.bool.isRequired,
|
|
26
|
+
model: PropTypes.object,
|
|
27
|
+
name: PropTypes.string,
|
|
28
|
+
onChange: PropTypes.func,
|
|
29
|
+
onMatchValidationError: PropTypes.func,
|
|
30
|
+
type: PropTypes.string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
visibleInputRef = React.createRef()
|
|
34
|
+
state = {
|
|
35
|
+
blankInputName: digg(this, "props", "inputProps", "type") == "file"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
render () {
|
|
39
|
+
const {
|
|
40
|
+
attribute,
|
|
41
|
+
autoRefresh,
|
|
42
|
+
autoSubmit,
|
|
43
|
+
defaultValue,
|
|
44
|
+
formatValue,
|
|
45
|
+
id,
|
|
46
|
+
inputProps,
|
|
47
|
+
inputRef,
|
|
48
|
+
localizedNumber,
|
|
49
|
+
model,
|
|
50
|
+
name,
|
|
51
|
+
onChange,
|
|
52
|
+
onErrors,
|
|
53
|
+
onMatchValidationError,
|
|
54
|
+
type,
|
|
55
|
+
wrapperOpts,
|
|
56
|
+
...restProps
|
|
57
|
+
} = this.props
|
|
58
|
+
|
|
59
|
+
const sharedProps = {
|
|
60
|
+
id: localizedNumber ? null : inputProps.id,
|
|
61
|
+
name: localizedNumber ? null : inputProps.name,
|
|
62
|
+
}
|
|
63
|
+
const ref = localizedNumber ? this.visibleInputRef : this.inputReference()
|
|
64
|
+
const {ref: inputPropsRef, ...inputPropsWithoutRef} = inputProps
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
{autoRefresh && model &&
|
|
69
|
+
<EventUpdated model={model} onUpdated={this.onModelUpdated} />
|
|
70
|
+
}
|
|
71
|
+
{localizedNumber &&
|
|
72
|
+
<input
|
|
73
|
+
defaultValue={defaultValue}
|
|
74
|
+
id={inputProps.id}
|
|
75
|
+
name={this.inputName()}
|
|
76
|
+
ref={this.inputReference()}
|
|
77
|
+
type="hidden"
|
|
78
|
+
/>
|
|
79
|
+
}
|
|
80
|
+
{type == "money" &&
|
|
81
|
+
<Money
|
|
82
|
+
attribute={attribute}
|
|
83
|
+
defaultValue={this.inputDefaultValueLocalized()}
|
|
84
|
+
inputRef={this.inputReference()}
|
|
85
|
+
model={model}
|
|
86
|
+
onChange={this.onInputChanged}
|
|
87
|
+
{...inputPropsWithoutRef}
|
|
88
|
+
{...sharedProps}
|
|
89
|
+
{...restProps}
|
|
90
|
+
/>
|
|
91
|
+
}
|
|
92
|
+
{type == "textarea" &&
|
|
93
|
+
<textarea
|
|
94
|
+
defaultValue={this.inputDefaultValueLocalized()}
|
|
95
|
+
onChange={this.onInputChanged}
|
|
96
|
+
ref={ref}
|
|
97
|
+
{...inputPropsWithoutRef}
|
|
98
|
+
{...sharedProps}
|
|
99
|
+
{...restProps}
|
|
100
|
+
/>
|
|
101
|
+
}
|
|
102
|
+
{type != "money" && type != "textarea" &&
|
|
103
|
+
<input
|
|
104
|
+
defaultValue={this.inputDefaultValueLocalized()}
|
|
105
|
+
onChange={this.onInputChanged}
|
|
106
|
+
ref={ref}
|
|
107
|
+
{...inputPropsWithoutRef}
|
|
108
|
+
{...sharedProps}
|
|
109
|
+
name={localizedNumber ? null : this.inputName()}
|
|
110
|
+
{...restProps}
|
|
111
|
+
/>
|
|
112
|
+
}
|
|
113
|
+
</>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
actualValue (visibleInput) {
|
|
118
|
+
const {localizedNumber} = digs(this.props, "localizedNumber")
|
|
119
|
+
const value = digg(visibleInput, "value")
|
|
120
|
+
|
|
121
|
+
if (localizedNumber) {
|
|
122
|
+
const decimal = I18n.t("number.currency.format.separator")
|
|
123
|
+
const integerSeparator = I18n.t("number.currency.format.delimiter")
|
|
124
|
+
|
|
125
|
+
let unformatted = replaceall(integerSeparator, "", value)
|
|
126
|
+
|
|
127
|
+
unformatted = replaceall(decimal, ".", unformatted)
|
|
128
|
+
|
|
129
|
+
return unformatted
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
autoSubmit = () => {
|
|
136
|
+
new AutoSubmit({component: this}).autoSubmit()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
formatValue (value) {
|
|
140
|
+
const {formatValue, type} = this.props
|
|
141
|
+
|
|
142
|
+
if (formatValue) {
|
|
143
|
+
return formatValue(value)
|
|
144
|
+
} else if (value instanceof Date && !isNaN(value.getTime())) {
|
|
145
|
+
// We need to use a certain format for datetime-local
|
|
146
|
+
if (type == "datetime-local") {
|
|
147
|
+
return strftime("%Y-%m-%dT%H:%M:%S", value)
|
|
148
|
+
} else if (type == "date") {
|
|
149
|
+
return strftime("%Y-%m-%d", value)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return value
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
inputDefaultValueLocalized () {
|
|
157
|
+
const {defaultValue} = this.props
|
|
158
|
+
const {localizedNumber} = digs(this.props, "localizedNumber")
|
|
159
|
+
|
|
160
|
+
if (localizedNumber && defaultValue !== null && defaultValue !== undefined) {
|
|
161
|
+
const separator = I18n.t("number.currency.format.separator")
|
|
162
|
+
const delimiter = I18n.t("number.currency.format.delimiter")
|
|
163
|
+
|
|
164
|
+
let formatted = `${defaultValue}`
|
|
165
|
+
|
|
166
|
+
formatted = replaceall(".", "{{separator}}", formatted)
|
|
167
|
+
formatted = replaceall(",", "{{delimiter}}", formatted)
|
|
168
|
+
formatted = replaceall("{{separator}}", separator, formatted)
|
|
169
|
+
formatted = replaceall("{{delimiter}}", delimiter, formatted)
|
|
170
|
+
|
|
171
|
+
return formatted
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return defaultValue
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
inputName () {
|
|
178
|
+
if (this.state.blankInputName) return ""
|
|
179
|
+
|
|
180
|
+
return this.props.inputProps.name
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
inputReference() {
|
|
184
|
+
return digg(this, "props", "inputProps", "ref")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
onModelUpdated = (args) => {
|
|
188
|
+
const inputRef = this.inputReference()
|
|
189
|
+
|
|
190
|
+
if (!inputRef.current) {
|
|
191
|
+
// This can happen if the component is being unmounted
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const {attribute} = digs(this.props, "attribute")
|
|
196
|
+
const newModel = digg(args, "model")
|
|
197
|
+
const currentValue = digg(inputRef, "current", "value")
|
|
198
|
+
const newValue = newModel.readAttribute(attribute)
|
|
199
|
+
const newFormattedValue = this.formatValue(newValue)
|
|
200
|
+
|
|
201
|
+
if (currentValue != newFormattedValue) {
|
|
202
|
+
inputRef.current.value = newFormattedValue
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
onInputChanged = (e) => {
|
|
207
|
+
const {attribute, autoSubmit, inputProps, model, onChange} = this.props
|
|
208
|
+
const {localizedNumber} = digs(this.props, "localizedNumber")
|
|
209
|
+
|
|
210
|
+
if (localizedNumber) this.inputReference().current.value = this.actualValue(digg(e, "target"))
|
|
211
|
+
|
|
212
|
+
if (attribute && autoSubmit && model) this.delayAutoSubmit()
|
|
213
|
+
if (digg(inputProps, "type") == "file") this.setState({blankInputName: this.getBlankInputName()})
|
|
214
|
+
if (onChange) onChange(e)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
delayAutoSubmit () {
|
|
218
|
+
if (this.delayAutoSubmitTimeout) {
|
|
219
|
+
clearTimeout(this.delayAutoSubmitTimeout)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.delayAutoSubmitTimeout = setTimeout(this.autoSubmit, 200)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// This fixes an issue in Firefox and ActiveStorage, where uploads would be a blank string if a file wasn't chosen
|
|
226
|
+
getBlankInputName () {
|
|
227
|
+
const value = dig(this.inputReference(), "current", "value")
|
|
228
|
+
|
|
229
|
+
if (this.props.inputProps.type == "file" && value == "")
|
|
230
|
+
return true
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export {ApiMakerInputsInput as Input}
|
|
235
|
+
export default inputWrapper(ApiMakerInputsInput)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import classNames from "classnames"
|
|
2
|
+
import Config from "../config.mjs"
|
|
3
|
+
import {digg} from "diggerize"
|
|
4
|
+
import idForComponent from "./id-for-component.mjs"
|
|
5
|
+
import inflection from "inflection"
|
|
6
|
+
import MoneyFormatter from "../money-formatter"
|
|
7
|
+
import PropTypes from "prop-types"
|
|
8
|
+
import PropTypesExact from "prop-types-exact"
|
|
9
|
+
import React from "react"
|
|
10
|
+
|
|
11
|
+
export default class ApiMakerInputsMoney extends React.PureComponent {
|
|
12
|
+
static defaultProps = {
|
|
13
|
+
disabled: false,
|
|
14
|
+
showCurrencyOptions: true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static propTypes = PropTypesExact({
|
|
18
|
+
attribute: PropTypes.string,
|
|
19
|
+
centsInputName: PropTypes.string,
|
|
20
|
+
className: PropTypes.string,
|
|
21
|
+
currenciesCollection: PropTypes.array,
|
|
22
|
+
currencyName: PropTypes.string,
|
|
23
|
+
defaultValue: PropTypes.object,
|
|
24
|
+
disabled: PropTypes.bool.isRequired,
|
|
25
|
+
id: PropTypes.string,
|
|
26
|
+
inputRef: PropTypes.object,
|
|
27
|
+
label: PropTypes.node,
|
|
28
|
+
model: PropTypes.object,
|
|
29
|
+
name: PropTypes.string,
|
|
30
|
+
onChange: PropTypes.func,
|
|
31
|
+
placeholder: PropTypes.node,
|
|
32
|
+
showCurrencyOptions: PropTypes.bool,
|
|
33
|
+
small: PropTypes.bool,
|
|
34
|
+
type: PropTypes.string
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
inputRef = React.createRef()
|
|
38
|
+
|
|
39
|
+
getInputRef () {
|
|
40
|
+
return this.props.inputRef || this.inputRef
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render () {
|
|
44
|
+
const {attribute, className, disabled, model, showCurrencyOptions} = this.props
|
|
45
|
+
let {currenciesCollection} = this.props
|
|
46
|
+
|
|
47
|
+
if (!currenciesCollection) currenciesCollection = Config.getCurrenciesCollection()
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="api-maker-inputs-money" data-attribute={attribute} data-model-id={model?.id()}>
|
|
51
|
+
<input defaultValue={this.inputDefaultCentsValue()} id={this.inputCentsId()} name={this.inputCentsName()} ref={this.getInputRef()} type="hidden" />
|
|
52
|
+
<input
|
|
53
|
+
className={classNames("money-cents", className)}
|
|
54
|
+
defaultValue={this.inputDefaultValue()}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
id={this.inputId()}
|
|
57
|
+
onBlur={digg(this, "setAmount")}
|
|
58
|
+
onChange={digg(this, "setCents")}
|
|
59
|
+
onKeyUp={digg(this, "setCents")}
|
|
60
|
+
placeholder={this.props.placeholder}
|
|
61
|
+
ref="whole"
|
|
62
|
+
type="text"
|
|
63
|
+
/>
|
|
64
|
+
{showCurrencyOptions &&
|
|
65
|
+
<select
|
|
66
|
+
className="money-currency"
|
|
67
|
+
defaultValue={this.inputCurrencyValue()}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
id={this.inputCurrencyId()}
|
|
70
|
+
name={this.inputCurrencyName()}
|
|
71
|
+
onChange={digg(this, "onCurrencyChanged")}
|
|
72
|
+
ref="currency"
|
|
73
|
+
>
|
|
74
|
+
<option></option>
|
|
75
|
+
{currenciesCollection.map((option) => (
|
|
76
|
+
<option key={`select-option-${option[1]}`} value={option[1]}>
|
|
77
|
+
{this.props.small && option[1]}
|
|
78
|
+
{!this.props.small && option[0]}
|
|
79
|
+
</option>
|
|
80
|
+
))}
|
|
81
|
+
</select>
|
|
82
|
+
}
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
inputCurrencyId () {
|
|
88
|
+
return `${this.inputId()}_currency`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
inputCurrencyName () {
|
|
92
|
+
if ("currencyName" in this.props)
|
|
93
|
+
return this.props.currencyName
|
|
94
|
+
|
|
95
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_currency]`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
inputCurrencyValue () {
|
|
99
|
+
const {defaultValue} = this.props
|
|
100
|
+
|
|
101
|
+
if (defaultValue) {
|
|
102
|
+
return MoneyFormatter.currencyFromMoney(defaultValue).code
|
|
103
|
+
} else {
|
|
104
|
+
return "DKK"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
inputDefaultValue () {
|
|
109
|
+
const {defaultValue} = this.props
|
|
110
|
+
|
|
111
|
+
if (defaultValue) {
|
|
112
|
+
return MoneyFormatter.fromMoney({amount: defaultValue.amount, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
|
|
113
|
+
} else {
|
|
114
|
+
return ""
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
inputDefaultCentsValue () {
|
|
119
|
+
const {defaultValue} = this.props
|
|
120
|
+
|
|
121
|
+
if (this.getInputRef().current) {
|
|
122
|
+
return digg(this.getInputRef(), "current", "value")
|
|
123
|
+
} else if (defaultValue) {
|
|
124
|
+
return MoneyFormatter.amountFromMoney(defaultValue)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
inputCentsId () {
|
|
129
|
+
return `${this.inputId()}_cents`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
inputCentsName () {
|
|
133
|
+
if ("name" in this.props)
|
|
134
|
+
return this.props.name
|
|
135
|
+
|
|
136
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_cents]`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
inputId () {
|
|
140
|
+
return idForComponent(this)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
onCurrencyChanged = () => {
|
|
144
|
+
if (this.props.onChange)
|
|
145
|
+
this.props.onChange()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
setAmount = () => {
|
|
149
|
+
const inputElement = this.getInputRef().current
|
|
150
|
+
|
|
151
|
+
if (!inputElement.value && inputElement.value == "") {
|
|
152
|
+
this.refs.whole.value = ""
|
|
153
|
+
} else {
|
|
154
|
+
const cents = parseFloat(inputElement.value)
|
|
155
|
+
const formatted = MoneyFormatter.fromMoney({amount: cents, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
|
|
156
|
+
|
|
157
|
+
this.refs.whole.value = formatted
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setCents = () => {
|
|
162
|
+
const inputElement = this.getInputRef().current
|
|
163
|
+
|
|
164
|
+
let whole = MoneyFormatter.stringToFloat(this.refs.whole.value)
|
|
165
|
+
let cents = parseInt(whole * 100, 10)
|
|
166
|
+
let oldCents = parseInt(inputElement.value, 10)
|
|
167
|
+
|
|
168
|
+
if (cents) {
|
|
169
|
+
inputElement.value = cents
|
|
170
|
+
} else {
|
|
171
|
+
inputElement.value = ''
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.props.onChange && oldCents != cents)
|
|
175
|
+
this.props.onChange()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import inflection from "inflection"
|
|
2
|
+
|
|
3
|
+
export default function apiMakerNameForComponent (component) {
|
|
4
|
+
if ("name" in component.props) {
|
|
5
|
+
return component.props.name
|
|
6
|
+
} else if (component.props.attribute && component.props.model) {
|
|
7
|
+
let attributeName = inflection.underscore(component.props.attribute)
|
|
8
|
+
|
|
9
|
+
if (component.props.type == "money") {
|
|
10
|
+
attributeName += "_cents"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return `${component.props.model.modelClassData().paramKey}[${attributeName}]`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {dig} from "diggerize"
|
|
2
|
+
import inputWrapper from "./input-wrapper"
|
|
3
|
+
import PropTypes from "prop-types"
|
|
4
|
+
import React from "react"
|
|
5
|
+
|
|
6
|
+
class ApiMakerInputsSelect extends React.PureComponent {
|
|
7
|
+
static propTypes = {
|
|
8
|
+
attribute: PropTypes.string,
|
|
9
|
+
children: PropTypes.node,
|
|
10
|
+
defaultValue: PropTypes.oneOfType([PropTypes.array, PropTypes.number, PropTypes.string]),
|
|
11
|
+
id: PropTypes.string,
|
|
12
|
+
includeBlank: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
|
|
13
|
+
inputProps: PropTypes.object.isRequired,
|
|
14
|
+
model: PropTypes.object,
|
|
15
|
+
name: PropTypes.string,
|
|
16
|
+
options: PropTypes.array,
|
|
17
|
+
wrapperOpts: PropTypes.object.isRequired
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
render () {
|
|
21
|
+
const {
|
|
22
|
+
attribute,
|
|
23
|
+
children,
|
|
24
|
+
defaultValue,
|
|
25
|
+
id,
|
|
26
|
+
includeBlank,
|
|
27
|
+
inputProps,
|
|
28
|
+
inputRef,
|
|
29
|
+
model,
|
|
30
|
+
name,
|
|
31
|
+
options,
|
|
32
|
+
wrapperOpts,
|
|
33
|
+
...restProps
|
|
34
|
+
} = this.props
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<select {...inputProps} {...restProps}>
|
|
38
|
+
{this.includeBlank() &&
|
|
39
|
+
<option data-include-blank="true">
|
|
40
|
+
{typeof includeBlank != "boolean" ? includeBlank : null}
|
|
41
|
+
</option>
|
|
42
|
+
}
|
|
43
|
+
{options && options.map((option) =>
|
|
44
|
+
<option key={this.optionKey(option)} value={this.optionValue(option)}>
|
|
45
|
+
{this.optionLabel(option)}
|
|
46
|
+
</option>
|
|
47
|
+
)}
|
|
48
|
+
{children}
|
|
49
|
+
</select>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
optionKey (option) {
|
|
54
|
+
if (Array.isArray(option)) {
|
|
55
|
+
return `select-option-${option[1]}`
|
|
56
|
+
} else {
|
|
57
|
+
return `select-option-${option}`
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
optionLabel (option) {
|
|
62
|
+
if (Array.isArray(option)) {
|
|
63
|
+
return option[0]
|
|
64
|
+
} else {
|
|
65
|
+
return option
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
optionValue (option) {
|
|
70
|
+
if (Array.isArray(option)) {
|
|
71
|
+
return option[1]
|
|
72
|
+
} else {
|
|
73
|
+
return option
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
includeBlank () {
|
|
78
|
+
if (this.props.includeBlank && !this.props.multiple) {
|
|
79
|
+
return true
|
|
80
|
+
} else {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {ApiMakerInputsSelect as Select}
|
|
87
|
+
export default inputWrapper(ApiMakerInputsSelect)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {digg} from "diggerize"
|
|
2
2
|
import inflection from "inflection"
|
|
3
|
-
import * as models from "
|
|
3
|
+
import * as models from "./models.mjs.erb"
|
|
4
4
|
|
|
5
5
|
export default (modelName) => {
|
|
6
6
|
const requireName = inflection.camelize(modelName)
|
package/src/model-name.mjs
CHANGED