From 4f9d9aee97b65e58c3b247f23ef55985ca558d58 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 4 May 2026 11:48:35 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-compile):=20=D0=B3=D1=80=D1=83=D0=BF?= =?UTF-8?q?=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=D0=BE=D0=BA=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86?= =?UTF-8?q?=D1=8B=20(ColumnGroup)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый DSL-ключ columnGroup со значением-ориентацией horizontal/vertical/inCell для элемента внутри columns таблицы. Поддерживает вложение, showTitle/showInHeader/width, тихие синонимы ColumnGroup и ГруппаКолонок. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/form-compile/SKILL.md | 30 +++++++++++ .../form-compile/scripts/form-compile.ps1 | 53 +++++++++++++++++-- .../form-compile/scripts/form-compile.py | 47 ++++++++++++++-- docs/form-dsl-spec.md | 29 +++++++++- .../cases/form-compile/column-group.json | 40 ++++++++++++++ 5 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 tests/skills/cases/form-compile/column-group.json diff --git a/.claude/skills/form-compile/SKILL.md b/.claude/skills/form-compile/SKILL.md index 6e0f8ffc..564a56fb 100644 --- a/.claude/skills/form-compile/SKILL.md +++ b/.claude/skills/form-compile/SKILL.md @@ -62,6 +62,7 @@ powershell.exe -NoProfile -File .claude/skills/form-compile/scripts/form-compile | DSL ключ | XML элемент | Значение ключа | |--------------|-------------------|---------------------------------------------------| | `"group"` | UsualGroup | `"horizontal"` / `"vertical"` / `"alwaysHorizontal"` / `"alwaysVertical"` / `"collapsible"` | +| `"columnGroup"` | ColumnGroup | `"horizontal"` / `"vertical"` / `"inCell"` — только внутри `columns` таблицы | | `"input"` | InputField | имя элемента | | `"check"` | CheckBoxField | имя | | `"radio"` | RadioButtonField | имя | @@ -210,6 +211,35 @@ powershell.exe -NoProfile -File .claude/skills/form-compile/scripts/form-compile | `rowPictureDataPath` | Путь к картинке строки (напр. `"Список.DefaultPicture"`) | | `tableAutofill: false` | Управление Autofill внутреннего AutoCommandBar | +Колонки можно группировать через `columnGroup` (см. ниже). + +### Группа колонок (columnGroup) + +Используется только внутри `columns` таблицы. Значение ключа задаёт ориентацию: `"horizontal"`, `"vertical"`, `"inCell"` (склеивает колонки в одну ячейку шапки). Допускается вложение `columnGroup` в `columnGroup`. + +| Ключ | Описание | +|------|----------| +| `name` | Имя элемента (рекомендуется задавать явно) | +| `title` | Заголовок группы | +| `showTitle: false` | Скрыть заголовок | +| `showInHeader: true/false` | Показывать ли группу в шапке таблицы | +| `width` | Ширина | +| `horizontalStretch: false` | Растягивание | +| `children: [...]` | Колонки внутри группы (`input`, `labelField`, `picField`, вложенный `columnGroup` …) | + +```json +{ "table": "Список", "path": "Список", "columns": [ + { "columnGroup": "horizontal", "name": "ГруппаДата", "title": "Срок", "children": [ + { "input": "СрокИсполнения", "path": "Список.СрокИсполнения" }, + { "labelField": "Просрочено", "path": "Список.Просрочено" } + ]}, + { "columnGroup": "inCell", "name": "ГруппаИсполнитель", "showInHeader": true, "children": [ + { "input": "Исполнитель", "path": "Список.Исполнитель" } + ]}, + { "input": "Комментарий", "path": "Список.Комментарий" } +]} +``` + ### Страницы (pages + page) | Ключ (pages) | Описание | diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 8a3cd3f9..1e2ca8aa 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -1,4 +1,4 @@ -# form-compile v1.19 — Compile 1C managed form from JSON or object metadata +# form-compile v1.20 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -1853,6 +1853,8 @@ function Emit-Element { "UsualGroup" = "group" "Группа" = "group" "ОбычнаяГруппа" = "group" + "ColumnGroup" = "columnGroup" + "ГруппаКолонок" = "columnGroup" "Pages" = "pages" "ГруппаСтраниц" = "pages" "Page" = "page" @@ -1876,7 +1878,7 @@ function Emit-Element { $typeKey = $null $xmlTag = $null - foreach ($key in @("group","input","check","radio","label","labelField","table","pages","page","button","picture","picField","calendar","cmdBar","popup")) { + foreach ($key in @("columnGroup","group","input","check","radio","label","labelField","table","pages","page","button","picture","picField","calendar","cmdBar","popup")) { if ($el.$key -ne $null) { $typeKey = $key break @@ -1891,8 +1893,10 @@ function Emit-Element { # Validate known keys — warn about typos and unknown properties $knownKeys = @{ # type keys - "group"=1;"input"=1;"check"=1;"radio"=1;"label"=1;"labelField"=1;"table"=1;"pages"=1;"page"=1 + "group"=1;"columnGroup"=1;"input"=1;"check"=1;"radio"=1;"label"=1;"labelField"=1;"table"=1;"pages"=1;"page"=1 "button"=1;"picture"=1;"picField"=1;"calendar"=1;"cmdBar"=1;"popup"=1 + # columnGroup-specific + "showInHeader"=1 # radio-specific "radioButtonType"=1;"choiceList"=1;"columnsCount"=1 # naming & binding @@ -1939,6 +1943,7 @@ function Emit-Element { switch ($typeKey) { "group" { Emit-Group -el $el -name $name -id $id -indent $indent } + "columnGroup" { Emit-ColumnGroup -el $el -name $name -id $id -indent $indent } "input" { Emit-Input -el $el -name $name -id $id -indent $indent } "check" { Emit-Check -el $el -name $name -id $id -indent $indent } "radio" { Emit-Radio -el $el -name $name -id $id -indent $indent } @@ -2060,6 +2065,48 @@ function Emit-Group { X "$indent" } +function Emit-ColumnGroup { + param($el, [string]$name, [int]$id, [string]$indent) + + X "$indent" + $inner = "$indent`t" + + Emit-Title -el $el -name $name -indent $inner + + # Group orientation (horizontal / vertical / inCell — последнее только здесь) + $groupVal = "$($el.columnGroup)" + $orientation = switch ($groupVal) { + "horizontal" { "Horizontal" } + "vertical" { "Vertical" } + "inCell" { "InCell" } + default { $null } + } + if ($orientation) { X "$inner$orientation" } + + if ($el.showTitle -eq $false) { X "$innerfalse" } + if ($null -ne $el.showInHeader) { + $shVal = if ($el.showInHeader) { "true" } else { "false" } + X "$inner$shVal" + } + if ($el.width) { X "$inner$($el.width)" } + + Emit-CommonFlags -el $el -indent $inner + + # Companion: ExtendedTooltip + Emit-Companion -tag "ExtendedTooltip" -name "${name}РасширеннаяПодсказка" -indent $inner + + # Children + if ($el.children -and $el.children.Count -gt 0) { + X "$inner" + foreach ($child in $el.children) { + Emit-Element -el $child -indent "$inner`t" + } + X "$inner" + } + + X "$indent" +} + function Emit-Input { param($el, [string]$name, [int]$id, [string]$indent) diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index a0afda00..992c83f3 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-compile v1.19 — Compile 1C managed form from JSON or object metadata +# form-compile v1.20 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -1338,8 +1338,9 @@ KNOWN_FORM_EVENTS = [ ] KNOWN_KEYS = { - "group", "input", "check", "radio", "label", "labelField", "table", "pages", "page", + "group", "columnGroup", "input", "check", "radio", "label", "labelField", "table", "pages", "page", "button", "picture", "picField", "calendar", "cmdBar", "popup", + "showInHeader", "radioButtonType", "choiceList", "columnsCount", "name", "path", "title", "visible", "hidden", "enabled", "disabled", "readOnly", "userVisible", @@ -1362,7 +1363,7 @@ KNOWN_KEYS = { "rowPictureDataPath", "tableAutofill", } -TYPE_KEYS = ["group", "input", "check", "radio", "label", "labelField", "table", "pages", "page", +TYPE_KEYS = ["columnGroup", "group", "input", "check", "radio", "label", "labelField", "table", "pages", "page", "button", "picture", "picField", "calendar", "cmdBar", "popup"] # Synonyms: model often writes XML name or Russian (ПолеПереключателя/RadioButtonField → radio) @@ -1390,6 +1391,8 @@ ELEMENT_TYPE_SYNONYMS = { "UsualGroup": "group", "Группа": "group", "ОбычнаяГруппа": "group", + "ColumnGroup": "columnGroup", + "ГруппаКолонок": "columnGroup", "Pages": "pages", "ГруппаСтраниц": "pages", "Page": "page", @@ -1787,6 +1790,7 @@ def emit_element(lines, el, indent, in_cmd_bar=False): emitters = { 'group': emit_group, + 'columnGroup': emit_column_group, 'input': emit_input, 'check': emit_check, 'radio': emit_radio_button_field, @@ -1870,6 +1874,43 @@ def emit_group(lines, el, name, eid, indent): lines.append(f'{indent}') +def emit_column_group(lines, el, name, eid, indent): + lines.append(f'{indent}') + inner = f'{indent}\t' + + emit_title(lines, el, name, inner) + + group_val = str(el.get('columnGroup', '')) + orientation_map = { + 'horizontal': 'Horizontal', + 'vertical': 'Vertical', + 'inCell': 'InCell', + } + orientation = orientation_map.get(group_val) + if orientation: + lines.append(f'{inner}{orientation}') + + if el.get('showTitle') is False: + lines.append(f'{inner}false') + if el.get('showInHeader') is not None: + sh_val = 'true' if el['showInHeader'] else 'false' + lines.append(f'{inner}{sh_val}') + if el.get('width'): + lines.append(f'{inner}{el["width"]}') + + emit_common_flags(lines, el, inner) + + emit_companion(lines, 'ExtendedTooltip', f'{name}РасширеннаяПодсказка', inner) + + if el.get('children') and len(el['children']) > 0: + lines.append(f'{inner}') + for child in el['children']: + emit_element(lines, child, f'{inner}\t') + lines.append(f'{inner}') + + lines.append(f'{indent}') + + def emit_input(lines, el, name, eid, indent): lines.append(f'{indent}') inner = f'{indent}\t' diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 32e11c3b..b887549f 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -268,7 +268,7 @@ | Свойство | Тип | Описание | |----------|-----|----------| | `path` | string | DataPath | -| `columns` | array | Колонки (элементы input/check/labelField/picField) | +| `columns` | array | Колонки (элементы input/check/labelField/picField, либо `columnGroup` для группировки) | | `representation` | string | `List`, `Tree`, `HierarchicalList` | | `changeRowSet` | bool | Разрешить добавление/удаление строк | | `changeRowOrder` | bool | Разрешить перемещение строк | @@ -278,6 +278,33 @@ | `commandBarLocation` | string | `None`, `Top`, `Bottom`, `Auto` | | `searchStringLocation` | string | `None`, `Top`, `Bottom`, `CommandBar`, `Auto` | +#### columnGroup — ColumnGroup + +Группа колонок таблицы. Используется только внутри `columns` таблицы. Допускается вложение `columnGroup` в `columnGroup`. + +```json +{ "table": "Список", "path": "Список", "columns": [ + { "columnGroup": "horizontal", "name": "ГруппаДата", "title": "Срок", "children": [ + { "input": "ДатаНачала", "path": "Список.ДатаНачала" }, + { "input": "ДатаОкончания", "path": "Список.ДатаОкончания" } + ]}, + { "columnGroup": "inCell", "name": "ГруппаИсполнитель", "showInHeader": true, "children": [ + { "input": "Исполнитель", "path": "Список.Исполнитель" } + ]} +]} +``` + +| Свойство | Тип | Описание | +|----------|-----|----------| +| `columnGroup` | string | Ориентация: `horizontal`, `vertical`, `inCell` (склейка колонок в одной ячейке шапки) | +| `name` | string | Имя элемента (рекомендуется задавать явно) | +| `title` | string/object | Заголовок группы | +| `showTitle` | bool | Показывать заголовок | +| `showInHeader` | bool | Показывать в шапке таблицы | +| `width` | int | Ширина | +| `horizontalStretch` | bool | Растягивание | +| `children` | array | Колонки внутри группы | + #### pages / page — Pages / Page ```json diff --git a/tests/skills/cases/form-compile/column-group.json b/tests/skills/cases/form-compile/column-group.json new file mode 100644 index 00000000..6243fc13 --- /dev/null +++ b/tests/skills/cases/form-compile/column-group.json @@ -0,0 +1,40 @@ +{ + "name": "Форма с группировкой колонок таблицы", + "preRun": [ + { + "script": "meta-compile/scripts/meta-compile", + "input": { "type": "DataProcessor", "name": "Задачи" }, + "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" } + }, + { + "script": "form-add/scripts/form-add", + "args": { "-ObjectPath": "{workDir}/DataProcessors/Задачи.xml", "-FormName": "Форма" } + } + ], + "params": { "outputPath": "DataProcessors/Задачи/Forms/Форма/Ext/Form.xml" }, + "validatePath": "DataProcessors/Задачи/Forms/Форма/Ext/Form.xml", + "input": { + "title": "Список задач", + "elements": [ + { "table": "Список", "path": "Список", "columns": [ + { "input": "Наименование", "path": "Список.Наименование" }, + { "columnGroup": "horizontal", "name": "ГруппаСрок", "title": "Срок", "children": [ + { "input": "ДатаНачала", "path": "Список.ДатаНачала" }, + { "input": "ДатаОкончания", "path": "Список.ДатаОкончания" } + ]}, + { "columnGroup": "inCell", "name": "ГруппаИсполнитель", "showInHeader": true, "children": [ + { "input": "Исполнитель", "path": "Список.Исполнитель" } + ]} + ]} + ], + "attributes": [ + { "name": "Объект", "type": "DataProcessorObject.Задачи", "main": true }, + { "name": "Список", "type": "ValueTable", "columns": [ + { "name": "Наименование", "type": "string(150)" }, + { "name": "ДатаНачала", "type": "date" }, + { "name": "ДатаОкончания", "type": "date" }, + { "name": "Исполнитель", "type": "string(100)" } + ]} + ] + } +}