Jump to content

Recommended Posts

Posted

Nested Checkboxes

An inputfield for Page Reference fields that groups options by their parent page, and optionally by grandparent page too.

This can help editors understand the grouping of the selectable pages, and also makes it quicker for an editor to select or unselect an entire group of pages.

Screen recording

The checkboxes at the parent and grandparent level are not for storing those pages in the field value - only for quickly selecting or unselecting groups of pages at the lowest level of the hierarchy. For example, in the screen recording above the "Cities" Page Reference field allows only pages with the "city" template, and the pages at the country and continent level are not included in the field value.

The inputfield is only for use with Page Reference fields because the structure comes from the page tree.

Requires PW >= v3.0.248.

Configuration

For each field that uses the inputfield you have these options:

  • Checkboxes structure: choose "Parents" or "Parents and grandparents".
  • Collapse sections that contain no checked checkboxes: this option makes the inputfield more compact.
  • There are also the standard column width and column quantity options familiar from the InputfieldCheckboxes inputfield. These apply to the selectable pages at the lowest level of the hierarchy, and the structure is arguably more readable when these are left at their defaults.

 

https://github.com/Toutouwai/InputfieldNestedCheckboxes
https://processwire.com/modules/inputfield-nested-checkboxes/

  • Like 19
  • Thanks 5
  • 3 weeks later...
Posted

Amazing! Thanks for open-sourcing 🙂

My main use case for this would be a nested page tree of categories. So a single template, but nested one or two levels deep, and both parents and children can be selected. Is this currently supported?

 

Posted
2 hours ago, d'Hinnisdaël said:

Amazing! Thanks for open-sourcing 🙂

Thanks!

2 hours ago, d'Hinnisdaël said:

My main use case for this would be a nested page tree of categories. So a single template, but nested one or two levels deep, and both parents and children can be selected. Is this currently supported?

Sorry, no, the exclusion of the grandparents and parents from the Page Reference field value is baked in. What you're describing would need to be handled by a different module.

Posted
8 hours ago, Robin S said:

Sorry, no, the exclusion of the grandparents and parents from the Page Reference field value is baked in. What you're describing would need to be handled by a different module.

Good to know. Makes sense to keep it simple 🙂

Posted

@d'Hinnisdaël, I was curious about organising a PageArray into a hierarchy, so here is some hook code for modifying a normal InputfieldCheckboxes (when used with a Page Reference field) that you could experiment with.

$wire->addHookBefore('InputfieldCheckboxes::render', function(HookEvent $event) {
	/** @var InputfieldCheckboxes */
	$inputfield = $event->object;
	$field = $inputfield->hasField;
	if(!$field || $field->name !== 'YOUR_FIELD_NAME') return;

	function buildHierarchy($pagearray) {
		// Organise pages by their parent ID and collect all IDs
		$grouped = [];
		$itemsById = [];
		$allIds = [];
		foreach($pagearray as $page) {
			$itemsById[$page->id] = $page;
			$allIds[$page->id] = true;
			$grouped[$page->parent->id][] = $page;
		}
		// Find orphaned parents - parent IDs that don't exist in the PageArray
		$orphanedParents = [];
		foreach($grouped as $parentId => $items) {
			if($parentId !== null && !isset($allIds[$parentId])) {
				$orphanedParents[] = $parentId;
			}
		}
		// Recursive function to build children
		function buildChildren($parentId, &$grouped, &$itemsById) {
			if(!isset($grouped[$parentId])) {
				return [];
			}
			$children = [];
			foreach($grouped[$parentId] as $item) {
				$node = clone $item;
				// Not using "children" as the property name here to avoid clashing with native property
				$node->nodeChildren = buildChildren($item->id, $grouped, $itemsById);
				$children[] = $node;
			}
			return $children;
		}
		// Build trees for all orphaned parents
		$hierarchy = [];
		foreach($orphanedParents as $parentId) {
			$hierarchy = array_merge($hierarchy, buildChildren($parentId, $grouped, $itemsById));
		}
		return $hierarchy;
	}

	function renderCheckboxesList($items, $inputfield) {
		$out = "<div class='nested-checkboxes-list'>";
		foreach($items as $item) {
			$label = $item->getFormatted('title');
			$checked = '';
			if($inputfield->isOptionSelected($item->id)) $checked = " checked='checked'";
			$out .= "<div class='nested-checkboxes-item'><label><input$checked type='checkbox' name='{$inputfield->name}[]' value='{$item->id}' class='uk-checkbox'><span class='pw-no-select'>$label</span></label>";
			if($item->nodeChildren) $out .= renderCheckboxesList($item->nodeChildren, $inputfield);
			$out .= "</div>";
		}
		$out .= "</div>";
		return $out;
	}

	$options = $inputfield->getOptions();
	$optionIdsString = implode('|', array_keys($options));
	$selectable = $event->wire()->pages->find("id=$optionIdsString, sort=parent.sort, sort=sort");
	$hierarchy = buildHierarchy($selectable);
	$out = renderCheckboxesList($hierarchy, $inputfield);
	$out .= <<<EOT
<style>
.nested-checkboxes-list:not(.InputfieldCheckboxes > .nested-checkboxes-list) { padding-left:25px; }
.nested-checkboxes-item input { margin-right:0.5em; }
</style>
EOT;
	$event->replace = true;
	$event->return = $out;
});

Before:
image.png.137e9d89619a3afea1e1fbd88445fb9d.png

After:
image.png.4890b7a7776bf494b347bf3288fd4bf1.png

There's no JavaScript (I'll leave that to you), but in any case you would likely need to use PHP logic to set the selection state of parent items on Pages::saveReady() or else the field value would get out of whack any time an option was set via the API rather than via the inputfield. That's why having the parents/grandparents in the field value is something that can't really be handled by a module that is an inputfield only and is probably best done in custom code that's specific to your project.

  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...