@kaspernj/api-maker 1.0.412 → 1.0.413
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 +3 -1
- package/src/super-admin/layout/header/index.jsx +3 -3
- package/src/table/column-content.jsx +115 -0
- package/src/table/model-callback-args.mjs +10 -0
- package/src/table/model-row.jsx +23 -114
- package/src/table/settings/download-action.jsx +66 -0
- package/src/table/settings/index.jsx +2 -0
- package/src/table/table.jsx +4 -4
- package/src/icon.jsx +0 -51
- package/src/icons/bars-solid.svg +0 -1
- package/src/icons/gear-solid.svg +0 -1
- package/src/icons/magnifying-glass-solid.svg +0 -1
- package/src/icons/pen-solid.svg +0 -1
- package/src/icons/xmark-solid.svg +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaspernj/api-maker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.413",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"debounce": ">= 2.0.0",
|
|
25
25
|
"diggerize": ">= 1.0.5",
|
|
26
26
|
"epic-locks": ">= 1.0.2",
|
|
27
|
+
"file-saver": "^2.0.5",
|
|
27
28
|
"form-data-objectizer": ">= 1.0.0",
|
|
28
29
|
"form-serialize": ">= 0.7.2",
|
|
29
30
|
"format-number": ">= 3.0.0",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"object-to-formdata": ">= 4.1.0",
|
|
35
36
|
"on-location-changed": ">= 1.0.13",
|
|
36
37
|
"qs": ">= 6.9.3",
|
|
38
|
+
"react-native-vector-icons": "^10.2.0",
|
|
37
39
|
"replaceall": ">= 0.1.6",
|
|
38
40
|
"set-state-compare": "^1.0.49",
|
|
39
41
|
"spark-md5": "^3.0.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./style"
|
|
2
2
|
import BaseComponent from "../../../base-component"
|
|
3
|
-
import
|
|
3
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome"
|
|
4
4
|
import {memo, useRef} from "react"
|
|
5
5
|
import PropTypes from "prop-types"
|
|
6
6
|
import PropTypesExact from "prop-types-exact"
|
|
@@ -108,11 +108,11 @@ export default memo(shapeComponent(class ApiMakerSuperAdminLayoutHeader extends
|
|
|
108
108
|
<View dataSet={{class: "burger-menu-container"}}>
|
|
109
109
|
{actions &&
|
|
110
110
|
<Pressable dataSet={{class: "actions-link"}} onPress={this.tt.onGearsClicked} style={{marginRight: 8, fontSize: 22}}>
|
|
111
|
-
<
|
|
111
|
+
<FontAwesomeIcon name="gear-solid" size={20} />
|
|
112
112
|
</Pressable>
|
|
113
113
|
}
|
|
114
114
|
<Pressable dataSet={{class: "burger-menu-link"}} onPress={onTriggerMenu}>
|
|
115
|
-
<
|
|
115
|
+
<FontAwesomeIcon icon="bars" size={20} />
|
|
116
116
|
</Pressable>
|
|
117
117
|
</View>
|
|
118
118
|
</View>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {digs} from "diggerize"
|
|
2
|
+
import * as inflection from "inflection"
|
|
3
|
+
import modelCallbackArgs from "./model-callback-args.mjs"
|
|
4
|
+
import MoneyFormatter from "../money-formatter"
|
|
5
|
+
import {Text} from "react-native"
|
|
6
|
+
|
|
7
|
+
export default class ApiMakerTableColumnContent {
|
|
8
|
+
constructor({column, mode = "react-native", model, table}) {
|
|
9
|
+
this.column = column
|
|
10
|
+
this.mode = mode
|
|
11
|
+
this.model = model
|
|
12
|
+
this.modelCallbackArgs = modelCallbackArgs(table, model)
|
|
13
|
+
this.table = table
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
columnContentFromContentArg(column, _model) {
|
|
17
|
+
const value = column.content(this.modelCallbackArgs)
|
|
18
|
+
|
|
19
|
+
return this.presentColumnValue(value)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
columnsContentFromAttributeAndPath() {
|
|
23
|
+
const {attribute: attributeName} = digs(this.column, "attribute")
|
|
24
|
+
const attributeNameUnderscore = inflection.underscore(attributeName)
|
|
25
|
+
const path = this.column.path || []
|
|
26
|
+
let value
|
|
27
|
+
let currentModel = this.model
|
|
28
|
+
|
|
29
|
+
if (path.length > 0) {
|
|
30
|
+
for (const pathPart of path) {
|
|
31
|
+
currentModel = currentModel[pathPart]()
|
|
32
|
+
if (!currentModel) return
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!(attributeName in currentModel)) {
|
|
37
|
+
throw new Error(`${currentModel.constructor.modelName().human()} doesn't respond to ${attributeName}`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (currentModel.isAttributeLoaded(attributeName)) {
|
|
41
|
+
value = currentModel[attributeName]()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const attribute = currentModel.constructor.attributes().find((attribute) => attribute.name() == attributeNameUnderscore)
|
|
45
|
+
const modelColumn = attribute?.getColumn()
|
|
46
|
+
|
|
47
|
+
if (modelColumn?.getType() == "date" && value) {
|
|
48
|
+
const contentText = this.presentDateTime({apiMakerType: "date", value})
|
|
49
|
+
|
|
50
|
+
if (this.mode == "html") {
|
|
51
|
+
return contentText
|
|
52
|
+
} else {
|
|
53
|
+
return (
|
|
54
|
+
<Text>{contentText}</Text>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return this.presentColumnValue(value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
content() {
|
|
63
|
+
const {column, model} = digs(this, "column", "model")
|
|
64
|
+
|
|
65
|
+
if (column.content) {
|
|
66
|
+
return this.columnContentFromContentArg(column, model)
|
|
67
|
+
} else if (!column.content && column.attribute) {
|
|
68
|
+
return this.columnsContentFromAttributeAndPath(column, model)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
presentColumnValue(value) {
|
|
73
|
+
let contentText
|
|
74
|
+
|
|
75
|
+
if (value instanceof Date) {
|
|
76
|
+
contentText = this.presentDateTime({value})
|
|
77
|
+
} else if (MoneyFormatter.isMoney(value)) {
|
|
78
|
+
contentText = MoneyFormatter.format(value)
|
|
79
|
+
} else if (typeof value == "boolean") {
|
|
80
|
+
if (value) {
|
|
81
|
+
contentText = I18n.t("js.shared.yes", {defaultValue: "Yes"})
|
|
82
|
+
} else {
|
|
83
|
+
contentText = I18n.t("js.shared.no", {defaultValue: "No"})
|
|
84
|
+
}
|
|
85
|
+
} else if (Array.isArray(value)) {
|
|
86
|
+
contentText = value
|
|
87
|
+
.map((valuePart) => this.presentColumnValue(valuePart))
|
|
88
|
+
.filter((valuePart) => Boolean(valuePart))
|
|
89
|
+
.join(", ")
|
|
90
|
+
|
|
91
|
+
} else if (typeof value == "string") {
|
|
92
|
+
contentText = value
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.mode == "html") {
|
|
96
|
+
return contentText
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return <Text>{contentText}</Text>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
presentDateTime({apiMakerType, value}) {
|
|
103
|
+
if (!apiMakerType || apiMakerType == "time") {
|
|
104
|
+
const dateTimeFormatName = this.table.props.defaultDateTimeFormatName || "time.formats.default"
|
|
105
|
+
|
|
106
|
+
return I18n.l(dateTimeFormatName, value)
|
|
107
|
+
} else if (apiMakerType == "date") {
|
|
108
|
+
const dateFormatName = this.table.props.defaultDateFormatName || "date.formats.default"
|
|
109
|
+
|
|
110
|
+
return I18n.l(dateFormatName, value)
|
|
111
|
+
} else {
|
|
112
|
+
throw new Error(`Unhandled type: ${apiMakerType}`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as inflection from "inflection"
|
|
2
|
+
|
|
3
|
+
export default function modelCallbackArgs(table, model) {
|
|
4
|
+
const modelArgName = inflection.camelize(table.props.modelClass.modelClassData().name, true)
|
|
5
|
+
const modelCallbackArgs = {model}
|
|
6
|
+
|
|
7
|
+
modelCallbackArgs[modelArgName] = model
|
|
8
|
+
|
|
9
|
+
return modelCallbackArgs
|
|
10
|
+
}
|
package/src/table/model-row.jsx
CHANGED
|
@@ -2,13 +2,13 @@ import {Pressable, Text, View} from "react-native"
|
|
|
2
2
|
import BaseComponent from "../base-component"
|
|
3
3
|
import classNames from "classnames"
|
|
4
4
|
import Column from "./components/column"
|
|
5
|
+
import ColumnContent from "./column-content"
|
|
5
6
|
import columnIdentifier from "./column-identifier.mjs"
|
|
6
7
|
import columnVisible from "./column-visible.mjs"
|
|
7
|
-
import
|
|
8
|
-
import Icon from "../icon"
|
|
8
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome"
|
|
9
9
|
import * as inflection from "inflection"
|
|
10
|
+
import modelCallbackArgs from "./model-callback-args.mjs"
|
|
10
11
|
import Link from "../link"
|
|
11
|
-
import MoneyFormatter from "../money-formatter"
|
|
12
12
|
import PropTypes from "prop-types"
|
|
13
13
|
import propTypesExact from "prop-types-exact"
|
|
14
14
|
import Row from "./components/row"
|
|
@@ -24,20 +24,20 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
24
24
|
index: PropTypes.number.isRequired,
|
|
25
25
|
isSmallScreen: PropTypes.bool.isRequired,
|
|
26
26
|
model: PropTypes.object.isRequired,
|
|
27
|
-
|
|
27
|
+
table: PropTypes.object.isRequired,
|
|
28
28
|
preparedColumns: PropTypes.array,
|
|
29
29
|
tableSettingFullCacheKey: PropTypes.string.isRequired
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
render() {
|
|
33
|
-
const {index,
|
|
34
|
-
const {modelClass, workplace} =
|
|
35
|
-
const {actionsContent, destroyEnabled, editModelPath, viewModelPath} =
|
|
36
|
-
const {columns, currentWorkplace} =
|
|
37
|
-
const {styleForColumn, styleForRow} =
|
|
33
|
+
const {index, table, model} = this.p
|
|
34
|
+
const {modelClass, workplace} = table.p
|
|
35
|
+
const {actionsContent, destroyEnabled, editModelPath, viewModelPath} = table.props
|
|
36
|
+
const {columns, currentWorkplace} = table.state
|
|
37
|
+
const {styleForColumn, styleForRow} = table.tt
|
|
38
38
|
const even = index % 2 == 0
|
|
39
39
|
|
|
40
|
-
this.modelCallbackArgs =
|
|
40
|
+
this.modelCallbackArgs = modelCallbackArgs(table, model) // 'model' can change so this needs to be re-cached for every render
|
|
41
41
|
|
|
42
42
|
let editPath, viewPath
|
|
43
43
|
|
|
@@ -65,20 +65,20 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
65
65
|
}
|
|
66
66
|
{columns && this.columnsContentFromColumns(model, even)}
|
|
67
67
|
<Column dataSet={{class: "actions-column"}} style={styleForColumn({even, style: {}, type: "actions"})}>
|
|
68
|
-
{actionsContent && actionsContent(this.modelCallbackArgs)}
|
|
68
|
+
{actionsContent && actionsContent(this.tt.modelCallbackArgs)}
|
|
69
69
|
{viewPath &&
|
|
70
|
-
<Link dataSet={{class: "view-button"}} to={viewPath}>
|
|
71
|
-
<
|
|
70
|
+
<Link dataSet={{class: "view-button"}} style={{marginLeft: 2, marginRight: 2}} to={viewPath}>
|
|
71
|
+
<FontAwesomeIcon name="search" size={18} />
|
|
72
72
|
</Link>
|
|
73
73
|
}
|
|
74
74
|
{editPath &&
|
|
75
|
-
<Link dataSet={{class: "edit-button"}} to={editPath}>
|
|
76
|
-
<
|
|
75
|
+
<Link dataSet={{class: "edit-button"}} style={{marginLeft: 2, marginRight: 2}} to={editPath}>
|
|
76
|
+
<FontAwesomeIcon name="pencil" size={20} />
|
|
77
77
|
</Link>
|
|
78
78
|
}
|
|
79
79
|
{destroyEnabled && model.can("destroy") &&
|
|
80
|
-
<Pressable dataSet={{class: "destroy-button"}} onPress={this.tt.onDestroyClicked}>
|
|
81
|
-
<
|
|
80
|
+
<Pressable dataSet={{class: "destroy-button"}} style={{marginLeft: 2, marginRight: 2}} onPress={this.tt.onDestroyClicked}>
|
|
81
|
+
<FontAwesomeIcon name="remove" size={22} />
|
|
82
82
|
</Pressable>
|
|
83
83
|
}
|
|
84
84
|
</Column>
|
|
@@ -96,7 +96,7 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
columnsContentFromColumns(model, even) {
|
|
99
|
-
const {isSmallScreen,
|
|
99
|
+
const {isSmallScreen, table, preparedColumns} = this.p
|
|
100
100
|
|
|
101
101
|
return preparedColumns?.map(({column, tableSettingColumn, width}, columnIndex) => columnVisible(column, tableSettingColumn) &&
|
|
102
102
|
<Column
|
|
@@ -105,74 +105,25 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
105
105
|
identifier: columnIdentifier(column)
|
|
106
106
|
}}
|
|
107
107
|
key={columnIdentifier(column)}
|
|
108
|
-
style={
|
|
109
|
-
{...
|
|
108
|
+
style={table.styleForColumn({column, columnIndex, even, style: {width: `${width}%`}})}
|
|
109
|
+
{...table.columnProps(column)}
|
|
110
110
|
>
|
|
111
111
|
{isSmallScreen &&
|
|
112
112
|
<View dataSet={{class: "table--column-label"}}>
|
|
113
113
|
<Text>
|
|
114
|
-
{
|
|
114
|
+
{table.headerLabelForColumn(column)}
|
|
115
115
|
</Text>
|
|
116
116
|
</View>
|
|
117
117
|
}
|
|
118
118
|
<View dataSet={{class: "table--column-value"}}>
|
|
119
|
-
{
|
|
120
|
-
{!column.content && column.attribute && this.columnsContentFromAttributeAndPath(column, model)}
|
|
119
|
+
{new ColumnContent({column, model, table}).content()}
|
|
121
120
|
</View>
|
|
122
121
|
</Column>
|
|
123
122
|
)
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
columnContentFromContentArg (column, _model) {
|
|
127
|
-
const value = column.content(this.modelCallbackArgs)
|
|
128
|
-
|
|
129
|
-
return this.presentColumnValue(value)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
columnsContentFromAttributeAndPath (column, model) {
|
|
133
|
-
const {attribute: attributeName} = digs(column, "attribute")
|
|
134
|
-
const attributeNameUnderscore = inflection.underscore(attributeName)
|
|
135
|
-
const path = column.path || []
|
|
136
|
-
let value
|
|
137
|
-
let currentModel = model
|
|
138
|
-
|
|
139
|
-
if (path.length > 0) {
|
|
140
|
-
for (const pathPart of path) {
|
|
141
|
-
currentModel = currentModel[pathPart]()
|
|
142
|
-
if (!currentModel) return
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!(attributeName in currentModel)) {
|
|
147
|
-
throw new Error(`${currentModel.constructor.modelName().human()} doesn't respond to ${attributeName}`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (currentModel.isAttributeLoaded(attributeName)) value = currentModel[attributeName]()
|
|
151
|
-
|
|
152
|
-
const attribute = currentModel.constructor.attributes().find((attribute) => attribute.name() == attributeNameUnderscore)
|
|
153
|
-
const modelColumn = attribute?.getColumn()
|
|
154
|
-
|
|
155
|
-
if (modelColumn?.getType() == "date" && value) {
|
|
156
|
-
return (
|
|
157
|
-
<Text>{this.presentDateTime({apiMakerType: "date", value})}</Text>
|
|
158
|
-
)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return this.presentColumnValue(value)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
_modelCallbackArgs () {
|
|
165
|
-
const {model} = this.p
|
|
166
|
-
const modelArgName = inflection.camelize(this.props.liveTable.props.modelClass.modelClassData().name, true)
|
|
167
|
-
const modelCallbackArgs = {model}
|
|
168
|
-
|
|
169
|
-
modelCallbackArgs[modelArgName] = model
|
|
170
|
-
|
|
171
|
-
return modelCallbackArgs
|
|
172
|
-
}
|
|
173
|
-
|
|
174
125
|
onDestroyClicked = async () => {
|
|
175
|
-
const {destroyMessage} = this.p.
|
|
126
|
+
const {destroyMessage} = this.p.table.props
|
|
176
127
|
const {model} = this.p
|
|
177
128
|
|
|
178
129
|
if (!confirm(I18n.t("js.shared.are_you_sure"))) {
|
|
@@ -189,46 +140,4 @@ export default memo(shapeComponent(class ApiMakerBootStrapLiveTableModelRow exte
|
|
|
189
140
|
FlashMessage.errorResponse(error)
|
|
190
141
|
}
|
|
191
142
|
}
|
|
192
|
-
|
|
193
|
-
presentColumnValue(value) {
|
|
194
|
-
if (value instanceof Date) {
|
|
195
|
-
return <Text>{this.presentDateTime({value})}</Text>
|
|
196
|
-
} else if (MoneyFormatter.isMoney(value)) {
|
|
197
|
-
return <Text>{MoneyFormatter.format(value)}</Text>
|
|
198
|
-
} else if (typeof value == "boolean") {
|
|
199
|
-
if (value) {
|
|
200
|
-
return <Text>{I18n.t("js.shared.yes", {defaultValue: "Yes"})}</Text>
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return <Text>{I18n.t("js.shared.no", {defaultValue: "No"})}</Text>
|
|
204
|
-
} else if (Array.isArray(value)) {
|
|
205
|
-
return (
|
|
206
|
-
<Text>
|
|
207
|
-
{value
|
|
208
|
-
.map((valuePart) => this.presentColumnValue(valuePart))
|
|
209
|
-
.filter((valuePart) => Boolean(valuePart))
|
|
210
|
-
.join(", ")
|
|
211
|
-
}
|
|
212
|
-
</Text>
|
|
213
|
-
)
|
|
214
|
-
} else if (typeof value == "string") {
|
|
215
|
-
return <Text>{value}</Text>
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return <Text>{value}</Text>
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
presentDateTime({apiMakerType, value}) {
|
|
222
|
-
if (!apiMakerType || apiMakerType == "time") {
|
|
223
|
-
const dateTimeFormatName = this.props.liveTable.props.defaultDateTimeFormatName || "time.formats.default"
|
|
224
|
-
|
|
225
|
-
return I18n.l(dateTimeFormatName, value)
|
|
226
|
-
} else if (apiMakerType == "date") {
|
|
227
|
-
const dateFormatName = this.props.liveTable.props.defaultDateFormatName || "date.formats.default"
|
|
228
|
-
|
|
229
|
-
return I18n.l(dateFormatName, value)
|
|
230
|
-
} else {
|
|
231
|
-
throw new Error(`Unhandled type: ${apiMakerType}`)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
143
|
}))
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import BaseComponent from "../../base-component"
|
|
2
|
+
import ColumnContent from "../column-content"
|
|
3
|
+
import columnIdentifier from "../column-identifier.mjs"
|
|
4
|
+
import columnVisible from "../column-visible.mjs"
|
|
5
|
+
import {saveAs} from "file-saver"
|
|
6
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome"
|
|
7
|
+
import {memo} from "react"
|
|
8
|
+
import PropTypes from "prop-types"
|
|
9
|
+
import propTypesExact from "prop-types-exact"
|
|
10
|
+
import {renderToString} from "react-dom/server"
|
|
11
|
+
import {shapeComponent} from "set-state-compare/src/shape-component.js"
|
|
12
|
+
import {Pressable, Text} from "react-native"
|
|
13
|
+
|
|
14
|
+
export default memo(shapeComponent(class ApiMakerTableSettingsDownloadAction extends BaseComponent {
|
|
15
|
+
static propTypes = propTypesExact({
|
|
16
|
+
table: PropTypes.object.isRequired
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
return (
|
|
21
|
+
<Pressable onPress={this.tt.onDownloadPress} style={{flexDirection: "row", alignItems: "center"}}>
|
|
22
|
+
<FontAwesomeIcon name="download" size={20} />
|
|
23
|
+
<Text style={{marginLeft: 5}}>
|
|
24
|
+
Download
|
|
25
|
+
</Text>
|
|
26
|
+
</Pressable>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onDownloadPress = () => {
|
|
31
|
+
const {table} = this.p
|
|
32
|
+
const {modelClass} = table.p
|
|
33
|
+
const {collection} = table.tt
|
|
34
|
+
const {models} = collection
|
|
35
|
+
const {preparedColumns} = table.s
|
|
36
|
+
const tableElement = (
|
|
37
|
+
<table>
|
|
38
|
+
<thead>
|
|
39
|
+
<tr>
|
|
40
|
+
{preparedColumns?.map(({column, tableSettingColumn}) => columnVisible(column, tableSettingColumn) &&
|
|
41
|
+
<th key={columnIdentifier(column)}>
|
|
42
|
+
{table.headerLabelForColumn(column)}
|
|
43
|
+
</th>
|
|
44
|
+
)}
|
|
45
|
+
</tr>
|
|
46
|
+
</thead>
|
|
47
|
+
<tbody>
|
|
48
|
+
{models.map((model) =>
|
|
49
|
+
<tr key={model.id()}>
|
|
50
|
+
{preparedColumns?.map(({column, tableSettingColumn}) => columnVisible(column, tableSettingColumn) &&
|
|
51
|
+
<td key={columnIdentifier(column)}>
|
|
52
|
+
{new ColumnContent({column, mode: "html", model, table}).content()}
|
|
53
|
+
</td>
|
|
54
|
+
)}
|
|
55
|
+
</tr>
|
|
56
|
+
)}
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
)
|
|
60
|
+
const tableHTML = renderToString(tableElement)
|
|
61
|
+
const blob = new Blob([tableHTML], {type: "text/html;charset=utf-8"})
|
|
62
|
+
const fileName = `${modelClass.modelName().human({count: 2})}.html`
|
|
63
|
+
|
|
64
|
+
saveAs(blob, fileName)
|
|
65
|
+
}
|
|
66
|
+
}))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import BaseComponent from "../../base-component"
|
|
2
2
|
import columnIdentifier from "../column-identifier.mjs"
|
|
3
3
|
import ColumnRow from "./column-row"
|
|
4
|
+
import DownloadAction from "./download-action"
|
|
4
5
|
import {memo, useRef} from "react"
|
|
5
6
|
import PropTypes from "prop-types"
|
|
6
7
|
import propTypesExact from "prop-types-exact"
|
|
@@ -46,6 +47,7 @@ export default memo(shapeComponent(class ApiMakerTableSettings extends BaseCompo
|
|
|
46
47
|
Settings
|
|
47
48
|
</Text>
|
|
48
49
|
</View>
|
|
50
|
+
<DownloadAction table={table} />
|
|
49
51
|
<View style={{marginBottom: 5}}>
|
|
50
52
|
<Text style={{fontSize: 16, fontWeight: "bold"}}>
|
|
51
53
|
Columns
|
package/src/table/table.jsx
CHANGED
|
@@ -9,9 +9,9 @@ import columnVisible from "./column-visible.mjs"
|
|
|
9
9
|
import debounce from "debounce"
|
|
10
10
|
import Filters from "./filters"
|
|
11
11
|
import FlatList from "./components/flat-list"
|
|
12
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome"
|
|
12
13
|
import Header from "./components/header"
|
|
13
14
|
import HeaderColumn from "./header-column"
|
|
14
|
-
import Icon from "../icon"
|
|
15
15
|
import * as inflection from "inflection"
|
|
16
16
|
import modelClassRequire from "../model-class-require.mjs"
|
|
17
17
|
import ModelRow from "./model-row"
|
|
@@ -480,9 +480,9 @@ export default memo(shapeComponent(class ApiMakerTable extends BaseComponent {
|
|
|
480
480
|
index={index}
|
|
481
481
|
isSmallScreen={this.tt.isSmallScreen}
|
|
482
482
|
key={model.id()}
|
|
483
|
-
liveTable={this}
|
|
484
483
|
model={model}
|
|
485
484
|
preparedColumns={preparedColumns}
|
|
485
|
+
table={this}
|
|
486
486
|
tableSettingFullCacheKey={tableSettingFullCacheKey}
|
|
487
487
|
/>
|
|
488
488
|
)
|
|
@@ -556,14 +556,14 @@ export default memo(shapeComponent(class ApiMakerTable extends BaseComponent {
|
|
|
556
556
|
<View style={{flexDirection: "row"}}>
|
|
557
557
|
{controls && controls({models, qParams, query, result})}
|
|
558
558
|
<Pressable dataSet={{class: "filter-button"}} onPress={this.tt.onFilterClicked}>
|
|
559
|
-
<
|
|
559
|
+
<FontAwesomeIcon name="search" size={20} />
|
|
560
560
|
</Pressable>
|
|
561
561
|
<View style={{position: "relative"}}>
|
|
562
562
|
{showSettings &&
|
|
563
563
|
<Settings onRequestClose={this.tt.onRequestCloseSettings} table={this} />
|
|
564
564
|
}
|
|
565
565
|
<Pressable dataSet={{class: "settings-button"}} onPress={this.tt.onSettingsClicked}>
|
|
566
|
-
<
|
|
566
|
+
<FontAwesomeIcon name="gear" size={20} />
|
|
567
567
|
</Pressable>
|
|
568
568
|
</View>
|
|
569
569
|
</View>
|
package/src/icon.jsx
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import BaseComponent from "./base-component"
|
|
2
|
-
import {Image} from "react-native"
|
|
3
|
-
import {memo, useMemo} from "react"
|
|
4
|
-
import PropTypes from "prop-types"
|
|
5
|
-
import {shapeComponent} from "set-state-compare/src/shape-component.js"
|
|
6
|
-
|
|
7
|
-
export default memo(shapeComponent(class ComponentsIcon extends BaseComponent {
|
|
8
|
-
static propTypes = {
|
|
9
|
-
icon: PropTypes.string.isRequired
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
setup() {
|
|
13
|
-
const {icon} = this.p
|
|
14
|
-
|
|
15
|
-
this.useStates({
|
|
16
|
-
IconComponent: null,
|
|
17
|
-
imageSource: null
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
useMemo(() => {
|
|
21
|
-
this.loadIcon()
|
|
22
|
-
}, [icon])
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async loadIcon() {
|
|
26
|
-
const {icon} = this.p
|
|
27
|
-
const IconComponent = await import(`./icons/${icon}.svg`)
|
|
28
|
-
|
|
29
|
-
this.setState({IconComponent})
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
render() {
|
|
33
|
-
const {icon, style, ...restProps} = this.props
|
|
34
|
-
const {IconComponent} = this.s
|
|
35
|
-
const actualStyle = Object.assign(
|
|
36
|
-
{
|
|
37
|
-
width: 16,
|
|
38
|
-
height: 16
|
|
39
|
-
},
|
|
40
|
-
style
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
if (!IconComponent) {
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<Image source={IconComponent.default} style={actualStyle} {...restProps} />
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
}))
|
package/src/icons/bars-solid.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"/></svg>
|
package/src/icons/gear-solid.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
package/src/icons/pen-solid.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>
|