From 0443747b70ac7ea67a27e2f3d0b501b811831e21 Mon Sep 17 00:00:00 2001 From: "egg.boiled" Date: Fri, 16 May 2025 12:57:53 +0900 Subject: [PATCH 1/3] feat: Add search term highlighting in table results --- viz-lib/src/visualizations/table/Renderer.tsx | 20 ++++--- viz-lib/src/visualizations/table/utils.tsx | 54 +++++++++++++++++-- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/viz-lib/src/visualizations/table/Renderer.tsx b/viz-lib/src/visualizations/table/Renderer.tsx index 3812e82b82..a5b571bbaf 100644 --- a/viz-lib/src/visualizations/table/Renderer.tsx +++ b/viz-lib/src/visualizations/table/Renderer.tsx @@ -92,13 +92,19 @@ export default function Renderer({ options, data }: any) { // @ts-expect-error ts-migrate(2322) FIXME: Type '(event: any) => void' is not assignable to t... Remove this comment to see the full error message setSearchTerm(event.target.value)} /> ) : null; - return prepareColumns(options.columns, searchInput, orderBy, (newOrderBy: any) => { - setOrderBy(newOrderBy); - // Remove text selection - may occur accidentally - // @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. - document.getSelection().removeAllRanges(); - }); - }, [options.columns, searchColumns, orderBy]); + return prepareColumns( + options.columns, + searchInput, + orderBy, + (newOrderBy: any) => { + setOrderBy(newOrderBy); + // Remove text selection - may occur accidentally + // @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. + document.getSelection().removeAllRanges(); + }, + searchTerm + ); + }, [options.columns, searchColumns, orderBy, searchTerm]); const preparedRows = useMemo(() => sortRows(filterRows(initRows(data.rows), searchTerm, searchColumns), orderBy), [ data.rows, diff --git a/viz-lib/src/visualizations/table/utils.tsx b/viz-lib/src/visualizations/table/utils.tsx index 298cdb7f96..ee901b3dd8 100644 --- a/viz-lib/src/visualizations/table/utils.tsx +++ b/viz-lib/src/visualizations/table/utils.tsx @@ -4,6 +4,34 @@ import cx from "classnames"; import Tooltip from "antd/lib/tooltip"; import ColumnTypes from "./columns"; +function highlightSearchTerm(text: string, searchTerm: string) { + if (!searchTerm || searchTerm === "" || !text) { + return text; + } + + const textStr = toString(text); + const upperText = textStr.toUpperCase(); + const upperSearchTerm = searchTerm.toUpperCase(); + + const index = upperText.indexOf(upperSearchTerm); + + if (index >= 0) { + const before = textStr.substring(0, index); + const match = textStr.substring(index, index + searchTerm.length); + const after = textStr.substring(index + searchTerm.length); + + return ( + + {before} + {match} + {after} + + ); + } + + return text; +} + function nextOrderByDirection(direction: any) { switch (direction) { case "ascend": @@ -50,7 +78,7 @@ function getOrderByInfo(orderBy: any) { return result; } -export function prepareColumns(columns: any, searchInput: any, orderBy: any, onOrderByChange: any) { +export function prepareColumns(columns: any, searchInput: any, orderBy: any, onOrderByChange: any, searchTerm: string = '') { columns = filter(columns, "visible"); columns = sortBy(columns, "order"); @@ -102,10 +130,26 @@ export function prepareColumns(columns: any, searchInput: any, orderBy: any, onO const initColumn = ColumnTypes[column.displayAs]; const Component = initColumn(column); // @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type '{ key: a... Remove this comment to see the full error message - result.render = (unused: any, row: any) => ({ - children: , - props: { className: `display-as-${column.displayAs}` }, - }); + result.render = (unused: any, row: any) => { + const isSearchable = column.allowSearch === true; + + // 검색어가 있고 컬럼이 검색 가능하면 하이라이트 적용 + if (searchTerm && isSearchable) { + const { prepareData } = initColumn(column); + const { text } = prepareData(row.record); + + return { + children: highlightSearchTerm(text, searchTerm), + props: { className: `display-as-${column.displayAs}` }, + }; + } + + // 기존 렌더링 로직 + return { + children: , + props: { className: `display-as-${column.displayAs}` }, + }; + }; return result; }); From 7c0a1c297633358fa09c59c4f72034646a297b38 Mon Sep 17 00:00:00 2001 From: "egg.boiled" Date: Fri, 16 May 2025 13:09:35 +0900 Subject: [PATCH 2/3] delete: remove unnecessary comments --- viz-lib/src/visualizations/table/utils.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/viz-lib/src/visualizations/table/utils.tsx b/viz-lib/src/visualizations/table/utils.tsx index ee901b3dd8..606abfaf8e 100644 --- a/viz-lib/src/visualizations/table/utils.tsx +++ b/viz-lib/src/visualizations/table/utils.tsx @@ -133,7 +133,6 @@ export function prepareColumns(columns: any, searchInput: any, orderBy: any, onO result.render = (unused: any, row: any) => { const isSearchable = column.allowSearch === true; - // 검색어가 있고 컬럼이 검색 가능하면 하이라이트 적용 if (searchTerm && isSearchable) { const { prepareData } = initColumn(column); const { text } = prepareData(row.record); @@ -144,7 +143,6 @@ export function prepareColumns(columns: any, searchInput: any, orderBy: any, onO }; } - // 기존 렌더링 로직 return { children: , props: { className: `display-as-${column.displayAs}` }, From 220da0bbb6950d9ae8d5075f07b548b2452f4b3d Mon Sep 17 00:00:00 2001 From: "egg.boiled" Date: Thu, 22 May 2025 10:42:39 +0900 Subject: [PATCH 3/3] refactoring: replace inline styles with semantic HTML mark element --- viz-lib/src/visualizations/table/utils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viz-lib/src/visualizations/table/utils.tsx b/viz-lib/src/visualizations/table/utils.tsx index 606abfaf8e..e9e2179239 100644 --- a/viz-lib/src/visualizations/table/utils.tsx +++ b/viz-lib/src/visualizations/table/utils.tsx @@ -23,7 +23,7 @@ function highlightSearchTerm(text: string, searchTerm: string) { return ( {before} - {match} + {match} {after} );