Files
nocodb/packages/nc-gui/utils/sortUtils.ts
Anbarasu 04f49c13d8 feat: infinite scroll (#9403)
* feat: infinite scroll wip

* feat: implement column create

* feat: improve scroll performance and minor bugs

* fix: optimize cache clear fix: preserver selected items from cache clear

* feat: add keyboard support

* fix: get rid of unwanted data

* feat: infinite scroll

* fix: reload visible data

* fix: rowIndex Sync

* fix: row height fix

* fix: performance issues

* fix: small issues

* fix: stablize scrolling

* fix: scroll fails to load new data

* fix: best part is no part remove bunch of manual handling and move to computedProperty

* fix: load data as chunks instead of offset

* fix: deboucne 500 ms

* feat: safe chunk clearing

* feat: working infinite table(wip)

* fix: handle delete selected range of rows

* fix: update types

* fix: nuxt warnings

* fix: table fixes

* feat: undo-redo support for infiniteTable

* fix: fix addEmptyRow

* fix: groupby fixes

* fix: refactor visibleDtaa computed

* fix: cache clear

* fix: invalid offset error

* fix: add empty row elem

* fix: rows not loading at end

* fix: refactor

* fix: more tests passing

* fix: perf optimizations

* fix: couple tests

* fix: row height tests

* fix: row height tests

* fix: row height tests

* fix: sync row comment count

* fix: fixes

* fix: lot of tests

* fix: update the row placeholder columns size calculation

* fix: update the totalRows on loadData

* fix: tests when count is 0

* fix: hide placeholder if rowHeight is small

* fix: not required imo as infinite scroll is implemented

* fix: links tests

* fix: filter tests

* fix: insert after test fix: Row: Create, Update, Delete fix: Row height fix: Create column tests

* fix: error, timezone bug fix: shared view data not loading after 100

* fix: ignore shifting. this fixes errors in rows, which has some mandatory required cells

* fix: keyboardShortcuts test

* fix: project collaboration test

* fix: increase local cache fix: records empty on switching to full screen mode fix: links issue on new records

* fix: row and col margin for improved data rendering

* fix: addEmptyRow to table bottom

* fix: update gridShare test fix: shared view grid feat: new count endpoint public

* fix: undo-redo test failing

* fix: bulkUpdate chore: disabled bulkUpdate tests for now

* fix: slow searchBtn

* fix: limit max selection

* fix: limit selection to 100

* fix: initial chunk load to 100

* fix: couple fixes

* fix: couple fixes

* fix: row expand

* fix: scrollto Top and scrollTo Bottom on Shift Cmd Down/Up

* fix: drop support for cmd A

* fix: failing tests

* fix: error on clicking resize col

* fix: premature fetching

* fix: deleteSelected not working properly

* fix: test build

* fix: test build

* fix: throttled 500

* fix: scroll related issues fix: added transitions

* fix: scroll related issues

* fix: decrease col margin

* fix: increase local cache and update Buffer

* fix: decrease throttle

* fix: improve scroll performance

* fix: improve scroll performance

* fix: improve scroll performance

* fix: fixes

* feat: beta toggle show

* feat: beta toggle show

* fix: hold scroll action

* fix: sync visible data reloadVisibleDataHook

* fix: refactor useBetaFeatureToggle fix: useMultiSelect in table

* fix: dynamically reduce margin while loading records

* fix: some bugs

* fix: data loading in infinitescroll

* fix: shared view and search issues

* feat: betaToggles menu

* fix: scroll showing up in aggregation

* fix: text

* fix: implement shifting in addEmptyRow

* fix: calculate slices on rowHeight modified

* fix: keep invalid cells until another row selected

* fix: remove row if filter gets failed

* fix: update styles

* fix: move filter handling to nocodb-sdk

* fix: user field filter

* fix: sort order

* fix:user field sorting

* fix: update virtual cols

* fix: updated sort handling

* fix: updated sort handling

* fix: updated sort handling for bulkUpdate and undo-redo

* fix: unit tests

* fix: deleteSelectedRecords fails

* fix: chunkstates errors

* fix: sort bugs

* fix: scroll position

* fix: delete selectedrange of records

* fix: improved chunk management

* fix: sync toggle states across tabs

* fix: sync between tabs

* fix: limit issues

* fix: update issues

* fix: zIndex

* fix: minor fixes

* fix: cmd arrow issue

* fix: bulkdelete index issues

* fix: empty rows at top

* fix: queue add new record behaviour

* fix: resolve rowObj addEmptyRow

* fix: typo

* fix: clear indexes

* fix: reload if width is zero

* fix: manual handling

* fix: remove console

* fix: prefetch when scroll from below

* fix: refactor fixes

* fix:undo-redo with sort

---------

Co-authored-by: mertmit <mertmit99@gmail.com>
2024-11-08 11:38:47 +05:30

226 lines
5.9 KiB
TypeScript

import { type ColumnType, type SortType, UITypes } from 'nocodb-sdk'
import dayjs from 'dayjs'
export const getSortDirectionOptions = (uidt: UITypes | string, isGroupBy?: boolean) => {
const groupByOptions = isGroupBy
? [
{ text: 'Count (9 → 1)', value: 'count-desc' },
{ text: 'Count (1 → 9)', value: 'count-asc' },
]
: []
switch (uidt) {
case UITypes.Year:
case UITypes.Number:
case UITypes.Decimal:
case UITypes.Rating:
case UITypes.Count:
case UITypes.AutoNumber:
case UITypes.Time:
case UITypes.Currency:
case UITypes.Percent:
case UITypes.Duration:
case UITypes.PhoneNumber:
case UITypes.Date:
case UITypes.DateTime:
case UITypes.CreatedTime:
case UITypes.LastModifiedTime:
return [
{ text: '1 → 9', value: 'asc' },
{ text: '9 → 1', value: 'desc' },
].concat(groupByOptions)
case UITypes.Checkbox:
return [
{ text: '▢ → ✓', value: 'asc' },
{ text: '✓ → ▢', value: 'desc' },
].concat(groupByOptions)
default:
return [
{ text: 'A → Z', value: 'asc' },
{ text: 'Z → A', value: 'desc' },
].concat(groupByOptions)
}
}
export const sortByUIType = ({
uidt,
a,
b,
options: { caseSensitive = true, direction },
}: {
uidt: UITypes
a: any
b: any
options: {
caseSensitive?: boolean
direction?: 'asc' | 'desc' | 'count-asc' | 'count-desc'
}
}) => {
let nullsLast = direction !== 'asc'
if ([UITypes.Formula, UITypes.User].includes(uidt)) {
nullsLast = !nullsLast
}
if (a === null || a === undefined) {
return nullsLast ? 1 : -1
}
if (b === null || b === undefined) {
return nullsLast ? -1 : 1
}
if (a === '' && b !== '') return nullsLast ? 1 : -1
if (b === '' && a !== '') return nullsLast ? -1 : 1
let result = 0
switch (uidt) {
case UITypes.Number:
case UITypes.Decimal:
case UITypes.Currency:
case UITypes.Percent:
case UITypes.Rating:
case UITypes.Duration:
case UITypes.ID:
case UITypes.Rollup:
result = Number(a) - Number(b)
break
case UITypes.Links: {
const getLinksValue = (links: any) => {
if (links === null) return null
if (typeof links === 'number') return links
if (links && typeof links === 'object') {
return Object.values(links)[0]
}
return links
}
const valA = getLinksValue(a)
const valB = getLinksValue(b)
if (typeof valA === 'number' && typeof valB === 'number') {
result = valA - valB
} else {
result = String(valA).localeCompare(String(valB))
}
break
}
case UITypes.DateTime:
case UITypes.CreatedTime:
case UITypes.LastModifiedTime:
result = dayjs(a).valueOf() - dayjs(b).valueOf()
break
case UITypes.Time: {
const normalizeTimeValue = (value: any): dayjs.Dayjs => {
// If it's already a dayjs object
if (dayjs.isDayjs(value)) {
return dayjs(`1999-01-01 ${value.format('HH:mm:ss')}`)
}
// If it's a string in HH:mm:ss format (from server)
if (typeof value === 'string' && /^\d{2}:\d{2}:\d{2}$/.test(value)) {
return dayjs(`1999-01-01 ${value}`)
}
// If it's a string in HH:mm format (from local state)
if (typeof value === 'string' && /^\d{2}:\d{2}$/.test(value)) {
return dayjs(`1999-01-01 ${value}:00`)
}
// For any other format, try parsing with dayjs
let parsed = dayjs(value)
// If not valid, try parsing as time only
if (!parsed.isValid()) {
parsed = dayjs(value, 'HH:mm:ss')
}
// If still not valid, try with dummy date
if (!parsed.isValid()) {
parsed = dayjs(`1999-01-01 ${value}`)
}
return parsed
}
const timeA = normalizeTimeValue(a)
const timeB = normalizeTimeValue(b)
result = timeA.valueOf() - timeB.valueOf()
break
}
case UITypes.Year:
result = Number(a) - Number(b)
break
case UITypes.Checkbox:
result = a === b ? 0 : a ? -1 : 1
break
case UITypes.SingleSelect:
case UITypes.MultiSelect:
result = String(a).localeCompare(String(b))
break
case UITypes.Attachment: {
const getAttachmentValue = (att) => {
if (Array.isArray(att) && att.length > 0) {
return att[0].title || att[0].path || ''
}
return ''
}
result = getAttachmentValue(a).localeCompare(getAttachmentValue(b))
break
}
case UITypes.User:
case UITypes.CreatedBy:
case UITypes.LastModifiedBy: {
const getUserValue = (user) => {
if (Array.isArray(user) && user.length > 0) {
return user[0].display_name || user[0].email || ''
}
if (user && typeof user === 'object') {
return user.display_name || user.email || ''
}
return String(user)
}
result = getUserValue(a).localeCompare(getUserValue(b))
break
}
case UITypes.SingleLineText:
case UITypes.LongText:
case UITypes.Email:
case UITypes.URL:
case UITypes.PhoneNumber:
case UITypes.Formula:
if (caseSensitive) {
result = String(a).localeCompare(String(b))
} else {
result = String(a).toLowerCase().localeCompare(String(b).toLowerCase())
}
break
case UITypes.JSON:
result = JSON.stringify(a).localeCompare(JSON.stringify(b))
break
default:
result = String(a).localeCompare(String(b))
}
return direction === 'desc' ? -result : result
}
export const isSortRelevantChange = (
changedFields: string[],
sorts: SortType[],
columnsById: Record<string, ColumnType>,
): boolean => {
const sortColumnTitles = new Set(sorts.map((sort) => columnsById[sort.fk_column_id!]?.title).filter(Boolean))
return changedFields.some((field) => sortColumnTitles.has(field))
}