@kaspernj/api-maker 1.0.216 → 1.0.219

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.
Files changed (52) hide show
  1. package/package.json +2 -1
  2. package/src/base-model.mjs +1 -1
  3. package/src/bootstrap/attribute-row/basic-style.scss +9 -0
  4. package/src/bootstrap/attribute-row/index.jsx +84 -0
  5. package/src/bootstrap/attribute-rows.jsx +27 -0
  6. package/src/bootstrap/card.jsx +135 -0
  7. package/src/bootstrap/checkbox.jsx +79 -0
  8. package/src/bootstrap/checkboxes.jsx +122 -0
  9. package/src/bootstrap/index.js +0 -0
  10. package/src/bootstrap/input.jsx +160 -0
  11. package/src/bootstrap/invalid-feedback.jsx +31 -0
  12. package/src/bootstrap/live-table/model-row.jsx +150 -0
  13. package/src/bootstrap/live-table.jsx +399 -0
  14. package/src/bootstrap/paginate.jsx +153 -0
  15. package/src/bootstrap/radio-buttons.jsx +87 -0
  16. package/src/bootstrap/select.jsx +110 -0
  17. package/src/bootstrap/sort-link.jsx +102 -0
  18. package/src/collection-loader.jsx +7 -8
  19. package/src/inputs/auto-submit.mjs +37 -0
  20. package/src/inputs/checkbox.jsx +97 -0
  21. package/src/inputs/checkboxes.jsx +113 -0
  22. package/src/inputs/id-for-component.mjs +15 -0
  23. package/src/inputs/input-wrapper.jsx +170 -0
  24. package/src/inputs/input.jsx +235 -0
  25. package/src/inputs/money.jsx +177 -0
  26. package/src/inputs/name-for-component.mjs +15 -0
  27. package/src/inputs/select.jsx +87 -0
  28. package/src/model-class-require.mjs +1 -1
  29. package/src/model-name.mjs +6 -2
  30. package/src/params.mjs +7 -0
  31. package/src/super-admin/index-page/index.jsx +42 -0
  32. package/src/super-admin/index.jsx +46 -0
  33. package/src/super-admin/layout/header/index.jsx +60 -0
  34. package/src/super-admin/layout/header/style.scss +124 -0
  35. package/src/super-admin/layout/index.jsx +156 -0
  36. package/src/super-admin/layout/menu/index.jsx +116 -0
  37. package/src/super-admin/layout/menu/menu-content.jsx +55 -0
  38. package/src/super-admin/layout/menu/menu-item/index.jsx +27 -0
  39. package/src/super-admin/layout/menu/menu-item/style.scss +30 -0
  40. package/src/super-admin/layout/menu/style.scss +103 -0
  41. package/src/super-admin/layout/no-access.jsx +16 -0
  42. package/src/super-admin/layout/style.scss +25 -0
  43. package/src/super-admin/show-page.jsx +9 -0
  44. package/src/table/column-identifier.mjs +23 -0
  45. package/src/table/column-visible.mjs +7 -0
  46. package/src/table/model-row.jsx +182 -0
  47. package/src/table/select-calculator.mjs +48 -0
  48. package/src/table/style.scss +72 -0
  49. package/src/table/table-settings.js +175 -0
  50. package/src/table/table.jsx +498 -0
  51. package/src/table/variables.scss +11 -0
  52. 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 "@kaspernj/api-maker/src/models.mjs.erb"
3
+ import * as models from "./models.mjs.erb"
4
4
 
5
5
  export default (modelName) => {
6
6
  const requireName = inflection.camelize(modelName)
@@ -1,4 +1,5 @@
1
- import Config from "@kaspernj/api-maker/src/config.mjs"
1
+ import Config from "./config.mjs"
2
+ import inflection from "inflection"
2
3
 
3
4
  export default class ModelName {
4
5
  constructor(data) {
@@ -23,8 +24,11 @@ export default class ModelName {
23
24
  }
24
25
 
25
26
  const key = `activerecord.models.${this.data.modelClassData.i18nKey}.${countKey}`
27
+ let defaultModelName = this.data.modelClassData.name
26
28
 
27
- return Config.getI18n().t(key)
29
+ if (args?.count > 1) defaultModelName = inflection.pluralize(defaultModelName)
30
+
31
+ return Config.getI18n().t(key, {defaultValue: defaultModelName})
28
32
  }
29
33
 
30
34
  paramKey() {