Skip to content

Commit b2b1cb1

Browse files
authored
Merge pull request #16989 from craftcms/feature/cms-1382-default-placement-relational-field-setting
Default Placement relational field setting
2 parents 693d5fb + 7fee2c4 commit b2b1cb1

File tree

11 files changed

+54
-3
lines changed

11 files changed

+54
-3
lines changed

CHANGELOG-WIP.md

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- “Template” field layout UI elements are now re-rendered on each autosave. ([#16837](https://github.com/craftcms/cms/discussions/16837))
3737
- Added the “SMS” link type for Link fields. ([#16850](https://github.com/craftcms/cms/discussions/16850))
3838
- Added the “Allow custom URL schemes” setting for Link fields with the “URL” link type enabled. ([#16850](https://github.com/craftcms/cms/discussions/16850))
39+
- Relational fields now have “Default [Type] Placement” settings, which control whether newly-related elements are placed before or after existing relations. ([#16989](https://github.com/craftcms/cms/pull/16989))
3940
- Sections’ “Max Authors” settings are now optional, allowing unlimited authors when blank. ([#16898](https://github.com/craftcms/cms/pull/16898))
4041
- Sections’ “Max Authors” settings can now be set to `0`, preving the “Author” field from being displayed on Edit Entry screens. ([#16898](https://github.com/craftcms/cms/pull/16898))
4142
- The email settings page now shows a “Test” button when `allowAdminChanges` is disabled. ([#16508](https://github.com/craftcms/cms/discussions/16508))

src/fields/BaseRelationField.php

+16
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ abstract class BaseRelationField extends Field implements
7474
*/
7575
public const EVENT_DEFINE_SELECTION_CRITERIA = 'defineSelectionCriteria';
7676

77+
/** @since 5.7.0 */
78+
public const DEFAULT_PLACEMENT_BEGINNING = 'beginning';
79+
/** @since 5.7.0 */
80+
public const DEFAULT_PLACEMENT_END = 'end';
81+
7782
private static bool $validatingRelatedElements = false;
7883

7984
/**
@@ -237,6 +242,13 @@ public static function existsQueryCondition(self $field, bool $enabledOnly = tru
237242
*/
238243
public ?int $branchLimit = null;
239244

245+
/**
246+
* @var string Default placement
247+
* @phpstan-var self::DEFAULT_PLACEMENT_*
248+
* @since 5.7.0
249+
*/
250+
public string $defaultPlacement = self::DEFAULT_PLACEMENT_END;
251+
240252
/**
241253
* @var string|null The view mode
242254
*/
@@ -452,6 +464,7 @@ public function settingsAttributes(): array
452464
$attributes[] = 'sources';
453465
$attributes[] = 'targetSiteId';
454466
$attributes[] = 'validateRelatedElements';
467+
$attributes[] = 'defaultPlacement';
455468
$attributes[] = 'viewMode';
456469
$attributes[] = 'showCardsInGrid';
457470
$attributes[] = 'allowSelfRelations';
@@ -501,6 +514,7 @@ public function getSettingsHtml(): ?string
501514
$view->namespaceInputId('branch-limit-field'),
502515
$view->namespaceInputId('min-relations-field'),
503516
$view->namespaceInputId('max-relations-field'),
517+
$view->namespaceInputId('default-placement-field'),
504518
$view->namespaceInputId('viewMode-field'),
505519
],
506520
]);
@@ -1377,6 +1391,7 @@ protected function settingsTemplateVariables(): array
13771391

13781392
return [
13791393
'field' => $this,
1394+
'upperElementType' => $elementType::displayName(),
13801395
'elementType' => $elementType::lowerDisplayName(),
13811396
'pluralElementType' => $elementType::pluralLowerDisplayName(),
13821397
'selectionCondition' => $selectionConditionHtml ?? null,
@@ -1455,6 +1470,7 @@ protected function inputTemplateVariables(array|ElementQueryInterface $value = n
14551470
'sourceElementId' => !empty($element->id) ? $element->id : null,
14561471
'disabledElementIds' => $disabledElementIds,
14571472
'limit' => $this->allowLimit ? $this->maxRelations : null,
1473+
'defaultPlacement' => $this->defaultPlacement,
14581474
'viewMode' => $this->viewMode(),
14591475
'showCardsInGrid' => $this->showCardsInGrid,
14601476
'selectionLabel' => $this->selectionLabel ? Craft::t('site', $this->selectionLabel) : static::defaultSelectionLabel(),

src/templates/_components/fieldtypes/Assets/settings.twig

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
}) }}
162162

163163
{{ block('limitFields') }}
164+
{{ block('defaultPlacementField') }}
164165
{{ block('viewModeField') }}
165166
{{ block('selectionLabelField') }}
166167
{{ block('validateRelatedElementsField') }}

src/templates/_components/fieldtypes/Categories/settings.twig

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
}) }}
1919
{% endblock %}
2020

21+
{{ block('defaultPlacementField') }}
2122
{{ block('viewModeField') }}
2223
{{ block('selectionLabelField') }}
2324
{{ block('validateRelatedElementsField') }}

src/templates/_components/fieldtypes/elementfieldsettings.twig

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
{% set sourceOptions = field.getSourceOptions() %}
44
{% set elementType = elementType ?? pluralElementType %}
5+
{% set upperElementType = upperElementType ?? elementType|capitalize %}
6+
57
{% block fieldSettings %}
68
{% block sourcesField %}
79
{% if sourceOptions %}
@@ -91,6 +93,22 @@
9193
}) }}
9294
{% endblock %}
9395

96+
{% block defaultPlacementField %}
97+
{{ forms.selectField({
98+
label: 'Default {type} Placement'|t('app', {type: upperElementType}),
99+
instructions: 'Where new {type} should be placed by default in the field.'|t('app', {type: pluralElementType}),
100+
id: 'default-placement',
101+
name: 'defaultPlacement',
102+
options: [
103+
{label: 'Before other {type}'|t('app', {type: pluralElementType}), value: 'beginning'},
104+
{label: 'After other {type}'|t('app', {type: pluralElementType}), value: 'end'},
105+
],
106+
value: field.defaultPlacement,
107+
errors: field.getErrors('defaultPlacement'),
108+
data: {'error-key': 'defaultPlacement'},
109+
})}}
110+
{% endblock %}
111+
94112
{% block viewModeField %}
95113
{{ field.getViewModeFieldHtml()|raw }}
96114

src/templates/_includes/forms/elementSelect.twig

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
{% set criteria = criteria ?? null -%}
1212
{% set sourceElementId = sourceElementId ?? null -%}
1313
{% set storageKey = storageKey ?? null -%}
14+
{% set defaultPlacement = defaultPlacement ?? 'end' %}
1415
{% set viewMode = viewMode ?? 'list' %}
1516
{% set prevalidate = prevalidate ?? false %}
1617
{% set fieldId = fieldId ?? null %}
@@ -110,6 +111,7 @@
110111
branchLimit: branchLimit ?? null,
111112
sourceElementId: sourceElementId,
112113
disabledElementIds: disabledElementIds ?? null,
114+
defaultPlacement,
113115
viewMode: viewMode,
114116
single: single,
115117
limit: limit,

src/translations/en/app.php

+1
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,7 @@
19991999
'Where assets should be stored on the filesystem.' => 'Where assets should be stored on the filesystem.',
20002000
'Where assets should be stored when they are uploaded directly to the field.' => 'Where assets should be stored when they are uploaded directly to the field.',
20012001
'Where do you want to store user photos? Note that the subfolder path can contain variables like <code>{username}</code>.' => 'Where do you want to store user photos? Note that the subfolder path can contain variables like <code>{username}</code>.',
2002+
'Where new {type} should be placed by default in the field.' => 'Where new {type} should be placed by default in the field.',
20022003
'Where new {type} should be placed by default in the structure.' => 'Where new {type} should be placed by default in the structure.',
20032004
'Where transforms should be stored on the filesystem.' => 'Where transforms should be stored on the filesystem.',
20042005
'Whether authors should be able to choose which time zone the time is in.' => 'Whether authors should be able to choose which time zone the time is in.',

src/web/assets/cp/dist/cp.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/web/assets/cp/dist/cp.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/web/assets/cp/src/js/BaseElementSelectInput.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,12 @@ Craft.BaseElementSelectInput = Garnish.Base.extend(
987987
},
988988

989989
appendElement: function ($element) {
990-
$('<li/>').append($element).appendTo(this.$elementsContainer);
990+
const $li = $('<li/>').append($element);
991+
if (this.settings.defaultPlacement === 'beginning') {
992+
$li.prependTo(this.$elementsContainer);
993+
} else {
994+
$li.appendTo(this.$elementsContainer);
995+
}
991996
},
992997

993998
animateElementIntoPlace: async function ($modalElement, $inputElement) {
@@ -1124,6 +1129,7 @@ Craft.BaseElementSelectInput = Garnish.Base.extend(
11241129
maintainHierarchy: false,
11251130
branchLimit: null,
11261131
limit: null,
1132+
defaultPlacement: 'end',
11271133
showSiteMenu: false,
11281134
modalStorageKey: null,
11291135
modalSettings: {},

src/web/assets/cp/src/js/ElementFieldSettings.js

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Craft.ElementFieldSettings = Garnish.Base.extend({
99
$branchLimitField: null,
1010
$maxRelationsField: null,
1111
$minRelationsField: null,
12+
$defaultPlacementField: null,
1213
$viewModeField: null,
1314

1415
init: function (
@@ -18,6 +19,7 @@ Craft.ElementFieldSettings = Garnish.Base.extend({
1819
branchLimitFieldId,
1920
minRelationsFieldId,
2021
maxRelationsFieldId,
22+
defaultPlacementFieldId,
2123
viewModeFieldId
2224
) {
2325
this.allowMultipleSources = allowMultipleSources;
@@ -30,6 +32,7 @@ Craft.ElementFieldSettings = Garnish.Base.extend({
3032
this.$branchLimitField = $(`#${branchLimitFieldId}`);
3133
this.$minRelationsField = $(`#${minRelationsFieldId}`);
3234
this.$maxRelationsField = $(`#${maxRelationsFieldId}`);
35+
this.$defaultPlacementField = $(`#${defaultPlacementFieldId}`);
3336
this.$viewModeField = $(`#${viewModeFieldId}`);
3437

3538
this.updateLimitFields();
@@ -67,11 +70,13 @@ Craft.ElementFieldSettings = Garnish.Base.extend({
6770
this.$minRelationsField.addClass('hidden');
6871
this.$maxRelationsField.addClass('hidden');
6972
this.$branchLimitField.removeClass('hidden');
73+
this.$defaultPlacementField.addClass('hidden');
7074
this.$viewModeField.addClass('hidden');
7175
} else {
7276
this.$branchLimitField.addClass('hidden');
7377
this.$minRelationsField.removeClass('hidden');
7478
this.$maxRelationsField.removeClass('hidden');
79+
this.$defaultPlacementField.removeClass('hidden');
7580
this.$viewModeField.removeClass('hidden');
7681
}
7782
},

0 commit comments

Comments
 (0)