diff --git a/package-lock.json b/package-lock.json index bcd4264b7..0e4fd82c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "6.7.0", "license": "BSD-2-Clause", "dependencies": { + "@monaco-editor/react": "^4.5.1", "@reduxjs/toolkit": "^1.9.5", "@terrestris/base-util": "^1.0.1", "@terrestris/mapfish-print-manager": "^10.0.0", @@ -14395,6 +14396,11 @@ "lodash": "^4.17.21" } }, + "node_modules/geostyler/node_modules/monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, "node_modules/geotiff": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz", @@ -19185,9 +19191,10 @@ } }, "node_modules/monaco-editor": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", - "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==", + "peer": true }, "node_modules/mrmime": { "version": "1.0.1", @@ -40136,6 +40143,13 @@ "react-color": "^2.19.3", "react-rnd": "^10.3.7", "typescript-json-schema": "^0.55.0" + }, + "dependencies": { + "monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + } } }, "geostyler-cql-parser": { @@ -43836,9 +43850,10 @@ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "monaco-editor": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", - "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==", + "peer": true }, "mrmime": { "version": "1.0.1", diff --git a/package.json b/package.json index 3b31bcc3e..54e489926 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "not IE 11" ], "dependencies": { + "@monaco-editor/react": "^4.5.1", "@reduxjs/toolkit": "^1.9.5", "@terrestris/base-util": "^1.0.1", "@terrestris/mapfish-print-manager": "^10.0.0", diff --git a/src/bootstrap.tsx b/src/bootstrap.tsx index 1bf4e52ff..bf43166d6 100644 --- a/src/bootstrap.tsx +++ b/src/bootstrap.tsx @@ -1,5 +1,9 @@ import React from 'react'; +import { + loader +} from '@monaco-editor/react'; + import { Alert, ConfigProvider, @@ -564,6 +568,12 @@ const matchRole = (role: string | RegExp, element: string): boolean => { const renderApp = async () => { try { + loader.config({ + paths: { + vs: './vs' + } + }); + const keycloak = await initKeycloak(); if (keycloak) { diff --git a/src/components/DisplayField/index.tsx b/src/components/DisplayField/index.tsx index ae7e6745f..26b672691 100644 --- a/src/components/DisplayField/index.tsx +++ b/src/components/DisplayField/index.tsx @@ -12,18 +12,22 @@ import { useTranslation } from 'react-i18next'; +import JsonModal from '../JsonModal'; + export type ValueType = string | number | boolean | moment.Moment; export type DisplayFieldProps = { format?: string; suffix?: string; value?: ValueType | ValueType[]; + label?: string; }; export const DisplayField: React.FC = ({ format = 'DD.MM.YYYY', suffix, value, + label, ...passThroughProps }): JSX.Element => { @@ -53,6 +57,27 @@ export const DisplayField: React.FC = ({ displayText = value.join(', '); } + const isJson = (val: ValueType | ValueType[]): val is string => { + let v = typeof val !== 'string' ? JSON.stringify(val) : val; + + try { + v = JSON.parse(v); + } catch (e) { + return false; + } + + return typeof v === 'object' && v !== null; + }; + + if (value && isJson(value)) { + return ( + + ); + } + return ( ', () => { + + it('is defined', () => { + expect(JsonModal).toBeDefined(); + }); + +}); diff --git a/src/components/JsonModal/index.tsx b/src/components/JsonModal/index.tsx new file mode 100644 index 000000000..7a11bf34c --- /dev/null +++ b/src/components/JsonModal/index.tsx @@ -0,0 +1,109 @@ +import React, { + useState +} from 'react'; + +import { + faBoxOpen +} from '@fortawesome/free-solid-svg-icons'; +import { + FontAwesomeIcon +} from '@fortawesome/react-fontawesome'; + +import Editor from '@monaco-editor/react'; + +import { + Button, + Modal, + ModalProps +} from 'antd'; + +import { + useTranslation +} from 'react-i18next'; + +import './index.less'; + +export type ValueType = string | number | boolean | moment.Moment; + +export type JsonModalProps = { + value?: string; + label?: string; +} & ModalProps ; + +export const JsonModal: React.FC = ({ + value, + label, + ...passThroughProps +}): JSX.Element => { + + const [isOpen, setIsOpen] = useState(false); + + const { + t + } = useTranslation(); + + const onButtonClick = () => { + setIsOpen(true); + }; + + const onCancel = () => { + setIsOpen(false); + }; + + if (!value) { + return <>; + } + + return ( + <> + + + + + + ); +}; + +export default JsonModal; diff --git a/src/components/ToolMenu/FeatureInfo/FeatureInfoForm/index.tsx b/src/components/ToolMenu/FeatureInfo/FeatureInfoForm/index.tsx index 42e105efb..1e9a0eea9 100644 --- a/src/components/ToolMenu/FeatureInfo/FeatureInfoForm/index.tsx +++ b/src/components/ToolMenu/FeatureInfo/FeatureInfoForm/index.tsx @@ -38,19 +38,7 @@ export const FeatureInfoForm: React.FC = ({ form.setFieldsValue(feature.getProperties()); }, [feature, form]); - const createFormItem = (fieldCfg: any) => { - let field: React.ReactNode; - - const createReadOnlyComponent = (fieldConfig: any) => { - return ( - - ); - }; - - field = createReadOnlyComponent(fieldCfg); - + const createFormItem = (fieldCfg: PropertyFormItemReadConfig) => { return ( = ({ label={fieldCfg.displayName || fieldCfg.propertyName} {...fieldCfg.fieldProps} > - {field} + ); }; diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 17ab9c98f..84ee7dbd1 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -213,6 +213,9 @@ export default { PaginationToolbar: { copyAsGeoJson: 'Als GeoJSON kopieren (inkl. Geometrie)', copyAsObject: 'Als Objekt kopieren (nur angezeigte Werte)' + }, + JsonModal: { + buttonTitle: 'Öffne {{propertyName}}' } } }, @@ -430,6 +433,9 @@ export default { PaginationToolbar: { copyAsGeoJson: 'Copy as GeoJSON (incl. geometry)', copyAsObject: 'Copy as object (displayed values only)' + }, + JsonModal: { + buttonTitle: 'Show {{propertyName}}' } } } diff --git a/webpack.common.js b/webpack.common.js index 19c091735..ff30ed512 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -82,6 +82,9 @@ module.exports = { patterns: [{ from: path.join(__dirname, 'resources', 'config', 'gis-client-config.js'), to: '.' + }, { + from: './node_modules/monaco-editor/min/vs', + to: 'vs' }] }), new webpack.DefinePlugin({