Компоненты колонок разделены на файлы

This commit is contained in:
Александр Сироткин 2022-03-02 21:18:13 +05:00
parent 0c092aa138
commit 592cfc30e2
9 changed files with 461 additions and 364 deletions

View File

@ -0,0 +1,63 @@
import { ReactNode } from 'react'
import { Rule } from 'antd/lib/form'
import { ColumnProps } from 'antd/lib/table'
export {
RegExpIsFloat,
makeNumericRender,
makeNumericColumn,
makeNumericColumnOptions,
makeNumericColumnPlanFact,
makeNumericStartEnd,
makeNumericMinMax,
makeNumericAvgRange
} from './numeric'
export { makeColumnsPlanFact } from './plan_fact'
export { makeSelectColumn } from './select'
export { makeTagColumn, makeTagInput } from './tag'
export { makeFilterTextMatch, makeTextColumn } from './text'
export {
rawTimezones,
timezoneOptions,
TimezoneSelect,
makeTimezoneColumn,
makeTimezoneRenderer
} from './timezone'
export type { TagInputProps } from './tag'
export type DataType<T = any> = Record<string, T>
export type RenderMethod<T = any> = (value: T, dataset?: DataType<T>, index?: number) => ReactNode
export type SorterMethod<T = any> = (a?: DataType<T> | null, b?: DataType<T> | null) => number
/*
other - объект с дополнительными свойствами колонки
поддерживаются все базовые свойства из описания https://ant.design/components/table/#Column
плю дополнительные для колонок EditableTable: */
export type columnPropsOther<T = any> = ColumnProps<T> & {
// редактируемая колонка
editable?: boolean
// react компонента редактора
input?: ReactNode
// значение может быть пустым
isRequired?: boolean
// css класс для <FormItem/>, если требуется
formItemClass?: string
// массив правил валидации значений https://ant.design/components/form/#Rule
formItemRules?: Rule[]
// дефолтное значение при добавлении новой строки
initialValue?: string | number
render?: RenderMethod<T>
}
export const makeColumn = (title: ReactNode, key: string, other?: columnPropsOther) => ({
title: title,
key: key,
dataIndex: key,
...other,
})
export const makeGroupColumn = (title: ReactNode, children: object[]) => ({ title, children })
export default makeColumn

View File

