├── .gitignore ├── images ├── .DS_Store ├── icon--expand.svg └── icon--collapse.svg ├── assets ├── bannerPSD.psd ├── screenshot-1.jpg ├── screenshot-2.jpg ├── screenshot-3.jpg ├── screenshot-4.jpg ├── screenshot-5.jpg ├── screenshot-6.jpg ├── banner-1554x500.png └── banner-772x250.png ├── js ├── .jshintrc ├── .jscsrc ├── acf5_repeater_collapser_admin.js └── acf4_repeater_collapser_admin.js ├── css ├── acf5_repeater_collapser_admin.css └── acf4_repeater_collapser_admin.css ├── acf_repeater_collapser.php └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /.DS_Store 2 | -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/images/.DS_Store -------------------------------------------------------------------------------- /assets/bannerPSD.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/bannerPSD.psd -------------------------------------------------------------------------------- /assets/screenshot-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-1.jpg -------------------------------------------------------------------------------- /assets/screenshot-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-2.jpg -------------------------------------------------------------------------------- /assets/screenshot-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-3.jpg -------------------------------------------------------------------------------- /assets/screenshot-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-4.jpg -------------------------------------------------------------------------------- /assets/screenshot-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-5.jpg -------------------------------------------------------------------------------- /assets/screenshot-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/screenshot-6.jpg -------------------------------------------------------------------------------- /assets/banner-1554x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/banner-1554x500.png -------------------------------------------------------------------------------- /assets/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrwweb/ACF-Repeater-Collapser/HEAD/assets/banner-772x250.png -------------------------------------------------------------------------------- /js/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "es3": true, 7 | "expr": true, 8 | "immed": true, 9 | "noarg": true, 10 | "onevar": true, 11 | "quotmark": "single", 12 | "trailing": true, 13 | "undef": true, 14 | "unused": true, 15 | 16 | "browser": true, 17 | 18 | "globals": { 19 | "_": false, 20 | "Backbone": false, 21 | "jQuery": false, 22 | "JSON": false, 23 | "wp": false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /images/icon--expand.svg: -------------------------------------------------------------------------------- 1 | maximize -------------------------------------------------------------------------------- /images/icon--collapse.svg: -------------------------------------------------------------------------------- 1 | minimize -------------------------------------------------------------------------------- /css/acf5_repeater_collapser_admin.css: -------------------------------------------------------------------------------- 1 | .collapse { 2 | box-sizing: border-box; 3 | position: absolute; 4 | top:15px; 5 | right:12px; 6 | border:1px solid #d1d1d1; 7 | background:#FFF; 8 | font-weight:normal; 9 | color:#6a6f74; 10 | padding:1px 10px 1px 30px; 11 | text-align: right; 12 | min-width: 114px; 13 | transition: all 100ms ease-in-out; 14 | background-color: rgba(249, 249, 249, 0.50); 15 | user-select: none; 16 | } 17 | .collapse:hover { 18 | border:1px solid #6a6f74; 19 | background-color: #FFF; 20 | cursor: pointer; 21 | } 22 | .collapse:focus { 23 | background-color: #FFF; 24 | box-shadow: none; 25 | outline:none; 26 | } 27 | .collapse:before { 28 | position: absolute; 29 | top:0; 30 | left:3px; 31 | content: ""; 32 | background: url(../images/icon--collapse.svg); 33 | background-size: 10px; 34 | width: 20px; 35 | height: 20px; 36 | background-repeat: no-repeat; 37 | background-position: center center; 38 | } 39 | 40 | .collapse--active { 41 | 42 | } 43 | .collapse--active:before { 44 | background: url(../images/icon--expand.svg); 45 | background-size: 10px; 46 | width: 20px; 47 | height: 20px; 48 | background-repeat: no-repeat; 49 | background-position: center center; 50 | } 51 | 52 | .flexi-label { 53 | font-style: italic; 54 | padding-left: 5px; 55 | font-size: 12px; 56 | color:#999; 57 | } 58 | 59 | 60 | 61 | /*.acf-flexible-content .layout .acf-fc-layout-handle:hover { 62 | background: #f9f9f9; 63 | }*/ 64 | 65 | .acf-flexible-content .layout .acf-fc-layout-handle { 66 | background: rgba(249, 249, 249, 0.50); 67 | } 68 | /*.acf-flexible-content .layout .acf-fc-layout-handle:hover .acf-fc-layout-order, 69 | .acf-flexible-content .layout.-collapsed .acf-fc-layout-order { 70 | background: #FFF; 71 | }*/ 72 | 73 | 74 | 75 | .acf-fields.-left .collapse{ 76 | position: relative; 77 | left:0; 78 | top:0; 79 | } 80 | -------------------------------------------------------------------------------- /acf_repeater_collapser.php: -------------------------------------------------------------------------------- 1 | settings['version']; 31 | if ( version_compare( $acf_version, '4.0', '<' ) ) { 32 | return; 33 | } 34 | 35 | $uri = plugin_dir_url( __FILE__ ); 36 | $version = ACF_REPEATER_COLLAPSER_VERSION; 37 | $prefix = version_compare( $acf_version, '5.3.1', '>=' ) ? 'acf5' : 'acf4'; 38 | 39 | wp_enqueue_script( 40 | 'acf_repeater_collapser_admin_js', 41 | esc_url( "{$uri}js/{$prefix}_repeater_collapser_admin.js" ), 42 | array( 'jquery' ), 43 | $version 44 | ); 45 | wp_localize_script( 'acf_repeater_collapser_admin_js', 'acfrcL10n', array( 46 | 'collapseRows' => esc_html__( 'Collapse All Rows', 'advanced-custom-field-repeater-collapser' ), 47 | 'collapseRow' => esc_html__( 'Collapse Row', 'advanced-custom-field-repeater-collapser' ), 48 | 'expandRows' => esc_html__( 'Expand All Rows', 'advanced-custom-field-repeater-collapser' ), 49 | 'expandRow' => esc_html__( 'Expand Row', 'advanced-custom-field-repeater-collapser' ), 50 | 'expandAll' => esc_html__( 'Expand All', 'advanced-custom-field-repeater-collapser' ), 51 | 'collapseAll' => esc_html__( 'Collapse All', 'advanced-custom-field-repeater-collapser' ), 52 | ) ); 53 | 54 | wp_enqueue_style( 55 | 'acf_repeater_collapser_admin_css', 56 | esc_url( "{$uri}css/{$prefix}_repeater_collapser_admin.css" ), 57 | false, 58 | $version 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /js/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireSpacesInConditionalExpression": true, 3 | "requireSpacesInFunction": { 4 | "beforeOpeningCurlyBrace": true 5 | }, 6 | "disallowSpacesInFunction": { 7 | "beforeOpeningRoundBrace": true 8 | }, 9 | "requireMultipleVarDecl": "onevar", 10 | "requireSpacesInsideObjectBrackets": "all", 11 | "disallowSpaceAfterObjectKeys": true, 12 | "requireSpaceAfterBinaryOperators": [ 13 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 14 | "&=", "|=", "^=", "+=", 15 | 16 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 17 | "|", "^", "&&", "||", "===", "==", ">=", 18 | "<=", "<", ">", "!=", "!==" 19 | ], 20 | "requireSpaceBeforeBinaryOperators": [ 21 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 22 | "&=", "|=", "^=", "+=", 23 | 24 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 25 | "|", "^", "&&", "||", "===", "==", ">=", 26 | "<=", "<", ">", "!=", "!==" 27 | ], 28 | "disallowKeywords": ["with"], 29 | "disallowMultipleLineBreaks": true, 30 | "validateLineBreaks": "LF", 31 | "disallowMixedSpacesAndTabs": "smart", 32 | "disallowTrailingWhitespace": true, 33 | 34 | "requireCurlyBraces": [ "if", "else", "for", "while", "do", "try", "catch" ], 35 | "requireSpaceBeforeBlockStatements": true, 36 | "requireParenthesesAroundIIFE": true, 37 | "requireBlocksOnNewline": true, 38 | "requireOperatorBeforeLineBreak": [ 39 | "?", 40 | "=", 41 | "+", 42 | "-", 43 | "/", 44 | "*", 45 | "==", 46 | "===", 47 | "!=", 48 | "!==", 49 | ">", 50 | ">=", 51 | "<", 52 | "<=" 53 | ], 54 | "requireSpaceBeforeBinaryOperators": [ 55 | "?", 56 | "=", 57 | "+", 58 | "-", 59 | "/", 60 | "*", 61 | "==", 62 | "===", 63 | "!=", 64 | "!==", 65 | ">", 66 | ">=", 67 | "<", 68 | "<=" 69 | ], 70 | "requireSpaceAfterBinaryOperators": [ 71 | "?", 72 | "=", 73 | "+", 74 | "/", 75 | "*", 76 | ":", 77 | "==", 78 | "===", 79 | "!=", 80 | "!==", 81 | ">", 82 | ">=", 83 | "<", 84 | "<=" 85 | ], 86 | "disallowSpaceBeforeBinaryOperators": [","], 87 | "disallowSpaceAfterBinaryOperators": [], 88 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~"], 89 | "requireSpaceAfterPrefixUnaryOperators": ["!"], 90 | "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], 91 | "requireCamelCaseOrUpperCaseIdentifiers": true, 92 | "disallowMultipleLineStrings": true, 93 | "validateQuoteMarks": "'", 94 | "validateIndentation": "\t", 95 | "requireLineFeedAtFileEnd": true, 96 | "requireDotNotation": true, 97 | "disallowNewlineBeforeBlockStatements": true, 98 | 99 | "disallowTrailingComma": true, 100 | "disallowPaddingNewlinesInBlocks": true, 101 | "disallowEmptyBlocks": true, 102 | "disallowQuotedKeysInObjects": "allButReserved", 103 | "disallowDanglingUnderscores": true, 104 | "requireCommaBeforeLineBreak": true, 105 | "disallowKeywordsOnNewLine": ["else"], 106 | "requireCapitalizedConstructors": true, 107 | "safeContextKeyword": [ "that" ], 108 | "validateJSDoc": { 109 | "checkParamNames": true, 110 | "checkRedundantParams": true, 111 | "requireParamTypes": true 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /js/acf5_repeater_collapser_admin.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | // Exists function 4 | $.fn.exists = function(){ return this.length > 0; } 5 | 6 | // Check if we should show the repeter collapse button 7 | $.fn.repeaterShowcollapse = function() { 8 | 9 | // If we can find at the acf icon to collapse the fields - show (or remove) 10 | if ( $(this).find('a.-collapse').exists() ) { 11 | //$(this).find('.collapse').show(); 12 | //console.log('collapse exists'); 13 | } else { 14 | 15 | // If we can't find a collapse button 16 | // The user hasn't set the collapsed target, so lets set one 17 | $(this).find('.acf-row').each(function( index ) { 18 | $(this).find('.acf-field').first().addClass('-collapsed-target'); 19 | }); 20 | 21 | } 22 | 23 | // If the repeater is empty - hide (target the first repeater to support nested repeaters) 24 | if( $(this).find('.acf-repeater').first().is('.-empty')) { 25 | $(this).find('.collapse').hide(); 26 | } else { 27 | $(this).find('.collapse').show(); 28 | } 29 | 30 | } 31 | 32 | // Check if we should show or hide the flexi collapse button 33 | $.fn.flexiShowcollapse = function() { 34 | if( $(this).find('.acf-flexible-content').is('.empty')) { 35 | $(this).find('.collapse').first().hide(); 36 | } else { 37 | $(this).find('.collapse').first().show(); 38 | } 39 | } 40 | 41 | /** 42 | Doc Ready 43 | **/ 44 | $(document).ready(function(){ 45 | 46 | /** 47 | Repeater Functionality 48 | **/ 49 | 50 | // Open all fields on page load 51 | $('.acf-row').removeClass('-collapsed'); 52 | 53 | // Append the collapse button 54 | $( ".acf-field-repeater > .acf-label" ).append(''); 55 | 56 | // Check if we should show the button 57 | $( ".acf-field-repeater" ).each(function( index ) { 58 | $(this).repeaterShowcollapse(); 59 | }); 60 | 61 | // On the click of the repeater toggle 62 | $('body').on('click','.acf-field-repeater .collapse', function( event ) { 63 | 64 | event.preventDefault(); 65 | 66 | var active = $(this).hasClass('collapse--active'); 67 | 68 | // open all the things 69 | if( active ) { 70 | $(this).closest('.acf-field').find('.acf-row').removeClass('-collapsed'); 71 | $(this).removeClass('collapse--active'); 72 | $(this).html( acfrcL10n.collapseAll ); 73 | 74 | // close all the things 75 | } else { 76 | $(this).closest('.acf-field').find('.acf-row').addClass('-collapsed'); 77 | $(this).addClass('collapse--active'); 78 | $(this).html( acfrcL10n.expandAll ); 79 | } 80 | 81 | 82 | }); 83 | 84 | 85 | /** 86 | Flexi Functionality 87 | **/ 88 | 89 | // Append the collapse button 90 | $(this).find('.acf-field-flexible-content > .acf-label label').append(''); 91 | 92 | // On the click of the toggle 93 | $('.acf-field-flexible-content').find('.collapse').first().click(function( event ) { 94 | 95 | event.preventDefault(); 96 | 97 | var active = $(this).hasClass('collapse--active'); 98 | 99 | // open all the things 100 | if( active ) { 101 | $(this).removeClass('collapse--active'); 102 | $(this).html( acfrcL10n.collapseAll); 103 | $(this).closest('.acf-fields').each(function( index ) { 104 | $(this).find('.layout').removeClass('-collapsed'); 105 | }); 106 | // close all the things 107 | } else { 108 | $(this).addClass('collapse--active'); 109 | $(this).html( acfrcL10n.expandAll ); 110 | $(this).closest('.acf-fields').each(function( index ) { 111 | $(this).find('.layout').addClass('-collapsed'); 112 | }); 113 | } 114 | 115 | 116 | 117 | }); 118 | 119 | // Hide the button if the field is empty 120 | $( ".acf-field-flexible-content" ).each(function( index ) { 121 | $(this).flexiShowcollapse(); 122 | }); 123 | 124 | }); // Doc ready 125 | 126 | 127 | /** 128 | AJAX Complete 129 | This runs after a row has been added / removed or a single row collapsed / collapseed 130 | **/ 131 | 132 | $(document).ajaxComplete(function() { 133 | 134 | // Check if we should show the button on each repeater field 135 | $( ".acf-field-repeater" ).each(function( index ) { 136 | $(this).repeaterShowcollapse(); 137 | }); 138 | 139 | // Check if we should show the button on each flexi field 140 | $( ".acf-field-flexible-content" ).each(function( index ) { 141 | $(this).flexiShowcollapse(); 142 | }); 143 | 144 | }); 145 | 146 | 147 | })(jQuery); -------------------------------------------------------------------------------- /css/acf4_repeater_collapser_admin.css: -------------------------------------------------------------------------------- 1 | /* Expand / Collapse Buttons */ 2 | .field-repeater-toggle-all { 3 | float:right; 4 | margin: -5px 0 15px !important; 5 | } 6 | 7 | .button.field-repeater-toggle-single { 8 | padding: 1px 1px 0 1px; 9 | } 10 | .layout .field-repeater-toggle-single { 11 | margin: 0 5px 0 -5px; 12 | } 13 | .repeater-button-cell { 14 | background: #f9f9f9 !important; 15 | padding: 0 !important; 16 | vertical-align: middle !important; 17 | text-align: center !important; 18 | border-right-color: #e1e1e1 !important; 19 | width: 30px !important; 20 | /* Sorry 'bout the !importants. They are gross. Sadly, so are the ACF selectors I have to battle. */ 21 | } 22 | 23 | .field_type-flexible_content .layout { 24 | padding-left: 30px; 25 | background: transparent; 26 | } 27 | 28 | .field_type-flexible_content .field-repeater-toggle-single { 29 | position: absolute; 30 | top: 50%; 31 | margin-top: -15px; 32 | left: 5px; 33 | } 34 | 35 | .field_type-flexible_content .acf-fc-layout-handle { 36 | background: #fff; 37 | } 38 | 39 | .button.field-repeater-toggle-single { 40 | display: inline; 41 | -webkit-font-smoothing: antialiased; 42 | font: normal 20px/1 'dashicons'; 43 | vertical-align: text-bottom; 44 | color: #777; 45 | background: url(../images/icon--collapse.svg); 46 | background-size: 10px; 47 | width: 20px; 48 | height: 20px; 49 | background-repeat: no-repeat; 50 | background-position: center center; 51 | box-shadow: none; 52 | border:1px solid #d1d1d1; 53 | } 54 | 55 | .button.field-repeater-toggle-single:focus { 56 | background: url(../images/icon--collapse.svg) center center no-repeat #FFF; 57 | background-size: 10px; 58 | box-shadow: none; 59 | border:1px solid #d1d1d1; 60 | } 61 | .field-repeater-toggle-all::before { 62 | margin: 3px 5px 0 0; 63 | } 64 | .button.field-repeater-toggle-single:hover { 65 | border:1px solid #d1d1d1; 66 | background: url(../images/icon--collapse.svg) center center no-repeat #FFF; 67 | background-size: 10px; 68 | } 69 | 70 | .layout.collapsed-row .button.field-repeater-toggle-single, 71 | .row.collapsed-row .button.field-repeater-toggle-single { 72 | background: url(../images/icon--expand.svg) center center no-repeat; 73 | background-size: 10px; 74 | } 75 | 76 | .layout.collapsed-row .button.field-repeater-toggle-single:hover, 77 | .row.collapsed-row .button.field-repeater-toggle-single:hover { 78 | border:1px solid #d1d1d1; 79 | background: url(../images/icon--expand.svg) center center no-repeat #FFF; 80 | background-size: 10px; 81 | } 82 | 83 | 84 | 85 | 86 | 87 | .acf_flexible_content, 88 | .repeater { 89 | clear: both; 90 | } 91 | 92 | .collapsed-repeater .acf_input { 93 | position: relative; 94 | } 95 | 96 | .collapsed-row .acf_input-wrap tr { 97 | display: none; 98 | } 99 | 100 | .collapsed-row .acf_input-wrap tr:first-child { 101 | display: table-row; 102 | } 103 | 104 | /* "Masks" the first field in order to prevent clicking on it */ 105 | .collapsed-row .acf_input-wrap tr::after { 106 | position: absolute; 107 | top: 0; bottom: 0; left: 0px; right: 0; 108 | display: block; 109 | content: ' '; 110 | background: -webkit-gradient(linear, left top, left bottom, from(rgba( 244, 244, 244, .2 )), to(rgba( 244, 244, 244, 1 ))); 111 | background: -o-linear-gradient( rgba( 244, 244, 244, .2 ), rgba( 244, 244, 244, 1 ) ); 112 | background: -moz-linear-gradient( rgba( 244, 244, 244, .2 ), rgba( 244, 244, 244, 1 ) ); 113 | background: -webkit-linear-gradient( rgba( 244, 244, 244, .2 ), rgba( 244, 244, 244, 1 ) ); 114 | background: linear-gradient( rgba( 244, 244, 244, .2 ), rgba( 244, 244, 244, 1 ) ); 115 | } 116 | 117 | /* 2017 button styles */ 118 | .button.field-repeater-toggle-all { 119 | box-sizing: border-box; 120 | position: relative; 121 | border:1px solid #d1d1d1; 122 | background:#FFF; 123 | color:#e5e5e5; 124 | float:right; 125 | font-weight:normal; 126 | color:#6a6f74; 127 | padding:1px 10px 1px 30px; 128 | text-align: right; 129 | min-width: 114px; 130 | transition: all 100ms ease-in-out; 131 | background-color: rgba(249, 249, 249, 0.50); 132 | user-select: none; 133 | box-shadow: none; 134 | } 135 | 136 | .button.field-repeater-toggle-all:before { 137 | position: absolute; 138 | top:0; 139 | left:3px; 140 | content: ""; 141 | background: url(../images/icon--collapse.svg); 142 | background-size: 10px; 143 | width: 20px; 144 | height: 20px; 145 | background-repeat: no-repeat; 146 | background-position: center center; 147 | } 148 | 149 | .button.field-repeater-toggle-all.collapse:hover { 150 | border:1px solid #d1d1d1; 151 | background-color: #FFF; 152 | color:#b0a9a8; 153 | } 154 | 155 | .button.field-repeater-toggle-all:focus { 156 | outline: none; 157 | box-shadow: none; 158 | border:1px solid #d1d1d1; 159 | color:#6a6f74; 160 | } 161 | 162 | div.field_type-flexible_content.collapsed-repeater .button.field-repeater-toggle-all:before, 163 | div.field_type-repeater.collapsed-repeater .button.field-repeater-toggle-all:before { 164 | background: url(../images/icon--expand.svg); 165 | background-size: 10px; 166 | width: 20px; 167 | height: 20px; 168 | background-repeat: no-repeat; 169 | background-position: center center; 170 | } 171 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Advanced Custom Fields Repeater & Flexible Content Fields Collapser === 2 | Contributors: aaronrutley,mrwweb 3 | Tags: advanced custom fields, acf, repeater 4 | Requires at least: 3.0.0 5 | Tested up to: 4.3 6 | Stable tag: 1.5.0 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | Easier sorting for large repeated fields in the Advanced Custom Fields plugin. 11 | 12 | == Description == 13 | 14 | The Repeater and Flexible Content Field features make ACF really powerful, but if you have more than a few fields with either, they becomes unwieldy to sort. This plugin collapses each instance of the repeated fields—**only for the "Row" layout**—to allow for easy sorting. 15 | 16 | *To help identify each repeater field group when collapsed, the first field of each repeated field group is shown. See the [screenshots](https://wordpress.org/plugins/advanced-custom-field-repeater-collapser/screenshots/) for an example.* 17 | 18 | ------------------------------------------------ 19 | 20 | **Requirements:** 21 | 22 | * [Advanced Custom Fields](https://wordpress.org/extend/plugins/advanced-custom-fields/) 4.X *AND* one or both of the [Repeater Field](http://www.advancedcustomfields.com/add-ons/repeater-field/) or [Flexible Content Field](http://www.advancedcustomfields.com/add-ons/flexible-content-field/) paid add-ons 23 | * ACF 5.X PRO 24 | * Both ACF 4.X and ACF 5.X: Use the **Row Layout** 25 | 26 | ------------------------------------------------ 27 | 28 | **Other contributors:** 29 | 30 | * [mrwweb](https://github.com/mrwweb) 31 | * [brasofilo](https://profiles.wordpress.org/brasofilo/) 32 | * [weskoop](https://profiles.wordpress.org/weskoop/) 33 | * [philiphetue](https://github.com/philiphetue) 34 | * [robneu](https://github.com/robneu) 35 | 36 | == Frequently Asked Questions == 37 | 38 | = Where is the "Collapse Rows" button? I don't see it. = 39 | The plugin only supports the "Row" layout for repeaters. 40 | 41 | = Why don't rows collapse? / Why is the row only grayed out? = 42 | To help identify each repeater field group when collapsed, the first field of each repeated field group is shown. See the screenshots for an example. 43 | 44 | When possible, use a short field (like a text field or dropdown) as the first row in a repeatable fieldset. 45 | 46 | = Can I collapse individual rows? = 47 | Now you can, as of version 1.3.0. 48 | 49 | = What exactly is the "Collapse All Rows" button behaviour = 50 | With the addition of the ability to collapse single rows, the button now is always in "Collapse" mode unless all rows are collapsed. Then it's "Expand." 51 | 52 | == Installation == 53 | 54 | 1. Download the plugin. 55 | 1. Upload all files to /wp-content/plugins/ 56 | 1. Activate the plugin on the Plugins page. 57 | 58 | == Screenshots == 59 | 60 | 1. Even with a few fields per repeater instance, it becomes unwieldy to sort. 61 | 2. This plugin collapses all but the rows first field for easy sorting. (Only "Row Layout" fields are supported.) 62 | 3. Once collapsed, easily reorder your rows! 63 | 4. The plugin supports nested repeaters. 64 | 5. Flexible fields too! 65 | 6. New in v1.3.0, you can collapse individual fields! (Thanks to Angie Meeker Designs for sponsoring this feature.) 66 | 67 | == Changelog == 68 | 69 | = 1.5.0 = 70 | * [ACF v4] - Style fixes and new icons 71 | * [ACF v5] - New JS so Collapse All & Expand All now works again 72 | 73 | = 1 June 2016 = 74 | * Added `adopt-me` tag. Please [contact me](https://mrwweb.com/contact/) if you're interested. 75 | 76 | = 1.4.3 (15 Oct 2015) = 77 | * THIS UPDATE DOES NOT FIX THE PLUGIN WITH ACF 5.3.1. IT IS PURELY FOR FUTURE TRANSLATION FEATURES REQUIRED BY THE PLUGIN REPOSITORY. 78 | * [i18n] Add text-domain and make all strings translatable for upcoming translate.wordpress.org plugin migration. 79 | 80 | = 1.4.2 (7 Sep 2015) = 81 | * [Fix] Include [compatibility filter](http://www.advancedcustomfields.com/blog/acf-pro-5-2-7-update/) to fix in ACF PRO since v5.2.7. 82 | * [Fix] Prevent fatal error if ACF is not activated. 83 | * [Misc] Code cleanup. Thanks to robneu on GitHub. 84 | 85 | = 1.4.1 (19 Nov 2014) = 86 | * [Fix] Pass `event` to collapsing JS functions to prevent Firefox errors. 87 | * [Fix] No margin on table-layout fields (ACF5 only) 88 | * A big thanks to [@tmconnect](https://github.com/tmconnect) for the pull requests that made up this release. 89 | 90 | = 1.4.0 (15 Aug 2014) = 91 | * [New] **ACF5 Support** 92 | * [Fix] aria-controls attribute fix 93 | * Note: The plugin supports both ACF4 and ACF5; however, all future feature improvements will only be made for ACF 5.X. 94 | * Did this plugin save you some time or help build a site for a client? [Buy me a beverage!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=HH4FKZHK75MTN&lc=US&item_name=MRW%20Web%20Design%20%2f%20Mark%20Root%2dWiley&item_number=Buy%20Me%20a%20Beverage%21%20%2d%20ACF%20Repeater%20Collapser¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted). 95 | 96 | = 1.3.0 (18 Jul 2014) = 97 | * [New] **Ability to collapse single fields** 98 | * [New] Added aria-expanded / aria-controls attributes on rows for improved accessibility 99 | * [Tweak] Clarified button text and added icons to buttons for improved UI 100 | * [Fix] Fixed errant closing tag on ', 11 | $collapseSingleButtonTable = '
', 12 | $collapseSingleButton = ''; 13 | 14 | // find each repeater & flexible instance, add the button if the field uses the row layout 15 | $('.field_type-repeater, .field_type-flexible_content').each( function() { 16 | var $repeater = $(this); 17 | 18 | // only use this on row layout 19 | if( $( '.acf-input-table', $repeater ).hasClass('row_layout') ) { 20 | $repeater.data('acf-rowset-collapsed', false).attr('aria-expanded', false); 21 | 22 | // first: nested, second: parent 23 | if( $repeater.is( 'tr' ) ) { 24 | $repeater.children( 'td:last-child' ) 25 | .children( '.inner' ) 26 | .prepend( $collapseAllButton ) 27 | .data('acf-rowset-collapsed', false) 28 | .data('acf-repeater-nested', true); 29 | $('.row,.row-clone', $repeater ).data('acf-repeater-nested', true); 30 | } else { 31 | $repeater.prepend( $collapseAllButton ).data('acf-rowset-collapsed', false); 32 | } 33 | } 34 | }); 35 | 36 | // append single repeater collapse to each row of repeater field 37 | $('.field_type-repeater .row_layout .row,.field_type-repeater .row_layout .row-clone').each( function() { 38 | var id = 'acf-repeater-' + i, 39 | $that = $(this); 40 | 41 | $that.prepend( $collapseSingleButtonTable ) 42 | .data('acf-row-collapsed', false).attr('aria-expanded', true) 43 | .attr('id','acf-repeater-' + i) 44 | .attr('aria-live','off'); 45 | $('.field-repeater-toggle-single', $that.first().attr('aria-controls',id ) ); 46 | 47 | i++; 48 | }); 49 | 50 | // append single repeater collapse to flex fields 51 | $('.field_type-flexible_content .layout').each( function() { 52 | var $that = $(this), 53 | id = 'acf-repeater-' + i; 54 | if( $('.acf-input-table', $that).hasClass('row_layout') ) { 55 | i++; 56 | 57 | $that.prepend( $collapseSingleButton ) 58 | .data('acf-row-collapsed', false) 59 | .attr('aria-expanded', true) 60 | .attr('id','acf-repeater-' + i) 61 | .attr('aria-live','off'); 62 | 63 | $('.field-repeater-toggle-single', $that).first().attr('aria-controls',id); 64 | } 65 | }); 66 | 67 | // Bind click events to the toggle functions 68 | // delegated to higher DOM element to handle dynamically added repeaters 69 | $( '.field_type-repeater, .field_type-flexible_content' ).on( 70 | 'click', 71 | '.field-repeater-toggle-all', 72 | acfRepeaterToggleAll 73 | ); 74 | $( '.field_type-repeater .row_layout,.field_type-flexible_content' ).on( 75 | 'click', 76 | '.field-repeater-toggle-single', 77 | acfRepeaterToggleSingle 78 | ); 79 | 80 | // prevent default flexible field collapsing for clarity 81 | $('.field_type-flexible_content').on( 82 | 'click', 83 | '.acf-fc-layout-handle', 84 | false 85 | ); 86 | } 87 | 88 | /** 89 | * Collapse a row or rows 90 | */ 91 | function acfRepeaterCollapseRow( $rows ) { 92 | var $rowButtonText = $('.screen-reader-text', $rows); 93 | $rows.addClass('collapsed-row') 94 | .data('acf-row-collapsed', true) 95 | .attr('aria-expanded', false); 96 | $rowButtonText.text(acfrcL10n.expandRow); 97 | } 98 | 99 | /** 100 | * Expand a row or rows 101 | */ 102 | function acfRepeaterExpandRow( $rows ) { 103 | var $rowButtonText = $('.screen-reader-text', $rows); 104 | $rows.removeClass('collapsed-row') 105 | .data('acf-row-collapsed', false) 106 | .attr('aria-expanded', true); 107 | $rowButtonText.text(acfrcL10n.collapseRow); 108 | } 109 | 110 | /** 111 | * Indicate a collapsed rowset 112 | */ 113 | function acfRepeaterExpandRowset( $wrapper ) { 114 | var $button = $('.field-repeater-toggle-all', $wrapper).first(); 115 | 116 | $wrapper.removeClass('collapsed-repeater').data('acf-rowset-collapsed', false); 117 | $button.text(acfrcL10n.collapseRows); 118 | } 119 | 120 | /** 121 | * Indicate an expanded rowset 122 | */ 123 | function acfRepeaterCollapseRowset( $wrapper ) { 124 | var $button = $('.field-repeater-toggle-all', $wrapper).first(); 125 | 126 | $wrapper.addClass('collapsed-repeater') 127 | .data('acf-rowset-collapsed', true); 128 | $button.text(acfrcL10n.expandRows); 129 | } 130 | 131 | /** 132 | * toggles set of repeater rows or flexible fields 133 | */ 134 | function acfRepeaterToggleAll( event ) { 135 | var $rows, 136 | $that = $(this), 137 | $rowsetButton = $that, 138 | $rowsetWrapper = $that.parent(); 139 | 140 | // select either nested or unnested repeater rows, not both 141 | if( true === $rowsetWrapper.data('acf-repeater-nested') ) { 142 | $rows = $('.row:data(acf-repeater-nested),.layout', $rowsetWrapper); 143 | } else { 144 | $rows = $('.row,.layout', $rowsetWrapper).not(':data(acf-repeater-nested)'); 145 | } 146 | 147 | // toggle repeater state and all rows 148 | if( true !== $rowsetWrapper.data('acf-rowset-collapsed') ) { 149 | acfRepeaterCollapseRowset( $rowsetWrapper ); 150 | acfRepeaterCollapseRow( $rows ); 151 | } else { 152 | acfRepeaterExpandRowset( $rowsetWrapper ); 153 | acfRepeaterExpandRow( $rows ); 154 | } 155 | 156 | // prevent bubbling up to parent repeater rowset 157 | event.stopPropagation(); 158 | } 159 | 160 | /** 161 | * toggles single repeater row or flexible field 162 | */ 163 | function acfRepeaterToggleSingle( event ) { 164 | var $rowButton = $(this), 165 | $rowButtonText = $('.screen-reader-text', $rowButton), 166 | $row = $rowButton.closest('.row,.layout'), 167 | $rowsetWrapper; 168 | 169 | // select either parent repeater field wrapper 170 | if( true === $row.data('acf-repeater-nested') ) { 171 | $rowsetWrapper = $row.closest( '.inner' ); 172 | } else { 173 | $rowsetWrapper = $row.closest('.field_type-repeater,.field_type-flexible_content'); 174 | } 175 | 176 | // toggle the row state and button text 177 | if( true !== $row.data('acf-row-collapsed') ) { 178 | acfRepeaterCollapseRow( $row ); 179 | } else { 180 | acfRepeaterExpandRow( $row ); 181 | } 182 | 183 | if( true === acfRepeaterAllCollapsed( $rowsetWrapper ) ) { 184 | acfRepeaterCollapseRowset( $rowsetWrapper ); 185 | } else { 186 | acfRepeaterExpandRowset( $rowsetWrapper ); 187 | } 188 | 189 | // prevent bubbling up to parent row button 190 | event.stopPropagation(); 191 | } 192 | 193 | /** 194 | * check to see if all rows in a rowset are collapsed 195 | * @param obj $rowsetWrapper jquery object 196 | * @return bool true if all rows in rowset are collapsed 197 | */ 198 | function acfRepeaterAllCollapsed( $rowsetWrapper ) { 199 | var $rows, 200 | rowStates = [], 201 | allCollapsed; 202 | // select either nested or unnested repeater rows, not both 203 | if( true === $rowsetWrapper.data('acf-repeater-nested') ) { 204 | $rows = $('.row:data(acf-repeater-nested),.layout:data(acf-repeater-nested)', $rowsetWrapper); 205 | } else { 206 | $rows = $('.row,.values .layout', $rowsetWrapper).not(':data(acf-repeater-nested)'); 207 | } 208 | 209 | // store every row collapsed state in an array 210 | $rows.each( function() { 211 | rowStates.push( $(this).data('acf-row-collapsed') ); 212 | }); 213 | 214 | // check if any rows are expanded 215 | allCollapsed = 0 > $.inArray( false, rowStates ); 216 | 217 | return allCollapsed; 218 | } 219 | 220 | // Initiatilize the plugin 221 | acfRepeaterCollapserInit(); 222 | }); 223 | --------------------------------------------------------------------------------