@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.
Files changed (48) hide show
  1. package/package.json +5 -4
  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 +1 -1
  30. package/src/super-admin/index.jsx +11 -0
  31. package/src/super-admin/layout/header/index.jsx +60 -0
  32. package/src/super-admin/layout/header/style.scss +124 -0
  33. package/src/super-admin/layout/index.jsx +156 -0
  34. package/src/super-admin/layout/menu/index.jsx +116 -0
  35. package/src/super-admin/layout/menu/menu-content.jsx +106 -0
  36. package/src/super-admin/layout/menu/menu-item/index.jsx +27 -0
  37. package/src/super-admin/layout/menu/menu-item/style.scss +30 -0
  38. package/src/super-admin/layout/menu/style.scss +103 -0
  39. package/src/super-admin/layout/style.scss +25 -0
  40. package/src/table/column-identifier.mjs +23 -0
  41. package/src/table/column-visible.mjs +7 -0
  42. package/src/table/model-row.jsx +182 -0
  43. package/src/table/select-calculator.mjs +48 -0
  44. package/src/table/style.scss +72 -0
  45. package/src/table/table-settings.js +175 -0
  46. package/src/table/table.jsx +498 -0
  47. package/src/table/variables.scss +11 -0
  48. package/src/table/with-breakpoint.jsx +48 -0
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.214",
19
+ "version": "1.0.217",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -49,6 +49,7 @@
49
49
  "js-money": ">= 0.6.3",
50
50
  "numberable": ">= 1.0.0",
51
51
  "object-to-formdata": ">= 4.1.0",
52
+ "on-location-changed": ">= 1.0.7",
52
53
  "qs": ">= 6.9.3",
53
54
  "replaceall": ">= 0.1.6",
54
55
  "strftime": ">= 0.10.0",
@@ -67,13 +68,13 @@
67
68
  "@babel/eslint-parser": "^7.16.3",
68
69
  "@babel/preset-env": "^7.12.11",
69
70
  "@babel/preset-react": "^7.12.10",
70
- "babel-jest": "^28.0.3",
71
+ "babel-jest": "^29.0.1",
71
72
  "eslint": "^8.2.0",
72
73
  "eslint-find-rules": "^4.0.0",
73
- "eslint-plugin-jest": "^26.0.0",
74
+ "eslint-plugin-jest": "^27.0.1",
74
75
  "eslint-plugin-react": "^7.23.2",
75
76
  "i18n-on-steroids": "^1.0.2",
76
- "jest": "^28.0.3",
77
+ "jest": "^29.0.1",
77
78
  "jsdom": "^20.0.0"
78
79
  }
79
80
  }
@@ -1,7 +1,7 @@
1
1
  import AttributeNotLoadedError from "./attribute-not-loaded-error.mjs"
2
2
  import Collection from "./collection.mjs"
3
3
  import CommandsPool from "./commands-pool.mjs"
4
- import Config from "@kaspernj/api-maker/src/config.mjs"
4
+ import Config from "./config.mjs"
5
5
  import CustomError from "./custom-error.mjs"
6
6
  import {digg} from "diggerize"
7
7
  import FormDataObjectizer from "form-data-objectizer"