@ -0,0 +1,111 @@
import { InputNumber } from 'antd'
import { ReactNode } from 'react'
import { makeNumericSorter } from '../sorters'
import { columnPropsOther, makeGroupColumn, RenderMethod } from '.'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
export const makeNumericRender = <T extends unknown>(fixed?: number): RenderMethod<T> => (value) => {
let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) {
val = (fixed ?? null) !== null
? (+value).toFixed(fixed)
: (+value).toPrecision(5)
}
return (
<div className={'text-align-r-container'}>
<span>{val}</span>
</div>
)
}
export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string): columnPropsOther => ({
editable: true,
initialValue: 0,
width: 100,
sorter: sorterKey ? makeNumericSorter(sorterKey) : undefined,
formItemRules: [{
required: true,
message: 'Введите число',
pattern: RegExpIsFloat,
}],
render: makeNumericRender(fixed),
})
export const makeNumericColumn = (
title: ReactNode,
dataIndex: string,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
other?: columnPropsOther
) => ({
title: title,
dataIndex: dataIndex,
key: dataIndex,
filters: filters,
onFilter: filterDelegate ? filterDelegate(dataIndex) : null,
sorter: makeNumericSorter(dataIndex),
width: width,
input: <InputNumber style={{ width: '100%' }}/>,
render: renderDelegate ?? makeNumericRender(),
align: 'right',
...other
})
export const makeNumericColumnPlanFact = (
title: ReactNode,
dataIndex: string,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string
) => makeGroupColumn(title, [
makeNumericColumn('п', dataIndex + 'Plan', filters, filterDelegate, renderDelegate, width),
makeNumericColumn('ф', dataIndex + 'Fact', filters, filterDelegate, renderDelegate, width),
])
export const makeNumericStartEnd = (
title: ReactNode,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
) => makeGroupColumn(title, [
makeNumericColumn('старт', dataIndex + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Start')),
makeNumericColumn('конец', dataIndex + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'End'))
])
export const makeNumericMinMax = (
title: ReactNode,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
) => makeGroupColumn(title, [
makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max')),
])
export const makeNumericAvgRange = (
title: ReactNode,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string
) => makeGroupColumn(title, [
makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
makeNumericColumn('сред', dataIndex + 'Avg', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Avg')),
makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max'))
])
export default makeNumericColumn

View File

@ -0,0 +1,38 @@
import { ReactNode } from 'react'
import { columnPropsOther, makeColumn } from '.'
export const makeColumnsPlanFact = (
title: string | ReactNode,
key: string | string[],
columsOther?: columnPropsOther | [columnPropsOther, columnPropsOther],
gruopOther?: any
) => {
let keyPlanLocal: string
let keyFactLocal: string
if (key instanceof Array) {
keyPlanLocal = key[0]
keyFactLocal = key[1]
} else {
keyPlanLocal = key + 'Plan'
keyFactLocal = key + 'Fact'
}
let columsOtherLocal : any[2]
if (columsOther instanceof Array)
columsOtherLocal = [columsOther[0], columsOther[1]]
else
columsOtherLocal = [columsOther, columsOther]
return {
title: title,
...gruopOther,
children: [
makeColumn('план', keyPlanLocal, columsOtherLocal[0]),
makeColumn('факт', keyFactLocal, columsOtherLocal[1]),
]
}
}
export default makeColumnsPlanFact

View File

@ -0,0 +1,22 @@
import { Select, SelectProps } from 'antd'
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
import { columnPropsOther, makeColumn } from '.'
export const makeSelectColumn = <T extends unknown = string>(
title: string,
dataIndex: string,
options: DefaultOptionType[],
defaultValue?: T,
other?: columnPropsOther,
selectOther?: SelectProps<SelectValue>
) => makeColumn(title, dataIndex, {
...other,
input: <Select options={options} {...selectOther}/>,
render: (value) => {
const item = options?.find(option => option?.value === value)
return other?.render?.(item?.value) ?? item?.label ?? defaultValue ?? value ?? '--'
}
})
export default makeSelectColumn

View File

@ -0,0 +1,74 @@
import { memo, ReactNode, useCallback, useEffect, useState } from 'react'
import { Select, SelectProps, Tag } from 'antd'
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
import { OmitExtends } from '@utils'
import { columnPropsOther, DataType, makeColumn } from '.'
export type TagInputProps<T extends DataType> = OmitExtends<{
options: T[],
value?: T[],
onChange?: (values: T[]) => void,
}, SelectProps<SelectValue>>
export const makeTagInput = <T extends DataType>(value_key: string, label_key: string) =>
memo<TagInputProps<T>>(({ options, value, onChange, ...other }) => {
const [selectOptions, setSelectOptions] = useState<DefaultOptionType[]>([])
const [selectedValue, setSelectedValue] = useState<SelectValue>([])
useEffect(() => setSelectOptions(options.map((elm) => ({
value: String(elm[value_key]),
label: elm[label_key],
}))), [options])
useEffect(() => {
setSelectedValue(value?.map((elm) => String(elm[value_key])) ?? [])
}, [value])
const onSelectChange = useCallback((rawValues?: SelectValue) => {
let values: any[] = []
if (typeof rawValues === 'string')
values = rawValues.split(',')
else if (Array.isArray(rawValues))
values = rawValues
const objectValues: T[] = values.reduce((out: T[], value: string) => {
const res = options.find((option) => String(option[value_key]) === String(value))
if (res) out.push(res)
return out
}, [])
onChange?.(objectValues)
}, [onChange, options])
return (
<Select
{...other}
mode={'tags'}
options={selectOptions}
value={selectedValue}
onChange={onSelectChange}
/>
)
})
export const makeTagColumn = <T extends DataType>(
title: ReactNode,
dataIndex: string,
options: T[],
value_key: keyof DataType,
label_key: keyof DataType,
other?: columnPropsOther,
tagOther?: TagInputProps<T>
) => {
const InputComponent = makeTagInput<T>(value_key, label_key)
return makeColumn(title, dataIndex, {
...other,
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-',
input: <InputComponent {...tagOther} options={options} />,
})
}
export default makeTagColumn

View File

@ -0,0 +1,27 @@
import { ReactNode } from 'react'
import { columnPropsOther, DataType, RenderMethod, SorterMethod } from '.'
import { makeStringSorter } from '../sorters'
export const makeFilterTextMatch = <T extends unknown>(key: keyof DataType<T>) =>
(filterValue: T, dataItem: DataType<T>) => dataItem[key] === filterValue
export const makeTextColumn = <T extends unknown = any>(
title: ReactNode,
dataIndex: string,
filters: object[],
sorter?: SorterMethod<T>,
render?: RenderMethod<T>,
other?: columnPropsOther
) => ({
title: title,
dataIndex: dataIndex,
key: dataIndex,
filters: filters,
onFilter: filters ? makeFilterTextMatch(dataIndex) : null,
sorter: sorter ?? makeStringSorter(dataIndex),
render: render,
...other
})
export default makeTextColumn

View File

@ -0,0 +1,72 @@
import { memo, ReactNode, useEffect, useState } from 'react'
import { Select, SelectProps } from 'antd'
import { SimpleTimezoneDto } from '@api'
import { columnPropsOther, makeColumn } from '.'
export const rawTimezones = {
'Калининград': 2,
'Москва': 3,
'Самара': 4,
'Екатеринбург': 5,
'Омск': 6,
'Красноярск': 7,
'Новосибирск': 7,
'Иркутск': 8,
'Чита': 9,
'Владивосток': 10,
'Магадан': 11,
'Южно-Сахалинск': 11,
'Среднеколымск': 11,
'Анадырь': 12,
'Петропавловск-Камчатский': 12,
}
export const timezoneOptions = Object
.entries(rawTimezones)
.sort((a, b) => a[1] - b[1])
.map(([id, hours]) => ({
label: `UTC${hours > 0 ? '+':''}${('0' + hours).slice(-2)} :: ${id}`,
value: id,
}))
export const TimezoneSelect = memo<SelectProps>(({ onChange, ...other }) => {
const [id, setId] = useState<keyof typeof rawTimezones | null>(null)
useEffect(() => onChange?.({
timezoneId: id,
hours: id ? rawTimezones[id] : 0,
isOverride: false,
}, []), [id, onChange])
return (<Select {...other} onChange={setId} value={id} />)
})
export const makeTimezoneRenderer = () => (timezone?: SimpleTimezoneDto) => {
if (!timezone) return 'UTC~?? :: Неизвестно'
const { hours, timezoneId } = timezone
return `UTC${hours && hours > 0 ? '+':''}${hours ? ('0' + hours).slice(-2) : '~??'} :: ${timezoneId ?? 'Неизвестно'}`
}
export const makeTimezoneColumn = (
title: ReactNode = 'Зона',
key: string = 'timezone',
defaultValue?: SimpleTimezoneDto,
allowClear: boolean = true,
other?: columnPropsOther
) => makeColumn(title, key, {
width: 100,
editable: true,
render: makeTimezoneRenderer(),
input: (
<TimezoneSelect
allowClear={allowClear}
options={timezoneOptions}
defaultValue={defaultValue}
/>
),
...other
})
export default makeTimezoneColumn

View File

@ -1,370 +1,55 @@
import { memo, useEffect, useState, ReactNode } from 'react'
import { InputNumber, Select, Tag, SelectProps } from 'antd'
import { ColumnProps } from 'antd/lib/table'
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
import { Rule } from 'rc-field-form/lib/interface'
import { SimpleTimezoneDto } from '@api'
import { makeNumericSorter, makeStringSorter } from './sorters'
export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
export { EditableTable, makeActionHandler } from './EditableTable'
export { DatePickerWrapper } from './DatePickerWrapper'
export { Table } from './Table'
export {
RegExpIsFloat,
rawTimezones,
timezoneOptions,
TimezoneSelect,
makeGroupColumn,
makeColumn,
makeColumnsPlanFact,
makeFilterTextMatch,
makeNumericRender,
makeNumericColumn,
makeNumericColumnOptions,
makeNumericColumnPlanFact,
makeNumericStartEnd,
makeNumericMinMax,
makeNumericAvgRange,
makeSelectColumn,
makeTagColumn,
makeTagInput,
makeTextColumn,
makeTimezoneColumn,
makeTimezoneRenderer,
} from './Columns'
export type {
DataType,
RenderMethod,
SorterMethod,
TagInputProps,
columnPropsOther,
} from './Columns'
export type { BaseTableColumn, TableColumns, TableContainer } from './Table'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
export const defaultPagination = {
defaultPageSize: 14,
showSizeChanger: true,
}
export const makeNumericRender = (fixed?: number) => (value: any, _: object): ReactNode => {
let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) {
val = (fixed ?? null) !== null
? (+value).toFixed(fixed)
: (+value).toPrecision(5)
}
return (
<div className={'text-align-r-container'}>
<span>{val}</span>
</div>
)
}
export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string): columnPropsOther => ({
editable: true,
initialValue: 0,
width: 100,
sorter: sorterKey ? makeNumericSorter(sorterKey) : undefined,
formItemRules: [
{
required: true,
message: 'Введите число',
pattern: RegExpIsFloat,
},
],
render: makeNumericRender(fixed),
})
/*
other - объект с дополнительными свойствами колонки
поддерживаются все базовые свойства из описания https://ant.design/components/table/#Column
плю дополнительные для колонок EditableTable: */
type columnPropsOther<T = any> = ColumnProps<T> & {
// редактируемая колонка
editable?: boolean
// react компонента редактора
input?: ReactNode
// значение может быть пустым
isRequired?: boolean
// css класс для <FormItem/>, если требуется
formItemClass?: string
// массив правил валидации значений https://ant.design/components/form/#Rule
formItemRules?: Rule[]
// дефолтное значение при добавлении новой строки
initialValue?: string | number
render?: (...attributes: any) => any
}
export const makeColumn = (title: string | ReactNode, key: string, other?: columnPropsOther) => ({
title: title,
key: key,
dataIndex: key,
...other,
})
export const makeColumnsPlanFact = (
title: string | ReactNode,
key: string | string[],
columsOther?: any | any[],
gruopOther?: any
) => {
let keyPlanLocal: string
let keyFactLocal: string
if (key instanceof Array) {
keyPlanLocal = key[0]
keyFactLocal = key[1]
} else {
keyPlanLocal = key + 'Plan'
keyFactLocal = key + 'Fact'
}
let columsOtherLocal : any[2]
if (columsOther instanceof Array)
columsOtherLocal = [columsOther[0], columsOther[1]]
else
columsOtherLocal = [columsOther, columsOther]
return {
title: title,
...gruopOther,
children: [
makeColumn('план', keyPlanLocal, columsOtherLocal[0]),
makeColumn('факт', keyFactLocal, columsOtherLocal[1]),
]
}
}
export const makeFilterTextMatch = (key: string | number) => (
(filterValue: string | number, dataItem: any) => dataItem[key] === filterValue
)
export const makeGroupColumn = (title: string, children: object[]) => ({ title, children })
export const makeTextColumn = (
title: string,
dataIndex: string,
filters: object[],
sorter?: (key: string) => any,
render?: any,
other?: any
) => ({
title: title,
dataIndex: dataIndex,
key: dataIndex,
filters: filters,
onFilter: filters ? makeFilterTextMatch(dataIndex) : null,
sorter: sorter ?? makeStringSorter(dataIndex),
render: render,
...other
})
export const makeNumericColumn = (
title: string,
dataIndex: string,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
other?: columnPropsOther
) => ({
title: title,
dataIndex: dataIndex,
key: dataIndex,
filters: filters,
onFilter: filterDelegate ? filterDelegate(dataIndex) : null,
sorter: makeNumericSorter(dataIndex),
width: width,
input: <InputNumber style={{ width: '100%' }}/>,
render: renderDelegate ?? makeNumericRender(),
align: 'right',
...other
})
export const makeNumericColumnPlanFact = (
title: string,
dataIndex: string,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string
) => makeGroupColumn(title, [
makeNumericColumn('п', dataIndex + 'Plan', filters, filterDelegate, renderDelegate, width),
makeNumericColumn('ф', dataIndex + 'Fact', filters, filterDelegate, renderDelegate, width),
])
export const makeNumericStartEnd = (
title: string,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
) => makeGroupColumn(title, [
makeNumericColumn('старт', dataIndex + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Start')),
makeNumericColumn('конец', dataIndex + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'End'))
])
export const makeNumericMinMax = (
title: string,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string,
) => makeGroupColumn(title, [
makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max')),
])
export const makeNumericAvgRange = (
title: string,
dataIndex: string,
fixed: number,
filters: object[],
filterDelegate: (key: string | number) => any,
renderDelegate: (_: any, row: object) => any,
width: string
) => makeGroupColumn(title, [
makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
makeNumericColumn('сред', dataIndex + 'Avg', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Avg')),
makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max'))
])
export const makeSelectColumn = <T extends unknown = string>(
title: string,
dataIndex: string,
options: DefaultOptionType[],
defaultValue?: T,
other?: columnPropsOther,
selectOther?: SelectProps<SelectValue>
) => makeColumn(title, dataIndex, {
...other,
input: <Select options={options} {...selectOther}/>,
render: (value) => {
const item = options?.find(option => option?.value === value)
return other?.render?.(item?.value) ?? item?.label ?? defaultValue ?? value ?? '--'
}
})
const makeTagInput = <T extends Record<string, any>>(value_key: string, label_key: string) => memo<{
options: T[],
value?: T[],
onChange?: (values: T[]) => void,
other?: SelectProps<SelectValue>,
}>(({ options, value, onChange, other }) => {
const [selectOptions, setSelectOptions] = useState<DefaultOptionType[]>([])
const [selectedValue, setSelectedValue] = useState<SelectValue>([])
useEffect(() => {
setSelectOptions(options.map((elm) => ({
value: String(elm[value_key]),
label: elm[label_key],
})))
}, [options])
useEffect(() => {
setSelectedValue(value?.map((elm) => String(elm[value_key])) ?? [])
}, [value])
const onSelectChange = (rawValues?: SelectValue) => {
let values: any[] = []
if (typeof rawValues === 'string')
values = rawValues.split(',')
else if (Array.isArray(rawValues))
values = rawValues
const objectValues: T[] = values.reduce((out: T[], value: string) => {
const res = options.find((option) => String(option[value_key]) === String(value))
if (res) out.push(res)
return out
}, [])
onChange?.(objectValues)
}
return (
<Select
{...other}
mode={'tags'}
options={selectOptions}
value={selectedValue}
onChange={onSelectChange}
/>
)
})
export const makeTagColumn = <T extends Record<string, any>>(
title: string,
dataIndex: string,
options: T[],
value_key: string,
label_key: string,
other?: columnPropsOther,
tagOther?: SelectProps<SelectValue>
) => {
const InputComponent = makeTagInput<T>(value_key, label_key)
return makeColumn(title, dataIndex, {
...other,
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-',
input: <InputComponent options={options} other={tagOther} />,
})
defaultPageSize: 14,
showSizeChanger: true,
}
type PaginationContainer = {
skip?: number
take?: number
count?: number
items?: any[] | null
skip?: number
take?: number
count?: number
items?: any[] | null
}
export const makePaginationObject = (сontainer: PaginationContainer, ...other: any) => ({
...other,
pageSize: сontainer.take,
total: сontainer.count ?? сontainer.items?.length ?? 0,
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
})
const rawTimezones = {
'Калининград': 2,
'Москва': 3,
'Самара': 4,
'Екатеринбург': 5,
'Омск': 6,
'Красноярск': 7,
'Новосибирск': 7,
'Иркутск': 8,
'Чита': 9,
'Владивосток': 10,
'Магадан': 11,
'Южно-Сахалинск': 11,
'Среднеколымск': 11,
'Анадырь': 12,
'Петропавловск-Камчатский': 12,
}
const timezoneOptions = Object
.entries(rawTimezones)
.sort((a, b) => a[1] - b[1])
.map(([id, hours]) => ({
label: `UTC${hours > 0 ? '+':''}${('0' + hours).slice(-2)} :: ${id}`,
value: id,
}))
export const TimezoneSelect = memo<SelectProps>(({ onChange, ...other }) => {
const [id, setId] = useState<keyof typeof rawTimezones | null>(null)
useEffect(() => onChange?.({
timezoneId: id,
hours: id ? rawTimezones[id] : 0,
isOverride: false,
}, []), [id, onChange])
return (<Select {...other} onChange={setId} value={id} />)
})
export const makeTimezoneRenderer = () => (timezone?: SimpleTimezoneDto) => {
if (!timezone) return 'UTC~?? :: Неизвестно'
const { hours, timezoneId } = timezone
return `UTC${hours && hours > 0 ? '+':''}${hours ? ('0' + hours).slice(-2) : '~??'} :: ${timezoneId ?? 'Неизвестно'}`
}
export const makeTimezoneColumn = (
title: string = 'Зона',
key: string = 'timezone',
defaultValue: any = null,
allowClear: boolean = true,
other?: columnPropsOther
) => makeColumn(title, key, {
width: 100,
editable: true,
render: makeTimezoneRenderer(),
input: (
<TimezoneSelect
allowClear={allowClear}
options={timezoneOptions}
defaultValue={defaultValue}
/>
),
...other
...other,
pageSize: сontainer.take,
total: сontainer.count ?? сontainer.items?.length ?? 0,
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
})

View File

@ -1,20 +1,25 @@
import { RawDate } from "@asb/utils"
import { isRawDate } from '@utils'
export const makeNumericSorter = (key: string) => (a: Record<string, number>, b: Record<string, number>) => Number(a[key]) - Number(b[key])
import { DataType } from './Columns'
export const makeStringSorter = (key: string) => (a: Record<string, string> | null | undefined, b: Record<string, string> | null | undefined) => {
export const makeNumericSorter = <T extends unknown>(key: keyof DataType<T>) =>
(a: DataType<T>, b: DataType<T>) => Number(a[key]) - Number(b[key])
export const makeStringSorter = <T extends unknown>(key: keyof DataType<T>) => (a?: DataType<T> | null, b?: DataType<T> | null) => {
if (!a && !b) return 0
if (!a) return 1
if (!b) return -1
return ('' + a[key]).localeCompare(b[key])
return String(a[key]).localeCompare(String(b[key]))
}
export const makeDateSorter = (key: string) => (a: Record<string, RawDate>, b: Record<string, RawDate>) => {
const date = new Date(a[key])
if (Number.isNaN(date.getTime()))
export const makeDateSorter = <T extends unknown>(key: keyof DataType<T>) => (a: DataType<T>, b: DataType<T>) => {
const adate = a[key]
const bdate = b[key]
if (!isRawDate(adate) || !isRawDate(bdate))
throw new Error('Date column contains not date formatted string(s)')
return date.getTime() - new Date(b[key]).getTime()
const date = new Date(adate)
return date.getTime() - new Date(bdate).getTime()
}