@discoveryjs/discovery
Version:
Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards
152 lines (134 loc) • 5.9 kB
JavaScript
import { escapeHtml, numDelim } from '../../core/utils/html.js';
import { jsonStringifyInfo } from '../../core/utils/json.js';
import { copyText } from '../../core/utils/copy-text.js';
function formatSize(size) {
if (!size) {
return '';
}
return ', ' + numDelim(size) + ' bytes';
}
function findRootData(context) {
while (context.parent !== null) {
context = context.parent;
}
return context.host[''];
}
export function createValueActionsPopup(host, elementData, elementContext, buildPathForElement) {
return new host.view.Popup({
className: 'view-struct-actions-popup',
render(popupEl, triggerEl, hide) {
const el = triggerEl.parentNode;
const data = elementData.get(el);
let actions = [];
if (typeof data === 'string') {
actions = [
{
text: 'Copy as quoted string',
action: () => copyText(JSON.stringify(data))
},
{
text: 'Copy as unquoted string',
action: () => copyText(JSON.stringify(data).slice(1, -1))
},
{
text: 'Copy a value (unescaped)',
action: () => copyText(data)
}
];
} else {
const path = host.pathToQuery(buildPathForElement(el));
const maxAllowedSize = 1024 * 1024 * 1024;
let jsonFormattedStringifyError = false;
let jsonCompactStringifyError = false;
let compactSize = 0;
let formattedSize = 0;
try {
const { bytes, spaceBytes, circular } = jsonStringifyInfo(data, { space: 4 });
if (circular.length) {
jsonFormattedStringifyError = 'Converting circular structure to JSON';
jsonCompactStringifyError = 'Converting circular structure to JSON';
} else {
compactSize = bytes - spaceBytes;
formattedSize = bytes + 2;
if (compactSize > maxAllowedSize) {
jsonCompactStringifyError = 'Resulting JSON is over 1 Gb';
}
if (formattedSize > maxAllowedSize) {
jsonFormattedStringifyError = 'Resulting JSON is over 1 Gb';
}
}
} catch (e) {
jsonCompactStringifyError = jsonFormattedStringifyError = /Maximum call stack size|too much recursion/i.test(e.message)
? 'Too much nested structure'
: e.message;
}
if (jsonCompactStringifyError) {
jsonCompactStringifyError = 'Can\'t be copied: ' + jsonCompactStringifyError;
}
if (jsonFormattedStringifyError) {
jsonFormattedStringifyError = 'Can\'t be copied: ' + jsonFormattedStringifyError;
}
if (path) {
actions.push({
text: 'Copy path:',
notes: escapeHtml(path),
action: () => copyText(path)
});
const context = elementContext.get(el);
const rootData = findRootData(context);
if (host.action.has('queryAcceptChanges') && host.action.call('queryAcceptChanges', rootData)) {
host.action.has('querySubquery') && actions.push({
groupStart: true,
text: 'Create a subquery from the path',
action: () => host.action.call('querySubquery', path, rootData)
});
host.action.has('queryAppend') && actions.push({
text: 'Append path to current query',
action: () => host.action.call('queryAppend', path, rootData)
});
}
}
actions.push({
groupStart: true,
text: 'Copy as JSON',
notes: `(formatted${formatSize(formattedSize)})`,
error: jsonFormattedStringifyError,
disabled: Boolean(jsonFormattedStringifyError),
action: () => copyText(JSON.stringify(data, null, 4))
});
actions.push({
text: 'Copy as JSON',
notes: `(compact${formatSize(compactSize)})`,
error: jsonCompactStringifyError,
disabled: Boolean(jsonCompactStringifyError),
action: () => copyText(JSON.stringify(data))
});
}
host.view.render(popupEl, {
view: 'menu',
onClick(item) {
hide();
item.action();
},
itemConfig: {
className: '=groupStart ? "group-start" : null'
},
item: [
'html:text',
{
view: 'block',
when: 'notes',
className: 'notes',
content: 'html:notes'
},
{
view: 'block',
when: 'error',
className: 'error',
content: 'text:error'
}
]
}, actions);
}
});
}