diff --git a/public/reearth.yml b/public/reearth.yml
index 9cf9700..020ca86 100644
--- a/public/reearth.yml
+++ b/public/reearth.yml
@@ -129,6 +129,15 @@ extensions:
description: "Specify the fields to display in the infobox, please use field keys separated by commas. Left empty to show all fields."
type: string
ui: multiline
+ - id: title_hidden_fields
+ title: Title Hidden Fields
+ description: "Specify the fields to hide their titles from the infobox, please use field keys separated by commas."
+ type: string
+ ui: multiline
+ - id: title_field
+ title: Title Field
+ description: "Specify the field to use as the title in the infobox."
+ type: string
- id: inspector_block
type: infoboxBlock
name: CMS Data Inspector Block
diff --git a/src/extensions/inspector_block/inspector_block.ts b/src/extensions/inspector_block/inspector_block.ts
index 0ed5e82..76da538 100644
--- a/src/extensions/inspector_block/inspector_block.ts
+++ b/src/extensions/inspector_block/inspector_block.ts
@@ -3,27 +3,26 @@ import html_main from "@distui/inspector_block/main/index.html?raw";
import { GlobalThis } from "@/shared/reearthTypes";
type UIMessage = {
- action: "getProperties";
+ action: "getInspector";
};
const reearth = (globalThis as unknown as GlobalThis).reearth;
reearth.ui.show(html_main);
-const sendProperties = () => {
+const sendInspector = () => {
reearth.ui.postMessage({
- action: "getProperties",
+ action: "getInspector",
payload:
- reearth.layers.selectedFeature?.properties?.__inspector_fields ||
- undefined,
+ reearth.layers.selectedFeature?.properties?.__inspector || undefined,
});
};
-reearth.layers.on("select", sendProperties);
+reearth.layers.on("select", sendInspector);
reearth.extension.on("message", (message: unknown) => {
const msg = message as UIMessage;
- if (msg.action === "getProperties") {
- sendProperties();
+ if (msg.action === "getInspector") {
+ sendInspector();
}
});
diff --git a/src/extensions/inspector_block/main/App.tsx b/src/extensions/inspector_block/main/App.tsx
index 1164b23..95e9ece 100644
--- a/src/extensions/inspector_block/main/App.tsx
+++ b/src/extensions/inspector_block/main/App.tsx
@@ -1,32 +1,36 @@
import useHooks from "./hooks";
-import PropertyValue, { SingleValueProperty } from "./PropertyValue";
+import PropertyItem from "./PropertyItem";
function App() {
- const { properties } = useHooks();
+ const { inspector } = useHooks();
- if (!properties) {
+ if (!inspector) {
return null;
}
return (
- {properties.map((prop) => (
-
-
{prop.name ?? prop.key}
- {Array.isArray(prop.value) ? (
-
- {prop.value.map((item, index) => (
-
- ))}
+ {inspector.title && (
+
{inspector.title}
+ )}
+ {inspector.properties.map((property) =>
+ property.type === "group" ? (
+
+ {!property.hideTitle && (
+
+ {property.name ?? property.key}
+
+ )}
+
+ {property.children?.map((child) => (
+
+ )) ?? null}
- ) : (
-
- )}
-
- ))}
+
+ ) : (
+
+ )
+ )}
);
}
diff --git a/src/extensions/inspector_block/main/PropertyItem.tsx b/src/extensions/inspector_block/main/PropertyItem.tsx
new file mode 100644
index 0000000..6366c04
--- /dev/null
+++ b/src/extensions/inspector_block/main/PropertyItem.tsx
@@ -0,0 +1,43 @@
+import { FC } from "react";
+
+import { Property } from "./hooks";
+import PropertyValue, { SingleValueProperty } from "./PropertyValue";
+
+type PropertyItemProps = {
+ property: Property;
+};
+
+const PropertyItem: FC
= ({ property }) => {
+ if (
+ property.hideTitle &&
+ (property.value === undefined ||
+ property.value === "" ||
+ property.value === null)
+ ) {
+ return null;
+ }
+
+ return (
+
+ {!property.hideTitle && (
+
+ {property.name ?? property.key}
+
+ )}
+ {Array.isArray(property.value) ? (
+
+ {property.value.map((item, index) => (
+
+ ))}
+
+ ) : (
+
+ )}
+
+ );
+};
+
+export default PropertyItem;
diff --git a/src/extensions/inspector_block/main/PropertyValue.tsx b/src/extensions/inspector_block/main/PropertyValue.tsx
index b04dd95..5ab6d43 100644
--- a/src/extensions/inspector_block/main/PropertyValue.tsx
+++ b/src/extensions/inspector_block/main/PropertyValue.tsx
@@ -73,5 +73,5 @@ const PropertyValue: FC = ({ property }) => {
export default PropertyValue;
const isImageUrl = (url: string) => {
- return /\.(jpeg|jpg|gif|png|svg)$/.test(url);
+ return /\.(jpeg|jpg|gif|png|svg)$/.test(url.toLowerCase());
};
diff --git a/src/extensions/inspector_block/main/hooks.ts b/src/extensions/inspector_block/main/hooks.ts
index e317bb2..bc9f1e7 100644
--- a/src/extensions/inspector_block/main/hooks.ts
+++ b/src/extensions/inspector_block/main/hooks.ts
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { postMsg } from "@/shared/utils";
-export type Field = {
+export type Property = {
id: string;
key: string;
type: string;
@@ -15,18 +15,25 @@ export type Field = {
| number[]
| boolean[];
name: string;
+ hideTitle?: boolean;
+ children?: Property[];
+};
+
+export type Inspector = {
+ title?: string;
+ properties: Property[];
};
export default () => {
- const [properties, setProperties] = useState(null);
+ const [inspector, setInspector] = useState(null);
useEffect(() => {
- postMsg("getProperties");
+ postMsg("getInspector");
}, []);
const handleMessage = (e: MessageEvent) => {
- if (e.data.action === "getProperties") {
- setProperties(e.data.payload);
+ if (e.data.action === "getInspector") {
+ setInspector(e.data.payload);
}
};
@@ -38,6 +45,6 @@ export default () => {
}, []);
return {
- properties,
+ inspector,
};
};
diff --git a/src/extensions/visualizer/visualizer.ts b/src/extensions/visualizer/visualizer.ts
index b2f7f6f..08e1c66 100644
--- a/src/extensions/visualizer/visualizer.ts
+++ b/src/extensions/visualizer/visualizer.ts
@@ -13,6 +13,8 @@ type VisualizationConfig = {
type InspectorConfig = {
display_fields?: string;
+ title_hidden_fields?: string;
+ title_field?: string;
};
type WidgetProperty = {
@@ -27,6 +29,8 @@ type ItemField = {
type: string;
value: unknown;
name?: string;
+ group?: string;
+ children?: ItemField[];
};
type Item = {
@@ -132,9 +136,13 @@ const generateGeoJSON = (
// Convert items to GeoJSON features
const inspectorConfig =
(reearth.extension?.widget?.property as WidgetProperty)?.inspector || {};
+
const displayFields = inspectorConfig.display_fields
? inspectorConfig.display_fields.split(",").map((f) => f.trim())
: [];
+ const titleHiddenFields = inspectorConfig.title_hidden_fields
+ ? inspectorConfig.title_hidden_fields.split(",").map((f) => f.trim())
+ : [];
const features = items
.map((item) => {
@@ -146,8 +154,18 @@ const generateGeoJSON = (
properties[field.key] = field.value;
});
- // Add filtered fields for inspector
- properties.__inspector_fields = filterFields(item.fields, displayFields);
+ // Add property for inspector
+ properties.__inspector = {
+ title: inspectorConfig.title_field
+ ? item.fields.find((f) => f.key === inspectorConfig.title_field)
+ ?.value
+ : undefined,
+ properties: processProperties(
+ item.fields,
+ displayFields,
+ titleHiddenFields
+ ),
+ };
// Get location
const coordinates = [];
@@ -163,7 +181,7 @@ const generateGeoJSON = (
return null;
}
- coordinates.push(lng, lat);
+ coordinates.push(Number(lng), Number(lat));
} else if (config.location_type === "lng_lat_array_field") {
const latLngArray = item.fields.find(
(f) => f.key === config.longitude_latitude_array_field_key
@@ -172,7 +190,7 @@ const generateGeoJSON = (
console.log(`Invalid Longitude & Latitude array for item ${item.id}`);
return null;
}
- coordinates.push(latLngArray[0], latLngArray[1]);
+ coordinates.push(Number(latLngArray[0]), Number(latLngArray[1]));
} else if (config.location_type === "geojson_field") {
try {
const geojson = item.fields.find(
@@ -264,3 +282,37 @@ const filterFields = (
}
return filteredFields;
};
+
+const processProperties = (
+ originalFields: ItemField[],
+ displayFieldKeys: string[],
+ titleHiddenFieldKeys: string[]
+) => {
+ const fieldsToProcess = filterFields(originalFields, displayFieldKeys).map(
+ (field) => ({
+ ...field,
+ hideTitle: titleHiddenFieldKeys.includes(field.key),
+ })
+ );
+
+ const result = [];
+ const byGroup = new Map();
+
+ for (const field of fieldsToProcess) {
+ if (field.group) {
+ const list = byGroup.get(field.group) || [];
+ list.push(field);
+ byGroup.set(field.group, list);
+ } else {
+ result.push(field);
+ }
+ }
+
+ for (const field of result) {
+ if (field.type === "group") {
+ field.children = byGroup.get(field.value) ?? [];
+ }
+ }
+
+ return result;
+};
diff --git a/src/shared/global.css b/src/shared/global.css
index 9bc2dbd..453fcf3 100644
--- a/src/shared/global.css
+++ b/src/shared/global.css
@@ -140,7 +140,7 @@ input[type="number"] {
@layer utilities {
.prose {
@apply text-xs leading-relaxed max-w-none;
- font-size: 12px;
+ font-size: 14px;
}
.prose p {