Skip to content

Commit 85e2b80

Browse files
committed
fix: review
1 parent 91bf0d2 commit 85e2b80

File tree

6 files changed

+243
-229
lines changed

6 files changed

+243
-229
lines changed

src/components/JsonViewer/JsonViewer.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
position: absolute;
3737

3838
margin-top: -2px;
39-
margin-left: -5ex;
39+
margin-left: -3ex;
4040
}
4141

4242
&__match-counter {
@@ -72,7 +72,7 @@
7272
}
7373

7474
&__filter {
75-
max-width: 300px;
75+
width: 300px;
7676
}
7777

7878
&__filtered {

src/components/JsonViewer/JsonViewer.tsx

Lines changed: 4 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,27 @@ import React from 'react';
33
import type * as DT100 from '@gravity-ui/react-data-table';
44
import DataTable from '@gravity-ui/react-data-table';
55
import {ActionTooltip, Button, Flex, Icon} from '@gravity-ui/uikit';
6-
import fill_ from 'lodash/fill';
76

87
import {CASE_SENSITIVE_JSON_SEARCH} from '../../utils/constants';
98
import {useSetting} from '../../utils/hooks';
109

10+
import {Cell} from './components/Cell';
1111
import {Filter} from './components/Filter';
1212
import {FullValueDialog} from './components/FullValueDialog';
13-
import {MultiHighlightedText} from './components/HighlightedText';
1413
import {block} from './constants';
1514
import i18n from './i18n';
1615
import type {UnipikaSettings, UnipikaValue} from './unipika/StructuredYsonTypes';
1716
import {flattenUnipika} from './unipika/flattenUnipika';
1817
import type {
19-
BlockType,
2018
CollapsedState,
2119
FlattenUnipikaResult,
2220
SearchInfo,
2321
UnipikaFlattenTreeItem,
2422
} from './unipika/flattenUnipika';
25-
import {defaultUnipikaSettings, unipika} from './unipika/unipika';
26-
import {getHightLightedClassName} from './utils';
23+
import {unipika} from './unipika/unipika';
2724

2825
import ArrowDownToLineIcon from '@gravity-ui/icons/svgs/arrow-down-to-line.svg';
2926
import ArrowUpFromLineIcon from '@gravity-ui/icons/svgs/arrow-up-from-line.svg';
30-
import ArrowUpRightFromSquareIcon from '@gravity-ui/icons/svgs/arrow-up-right-from-square.svg';
3127

3228
import './JsonViewer.scss';
3329

@@ -268,12 +264,12 @@ export function JsonViewer({
268264
<Flex gap={2} wrap="nowrap" className={block('toolbar')}>
269265
<Flex gap={1} wrap="nowrap">
270266
<ActionTooltip title={i18n('action_expand-all')}>
271-
<Button onClick={onExpandAll}>
267+
<Button onClick={onExpandAll} view="flat-secondary">
272268
<Icon data={ArrowDownToLineIcon} />
273269
</Button>
274270
</ActionTooltip>
275271
<ActionTooltip title={i18n('action_collapse-all')}>
276-
<Button onClick={onCollapseAll}>
272+
<Button onClick={onCollapseAll} view="flat-secondary">
277273
<Icon data={ArrowUpFromLineIcon} />
278274
</Button>
279275
</ActionTooltip>
@@ -336,224 +332,6 @@ export function JsonViewer({
336332
);
337333
}
338334

339-
const OFFSETS_BY_LEVEL: {[key: number]: React.ReactNode} = {};
340-
341-
function getLevelOffsetSpaces(level: number) {
342-
let res = OFFSETS_BY_LEVEL[level];
343-
if (!res) {
344-
const __html = fill_(Array(level * 4), '&nbsp;').join('');
345-
res = OFFSETS_BY_LEVEL[level] = <span dangerouslySetInnerHTML={{__html}} />;
346-
}
347-
return res;
348-
}
349-
350-
interface CellProps {
351-
matched: SearchInfo;
352-
row: UnipikaFlattenTreeItem;
353-
settings?: UnipikaSettings;
354-
collapsedState?: {readonly [key: string]: boolean};
355-
onToggleCollapse: (path: string) => void;
356-
filter?: string;
357-
index: number;
358-
showFullText: (index: number) => void;
359-
}
360-
361-
function Cell(props: CellProps) {
362-
const {
363-
row: {level, open, close, key, value, hasDelimiter, path, collapsed, depth},
364-
settings,
365-
onToggleCollapse,
366-
matched,
367-
filter,
368-
showFullText,
369-
index,
370-
} = props;
371-
372-
const handleToggleCollapse = React.useCallback(() => {
373-
if (!path) {
374-
return;
375-
}
376-
onToggleCollapse(path);
377-
}, [path, onToggleCollapse]);
378-
379-
const handleShowFullText = React.useCallback(() => {
380-
showFullText(index);
381-
}, [showFullText, index]);
382-
383-
return (
384-
<div className={block('cell', 'unipika')}>
385-
{getLevelOffsetSpaces(level)}
386-
{path && (
387-
<ToggleCollapseButton
388-
collapsed={collapsed}
389-
path={path}
390-
onToggle={handleToggleCollapse}
391-
/>
392-
)}
393-
<Key text={key} settings={settings} matched={matched?.keyMatch} filter={filter} />
394-
{open && <OpenClose type={open} settings={settings} />}
395-
{depth !== undefined && (
396-
<span className={'unipika'}>{i18n('context_items-count', {count: depth})}</span>
397-
)}
398-
{value !== undefined && (
399-
<Value
400-
text={value}
401-
settings={settings}
402-
matched={matched?.valueMatch}
403-
filter={filter}
404-
showFullText={handleShowFullText}
405-
/>
406-
)}
407-
{collapsed && depth === undefined && <span className={'unipika'}>...</span>}
408-
{close && <OpenClose type={close} settings={settings} close />}
409-
{hasDelimiter && <SlaveText text={','} />}
410-
</div>
411-
);
412-
}
413-
414-
interface KeyProps {
415-
text: UnipikaFlattenTreeItem['key'] | UnipikaFlattenTreeItem['value'];
416-
settings?: UnipikaSettings;
417-
filter?: string;
418-
matched?: Array<number>;
419-
}
420-
421-
function Key(props: KeyProps) {
422-
const text: React.ReactNode = renderKeyWithFilter(props);
423-
return text ? (
424-
<React.Fragment>
425-
{text}
426-
<SlaveText text={': '} />
427-
</React.Fragment>
428-
) : null;
429-
}
430-
431-
interface ValueProps extends KeyProps {
432-
showFullText?: () => void;
433-
}
434-
435-
function Value(props: ValueProps) {
436-
return (
437-
<React.Fragment>
438-
{renderValueWithFilter(props, block('value', {type: props.text?.$type}))}
439-
</React.Fragment>
440-
);
441-
}
442-
443335
function asModifier(path = '') {
444336
return path.replace(/[^-\w\d]/g, '_');
445337
}
446-
447-
function renderValueWithFilter(props: ValueProps, className: string) {
448-
if ('string' === props.text?.$type) {
449-
return renderStringWithFilter(props, className, 100);
450-
}
451-
return renderWithFilter(props, block('value'));
452-
}
453-
454-
function renderStringWithFilter(props: ValueProps, className: string, maxWidth = Infinity) {
455-
const {text, settings = defaultUnipikaSettings, matched = [], filter, showFullText} = props;
456-
const tmp = unipika.format(text, {...settings, asHTML: false});
457-
const visible = tmp.substr(1, Math.min(tmp.length - 2, maxWidth));
458-
const truncated = visible.length < tmp.length - 2;
459-
let hasHiddenMatch = false;
460-
if (truncated) {
461-
for (let i = matched.length - 1; i >= 0; --i) {
462-
if (visible.length < matched[i] + (filter?.length || 0)) {
463-
hasHiddenMatch = true;
464-
break;
465-
}
466-
}
467-
}
468-
return (
469-
<span>
470-
<MultiHighlightedText
471-
className={getHightLightedClassName(className)}
472-
text={visible}
473-
starts={matched}
474-
length={filter?.length}
475-
/>
476-
{truncated && (
477-
<span
478-
className={block('filtered', {
479-
highlighted: hasHiddenMatch,
480-
clickable: true,
481-
})}
482-
onClick={showFullText}
483-
>
484-
{'\u2026'}
485-
<Icon data={ArrowUpRightFromSquareIcon} />
486-
</span>
487-
)}
488-
</span>
489-
);
490-
}
491-
492-
function renderKeyWithFilter(props: KeyProps) {
493-
if (!props?.text) {
494-
return null;
495-
}
496-
return renderStringWithFilter(props, block('key'));
497-
}
498-
499-
function renderWithFilter(props: KeyProps, className: string) {
500-
const {text, filter, settings, matched} = props;
501-
let res: React.ReactNode = null;
502-
if (matched && filter) {
503-
const tmp = unipika.format(text, {...settings, asHTML: false});
504-
res = (
505-
<MultiHighlightedText
506-
className={getHightLightedClassName(className)}
507-
text={tmp}
508-
starts={matched}
509-
length={filter?.length}
510-
/>
511-
);
512-
} else {
513-
res = text ? formatValue(text, settings) : undefined;
514-
}
515-
return res ? res : null;
516-
}
517-
518-
function SlaveText({text}: {text: string}) {
519-
return <span className={''}>{text}</span>;
520-
}
521-
522-
function OpenClose(props: {
523-
type: BlockType;
524-
close?: boolean;
525-
settings: JsonViewerProps['unipikaSettings'];
526-
}) {
527-
const {type, close} = props;
528-
switch (type) {
529-
case 'array':
530-
return <SlaveText text={close ? ']' : '['} />;
531-
case 'object':
532-
return <SlaveText text={close ? '}' : '{'} />;
533-
}
534-
}
535-
536-
interface ToggleCollapseProps {
537-
collapsed?: boolean;
538-
path?: UnipikaFlattenTreeItem['path'];
539-
onToggle: () => void;
540-
}
541-
542-
function ToggleCollapseButton(props: ToggleCollapseProps) {
543-
const {collapsed, onToggle, path} = props;
544-
return (
545-
<span title={path} className={block('collapsed')}>
546-
<Button onClick={onToggle} view="flat-secondary" size={'s'}>
547-
<span className={'unipika'}>{collapsed ? '[+]' : '[-]'}</span>
548-
</Button>
549-
</span>
550-
);
551-
}
552-
553-
function formatValue(
554-
value: UnipikaFlattenTreeItem['key'] | UnipikaFlattenTreeItem['value'],
555-
settings: UnipikaSettings = defaultUnipikaSettings,
556-
) {
557-
const __html = unipika.formatValue(value, {...defaultUnipikaSettings, ...settings}, 0);
558-
return <span className={'unipika'} dangerouslySetInnerHTML={{__html}} />;
559-
}

0 commit comments

Comments
 (0)