Gutenberg blocks
Each of the four shortcodes ships as a dynamic Gutenberg block so authors who work in the block editor don't have to remember shortcode syntax.
| Block | Wraps | Attributes (sidebar) |
|---|---|---|
pfm/form | [pfm_form] | entity, sysId, returnUrl, successMessage |
pfm/list | [pfm_list] | entity, filter, columns, pageSize, view |
pfm/record | [pfm_record] | entity, sysId, template |
pfm/action-button | [pfm_action] | action ("<entity>.<slug>"), sysId, label, style |
Every block is dynamic — the JS save() returns null and the PHP render_callback calls the same handler the shortcode uses. That guarantees both surfaces emit identical HTML, run the same permission gate, and flip the same AssetLoader::mark_needed() flag so the front-end bundle enqueues conditionally.
Editor preview is a placeholder card with the entity slug + an <InspectorControls> panel; the real React render happens only at view time on the visitor's page.
Block widgets work out of the box because any block can be dropped into a Widget Area. Pattern variations / templates are not registered yet — they're a follow-up.