@@ -0,0 +1,9 @@
1
+ .component-api-maker-attribute-row {
2
+ .attribute-row-label {
3
+ font-weight: bold;
4
+ }
5
+
6
+ + .component-api-maker-attribute-row {
7
+ margin-top: 12px;
8
+ }
9
+ }
@@ -0,0 +1,84 @@
1
+ import classNames from "classnames"
2
+ import {digg} from "diggerize"
3
+ import MoneyFormatter from "../../money-formatter"
4
+ import PropTypes from "prop-types"
5
+ import React from "react"
6
+ import strftime from "strftime"
7
+
8
+ export default class ApiMakerBootstrapAttributeRow extends React.PureComponent {
9
+ static defaultProps = {
10
+ checkIfAttributeLoaded: false
11
+ }
12
+
13
+ static propTypes = {
14
+ attribute: PropTypes.string,
15
+ checkIfAttributeLoaded: PropTypes.bool.isRequired,
16
+ children: PropTypes.node,
17
+ identifier: PropTypes.string,
18
+ label: PropTypes.node,
19
+ model: PropTypes.object,
20
+ value: PropTypes.node
21
+ }
22
+
23
+ render () {
24
+ const {attribute, checkIfAttributeLoaded, children, className, identifier, label, model, value, ...restProps} = this.props
25
+
26
+ return (
27
+ <div
28
+ className={classNames(className, "component-api-maker-attribute-row")}
29
+ data-attribute={attribute}
30
+ data-identifier={identifier}
31
+ {...restProps}
32
+ >
33
+ <div className="attribute-row-label">
34
+ {this.label()}
35
+ </div>
36
+ <div className="attribute-row-value">
37
+ {this.value()}
38
+ </div>
39
+ </div>
40
+ )
41
+ }
42
+
43
+ label() {
44
+ const {attribute, label, model} = this.props
45
+
46
+ if ("label" in this.props) return label
47
+ if (attribute && model) return model.constructor.humanAttributeName(attribute)
48
+
49
+ throw new Error("Couldn't figure out label")
50
+ }
51
+
52
+ value() {
53
+ const {attribute, checkIfAttributeLoaded, children, model} = this.props
54
+
55
+ if (children) return children
56
+
57
+ if (attribute && !(attribute in model))
58
+ throw new Error(`Attribute not found: ${digg(model.modelClassData(), "name")}#${attribute}`)
59
+
60
+ if (attribute && checkIfAttributeLoaded && !model.isAttributeLoaded(attribute))
61
+ return null
62
+
63
+ if (attribute && model) {
64
+ const value = model[attribute]()
65
+
66
+ return this.valueContent(value)
67
+ }
68
+ }
69
+
70
+ valueContent(value) {
71
+ if (value instanceof Date) {
72
+ return strftime("%Y-%m-%d %H:%M", value)
73
+ } else if (typeof value === "boolean") {
74
+ if (value)
75
+ return I18n.t("js.shared.yes")
76
+
77
+ return I18n.t("js.shared.no")
78
+ } else if (MoneyFormatter.isMoney(value)) {
79
+ return MoneyFormatter.format(value)
80
+ } else {
81
+ return value
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,27 @@
1
+ import AttributeRow from "./attribute-row"
2
+ import {digs} from "diggerize"
3
+ import PropTypes from "prop-types"
4
+ import propTypesExact from "prop-types-exact"
5
+ import React from "react"
6
+
7
+ export default class ApiMakerBootstrapAttributeRows extends React.PureComponent {
8
+ static defaultProps = {
9
+ checkIfAttributeLoaded: false
10
+ }
11
+
12
+ static propTypes = propTypesExact({
13
+ attributes: PropTypes.array.isRequired,
14
+ checkIfAttributeLoaded: PropTypes.bool.isRequired,
15
+ model: PropTypes.object.isRequired
16
+ })
17
+
18
+ classObject = this.props.model.modelClass()
19
+
20
+ render () {
21
+ const {attributes, checkIfAttributeLoaded, model} = digs(this.props, "attributes", "checkIfAttributeLoaded", "model")
22
+
23
+ return attributes.map((attribute) =>
24
+ <AttributeRow attribute={attribute} checkIfAttributeLoaded={checkIfAttributeLoaded} key={`attribute-${attribute}`} model={model} />
25
+ )
26
+ }
27
+ }
@@ -0,0 +1,135 @@
1
+ import {digs} from "diggerize"
2
+ import PropTypes from "prop-types"
3
+ import React from "react"
4
+
5
+ export default class ApiMakerBootstrapCard extends React.PureComponent {
6
+ static defaultProps = {
7
+ defaultExpanded: true,
8
+ expandable: false,
9
+ expandableHide: false,
10
+ responsiveTable: true,
11
+ table: false
12
+ }
13
+
14
+ static propTypes = {
15
+ className: PropTypes.string,
16
+ children: PropTypes.node,
17
+ controls: PropTypes.node,
18
+ defaultExpanded: PropTypes.bool.isRequired,
19
+ expandable: PropTypes.bool.isRequired,
20
+ expandableHide: PropTypes.bool.isRequired,
21
+ header: PropTypes.node,
22
+ striped: PropTypes.bool,
23
+ responsiveTable: PropTypes.bool.isRequired,
24
+ table: PropTypes.bool.isRequired
25
+ }
26
+
27
+ constructor (props) {
28
+ super(props)
29
+ this.state = {
30
+ expanded: props.defaultExpanded
31
+ }
32
+ }
33
+
34
+ render () {
35
+ const {
36
+ children,
37
+ className,
38
+ controls,
39
+ defaultExpanded,
40
+ expandable,
41
+ expandableHide,
42
+ header,
43
+ responsiveTable,
44
+ striped,
45
+ table,
46
+ ...restProps
47
+ } = this.props
48
+ const {expanded} = digs(this.state, "expanded")
49
+
50
+ return (
51
+ <div className={this.classNames()} ref="card" {...restProps}>
52
+ {(controls || expandable || header) &&
53
+ <div className={`card-header d-flex ${!expanded && "border-bottom-0"}`}>
54
+ <div style={{alignSelf: "center", marginRight: "auto"}}>
55
+ {header}
56
+ </div>
57
+ {(controls || expandable) &&
58
+ <div style={{alignSelf: "center"}}>
59
+ {controls}
60
+ {expandable && expanded &&
61
+ <a className="collapse-card-button text-muted" href="#" onClick={this.onCollapseClicked}>
62
+ <i className="la la-angle-up" />
63
+ </a>
64
+ }
65
+ {expandable && !expanded &&
66
+ <a className="expand-card-button text-muted" href="#" onClick={this.onExpandClicked}>
67
+ <i className="la la-angle-down" />
68
+ </a>
69
+ }
70
+ </div>
71
+ }
72
+ </div>
73
+ }
74
+ {(expanded || expandableHide) &&
75
+ <div className={this.bodyClassNames()}>
76
+ {table &&
77
+ <table className={this.tableClassNames()}>
78
+ {children}
79
+ </table>
80
+ }
81
+ {!table && children}
82
+ </div>
83
+ }
84
+ </div>
85
+ )
86
+ }
87
+
88
+ classNames () {
89
+ const classNames = ["component-bootstrap-card", "card", "card-default"]
90
+
91
+ if (this.props.className)
92
+ classNames.push(this.props.className)
93
+
94
+ return classNames.join(" ")
95
+ }
96
+
97
+ bodyClassNames () {
98
+ const {expandableHide, responsiveTable, table} = digs(this.props, "expandableHide", "responsiveTable", "table")
99
+ const {expanded} = digs(this.state, "expanded")
100
+ const classNames = ["card-body"]
101
+
102
+ if (!expanded && expandableHide) {
103
+ classNames.push("d-none")
104
+ }
105
+
106
+ if (table) {
107
+ if (responsiveTable){
108
+ classNames.push("table-responsive")
109
+ }
110
+
111
+ classNames.push("p-0")
112
+ }
113
+
114
+ return classNames.join(" ")
115
+ }
116
+
117
+ onCollapseClicked = (e) => {
118
+ e.preventDefault()
119
+ this.setState({expanded: false})
120
+ }
121
+
122
+ onExpandClicked = (e) => {
123
+ e.preventDefault()
124
+ this.setState({expanded: true})
125
+ }
126
+
127
+ tableClassNames () {
128
+ const classNames = ["table", "table-hover", "mb-0", "w-100"]
129
+
130
+ if (this.props.striped)
131
+ classNames.push("table-striped")
132
+
133
+ return classNames.join(" ")
134
+ }
135
+ }
@@ -0,0 +1,79 @@
1
+ import {Checkbox} from "../inputs/checkbox"
2
+ import classNames from "classnames"
3
+ import {digs} from "diggerize"
4
+ import inputWrapper from "../inputs/input-wrapper"
5
+ import InvalidFeedback from "./invalid-feedback"
6
+ import PropTypes from "prop-types"
7
+ import React from "react"
8
+
9
+ class ApiMakerBootstrapCheckbox extends React.PureComponent {
10
+ static defaultProps = {
11
+ defaultValue: 1,
12
+ zeroInput: true
13
+ }
14
+
15
+ static propTypes = {
16
+ attribute: PropTypes.string,
17
+ className: PropTypes.string,
18
+ defaultChecked: PropTypes.bool,
19
+ defaultValue: PropTypes.node,
20
+ hint: PropTypes.node,
21
+ id: PropTypes.string,
22
+ label: PropTypes.node,
23
+ labelClassName: PropTypes.string,
24
+ model: PropTypes.object,
25
+ name: PropTypes.string,
26
+ onChange: PropTypes.func,
27
+ wrapperClassName: PropTypes.string,
28
+ zeroInput: PropTypes.bool
29
+ }
30
+
31
+ render () {
32
+ const {className, hint, id, inputProps, inputRef, label, labelClassName, wrapperClassName, wrapperOpts, ...restProps} = this.props
33
+ const {errors} = digs(wrapperOpts, "errors")
34
+
35
+ return (
36
+ <div className={this.wrapperClassName()}>
37
+ <div className="form-check">
38
+ <Checkbox
39
+ className={classNames("form-check-input", className, {"is-invalid": errors.length > 0})}
40
+ inputProps={inputProps}
41
+ wrapperOpts={wrapperOpts}
42
+ {...restProps}
43
+ />
44
+ {wrapperOpts.label &&
45
+ <label className={this.labelClassName()} htmlFor={inputProps.id}>
46
+ {wrapperOpts.label}
47
+ </label>
48
+ }
49
+ {hint &&
50
+ <p className="text-muted">
51
+ {hint}
52
+ </p>
53
+ }
54
+ {errors.length > 0 && <InvalidFeedback errors={errors} />}
55
+ </div>
56
+ </div>
57
+ )
58
+ }
59
+
60
+ labelClassName () {
61
+ const classNames = ["form-check-label"]
62
+
63
+ if (this.props.labelClassName)
64
+ classNames.push(this.props.labelClassName)
65
+
66
+ return classNames.join(" ")
67
+ }
68
+
69
+ wrapperClassName () {
70
+ const classNames = ["component-bootstrap-checkbox", "form-group"]
71
+
72
+ if (this.props.wrapperClassName)
73
+ classNames.push(this.props.wrapperClassName)
74
+
75
+ return classNames.join(" ")
76
+ }
77
+ }
78
+
79
+ export default inputWrapper(ApiMakerBootstrapCheckbox, {type: "checkbox"})
@@ -0,0 +1,122 @@
1
+ import {digs} from "diggerize"
2
+ import inputWrapper from "../inputs/input-wrapper"
3
+ import inflection from "inflection"
4
+ import InvalidFeedback from "./invalid-feedback"
5
+ import PropTypes from "prop-types"
6
+ import propTypesExact from "prop-types-exact"
7
+ import React from "react"
8
+
9
+ class ApiMakerBootstrapCheckboxes extends React.PureComponent {
10
+ static propTypes = propTypesExact({
11
+ attribute: PropTypes.string,
12
+ defaultValue: PropTypes.array,
13
+ inputProps: PropTypes.object.isRequired,
14
+ label: PropTypes.string,
15
+ labelClassName: PropTypes.string,
16
+ model: PropTypes.object,
17
+ name: PropTypes.string,
18
+ onChange: PropTypes.func,
19
+ options: PropTypes.array.isRequired,
20
+ wrapperOpts: PropTypes.object.isRequired
21
+ })
22
+
23
+ render () {
24
+ return (
25
+ <div className="component-bootstrap-checkboxes form-group">
26
+ <label className={this.labelClassName()}>
27
+ {this.props.wrapperOpts.label}
28
+ </label>
29
+
30
+ <input name={this.inputName()} ref={this.props.inputProps.ref} type="hidden" value="" />
31
+ {this.props.options.map((option, index) => this.optionElement(option, index))}
32
+ </div>
33
+ )
34
+ }
35
+
36
+ inputDefaultValue () {
37
+ const {attribute, defaultValue, model} = this.props
38
+
39
+ if (defaultValue) {
40
+ return defaultValue
41
+ } else if (attribute && model) {
42
+ if (!model[attribute])
43
+ throw `No such attribute: ${attribute}`
44
+
45
+ return this.props.model[attribute]()
46
+ }
47
+ }
48
+
49
+ inputCheckboxClassName () {
50
+ const classNames = []
51
+
52
+ if (this.props.wrapperOpts.errors.length > 0) classNames.push("is-invalid")
53
+
54
+ return classNames.join(" ")
55
+ }
56
+
57
+ inputName () {
58
+ if (this.props.name) {
59
+ return `${this.props.name}[]`
60
+ } else if (this.props.model) {
61
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
62
+ }
63
+ }
64
+
65
+ isDefaultSelected (option) {
66
+ let defaultValue = this.inputDefaultValue()
67
+
68
+ if (!defaultValue) return false
69
+
70
+ if (defaultValue.constructor === Array) {
71
+ return defaultValue.includes(option)
72
+ } else {
73
+ return defaultValue == option
74
+ }
75
+ }
76
+
77
+ labelClassName () {
78
+ const classNames = []
79
+
80
+ if (this.props.labelClassName) classNames.push(this.props.labelClassName)
81
+
82
+ return classNames.join(" ")
83
+ }
84
+
85
+ generatedId () {
86
+ if (!this.generatedIdValue)
87
+ this.generatedIdValue = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
88
+
89
+ return this.generatedIdValue
90
+ }
91
+
92
+ optionElement (option, index) {
93
+ const {onChange, options, wrapperOpts} = this.props
94
+ const {errors} = digs(wrapperOpts, "errors")
95
+ const id = `${this.generatedId()}-${index}`
96
+
97
+ return (
98
+ <div className="checkboxes-option" key={`option-${option[1]}`}>
99
+ <input
100
+ className={this.inputCheckboxClassName()}
101
+ data-option-value={option[1]}
102
+ defaultChecked={this.isDefaultSelected(option[1])}
103
+ id={id}
104
+ name={this.inputName()}
105
+ onChange={onChange}
106
+ type="checkbox"
107
+ value={option[1]}
108
+ />
109
+
110
+ <label className="ml-1" htmlFor={id}>
111
+ {option[0]}
112
+ </label>
113
+
114
+ {(index + 1) == options.length && errors.length > 0 &&
115
+ <InvalidFeedback errors={errors} />
116
+ }
117
+ </div>
118
+ )
119
+ }
120
+ }
121
+
122
+ export default inputWrapper(ApiMakerBootstrapCheckboxes)
File without changes
@@ -0,0 +1,160 @@
1
+ import {digs} from "diggerize"
2
+ import inputWrapper from "../inputs/input-wrapper"
3
+ import {Input} from "../inputs/input"
4
+ import InvalidFeedback from "./invalid-feedback"
5
+ import Money from "../inputs/money"
6
+ import PropTypes from "prop-types"
7
+ import React from "react"
8
+
9
+ class ApiMakerBootstrapInput extends React.PureComponent {
10
+ static propTypes = {
11
+ append: PropTypes.node,
12
+ appendText: PropTypes.node,
13
+ attribute: PropTypes.string,
14
+ className: PropTypes.string,
15
+ currenciesCollection: PropTypes.array,
16
+ currencyName: PropTypes.string,
17
+ hint: PropTypes.node,
18
+ hintBottom: PropTypes.node,
19
+ id: PropTypes.string,
20
+ label: PropTypes.node,
21
+ labelClassName: PropTypes.string,
22
+ model: PropTypes.object,
23
+ name: PropTypes.string,
24
+ placeholder: PropTypes.node,
25
+ prepend: PropTypes.node,
26
+ prependText: PropTypes.node,
27
+ small: PropTypes.bool,
28
+ type: PropTypes.string,
29
+ wrapperClassName: PropTypes.string
30
+ }
31
+
32
+ render () {
33
+ const {
34
+ append,
35
+ appendText,
36
+ attribute,
37
+ className,
38
+ currenciesCollection,
39
+ currencyName,
40
+ hint,
41
+ hintBottom,
42
+ id,
43
+ inputClassName,
44
+ inputProps,
45
+ inputRef,
46
+ label,
47
+ labelClassName,
48
+ model,
49
+ name,
50
+ prepend,
51
+ prependText,
52
+ type,
53
+ wrapperClassName,
54
+ wrapperOpts,
55
+ ...restProps
56
+ } = this.props
57
+ const {ref, ...forwardedInputProps} = inputProps
58
+ const {errors} = digs(wrapperOpts, "errors")
59
+
60
+ return (
61
+ <div className={this.wrapperClassName()} ref="wrapper">
62
+ {wrapperOpts.label &&
63
+ <label className={this.labelClassName()} htmlFor={inputProps.id}>
64
+ {wrapperOpts.label}
65
+ </label>
66
+ }
67
+ {hint &&
68
+ <span className="form-text text-muted font-smoothing font-xs">
69
+ {hint}
70
+ </span>
71
+ }
72
+ {type == "money" &&
73
+ <Money
74
+ attribute={attribute}
75
+ currenciesCollection={currenciesCollection}
76
+ currencyName={currencyName}
77
+ model={model}
78
+ name={inputProps.name}
79
+ className={this.inputClassName()}
80
+ onChange={this.props.onChange}
81
+ placeholder={this.props.placeholder}
82
+ small={this.props.small}
83
+ ref="money"
84
+ />
85
+ }
86
+ {type != "money" &&
87
+ <div className="input-group">
88
+ {(prepend || prependText) &&
89
+ <div className="input-group-prepend">
90
+ {prependText &&
91
+ <span className="input-group-text">
92
+ {prependText}
93
+ </span>
94
+ }
95
+ {prepend}
96
+ </div>
97
+ }
98
+ <Input
99
+ attribute={attribute}
100
+ className={this.inputClassName()}
101
+ inputProps={inputProps}
102
+ model={model}
103
+ wrapperOpts={wrapperOpts}
104
+ {...forwardedInputProps}
105
+ {...restProps}
106
+ />
107
+ {(append || appendText) &&
108
+ <div className="input-group-append">
109
+ {appendText &&
110
+ <span className="input-group-text">
111
+ {appendText}
112
+ </span>
113
+ }
114
+ {append}
115
+ </div>
116
+ }
117
+ {errors.length > 0 && <InvalidFeedback errors={errors} />}
118
+ </div>
119
+ }
120
+ {hintBottom &&
121
+ <span className="form-text text-muted font-smoothing font-xs">
122
+ {hintBottom}
123
+ </span>
124
+ }
125
+ </div>
126
+ )
127
+ }
128
+
129
+ inputClassName () {
130
+ const classNames = ["form-control"]
131
+
132
+ if (this.props.className)
133
+ classNames.push(this.props.className)
134
+
135
+ if (this.props.wrapperOpts.errors.length > 0)
136
+ classNames.push("is-invalid")
137
+
138
+ return classNames.join(" ")
139
+ }
140
+
141
+ labelClassName () {
142
+ const classNames = []
143
+
144
+ if (this.props.labelClassName)
145
+ classNames.push(this.props.labelClassName)
146
+
147
+ return classNames.join(" ")
148
+ }
149
+
150
+ wrapperClassName () {
151
+ const classNames = ["form-group", "component-bootstrap-string-input"]
152
+
153
+ if (this.props.wrapperClassName)
154
+ classNames.push(this.props.wrapperClassName)
155
+
156
+ return classNames.join(" ")
157
+ }
158
+ }
159
+
160
+ export default inputWrapper(ApiMakerBootstrapInput)
@@ -0,0 +1,31 @@
1
+ import {digs} from "diggerize"
2
+ import PropTypes from "prop-types"
3
+ import propTypesExact from "prop-types-exact"
4
+ import React from "react"
5
+
6
+ export default class ApiMakerBootstrapInvalidFeedback extends React.PureComponent {
7
+ static propTypes = propTypesExact({
8
+ errors: PropTypes.array.isRequired
9
+ })
10
+
11
+ render () {
12
+ return (
13
+ <div className="invalid-feedback">
14
+ {this.errorMessages().join(". ")}
15
+ </div>
16
+ )
17
+ }
18
+
19
+ errorMessages () {
20
+ const {errors} = digs(this.props, "errors")
21
+ const errorMessages = []
22
+
23
+ for (const error of errors) {
24
+ for (const errorMessage of error.getErrorMessages()) {
25
+ errorMessages.push(errorMessage)
26
+ }
27
+ }
28
+
29
+ return errorMessages
30
+ }
31
+ }