feat(dict): таблица справочника — hide tail columns по умолчанию (без H-scroll)
This commit is contained in:
@@ -28,11 +28,20 @@ const DEFAULT_STATE: DictTableState = { sort: null, hidden: [] }
|
|||||||
|
|
||||||
const storageKey = (dictName: string): string => `ord-dict-table:${dictName}`
|
const storageKey = (dictName: string): string => `ord-dict-table:${dictName}`
|
||||||
|
|
||||||
const readState = (dictName: string): DictTableState => {
|
/**
|
||||||
if (typeof window === 'undefined') return DEFAULT_STATE
|
* Прочитать persisted state. Если в storage пусто (юзер ни разу не
|
||||||
|
* взаимодействовал с этим dict) → используем `fallbackHidden` как initial
|
||||||
|
* visibility (для info-dense schemas: hide tail columns по умолчанию,
|
||||||
|
* чтобы таблица помещалась в viewport без H-scroll). Любой toggle/setHidden
|
||||||
|
* запишет state в storage → дальнейшие посещения respect юзерский выбор
|
||||||
|
* вне зависимости от fallback.
|
||||||
|
*/
|
||||||
|
const readState = (dictName: string, fallbackHidden: string[]): DictTableState => {
|
||||||
|
const fallback: DictTableState = { sort: null, hidden: fallbackHidden }
|
||||||
|
if (typeof window === 'undefined') return fallback
|
||||||
try {
|
try {
|
||||||
const raw = window.localStorage.getItem(storageKey(dictName))
|
const raw = window.localStorage.getItem(storageKey(dictName))
|
||||||
if (!raw) return DEFAULT_STATE
|
if (!raw) return fallback
|
||||||
const parsed = JSON.parse(raw) as Partial<DictTableState>
|
const parsed = JSON.parse(raw) as Partial<DictTableState>
|
||||||
return {
|
return {
|
||||||
sort:
|
sort:
|
||||||
@@ -46,7 +55,7 @@ const readState = (dictName: string): DictTableState => {
|
|||||||
: [],
|
: [],
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return DEFAULT_STATE
|
return fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,13 +69,19 @@ const writeState = (dictName: string, state: DictTableState): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDictTableState(dictName: string) {
|
export function useDictTableState(dictName: string, defaultHidden: string[] = []) {
|
||||||
const [state, setState] = useState<DictTableState>(() => readState(dictName))
|
// Stable key для defaultHidden чтобы useEffect не зацикливался на каждый
|
||||||
|
// render (массив literal каждый раз new reference). Caller обычно строит
|
||||||
|
// defaultHidden из useMemo, но guard всё равно нужен.
|
||||||
|
const defaultKey = defaultHidden.join(',')
|
||||||
|
const [state, setState] = useState<DictTableState>(() => readState(dictName, defaultHidden))
|
||||||
|
|
||||||
// Перечитать когда смена dictName (navigation между справочниками).
|
// Перечитать когда смена dictName (navigation между справочниками) или
|
||||||
|
// когда меняется набор defaultHidden (schema reload → новые extra columns).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setState(readState(dictName))
|
setState(readState(dictName, defaultHidden))
|
||||||
}, [dictName])
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- defaultKey stable
|
||||||
|
}, [dictName, defaultKey])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
writeState(dictName, state)
|
writeState(dictName, state)
|
||||||
|
|||||||
@@ -372,9 +372,28 @@ function DictionaryDetail() {
|
|||||||
setPage(0)
|
setPage(0)
|
||||||
}, [search, scopeFilter, aoi])
|
}, [search, scopeFilter, aoi])
|
||||||
|
|
||||||
|
// First-visit defaults: с info-dense schemas (10+ scalar полей) полная
|
||||||
|
// таблица не помещается в viewport → H-scroll. Hide хвост по умолчанию
|
||||||
|
// — показываем businessKey, name, scope, validFrom + первые 2 extra
|
||||||
|
// (обычно самые важные ключи). Юзер открывает menu Колонки и сам
|
||||||
|
// добавляет нужные — выбор persisted в localStorage и при следующем
|
||||||
|
// визите fallback уже не применяется.
|
||||||
|
const defaultHiddenExtras = useMemo<string[]>(() => {
|
||||||
|
const props = detailQuery.data?.schemaJson?.properties ?? {}
|
||||||
|
const eligible: string[] = []
|
||||||
|
for (const [key, prop] of Object.entries(props)) {
|
||||||
|
if (key === 'name' || key === 'code' || key === 'businessKey') continue
|
||||||
|
if (Boolean(prop['x-localized'])) continue
|
||||||
|
const t = prop.type
|
||||||
|
if (t === 'array' || t === 'object') continue
|
||||||
|
eligible.push(key)
|
||||||
|
}
|
||||||
|
return eligible.slice(2) // первые 2 видны, остальные default-hidden
|
||||||
|
}, [detailQuery.data?.schemaJson])
|
||||||
|
|
||||||
// Table state — sort (cycle none→asc→desc) + hidden columns persisted в
|
// Table state — sort (cycle none→asc→desc) + hidden columns persisted в
|
||||||
// localStorage per dict через useDictTableState.
|
// localStorage per dict через useDictTableState.
|
||||||
const dictTable = useDictTableState(name)
|
const dictTable = useDictTableState(name, defaultHiddenExtras)
|
||||||
|
|
||||||
// Getter для applySort — извлекает значение по col key из record.
|
// Getter для applySort — извлекает значение по col key из record.
|
||||||
// Изолирован чтобы sort работал унифицировано для всех типов колонок.
|
// Изолирован чтобы sort работал унифицировано для всех типов колонок.
|
||||||
|
|||||||
Reference in New Issue
Block a user