Line data Source code
1 : import * as React from 'react';
2 : import * as styles from './json_view.module.css';
3 :
4 : import { useJsonViewerState } from './json_view_state.js';
5 :
6 1 : export function isFloat(n: number) { return (Number(n) === n && n % 1 !== 0) || isNaN(n); }
7 :
8 : interface ValueProps {
9 : value: unknown;
10 : }
11 :
12 1 : export function JsonLiteral(props: ValueProps) {
13 1 : const { value } = props;
14 1 : if (value instanceof URL) {
15 0 : return <UrlLiteral>{value}</UrlLiteral>;
16 : }
17 1 : if (typeof value === 'string') {
18 1 : return <StringLiteral>{value}</StringLiteral>;
19 : }
20 1 : if (value === true) {
21 1 : return <TrueLiteral />;
22 : }
23 1 : if (value === false) {
24 1 : return <FalseLiteral />;
25 : }
26 1 : if (value === null) {
27 1 : return <NullLiteral />;
28 : }
29 1 : if (value === undefined) {
30 1 : return <UndefinedLiteral />;
31 : }
32 1 : if (value instanceof Date) {
33 0 : return <DateLiteral>{value}</DateLiteral>;
34 : }
35 :
36 1 : if (typeof value === 'number' && isNaN(value)) {
37 0 : return <NanLiteral />;
38 1 : } else if (typeof value === 'number' && isFloat(value)) {
39 0 : return <FloatLiteral>{value}</FloatLiteral>;
40 1 : } else if (typeof value === 'bigint') {
41 1 : return <BigintLiteral>{value}</BigintLiteral>;
42 1 : } else if (typeof value === 'number') {
43 1 : return <IntLiteral>{value}</IntLiteral>;
44 : }
45 :
46 0 : return null;
47 1 : };
48 :
49 1 : export function bigIntToString(bi?: BigInt | string) {
50 1 : if (bi === undefined) {
51 0 : return '0n';
52 1 : } else if (typeof bi === 'string') {
53 1 : try {
54 1 : bi = BigInt(bi);
55 1 : } catch (e) {
56 0 : return '0n';
57 0 : }
58 : }
59 1 : return bi ? bi.toString() + 'n' : '0n';
60 1 : };
61 :
62 1 : function StringLiteral(props: { children: string }) {
63 1 : const { variant, shortenTextAfterLength: length = 30, stringEllipsis = '...' } = useJsonViewerState();
64 1 : const childrenStr = props.children as string;
65 1 : const [shorten, setShorten] = React.useState(length && childrenStr.length > length);
66 1 : React.useEffect(() => setShorten(length && childrenStr.length > length), [length]);
67 :
68 1 : const style: React.CSSProperties = length > 0 ? {
69 1 : cursor: childrenStr.length <= length ? 'initial' : 'pointer',
70 1 : } : {};
71 :
72 1 : const onClick = length > 0 && childrenStr.length > length ? () => setShorten(!shorten) : undefined;
73 1 : const text = shorten ? `${childrenStr.slice(0, length)}${stringEllipsis}` : childrenStr;
74 1 : const cls = shorten ? styles.literal_string_short : styles.literal_string;
75 :
76 : // The explorer shows strings without quotes, so an empty string needs a placeholder
77 1 : if (variant === 'explorer') {
78 1 : if (childrenStr.length === 0) {
79 1 : return <span className={styles.explorer_placeholder}>empty</span>;
80 : }
81 1 : return (
82 1 : <span className={cls} style={style} onClick={onClick}>
83 1 : {text}
84 1 : </span>
85 : );
86 1 : }
87 1 : return (
88 1 : <React.Fragment>
89 1 : <LiteralQuotes />
90 1 : <span className={cls} style={style} onClick={onClick}>
91 1 : {text}
92 1 : </span>
93 1 : <LiteralQuotes />
94 1 : </React.Fragment>
95 : );
96 1 : };
97 :
98 1 : function LiteralQuotes() {
99 1 : const { variant } = useJsonViewerState();
100 1 : if (variant === 'explorer') return null;
101 1 : return (
102 1 : <span className={styles.literal_quotes}>
103 : "
104 1 : </span>
105 : );
106 1 : };
107 :
108 1 : function TrueLiteral() {
109 1 : return (
110 1 : <span className={styles.literal_true}>
111 1 : {true.toString()}
112 1 : </span>
113 : );
114 1 : };
115 :
116 1 : function FalseLiteral() {
117 1 : return (
118 1 : <span className={styles.literal_false}>
119 1 : {false.toString()}
120 1 : </span>
121 : );
122 1 : };
123 :
124 0 : function FloatLiteral(props: { children: number }) {
125 0 : return (
126 0 : <span className={styles.literal_float}>
127 0 : {props.children.toString()}
128 0 : </span>
129 : );
130 0 : };
131 :
132 1 : function IntLiteral(props: { children: number }) {
133 1 : return (
134 1 : <span className={styles.literal_int}>
135 1 : {props.children.toString()}
136 1 : </span>
137 : );
138 1 : };
139 :
140 1 : function BigintLiteral(props: { children: bigint }) {
141 1 : return (
142 1 : <span className={styles.literal_bigint}>
143 1 : {bigIntToString(props.children.toString())}
144 1 : </span>
145 : );
146 1 : };
147 :
148 0 : function UrlLiteral(props: { children: URL }) {
149 0 : return (
150 0 : <a className={styles.literal_url} href={props.children?.href} target="_blank" rel="noopener noreferrer">
151 0 : <LiteralQuotes />
152 0 : {props.children?.href}
153 0 : <LiteralQuotes />
154 0 : </a>
155 : );
156 0 : };
157 :
158 0 : function DateLiteral(props: { children?: Date }) {
159 0 : const childStr = props.children instanceof Date ? props.children.toLocaleString() : props.children;
160 0 : return (
161 0 : <span className={styles.literal_date}>
162 0 : {childStr}
163 0 : </span>
164 : );
165 0 : };
166 :
167 1 : function UndefinedLiteral() {
168 1 : return (
169 1 : <span className={styles.literal_undefined}>
170 : undefined
171 1 : </span>
172 : );
173 1 : };
174 :
175 1 : function NullLiteral() {
176 1 : return (
177 1 : <span className={styles.literal_null}>
178 : null
179 1 : </span>
180 : );
181 1 : };
182 :
183 0 : function NanLiteral() {
184 0 : return (
185 0 : <span className={styles.literal_nan}>
186 : nan
187 0 : </span>
188 : );
189 0 : };
|