Skip to content

Commit a0c502a

Browse files
committed
feat: add case-sensitive search
1 parent 8b59022 commit a0c502a

File tree

8 files changed

+123
-37
lines changed

8 files changed

+123
-37
lines changed

src/ReactUnipika/ReactUnipika.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface ReactUnipikaProps {
2222
toolbarStickyTop?: number;
2323
renderToolbar?: (props: ToolbarProps) => React.ReactNode;
2424
collapseIconType?: CollapseIconType;
25+
caseSensitiveSearch?: boolean;
2526
}
2627

2728
const defaultUnipikaSettings = {
@@ -45,6 +46,7 @@ export function ReactUnipika({
4546
toolbarStickyTop = 0,
4647
renderToolbar,
4748
collapseIconType,
49+
caseSensitiveSearch,
4850
}: ReactUnipikaProps) {
4951
const convertedValue = React.useMemo(() => {
5052
// TODO: fix me later
@@ -98,6 +100,7 @@ export function ReactUnipika({
98100
toolbarStickyTop={toolbarStickyTop}
99101
renderToolbar={renderToolbar}
100102
collapseIconType={collapseIconType}
103+
caseSensitiveSearch={caseSensitiveSearch}
101104
/>
102105
) : (
103106
<pre

src/ReactUnipika/__stories__/ReactUnipika.stories.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export const Yson: StoryObj<ReactUnipikaProps> = {
2525
},
2626
};
2727

28+
export const WithCaseSensitiveSearch: StoryObj<ReactUnipikaProps> = {
29+
args: {
30+
value: data,
31+
caseSensitiveSearch: true,
32+
},
33+
};
34+
2835
export const WithContentAbove: StoryObj<ReactUnipikaProps> = {
2936
render() {
3037
return (

src/StructuredYson/StructuredYson.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface Props {
3939
toolbarStickyTop?: number;
4040
renderToolbar?: (props: ToolbarProps) => React.ReactNode;
4141
collapseIconType?: CollapseIconType;
42+
caseSensitiveSearch?: boolean;
4243
}
4344

4445
interface State {
@@ -60,13 +61,15 @@ function calculateState(
6061
value: State['value'],
6162
collapsedState: CollapsedState,
6263
filter: string,
64+
caseSensitive: boolean,
6365
settings: UnipikaSettings,
6466
) {
6567
const flattenResult = flattenUnipika(value, {
6668
isJson: settings.format !== 'yson',
6769
collapsedState: collapsedState,
6870
filter,
6971
settings: settings,
72+
caseSensitive,
7073
});
7174

7275
return Object.assign(
@@ -93,7 +96,13 @@ export class StructuredYson extends React.PureComponent<Props, State> {
9396
if (prevValue !== value || !isEmpty_(res)) {
9497
Object.assign<Partial<State>, Partial<State>>(res, {
9598
value,
96-
...calculateState(value, state.collapsedState, state.filter, settings),
99+
...calculateState(
100+
value,
101+
state.collapsedState,
102+
state.filter,
103+
props.caseSensitiveSearch || false,
104+
settings,
105+
),
97106
});
98107
}
99108
return isEmpty_(res) ? null : res;
@@ -131,6 +140,7 @@ export class StructuredYson extends React.PureComponent<Props, State> {
131140
cb?: () => void,
132141
) {
133142
const {value, settings} = this.state;
143+
const {caseSensitiveSearch} = this.props;
134144
const {
135145
collapsedState = this.state.collapsedState,
136146
matchIndex = this.state.matchIndex,
@@ -142,7 +152,13 @@ export class StructuredYson extends React.PureComponent<Props, State> {
142152
collapsedState,
143153
filter,
144154
matchIndex,
145-
...calculateState(value, collapsedState, filter, settings),
155+
...calculateState(
156+
value,
157+
collapsedState,
158+
filter,
159+
caseSensitiveSearch || false,
160+
settings,
161+
),
146162
},
147163
cb,
148164
);

src/StructuredYson/StructuredYsonToolbar.tsx

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React, {useRef} from 'react';
22

3-
import {Button, Icon, TextInput} from '@gravity-ui/uikit';
3+
import {Button, Icon, TextInput, ActionTooltip} from '@gravity-ui/uikit';
44
import {ArrowDownToLine, ArrowUpToLine, ChevronDown, ChevronUp} from '@gravity-ui/icons';
55

66
import {Toolbar} from '../Toolbar/Toolbar';
77
import {cn} from '../utils/classname';
88

9+
import i18n from './i18n';
10+
911
import './StructuredYson.scss';
1012

1113
const block = cn('g-ru-structured-yson');
@@ -50,35 +52,38 @@ export const StructuredYsonToolbar: React.FC<StructuredYsonToolbarProps> = ({
5052
size="m"
5153
type="text"
5254
value={filter}
53-
placeholder="Search..."
55+
placeholder={i18n('description_search')}
5456
onUpdate={onFilterChange}
5557
autoFocus={false}
5658
onKeyDown={onEnterKeyDown}
5759
qa={'qa:structuredyson:search'}
5860
/>
59-
<Button
60-
className={block('match-btn')}
61-
view="flat-secondary"
62-
title="Next"
63-
onClick={onNextMatch}
64-
disabled={!count}
65-
pin={'clear-clear'}
66-
qa={'qa:structuredyson:search:next'}
67-
>
68-
<Icon data={ChevronDown} />
69-
</Button>
70-
<Button
71-
className={block('match-btn')}
72-
view="flat-secondary"
73-
title="Back"
74-
onClick={onPrevMatch}
75-
disabled={!count}
76-
pin={'brick-brick'}
77-
qa={'qa:structuredyson:search:prev'}
78-
>
79-
<Icon data={ChevronUp} />
80-
</Button>
81-
<span className={block('match-counter')} title={'Matched rows'}>
61+
62+
<ActionTooltip title={i18n('action_next')}>
63+
<Button
64+
className={block('match-btn')}
65+
view="flat-secondary"
66+
onClick={onNextMatch}
67+
disabled={!count}
68+
pin={'clear-clear'}
69+
qa={'qa:structuredyson:search:next'}
70+
>
71+
<Icon data={ChevronDown} />
72+
</Button>
73+
</ActionTooltip>
74+
<ActionTooltip title={i18n('action_back')}>
75+
<Button
76+
className={block('match-btn')}
77+
view="flat-secondary"
78+
onClick={onPrevMatch}
79+
disabled={!count}
80+
pin={'brick-brick'}
81+
qa={'qa:structuredyson:search:prev'}
82+
>
83+
<Icon data={ChevronUp} />
84+
</Button>
85+
</ActionTooltip>
86+
<span className={block('match-counter')} title={i18n('description_matched-rows')}>
8287
{matchPosition} / {count || 0}
8388
</span>
8489
</React.Fragment>
@@ -93,13 +98,17 @@ export const StructuredYsonToolbar: React.FC<StructuredYsonToolbarProps> = ({
9398
name: 'buttons',
9499
node: (
95100
<span className={block('buttons')}>
96-
<Button title="Expand all" onClick={onExpandAll}>
97-
<Icon data={ArrowDownToLine} />
98-
</Button>
101+
<ActionTooltip title={i18n('action_expand-all')}>
102+
<Button onClick={onExpandAll} view="flat-secondary">
103+
<Icon data={ArrowDownToLine} />
104+
</Button>
105+
</ActionTooltip>
99106
&nbsp;&nbsp;
100-
<Button onClick={onCollapseAll} title="Collapse all">
101-
<Icon data={ArrowUpToLine} />
102-
</Button>
107+
<ActionTooltip title={i18n('action_collapse-all')}>
108+
<Button onClick={onCollapseAll} view="flat-secondary">
109+
<Icon data={ArrowUpToLine} />
110+
</Button>
111+
</ActionTooltip>
103112
</span>
104113
),
105114
},

src/StructuredYson/i18n/en.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"action_collapse-all": "Collapse all",
3+
"action_expand-all": "Expand all",
4+
"action_next": "Next",
5+
"action_back": "Back",
6+
"description_search": "Search...",
7+
"description_matched-rows": "Matched rows",
8+
"description_full-value": "Full value",
9+
"context_case-sensitive-search": "Case sensitive search enadled",
10+
"context_case-sensitive-search-disabled": "Case sensitive search disabled",
11+
"context_items-count": [
12+
" {{count}} item ",
13+
" {{count}} items ",
14+
" {{count}} items ",
15+
" {{count}} items "
16+
]
17+
}

src/StructuredYson/i18n/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {makeKeyset} from '../../i18n';
2+
3+
import en from './en.json';
4+
import ru from './ru.json';
5+
6+
const COMPONENT = 'structured-yson';
7+
8+
export default makeKeyset(COMPONENT, {ru, en});

src/StructuredYson/i18n/ru.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"action_collapse-all": "Свернуть все",
3+
"action_expand-all": "Развернуть все",
4+
"action_next": "Далее",
5+
"action_back": "Назад",
6+
"description_search": "Поиск...",
7+
"description_matched-rows": "Совпадающие строки",
8+
"description_full-value": "Полное значение",
9+
"context_case-sensitive-search": "Поиск с учетом регистра включен",
10+
"context_case-sensitive-search-disabled": "Поиск с учетом регистра отключен",
11+
"context_items-count": [
12+
" {{count}} элемент ",
13+
" {{count}} элемента ",
14+
" {{count}} элементов ",
15+
" {{count}} элементов "
16+
]
17+
}

src/utils/flattenUnipika.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ interface FlattenUnipikaOptions {
5151
matchedState?: {};
5252
settings?: UnipikaSettings;
5353
filter?: string;
54+
caseSensitive?: boolean;
5455
}
5556

5657
export interface FlattenUnipikaResult {
@@ -75,6 +76,7 @@ export function flattenUnipika(
7576
flattenUnipikaImpl(value, 0, ctx);
7677
const searchIndex = makeSearchIndex(ctx.dst, options?.filter, {
7778
settings: options?.settings,
79+
caseSensitive: options?.caseSensitive,
7880
});
7981
return {data: ctx.dst, searchIndex};
8082
}
@@ -438,6 +440,7 @@ function fromUnipikaPrimitive(value: UnipikaPrimitive, level: number): UnipikaFl
438440

439441
interface SearchParams {
440442
settings?: UnipikaSettings;
443+
caseSensitive?: boolean;
441444
}
442445

443446
export interface SearchInfo {
@@ -463,8 +466,8 @@ export function makeSearchIndex(
463466
const res: SearchIndex = {};
464467
for (let i = 0; i < tree.length; ++i) {
465468
const {key, value} = tree[i];
466-
const keyMatch = rowSearchInfo(key, filter, settings);
467-
const valueMatch = rowSearchInfo(value, filter, settings);
469+
const keyMatch = rowSearchInfo(key, filter, settings, options?.caseSensitive);
470+
const valueMatch = rowSearchInfo(value, filter, settings, options?.caseSensitive);
468471
if (keyMatch || valueMatch) {
469472
res[i] = Object.assign({}, keyMatch && {keyMatch}, valueMatch && {valueMatch});
470473
}
@@ -478,6 +481,7 @@ function rowSearchInfo(
478481
v: SearchValue,
479482
filter: string,
480483
settings: UnipikaSettings,
484+
caseSensitive?: boolean,
481485
): Array<number> | undefined {
482486
if (!v) {
483487
return undefined;
@@ -492,12 +496,17 @@ function rowSearchInfo(
492496
tmp = tmp.substring(1, tmp.length - 1); // skip quotes
493497
}
494498
let from = 0;
499+
let normolizedFilter = filter;
500+
if (!caseSensitive) {
501+
tmp = tmp.toLowerCase();
502+
normolizedFilter = filter.toLowerCase();
503+
}
495504
while (from >= 0 && from < tmp.length) {
496-
const index = tmp.indexOf(filter, from);
505+
const index = tmp.indexOf(normolizedFilter, from);
497506
if (-1 === index) {
498507
break;
499508
}
500-
from = index + filter.length;
509+
from = index + normolizedFilter.length;
501510
res.push(index);
502511
}
503512
return res.length ? res : undefined;

0 commit comments

Comments
 (0)