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:
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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');
|
||||
|
Reference in New Issue
Block a user