All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Introduced a new timeline view in the ObjectTable component, allowing users to visualize data over time. - Updated view mode utilities to include timeline-specific settings for start and end dates, and color properties. - Enhanced ObjectTableViewButton to manage timeline settings and integrate timeline icon. - Added CSS styles for timeline components to improve layout and visual feedback. - Refactored StateTag component to utilize a utility function for state representation, improving maintainability.
96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
import dayjs from 'dayjs'
|
|
import { getPropertyValue } from '../../../database/ObjectModels'
|
|
|
|
const toTimestamp = (value) => {
|
|
if (value == null || value === '') return null
|
|
const date = dayjs(value)
|
|
return date.isValid() ? date.valueOf() : null
|
|
}
|
|
|
|
export const getTimelineDates = (values = []) => {
|
|
const timestamps = []
|
|
for (const value of values) {
|
|
const timestamp = toTimestamp(value)
|
|
if (timestamp != null) timestamps.push(timestamp)
|
|
}
|
|
return timestamps
|
|
}
|
|
|
|
export const getTimelineRangeFromValues = (startValues = [], endValues = []) => {
|
|
const timestamps = [
|
|
...getTimelineDates(startValues),
|
|
...getTimelineDates(endValues)
|
|
]
|
|
if (timestamps.length === 0) return null
|
|
|
|
const minimum = Math.min(...timestamps)
|
|
const maximum = Math.max(...timestamps)
|
|
const start = dayjs(minimum).startOf('day').subtract(1, 'day')
|
|
const end = dayjs(maximum).startOf('day').add(2, 'day')
|
|
|
|
return {
|
|
start: start.valueOf(),
|
|
end: end.valueOf()
|
|
}
|
|
}
|
|
|
|
export const getTimelineRange = (records, startDate, endDate) => {
|
|
const startValues = []
|
|
const endValues = []
|
|
|
|
records.forEach((record) => {
|
|
if (record?.isSkeleton) return
|
|
if (startDate) startValues.push(getPropertyValue(record, startDate))
|
|
if (endDate) endValues.push(getPropertyValue(record, endDate))
|
|
})
|
|
|
|
return getTimelineRangeFromValues(startValues, endValues)
|
|
}
|
|
|
|
export const getTimelineTicks = (rangeStart, rangeEnd) => {
|
|
if (rangeStart == null || rangeEnd == null) return []
|
|
|
|
const start = dayjs(rangeStart).startOf('day')
|
|
const end = dayjs(rangeEnd).startOf('day')
|
|
const dayCount = Math.max(1, end.diff(start, 'day'))
|
|
const lastDay = end.subtract(1, 'day')
|
|
const includeYear = start.year() !== lastDay.year() || start.year() !== end.year()
|
|
const format = includeYear ? 'MMM D YYYY' : 'MMM D'
|
|
|
|
return Array.from({ length: dayCount + 1 }, (_, index) => {
|
|
const value = start.add(index, 'day')
|
|
return {
|
|
value: value.valueOf(),
|
|
label: value.format(format)
|
|
}
|
|
})
|
|
}
|
|
|
|
export const getTimelineItemPosition = (
|
|
startValue,
|
|
endValue,
|
|
rangeStart,
|
|
rangeEnd
|
|
) => {
|
|
if (startValue == null || startValue === '') return null
|
|
const start = dayjs(startValue)
|
|
const rangeStartMs = dayjs(rangeStart).valueOf()
|
|
const rangeEndMs = dayjs(rangeEnd).valueOf()
|
|
const duration = rangeEndMs - rangeStartMs
|
|
|
|
if (!start.isValid() || duration <= 0) return null
|
|
|
|
const startMs = Math.min(rangeEndMs, Math.max(rangeStartMs, start.valueOf()))
|
|
const end = endValue == null || endValue === '' ? null : dayjs(endValue)
|
|
const hasValidEnd = end?.isValid() && end.valueOf() > startMs
|
|
const endMs = hasValidEnd
|
|
? Math.min(rangeEndMs, Math.max(startMs, end.valueOf()))
|
|
: startMs
|
|
|
|
return {
|
|
left: ((startMs - rangeStartMs) / duration) * 100,
|
|
width: ((endMs - startMs) / duration) * 100,
|
|
isPoint: !hasValidEnd
|
|
}
|
|
}
|