chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
|
|
@ -0,0 +1,3 @@
|
|||
import React from 'react'
|
||||
|
||||
export default React.createContext()
|
||||
129
packages/admin-ui/src/components/editableTable/Header.jsx
Normal file
129
packages/admin-ui/src/components/editableTable/Header.jsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import classnames from 'classnames'
|
||||
import * as R from 'ramda'
|
||||
import React, { useContext } from 'react'
|
||||
import {
|
||||
Td,
|
||||
THead,
|
||||
TDoubleLevelHead,
|
||||
ThDoubleLevel
|
||||
} from 'src/components/fake-table/Table'
|
||||
|
||||
import { sentenceCase } from 'src/utils/string'
|
||||
|
||||
import TableCtx from './Context'
|
||||
|
||||
const groupSecondHeader = elements => {
|
||||
const doubleHeader = R.prop('doubleHeader')
|
||||
const sameDoubleHeader = (a, b) => doubleHeader(a) === doubleHeader(b)
|
||||
const group = R.pipe(
|
||||
R.groupWith(sameDoubleHeader),
|
||||
R.map(group =>
|
||||
R.isNil(doubleHeader(group[0])) // No doubleHeader
|
||||
? group
|
||||
: [
|
||||
{
|
||||
width: R.sum(R.map(R.prop('width'), group)),
|
||||
elements: group,
|
||||
name: doubleHeader(group[0])
|
||||
}
|
||||
]
|
||||
),
|
||||
R.reduce(R.concat, [])
|
||||
)
|
||||
|
||||
return R.all(R.pipe(doubleHeader, R.isNil), elements)
|
||||
? [elements, THead]
|
||||
: [group(elements), TDoubleLevelHead]
|
||||
}
|
||||
|
||||
const Header = () => {
|
||||
const {
|
||||
elements,
|
||||
enableEdit,
|
||||
enableEditText,
|
||||
editWidth,
|
||||
enableDelete,
|
||||
deleteWidth,
|
||||
enableToggle,
|
||||
toggleWidth,
|
||||
orderedBy,
|
||||
DEFAULT_COL_SIZE
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const mapElement2 = (it, idx) => {
|
||||
const { width, elements, name } = it
|
||||
|
||||
if (elements && elements.length) {
|
||||
return (
|
||||
<ThDoubleLevel key={idx} width={width} title={name}>
|
||||
{elements.map(mapElement)}
|
||||
</ThDoubleLevel>
|
||||
)
|
||||
}
|
||||
|
||||
return mapElement(it, idx)
|
||||
}
|
||||
|
||||
const mapElement = (
|
||||
{ name, display, width = DEFAULT_COL_SIZE, header, textAlign },
|
||||
idx
|
||||
) => {
|
||||
const orderClasses = classnames({
|
||||
'whitespace-nowrap':
|
||||
R.isNil(header) && !R.isNil(orderedBy) && R.equals(name, orderedBy.code)
|
||||
})
|
||||
|
||||
const attachOrderedByToComplexHeader = header => {
|
||||
if (!R.isNil(orderedBy) && R.equals(name, orderedBy.code)) {
|
||||
try {
|
||||
const cloneHeader = R.clone(header)
|
||||
const children = R.path(['props', 'children'], cloneHeader)
|
||||
const spanChild = R.find(it => R.equals(it.type, 'span'), children)
|
||||
spanChild.props.children = R.append(' -', spanChild.props.children)
|
||||
return cloneHeader
|
||||
} catch (e) {
|
||||
return header
|
||||
}
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
return (
|
||||
<Td header key={idx} width={width} textAlign={textAlign}>
|
||||
{!R.isNil(header) ? (
|
||||
<>{attachOrderedByToComplexHeader(header) ?? header}</>
|
||||
) : (
|
||||
<span className={orderClasses}>
|
||||
{!R.isNil(display) ? display : sentenceCase(name)}{' '}
|
||||
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
|
||||
</span>
|
||||
)}
|
||||
</Td>
|
||||
)
|
||||
}
|
||||
|
||||
const [innerElements, HeaderElement] = groupSecondHeader(elements)
|
||||
|
||||
return (
|
||||
<HeaderElement>
|
||||
{innerElements.map(mapElement2)}
|
||||
{enableEdit && (
|
||||
<Td header width={editWidth} textAlign="center">
|
||||
{enableEditText ?? `Edit`}
|
||||
</Td>
|
||||
)}
|
||||
{enableDelete && (
|
||||
<Td header width={deleteWidth} textAlign="center">
|
||||
Delete
|
||||
</Td>
|
||||
)}
|
||||
{enableToggle && (
|
||||
<Td header width={toggleWidth} textAlign="center">
|
||||
Enable
|
||||
</Td>
|
||||
)}
|
||||
</HeaderElement>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||
|
||||
import EditableTable from './Table'
|
||||
|
||||
const NamespacedTable = ({
|
||||
name,
|
||||
save,
|
||||
data = {},
|
||||
namespaces = [],
|
||||
...props
|
||||
}) => {
|
||||
const innerSave = (...[, it]) => {
|
||||
return save(toNamespace(it.id)(R.omit(['id2'], it)))
|
||||
}
|
||||
|
||||
const innerData = R.map(it => ({
|
||||
id: it,
|
||||
...fromNamespace(it)(data)
|
||||
}))(namespaces)
|
||||
|
||||
return (
|
||||
<EditableTable name={name} data={innerData} save={innerSave} {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
export default NamespacedTable
|
||||
299
packages/admin-ui/src/components/editableTable/Row.jsx
Normal file
299
packages/admin-ui/src/components/editableTable/Row.jsx
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
import Switch from '@mui/material/Switch'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import SvgIcon from '@mui/material/SvgIcon'
|
||||
import classnames from 'classnames'
|
||||
import { Field, useFormikContext } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React, { useContext, useState } from 'react'
|
||||
import { DeleteDialog } from 'src/components/DeleteDialog'
|
||||
import { Td, Tr } from 'src/components/fake-table/Table'
|
||||
import { Label2 } from 'src/components/typography'
|
||||
import DisabledDeleteIcon from 'src/styling/icons/action/delete/disabled.svg?react'
|
||||
import DeleteIcon from 'src/styling/icons/action/delete/enabled.svg?react'
|
||||
import DisabledEditIcon from 'src/styling/icons/action/edit/disabled.svg?react'
|
||||
import EditIcon from 'src/styling/icons/action/edit/enabled.svg?react'
|
||||
import StripesSvg from 'src/styling/icons/stripes.svg?react'
|
||||
|
||||
import { Link } from 'src/components/buttons'
|
||||
|
||||
import TableCtx from './Context'
|
||||
import moduleStyles from './Row.module.css'
|
||||
|
||||
const ActionCol = ({ disabled, editing }) => {
|
||||
const { values, submitForm, resetForm } = useFormikContext()
|
||||
const {
|
||||
editWidth,
|
||||
onEdit,
|
||||
enableEdit,
|
||||
enableDelete,
|
||||
disableRowEdit,
|
||||
onDelete,
|
||||
deleteWidth,
|
||||
enableToggle,
|
||||
onToggle,
|
||||
toggleWidth,
|
||||
forceAdd,
|
||||
clearError,
|
||||
actionColSize,
|
||||
error
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const disableEdit = disabled || (disableRowEdit && disableRowEdit(values))
|
||||
const cancel = () => {
|
||||
clearError()
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const [deleteDialog, setDeleteDialog] = useState(false)
|
||||
|
||||
const onConfirmed = () => {
|
||||
onDelete(values.id).then(res => {
|
||||
if (!R.isNil(res)) setDeleteDialog(false)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{editing && (
|
||||
<Td textAlign="center" width={actionColSize}>
|
||||
<Link
|
||||
className={moduleStyles.saveButton}
|
||||
type="submit"
|
||||
color="primary"
|
||||
onClick={submitForm}>
|
||||
Save
|
||||
</Link>
|
||||
{!forceAdd && (
|
||||
<Link color="secondary" onClick={cancel}>
|
||||
Cancel
|
||||
</Link>
|
||||
)}
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableEdit && (
|
||||
<Td textAlign="center" width={editWidth}>
|
||||
<IconButton
|
||||
disabled={disableEdit}
|
||||
onClick={() => onEdit && onEdit(values.id)}
|
||||
size="small">
|
||||
<SvgIcon>
|
||||
{disableEdit ? <DisabledEditIcon /> : <EditIcon />}
|
||||
</SvgIcon>
|
||||
</IconButton>
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableDelete && (
|
||||
<Td textAlign="center" width={deleteWidth}>
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
setDeleteDialog(true)
|
||||
}}
|
||||
size="small">
|
||||
<SvgIcon>
|
||||
{disabled ? <DisabledDeleteIcon /> : <DeleteIcon />}
|
||||
</SvgIcon>
|
||||
</IconButton>
|
||||
<DeleteDialog
|
||||
open={deleteDialog}
|
||||
setDeleteDialog={setDeleteDialog}
|
||||
onConfirmed={onConfirmed}
|
||||
onDismissed={() => {
|
||||
setDeleteDialog(false)
|
||||
clearError()
|
||||
}}
|
||||
errorMessage={error}
|
||||
/>
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableToggle && (
|
||||
<Td textAlign="center" width={toggleWidth}>
|
||||
<Switch
|
||||
checked={!!values.active}
|
||||
value={!!values.active}
|
||||
disabled={disabled}
|
||||
onChange={() => onToggle(values.id)}
|
||||
/>
|
||||
</Td>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ECol = ({ editing, focus, config, extraPaddingRight, extraPadding }) => {
|
||||
const {
|
||||
name,
|
||||
names,
|
||||
bypassField,
|
||||
input,
|
||||
editable = true,
|
||||
size,
|
||||
bold,
|
||||
width,
|
||||
textAlign,
|
||||
editingAlign = textAlign,
|
||||
prefix,
|
||||
PrefixComponent = Label2,
|
||||
suffix,
|
||||
SuffixComponent = Label2,
|
||||
textStyle = it => {},
|
||||
isHidden = it => false,
|
||||
view = it => it?.toString(),
|
||||
inputProps = {}
|
||||
} = config
|
||||
|
||||
const fields = names ?? [name]
|
||||
|
||||
const { values } = useFormikContext()
|
||||
const isEditable = editable => {
|
||||
if (typeof editable === 'function') return editable(values)
|
||||
return editable
|
||||
}
|
||||
const isEditing = editing && isEditable(editable)
|
||||
const isField = !bypassField
|
||||
|
||||
const innerProps = {
|
||||
fullWidth: true,
|
||||
autoFocus: focus,
|
||||
size,
|
||||
bold,
|
||||
textAlign: isEditing ? editingAlign : textAlign,
|
||||
...inputProps
|
||||
}
|
||||
|
||||
const newAlign = isEditing ? editingAlign : textAlign
|
||||
const justifyContent = newAlign === 'right' ? 'flex-end' : newAlign
|
||||
const style = suffix || prefix ? { justifyContent } : {}
|
||||
|
||||
return (
|
||||
<div className={moduleStyles.fields}>
|
||||
{fields.map((f, idx) => (
|
||||
<Td
|
||||
style={style}
|
||||
key={idx}
|
||||
className={{
|
||||
[moduleStyles.extraPaddingRight]: extraPaddingRight,
|
||||
[moduleStyles.extraPadding]: extraPadding,
|
||||
'flex items-center': suffix || prefix
|
||||
}}
|
||||
width={width}
|
||||
size={size}
|
||||
bold={bold}
|
||||
textAlign={textAlign}>
|
||||
{prefix && !isHidden(values) && (
|
||||
<PrefixComponent
|
||||
className={moduleStyles.prefix}
|
||||
style={isEditing ? {} : textStyle(values, isEditing)}>
|
||||
{typeof prefix === 'function' ? prefix(f) : prefix}
|
||||
</PrefixComponent>
|
||||
)}
|
||||
{isEditing && isField && !isHidden(values) && (
|
||||
<Field name={f} component={input} {...innerProps} />
|
||||
)}
|
||||
{isEditing && !isField && !isHidden(values) && (
|
||||
<config.input name={f} />
|
||||
)}
|
||||
{!isEditing && values && !isHidden(values) && (
|
||||
<div style={textStyle(values, isEditing)}>
|
||||
{view(values[f], values)}
|
||||
</div>
|
||||
)}
|
||||
{suffix && !isHidden(values) && (
|
||||
<SuffixComponent
|
||||
className={moduleStyles.suffix}
|
||||
style={isEditing ? {} : textStyle(values, isEditing)}>
|
||||
{suffix}
|
||||
</SuffixComponent>
|
||||
)}
|
||||
{isHidden(values) && <StripesSvg />}
|
||||
</Td>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const groupStriped = elements => {
|
||||
const [toStripe, noStripe] = R.partition(R.propEq('stripe', true))(elements)
|
||||
|
||||
if (!toStripe.length) {
|
||||
return elements
|
||||
}
|
||||
|
||||
const index = R.indexOf(toStripe[0], elements)
|
||||
const width = R.compose(R.sum, R.map(R.path(['width'])))(toStripe)
|
||||
|
||||
return R.insert(
|
||||
index,
|
||||
{ width, editable: false, view: () => <StripesSvg /> },
|
||||
noStripe
|
||||
)
|
||||
}
|
||||
|
||||
const ERow = ({ editing, disabled, lastOfGroup, newRow }) => {
|
||||
const { touched, errors, values } = useFormikContext()
|
||||
const {
|
||||
elements,
|
||||
enableEdit,
|
||||
enableDelete,
|
||||
error,
|
||||
enableToggle,
|
||||
rowSize,
|
||||
stripeWhen
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const shouldStripe = !editing && stripeWhen && stripeWhen(values)
|
||||
|
||||
const innerElements = shouldStripe ? groupStriped(elements) : elements
|
||||
const [toSHeader] = R.partition(R.has('doubleHeader'))(elements)
|
||||
|
||||
const extraPaddingIndex = toSHeader?.length
|
||||
? R.indexOf(toSHeader[0], elements)
|
||||
: -1
|
||||
|
||||
const extraPaddingRightIndex = toSHeader?.length
|
||||
? R.indexOf(toSHeader[toSHeader.length - 1], elements)
|
||||
: -1
|
||||
|
||||
const elementToFocusIndex = innerElements.findIndex(
|
||||
it => it.editable === undefined || it.editable
|
||||
)
|
||||
|
||||
const classNames = {
|
||||
[moduleStyles.lastOfGroup]: lastOfGroup
|
||||
}
|
||||
|
||||
const touchedErrors = R.pick(R.keys(touched), errors)
|
||||
const hasTouchedErrors = touchedErrors && R.keys(touchedErrors).length > 0
|
||||
const hasErrors = hasTouchedErrors || !!error
|
||||
|
||||
const errorMessage =
|
||||
error || (touchedErrors && R.values(touchedErrors).join(', '))
|
||||
|
||||
return (
|
||||
<Tr
|
||||
className={classnames(classNames)}
|
||||
size={rowSize}
|
||||
error={editing && hasErrors}
|
||||
newRow={newRow && !hasErrors}
|
||||
shouldShowError
|
||||
errorMessage={errorMessage}>
|
||||
{innerElements.map((it, idx) => {
|
||||
return (
|
||||
<ECol
|
||||
key={idx}
|
||||
config={it}
|
||||
editing={editing}
|
||||
focus={idx === elementToFocusIndex && editing}
|
||||
extraPaddingRight={extraPaddingRightIndex === idx}
|
||||
extraPadding={extraPaddingIndex === idx}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{(enableEdit || enableDelete || enableToggle) && (
|
||||
<ActionCol disabled={disabled} editing={editing} />
|
||||
)}
|
||||
</Tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default ERow
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
.saveButton {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.lastOfGroup {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.extraPadding {
|
||||
padding-left: 35px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.extraPaddingRight {
|
||||
padding-right: 39px;
|
||||
}
|
||||
|
||||
.suffix {
|
||||
margin: 0 0 0 7px;
|
||||
}
|
||||
|
||||
.prefix {
|
||||
margin: 0 7px 0 0;
|
||||
}
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
249
packages/admin-ui/src/components/editableTable/Table.jsx
Normal file
249
packages/admin-ui/src/components/editableTable/Table.jsx
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
import { Form, Formik } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||
import Link from 'src/components/buttons/Link'
|
||||
import { TBody, Table } from 'src/components/fake-table/Table'
|
||||
import { Info2, TL1 } from 'src/components/typography'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { AddButton } from 'src/components/buttons/index'
|
||||
|
||||
import TableCtx from './Context'
|
||||
import Header from './Header'
|
||||
import ERow from './Row'
|
||||
import classes from './Table.module.css'
|
||||
|
||||
const ACTION_COL_SIZE = 87
|
||||
const DEFAULT_COL_SIZE = 100
|
||||
|
||||
const getWidth = R.compose(
|
||||
R.reduce(R.add)(0),
|
||||
R.map(it => it.width ?? DEFAULT_COL_SIZE)
|
||||
)
|
||||
|
||||
const ETable = ({
|
||||
name,
|
||||
title,
|
||||
titleLg,
|
||||
elements = [],
|
||||
data = [],
|
||||
save,
|
||||
error: externalError,
|
||||
rowSize = 'md',
|
||||
validationSchema,
|
||||
enableCreate,
|
||||
enableEdit,
|
||||
enableEditText,
|
||||
editWidth: outerEditWidth,
|
||||
enableDelete,
|
||||
deleteWidth = ACTION_COL_SIZE,
|
||||
enableToggle,
|
||||
toggleWidth = ACTION_COL_SIZE,
|
||||
onToggle,
|
||||
forceDisable,
|
||||
disableAdd,
|
||||
initialValues,
|
||||
setEditing,
|
||||
shouldOverrideEdit,
|
||||
editOverride,
|
||||
stripeWhen,
|
||||
disableRowEdit,
|
||||
groupBy,
|
||||
sortBy,
|
||||
createText = 'Add override',
|
||||
forceAdd = false,
|
||||
tbodyWrapperClass,
|
||||
orderedBy = null
|
||||
}) => {
|
||||
const [editingId, setEditingId] = useState(null)
|
||||
const [adding, setAdding] = useState(false)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
useEffect(() => setError(externalError), [externalError])
|
||||
useEffect(() => {
|
||||
setError(null)
|
||||
setAdding(forceAdd)
|
||||
}, [forceAdd])
|
||||
|
||||
const innerSave = async value => {
|
||||
if (saving) return
|
||||
|
||||
setSaving(true)
|
||||
|
||||
const it = validationSchema.cast(value, { assert: 'ignore-optionality' })
|
||||
const index = R.findIndex(R.propEq('id', it.id))(data)
|
||||
const list = index !== -1 ? R.update(index, it, data) : R.prepend(it, data)
|
||||
|
||||
if (!R.equals(data[index], it)) {
|
||||
try {
|
||||
await save({ [name]: list }, it)
|
||||
} catch (err) {
|
||||
setSaving(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setAdding(false)
|
||||
setEditing && setEditing(false)
|
||||
setSaving(false)
|
||||
}
|
||||
|
||||
const onDelete = id => {
|
||||
const list = R.reject(it => it.id === id, data)
|
||||
return save({ [name]: list })
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
setAdding(false)
|
||||
setEditingId(null)
|
||||
setEditing && setEditing(false)
|
||||
}
|
||||
|
||||
const onEdit = it => {
|
||||
if (shouldOverrideEdit && shouldOverrideEdit(it)) return editOverride(it)
|
||||
setEditingId(it)
|
||||
setError(null)
|
||||
setEditing && setEditing(it, true)
|
||||
}
|
||||
|
||||
const addField = () => {
|
||||
setAdding(true)
|
||||
setError(null)
|
||||
setEditing && setEditing(true, true)
|
||||
}
|
||||
|
||||
const widthIfEditNull =
|
||||
enableDelete || enableToggle ? ACTION_COL_SIZE : ACTION_COL_SIZE * 2
|
||||
|
||||
const editWidth = R.defaultTo(widthIfEditNull)(outerEditWidth)
|
||||
|
||||
const actionColSize =
|
||||
((enableDelete && deleteWidth) ?? 0) +
|
||||
((enableEdit && editWidth) ?? 0) +
|
||||
((enableToggle && toggleWidth) ?? 0)
|
||||
|
||||
const width = getWidth(elements) + actionColSize
|
||||
|
||||
const showButtonOnEmpty = !data.length && enableCreate && !adding
|
||||
const canAdd = !forceDisable && !editingId && !disableAdd && !adding
|
||||
const showTable = adding || data.length !== 0
|
||||
|
||||
const innerData = sortBy ? R.sortWith(sortBy)(data) : data
|
||||
|
||||
const ctxValue = {
|
||||
elements,
|
||||
enableEdit,
|
||||
enableEditText,
|
||||
onEdit,
|
||||
clearError: () => setError(null),
|
||||
error: error,
|
||||
disableRowEdit,
|
||||
editWidth,
|
||||
enableDelete,
|
||||
onDelete,
|
||||
deleteWidth,
|
||||
enableToggle,
|
||||
rowSize,
|
||||
onToggle,
|
||||
toggleWidth,
|
||||
actionColSize,
|
||||
stripeWhen,
|
||||
forceAdd,
|
||||
orderedBy,
|
||||
DEFAULT_COL_SIZE
|
||||
}
|
||||
|
||||
return (
|
||||
<TableCtx.Provider value={ctxValue}>
|
||||
<div style={{ width }}>
|
||||
{showButtonOnEmpty && canAdd && (
|
||||
<AddButton onClick={addField}>{createText}</AddButton>
|
||||
)}
|
||||
{showTable && (
|
||||
<>
|
||||
{(title || enableCreate) && (
|
||||
<div className={classes.outerHeader}>
|
||||
{title && titleLg && (
|
||||
<TL1 className={classes.title}>{title}</TL1>
|
||||
)}
|
||||
{title && !titleLg && (
|
||||
<Info2 className={classes.title}>{title}</Info2>
|
||||
)}
|
||||
{enableCreate && canAdd && (
|
||||
<Link className={classes.addLink} onClick={addField}>
|
||||
{createText}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Table>
|
||||
<Header />
|
||||
<div className={tbodyWrapperClass}>
|
||||
<TBody>
|
||||
{adding && (
|
||||
<Formik
|
||||
validateOnBlur={false}
|
||||
validateOnChange={false}
|
||||
initialValues={{ id: uuidv4(), ...initialValues }}
|
||||
onReset={onReset}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={innerSave}>
|
||||
<Form>
|
||||
<PromptWhenDirty />
|
||||
<ERow
|
||||
editing={true}
|
||||
disabled={forceDisable}
|
||||
newRow={true}
|
||||
/>
|
||||
</Form>
|
||||
</Formik>
|
||||
)}
|
||||
{innerData.map((it, idx) => {
|
||||
const nextElement = innerData[idx + 1]
|
||||
|
||||
const canGroup = !!groupBy && nextElement
|
||||
const isFunction = R.type(groupBy) === 'Function'
|
||||
const groupFunction = isFunction ? groupBy : R.prop(groupBy)
|
||||
|
||||
const isLastOfGroup =
|
||||
canGroup &&
|
||||
groupFunction(it) !== groupFunction(nextElement)
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur={false}
|
||||
validateOnChange={false}
|
||||
key={it.id ?? idx}
|
||||
enableReinitialize
|
||||
initialValues={it}
|
||||
onReset={onReset}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={innerSave}>
|
||||
<Form>
|
||||
<PromptWhenDirty />
|
||||
<ERow
|
||||
lastOfGroup={isLastOfGroup}
|
||||
editing={editingId === it.id}
|
||||
disabled={
|
||||
forceDisable ||
|
||||
(editingId && editingId !== it.id) ||
|
||||
adding
|
||||
}
|
||||
/>
|
||||
</Form>
|
||||
</Formik>
|
||||
)
|
||||
})}
|
||||
</TBody>
|
||||
</div>
|
||||
</Table>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</TableCtx.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default ETable
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
.addLink {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
color: var(--comet);
|
||||
}
|
||||
|
||||
.outerHeader {
|
||||
min-height: 16px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
4
packages/admin-ui/src/components/editableTable/index.js
Normal file
4
packages/admin-ui/src/components/editableTable/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import NamespacedTable from './NamespacedTable'
|
||||
import Table from './Table'
|
||||
|
||||
export { Table, NamespacedTable }
|
||||
Loading…
Add table
Add a link
Reference in a new issue