Skip to content

feat: limit how often a brick can be used with getMaxInstances() - #89

Open
JVariance wants to merge 2 commits into
awcodes:3.xfrom
JVariance:split/max-instances
Open

JVariance wants to merge 2 commits into
awcodes:3.xfrom
JVariance:split/max-instances

Conversation

@JVariance

@JVariance JVariance commented Oct 31, 2026 •

Copy link
Copy Markdown

As proposed in #82: some bricks belong on a page only once, such as a hero or a page meta brick. Disabling duplicate alone isn't enough, since a brick can still be added from the sidebar, the picker or the add line.

public static function getMaxInstances(): ?int
{
    return 1; // null (default) = no limit
}

The sidebar and the brick picker show how often a limited brick is used, e.g. 0/1 (read out as "0 of 1 used"), and grey it out with a hint once the limit is reached, so it can't be dragged in or picked. Unlimited bricks look as before. The server checks it as well: the brick action can't be mounted to insert another one (editing still works), executeInsertBrick() ignores it, and saving fails validation if the content has too many, e.g. from older content or a request made outside the editor. Documented in docs/bricks/creating.md; the workbench hero is limited to one.

The limit applies per field. A Mason field in a brick's own form counts only its own bricks, which keeps Mason free of any knowledge about nesting:

Goal How
At most N per page, top level only (hero, page meta) getMaxInstances() and leave the brick out of inner fields' bricks()
At most N per field, nested fields included getMaxInstances() alone
At most N per page, on any level Not covered; would need a small hook that lets a field count bricks elsewhere. Happy to propose one if there's demand

With #85 (duplicate), the server already refuses a copy past the limit, since DuplicateBrickAction inserts through executeCommands(). Hiding the Duplicate control at the limit is ready on top of #85: the preview and the outline drop it for a brick at its limit, and the editor doesn't send the request. I'll add it to whichever of the two lands second.

Tests go through the Livewire action and the insert command, check the validation message in English and Arabic, and the counter in the sidebar and picker. Checked in Chromium and WebKit.

🤖 Generated with Claude Code

@awcodes awcodes left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The server side is well thought through. Validation on save is the real guarantee, and executeInsertBrick() catches the insert paths that the disabled() check doesn't.

I haven't decided on the feature yet, since this came in before I'd replied in #82. If it goes ahead, it needs:

  1. Hide Duplicate at the limit. The server already refuses a duplicate past the limit, because DuplicateBrickAction inserts through executeCommands(), so no server change is needed. But the button still shows, does nothing when clicked, and may push an empty undo step.
  2. Naming. Mason::getMaxInstances(): array shares a name with Brick::getMaxInstances(): ?int but returns something different. Something like getBrickLimits() would read more clearly.
  3. aria-disabled on the sidebar divs. They have no role, so it isn't announced. Either give them a role or drop it.
  4. Docs. Adding a limit to an existing brick blocks saving any record already over it, until an author removes the extra. That's the right behaviour, but please call it out in the docs.
  5. Drop the workbench/resources/dist/ changes when you rebase. That directory is git-ignored on 3.x now.

The ->disabled() in BrickAction can also be replaced by any brick's configureBrickAction(), or by a field's brickAction() callback if #92 lands. The server checks still hold, so it's only a UX gap, but it's worth knowing.

JPS and others added 2 commits October 31, 2026 19:23
Some bricks belong on a page only once, such as a hero or a page meta brick.
Bricks can now set a limit per field. The sidebar and the brick picker show how
often a limited brick is used, e.g. 1/1, and grey it out once the limit is
reached, so it can't be dragged in or picked.

The server checks it as well: the brick action can't be mounted to insert
another one, the insert command ignores it, and saving fails validation if the
content has too many.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d from the sidebar

Mason::getMaxInstances() shared its name with Brick::getMaxInstances() but
returned the limits of all bricks; it is now getBrickLimits(). The sidebar's
bricks are plain drag sources without a role, so aria-disabled was never
announced; the screen reader text with the count stays. The docs warn that
adding a limit blocks saving content that is already over it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JVariance
JVariance force-pushed the split/max-instances branch from a95357f to 5b8db25 Compare October 31, 2026 18:33
@JVariance

Copy link
Copy Markdown
Author

Thanks. Rebased onto 3.x without the workbench dist files. In 5b8db25: the field method is now getBrickLimits() (and brickLimits in the JS), aria-disabled is gone from the sidebar bricks (they're drag sources only, so a role would promise more than they do; the screen-reader count stays), and the docs warn that adding a limit blocks saving content that's already over it. Hiding Duplicate at the limit is ready on top of #85: the preview and the outline drop the control and the editor doesn't send the request, with tests. I'll add it here once #85 is in, or to #85 if this lands first. Noted on ->disabled() being replaceable by configureBrickAction() or brickAction().

@JVariance
JVariance requested a review from awcodes October 31, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants