editor panel improve experience
This commit is contained in:
@@ -47,13 +47,9 @@ import {
|
|||||||
} from "@/uhm/lib/editor/snapshot/editorSnapshot";
|
} from "@/uhm/lib/editor/snapshot/editorSnapshot";
|
||||||
import {
|
import {
|
||||||
buildClientEntityId,
|
buildClientEntityId,
|
||||||
formatEntityNamesForDisplay,
|
|
||||||
mergeEntitySearchResults,
|
mergeEntitySearchResults,
|
||||||
} from "@/uhm/lib/editor/entity/entityBinding";
|
} from "@/uhm/lib/editor/entity/entityBinding";
|
||||||
import { buildFeatureEntityPatch } from "@/uhm/lib/editor/entity/entityBinding";
|
import { buildFeatureEntityPatch } from "@/uhm/lib/editor/entity/entityBinding";
|
||||||
import {
|
|
||||||
formatBindingIdsForDisplay,
|
|
||||||
} from "@/uhm/lib/editor/geometry/geometryMetadata";
|
|
||||||
import {
|
import {
|
||||||
loadBackgroundLayerVisibilityFromStorage,
|
loadBackgroundLayerVisibilityFromStorage,
|
||||||
persistBackgroundLayerVisibility,
|
persistBackgroundLayerVisibility,
|
||||||
@@ -826,10 +822,6 @@ export default function Page() {
|
|||||||
setGeometryMetaForm((prev) => ({ ...prev, [key]: value }));
|
setGeometryMetaForm((prev) => ({ ...prev, [key]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEntityIdsChange = (values: string[]) => {
|
|
||||||
setSelectedGeometryEntityIds(uniqueEntityIds(values));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddEntityRefToProject = useCallback((entity: Entity) => {
|
const handleAddEntityRefToProject = useCallback((entity: Entity) => {
|
||||||
const id = String(entity.id || "").trim();
|
const id = String(entity.id || "").trim();
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
@@ -1600,19 +1592,6 @@ export default function Page() {
|
|||||||
{!wikiOnly && selectedFeature ? (
|
{!wikiOnly && selectedFeature ? (
|
||||||
<SelectedGeometryPanel
|
<SelectedGeometryPanel
|
||||||
selectedFeatures={selectedFeatures}
|
selectedFeatures={selectedFeatures}
|
||||||
selectedFeatureEntitySummary={
|
|
||||||
selectedFeature
|
|
||||||
? formatEntityNamesForDisplay(selectedFeature, entities)
|
|
||||||
: "Chưa gắn"
|
|
||||||
}
|
|
||||||
selectedFeatureBindingSummary={
|
|
||||||
selectedFeature
|
|
||||||
? formatBindingIdsForDisplay(selectedFeature)
|
|
||||||
: "Không có"
|
|
||||||
}
|
|
||||||
entities={entities}
|
|
||||||
selectedGeometryEntityIds={selectedGeometryEntityIds}
|
|
||||||
onEntityIdsChange={handleEntityIdsChange}
|
|
||||||
entityTypeOptions={GEOMETRY_TYPE_OPTIONS}
|
entityTypeOptions={GEOMETRY_TYPE_OPTIONS}
|
||||||
geometryMetaForm={geometryMetaForm}
|
geometryMetaForm={geometryMetaForm}
|
||||||
onGeometryMetaFormChange={handleGeometryMetaFormChange}
|
onGeometryMetaFormChange={handleGeometryMetaFormChange}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState, type KeyboardEvent } from "react";
|
import { useMemo, useState, type CSSProperties, type KeyboardEvent } from "react";
|
||||||
import NewBadge from "@/uhm/components/editor/NewBadge";
|
import NewBadge from "@/uhm/components/editor/NewBadge";
|
||||||
|
|
||||||
type GeometryChoice = {
|
type GeometryChoice = {
|
||||||
@@ -49,6 +49,16 @@ export default function GeometryBindingPanel({
|
|||||||
if (!selectedGeometryId) return null;
|
if (!selectedGeometryId) return null;
|
||||||
return rows.find((g) => g.id === selectedGeometryId) || null;
|
return rows.find((g) => g.id === selectedGeometryId) || null;
|
||||||
}, [rows, selectedGeometryId]);
|
}, [rows, selectedGeometryId]);
|
||||||
|
const visibleRows = useMemo(() => {
|
||||||
|
return rows
|
||||||
|
.filter((g) => g.id !== selectedGeometryId)
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aBound = bindingSet.has(a.id);
|
||||||
|
const bBound = bindingSet.has(b.id);
|
||||||
|
if (aBound !== bBound) return aBound ? -1 : 1;
|
||||||
|
return a.id.localeCompare(b.id);
|
||||||
|
});
|
||||||
|
}, [bindingSet, rows, selectedGeometryId]);
|
||||||
|
|
||||||
const handleFocusKeyDown = (event: KeyboardEvent<HTMLDivElement>, geometryId: string) => {
|
const handleFocusKeyDown = (event: KeyboardEvent<HTMLDivElement>, geometryId: string) => {
|
||||||
if (!canFocusGeometry) return;
|
if (!canFocusGeometry) return;
|
||||||
@@ -174,8 +184,7 @@ export default function GeometryBindingPanel({
|
|||||||
|
|
||||||
{collapsed ? null : rows.length ? (
|
{collapsed ? null : rows.length ? (
|
||||||
<div style={{ marginTop: "10px", display: "grid", gap: "6px", maxHeight: 250, overflowY: "auto", paddingRight: 4 }}>
|
<div style={{ marginTop: "10px", display: "grid", gap: "6px", maxHeight: 250, overflowY: "auto", paddingRight: 4 }}>
|
||||||
{rows
|
{visibleRows
|
||||||
.filter((g) => g.id !== selectedGeometryId)
|
|
||||||
.map((g) => {
|
.map((g) => {
|
||||||
const isBound = bindingSet.has(g.id);
|
const isBound = bindingSet.has(g.id);
|
||||||
return (
|
return (
|
||||||
@@ -184,8 +193,8 @@ export default function GeometryBindingPanel({
|
|||||||
style={{
|
style={{
|
||||||
padding: "8px",
|
padding: "8px",
|
||||||
borderRadius: "6px",
|
borderRadius: "6px",
|
||||||
border: "1px solid #1f2937",
|
border: isBound ? "1px solid rgba(20, 184, 166, 0.65)" : "1px solid #1f2937",
|
||||||
background: "transparent",
|
background: isBound ? "rgba(20, 184, 166, 0.12)" : "transparent",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 10,
|
gap: 10,
|
||||||
@@ -219,6 +228,7 @@ export default function GeometryBindingPanel({
|
|||||||
>
|
>
|
||||||
{g.label || g.id}
|
{g.label || g.id}
|
||||||
</span>
|
</span>
|
||||||
|
{isBound ? <span style={boundBadgeStyle}>bound</span> : null}
|
||||||
{g.isNew ? <NewBadge /> : null}
|
{g.isNew ? <NewBadge /> : null}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -283,6 +293,24 @@ export default function GeometryBindingPanel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const boundBadgeStyle: CSSProperties = {
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
flex: "0 0 auto",
|
||||||
|
height: 17,
|
||||||
|
padding: "0 6px",
|
||||||
|
borderRadius: 999,
|
||||||
|
border: "1px solid rgba(45, 212, 191, 0.5)",
|
||||||
|
background: "rgba(20, 184, 166, 0.18)",
|
||||||
|
color: "#99f6e4",
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: 900,
|
||||||
|
lineHeight: 1,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: 0,
|
||||||
|
};
|
||||||
|
|
||||||
function LockIcon() {
|
function LockIcon() {
|
||||||
return (
|
return (
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
|
|||||||
@@ -39,6 +39,22 @@ export default function ProjectEntityRefsPanel({
|
|||||||
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
const [activeEntityId, setActiveEntityId] = useState<string | null>(null);
|
const [activeEntityId, setActiveEntityId] = useState<string | null>(null);
|
||||||
|
const selectedEntityIdSet = useMemo(
|
||||||
|
() => new Set((selectedGeometryEntityIds || []).map(String)),
|
||||||
|
[selectedGeometryEntityIds]
|
||||||
|
);
|
||||||
|
const sortedEntityRefs = useMemo(() => {
|
||||||
|
const rows = [...(entityRefs || [])];
|
||||||
|
rows.sort((a, b) => {
|
||||||
|
const aBound = selectedEntityIdSet.has(String(a.id));
|
||||||
|
const bBound = selectedEntityIdSet.has(String(b.id));
|
||||||
|
if (aBound !== bBound) return aBound ? -1 : 1;
|
||||||
|
const aLabel = String(a.name || a.id || "");
|
||||||
|
const bLabel = String(b.name || b.id || "");
|
||||||
|
return aLabel.localeCompare(bLabel);
|
||||||
|
});
|
||||||
|
return rows;
|
||||||
|
}, [entityRefs, selectedEntityIdSet]);
|
||||||
|
|
||||||
const activeEntity = useMemo(
|
const activeEntity = useMemo(
|
||||||
() => (activeEntityId ? entityRefs.find((e) => String(e.id) === String(activeEntityId)) || null : null),
|
() => (activeEntityId ? entityRefs.find((e) => String(e.id) === String(activeEntityId)) || null : null),
|
||||||
@@ -90,83 +106,93 @@ export default function ProjectEntityRefsPanel({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{collapsed ? null : entityRefs.length ? (
|
{collapsed ? null : sortedEntityRefs.length ? (
|
||||||
<div style={{ marginTop: "10px", display: "grid", gap: "6px", maxHeight: 250, overflowY: "auto", paddingRight: 4 }}>
|
<div style={{ marginTop: "10px", display: "grid", gap: "6px", maxHeight: 250, overflowY: "auto", paddingRight: 4 }}>
|
||||||
{entityRefs.map((e) => (
|
{sortedEntityRefs.map((e) => {
|
||||||
<div
|
const entityId = String(e.id);
|
||||||
key={e.id}
|
const isBoundToSelectedGeometry = selectedEntityIdSet.has(entityId);
|
||||||
style={{
|
const isActive = activeEntityId === entityId;
|
||||||
padding: "8px",
|
return (
|
||||||
borderRadius: "6px",
|
<div
|
||||||
border: activeEntityId === String(e.id) ? "1px solid #2563eb" : "1px solid #1f2937",
|
key={e.id}
|
||||||
background: "transparent",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => openEntityEditor(e)}
|
|
||||||
title="Chon de sua"
|
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
padding: "8px",
|
||||||
minWidth: 0,
|
borderRadius: "6px",
|
||||||
textAlign: "left",
|
border: isActive
|
||||||
border: "none",
|
? "1px solid #2563eb"
|
||||||
background: "transparent",
|
: isBoundToSelectedGeometry
|
||||||
padding: 0,
|
? "1px solid rgba(20, 184, 166, 0.65)"
|
||||||
cursor: canEditEntity ? "pointer" : "default",
|
: "1px solid #1f2937",
|
||||||
|
background: isBoundToSelectedGeometry ? "rgba(20, 184, 166, 0.12)" : "transparent",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 10,
|
||||||
}}
|
}}
|
||||||
disabled={!canEditEntity}
|
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 0 }}>
|
|
||||||
<span style={{ fontSize: "12px", color: "#e5e7eb", fontWeight: 700, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
||||||
{e.name || e.id}
|
|
||||||
</span>
|
|
||||||
{isNewEntityRef(e) ? <NewBadge /> : null}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: "11px", color: "#94a3b8", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
||||||
{e.id}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{canBindToggle ? (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
title={selectedGeometryEntityIds!.includes(String(e.id)) ? "Unbind from selected geometry" : "Bind to selected geometry"}
|
onClick={() => openEntityEditor(e)}
|
||||||
onClick={() =>
|
title="Chon de sua"
|
||||||
onToggleBindEntityForSelectedGeometry!(
|
|
||||||
String(e.id),
|
|
||||||
!selectedGeometryEntityIds!.includes(String(e.id))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
style={{
|
style={{
|
||||||
display: "inline-flex",
|
flex: 1,
|
||||||
alignItems: "center",
|
minWidth: 0,
|
||||||
justifyContent: "center",
|
textAlign: "left",
|
||||||
width: 22,
|
border: "none",
|
||||||
height: 22,
|
background: "transparent",
|
||||||
borderRadius: 6,
|
padding: 0,
|
||||||
border: "1px solid #334155",
|
cursor: canEditEntity ? "pointer" : "default",
|
||||||
background: "#0b1220",
|
|
||||||
cursor: "pointer",
|
|
||||||
flex: "0 0 auto",
|
|
||||||
}}
|
}}
|
||||||
aria-label={
|
disabled={!canEditEntity}
|
||||||
selectedGeometryEntityIds!.includes(String(e.id))
|
|
||||||
? `Unbind entity ${String(e.id)} from selected geometry`
|
|
||||||
: `Bind entity ${String(e.id)} to selected geometry`
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{selectedGeometryEntityIds!.includes(String(e.id)) ? (
|
<div style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 0 }}>
|
||||||
<UnlockIcon />
|
<span style={{ fontSize: "12px", color: "#e5e7eb", fontWeight: 700, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
||||||
) : (
|
{e.name || e.id}
|
||||||
<LockIcon />
|
</span>
|
||||||
)}
|
{isBoundToSelectedGeometry ? <span style={boundBadgeStyle}>bound</span> : null}
|
||||||
|
{isNewEntityRef(e) ? <NewBadge /> : null}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: "11px", color: "#94a3b8", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
||||||
|
{e.id}
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
{canBindToggle ? (
|
||||||
</div>
|
<button
|
||||||
))}
|
type="button"
|
||||||
|
title={isBoundToSelectedGeometry ? "Unbind from selected geometry" : "Bind to selected geometry"}
|
||||||
|
onClick={() =>
|
||||||
|
onToggleBindEntityForSelectedGeometry!(
|
||||||
|
entityId,
|
||||||
|
!isBoundToSelectedGeometry
|
||||||
|
)
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
borderRadius: 6,
|
||||||
|
border: "1px solid #334155",
|
||||||
|
background: "#0b1220",
|
||||||
|
cursor: "pointer",
|
||||||
|
flex: "0 0 auto",
|
||||||
|
}}
|
||||||
|
aria-label={
|
||||||
|
isBoundToSelectedGeometry
|
||||||
|
? `Unbind entity ${entityId} from selected geometry`
|
||||||
|
: `Bind entity ${entityId} to selected geometry`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isBoundToSelectedGeometry ? (
|
||||||
|
<UnlockIcon />
|
||||||
|
) : (
|
||||||
|
<LockIcon />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -358,6 +384,24 @@ const entityInputStyle: CSSProperties = {
|
|||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const boundBadgeStyle: CSSProperties = {
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
flex: "0 0 auto",
|
||||||
|
height: 17,
|
||||||
|
padding: "0 6px",
|
||||||
|
borderRadius: 999,
|
||||||
|
border: "1px solid rgba(45, 212, 191, 0.5)",
|
||||||
|
background: "rgba(20, 184, 166, 0.18)",
|
||||||
|
color: "#99f6e4",
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: 900,
|
||||||
|
lineHeight: 1,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: 0,
|
||||||
|
};
|
||||||
|
|
||||||
function LockIcon() {
|
function LockIcon() {
|
||||||
return (
|
return (
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { type CSSProperties, useMemo, useState } from "react";
|
import { type CSSProperties, useMemo, useState } from "react";
|
||||||
import { Entity } from "@/uhm/api/entities";
|
|
||||||
import { Feature } from "@/uhm/lib/editor/state/useEditorState";
|
import { Feature } from "@/uhm/lib/editor/state/useEditorState";
|
||||||
import {
|
import {
|
||||||
GeometryPreset,
|
GeometryPreset,
|
||||||
@@ -14,11 +13,6 @@ import type { GeometryMetaFormState } from "@/uhm/lib/editor/session/sessionType
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
selectedFeatures: Feature[];
|
selectedFeatures: Feature[];
|
||||||
selectedFeatureEntitySummary: string;
|
|
||||||
selectedFeatureBindingSummary: string;
|
|
||||||
entities: Entity[];
|
|
||||||
selectedGeometryEntityIds: string[];
|
|
||||||
onEntityIdsChange: (values: string[]) => void;
|
|
||||||
entityTypeOptions: GeometryTypeOption[];
|
entityTypeOptions: GeometryTypeOption[];
|
||||||
geometryMetaForm: GeometryMetaFormState;
|
geometryMetaForm: GeometryMetaFormState;
|
||||||
onGeometryMetaFormChange: (key: keyof GeometryMetaFormState, value: string) => void;
|
onGeometryMetaFormChange: (key: keyof GeometryMetaFormState, value: string) => void;
|
||||||
@@ -29,11 +23,6 @@ type Props = {
|
|||||||
|
|
||||||
export default function SelectedGeometryPanel({
|
export default function SelectedGeometryPanel({
|
||||||
selectedFeatures,
|
selectedFeatures,
|
||||||
selectedFeatureEntitySummary,
|
|
||||||
selectedFeatureBindingSummary,
|
|
||||||
entities,
|
|
||||||
selectedGeometryEntityIds,
|
|
||||||
onEntityIdsChange,
|
|
||||||
entityTypeOptions,
|
entityTypeOptions,
|
||||||
geometryMetaForm,
|
geometryMetaForm,
|
||||||
onGeometryMetaFormChange,
|
onGeometryMetaFormChange,
|
||||||
@@ -103,7 +92,7 @@ export default function SelectedGeometryPanel({
|
|||||||
>
|
>
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, marginBottom: "8px" }}>
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, marginBottom: "8px" }}>
|
||||||
<div style={{ fontWeight: 700, fontSize: "14px" }}>
|
<div style={{ fontWeight: 700, fontSize: "14px" }}>
|
||||||
Entity & Geometry
|
Geometry property
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -130,67 +119,6 @@ export default function SelectedGeometryPanel({
|
|||||||
|
|
||||||
{collapsed ? null : (
|
{collapsed ? null : (
|
||||||
<div style={{ display: "grid", gap: "8px", fontSize: "13px" }}>
|
<div style={{ display: "grid", gap: "8px", fontSize: "13px" }}>
|
||||||
<div style={{ color: "#e2e8f0" }}>
|
|
||||||
ID: {selectedFeatures.map(f => String(f.properties.id)).join(", ")}
|
|
||||||
</div>
|
|
||||||
<div style={{ color: "#cbd5e1" }}>
|
|
||||||
Entities hiện tại: {selectedFeatureEntitySummary}
|
|
||||||
</div>
|
|
||||||
<div style={{ color: "#cbd5e1" }}>
|
|
||||||
Binding hiện tại: {selectedFeatureBindingSummary}
|
|
||||||
</div>
|
|
||||||
<div style={{ color: "#cbd5e1" }}>
|
|
||||||
Geometry preset: {formatGeometryPresetLabel(featureGeometryPreset)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ color: "#94a3b8", fontSize: "12px" }}>
|
|
||||||
Entities đã chọn:
|
|
||||||
</div>
|
|
||||||
{selectedGeometryEntityIds.length ? (
|
|
||||||
<div style={{ display: "grid", gap: "6px" }}>
|
|
||||||
{selectedGeometryEntityIds.map((entityId) => {
|
|
||||||
const entity = entities.find((item) => item.id === entityId) || null;
|
|
||||||
const label = entity?.name
|
|
||||||
? `${entity.name} (${entityId})`
|
|
||||||
: entityId;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={entityId}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: "8px",
|
|
||||||
background: "#111827",
|
|
||||||
border: "1px solid #334155",
|
|
||||||
borderRadius: "6px",
|
|
||||||
padding: "6px 8px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ color: "#e2e8f0" }}>{label}</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() =>
|
|
||||||
onEntityIdsChange(
|
|
||||||
selectedGeometryEntityIds.filter((id) => id !== entityId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={isEntitySubmitting}
|
|
||||||
style={removeButtonStyle}
|
|
||||||
>
|
|
||||||
Bỏ
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ color: "#fca5a5", fontSize: "12px" }}>
|
|
||||||
Chưa có entity nào được gắn.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "grid",
|
display: "grid",
|
||||||
@@ -306,16 +234,6 @@ const entityInputStyle: CSSProperties = {
|
|||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeButtonStyle: CSSProperties = {
|
|
||||||
border: "none",
|
|
||||||
borderRadius: "6px",
|
|
||||||
padding: "4px 8px",
|
|
||||||
cursor: "pointer",
|
|
||||||
background: "#7f1d1d",
|
|
||||||
color: "#ffffff",
|
|
||||||
fontSize: "12px",
|
|
||||||
};
|
|
||||||
|
|
||||||
const primaryGeometryButtonStyle: CSSProperties = {
|
const primaryGeometryButtonStyle: CSSProperties = {
|
||||||
border: "none",
|
border: "none",
|
||||||
borderRadius: "6px",
|
borderRadius: "6px",
|
||||||
@@ -404,11 +322,3 @@ function getAllowedGroupIdsForPreset(
|
|||||||
|
|
||||||
return ["polygon"];
|
return ["polygon"];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatGeometryPresetLabel(preset: GeometryPreset | null): string {
|
|
||||||
if (preset === "point") return "point - Điểm";
|
|
||||||
if (preset === "line") return "line - Tuyến";
|
|
||||||
if (preset === "circle-area") return "circle - Tròn";
|
|
||||||
if (preset === "polygon") return "polygon - Đa giác";
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user