Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/admin/json-editor.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Space, Typography } from "antd";6import jsonic from "jsonic";7import React, { useState } from "react";8import { useIntl } from "react-intl";910import { CSS } from "@cocalc/frontend/app-framework";11import { labels } from "@cocalc/frontend/i18n";12import { COLORS } from "@cocalc/util/theme";1314const { Paragraph } = Typography;1516// this is a simple json editor, basically a textarea that processes its content using jsonic1718interface Props {19value: string;20rows?: number;21onSave: (value: string) => void;22savePosition?: "top-bottom" | "top";23readonly?: boolean;24}2526export const JsonEditor: React.FC<Props> = (props: Props) => {27const intl = useIntl();28const { value, onSave, rows = 3, readonly = false } = props;29const [error, setError] = useState<string>("");30const [focused, setFocused] = useState<boolean>(false);31const [editing, setEditing] = useState<string>(value);3233function doCommit(save: boolean) {34try {35const val = jsonic(editing); // might throw error36const oneLine = JSON.stringify(val);37setEditing(oneLine); // one-line string38setFocused(false);39setError("");40if (save) onSave(oneLine);41} catch (err) {42setError(err.message);43}44}4546function setFormatted() {47try {48setEditing(JSON.stringify(jsonic(editing), null, 2));49} catch (err) {50setError(err.message);51}52}5354function onChange(next: string) {55setEditing(next);56}5758function renderError(): JSX.Element | null {59if (!error) return null;60return <div style={{ color: "red" }}>{error}</div>;61}6263function doCancel() {64setEditing(value); // that's the original value when the component was instantiated65setError("");66setFocused(false);67}6869function onFocus() {70if (readonly) return;71setFormatted();72setFocused(true);73}7475const style: CSS = {76...(!focused && { color: COLORS.GRAY, cursor: "pointer" }),77width: "100%",78};7980function renderMain(): JSX.Element {81if (focused) {82return (83<textarea84spellCheck="false"85onFocus={onFocus}86style={style}87rows={rows}88value={editing}89onChange={(event) => {90onChange(event.target.value);91}}92/>93);94} else {95return (96<Paragraph97onClick={onFocus}98type={readonly ? "secondary" : undefined}99style={{100...(readonly ? {} : { cursor: "pointer" }),101fontFamily: "monospace",102fontSize: "90%",103}}104>105{editing}106</Paragraph>107);108}109}110111function renderButtons() {112if (readonly) return;113return (114<Space>115<Button116size="small"117type={focused ? "primary" : undefined}118disabled={!focused}119onClick={() => doCommit(true)}120>121Commit122</Button>123<Button size="small" disabled={!focused} onClick={doCancel}>124{intl.formatMessage(labels.cancel)}125</Button>126</Space>127);128}129130return (131<div>132<div>{renderMain()}</div>133{renderError()}134{renderButtons()}135</div>136);137};138139140