Add Users list/detail with ACL toggles and library people update/status APIs. Fix project table layout and ANSI-safe cell rendering, and decode array responses from people/status so saves no longer fail on JSON unmarshal. Co-authored-by: Cursor <cursoragent@cursor.com>
166 lines
3.9 KiB
Go
166 lines
3.9 KiB
Go
package ui
|
|
|
|
import (
|
|
"github.com/eslider/go-onlyoffice/cmd/office/model"
|
|
"github.com/mattn/go-runewidth"
|
|
)
|
|
|
|
type columnLayout struct {
|
|
indices []int
|
|
widths map[int]int
|
|
}
|
|
|
|
func (t *DataTable) computeLayout() columnLayout {
|
|
contentW := t.lineContentWidth()
|
|
if len(t.columns) == 0 || contentW <= 0 {
|
|
return columnLayout{widths: map[int]int{}}
|
|
}
|
|
if t.spec.Subject == model.SubjectProjects {
|
|
return layoutProjectTable(t.columns, contentW)
|
|
}
|
|
if t.spec.Subject == model.SubjectUsers {
|
|
return layoutUserTable(t.columns, contentW)
|
|
}
|
|
return layoutScrollingTable(t.columns, t.colScroll, contentW)
|
|
}
|
|
|
|
func layoutScrollingTable(cols []model.TableColumn, colScroll, contentW int) columnLayout {
|
|
indices := pickVisibleColumnIndices(cols, colScroll, contentW)
|
|
widths := make(map[int]int, len(indices))
|
|
if len(indices) == 0 {
|
|
return columnLayout{indices: indices, widths: widths}
|
|
}
|
|
minSum := 0
|
|
for _, i := range indices {
|
|
minSum += cols[i].Width
|
|
}
|
|
widths = distributeColumnWidths(minSum, contentW, indices, cols)
|
|
return columnLayout{indices: indices, widths: widths}
|
|
}
|
|
|
|
func pickVisibleColumnIndices(cols []model.TableColumn, colScroll, contentW int) []int {
|
|
var out []int
|
|
used := 0
|
|
for colIdx := colScroll; colIdx < len(cols); colIdx++ {
|
|
w := cols[colIdx].Width
|
|
if len(out) > 0 && used+w > contentW {
|
|
break
|
|
}
|
|
out = append(out, colIdx)
|
|
used += w
|
|
}
|
|
if len(out) == 0 {
|
|
colIdx := colScroll
|
|
if colIdx < 0 || colIdx >= len(cols) {
|
|
colIdx = 0
|
|
}
|
|
out = []int{colIdx}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// distributeColumnWidths expands or shrinks visible columns to exactly fill total width.
|
|
func distributeColumnWidths(minSum, total int, indices []int, cols []model.TableColumn) map[int]int {
|
|
out := make(map[int]int, len(indices))
|
|
if len(indices) == 0 {
|
|
return out
|
|
}
|
|
if total < len(indices) {
|
|
total = len(indices)
|
|
}
|
|
if minSum <= 0 {
|
|
each := total / len(indices)
|
|
if each < 1 {
|
|
each = 1
|
|
}
|
|
for _, i := range indices {
|
|
out[i] = each
|
|
}
|
|
normalizeWidthSum(out, indices, total, indices[len(indices)-1])
|
|
return out
|
|
}
|
|
for _, i := range indices {
|
|
out[i] = cols[i].Width
|
|
}
|
|
if minSum >= total {
|
|
for _, i := range indices {
|
|
out[i] = cols[i].Width * total / minSum
|
|
if out[i] < 1 {
|
|
out[i] = 1
|
|
}
|
|
}
|
|
normalizeWidthSum(out, indices, total, flexColumnIndex(indices, cols))
|
|
return out
|
|
}
|
|
extra := total - minSum
|
|
flex := flexColumnIndices(indices, cols)
|
|
flexSum := 0
|
|
for _, i := range flex {
|
|
flexSum += cols[i].Width
|
|
}
|
|
if flexSum <= 0 {
|
|
flexSum = len(flex)
|
|
}
|
|
for _, i := range flex {
|
|
out[i] += extra * cols[i].Width / flexSum
|
|
}
|
|
normalizeWidthSum(out, indices, total, flexColumnIndex(indices, cols))
|
|
return out
|
|
}
|
|
|
|
func flexColumnIndices(indices []int, cols []model.TableColumn) []int {
|
|
flex := make([]int, 0, len(indices))
|
|
for _, i := range indices {
|
|
switch cols[i].Key {
|
|
case "title", "subtitle", "description", "displayName", "primaryEmail", "from", "to", "type":
|
|
flex = append(flex, i)
|
|
}
|
|
}
|
|
if len(flex) == 0 {
|
|
flex = append(flex, indices...)
|
|
}
|
|
return flex
|
|
}
|
|
|
|
func flexColumnIndex(indices []int, cols []model.TableColumn) int {
|
|
flex := flexColumnIndices(indices, cols)
|
|
if len(flex) == 0 {
|
|
return indices[len(indices)-1]
|
|
}
|
|
for _, i := range indices {
|
|
if cols[i].Key == "title" {
|
|
return i
|
|
}
|
|
}
|
|
return flex[0]
|
|
}
|
|
|
|
func normalizeWidthSum(widths map[int]int, indices []int, total, adjustIdx int) {
|
|
if len(indices) == 0 {
|
|
return
|
|
}
|
|
sum := 0
|
|
for _, i := range indices {
|
|
sum += widths[i]
|
|
}
|
|
if adjustIdx < 0 {
|
|
adjustIdx = indices[len(indices)-1]
|
|
}
|
|
widths[adjustIdx] += total - sum
|
|
if widths[adjustIdx] < 1 {
|
|
widths[adjustIdx] = 1
|
|
}
|
|
}
|
|
|
|
const cellHPadding = 0 // padding lives in outer lipgloss styles; column width is the full cell width
|
|
|
|
func truncateCellText(text string, colWidth int) string {
|
|
if colWidth < 1 {
|
|
colWidth = 1
|
|
}
|
|
if runewidth.StringWidth(text) <= colWidth {
|
|
return text
|
|
}
|
|
return runewidth.Truncate(text, colWidth, "…")
|
|
}
|