api_maker 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +476 -0
- data/Rakefile +27 -0
- data/app/channels/api_maker/subscriptions_channel.rb +80 -0
- data/app/controllers/api_maker/base_controller.rb +32 -0
- data/app/controllers/api_maker/commands_controller.rb +26 -0
- data/app/controllers/api_maker/devise_controller.rb +60 -0
- data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
- data/app/services/api_maker/application_service.rb +7 -0
- data/app/services/api_maker/collection_command_service.rb +24 -0
- data/app/services/api_maker/command_response.rb +67 -0
- data/app/services/api_maker/command_service.rb +31 -0
- data/app/services/api_maker/create_command.rb +62 -0
- data/app/services/api_maker/create_command_service.rb +18 -0
- data/app/services/api_maker/destroy_command.rb +39 -0
- data/app/services/api_maker/destroy_command_service.rb +22 -0
- data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
- data/app/services/api_maker/index_command.rb +96 -0
- data/app/services/api_maker/index_command_service.rb +22 -0
- data/app/services/api_maker/js_method_namer_service.rb +11 -0
- data/app/services/api_maker/member_command_service.rb +25 -0
- data/app/services/api_maker/model_content_generator_service.rb +108 -0
- data/app/services/api_maker/models_finder_service.rb +22 -0
- data/app/services/api_maker/models_generator_service.rb +104 -0
- data/app/services/api_maker/update_command.rb +43 -0
- data/app/services/api_maker/update_command_service.rb +21 -0
- data/app/services/api_maker/valid_command.rb +35 -0
- data/app/services/api_maker/valid_command_service.rb +21 -0
- data/app/views/api_maker/_data.html.erb +15 -0
- data/config/rails_best_practices.yml +55 -0
- data/config/routes.rb +7 -0
- data/lib/api_maker.rb +36 -0
- data/lib/api_maker/ability.rb +39 -0
- data/lib/api_maker/ability_loader.rb +21 -0
- data/lib/api_maker/action_controller_base_extensions.rb +5 -0
- data/lib/api_maker/base_command.rb +81 -0
- data/lib/api_maker/base_resource.rb +78 -0
- data/lib/api_maker/collection_serializer.rb +69 -0
- data/lib/api_maker/command_spec_helper.rb +57 -0
- data/lib/api_maker/configuration.rb +34 -0
- data/lib/api_maker/engine.rb +5 -0
- data/lib/api_maker/individual_command.rb +37 -0
- data/lib/api_maker/javascript/api.js +92 -0
- data/lib/api_maker/javascript/base-model.js +543 -0
- data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
- data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
- data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
- data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
- data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
- data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
- data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
- data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
- data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
- data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
- data/lib/api_maker/javascript/cable-subscription.js +33 -0
- data/lib/api_maker/javascript/collection.js +186 -0
- data/lib/api_maker/javascript/commands-pool.js +123 -0
- data/lib/api_maker/javascript/custom-error.js +14 -0
- data/lib/api_maker/javascript/deserializer.js +35 -0
- data/lib/api_maker/javascript/devise.js.erb +113 -0
- data/lib/api_maker/javascript/error-logger.js +119 -0
- data/lib/api_maker/javascript/event-connection.jsx +24 -0
- data/lib/api_maker/javascript/event-created.jsx +26 -0
- data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
- data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
- data/lib/api_maker/javascript/event-listener.jsx +41 -0
- data/lib/api_maker/javascript/event-updated.jsx +26 -0
- data/lib/api_maker/javascript/form-data-to-object.js +70 -0
- data/lib/api_maker/javascript/included.js +39 -0
- data/lib/api_maker/javascript/key-value-store.js +47 -0
- data/lib/api_maker/javascript/logger.js +23 -0
- data/lib/api_maker/javascript/model-name.js +21 -0
- data/lib/api_maker/javascript/model-template.js.erb +110 -0
- data/lib/api_maker/javascript/models-response-reader.js +43 -0
- data/lib/api_maker/javascript/paginate.jsx +128 -0
- data/lib/api_maker/javascript/params.js +68 -0
- data/lib/api_maker/javascript/resource-route.jsx +75 -0
- data/lib/api_maker/javascript/resource-routes.jsx +36 -0
- data/lib/api_maker/javascript/result.js +25 -0
- data/lib/api_maker/javascript/session-status-updater.js +113 -0
- data/lib/api_maker/javascript/sort-link.jsx +88 -0
- data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
- data/lib/api_maker/loader.rb +14 -0
- data/lib/api_maker/memory_storage.rb +65 -0
- data/lib/api_maker/model_extensions.rb +96 -0
- data/lib/api_maker/permitted_params_argument.rb +12 -0
- data/lib/api_maker/preloader.rb +91 -0
- data/lib/api_maker/preloader_belongs_to.rb +58 -0
- data/lib/api_maker/preloader_has_many.rb +69 -0
- data/lib/api_maker/preloader_has_one.rb +70 -0
- data/lib/api_maker/preloader_through.rb +101 -0
- data/lib/api_maker/railtie.rb +14 -0
- data/lib/api_maker/relationship_includer.rb +42 -0
- data/lib/api_maker/resource_routing.rb +8 -0
- data/lib/api_maker/result_parser.rb +50 -0
- data/lib/api_maker/serializer.rb +86 -0
- data/lib/api_maker/spec_helper.rb +100 -0
- data/lib/api_maker/version.rb +3 -0
- data/lib/tasks/api_maker_tasks.rake +5 -0
- metadata +581 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
import Api from "api-maker/api"
|
2
|
+
import formatNumber from "format-number"
|
3
|
+
import PropTypes from "prop-types"
|
4
|
+
import React from "react"
|
5
|
+
|
6
|
+
const inflection = require("inflection")
|
7
|
+
|
8
|
+
export default class BootstrapMoneyInput extends React.Component {
|
9
|
+
constructor(props) {
|
10
|
+
super(props)
|
11
|
+
this.state = {}
|
12
|
+
|
13
|
+
let formatOptions = {
|
14
|
+
decimal: I18n.t("number.currency.format.separator"),
|
15
|
+
integerSeparator: I18n.t("number.currency.format.delimiter")
|
16
|
+
}
|
17
|
+
this.formatter = formatNumber(formatOptions)
|
18
|
+
}
|
19
|
+
|
20
|
+
render() {
|
21
|
+
return (
|
22
|
+
<div className="component-api-maker-bootstrap-money-input">
|
23
|
+
<input defaultValue={this.inputDefaultCentsValue()} id={this.inputCentsId()} name={this.inputCentsName()} ref="input" type="hidden" />
|
24
|
+
|
25
|
+
<div className="input-group">
|
26
|
+
<input
|
27
|
+
className={this.props.className}
|
28
|
+
defaultValue={this.inputDefaultValue()}
|
29
|
+
id={this.inputId()}
|
30
|
+
onBlur={() => this.setAmount()}
|
31
|
+
onChange={() => this.setCents()}
|
32
|
+
onKeyUp={() => this.setCents()}
|
33
|
+
placeholder={this.props.placeholder}
|
34
|
+
ref="whole"
|
35
|
+
type="text"
|
36
|
+
/>
|
37
|
+
<select className="component-bootstrap-money-input" defaultValue={this.inputCurrencyValue()} id={this.inputCurrencyId()} name={this.inputCurrencyName()} onChange={() => { this.onCurrencyChanged() }} ref="currency">
|
38
|
+
<option></option>
|
39
|
+
{this.props.currenciesCollection.map(option => (
|
40
|
+
<option key={`select-option-${option[1]}`} value={option[1]}>
|
41
|
+
{this.props.small && option[1]}
|
42
|
+
{!this.props.small && option[0]}
|
43
|
+
</option>
|
44
|
+
))}
|
45
|
+
</select>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
)
|
49
|
+
}
|
50
|
+
|
51
|
+
inputCurrencyId() {
|
52
|
+
return `${this.inputId()}_currency`
|
53
|
+
}
|
54
|
+
|
55
|
+
inputCurrencyName() {
|
56
|
+
if (this.props.currencyName)
|
57
|
+
return this.props.currencyName
|
58
|
+
|
59
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_currency]`
|
60
|
+
}
|
61
|
+
|
62
|
+
inputCurrencyValue() {
|
63
|
+
let value = this.props.model[this.props.attribute]()
|
64
|
+
|
65
|
+
if (value) {
|
66
|
+
return MoneyFormatter.currencyFromMoney(value).code
|
67
|
+
} else {
|
68
|
+
return "DKK"
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
inputDefaultValue() {
|
73
|
+
let value = this.props.model[this.props.attribute]()
|
74
|
+
|
75
|
+
if (value) {
|
76
|
+
return MoneyFormatter.fromMoney({amount: value.amount, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
|
77
|
+
} else {
|
78
|
+
return ""
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
inputDefaultCentsValue() {
|
83
|
+
let value = this.props.model[this.props.attribute]()
|
84
|
+
|
85
|
+
if (this.refs.input)
|
86
|
+
return this.refs.input.value
|
87
|
+
|
88
|
+
if (value)
|
89
|
+
return MoneyFormatter.amountFromMoney(value)
|
90
|
+
}
|
91
|
+
|
92
|
+
inputCentsId() {
|
93
|
+
return `${this.inputId()}_cents`
|
94
|
+
}
|
95
|
+
|
96
|
+
inputCentsName() {
|
97
|
+
if (this.props.name)
|
98
|
+
return this.props.name
|
99
|
+
|
100
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_cents]`
|
101
|
+
}
|
102
|
+
|
103
|
+
inputId() {
|
104
|
+
return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
|
105
|
+
}
|
106
|
+
|
107
|
+
onCurrencyChanged() {
|
108
|
+
if (this.props.onChange)
|
109
|
+
this.props.onChange()
|
110
|
+
}
|
111
|
+
|
112
|
+
setAmount() {
|
113
|
+
if (!this.refs.input.value && this.refs.input.value == "") {
|
114
|
+
this.refs.whole.value = ""
|
115
|
+
} else {
|
116
|
+
let cents = parseFloat(this.refs.input.value)
|
117
|
+
let formatted = MoneyFormatter.fromMoney({amount: cents, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
|
118
|
+
|
119
|
+
this.refs.whole.value = formatted
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
setCents() {
|
124
|
+
let whole = MoneyFormatter.stringToFloat(this.refs.whole.value)
|
125
|
+
let cents = parseInt(whole * 100)
|
126
|
+
let oldCents = parseInt(this.refs.input.value)
|
127
|
+
this.refs.input.value = cents
|
128
|
+
|
129
|
+
if (this.props.onChange && oldCents != cents)
|
130
|
+
this.props.onChange()
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
BootstrapMoneyInput.propTypes = {
|
135
|
+
currenciesCollection: PropTypes.array.isRequired
|
136
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import PropTypesExact from "prop-types-exact"
|
2
|
+
import React from "react"
|
3
|
+
|
4
|
+
const inflection = require("inflection")
|
5
|
+
|
6
|
+
export default class BootstrapRadioButtons extends React.Component {
|
7
|
+
static propTypes = PropTypesExact({
|
8
|
+
attribute: PropTypes.string,
|
9
|
+
collection: PropTypes.array.isRequired,
|
10
|
+
defaultValue: PropTypes.oneOfType([
|
11
|
+
PropTypes.number,
|
12
|
+
PropTypes.string
|
13
|
+
]),
|
14
|
+
id: PropTypes.string,
|
15
|
+
name: PropTypes.string,
|
16
|
+
model: PropTypes.object,
|
17
|
+
wrapperClassName: PropTypes.string
|
18
|
+
})
|
19
|
+
|
20
|
+
render() {
|
21
|
+
return (
|
22
|
+
<div className={this.wrapperClassName()}>
|
23
|
+
<input name={this.inputName()} type="hidden" value="" />
|
24
|
+
{this.props.collection.map(option => this.optionElement(option))}
|
25
|
+
</div>
|
26
|
+
)
|
27
|
+
}
|
28
|
+
|
29
|
+
inputDefaultValue() {
|
30
|
+
if (this.props.defaultValue) {
|
31
|
+
return this.props.defaultValue
|
32
|
+
} else if (this.props.model) {
|
33
|
+
if (!this.props.model[this.props.attribute])
|
34
|
+
throw new Error(`No such attribute: ${this.props.attribute}`)
|
35
|
+
|
36
|
+
return this.props.model[this.props.attribute]()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
inputName() {
|
41
|
+
if (this.props.name) {
|
42
|
+
return this.props.name
|
43
|
+
} else if (this.props.model) {
|
44
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
generatedId() {
|
49
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
50
|
+
}
|
51
|
+
|
52
|
+
optionElement(option) {
|
53
|
+
let id = this.generatedId()
|
54
|
+
|
55
|
+
return (
|
56
|
+
<div key={`option-${option[1]}`}>
|
57
|
+
<input
|
58
|
+
data-option-value={option[1]}
|
59
|
+
defaultChecked={option[1] == this.inputDefaultValue()}
|
60
|
+
id={id}
|
61
|
+
name={this.inputName()}
|
62
|
+
type="radio"
|
63
|
+
value={option[1]} />
|
64
|
+
|
65
|
+
<label className="ml-1" htmlFor={id}>
|
66
|
+
{option[0]}
|
67
|
+
</label>
|
68
|
+
</div>
|
69
|
+
)
|
70
|
+
}
|
71
|
+
|
72
|
+
wrapperClassName() {
|
73
|
+
var classNames = ["component-bootstrap-radio-buttons"]
|
74
|
+
|
75
|
+
if (this.props.wrapperClassName)
|
76
|
+
classNames.push(this.props.wrapperClassName)
|
77
|
+
|
78
|
+
return classNames.join(" ")
|
79
|
+
}
|
80
|
+
}
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import PropTypes from "prop-types"
|
2
|
+
import PropTypesExact from "prop-types-exact"
|
3
|
+
import React from "react"
|
4
|
+
|
5
|
+
const inflection = require("inflection")
|
6
|
+
|
7
|
+
export default class BootstrapSelect extends React.Component {
|
8
|
+
static propTypes = PropTypesExact({
|
9
|
+
attribute: PropTypes.string,
|
10
|
+
children: PropTypes.node,
|
11
|
+
className: PropTypes.string,
|
12
|
+
"data-controller": PropTypes.string,
|
13
|
+
defaultValue: PropTypes.oneOfType([PropTypes.array, PropTypes.number, PropTypes.string]),
|
14
|
+
description: PropTypes.node,
|
15
|
+
disabled: PropTypes.bool,
|
16
|
+
id: PropTypes.string,
|
17
|
+
includeBlank: PropTypes.bool,
|
18
|
+
hideSearch: PropTypes.bool,
|
19
|
+
hint: PropTypes.node,
|
20
|
+
hintBottom: PropTypes.node,
|
21
|
+
label: PropTypes.node,
|
22
|
+
labelContainerClassName: PropTypes.string,
|
23
|
+
model: PropTypes.object,
|
24
|
+
multiple: PropTypes.bool,
|
25
|
+
name: PropTypes.string,
|
26
|
+
placeholder: PropTypes.string,
|
27
|
+
onChange: PropTypes.func,
|
28
|
+
options: PropTypes.array,
|
29
|
+
select2: PropTypes.bool,
|
30
|
+
wrapperClassName: PropTypes.string,
|
31
|
+
})
|
32
|
+
|
33
|
+
componentDidMount() {
|
34
|
+
if (this.props.select2 && this.props.onChange)
|
35
|
+
$(this.refs.select).on("change", this.props.onChange)
|
36
|
+
|
37
|
+
// Set default value to nothing when multiple
|
38
|
+
if (this.props.select2 && this.props.multiple && !this.inputDefaultValue())
|
39
|
+
$(this.refs.select).val("")
|
40
|
+
}
|
41
|
+
|
42
|
+
componentWillUnmount() {
|
43
|
+
if (this.props.select2 && this.props.onChange)
|
44
|
+
$(this.refs.select).off("change", this.props.onChange)
|
45
|
+
}
|
46
|
+
|
47
|
+
render() {
|
48
|
+
return (
|
49
|
+
<div className={this.wrapperClassName()}>
|
50
|
+
{this.label() &&
|
51
|
+
<div className={this.props.labelContainerClassName ? this.props.labelContainerClassName : null}>
|
52
|
+
<label className={this.labelClassName()} htmlFor={this.inputId()}>
|
53
|
+
{this.label()}
|
54
|
+
</label>
|
55
|
+
</div>
|
56
|
+
}
|
57
|
+
{this.props.description &&
|
58
|
+
<div className="mb-4">
|
59
|
+
{this.props.description}
|
60
|
+
</div>
|
61
|
+
}
|
62
|
+
{this.props.hint &&
|
63
|
+
<span className="form-text text-muted font-smoothing font-xs">
|
64
|
+
{this.props.hint}
|
65
|
+
</span>
|
66
|
+
}
|
67
|
+
<select
|
68
|
+
data-controller={this.dataController()}
|
69
|
+
data-hide-search={this.props.hideSearch}
|
70
|
+
data-placeholder={this.props.placeholder}
|
71
|
+
defaultValue={this.inputDefaultValue()}
|
72
|
+
className={`form-control ${this.props.className}`}
|
73
|
+
disabled={this.props.disabled}
|
74
|
+
id={this.inputId()}
|
75
|
+
multiple={this.props.multiple}
|
76
|
+
name={this.inputName()}
|
77
|
+
onChange={this.props.onChange}
|
78
|
+
ref="select"
|
79
|
+
>
|
80
|
+
{this.includeBlank() &&
|
81
|
+
<option />
|
82
|
+
}
|
83
|
+
{this.props.options && this.props.options.map(option => (
|
84
|
+
<option key={`select-option-${option[1]}`} value={option[1]}>{option[0]}</option>
|
85
|
+
))}
|
86
|
+
{this.props.children}
|
87
|
+
</select>
|
88
|
+
{this.props.hintBottom &&
|
89
|
+
<span className="form-text text-muted font-smoothing font-xs">
|
90
|
+
{this.props.hintBottom}
|
91
|
+
</span>
|
92
|
+
}
|
93
|
+
</div>
|
94
|
+
)
|
95
|
+
}
|
96
|
+
|
97
|
+
dataController() {
|
98
|
+
if ("data-controller" in this.props) {
|
99
|
+
return this.props["data-controller"]
|
100
|
+
} else if (this.props.select2) {
|
101
|
+
return "select2--default"
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
includeBlank() {
|
106
|
+
if (this.props.includeBlank || (this.props.placeholder && !this.props.multiple)) {
|
107
|
+
return true
|
108
|
+
} else {
|
109
|
+
return false
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
inputDefaultValue() {
|
114
|
+
if ("defaultValue" in this.props) {
|
115
|
+
return this.props.defaultValue
|
116
|
+
} else if (this.props.selected) {
|
117
|
+
return this.props.selected
|
118
|
+
} else if (this.props.model) {
|
119
|
+
if (!this.props.model[this.props.attribute])
|
120
|
+
throw new Error(`No attribute by that name: ${this.props.attribute}`)
|
121
|
+
|
122
|
+
return this.props.model[this.props.attribute]()
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
inputId() {
|
127
|
+
if ("id" in this.props) {
|
128
|
+
return this.props.id
|
129
|
+
} else if (this.props.model) {
|
130
|
+
return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
inputName() {
|
135
|
+
if ("name" in this.props) {
|
136
|
+
return this.props.name
|
137
|
+
} else if (this.props.model) {
|
138
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
label() {
|
143
|
+
if ("label" in this.props) {
|
144
|
+
return this.props.label
|
145
|
+
} else if (this.props.model) {
|
146
|
+
let attributeMethodName = inflection.camelize(this.props.attribute.replace(/_id$/, ""), true)
|
147
|
+
return this.props.model.modelClass().humanAttributeName(attributeMethodName)
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
labelClassName() {
|
152
|
+
let classNames = ["form-group-label"]
|
153
|
+
|
154
|
+
if (this.props.labelClassName)
|
155
|
+
classNames.push(this.props.labelClassName)
|
156
|
+
|
157
|
+
return classNames.join(" ")
|
158
|
+
}
|
159
|
+
|
160
|
+
wrapperClassName() {
|
161
|
+
let classNames = ["form-group", "component-bootstrap-select"]
|
162
|
+
|
163
|
+
if (this.props.wrapperClassName)
|
164
|
+
classNames.push(this.props.wrapperClassName)
|
165
|
+
|
166
|
+
return classNames.join(" ")
|
167
|
+
}
|
168
|
+
}
|
@@ -0,0 +1,203 @@
|
|
1
|
+
import MoneyInput from "./money-input"
|
2
|
+
import PropTypes from "prop-types"
|
3
|
+
import PropTypesExact from "prop-types-exact"
|
4
|
+
import React from "react"
|
5
|
+
|
6
|
+
const inflection = require("inflection")
|
7
|
+
|
8
|
+
export default class BootstrapStringInput extends React.Component {
|
9
|
+
static propTypes = PropTypesExact({
|
10
|
+
append: PropTypes.node,
|
11
|
+
attribute: PropTypes.string,
|
12
|
+
autoComplete: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
13
|
+
className: PropTypes.string,
|
14
|
+
currenciesCollection: PropTypes.array,
|
15
|
+
currencyName: PropTypes.string,
|
16
|
+
"data-controller": PropTypes.string,
|
17
|
+
defaultValue: PropTypes.node,
|
18
|
+
disabled: PropTypes.bool,
|
19
|
+
hint: PropTypes.node,
|
20
|
+
hintBottom: PropTypes.node,
|
21
|
+
id: PropTypes.string,
|
22
|
+
label: PropTypes.node,
|
23
|
+
labelClassName: PropTypes.string,
|
24
|
+
maxLength: PropTypes.number,
|
25
|
+
model: PropTypes.object,
|
26
|
+
name: PropTypes.string,
|
27
|
+
onChange: PropTypes.func,
|
28
|
+
onKeyUp: PropTypes.func,
|
29
|
+
placeholder: PropTypes.node,
|
30
|
+
rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
31
|
+
step: PropTypes.number,
|
32
|
+
small: PropTypes.bool,
|
33
|
+
type: PropTypes.string,
|
34
|
+
wrapperClassName: PropTypes.string
|
35
|
+
})
|
36
|
+
|
37
|
+
render() {
|
38
|
+
return (
|
39
|
+
<div className={this.wrapperClassName()} ref="wrapper">
|
40
|
+
{this.label() &&
|
41
|
+
<label className={this.labelClassName()} htmlFor={this.inputId()}>
|
42
|
+
{this.label()}
|
43
|
+
</label>
|
44
|
+
}
|
45
|
+
{this.props.hint &&
|
46
|
+
<span className="form-text text-muted font-smoothing font-xs">
|
47
|
+
{this.props.hint}
|
48
|
+
</span>
|
49
|
+
}
|
50
|
+
{this.inputType() == "textarea" &&
|
51
|
+
<textarea
|
52
|
+
className={this.inputClassName()}
|
53
|
+
data-controller={this.props["data-controller"]}
|
54
|
+
defaultValue={this.inputDefaultValue()}
|
55
|
+
id={this.inputId()}
|
56
|
+
maxLength={this.props.maxLength}
|
57
|
+
name={this.inputName()}
|
58
|
+
onChange={this.props.onChange}
|
59
|
+
onKeyUp={this.props.onKeyUp}
|
60
|
+
placeholder={this.props.placeholder}
|
61
|
+
ref="input"
|
62
|
+
rows={this.props.rows}
|
63
|
+
/>
|
64
|
+
}
|
65
|
+
{this.inputType() == "money" &&
|
66
|
+
<MoneyInput
|
67
|
+
attribute={this.props.attribute}
|
68
|
+
currenciesCollection={this.props.currenciesCollection}
|
69
|
+
currencyName={this.props.currencyName}
|
70
|
+
model={this.props.model}
|
71
|
+
name={this.props.name}
|
72
|
+
className={this.inputClassName()}
|
73
|
+
onChange={this.props.onChange}
|
74
|
+
placeholder={this.props.placeholder}
|
75
|
+
small={this.props.small}
|
76
|
+
ref="money"
|
77
|
+
/>
|
78
|
+
}
|
79
|
+
{this.inputType() != "textarea" && this.inputType() != "money" &&
|
80
|
+
<div className="input-group">
|
81
|
+
{this.props.prepend &&
|
82
|
+
<div className="input-group-prepend">
|
83
|
+
<span className="input-group-text">
|
84
|
+
{this.props.prepend}
|
85
|
+
</span>
|
86
|
+
</div>
|
87
|
+
}
|
88
|
+
<input
|
89
|
+
autoComplete={this.props.autoComplete}
|
90
|
+
className={this.inputClassName()}
|
91
|
+
data-controller={this.props["data-controller"]}
|
92
|
+
defaultValue={this.inputDefaultValue()}
|
93
|
+
disabled={this.props.disabled}
|
94
|
+
id={this.inputId()}
|
95
|
+
name={this.inputName()}
|
96
|
+
onChange={this.props.onChange}
|
97
|
+
onKeyUp={this.props.onKeyUp}
|
98
|
+
placeholder={this.props.placeholder}
|
99
|
+
ref="input"
|
100
|
+
step={this.props.step}
|
101
|
+
type={this.inputType()}
|
102
|
+
/>
|
103
|
+
{this.props.append &&
|
104
|
+
<div className="input-group-append">
|
105
|
+
<span className="input-group-text">
|
106
|
+
{this.props.append}
|
107
|
+
</span>
|
108
|
+
</div>
|
109
|
+
}
|
110
|
+
</div>
|
111
|
+
}
|
112
|
+
{this.props.hintBottom &&
|
113
|
+
<span className="form-text text-muted font-smoothing font-xs">
|
114
|
+
{this.props.hintBottom}
|
115
|
+
</span>
|
116
|
+
}
|
117
|
+
</div>
|
118
|
+
)
|
119
|
+
}
|
120
|
+
|
121
|
+
inputClassName() {
|
122
|
+
var classNames = ["form-control"]
|
123
|
+
|
124
|
+
if (this.props.className)
|
125
|
+
classNames.push(this.props.className)
|
126
|
+
|
127
|
+
return classNames.join(" ")
|
128
|
+
}
|
129
|
+
|
130
|
+
inputDefaultValue() {
|
131
|
+
if ("defaultValue" in this.props) {
|
132
|
+
return this.formatValue(this.props.defaultValue)
|
133
|
+
} else if (this.props.model) {
|
134
|
+
if (!this.props.model[this.props.attribute])
|
135
|
+
throw new Error(`No such attribute: ${this.props.model.modelClassData().name}#${this.props.attribute}`)
|
136
|
+
|
137
|
+
return this.formatValue(this.props.model[this.props.attribute]())
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
formatValue(value) {
|
142
|
+
// We need to use a certain format for datetime-local
|
143
|
+
if (this.inputType() == "datetime-local" && value instanceof Date) {
|
144
|
+
return I18n.strftime(value, "%Y-%m-%dT%H:%M:%S")
|
145
|
+
} else if (this.inputType() == "date" && value instanceof Date) {
|
146
|
+
return I18n.strftime(value, "%Y-%m-%d")
|
147
|
+
}
|
148
|
+
|
149
|
+
return value
|
150
|
+
}
|
151
|
+
|
152
|
+
inputId() {
|
153
|
+
if ("id" in this.props) {
|
154
|
+
return this.props.id
|
155
|
+
} else if (this.props.model) {
|
156
|
+
return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
inputName() {
|
161
|
+
if ("name" in this.props) {
|
162
|
+
return this.props.name
|
163
|
+
} else if (this.props.model) {
|
164
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
inputType() {
|
169
|
+
if (this.props.type) {
|
170
|
+
return this.props.type
|
171
|
+
} else {
|
172
|
+
return "text"
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
label() {
|
177
|
+
if (this.props.label === false) {
|
178
|
+
return null
|
179
|
+
} else if (this.props.label) {
|
180
|
+
return this.props.label
|
181
|
+
} else if (this.props.model) {
|
182
|
+
return this.props.model.modelClass().humanAttributeName(this.props.attribute)
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
labelClassName() {
|
187
|
+
var classNames = []
|
188
|
+
|
189
|
+
if (this.props.labelClassName)
|
190
|
+
classNames.push(this.props.labelClassName)
|
191
|
+
|
192
|
+
return classNames.join(" ")
|
193
|
+
}
|
194
|
+
|
195
|
+
wrapperClassName() {
|
196
|
+
var classNames = ["form-group", "component-bootstrap-string-input"]
|
197
|
+
|
198
|
+
if (this.props.wrapperClassName)
|
199
|
+
classNames.push(this.props.wrapperClassName)
|
200
|
+
|
201
|
+
return classNames.join(" ")
|
202
|
+
}
|
203
|
+
}
|