Skip to content

Commit 7a1c742

Browse files
committed
Expand/collapse/copy selected blocks
Resolves #18001
1 parent 7d45dc6 commit 7a1c742

File tree

4 files changed

+82
-41
lines changed

4 files changed

+82
-41
lines changed

CHANGELOG-WIP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Content Management
44
- Matrix fields using “Cards” or “Inline” view modes now show an “Add” button per entry type group, when the viewport is wide enough to support it. ([#17731](https://github.com/craftcms/cms/pull/17731))
5+
- Matrix fields set to the “Inline” view mode now have “Expand/collapse selected blocks” and “Copy selected blocks” field-level actions, if any blocks are selected. ([#18001](https://github.com/craftcms/cms/discussions/18001))
56
- Chips and cards are generally no longer hyperlinked. ([#17591](https://github.com/craftcms/cms/pull/17591))
67
- It’s now possible to add new sites to entries via their slideout editors. ([#17795](https://github.com/craftcms/cms/issues/17795))
78
- Elements created via “Save as a new…” actions now initially have an empty slug. ([#17932](https://github.com/craftcms/cms/pull/17932))

src/fields/Matrix.php

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -926,28 +926,42 @@ private function blockViewActionMenuItems(): array
926926
$view->registerJsWithVars(fn($expandAllId, $collapseAllId, $fieldId) => <<<JS
927927
(() => {
928928
const field = $('#' + $fieldId);
929-
const expandAllBtn = $('#' + $expandAllId);
930-
const collapseAllBtn = $('#' + $collapseAllId);
931-
const getBlocks = () => field.find(' > .blocks > .matrixblock');
932-
933-
expandAllBtn.on('activate', () => {
929+
const expandBtn = $('#' + $expandAllId);
930+
const collapseBtn = $('#' + $collapseAllId);
931+
const getBlocks = () => {
932+
const blocks = field.find(' > .blocks > .matrixblock');
933+
const selectedBlocks = blocks.filter('.sel');
934+
return selectedBlocks.length ? selectedBlocks : blocks;
935+
};
936+
937+
expandBtn.on('activate', () => {
934938
getBlocks().each((i, block) => {
935939
$(block).data('entry').expand();
936940
});
937941
});
938942
939-
collapseAllBtn.on('activate', () => {
943+
collapseBtn.on('activate', () => {
940944
getBlocks().each((i, block) => {
941945
$(block).data('entry').collapse();
942946
});
943947
});
944948
945949
setTimeout(() => {
946-
const menu = expandAllBtn.closest('.menu').data('disclosureMenu');
950+
const menu = expandBtn.closest('.menu').data('disclosureMenu');
947951
menu.on('show', () => {
948-
const blocks = getBlocks();
949-
menu.toggleItem(expandAllBtn[0], blocks.is('.collapsed'));
950-
menu.toggleItem(collapseAllBtn[0], blocks.is(':not(.collapsed)'));
952+
let blocks = getBlocks();
953+
let expandLabel, collapseLabel;
954+
if (blocks.is('.sel')) {
955+
expandLabel = Craft.t('app', 'Expand selected blocks');
956+
collapseLabel = Craft.t('app', 'Collapse selected blocks');
957+
} else {
958+
expandLabel = Craft.t('app', 'Expand all blocks');
959+
collapseLabel = Craft.t('app', 'Collapse all blocks');
960+
}
961+
expandBtn.find('.menu-item-label').text(expandLabel);
962+
collapseBtn.find('.menu-item-label').text(collapseLabel);
963+
menu.toggleItem(expandBtn[0], !!blocks.filter('.collapsed').length);
964+
menu.toggleItem(collapseBtn[0], !!blocks.filter(':not(.collapsed)').length);
951965
});
952966
}, 1);
953967
})();
@@ -957,7 +971,7 @@ private function blockViewActionMenuItems(): array
957971
$view->namespaceInputId($this->getInputId()),
958972
]);
959973

960-
// Copy all
974+
// Copy
961975
if ($this->maxEntries !== 1) {
962976
$items[] = ['type' => 'hr'];
963977

@@ -975,39 +989,62 @@ private function blockViewActionMenuItems(): array
975989
'type' => Entry::class,
976990
'fieldId' => $this->id,
977991
]);
978-
$copyAllJs = <<<JS
979-
copyAllBtn.on('activate', () => {
980-
const elementInfo = [];
981-
field.find('> .blocks > .matrixblock').each((i, element) => {
982-
element = $(element);
983-
elementInfo.push(Object.assign({
984-
id: element.data('id'),
985-
draftId: element.data('draftId'),
986-
revisionId: element.data('revisionId'),
987-
ownerId: element.data('ownerId'),
988-
siteId: element.data('siteId'),
989-
}, $baseInfo));
990-
});
991-
Craft.cp.copyElements(elementInfo);
992-
});
993-
JS;
994992

995-
$view->registerJsWithVars(fn($copyAllId, $fieldId) => <<<JS
993+
$view->registerJsWithVars(fn($copyAllId, $fieldId, $type) => <<<JS
996994
(() => {
997-
const copyAllBtn = $('#' + $copyAllId);
995+
const copyBtn = $('#' + $copyAllId);
998996
const field = $('#' + $fieldId);
997+
const getBlocks = () => {
998+
const blocks = field.find(' > .blocks > .matrixblock');
999+
const selectedBlocks = blocks.filter('.sel');
1000+
return selectedBlocks.length ? selectedBlocks : blocks;
1001+
};
1002+
9991003
if (field.length) {
1000-
$copyAllJs
1004+
copyBtn.on('activate', () => {
1005+
const elementInfo = [];
1006+
getBlocks().each((i, element) => {
1007+
element = $(element);
1008+
elementInfo.push(Object.assign({
1009+
id: element.data('id'),
1010+
draftId: element.data('draftId'),
1011+
revisionId: element.data('revisionId'),
1012+
ownerId: element.data('ownerId'),
1013+
siteId: element.data('siteId'),
1014+
}, $baseInfo));
1015+
});
1016+
Craft.cp.copyElements(elementInfo);
1017+
});
10011018
} else {
10021019
setTimeout(() => {
1003-
const menu = copyAllBtn.closest('.menu').data('disclosureMenu');
1004-
menu.removeItem(copyAllBtn[0]);
1020+
const menu = copyBtn.closest('.menu').data('disclosureMenu');
1021+
menu.removeItem(copyBtn[0]);
10051022
}, 1);
10061023
}
1024+
1025+
setTimeout(() => {
1026+
const menu = copyBtn.closest('.menu').data('disclosureMenu');
1027+
menu.on('show', () => {
1028+
let blocks = getBlocks();
1029+
let copyLabel;
1030+
if (blocks.is('.sel')) {
1031+
copyLabel = Craft.t('app', 'Copy selected {type}', {
1032+
type: $type,
1033+
});
1034+
} else {
1035+
copyLabel = Craft.t('app', 'Copy all {type}', {
1036+
type: $type,
1037+
});
1038+
}
1039+
copyBtn.find('.menu-item-label').text(copyLabel);
1040+
menu.toggleItem(copyBtn[0], !!blocks.length);
1041+
});
1042+
}, 1);
10071043
})();
10081044
JS, [
10091045
$view->namespaceInputId($copyAllId),
10101046
$view->namespaceInputId($this->getInputId()),
1047+
Entry::pluralLowerDisplayName(),
10111048
]);
10121049
}
10131050

@@ -1031,22 +1068,19 @@ private function cardViewActionMenuItems(): array
10311068
])),
10321069
];
10331070

1034-
$copyAllJs = <<<JS
1035-
copyAllBtn.on('activate', () => {
1036-
Craft.cp.copyElements(field.find('> .nested-element-cards > .elements > li > .element'));
1037-
});
1038-
JS;
10391071

10401072
$view->registerJsWithVars(fn($copyAllId, $fieldId) => <<<JS
10411073
(() => {
1042-
const copyAllBtn = $('#' + $copyAllId);
1074+
const copyBtn = $('#' + $copyAllId);
10431075
const field = $('#' + $fieldId);
10441076
if (field.length) {
1045-
$copyAllJs
1077+
copyBtn.on('activate', () => {
1078+
Craft.cp.copyElements(field.find('> .nested-element-cards > .elements > li > .element'));
1079+
});
10461080
} else {
10471081
setTimeout(() => {
1048-
const menu = copyAllBtn.closest('.menu').data('disclosureMenu');
1049-
menu.removeItem(copyAllBtn[0]);
1082+
const menu = copyBtn.closest('.menu').data('disclosureMenu');
1083+
menu.removeItem(copyBtn[0]);
10501084
}, 1);
10511085
}
10521086
})();

src/translations/en/app.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@
330330
'Close' => 'Close',
331331
'Closed Issues' => 'Closed Issues',
332332
'Collapse all blocks' => 'Collapse all blocks',
333+
'Collapse selected blocks' => 'Collapse selected blocks',
333334
'Collapse' => 'Collapse',
334335
'Color hex value' => 'Color hex value',
335336
'Color picker' => 'Color picker',
@@ -380,6 +381,7 @@
380381
'Copy password reset URL…' => 'Copy password reset URL…',
381382
'Copy plugin handle' => 'Copy plugin handle',
382383
'Copy reference tag' => 'Copy reference tag',
384+
'Copy selected {type}' => 'Copy selected {type}',
383385
'Copy the URL' => 'Copy the URL',
384386
'Copy the activation URL' => 'Copy the activation URL',
385387
'Copy the impersonation URL, and open it in a new private window.' => 'Copy the impersonation URL, and open it in a new private window.',
@@ -702,6 +704,7 @@
702704
'Everything in {edition}, plus…' => 'Everything in {edition}, plus…',
703705
'Existing {type}' => 'Existing {type}',
704706
'Expand all blocks' => 'Expand all blocks',
707+
'Expand selected blocks' => 'Expand selected blocks',
705708
'Expand {heading} sources' => 'Expand {heading} sources',
706709
'Expand' => 'Expand',
707710
'Expanded' => 'Expanded',

src/web/assets/cp/CpAsset.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,15 @@ private function _registerTranslations(View $view): void
163163
'Clear',
164164
'Close Preview',
165165
'Close',
166+
'Collapse selected blocks',
166167
'Color hex value',
167168
'Color picker',
168169
'Content',
169170
'Continue',
170171
'Copied to clipboard.',
171172
'Copy URL',
172173
'Copy from',
174+
'Copy selected {type}',
173175
'Copy the URL',
174176
'Copy the reference tag',
175177
'Copy to clipboard',
@@ -219,6 +221,7 @@ private function _registerTranslations(View $view): void
219221
'Enter your password to log back in.',
220222
'Error',
221223
'Existing {type}',
224+
'Expand selected blocks',
222225
'Export Type',
223226
'Export',
224227
'Export…',

0 commit comments

Comments
 (0)