@@ -3,31 +3,27 @@ import React from 'react';
33import type * as DT100 from '@gravity-ui/react-data-table' ;
44import DataTable from '@gravity-ui/react-data-table' ;
55import { ActionTooltip , Button , Flex , Icon } from '@gravity-ui/uikit' ;
6- import fill_ from 'lodash/fill' ;
76
87import { CASE_SENSITIVE_JSON_SEARCH } from '../../utils/constants' ;
98import { useSetting } from '../../utils/hooks' ;
109
10+ import { Cell } from './components/Cell' ;
1111import { Filter } from './components/Filter' ;
1212import { FullValueDialog } from './components/FullValueDialog' ;
13- import { MultiHighlightedText } from './components/HighlightedText' ;
1413import { block } from './constants' ;
1514import i18n from './i18n' ;
1615import type { UnipikaSettings , UnipikaValue } from './unipika/StructuredYsonTypes' ;
1716import { flattenUnipika } from './unipika/flattenUnipika' ;
1817import 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
2825import ArrowDownToLineIcon from '@gravity-ui/icons/svgs/arrow-down-to-line.svg' ;
2926import 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
3228import './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 ) , ' ' ) . 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-
443335function 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