Files
abra/vendor/github.com/evertras/bubble-table/table/options.go

511 lines
13 KiB
Go

package table
import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/lipgloss"
)
// RowStyleFuncInput is the input to the style function that can
// be applied to each row. This is useful for things like zebra
// striping or other data-based styles.
//
// Note that we use a struct here to allow for future expansion
// while keeping backwards compatibility.
type RowStyleFuncInput struct {
// Index is the index of the row, starting at 0.
Index int
// Row is the full row data.
Row Row
// IsHighlighted is true if the row is currently highlighted.
IsHighlighted bool
}
// WithRowStyleFunc sets a function that can be used to apply a style to each row
// based on the row data. This is useful for things like zebra striping or other
// data-based styles. It can be safely set to nil to remove it later.
// This style is applied after the base style and before individual row styles.
// This will override any HighlightStyle settings.
func (m Model) WithRowStyleFunc(f func(RowStyleFuncInput) lipgloss.Style) Model {
m.rowStyleFunc = f
return m
}
// WithHighlightedRow sets the highlighted row to the given index.
func (m Model) WithHighlightedRow(index int) Model {
m.rowCursorIndex = index
if m.rowCursorIndex >= len(m.GetVisibleRows()) {
m.rowCursorIndex = len(m.GetVisibleRows()) - 1
}
if m.rowCursorIndex < 0 {
m.rowCursorIndex = 0
}
m.currentPage = m.expectedPageForRowIndex(m.rowCursorIndex)
return m
}
// HeaderStyle sets the style to apply to the header text, such as color or bold.
func (m Model) HeaderStyle(style lipgloss.Style) Model {
m.headerStyle = style.Copy()
return m
}
// WithRows sets the rows to show as data in the table.
func (m Model) WithRows(rows []Row) Model {
m.rows = rows
m.visibleRowCacheUpdated = false
if m.rowCursorIndex >= len(m.rows) {
m.rowCursorIndex = len(m.rows) - 1
}
if m.rowCursorIndex < 0 {
m.rowCursorIndex = 0
}
if m.pageSize != 0 {
maxPage := m.MaxPages()
// MaxPages is 1-index, currentPage is 0 index
if maxPage <= m.currentPage {
m.pageLast()
}
}
return m
}
// WithKeyMap sets the key map to use for controls when focused.
func (m Model) WithKeyMap(keyMap KeyMap) Model {
m.keyMap = keyMap
return m
}
// KeyMap returns a copy of the current key map in use.
func (m Model) KeyMap() KeyMap {
return m.keyMap
}
// SelectableRows sets whether or not rows are selectable. If set, adds a column
// in the front that acts as a checkbox and responds to controls if Focused.
func (m Model) SelectableRows(selectable bool) Model {
m.selectableRows = selectable
hasSelectColumn := len(m.columns) > 0 && m.columns[0].key == columnKeySelect
if hasSelectColumn != selectable {
if selectable {
m.columns = append([]Column{
NewColumn(columnKeySelect, m.selectedText, len([]rune(m.selectedText))),
}, m.columns...)
} else {
m.columns = m.columns[1:]
}
}
m.recalculateWidth()
return m
}
// HighlightedRow returns the full Row that's currently highlighted by the user.
func (m Model) HighlightedRow() Row {
if len(m.GetVisibleRows()) > 0 {
return m.GetVisibleRows()[m.rowCursorIndex]
}
// TODO: Better way to do this without pointers/nil? Or should it be nil?
return Row{}
}
// SelectedRows returns all rows that have been set as selected by the user.
func (m Model) SelectedRows() []Row {
selectedRows := []Row{}
for _, row := range m.GetVisibleRows() {
if row.selected {
selectedRows = append(selectedRows, row)
}
}
return selectedRows
}
// HighlightStyle sets a custom style to use when the row is being highlighted
// by the cursor. This should not be used with WithRowStyleFunc. Instead, use
// the IsHighlighted field in the style function.
func (m Model) HighlightStyle(style lipgloss.Style) Model {
m.highlightStyle = style
return m
}
// Focused allows the table to show highlighted rows and take in controls of
// up/down/space/etc to let the user navigate the table and interact with it.
func (m Model) Focused(focused bool) Model {
m.focused = focused
return m
}
// Filtered allows the table to show rows that match the filter.
func (m Model) Filtered(filtered bool) Model {
m.filtered = filtered
m.visibleRowCacheUpdated = false
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// StartFilterTyping focuses the text input to allow user typing to filter.
func (m Model) StartFilterTyping() Model {
m.filterTextInput.Focus()
return m
}
// WithStaticFooter adds a footer that only displays the given text.
func (m Model) WithStaticFooter(footer string) Model {
m.staticFooter = footer
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// WithPageSize enables pagination using the given page size. This can be called
// again at any point to resize the height of the table.
func (m Model) WithPageSize(pageSize int) Model {
m.pageSize = pageSize
maxPages := m.MaxPages()
if m.currentPage >= maxPages {
m.currentPage = maxPages - 1
}
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// WithNoPagination disables pagination in the table.
func (m Model) WithNoPagination() Model {
m.pageSize = 0
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// WithPaginationWrapping sets whether to wrap around from the beginning to the
// end when navigating through pages. Defaults to true.
func (m Model) WithPaginationWrapping(wrapping bool) Model {
m.paginationWrapping = wrapping
return m
}
// WithSelectedText describes what text to show when selectable rows are enabled.
// The selectable column header will use the selected text string.
func (m Model) WithSelectedText(unselected, selected string) Model {
m.selectedText = selected
m.unselectedText = unselected
if len(m.columns) > 0 && m.columns[0].key == columnKeySelect {
m.columns[0] = NewColumn(columnKeySelect, m.selectedText, len([]rune(m.selectedText)))
m.recalculateWidth()
}
return m
}
// WithBaseStyle applies a base style as the default for everything in the table.
// This is useful for border colors, default alignment, default color, etc.
func (m Model) WithBaseStyle(style lipgloss.Style) Model {
m.baseStyle = style
return m
}
// WithTargetWidth sets the total target width of the table, including borders.
// This only takes effect when using flex columns. When using flex columns,
// columns will stretch to fill out to the total width given here.
func (m Model) WithTargetWidth(totalWidth int) Model {
m.targetTotalWidth = totalWidth
m.recalculateWidth()
return m
}
// WithMinimumHeight sets the minimum total height of the table, including borders.
func (m Model) WithMinimumHeight(minimumHeight int) Model {
m.minimumHeight = minimumHeight
m.recalculateHeight()
return m
}
// PageDown goes to the next page of a paginated table, wrapping to the first
// page if the table is already on the last page.
func (m Model) PageDown() Model {
m.pageDown()
return m
}
// PageUp goes to the previous page of a paginated table, wrapping to the
// last page if the table is already on the first page.
func (m Model) PageUp() Model {
m.pageUp()
return m
}
// PageLast goes to the last page of a paginated table.
func (m Model) PageLast() Model {
m.pageLast()
return m
}
// PageFirst goes to the first page of a paginated table.
func (m Model) PageFirst() Model {
m.pageFirst()
return m
}
// WithCurrentPage sets the current page (1 as the first page) of a paginated
// table, bounded to the total number of pages. The current selected row will
// be set to the top row of the page if the page changed.
func (m Model) WithCurrentPage(currentPage int) Model {
if m.pageSize == 0 || currentPage == m.CurrentPage() {
return m
}
if currentPage < 1 {
currentPage = 1
} else {
maxPages := m.MaxPages()
if currentPage > maxPages {
currentPage = maxPages
}
}
m.currentPage = currentPage - 1
m.rowCursorIndex = m.currentPage * m.pageSize
return m
}
// WithColumns sets the visible columns for the table, so that columns can be
// added/removed/resized or headers rewritten.
func (m Model) WithColumns(columns []Column) Model {
// Deep copy to avoid edits
m.columns = make([]Column, len(columns))
copy(m.columns, columns)
m.recalculateWidth()
if m.selectableRows {
// Re-add the selectable column
m = m.SelectableRows(true)
}
return m
}
// WithFilterInput makes the table use the provided text input bubble for
// filtering rather than using the built-in default. This allows for external
// text input controls to be used.
func (m Model) WithFilterInput(input textinput.Model) Model {
if m.filterTextInput.Value() != input.Value() {
m.pageFirst()
}
m.filterTextInput = input
m.visibleRowCacheUpdated = false
return m
}
// WithFilterInputValue sets the filter value to the given string, immediately
// applying it as if the user had typed it in. Useful for external filter inputs
// that are not necessarily a text input.
func (m Model) WithFilterInputValue(value string) Model {
if m.filterTextInput.Value() != value {
m.pageFirst()
}
m.filterTextInput.SetValue(value)
m.filterTextInput.Blur()
m.visibleRowCacheUpdated = false
return m
}
// WithFilterFunc adds a filter function to the model. If the function returns
// true, the row will be included in the filtered results. If the function
// is nil, the function won't be used and instead the default filtering will be applied,
// if any.
func (m Model) WithFilterFunc(shouldInclude FilterFunc) Model {
m.filterFunc = shouldInclude
m.visibleRowCacheUpdated = false
return m
}
// WithFuzzyFilter enables fuzzy filtering for the table.
func (m Model) WithFuzzyFilter() Model {
return m.WithFilterFunc(filterFuncFuzzy)
}
// WithFooterVisibility sets the visibility of the footer.
func (m Model) WithFooterVisibility(visibility bool) Model {
m.footerVisible = visibility
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// WithHeaderVisibility sets the visibility of the header.
func (m Model) WithHeaderVisibility(visibility bool) Model {
m.headerVisible = visibility
if m.minimumHeight > 0 {
m.recalculateHeight()
}
return m
}
// WithMaxTotalWidth sets the maximum total width that the table should render.
// If this width is exceeded by either the target width or by the total width
// of all the columns (including borders!), anything extra will be treated as
// overflow and horizontal scrolling will be enabled to see the rest.
func (m Model) WithMaxTotalWidth(maxTotalWidth int) Model {
m.maxTotalWidth = maxTotalWidth
m.recalculateWidth()
return m
}
// WithHorizontalFreezeColumnCount freezes the given number of columns to the
// left side. This is useful for things like ID or Name columns that should
// always be visible even when scrolling.
func (m Model) WithHorizontalFreezeColumnCount(columnsToFreeze int) Model {
m.horizontalScrollFreezeColumnsCount = columnsToFreeze
m.recalculateWidth()
return m
}
// ScrollRight moves one column to the right. Use with WithMaxTotalWidth.
func (m Model) ScrollRight() Model {
m.scrollRight()
return m
}
// ScrollLeft moves one column to the left. Use with WithMaxTotalWidth.
func (m Model) ScrollLeft() Model {
m.scrollLeft()
return m
}
// WithMissingDataIndicator sets an indicator to use when data for a column is
// not found in a given row. Note that this is for completely missing data,
// an empty string or other zero value that is explicitly set is not considered
// to be missing.
func (m Model) WithMissingDataIndicator(str string) Model {
m.missingDataIndicator = str
return m
}
// WithMissingDataIndicatorStyled sets a styled indicator to use when data for
// a column is not found in a given row. Note that this is for completely
// missing data, an empty string or other zero value that is explicitly set is
// not considered to be missing.
func (m Model) WithMissingDataIndicatorStyled(styled StyledCell) Model {
m.missingDataIndicator = styled
return m
}
// WithAllRowsDeselected deselects any rows that are currently selected.
func (m Model) WithAllRowsDeselected() Model {
rows := m.GetVisibleRows()
for i, row := range rows {
if row.selected {
rows[i] = row.Selected(false)
}
}
m.rows = rows
return m
}
// WithMultiline sets whether or not to wrap text in cells to multiple lines.
func (m Model) WithMultiline(multiline bool) Model {
m.multiline = multiline
return m
}
// WithAdditionalShortHelpKeys enables you to add more keybindings to the 'short help' view.
func (m Model) WithAdditionalShortHelpKeys(keys []key.Binding) Model {
m.additionalShortHelpKeys = func() []key.Binding {
return keys
}
return m
}
// WithAdditionalFullHelpKeys enables you to add more keybindings to the 'full help' view.
func (m Model) WithAdditionalFullHelpKeys(keys []key.Binding) Model {
m.additionalFullHelpKeys = func() []key.Binding {
return keys
}
return m
}
// WithGlobalMetadata applies the given metadata to the table. This metadata is passed to
// some functions in FilterFuncInput and StyleFuncInput to enable more advanced decisions,
// such as setting some global theme variable to reference, etc. Has no effect otherwise.
func (m Model) WithGlobalMetadata(metadata map[string]any) Model {
m.metadata = metadata
return m
}