Repository navigation
Layout editor: bugs fixed, import, catalogue of 123 controls, nested data binding, modules - #3
Merged
Merged
Conversation
- A table attribute whose sample rows set no field still seeds model_init
with one initial line per row: the canvas showed the rows, the class
started empty.
- An empty binding (@ without an attribute, # without a field, an empty
expression) is written by neither the view nor the class; the view used
to bind {/} - the whole model - or {}.
- Checks: two tables that share a row type (t_rows and rows -> ty_s_rows)
are an error; a rows binding has to name a table and a property binding
a scalar; a boolean property bound to a non-flag is a warning; a public
attribute nothing binds is a warning, as the abap2UI5 linter says.
- The attribute picker of a property offers only attributes that fit.
- A part (Column, Item, ...) no longer drops onto the view root;
insertNode( ) refuses what the aggregation does not take, so a Column
cannot get a second header; duplicate in a single slot says why.
- Typing after a change of selection starts a new undo step.
- test/model.test.mjs: the edits, the drop target, the row context, the
validation - with one regression test per item above.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
- {/NAME} on an attribute of the class becomes client->_bind( name ),
{FIELD} inside a list bound to a table with that field becomes a field
of the row; everything else stays a UI5 expression. A binding that
names no attribute is listed after the import, with one click to
create the attributes (a string per value, a table per rows binding)
and rebind.
- The import says what it dropped: event handlers, ids, controls the
palette does not know. Text content (<Text>Hello</Text>) becomes the
text property.
- A child without a wrapper tag lands where a drop would (IconTabBar
items, Bar contentMiddle), not in a nameless aggregation.
- Properties an import brought along that the catalogue does not know
show in the properties under Imported and can be removed; they used
to be written to the class unseen.
- Binding the rows of a list that someone built by hand asks before the
template is replaced; Cancel keeps it and sets the binding only.
- test/import.test.mjs and three browser tests cover the above.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
- Checks warns about a sample value the class cannot carry as it is: a decimal with more than two places, a time without seconds, a date or number in another shape. The canvas shows what the class sends - the decimal rounded, the time padded. - A design opened from a file or the browser store is normalised: ids the canvas can find, unique, and the node shape the editor relies on; a file of another version is refused with a reason instead of silently replaced. - Alt reaches the designer from the canvas (Alt+Up selects the parent); no shortcut acts while a dialog is open. README lists the keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
- A drop on the canvas lands before, into or after the control under the pointer - the upper quarter is before, the lower after - with a line where it will go; it used to land into or behind only. - The outline walks with the keys: up and down over the visible rows, left collapses or goes to the parent, right expands or goes to the first child, Home and End, Enter to the properties. Rows are focusable tree items with their level. - Ctrl+C, Ctrl+X and Ctrl+V copy, cut and paste the selection, through the system clipboard (into another design or tab) with the editor's own copy as the fallback; a paste lands where a drop would. The command palette lists them. - The drawer and the properties can be dragged wider or narrower, the width is remembered; double-click resets. - The panel tabs carry aria-selected and aria-controls, the panels are tab panels. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
Layout: VerticalLayout, HorizontalLayout, Grid, ScrollContainer, Carousel. Display: ObjectHeader with ObjectAttribute, Breadcrumbs, FormattedText, BusyIndicator, GenericTile with TileContent and NumericContent. Input: MultiComboBox, MultiInput with Token, DateTimePicker. Lists: ObjectListItem, DisplayListItem, InputListItem, and an infoToolbar on List and Table. test/ui5-1.71.json is regenerated from a 1.71.80 tree (68 controls); a `loose` property is one of a UI5 type of its own that the view writes as text (GridSpan, string[]). The metadata tool asked the dev server for a local tree under a path it does not serve - fixed. The kitchen sink places the new parts in their parents, so the linter, the runtime and both browser legs see every one of them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
…frame src/app.js (1,500 lines) is now the composition root: src/ui/store.js holds the state, commit( ) with undo and the browser store, and tells its listeners on 'change' and 'select'; each panel is a module of its own (insert, layers, inspector, data, code, preview, palette, files, keys, edit for drops and the clipboard) that imports the store and the DOM helpers and knows no other panel. No build step, ESM as before. Per edit the design was serialised four times and the class generated twice: commit( ) serialises once more than it must, the code pane keys its cache on a revision counter, abap.js builds the class once with its line-to-node map, and a burst of edits renders the panels once per animation frame. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
- The canvas says what UI5 is doing: a status line while it loads, the render error or a runtime error (a renderer, a formatter, an event - caught in the frame and reported), and after 25 seconds without UI5 the hint that the CDN is out of reach and how to serve a local tree. - tools/ui5-trees.mjs assembles OpenUI5 trees from the @openui5 npm packages into .ui5/; the browser job of CI caches them and runs on them, so the tests never depend on the CDN. npm run ui5:trees, npm run test:e2e:local. - The render test names what UI5 leaves out of the page (unselected tabs, dropdown items, carousel pages, and what depends on the width: overflow toolbars, breadcrumbs, columns with a minimum width, a tile that is not loaded); every other control has to be in the page. It used to tolerate a third missing. - File > Recent designs: what New, Open, Import or a link replaced, the last eight. Export > Copy share link: the design deflated into the URL; opening the link loads it. - A design of an older format version is migrated step by step (none needed yet), a newer one refused with its version. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
….unified sap.m: Wizard and WizardStep, TabContainer and TabContainerItem, MessagePage, HeaderContainer, SlideTile with Image-, News- and FeedContent, ObjectMarker, DraftIndicator, PullToRefresh, ToggleButton, SplitButton, MenuButton with Menu and MenuItem, OverflowToolbarButton, OverflowToolbarToggleButton, ToolbarSeparator, RangeSlider, MaskInput, DateRangeSelection, FeedInput, SelectList, Tokenizer, ColorPalette, PagingButton, Feed-, Notification-, Action- and GroupHeaderListItem. sap.ui.layout: Splitter, BlockLayout with row and cell. sap.ui.core: Title (a section title in a form), HTML. sap.ui.unified: FileUploader, Calendar. sap.ui.table: the grid Table and its Column - binding its rows to a table attribute builds one column per field with the template in the column, and the row context knows that the templates sit in the columns. sap.f: DynamicPage with title and header, Avatar, Card with its header. sap.uxap: ObjectPageLayout with dynamic header title, section and subsection. Every control is in test/ui5-1.71.json, regenerated from a 1.71.80 tree that now carries the five libraries (tools/ui5-trees.mjs); a control without a default aggregation in 1.71 (TabContainer, DynamicPage, Card) drops into a named one. Tree stays out: without a hierarchical binding UI5 throws, and the editor's data model has flat tables only. The kitchen sink is two classes now - one statement with every control is more than the transpiled runtime of the verification takes - and the render test knows what the new controls keep out of the page (wizard steps and tabs after the first, a menu, lazily loaded sections, a grid-table template without rows). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
A class name typed into the Controls panel (or Ctrl+K) - sap.m.Wizard, a library of one's own - is sent to the preview frame, which requires the class and answers with its metadata: properties with types and enum values, aggregations, the default aggregation, events. runtimeDef( ) turns that into a catalogue entry in RUNTIME, getControl( ) knows both maps, and from then on the control drops, binds, shows typed editors in the properties and writes its namespace into the view and the class (prefixFor( ) picks the library's last name, unique in the design). Described controls stay in the palette for the session; a design opened with such controls has them described anew once the canvas is ready. Nothing checks them against 1.71, and Checks says so. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
click from a list to its table attribute
- A table field can be a table: with fields and sample rows of its own,
edited on the Data tab under the row. The class gets a row type per
level (ty_s_tree, ty_s_tree_nodes), model_init seeds the nested rows
with nested VALUE #( ), the canvas gets nested arrays - what abap2UI5
sends, as the runtime check proves.
- sap.m.Tree is back, bound or nothing (UI5 fails on static tree items):
its items repeat a table attribute, the children the table field of
the row - items="{path:'/T_NODES',parameters:{arrayNames:['NODES']}}"
in the view, a string template around client->_bind( path = abap_true )
in the class. The builder model evaluates that template, the import
reads it back.
- A list inside a bound list repeats a table field of its row
(items="{NODES}", # in the Rows picker); the row context follows it,
so the inner template's fields resolve, and Checks knows a table field
from a scalar on both sides.
- Inserting a list, a table or a tree offers, in the toast, to create the
table attribute and bind it in one click - a tree with its nodes table.
- The kitchen sink carries a tree and a list in a list; the linter rule
relative-binding-without-context is excluded there: it sees no context
in a relative items binding, the runtime check does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
|
Great project! This is exactly what I’ve been looking for. |
Member
Author
|
Hi @Hhpy410, nice to hear and thanks for beta testing ;) if you want, describe a little bit more detailed what you need. Then I will throw it into Claude and see if it’s possible to realize. All the best. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
An analysis of the layout editor turned into ten commits: the bugs it found fixed, the catalogue grown from 68 to 123 controls plus any control by class name, data binding for nested tables (trees, lists in lists), the editor split into modules, and the browser tests freed from the CDN. One commit per step; each was verified with all four checks before the next.
The view and the class agree (bugs)
model_init(the canvas showed rows, the class started empty).@without an attribute,#without a field) is written by neither the view nor the class; the view used to bind{/}, the whole model.t_rowsandrows), a rows binding on a scalar, a boolean property on a non-flag, a public attribute nothing binds, a decimal with more than two places, a time without seconds.insertNode( )refuses what an aggregation does not take; typing after a change of selection is a new undo step; ids of opened designs are checked; Escape in a dialog no longer selects the view.Import
{/NAME}resolves toclient->_bind( name ),{FIELD}in a bound list to a field; unresolved paths are listed with one click to create the attributes. Dropped handlers and ids are reported. Imported properties the palette does not know show under Imported and can be removed. A child without a wrapper tag lands where a drop would.Canvas and outline
Drop before / into / after the control under the pointer. Arrow keys in the outline. Ctrl+C / X / V through the clipboard, also between tabs. Resizable panes. Tab panels with
aria-selected.Catalogue
121 controls checked against the real 1.71.80 metadata (
test/ui5-1.71.json, regenerated), across sap.m, sap.ui.core, sap.ui.layout, sap.ui.unified, sap.ui.table, sap.f and sap.uxap: DynamicPage, ObjectPageLayout, the grid Table, Wizard, TabContainer, MenuButton, Card, Avatar and more. Any other control by class name: the canvas's UI5 describes it, and it then works like one of the palette's, marked as unchecked against 1.71.Treeis bound or nothing, as UI5 demands.Data binding
A table field can be a table of its own, edited on the Data tab under the row. The class gets a row type per level,
model_initnestedVALUE #( ), the canvas nested arrays, as the runtime check proves. A tree bindsitems="{path:'/T_NODES',parameters:{arrayNames:['NODES']}}"through a string template aroundclient->_bind( path = abap_true ); a list inside a bound list repeats a table field of its row. Inserting a list, table or tree offers to create and bind the table attribute in one click.Editor
src/app.jsis the composition root;src/ui/store.jsholds state andcommit( ), one module per panel. Per edit the design is serialised once less, the class built once, the panels rendered once per frame. The canvas reports runtime errors and a CDN that does not answer.tools/ui5-trees.mjsbuilds UI5 trees from the@openui5npm packages; CI caches them, so the browser tests never depend on the CDN. Recent designs, a share link with the design in the URL, format migrations.Verification
Every commit:
npm test(186),npm run check:abap(0 findings),npm run verify:abap(6 classes transpiled and run on abap2UI5, view and model equal),npm run test:e2eon UI5 1.144.0 and 1.71.80 (35). The kitchen sink is two classes now: one statement with every control overruns the transpiled runtime's stack.Known limits
relative-binding-without-contextdoes not see the context a relativeitems="{NODES}"establishes; it is excluded for the kitchen sink, the runtime check covers it. A case for the linter repository.actions/cache+npm run ui5:trees) is new; its first run on GitHub will show.🤖 Generated with Claude Code
https://claude.ai/code/session_01Wdz84WSKQE9qRrS2Gu9dWp
Generated by Claude Code