Every GridStackItem now renders a close button next to its drag grip,
mirroring the grip's hover affordance. Closing detaches the item
server-side; grid-stack.ts already unregisters widgets via its
MutationObserver, so the layout is persisted without an extra protocol.
Closable is on by default and can be turned off per item with
setClosable(false); GridStackItem.CloseEvent lets views react.
The GridStackView "remove last" toolbar button is dropped — per-widget
close replaces it, so the view no longer tracks a widget stack. Its
counter now only grows, keeping a closed widget's id from being handed
to a new widget (which would inherit the saved position).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ue9ZtWUBQF4SuSHpzZ3zwq
The layout was saved to localStorage, but restoring it did not reliably
reproduce it:
- restore() applied the saved nodes one by one via grid.update(). A later
item could collide with one already put back and push it off its saved
spot, and nothing moved it back — so the restored layout was not the one
the user left. Use grid.load(nodes, false) instead: it sorts the nodes,
removes them from the engine before re-placing them, and runs in a single
batch. addRemove is off because Flow owns which children exist.
- gridstack's save() omits w/h when they are 1. The manual restore passed
them through as undefined, and the server read them as 0 and wrote a
zero-sized widget back onto the item. load() re-applies gridstack's own
defaults; onLayoutChange now reads a missing w/h as 1.
- A drag followed immediately by navigating to another route detached the
element inside the 150ms persist debounce, dropping the pending save.
disconnectedCallback now flushes it to localStorage first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ue9ZtWUBQF4SuSHpzZ3zwq
Adds a /gridstack route demonstrating GridStackLayout: four default
widgets (line/bar/pie chart cards plus a usage hint), buttons to add and
remove widgets at runtime, a reset-layout button and a layout-change
status line. Layout is persisted per browser via the existing
localStorage support. The view gets its own SideNavItem in MainLayout
and full de/en/es translations.
Fixes an infinite recursion in GridStackLayout.add(Component): a
single-argument add(item) resolves to that overload rather than the
varargs one, so its delegation to add(item) called itself. It now
appends through the element API and passes a GridStackItem through
unwrapped.
Adds the first test sources (browserless UI tests) covering the view's
widget count and the add/remove buttons.
Closes#7
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PYAcKoXKPN3rJ7nBroKZkc
Set German locale on TimePicker in FormView and wire TimePickerI18n
error messages (bad input, required, min, max) through the existing
vaadin-i18n translation bundles, mirroring the DatePicker.DatePickerI18n
setup already present in FormView.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PYAcKoXKPN3rJ7nBroKZkc