Fix form selection and common form rendering
This commit is contained in:
@@ -3055,13 +3055,16 @@ function FormDesignerPanel({
|
|||||||
}
|
}
|
||||||
}, [objectForms, selectedFormId, selectedFormQualifiedName]);
|
}, [objectForms, selectedFormId, selectedFormQualifiedName]);
|
||||||
const form =
|
const form =
|
||||||
objectForms.find((item) => item.form.lineage_id === selectedFormId) ??
|
|
||||||
objectForms.find((item) => item.form.qualified_name === selectedFormQualifiedName) ??
|
objectForms.find((item) => item.form.qualified_name === selectedFormQualifiedName) ??
|
||||||
|
objectForms.find((item) => item.form.lineage_id === selectedFormId) ??
|
||||||
objectForms[0];
|
objectForms[0];
|
||||||
const commands = form?.commands.slice(0, 6) ?? [];
|
const commands = form?.commands.slice(0, 6) ?? [];
|
||||||
const formKey = form?.form.lineage_id ?? "draft";
|
const formKey = form?.form.lineage_id ?? "draft";
|
||||||
const baseElements = useMemo(() => buildIdeFormElements(form), [form]);
|
const baseElements = useMemo(() => buildIdeFormElements(form), [form]);
|
||||||
const elements = elementDrafts[formKey] ?? baseElements;
|
const elements = elementDrafts[formKey] ?? baseElements;
|
||||||
|
const flatElements = useMemo(() => flattenIdeFormElements(elements), [elements]);
|
||||||
|
const sidebarElements = flatElements.slice(0, 160);
|
||||||
|
const propertyElements = flatElements.slice(0, 48);
|
||||||
const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента";
|
const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента";
|
||||||
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`;
|
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`;
|
||||||
const layout = layoutByForm[formKey] ?? "auto";
|
const layout = layoutByForm[formKey] ?? "auto";
|
||||||
@@ -3131,10 +3134,10 @@ function FormDesignerPanel({
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-5 border-t border-border pt-3 text-xs font-semibold uppercase text-muted-foreground">{t.formElements}</div>
|
<div className="mt-5 border-t border-border pt-3 text-xs font-semibold uppercase text-muted-foreground">{t.formElements}</div>
|
||||||
<div className="mt-3 space-y-1">
|
<div className="mt-3 space-y-1">
|
||||||
{flattenIdeFormElements(elements).length === 0 ? (
|
{flatElements.length === 0 ? (
|
||||||
<div className="text-sm text-muted-foreground">{t.none}</div>
|
<div className="text-sm text-muted-foreground">{t.none}</div>
|
||||||
) : (
|
) : (
|
||||||
flattenIdeFormElements(elements).map((element) => (
|
sidebarElements.map((element) => (
|
||||||
<div className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-background" key={element.id}>
|
<div className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-background" key={element.id}>
|
||||||
<OneCTreeIcon kind={element.controlKind === "table" ? "tabular" : "attribute"} />
|
<OneCTreeIcon kind={element.controlKind === "table" ? "tabular" : "attribute"} />
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
@@ -3144,6 +3147,11 @@ function FormDesignerPanel({
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
{flatElements.length > sidebarElements.length ? (
|
||||||
|
<div className="px-2 py-1 text-xs text-muted-foreground">
|
||||||
|
+{flatElements.length - sidebarElements.length} {language === "ru" ? "элементов в макете" : "layout items"}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 border-t border-border pt-3 text-xs font-semibold uppercase text-muted-foreground">{t.commands}</div>
|
<div className="mt-5 border-t border-border pt-3 text-xs font-semibold uppercase text-muted-foreground">{t.commands}</div>
|
||||||
<div className="mt-3 space-y-1">
|
<div className="mt-3 space-y-1">
|
||||||
@@ -3240,7 +3248,7 @@ function FormDesignerPanel({
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-2 border-b border-border p-3">
|
<div className="grid grid-cols-2 gap-2 border-b border-border p-3">
|
||||||
<IdeFormMetric label="elements" value={flattenIdeFormElements(elements).length} />
|
<IdeFormMetric label="elements" value={flatElements.length} />
|
||||||
<IdeFormMetric label="commands" value={commands.length} />
|
<IdeFormMetric label="commands" value={commands.length} />
|
||||||
</div>
|
</div>
|
||||||
<div className="border-b border-border p-3">
|
<div className="border-b border-border p-3">
|
||||||
@@ -3261,7 +3269,7 @@ function FormDesignerPanel({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="divide-y divide-border">
|
<div className="divide-y divide-border">
|
||||||
{flattenIdeFormElements(elements).map((element) => (
|
{propertyElements.map((element) => (
|
||||||
<div className="grid gap-2 p-3" key={`props-${element.id}`}>
|
<div className="grid gap-2 p-3" key={`props-${element.id}`}>
|
||||||
<div className="truncate text-xs font-semibold">{element.name}</div>
|
<div className="truncate text-xs font-semibold">{element.name}</div>
|
||||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.caption} onChange={(event) => updateElement(element.id, { caption: event.target.value })} />
|
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.caption} onChange={(event) => updateElement(element.id, { caption: event.target.value })} />
|
||||||
@@ -3283,6 +3291,13 @@ function FormDesignerPanel({
|
|||||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.binding} onChange={(event) => updateElement(element.id, { binding: event.target.value })} />
|
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.binding} onChange={(event) => updateElement(element.id, { binding: event.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{flatElements.length > propertyElements.length ? (
|
||||||
|
<div className="p-3 text-xs text-muted-foreground">
|
||||||
|
{language === "ru"
|
||||||
|
? `Показаны первые ${propertyElements.length} свойств из ${flatElements.length}. Остальные элементы доступны в макете формы.`
|
||||||
|
: `Showing first ${propertyElements.length} properties out of ${flatElements.length}. Other items are available in the form layout.`}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -819,6 +819,9 @@ export async function getBslCompletions(
|
|||||||
|
|
||||||
function ownerQualifiedNameForForm(formQualifiedName: string) {
|
function ownerQualifiedNameForForm(formQualifiedName: string) {
|
||||||
const parts = formQualifiedName.split(".");
|
const parts = formQualifiedName.split(".");
|
||||||
|
if (parts[0] === "ОбщаяФорма") {
|
||||||
|
return formQualifiedName;
|
||||||
|
}
|
||||||
return parts.length > 1 ? parts.slice(0, -1).join(".") : formQualifiedName;
|
return parts.length > 1 ? parts.slice(0, -1).join(".") : formQualifiedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4748,12 +4748,20 @@ async def get_object_ui(project_id: str, object_name: str) -> ObjectUiResponse:
|
|||||||
snapshot, graph = _snapshot_and_graph(project_id)
|
snapshot, graph = _snapshot_and_graph(project_id)
|
||||||
object_node = _find_graph_node(graph, object_name, _ACCESS_TARGET_KINDS)
|
object_node = _find_graph_node(graph, object_name, _ACCESS_TARGET_KINDS)
|
||||||
if object_node is None:
|
if object_node is None:
|
||||||
|
object_node = _find_graph_node(graph, object_name, {NodeKind.FORM})
|
||||||
|
if object_node is None:
|
||||||
|
common_form = _common_form_ui_from_source(project_id, object_name)
|
||||||
|
if common_form is not None:
|
||||||
|
return common_form
|
||||||
raise HTTPException(status_code=404, detail=f"Object not found: {object_name}")
|
raise HTTPException(status_code=404, detail=f"Object not found: {object_name}")
|
||||||
form_lineages = {
|
if object_node.kind == NodeKind.FORM:
|
||||||
edge.target_lineage
|
form_lineages = {object_node.lineage_id}
|
||||||
for edge in graph.edges.values()
|
else:
|
||||||
if edge.kind == EdgeKind.HAS_FORM and edge.source_lineage == object_node.lineage_id
|
form_lineages = {
|
||||||
}
|
edge.target_lineage
|
||||||
|
for edge in graph.edges.values()
|
||||||
|
if edge.kind == EdgeKind.HAS_FORM and edge.source_lineage == object_node.lineage_id
|
||||||
|
}
|
||||||
return ObjectUiResponse(
|
return ObjectUiResponse(
|
||||||
object=_named_node(object_node),
|
object=_named_node(object_node),
|
||||||
forms=_hydrate_object_ui_forms_from_source(
|
forms=_hydrate_object_ui_forms_from_source(
|
||||||
@@ -7924,6 +7932,52 @@ def _hydrate_object_ui_forms_from_source(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _common_form_ui_from_source(project_id: str, object_name: str) -> ObjectUiResponse | None:
|
||||||
|
if not object_name.startswith("ОбщаяФорма."):
|
||||||
|
return None
|
||||||
|
source_root = _current_project_source_root(project_id)
|
||||||
|
if source_root is None:
|
||||||
|
return None
|
||||||
|
form_name = object_name.split(".", 1)[1]
|
||||||
|
form_file = _edt_form_file_for_object(source_root, object_name, form_name)
|
||||||
|
if form_file is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
xml_objects = parse_one_c_xml_file(form_file)
|
||||||
|
except (OSError, UnicodeDecodeError, ET.ParseError):
|
||||||
|
return None
|
||||||
|
form_object = next((item for item in xml_objects if item.object_kind == "FORM"), None)
|
||||||
|
if form_object is None:
|
||||||
|
return None
|
||||||
|
source_path = form_file.as_posix()
|
||||||
|
form_node = NamedNode(
|
||||||
|
lineage_id=make_lineage_id(
|
||||||
|
NodeKind.FORM.value,
|
||||||
|
f"{project_id}:{source_path}:FORM:{form_object.qualified_name}",
|
||||||
|
),
|
||||||
|
kind=NodeKind.FORM.value,
|
||||||
|
name=form_object.name,
|
||||||
|
qualified_name=form_object.qualified_name,
|
||||||
|
attributes=form_object.attributes,
|
||||||
|
)
|
||||||
|
elements = [
|
||||||
|
NamedNode(
|
||||||
|
lineage_id=make_lineage_id(
|
||||||
|
NodeKind.FORM_ELEMENT.value,
|
||||||
|
f"{project_id}:{source_path}:ELEMENT:{item.qualified_name}",
|
||||||
|
),
|
||||||
|
kind=NodeKind.FORM_ELEMENT.value,
|
||||||
|
name=item.name,
|
||||||
|
qualified_name=item.qualified_name,
|
||||||
|
attributes=item.attributes,
|
||||||
|
)
|
||||||
|
for item in xml_objects
|
||||||
|
if item.object_kind == "ELEMENT" and item.qualified_name.startswith(f"{form_node.qualified_name}.")
|
||||||
|
]
|
||||||
|
form = FormSemanticsResponse(form=form_node, commands=[], elements=elements, command_handlers={})
|
||||||
|
return ObjectUiResponse(object=form_node, forms=[form])
|
||||||
|
|
||||||
|
|
||||||
def _hydrate_form_elements_from_source(
|
def _hydrate_form_elements_from_source(
|
||||||
project_id: str,
|
project_id: str,
|
||||||
source_root: Path,
|
source_root: Path,
|
||||||
|
|||||||
Reference in New Issue
Block a user