1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-10 00:37:02 +02:00

Add new page editor Inputfield visibility mode 'Tab' which makes any Inputfield display as a page editor tab. Options included for 'Tab', 'Tab (AJAX)', and 'Tab (locked)'.

This commit is contained in:
Ryan Cramer
2022-05-27 10:40:58 -04:00
parent 23a4cb455d
commit 6667caa1d0
5 changed files with 129 additions and 16 deletions

View File

@@ -59,6 +59,7 @@
* @property string $icon Optional font-awesome icon name to accompany label (excluding the "fa-") part). #pw-group-labels
* @property string $requiredLabel Optional custom label to display when missing required value. @since 3.0.98 #pw-group-labels
* @property string $head Optional text that appears below label but above description (only used by some Inputfields). #pw-internal
* @property string $tabLabel Label for tab if Inputfield rendered in its own tab via Inputfield::collapsedTab* setting. @since 3.0.201 #pw-group-labels
* @property string|null $prependMarkup Optional markup to prepend to the Inputfield content container. #pw-group-other
* @property string|null $appendMarkup Optional markup to append to the Inputfield content container. #pw-group-other
*
@@ -218,6 +219,30 @@ abstract class Inputfield extends WireData implements Module {
*/
const collapsedBlankAjax = 11;
/**
* Collapsed into a separate tab
* #pw-group-collapsed-constants
* @since 3.0.201
*
*/
const collapsedTab = 20;
/**
* Collapsed into a separate tab and AJAX loaded
* #pw-group-collapsed-constants
* @since 3.0.201
*
*/
const collapsedTabAjax = 21;
/**
* Collapsed into a separate tab and locked (not editable)
* #pw-group-collapsed-constants
* @since 3.0.201
*
*/
const collapsedTabLocked = 22;
/**
* Don't skip the label (default)
* #pw-group-skipLabel-constants
@@ -366,6 +391,7 @@ abstract class Inputfield extends WireData implements Module {
$this->set('notes', ''); // highlighted descriptive copy, below output of input field
$this->set('detail', ''); // text details that appear below notes
$this->set('head', ''); // below label, above description
$this->set('tabLabel', ''); // alternate label for tab when Inputfield::collapsedTab* in use
$this->set('required', 0); // set to 1 to make value required for this field
$this->set('requiredIf', ''); // optional conditions to make it required
$this->set('collapsed', ''); // see the collapsed* constants at top of class (use blank string for unset value)
@@ -1467,6 +1493,9 @@ abstract class Inputfield extends WireData implements Module {
if($this->hasFieldtype !== false) {
$field->addOption(self::collapsedYesAjax, $this->_('Closed + Load only when opened (AJAX)') . "");
$field->notes = sprintf($this->_('Options indicated with %s may not work with all input types or placements, test to ensure compatibility.'), '†');
$field->addOption(self::collapsedTab, $this->_('Tab'));
$field->addOption(self::collapsedTabAjax, $this->_('Tab + Load only when clicked (AJAX)') . "");
$field->addOption(self::collapsedTabLocked, $this->_('Tab + Locked (not editable)'));
}
$field->addOption(self::collapsedHidden, $this->_('Hidden (not shown in the editor)'));
$field->attr('value', (int) $this->collapsed);

View File

@@ -697,7 +697,13 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$classes = array();
$useColumnWidth = $this->useColumnWidth;
$renderAjaxInputfield = $this->wire()->config->ajax ? $this->wire()->input->get('renderInputfieldAjax') : null;
$lockedStates = array(Inputfield::collapsedNoLocked, Inputfield::collapsedYesLocked, Inputfield::collapsedBlankLocked);
$lockedStates = array(
Inputfield::collapsedNoLocked,
Inputfield::collapsedYesLocked,
Inputfield::collapsedBlankLocked,
Inputfield::collapsedTabLocked
);
if($useColumnWidth === true && isset($_classes['form']) && strpos($_classes['form'], 'InputfieldFormNoWidths') !== false) {
$useColumnWidth = false;
@@ -988,8 +994,9 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
public function ___renderInputfield(Inputfield $inputfield, $renderValueMode = false) {
$inputfieldID = $inputfield->attr('id');
$collapsed = $inputfield->getSetting('collapsed');
$ajaxInputfield = $collapsed == Inputfield::collapsedYesAjax || ($collapsed == Inputfield::collapsedBlankAjax && $inputfield->isEmpty());
$collapsed = (int) $inputfield->getSetting('collapsed');
$ajaxInputfield = $collapsed == Inputfield::collapsedYesAjax || $collapsed === Inputfield::collapsedTabAjax
|| ($collapsed == Inputfield::collapsedBlankAjax && $inputfield->isEmpty());
$ajaxHiddenInput = "<input type='hidden' name='processInputfieldAjax[]' value='$inputfieldID' />";
$ajaxID = $this->wire()->config->ajax ? $this->wire()->input->get('renderInputfieldAjax') : '';
$required = $inputfield->getSetting('required');
@@ -1000,6 +1007,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$ajaxInputfield = false;
if($collapsed == Inputfield::collapsedYesAjax) $inputfield->collapsed = Inputfield::collapsedYes;
if($collapsed == Inputfield::collapsedBlankAjax) $inputfield->collapsed = Inputfield::collapsedBlank;
if($collapsed == Inputfield::collapsedTabAjax) $inputfield->collapsed = Inputfield::collapsedTab;
// indicate to next processInput that this field can be processed
$inputfield->appendMarkup .= $ajaxHiddenInput;
}
@@ -1172,12 +1180,20 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
Inputfield::collapsedLocked,
Inputfield::collapsedNoLocked,
Inputfield::collapsedBlankLocked,
Inputfield::collapsedYesLocked
Inputfield::collapsedYesLocked,
Inputfield::collapsedTabLocked,
);
$ajaxTypes = array(
Inputfield::collapsedYesAjax,
Inputfield::collapsedBlankAjax,
Inputfield::collapsedTabAjax,
);
$collapsed = (int) $inputfield->getSetting('collapsed');
if(in_array($collapsed, $skipTypes)) return false;
if(in_array($collapsed, array(Inputfield::collapsedYesAjax, Inputfield::collapsedBlankAjax))) {
if(in_array($collapsed, $ajaxTypes)) {
$processAjax = $this->wire()->input->post('processInputfieldAjax');
if(is_array($processAjax) && in_array($inputfield->attr('id'), $processAjax)) {
// field can be processed (convention used by InputfieldWrapper)

View File

@@ -8,7 +8,7 @@
* For documentation about the fields used in this class, please see:
* /wire/core/Fieldtype.php
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
*
@@ -25,12 +25,19 @@ class InputfieldFieldsetTabOpen extends InputfieldFieldsetOpen {
public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields();
$inputfields->remove($inputfields->getChildByName('columnWidth'));
$in = $inputfields->getChildByName('columnWidth');
if($in) $inputfields->remove($in);
$in = $inputfields->getChildByName('collapsed');
//if($in->parent) $in->parent->set('collapsed', Inputfield::collapsedYes);
foreach($in->getOptions() as $key => $value) {
// tabs may not be collapsed
if($key != Inputfield::collapsedNo && $key != Inputfield::collapsedYesAjax) $in->removeOption($key);
if($in) {
foreach($in->getOptions() as $key => $value) {
// tabs may not be collapsed
if($key != Inputfield::collapsedNo && $key != Inputfield::collapsedYesAjax) {
$in->removeOption($key);
}
}
}
// tabs don't support showIf
$in = $inputfields->getChildByName('showIf');
@@ -49,15 +56,16 @@ class FieldtypeFieldsetTabOpen extends FieldtypeFieldsetOpen {
'version' => 100,
'summary' => 'Open a fieldset to group fields. Same as Fieldset (Open) except that it displays in a tab instead.',
'permanent' => true,
);
);
}
public function getInputfield(Page $page, Field $field) {
/** @var InputfieldFieldsetTabOpen $inputfield */
$inputfield = $this->wire(new InputfieldFieldsetTabOpen());
$inputfield->class = $this->className();
if($field->modal) {
$inputfield->modal = true;
} else if($field->collapsed == Inputfield::collapsedYesAjax) {
if($field->get('modal')) {
$inputfield->set('modal', true);
} else if($field->collapsed == Inputfield::collapsedYesAjax || $field->collapsed == Inputfield::collapsedTabAjax) {
$inputfield->collapsed = $field->collapsed;
}
return $inputfield;
@@ -70,7 +78,7 @@ class FieldtypeFieldsetTabOpen extends FieldtypeFieldsetOpen {
$in->label = $this->_('Open in modal window?');
$in->description = $this->_('Check the box to make this tab open in its own modal window. This can improve performance with large forms.');
$in->notes = $this->_('To solve a similar need, you might instead consider the AJAX option, available at: Input (tab) > Visibility > Presentation.');
if($field->modal) $in->attr('checked', 'checked');
if($field->get('modal')) $in->attr('checked', 'checked');
$inputfields->add($in);
return $inputfields;
}

View File

@@ -1175,6 +1175,15 @@ class ProcessField extends Process implements ConfigurableModule {
$field->attr('value', '');
$form->append($field);
}
// move the 'tabLabel' input right below the 'collapsed' input
$f1 = $form->getChildByName('tabLabel');
$fs = $form->getChildByName('visibility');
$f2 = $fs ? $fs->getChildByName('collapsed') : null;
if($f1 && $f2) {
$f1->getParent()->remove($f1);
$fs->insertAfter($f1, $f2);
}
$focus = $input->get('focus');
if($focus) {
@@ -1649,6 +1658,19 @@ class ProcessField extends Process implements ConfigurableModule {
$field->collapsed = Inputfield::collapsedBlank;
$form->add($field);
$languageFields[] = $field;
/** @var InputfieldText $field */
$field = $this->modules->get('InputfieldText');
$field->label = $this->_('Label for tab');
$field->attr('name', 'tabLabel');
$field->attr('value', (string) $this->field->get('tabLabel'));
$field->icon = 'tag';
$field->description = $this->_('If field is displayed in its own tab, optionally specify an alternate tab label if different from the field label.');
$field->notes = $this->_('The tab label should ideally be very short, like just one word.');
$field->collapsed = Inputfield::collapsedBlank;
$field->showIf = 'collapsed=' . Inputfield::collapsedTab . '|' . Inputfield::collapsedTabAjax . '|' . Inputfield::collapsedTabLocked;
$form->add($field);
$languageFields[] = $field;
if($languages) foreach($languageFields as $field) {
$field->useLanguages = true;
@@ -2286,7 +2308,7 @@ class ProcessField extends Process implements ConfigurableModule {
}
if($name === 'tags') {
$value = $sanitizer->words($value);
$value = $sanitizer->getTextTools()->strtolower($sanitizer->words($value));
}
$this->field->set($name, $value);

View File

@@ -914,6 +914,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
Inputfield::collapsedNoLocked,
Inputfield::collapsedBlankLocked,
Inputfield::collapsedYesLocked,
Inputfield::collapsedTabLocked,
);
$collapsed = $inputfield->getSetting('collapsed');
if($collapsed > 0 && !in_array($collapsed, $skipCollapsed)) {
@@ -932,6 +933,42 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
$tabWrap = null;
$tabOpen = null;
$tabViewable = null;
$fieldsetTab = null;
$collapsedTabTypes = array(
Inputfield::collapsedTab => 1,
Inputfield::collapsedTabAjax => 1,
Inputfield::collapsedTabLocked => 1,
);
// identify fields displayed as tabs and add fieldset open/close around them
foreach($contentTab as $inputfield) {
/** @var Inputfield $inputfield */
if(!isset($collapsedTabTypes[$inputfield->collapsed])) continue;
/** @var InputfieldFieldsetTabOpen $tab */
if(!$fieldsetTab) {
$fieldsetTab = $this->modules->get('FieldtypeFieldsetTabOpen');
$this->modules->get('FieldtypeFieldsetClose');
}
$tab = new InputfieldFieldsetTabOpen();
$this->wire($tab);
$tab->attr('name', '_tab_' . $inputfield->attr('name'));
$tab->attr('id', '_tab_' . $inputfield->attr('id'));
$tab->label = $inputfield->getSetting('tabLabel|label');
if($inputfield->collapsed === Inputfield::collapsedTabAjax) {
$tab->collapsed = Inputfield::collapsedYesAjax;
$inputfield->collapsed = Inputfield::collapsedNo;
if($this->isPost && !$contentTab->isProcessable($tab)) {
$contentTab->remove($inputfield);
continue;
}
}
$contentTab->insertBefore($tab, $inputfield);
/** @var InputfieldFieldsetClose $tabClose */
$tabClose = new InputfieldFieldsetClose();
$this->wire($tabClose);
$tabClose->attr('id+name', $tab->attr('name') . '_END');
$contentTab->insertAfter($tabClose, $inputfield);
}
foreach($contentTab as $inputfield) {
if(!$tabOpen && $inputfield->className() === 'InputfieldFieldsetTabOpen') {
@@ -945,6 +982,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
continue;
}
$tabOpen = $inputfield;
/** @var InputfieldWrapper $tabWrap */
$tabWrap = $this->wire(new InputfieldWrapper());
$tabWrap->attr('title', $tabOpen->getSetting('label'));
$tabWrap->id = $tabOpen->attr('id');