summaryrefslogtreecommitdiff
path: root/lib/data-table.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
commite0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch)
tree68543a65d88f5afb3a0202925804103daa91bc6f /lib/data-table.ts
3/25 까지의 대표님 작업사항
Diffstat (limited to 'lib/data-table.ts')
-rw-r--r--lib/data-table.ts181
1 files changed, 181 insertions, 0 deletions
diff --git a/lib/data-table.ts b/lib/data-table.ts
new file mode 100644
index 00000000..4fed7b9b
--- /dev/null
+++ b/lib/data-table.ts
@@ -0,0 +1,181 @@
+import type { ColumnType, Filter, FilterOperator, } from "@/types/table"
+import { type Column } from "@tanstack/react-table"
+
+import { dataTableConfig } from "@/config/data-table"
+import { FilterFn, Row } from "@tanstack/react-table"
+
+/**
+ * Generate common pinning styles for a table column.
+ *
+ * This function calculates and returns CSS properties for pinned columns in a data table.
+ * It handles both left and right pinning, applying appropriate styles for positioning,
+ * shadows, and z-index. The function also considers whether the column is the last left-pinned
+ * or first right-pinned column to apply specific shadow effects.
+ *
+ * @param options - The options for generating pinning styles.
+ * @param options.column - The column object for which to generate styles.
+ * @param options.withBorder - Whether to show a box shadow between pinned and scrollable columns.
+ * @returns A React.CSSProperties object containing the calculated styles.
+ */
+export function getCommonPinningStyles<TData>({
+ column,
+ withBorder = false,
+}: {
+ column: Column<TData>
+ /**
+ * Show box shadow between pinned and scrollable columns.
+ * @default false
+ */
+ withBorder?: boolean
+}): React.CSSProperties {
+ const isPinned = column.getIsPinned()
+ const isLastLeftPinnedColumn =
+ isPinned === "left" && column.getIsLastColumn("left")
+ const isFirstRightPinnedColumn =
+ isPinned === "right" && column.getIsFirstColumn("right")
+
+ return {
+ boxShadow: withBorder
+ ? isLastLeftPinnedColumn
+ ? "-4px 0 4px -4px hsl(var(--border)) inset"
+ : isFirstRightPinnedColumn
+ ? "4px 0 4px -4px hsl(var(--border)) inset"
+ : undefined
+ : undefined,
+ left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
+ right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
+ opacity: isPinned ? 0.97 : 1,
+ position: isPinned ? "sticky" : "relative",
+ background: isPinned ? "hsl(var(--background))" : "hsl(var(--background))",
+ width: column.getSize(),
+ zIndex: isPinned ? 1 : 0,
+ }
+}
+
+/**
+ * Determine the default filter operator for a given column type.
+ *
+ * This function returns the most appropriate default filter operator based on the
+ * column's data type. For text columns, it returns 'iLike' (case-insensitive like),
+ * while for all other types, it returns 'eq' (equality).
+ *
+ * @param columnType - The type of the column (e.g., 'text', 'number', 'date', etc.).
+ * @returns The default FilterOperator for the given column type.
+ */
+export function getDefaultFilterOperator(
+ columnType: ColumnType
+): FilterOperator {
+ if (columnType === "text") {
+ return "iLike"
+ }
+
+ return "eq"
+}
+
+/**
+ * Retrieve the list of applicable filter operators for a given column type.
+ *
+ * This function returns an array of filter operators that are relevant and applicable
+ * to the specified column type. It uses a predefined mapping of column types to
+ * operator lists, falling back to text operators if an unknown column type is provided.
+ *
+ * @param columnType - The type of the column for which to get filter operators.
+ * @returns An array of objects, each containing a label and value for a filter operator.
+ */
+export function getFilterOperators(columnType: ColumnType) {
+ const operatorMap: Record<
+ ColumnType,
+ { label: string; value: FilterOperator }[]
+ > = {
+ text: dataTableConfig.textOperators,
+ number: dataTableConfig.numericOperators,
+ select: dataTableConfig.selectOperators,
+ "multi-select": dataTableConfig.selectOperators,
+ boolean: dataTableConfig.booleanOperators,
+ date: dataTableConfig.dateOperators,
+ }
+
+ return operatorMap[columnType] ?? dataTableConfig.textOperators
+}
+
+/**
+ * Filters out invalid or empty filters from an array of filters.
+ *
+ * This function processes an array of filters and returns a new array
+ * containing only the valid filters. A filter is considered valid if:
+ * - It has an 'isEmpty' or 'isNotEmpty' operator, or
+ * - Its value is not empty (for array values, at least one element must be present;
+ * for other types, the value must not be an empty string, null, or undefined)
+ *
+ * @param filters - An array of Filter objects to be validated.
+ * @returns A new array containing only the valid filters.
+ */
+export function getValidFilters<TData>(
+ filters: Filter<TData>[]
+): Filter<TData>[] {
+ return filters?.filter(
+ (filter) =>
+ filter.operator === "isEmpty" ||
+ filter.operator === "isNotEmpty" ||
+ (Array.isArray(filter.value)
+ ? filter.value.length > 0
+ : filter.value !== "" &&
+ filter.value !== null &&
+ filter.value !== undefined)
+ )
+}
+
+interface NumericFilterValue {
+ operator: string
+ inputValue?: number
+}
+
+
+export const numericFilter: FilterFn<any> = (
+ row: Row<any>,
+ columnId: string,
+ filterValue: NumericFilterValue
+) => {
+ const rowValue = row.getValue(columnId)
+
+ // handle "isEmpty" / "isNotEmpty"
+ if (filterValue.operator === "isEmpty") {
+ return rowValue == null || rowValue === ""
+ } else if (filterValue.operator === "isNotEmpty") {
+ return !(rowValue == null || rowValue === "")
+ }
+
+ // parse rowValue → numeric
+ const numericRowVal =
+ typeof rowValue === "number" ? rowValue : parseFloat(String(rowValue))
+
+ if (isNaN(numericRowVal)) {
+ // rowValue not a number
+ return false
+ }
+
+ // parse filterValue.inputValue
+ const filterNum = filterValue.inputValue
+ if (filterNum == null || isNaN(filterNum)) {
+ // if user didn’t actually type a number, match everything or nothing (your choice)
+ return true
+ }
+
+ // compare based on operator
+ switch (filterValue.operator) {
+ case "eq":
+ return numericRowVal === filterNum
+ case "ne":
+ return numericRowVal !== filterNum
+ case "lt":
+ return numericRowVal < filterNum
+ case "lte":
+ return numericRowVal <= filterNum
+ case "gt":
+ return numericRowVal > filterNum
+ case "gte":
+ return numericRowVal >= filterNum
+ default:
+ return true
+ }
+} \ No newline at end of file