dinocollab-core 1.0.0
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/README.md +54 -0
- package/dist/_virtual/_rollupPluginBabelHelpers.js +431 -0
- package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/dist/assets/vector-404265a04f4f9c8be1f.webp +0 -0
- package/dist/node_modules/.pnpm/@rollup_plugin-typescript@1_d0d2002d9033600b6738d939bd598bc6/node_modules/tslib/tslib.es6.js +46 -0
- package/dist/node_modules/.pnpm/@rollup_plugin-typescript@1_d0d2002d9033600b6738d939bd598bc6/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/src/api-context/alert-global.js +151 -0
- package/dist/src/api-context/alert-global.js.map +1 -0
- package/dist/src/api-context/drawer-global.js +105 -0
- package/dist/src/api-context/drawer-global.js.map +1 -0
- package/dist/src/api-context/global-modal.js +87 -0
- package/dist/src/api-context/global-modal.js.map +1 -0
- package/dist/src/api-context/popover-global.js +102 -0
- package/dist/src/api-context/popover-global.js.map +1 -0
- package/dist/src/api-context/popover.js +86 -0
- package/dist/src/api-context/popover.js.map +1 -0
- package/dist/src/api-context/ui.units.js +21 -0
- package/dist/src/api-context/ui.units.js.map +1 -0
- package/dist/src/components/copy-to-clipboard.js +105 -0
- package/dist/src/components/copy-to-clipboard.js.map +1 -0
- package/dist/src/components/custom.breadcrumbs.js +61 -0
- package/dist/src/components/custom.breadcrumbs.js.map +1 -0
- package/dist/src/components/help-tooltip.js +91 -0
- package/dist/src/components/help-tooltip.js.map +1 -0
- package/dist/src/components/image-with-fallback.js +48 -0
- package/dist/src/components/image-with-fallback.js.map +1 -0
- package/dist/src/components/text-editor.js +117 -0
- package/dist/src/components/text-editor.js.map +1 -0
- package/dist/src/form/create.autocomplete.chips.js +218 -0
- package/dist/src/form/create.autocomplete.chips.js.map +1 -0
- package/dist/src/form/create.date-expired.js +201 -0
- package/dist/src/form/create.date-expired.js.map +1 -0
- package/dist/src/form/create.date-picker.js +125 -0
- package/dist/src/form/create.date-picker.js.map +1 -0
- package/dist/src/form/create.form-base.js +135 -0
- package/dist/src/form/create.form-base.js.map +1 -0
- package/dist/src/form/create.form-comfirm.js +119 -0
- package/dist/src/form/create.form-comfirm.js.map +1 -0
- package/dist/src/form/create.form-grid-layout.js +177 -0
- package/dist/src/form/create.form-grid-layout.js.map +1 -0
- package/dist/src/form/create.form-grid-layout.units.js +39 -0
- package/dist/src/form/create.form-grid-layout.units.js.map +1 -0
- package/dist/src/form/create.input-base.js +260 -0
- package/dist/src/form/create.input-base.js.map +1 -0
- package/dist/src/form/create.input.file.js +74 -0
- package/dist/src/form/create.input.file.js.map +1 -0
- package/dist/src/form/create.select-simple.js +104 -0
- package/dist/src/form/create.select-simple.js.map +1 -0
- package/dist/src/form/create.select-with-api.js +271 -0
- package/dist/src/form/create.select-with-api.js.map +1 -0
- package/dist/src/form/create.text-editor.js +156 -0
- package/dist/src/form/create.text-editor.js.map +1 -0
- package/dist/src/form/dino-form.js +42 -0
- package/dist/src/form/dino-form.js.map +1 -0
- package/dist/src/form/helper.js +157 -0
- package/dist/src/form/helper.js.map +1 -0
- package/dist/src/form/modal-wrapper.js +75 -0
- package/dist/src/form/modal-wrapper.js.map +1 -0
- package/dist/src/form/validator.js +186 -0
- package/dist/src/form/validator.js.map +1 -0
- package/dist/src/hooks/index.js +48 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/redux/create.hoc-lazy.js +67 -0
- package/dist/src/redux/create.hoc-lazy.js.map +1 -0
- package/dist/src/redux/dino.js +11 -0
- package/dist/src/redux/dino.js.map +1 -0
- package/dist/src/redux/types.js +9 -0
- package/dist/src/redux/types.js.map +1 -0
- package/dist/src/redux/ui.error-page.js +80 -0
- package/dist/src/redux/ui.error-page.js.map +1 -0
- package/dist/src/redux/vector-404.webp.js +4 -0
- package/dist/src/redux/vector-404.webp.js.map +1 -0
- package/dist/src/table/context.js +12 -0
- package/dist/src/table/context.js.map +1 -0
- package/dist/src/table/create.action-row.js +135 -0
- package/dist/src/table/create.action-row.js.map +1 -0
- package/dist/src/table/create.status-cell.js +49 -0
- package/dist/src/table/create.status-cell.js.map +1 -0
- package/dist/src/table/create.table.js +233 -0
- package/dist/src/table/create.table.js.map +1 -0
- package/dist/src/table/custom.filter-operators.js +89 -0
- package/dist/src/table/custom.filter-operators.js.map +1 -0
- package/dist/src/table/dino.js +129 -0
- package/dist/src/table/dino.js.map +1 -0
- package/dist/src/table/helpers.js +116 -0
- package/dist/src/table/helpers.js.map +1 -0
- package/dist/src/table/model-filter.js +23 -0
- package/dist/src/table/model-filter.js.map +1 -0
- package/dist/src/table/toolbar-pannel.js +134 -0
- package/dist/src/table/toolbar-pannel.js.map +1 -0
- package/dist/src/table/ui.buttons.js +60 -0
- package/dist/src/table/ui.buttons.js.map +1 -0
- package/dist/src/table/ui.units.js +201 -0
- package/dist/src/table/ui.units.js.map +1 -0
- package/dist/src/utils/dayjs-config.js +12 -0
- package/dist/src/utils/dayjs-config.js.map +1 -0
- package/dist/src/utils/helpers.js +197 -0
- package/dist/src/utils/helpers.js.map +1 -0
- package/dist/src/utils/json-object.js +38 -0
- package/dist/src/utils/json-object.js.map +1 -0
- package/dist/src/utils/query-param.js +172 -0
- package/dist/src/utils/query-param.js.map +1 -0
- package/package.json +52 -0
- package/rollup.config.js +39 -0
- package/src/@types/global.d.ts +5 -0
- package/src/api-context/alert-global.tsx +174 -0
- package/src/api-context/drawer-global.tsx +116 -0
- package/src/api-context/global-modal.tsx +109 -0
- package/src/api-context/index.ts +13 -0
- package/src/api-context/popover-global.tsx +107 -0
- package/src/api-context/popover.tsx +89 -0
- package/src/api-context/ui.units.tsx +10 -0
- package/src/components/copy-to-clipboard.tsx +86 -0
- package/src/components/custom.breadcrumbs.tsx +67 -0
- package/src/components/help-tooltip.tsx +75 -0
- package/src/components/image-with-fallback.tsx +51 -0
- package/src/components/index.tsx +1 -0
- package/src/components/input-debounce-timer.tsx +138 -0
- package/src/components/loading-buttons.tsx +35 -0
- package/src/components/text-editor.preview.tsx +30 -0
- package/src/components/text-editor.tsx +125 -0
- package/src/form/README.md +55 -0
- package/src/form/create.autocomplete.chips.tsx +199 -0
- package/src/form/create.date-expired.tsx +195 -0
- package/src/form/create.date-picker.tsx +122 -0
- package/src/form/create.form-base.tsx +102 -0
- package/src/form/create.form-comfirm.tsx +83 -0
- package/src/form/create.form-grid-layout.tsx +170 -0
- package/src/form/create.form-grid-layout.units.tsx +37 -0
- package/src/form/create.input-base.tsx +222 -0
- package/src/form/create.input.file.tsx +76 -0
- package/src/form/create.select-simple.tsx +101 -0
- package/src/form/create.select-with-api.tsx +213 -0
- package/src/form/create.text-editor.tsx +161 -0
- package/src/form/dino-form.tsx +40 -0
- package/src/form/helper.ts +132 -0
- package/src/form/index.ts +12 -0
- package/src/form/modal-wrapper.tsx +75 -0
- package/src/form/types.ts +16 -0
- package/src/form/validator.ts +202 -0
- package/src/hooks/index.ts +44 -0
- package/src/index.ts +7 -0
- package/src/lab/create.autocomplete.simple.tsx +57 -0
- package/src/lab/create.dino-store.ts +59 -0
- package/src/lab/create.multi-select-dropdown.tsx +189 -0
- package/src/lab/create.select-mul-with-api/index.tsx +271 -0
- package/src/lab/create.select-mul-with-api/table-custom.tsx +194 -0
- package/src/lab/create.select-mul-with-api/types.ts +26 -0
- package/src/lab/create.select-mul-with-api/ui.units.tsx +163 -0
- package/src/lab/filter-bar/base.tsx +162 -0
- package/src/lab/filter-bar/create.filter-bar.tsx +190 -0
- package/src/lab/filter-bar/create.filter-menu.tsx +156 -0
- package/src/lab/filter-bar/create.filter-panel.tsx +95 -0
- package/src/lab/filter-bar/create.filtered.tsx +41 -0
- package/src/lab/filter-bar/create.sort-menu.tsx +43 -0
- package/src/lab/filter-bar/demo.tsx +50 -0
- package/src/lab/filter-bar/index.ts +6 -0
- package/src/lab/filter-bar/types.ts +105 -0
- package/src/lab/filter-bar/ui.units.tsx +70 -0
- package/src/lab/grafana-dashboard/configs.ts +43 -0
- package/src/lab/grafana-dashboard/date-time-range/absolute-time-rage.tsx +137 -0
- package/src/lab/grafana-dashboard/date-time-range/helpers.ts +126 -0
- package/src/lab/grafana-dashboard/date-time-range/index.tsx +62 -0
- package/src/lab/grafana-dashboard/date-time-range/menu-wrap.tsx +101 -0
- package/src/lab/grafana-dashboard/date-time-range/quick-ranges.tsx +161 -0
- package/src/lab/grafana-dashboard/date-time-range/types.ts +9 -0
- package/src/lab/grafana-dashboard/date-time-range/units.tsx +18 -0
- package/src/lab/grafana-dashboard/helper.ts +25 -0
- package/src/lab/grafana-dashboard/hooks.tsx +79 -0
- package/src/lab/grafana-dashboard/icons.tsx +67 -0
- package/src/lab/grafana-dashboard/index.tsx +120 -0
- package/src/lab/grafana-dashboard/top-bar.tsx +62 -0
- package/src/lab/grafana-dashboard/top-bar.types.ts +5 -0
- package/src/lab/grafana-dashboard/types.ts +8 -0
- package/src/lab/media-player.core1.tsx +273 -0
- package/src/lab/media-player.muted.tsx +62 -0
- package/src/lab/media-player.units.ts +80 -0
- package/src/lab/table-grid/create.table-grid.tsx +183 -0
- package/src/lab/table-grid/demo.tsx +53 -0
- package/src/lab/table-grid/dino.tsx +8 -0
- package/src/lab/table-grid/helpers.tsx +11 -0
- package/src/lab/table-grid/index.ts +3 -0
- package/src/lab/table-grid/item-actions.tsx +138 -0
- package/src/lab/table-grid/toolbar-pannel.tsx +98 -0
- package/src/lab/table-grid/types.ts +68 -0
- package/src/redux/create.hoc-lazy.tsx +80 -0
- package/src/redux/dino.ts +9 -0
- package/src/redux/index.ts +6 -0
- package/src/redux/types.ts +27 -0
- package/src/redux/ui.error-page.tsx +62 -0
- package/src/redux/ui.units.tsx +41 -0
- package/src/redux/vector-404.webp +0 -0
- package/src/table/context.tsx +16 -0
- package/src/table/create.action-row.tsx +91 -0
- package/src/table/create.status-cell.tsx +51 -0
- package/src/table/create.table.tsx +239 -0
- package/src/table/custom.filter-operators.ts +94 -0
- package/src/table/dino.tsx +120 -0
- package/src/table/helpers.ts +94 -0
- package/src/table/index.ts +13 -0
- package/src/table/model-filter.ts +43 -0
- package/src/table/toolbar-pannel.tsx +106 -0
- package/src/table/types.ts +50 -0
- package/src/table/ui.buttons.tsx +54 -0
- package/src/table/ui.units.tsx +189 -0
- package/src/utils/dayjs-config.ts +13 -0
- package/src/utils/helpers.ts +171 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/json-object.ts +29 -0
- package/src/utils/mfe-events.tsx +34 -0
- package/src/utils/query-param.ts +129 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { FC } from 'react'
|
|
2
|
+
import { Box, Fade, IconButton, IconButtonProps, styled, Tooltip, Typography } from '@mui/material'
|
|
3
|
+
import CloseIcon from '@mui/icons-material/Close'
|
|
4
|
+
import HelpTooltip from '../../components/help-tooltip'
|
|
5
|
+
|
|
6
|
+
interface ButtonClearProps {
|
|
7
|
+
visibled?: boolean
|
|
8
|
+
onClick: IconButtonProps['onClick']
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ButtonClear: FC<ButtonClearProps> = (props) => (
|
|
12
|
+
<WrapIcon>
|
|
13
|
+
<Tooltip title='Remove filter'>
|
|
14
|
+
<Fade in={props.visibled} unmountOnExit>
|
|
15
|
+
<IconButton size='small' onClick={props.onClick}>
|
|
16
|
+
<CloseIcon fontSize='small' />
|
|
17
|
+
</IconButton>
|
|
18
|
+
</Fade>
|
|
19
|
+
</Tooltip>
|
|
20
|
+
</WrapIcon>
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export interface FilterHelpNotesProps {
|
|
24
|
+
title?: string
|
|
25
|
+
items?: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const FilterHelpNotes: FC<FilterHelpNotesProps> = (props) => {
|
|
29
|
+
if (!props.items || props.items.length < 1) return <></>
|
|
30
|
+
return (
|
|
31
|
+
<WrapIcon>
|
|
32
|
+
<HelpTooltip small title={props.title ?? 'The search includes'}>
|
|
33
|
+
<WrapList>
|
|
34
|
+
{props.items.map((item, index) => (
|
|
35
|
+
<Typography key={index} component='li' variant='body2'>
|
|
36
|
+
{item}
|
|
37
|
+
</Typography>
|
|
38
|
+
))}
|
|
39
|
+
</WrapList>
|
|
40
|
+
</HelpTooltip>
|
|
41
|
+
</WrapIcon>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const WrapIcon = styled(Box)({
|
|
46
|
+
display: 'flex',
|
|
47
|
+
flex: '0 0 auto',
|
|
48
|
+
width: '40px',
|
|
49
|
+
height: '40px',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
alignItems: 'center'
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const WrapList = styled('ul')({
|
|
55
|
+
paddingLeft: '1.7rem',
|
|
56
|
+
marginBottom: 0,
|
|
57
|
+
li: {
|
|
58
|
+
position: 'relative',
|
|
59
|
+
textAlign: 'justify',
|
|
60
|
+
'&::before': {
|
|
61
|
+
content: '"►"',
|
|
62
|
+
display: 'block',
|
|
63
|
+
position: 'absolute',
|
|
64
|
+
top: '50%',
|
|
65
|
+
right: 'calc(100% + 6px)',
|
|
66
|
+
transform: 'translateY(-50%)',
|
|
67
|
+
fontSize: '0.9em'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
import { ITopBarInfo } from './top-bar.types'
|
|
3
|
+
import { GetTopBarInfoQueryParam } from './helper'
|
|
4
|
+
import { DTRHelper, IQuickRangeConfig } from './date-time-range'
|
|
5
|
+
|
|
6
|
+
export const QuickRangeConfigs: IQuickRangeConfig[] = [
|
|
7
|
+
{ title: 'Last 7 days', ...DTRHelper.dayRange(-7) },
|
|
8
|
+
{ title: 'Last 28 days', ...DTRHelper.dayRange(-28) },
|
|
9
|
+
{ title: 'Last 90 days', ...DTRHelper.dayRange(-90) },
|
|
10
|
+
{ title: 'Last 365 days', ...DTRHelper.dayRange(-365) },
|
|
11
|
+
{ title: DTRHelper.yearString(0), ...DTRHelper.yearRange(0), lineAbove: true },
|
|
12
|
+
{ title: DTRHelper.yearString(-1), ...DTRHelper.yearRange(-1) },
|
|
13
|
+
{ title: DTRHelper.monthString(0), ...DTRHelper.monthRange(0), lineAbove: true },
|
|
14
|
+
{ title: DTRHelper.monthString(1), ...DTRHelper.monthRange(-1) },
|
|
15
|
+
{ title: DTRHelper.monthString(2), ...DTRHelper.monthRange(-2) }
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const GetDefaultSelected = (): IQuickRangeConfig | undefined => {
|
|
19
|
+
try {
|
|
20
|
+
const initial = GetTopBarInfoQueryParam()
|
|
21
|
+
|
|
22
|
+
const index = QuickRangeConfigs.findIndex((x) => x.from === initial.from && x.to === initial.to)
|
|
23
|
+
if (index > -1) return QuickRangeConfigs[index]
|
|
24
|
+
|
|
25
|
+
const from = initial.from ? dayjs(new Date(parseInt(initial.from))) : undefined
|
|
26
|
+
const to = initial.to ? dayjs(new Date(parseInt(initial.to))) : undefined
|
|
27
|
+
|
|
28
|
+
if (!from || !to) return QuickRangeConfigs[1]
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
title: DTRHelper.getTitle(initial.from, initial.to),
|
|
32
|
+
from: from?.toDate().getTime().toString(),
|
|
33
|
+
to: to?.toDate().getTime().toString()
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.log(error)
|
|
37
|
+
return QuickRangeConfigs[1]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const DefaultTopBarInfo: ITopBarInfo = {
|
|
42
|
+
QuickRange: GetDefaultSelected()
|
|
43
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
|
3
|
+
import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers'
|
|
4
|
+
import { alpha, Box, Button, Collapse, Typography } from '@mui/material'
|
|
5
|
+
import dayjs from 'dayjs'
|
|
6
|
+
import { DTRHelper } from './helpers'
|
|
7
|
+
import { IQuickRangeConfig } from './types'
|
|
8
|
+
import CreateFormBase from '../../../form/create.form-base'
|
|
9
|
+
import { FormValidator, getErrorMessage, SingleRuleValidate } from '../../../form'
|
|
10
|
+
|
|
11
|
+
const FormatString = 'MM/DD/YYYY'
|
|
12
|
+
|
|
13
|
+
const ParseDayjs = (value?: string) => {
|
|
14
|
+
return value ? dayjs(value, FormatString) : undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const FormBaseInstance = CreateFormBase<IQuickRangeConfig>({
|
|
18
|
+
validate: new FormValidator({
|
|
19
|
+
from: {
|
|
20
|
+
Rules: [
|
|
21
|
+
{ rule: SingleRuleValidate.Required },
|
|
22
|
+
{
|
|
23
|
+
rule: SingleRuleValidate.Custom,
|
|
24
|
+
message: 'DateFrom must precede DateTo',
|
|
25
|
+
Value: (value, model) => {
|
|
26
|
+
const from = ParseDayjs(model.from)
|
|
27
|
+
const to = ParseDayjs(model.to)
|
|
28
|
+
return !!to?.isBefore(from)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
to: { Rules: [{ rule: SingleRuleValidate.Required }] }
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
interface IProps {
|
|
38
|
+
data?: IQuickRangeConfig
|
|
39
|
+
onChangeRange?: (value: IQuickRangeConfig) => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default class AbsoluteTimeRage extends Component<IProps> {
|
|
43
|
+
render() {
|
|
44
|
+
return (
|
|
45
|
+
<Box sx={{ flex: 1 }}>
|
|
46
|
+
<FormBaseInstance.Form onSubmit={this.handleApply}>
|
|
47
|
+
<Typography variant='subtitle1' sx={{ fontWeight: 600, color: alpha('#000000', 0.7) }}>
|
|
48
|
+
Absolute time range
|
|
49
|
+
</Typography>
|
|
50
|
+
{this.dateTimeRangePickerRanges()}
|
|
51
|
+
{FormBaseInstance.contextMapping((context) => {
|
|
52
|
+
const eMessage = getErrorMessage(context.messageErrors, 'from')
|
|
53
|
+
const isDisabled = Object.keys(context.messageErrors).length > 0
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<Collapse in={!!eMessage.error} unmountOnExit>
|
|
57
|
+
<Box sx={{ pt: '6px' }}>
|
|
58
|
+
<Typography variant='caption' sx={{ color: '#d32f2f', fontSize: '0.65rem' }}>
|
|
59
|
+
{eMessage.message}
|
|
60
|
+
</Typography>
|
|
61
|
+
</Box>
|
|
62
|
+
</Collapse>
|
|
63
|
+
<Box sx={{ marginTop: '9px' }}>
|
|
64
|
+
<Button
|
|
65
|
+
disabled={isDisabled}
|
|
66
|
+
size='small'
|
|
67
|
+
variant='contained'
|
|
68
|
+
color='primary'
|
|
69
|
+
type='submit'
|
|
70
|
+
sx={{ textTransform: 'unset !important' }}
|
|
71
|
+
>
|
|
72
|
+
Apply time range
|
|
73
|
+
</Button>
|
|
74
|
+
</Box>
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
})}
|
|
78
|
+
</FormBaseInstance.Form>
|
|
79
|
+
</Box>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
date1Ref = React.createRef<HTMLInputElement>()
|
|
83
|
+
date2Ref = React.createRef<HTMLInputElement>()
|
|
84
|
+
dateTimeRangePickerRanges() {
|
|
85
|
+
return FormBaseInstance.contextMapping((context) => {
|
|
86
|
+
const from = DTRHelper.parseDayJS(this.props.data?.from)
|
|
87
|
+
const to = DTRHelper.parseDayJS(this.props.data?.to)
|
|
88
|
+
const eMessage = getErrorMessage(context.messageErrors, 'from')
|
|
89
|
+
return (
|
|
90
|
+
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
91
|
+
<Typography variant='caption' sx={{ fontWeight: 600, color: alpha('#000000', 0.7), mt: '6px', display: 'block' }}>
|
|
92
|
+
from:
|
|
93
|
+
</Typography>
|
|
94
|
+
<DatePicker
|
|
95
|
+
name='from'
|
|
96
|
+
key={this.props.data?.from ? this.props.data.from : 'from'}
|
|
97
|
+
inputRef={this.date1Ref}
|
|
98
|
+
defaultValue={from}
|
|
99
|
+
maxDate={dayjs()}
|
|
100
|
+
slotProps={{ textField: { size: 'small', placeholder: FormatString, error: eMessage.error } }}
|
|
101
|
+
onChange={(d) => {
|
|
102
|
+
if (this.date1Ref.current && d) {
|
|
103
|
+
this.date1Ref.current.value = d.format(FormatString)
|
|
104
|
+
context.onBlur('from')
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
<Typography variant='caption' sx={{ fontWeight: 600, color: alpha('#000000', 0.7), mt: '6px', display: 'block' }}>
|
|
109
|
+
to:
|
|
110
|
+
</Typography>
|
|
111
|
+
<DatePicker
|
|
112
|
+
name='to'
|
|
113
|
+
key={this.props.data?.to ? this.props.data.to : 'to'}
|
|
114
|
+
inputRef={this.date2Ref}
|
|
115
|
+
defaultValue={to}
|
|
116
|
+
maxDate={dayjs()}
|
|
117
|
+
slotProps={{ textField: { size: 'small', placeholder: FormatString, error: eMessage.error } }}
|
|
118
|
+
onChange={(d) => {
|
|
119
|
+
if (this.date2Ref.current && d) {
|
|
120
|
+
this.date2Ref.current.value = d.format(FormatString)
|
|
121
|
+
context.onBlur('to')
|
|
122
|
+
context.onBlur('from')
|
|
123
|
+
}
|
|
124
|
+
}}
|
|
125
|
+
/>
|
|
126
|
+
</LocalizationProvider>
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
handleApply = async (value: Partial<IQuickRangeConfig>) => {
|
|
132
|
+
const from = ParseDayjs(value.from)?.startOf('day').toDate().getTime().toString()
|
|
133
|
+
const to = ParseDayjs(value.to)?.endOf('day').toDate().getTime().toString()
|
|
134
|
+
const item: IQuickRangeConfig = { from, to, title: DTRHelper.getTitle(from, to) }
|
|
135
|
+
this.props.onChangeRange && this.props.onChangeRange(item)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
import { IQuickRangeConfig, IRange } from './types'
|
|
3
|
+
|
|
4
|
+
type TDateTimeKetType = /** 'minute' | 'hour' | */ 'day' | 'month' | 'year'
|
|
5
|
+
export const MapDateTimeKey = (count: number, type?: TDateTimeKetType) => {
|
|
6
|
+
if (count == 0) return 'now'
|
|
7
|
+
const value = count.toString()
|
|
8
|
+
switch (type) {
|
|
9
|
+
// case 'minute':
|
|
10
|
+
// return `now${value}m`
|
|
11
|
+
// case 'hour':
|
|
12
|
+
// return `now${value}h`
|
|
13
|
+
case 'day':
|
|
14
|
+
return `now${value}d`
|
|
15
|
+
case 'month':
|
|
16
|
+
return `now${value}M`
|
|
17
|
+
case 'year':
|
|
18
|
+
return `now${value}y`
|
|
19
|
+
default:
|
|
20
|
+
break
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DTRHelperBase {
|
|
25
|
+
formatString = 'MM/DD/YYYY'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* getTitle generates a formatted date range string based on the input dates.
|
|
29
|
+
* @param from - Start date as a string in milliseconds.
|
|
30
|
+
* @param to - End date as a string in milliseconds.
|
|
31
|
+
* @param quickRanges - quickRanges as a ```QuickRangeConfig[]```.
|
|
32
|
+
* @returns Formatted date range or quick range title or fallback string for invalid/undefined values.
|
|
33
|
+
*/
|
|
34
|
+
getTitle = (from?: string, to?: string, quickRanges?: IQuickRangeConfig[]) => {
|
|
35
|
+
const quickRangeItem = quickRanges?.find((x) => x.from === from && x.to === to)
|
|
36
|
+
if (!!quickRangeItem) {
|
|
37
|
+
return quickRangeItem.title
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fromDate = this.parseDayJS(from)?.format(this.formatString)
|
|
41
|
+
const toDate = this.parseDayJS(to)?.format(this.formatString)
|
|
42
|
+
if (fromDate && toDate) {
|
|
43
|
+
return `${fromDate} - ${toDate}`
|
|
44
|
+
} else if (!fromDate && toDate) {
|
|
45
|
+
return `Until ${toDate}`
|
|
46
|
+
} else if (fromDate && !toDate) {
|
|
47
|
+
return `Starting from ${fromDate}`
|
|
48
|
+
} else {
|
|
49
|
+
return 'Custom'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* getTime converts a formatted date string into a timestamp in milliseconds.
|
|
55
|
+
* Returns the timestamp as a string or NaN if the date is invalid.
|
|
56
|
+
* @param value - Date string in the format 'MM/DD/YYYY'.
|
|
57
|
+
* @returns Timestamp in milliseconds as a string.
|
|
58
|
+
*/
|
|
59
|
+
getTime = (value: string, options?: (d: dayjs.Dayjs) => dayjs.Dayjs) => {
|
|
60
|
+
let date = dayjs(value, this.formatString)
|
|
61
|
+
if (!!options) {
|
|
62
|
+
date = options(date)
|
|
63
|
+
}
|
|
64
|
+
return date.isValid() ? date.toDate().getTime().toString() : 'NaN' // Return 'NaN' if the date is invalid
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* parseDayJS parses a date in milliseconds into a dayjs object.
|
|
69
|
+
* Returns undefined if the date is invalid or if parsing fails.
|
|
70
|
+
* @param value - String representing a date in milliseconds or other formats.
|
|
71
|
+
* @returns A valid dayjs object or undefined if invalid.
|
|
72
|
+
*/
|
|
73
|
+
parseDayJS = (value?: string) => {
|
|
74
|
+
if (!value || isNaN(parseInt(value))) return undefined // Return undefined for non-numeric or empty values
|
|
75
|
+
const parsedDate = dayjs(new Date(parseInt(value)))
|
|
76
|
+
return parsedDate.isValid() ? parsedDate : undefined // Return undefined if the date is invalid
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dayRange = (range: number = 0): Required<IRange> => {
|
|
80
|
+
const currentDate = dayjs()
|
|
81
|
+
const targetDay = currentDate.subtract(Math.abs(range) - 1, 'day')
|
|
82
|
+
return {
|
|
83
|
+
from: targetDay.startOf('day').toDate().getTime().toString(),
|
|
84
|
+
to: currentDate.endOf('day').toDate().getTime().toString()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
monthRange = (range: number = 0): Required<IRange> => {
|
|
89
|
+
const currentDate = dayjs()
|
|
90
|
+
const targetMonth = currentDate.subtract(Math.abs(range), 'month')
|
|
91
|
+
let to = targetMonth.endOf('month').toDate().getTime().toString()
|
|
92
|
+
if (currentDate.isSame(targetMonth, 'month')) {
|
|
93
|
+
to = currentDate.endOf('day').toDate().getTime().toString()
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
from: targetMonth.startOf('month').toDate().getTime().toString(),
|
|
97
|
+
to
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
monthString = (range: number = 0) => {
|
|
102
|
+
const currentDate = dayjs()
|
|
103
|
+
const targetYear = currentDate.subtract(Math.abs(range), 'month')
|
|
104
|
+
if (currentDate.year() === targetYear.year()) return targetYear.format('MMMM')
|
|
105
|
+
return targetYear.format('MMMM YYYY')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
yearRange = (range: number = 0): Required<IRange> => {
|
|
109
|
+
const currentDate = dayjs()
|
|
110
|
+
const targetYear = currentDate.subtract(Math.abs(range), 'years')
|
|
111
|
+
let to = targetYear.endOf('year')
|
|
112
|
+
if (currentDate.year() === targetYear.year()) {
|
|
113
|
+
to = currentDate.endOf('day')
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
from: targetYear.startOf('year').toDate().getTime().toString(),
|
|
117
|
+
to: to.toDate().getTime().toString()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
yearString = (range: number = 0) => {
|
|
122
|
+
const targetYear = dayjs().subtract(Math.abs(range), 'years')
|
|
123
|
+
return targetYear.year().toString()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export const DTRHelper = new DTRHelperBase()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import { Box, Divider, Stack, styled } from '@mui/material'
|
|
3
|
+
import { DTRHelper } from './helpers'
|
|
4
|
+
import { IQuickRangeConfig } from './types'
|
|
5
|
+
import MenuWrap, { IMenuWrap } from './menu-wrap'
|
|
6
|
+
import { UTCTime } from './units'
|
|
7
|
+
import QuickRanges from './quick-ranges'
|
|
8
|
+
import AbsoluteTimeRage from './absolute-time-rage'
|
|
9
|
+
|
|
10
|
+
interface IProps {
|
|
11
|
+
data: IQuickRangeConfig[]
|
|
12
|
+
defaultSelected?: IQuickRangeConfig
|
|
13
|
+
onChange?: (value: IQuickRangeConfig) => void
|
|
14
|
+
}
|
|
15
|
+
interface IState {
|
|
16
|
+
selected?: IQuickRangeConfig
|
|
17
|
+
}
|
|
18
|
+
export default class DateTimeRange extends Component<IProps, IState> {
|
|
19
|
+
configs: IQuickRangeConfig[]
|
|
20
|
+
constructor(props: IProps) {
|
|
21
|
+
super(props)
|
|
22
|
+
this.configs = props.data
|
|
23
|
+
this.state = { selected: props.defaultSelected }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
refMenuWrap: IMenuWrap | null = null
|
|
27
|
+
render() {
|
|
28
|
+
const obj = this.getTitle()
|
|
29
|
+
return (
|
|
30
|
+
<MenuWrap title={obj.text} subTitle={obj.sub} ref={(ref) => (this.refMenuWrap = ref)}>
|
|
31
|
+
<Wrap>
|
|
32
|
+
<Box sx={{ display: 'flex', alignItems: 'stretch', flex: 1 }}>
|
|
33
|
+
<AbsoluteTimeRage data={this.state.selected} onChangeRange={this.handleSelectItem} />
|
|
34
|
+
<Divider flexItem orientation='vertical' sx={{ m: '0 6px 3px' }} />
|
|
35
|
+
<QuickRanges data={this.configs} onSelect={this.handleSelectItem} selectedValue={this.state.selected} />
|
|
36
|
+
</Box>
|
|
37
|
+
<Divider />
|
|
38
|
+
<UTCTime />
|
|
39
|
+
</Wrap>
|
|
40
|
+
</MenuWrap>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
handleSelectItem = (item: IQuickRangeConfig) => {
|
|
44
|
+
this.props.onChange && this.props.onChange(item)
|
|
45
|
+
this.setState({ selected: item })
|
|
46
|
+
this.refMenuWrap?.close()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getTitle = () => ({
|
|
50
|
+
text: DTRHelper.getTitle(this.state.selected?.from, this.state.selected?.to, this.props.data),
|
|
51
|
+
sub: DTRHelper.getTitle(this.state.selected?.from, this.state.selected?.to)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const Wrap = styled(Stack)({
|
|
56
|
+
padding: '9px 9px 0',
|
|
57
|
+
height: '100%',
|
|
58
|
+
overflow: 'hidden'
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export * from './types'
|
|
62
|
+
export * from './helpers'
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { Component, PropsWithChildren } from 'react'
|
|
2
|
+
import { Button, Menu, SxProps, Theme, Tooltip, styled } from '@mui/material'
|
|
3
|
+
import AccessTimeIcon from '@mui/icons-material/AccessTime'
|
|
4
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
5
|
+
|
|
6
|
+
interface IProps {
|
|
7
|
+
title?: string
|
|
8
|
+
subTitle?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface IState {
|
|
12
|
+
anchorEl: null | HTMLElement
|
|
13
|
+
}
|
|
14
|
+
export interface IMenuWrap {
|
|
15
|
+
close: () => void
|
|
16
|
+
}
|
|
17
|
+
export default class MenuWrap extends Component<PropsWithChildren<IProps>, IState> implements IMenuWrap {
|
|
18
|
+
constructor(props: IProps) {
|
|
19
|
+
super(props)
|
|
20
|
+
this.state = { anchorEl: null }
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
const { anchorEl } = this.state
|
|
24
|
+
const isOpen = Boolean(anchorEl)
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
{this.renderTooltip(
|
|
29
|
+
<CustomButton
|
|
30
|
+
variant='text'
|
|
31
|
+
color='inherit'
|
|
32
|
+
onClick={this.onOpen}
|
|
33
|
+
startIcon={<AccessTimeIcon />}
|
|
34
|
+
endIcon={<ExpandMoreIcon sx={{ transition: 'linear 0.1s', transform: `rotate(${isOpen ? -180 : 0}deg)` }} />}
|
|
35
|
+
sx={isOpen ? { backgroundColor: 'rgba(0, 0, 0, 0.04)' } : {}}
|
|
36
|
+
>
|
|
37
|
+
{this.props.title || 'Date time range'}
|
|
38
|
+
</CustomButton>
|
|
39
|
+
)}
|
|
40
|
+
<Menu
|
|
41
|
+
keepMounted
|
|
42
|
+
anchorEl={anchorEl}
|
|
43
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
|
44
|
+
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
|
45
|
+
slotProps={{ paper: { elevation: 0, sx: sxPaperProps } }}
|
|
46
|
+
open={isOpen}
|
|
47
|
+
onClose={this.close}
|
|
48
|
+
MenuListProps={{ component: 'div', sx: { height: '100%', padding: 0 } }}
|
|
49
|
+
>
|
|
50
|
+
{this.props.children}
|
|
51
|
+
</Menu>
|
|
52
|
+
</>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
renderTooltip = (child: JSX.Element) => {
|
|
57
|
+
const { title, subTitle } = this.props
|
|
58
|
+
const isTooltip = !!subTitle && title !== subTitle
|
|
59
|
+
if (!isTooltip) return child
|
|
60
|
+
return (
|
|
61
|
+
<Tooltip title={this.props.subTitle} placement='left' arrow>
|
|
62
|
+
{child}
|
|
63
|
+
</Tooltip>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
onOpen = (event: React.MouseEvent<HTMLElement>) => this.setState({ ...this.state, anchorEl: event.currentTarget })
|
|
68
|
+
close = () => this.setState({ ...this.state, anchorEl: null })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const sxPaperProps: SxProps<Theme> = {
|
|
72
|
+
overflow: 'visible',
|
|
73
|
+
filter: 'drop-shadow(0px 2px 8px rgba(99, 99, 99, 0.2))',
|
|
74
|
+
marginTop: '10px',
|
|
75
|
+
width: 'calc(100vw - 24px)',
|
|
76
|
+
maxWidth: '550px',
|
|
77
|
+
height: 'calc(100vh - 24px)',
|
|
78
|
+
maxHeight: '420px',
|
|
79
|
+
'&:before': {
|
|
80
|
+
content: '""',
|
|
81
|
+
display: 'block',
|
|
82
|
+
position: 'absolute',
|
|
83
|
+
top: 0,
|
|
84
|
+
right: 14,
|
|
85
|
+
width: 10,
|
|
86
|
+
height: 10,
|
|
87
|
+
backgroundColor: 'background.paper',
|
|
88
|
+
transform: 'translateY(-50%) rotate(45deg)',
|
|
89
|
+
zIndex: 0
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const CustomButton = styled(Button)({
|
|
94
|
+
textTransform: 'unset',
|
|
95
|
+
fontWeight: 600,
|
|
96
|
+
gap: '6px',
|
|
97
|
+
paddingLeft: '18px',
|
|
98
|
+
paddingRight: '18px',
|
|
99
|
+
minWidth: '220px',
|
|
100
|
+
justifyContent: 'space-between'
|
|
101
|
+
})
|