From 5b0dd8520b6012a51038ef724b99c6965829b94c Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Wed, 17 Jun 2026 15:08:08 -0500 Subject: [PATCH 1/3] WIP Initial port of Entry type edit page --- .../js/pages/settings/EntryTypesEdit.vue | 349 ++++++++++++++++++ .../Settings/EntryTypesController.php | 169 ++++----- 2 files changed, 414 insertions(+), 104 deletions(-) create mode 100644 resources/js/pages/settings/EntryTypesEdit.vue diff --git a/resources/js/pages/settings/EntryTypesEdit.vue b/resources/js/pages/settings/EntryTypesEdit.vue new file mode 100644 index 00000000000..cae9e38d987 --- /dev/null +++ b/resources/js/pages/settings/EntryTypesEdit.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/src/Http/Controllers/Settings/EntryTypesController.php b/src/Http/Controllers/Settings/EntryTypesController.php index 287c5793901..a02b41edc60 100644 --- a/src/Http/Controllers/Settings/EntryTypesController.php +++ b/src/Http/Controllers/Settings/EntryTypesController.php @@ -4,18 +4,13 @@ namespace CraftCms\Cms\Http\Controllers\Settings; -use CraftCms\Cms\Component\Contracts\Iconic; use CraftCms\Cms\Config\GeneralConfig; -use CraftCms\Cms\Cp\Html\ContentHtml; use CraftCms\Cms\Cp\Html\ElementHtml; -use CraftCms\Cms\Cp\Icons; use CraftCms\Cms\Entry\Data\EntryType; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\EntryTypes; use CraftCms\Cms\Entry\Models\EntryType as EntryTypeModel; use CraftCms\Cms\Entry\Resources\EntryTypeResource; -use CraftCms\Cms\Field\Contracts\ElementContainerFieldInterface; -use CraftCms\Cms\Field\Contracts\FieldInterface; use CraftCms\Cms\Field\Enums\TranslationMethod; use CraftCms\Cms\Field\Fields; use CraftCms\Cms\FieldLayout\FieldLayout; @@ -24,11 +19,10 @@ use CraftCms\Cms\Http\Requests\TableRequest; use CraftCms\Cms\Http\RespondsWithFlash; use CraftCms\Cms\Http\Responses\CpScreenResponse; -use CraftCms\Cms\Section\Data\Section; use CraftCms\Cms\Shared\Enums\Color; use CraftCms\Cms\Support\Arr; use CraftCms\Cms\Support\Facades\InputNamespace; -use CraftCms\Cms\Support\Html; +use CraftCms\Cms\Support\Facades\Sites; use CraftCms\Cms\Support\Str; use CraftCms\Cms\Support\Url; use CraftCms\Cms\View\HtmlStack; @@ -91,30 +85,12 @@ public function create(): CpScreenResponse { $entryType = new EntryType; - $fieldLayout = $entryType->getFieldLayout(); - - if ($entryType->hasTitleField && ! $fieldLayout->isFieldIncluded('title')) { - $fieldLayout->prependElements([new EntryTitleField]); - } - return new CpScreenResponse() ->title(t('Create a new entry type')) ->addCrumb(t('Settings'), 'settings') ->addCrumb(t('Entry Types'), 'settings/entry-types') - ->contentTemplate('settings/entry-types/_edit.twig', [ - 'entryTypeId' => null, - 'entryType' => $entryType, - 'typeName' => Entry::displayName(), - 'lowerTypeName' => Entry::lowerDisplayName(), - 'readOnly' => $this->readOnly, - ]) - ->action('entry-types/save') ->redirectUrl('settings/entry-types') - ->addAltAction(t('Save and continue editing'), [ - 'redirect' => 'settings/entry-types/{id}', - 'shortcut' => true, - 'retainScroll' => true, - ]); + ->inertiaPage('settings/EntryTypesEdit', $this->entryTypeProps($entryType, brandNew: true)); } public function edit(Request $request, ?EntryTypeModel $entryType = null): CpScreenResponse @@ -127,94 +103,79 @@ public function edit(Request $request, ?EntryTypeModel $entryType = null): CpScr abort_if(is_null($entryTypeData), 404, 'Entry type not found'); - $fieldLayout = $entryTypeData->getFieldLayout(); + return new CpScreenResponse() + ->editUrl($entryTypeData->getCpEditUrl()) + ->title(trim($entryTypeData->name) ?: t('Edit Entry Type')) + ->addCrumb(t('Settings'), 'settings') + ->addCrumb(t('Entry Types'), 'settings/entry-types') + ->redirectUrl('settings/entry-types') + ->inertiaPage('settings/EntryTypesEdit', $this->entryTypeProps($entryTypeData, brandNew: false)); + } - if ($entryTypeData->hasTitleField) { - // Ensure the Title field is present + /** + * Builds the Inertia props for the entry type edit/new screen. + */ + private function entryTypeProps(EntryType $entryType, bool $brandNew): array + { + $fieldLayout = $entryType->getFieldLayout(); + + // Normalize the Title field's presence so the round-tripped config matches what gets saved back. + if ($entryType->hasTitleField) { if (! $fieldLayout->isFieldIncluded('title')) { $fieldLayout->prependElements([new EntryTitleField]); } } else { - // Remove the title field foreach ($fieldLayout->getTabs() as $tab) { - $elements = array_filter($tab->getElements(), - fn (FieldLayoutElement $element) => ! $element instanceof EntryTitleField); + $elements = array_filter( + $tab->getElements(), + fn (FieldLayoutElement $element) => ! $element instanceof EntryTitleField, + ); $tab->setElements($elements); } } - return new CpScreenResponse() - ->editUrl($entryTypeData->getCpEditUrl()) - ->title(trim($entryTypeData->name) ?: t('Edit Entry Type')) - ->addCrumb(t('Settings'), 'settings') - ->addCrumb(t('Entry Types'), 'settings/entry-types') - ->contentTemplate('settings/entry-types/_edit.twig', [ - 'entryTypeId' => $entryTypeData->id, - 'entryType' => $entryTypeData, - 'typeName' => Entry::displayName(), - 'lowerTypeName' => Entry::lowerDisplayName(), - 'readOnly' => $this->readOnly, - ]) - ->unless( - $this->readOnly, - callback: function (CpScreenResponse $response) use ($entryTypeData) { - $response - ->action('entry-types/save') - ->redirectUrl('settings/entry-types') - ->addAltAction(t('Save and continue editing'), [ - 'redirect' => 'settings/entry-types/{id}', - 'shortcut' => true, - 'retainScroll' => true, - ]) - ->addAltAction(t('Save as a new entry type'), [ - 'params' => ['saveAsNew' => true], - 'redirect' => 'settings/entry-types/{id}', - ]) - ->addAltAction(t('Delete'), [ - 'action' => 'entry-types/delete', - 'destructive' => true, - ]) - ->metaSidebarHtml(app(ContentHtml::class)->metadataHtml([ - t('ID') => $entryTypeData->id, - t('Used by') => function () use ($entryTypeData) { - $usages = $entryTypeData->findUsages(); - if (empty($usages)) { - return Html::tag('i', t('No usages')); - } - - $labels = []; - $items = array_map(function (Section|ElementContainerFieldInterface $usage) use ( - &$labels - ) { - $icon = $usage instanceof FieldInterface && ! $usage instanceof Iconic - ? $usage::icon() - : $usage->getIcon(); - $label = $labels[] = $usage->getUiLabel(); - $labelHtml = Html::beginTag('span', [ - 'class' => ['flex', 'flex-nowrap', 'gap-s'], - ]). - Html::tag('div', Icons::svg($icon), [ - 'class' => ['cp-icon', 'small'], - ]). - Html::tag('span', Html::encode($label)). - Html::endTag('span'); - - return Html::a($labelHtml, $usage->getCpEditUrl()); - }, $entryTypeData->findUsages()); - - // sort by label - array_multisort($labels, SORT_ASC, $items); - - $items = array_map(fn ($item) => Html::li($item)->encode(false), $items); - - return Html::ul()->items(...$items)->render(); - }, - ])); - }, - default: function (CpScreenResponse $response) { - $response->noticeHtml(app(ContentHtml::class)->readOnlyNoticeHtml()); - }, - ); + // The field layout designer UI is deferred; round-trip the current config so saves preserve the layout. + $fieldLayoutConfig = [ + 'uid' => $fieldLayout->uid, + ...(array) $fieldLayout->getConfig(), + ]; + if ($fieldLayout->id) { + $fieldLayoutConfig['id'] = $fieldLayout->id; + } + $fieldLayoutConfig['type'] = Entry::class; + + $translationMethodOptions = [ + ['value' => TranslationMethod::None->value, 'label' => t('Not translatable')], + ['value' => TranslationMethod::Site->value, 'label' => t('Translate for each site')], + ['value' => TranslationMethod::SiteGroup->value, 'label' => t('Translate for each site group')], + ['value' => TranslationMethod::Language->value, 'label' => t('Translate for each language')], + ['value' => TranslationMethod::Custom->value, 'label' => t('Custom…')], + ]; + + return [ + 'brandNew' => $brandNew, + 'entryType' => [ + 'id' => $entryType->id, + 'name' => $entryType->name, + 'handle' => $entryType->handle, + 'description' => $entryType->description, + 'uiLabelFormat' => $entryType->uiLabelFormat, + 'titleTranslationMethod' => $entryType->titleTranslationMethod->value, + 'titleTranslationKeyFormat' => $entryType->titleTranslationKeyFormat, + 'titleFormat' => $entryType->titleFormat, + 'allowLineBreaksInTitles' => (bool) $entryType->allowLineBreaksInTitles, + 'showSlugField' => (bool) $entryType->showSlugField, + 'slugTranslationMethod' => $entryType->slugTranslationMethod->value, + 'slugTranslationKeyFormat' => $entryType->slugTranslationKeyFormat, + 'showStatusField' => (bool) $entryType->showStatusField, + ], + 'fieldLayoutConfig' => $fieldLayoutConfig, + 'translationMethodOptions' => $translationMethodOptions, + 'typeName' => Entry::displayName(), + 'lowerTypeName' => Entry::lowerDisplayName(), + 'isMultiSite' => Sites::isMultiSite(), + 'readOnly' => $this->readOnly, + ]; } #[Deprecated(message: 'in 6.0. Use `settings/entry-types` instead.')] From 91ebc68ae77177646f6101dd60193cdc7f7887ee Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Thu, 18 Jun 2026 15:34:20 -0500 Subject: [PATCH 2/3] First pass at the new entry type page --- .../craftcms-cp/src/components/input/input.ts | 2 + .../craftcms-cp/src/styles/form.styles.ts | 5 + .../craftcms-cp/src/styles/shared/tokens.css | 1 + packages/craftcms-cp/tailwind.css | 16 + packages/craftcms-legacy/cp/src/css/_fld.scss | 469 ---------------- .../craftcms-legacy/cp/src/css/craft.scss | 1 - .../cp/src/js/FieldLayoutDesigner.js | 51 +- .../craftcms-legacy/garnish/src/Garnish.js | 3 + resources/css/cp.css | 1 + resources/css/fld.css | 509 ++++++++++++++++++ .../composables/useFieldLayoutDesigner.ts | 52 ++ resources/js/common/types/globals.d.ts | 5 + .../js/pages/settings/EntryTypesEdit.vue | 40 +- resources/views/forms/fld/designer.blade.php | 115 ++++ resources/views/forms/fld/tab.blade.php | 33 ++ .../FieldLayoutDesigner.php | 145 ++--- src/FieldLayout/LayoutElements/BaseField.php | 9 +- .../LayoutElements/HorizontalRule.php | 2 +- src/FieldLayout/LayoutElements/LineBreak.php | 2 +- .../Settings/EntryTypesController.php | 21 +- 20 files changed, 857 insertions(+), 625 deletions(-) delete mode 100644 packages/craftcms-legacy/cp/src/css/_fld.scss create mode 100644 resources/css/fld.css create mode 100644 resources/js/common/composables/useFieldLayoutDesigner.ts create mode 100644 resources/views/forms/fld/designer.blade.php create mode 100644 resources/views/forms/fld/tab.blade.php diff --git a/packages/craftcms-cp/src/components/input/input.ts b/packages/craftcms-cp/src/components/input/input.ts index 7bafe449ff5..925e4fc50ac 100644 --- a/packages/craftcms-cp/src/components/input/input.ts +++ b/packages/craftcms-cp/src/components/input/input.ts @@ -14,6 +14,8 @@ export default class CraftInput extends LionInput { @property({reflect: true, type: Boolean}) small = false; @property({reflect: true, type: Boolean}) center = false; + @property({reflect: true, type: Boolean}) monospace = false; + override connectedCallback() { super.connectedCallback(); diff --git a/packages/craftcms-cp/src/styles/form.styles.ts b/packages/craftcms-cp/src/styles/form.styles.ts index eeba5d58567..a53d927560e 100644 --- a/packages/craftcms-cp/src/styles/form.styles.ts +++ b/packages/craftcms-cp/src/styles/form.styles.ts @@ -62,6 +62,11 @@ export const baseFieldStyles = css` export const inputStyles = css` ${baseFieldStyles} + :host([monospace]) .input-group__container { + font-family: var(--c-font-mono); + font-size: 0.9em; + } + ::slotted([slot='input']) { font: inherit; padding-block: 0; diff --git a/packages/craftcms-cp/src/styles/shared/tokens.css b/packages/craftcms-cp/src/styles/shared/tokens.css index 8338d1a75dd..f8db68dbb85 100644 --- a/packages/craftcms-cp/src/styles/shared/tokens.css +++ b/packages/craftcms-cp/src/styles/shared/tokens.css @@ -54,6 +54,7 @@ --c-spacing-2xl: calc(var(--c-spacing) * 16); --c-size-touch-target: calc(34rem / 16); + --c-size-touch-target-sm: calc(24rem / 16); --c-size-icon-xs: calc(10rem / 16); --c-size-icon-sm: calc(12rem / 16); diff --git a/packages/craftcms-cp/tailwind.css b/packages/craftcms-cp/tailwind.css index fd95de39987..66436a6c699 100644 --- a/packages/craftcms-cp/tailwind.css +++ b/packages/craftcms-cp/tailwind.css @@ -110,4 +110,20 @@ --color-border-default: var(--c-color-neutral-border-normal); --color-border-strong: var(--c-color-neutral-border-loud); --color-border-form: var(--c-form-control-border-color); + + /* ——— Fill utilities ——— */ + --color-fill-quiet: var( + --c-color-fill-quiet, + var(--c-color-neutral-fill-quiet) + ); + --color-fill-normal: var( + --c-color-fill-normal, + var(--c-color-neutral-fill-normal) + ); + --color-fill-loud: var(--c-color-fill-loud, var(--c-color-neutral-fill-loud)); + + /* ——— Text utilities ——— */ + --color-on-quiet: var(--c-color-on-quiet, var(--c-color-neutral-on-quiet)); + --color-on-normal: var(--c-color-on-normal, var(--c-color-neutral-on-normal)); + --color-on-loud: var(--c-color-on-loud, var(--c-color-neutral-on-loud)); } diff --git a/packages/craftcms-legacy/cp/src/css/_fld.scss b/packages/craftcms-legacy/cp/src/css/_fld.scss deleted file mode 100644 index 28b5b699e06..00000000000 --- a/packages/craftcms-legacy/cp/src/css/_fld.scss +++ /dev/null @@ -1,469 +0,0 @@ -@charset "UTF-8"; -@use 'sass:color'; -@use '@craftcms/sass/mixins'; - -$base: 24px; -$tabPadding: 14px; -$tabWidth: $base * 12; -$gridColor: var(--gray-100); - -@mixin workspace-bg { - background-color: var(--gray-050); - background-image: - linear-gradient(to right, $gridColor 1px, transparent 0), - linear-gradient(to bottom, $gridColor 1px, transparent 1px); - background-size: $base $base; -} - -.layoutdesigner { - container-type: inline-size; -} - -.fld-container { - display: flex; - align-items: stretch; - position: relative; - @include mixins.input-styles; - overflow: hidden; - box-shadow: none; - min-height: 500px; - - .errors > .layoutdesigner > & { - border: 1px solid var(--fg-error) !important; - } - - .fld-workspace { - flex: 1; - max-width: 100%; - border-start-start-radius: calc(var(--radius-sm) - 1px); - border-start-end-radius: 0; - border-end-end-radius: 0; - border-end-start-radius: calc(var(--radius-sm) - 1px); - padding-inline: $base 0; - padding-block: $base; - @include workspace-bg; - background-position: -1px -1px; - box-shadow: inset 0 1px 3px -1px - color.adjust(mixins.$grey200, $lightness: -10%); - - .fld-tabs { - display: flex; - align-items: flex-start; - flex-wrap: wrap; - } - } - - .fld-library { - display: none; - } -} - -.fld-library-hud { - width: $tabWidth - 1; - max-width: 100%; - - .main { - height: 100%; - padding: $tabPadding; - } -} - -.fld-new-tab-btn:active { - background-color: var(--gray-050); -} - -.fld-library { - display: flex; - flex-direction: column; - height: 100%; - - .btngroup { - margin-block-end: $tabPadding; - } - - .fld-field-library, - .fld-ui-library { - margin-block: -3px; - margin-inline: #{-$tabPadding}; - padding-block: 3px; - padding-inline: #{$tabPadding}; - flex: 1; - min-height: 0; - max-height: 550px; - overflow: auto; - } - - .fld-field-library { - .fld-field-group { - margin-block-start: $tabPadding; - - & > *:not(:first-child) { - margin-block-start: var(--s); - } - } - - .fld-field-indicators { - display: none; - } - } - - .fld-ui-library > *:not(:first-child) { - margin-block-start: var(--s); - } - - .filtered { - display: none !important; - } -} - -.layoutdesigner .fld-library, -.fld-tab .tabs .tab, -.fld-tab .fld-tabcontent { - background-color: var(--white); - box-shadow: - 0 0 0 1px color.adjust(mixins.$grey900, $alpha: -0.9), - 0 2px 5px -2px color.adjust(mixins.$grey900, $alpha: -0.8); -} - -.fld-new-tab-btn { - background: var(--white) !important; - box-shadow: - 0 0 0 1px #{color.adjust(mixins.$grey900, $alpha: -0.9)}, - 0 2px 5px -2px #{color.adjust(mixins.$grey900, $alpha: -0.8)}; -} - -.fld-tab .settings::before, -.fld-element .settings::before { - margin-block-start: -2px; - font-size: 16px; - opacity: 0.5; -} - -.fld-tab .settings:hover::before, -.fld-tab .settings.active::before, -.fld-element .settings:hover::before, -.fld-element .settings.active::before { - opacity: 1; -} - -.fld-tab { - width: $tabWidth + $base; - max-width: 100%; - padding-inline: 0 $base + 1; - padding-block: 0 $base; - box-sizing: border-box; - - .tabs { - margin-block: -10px 0; - margin-inline: -12px; - padding-block: 10px 0; - padding-inline: 12px; - overflow: hidden; - display: flex; - - .tab { - display: flex; - align-items: center; - gap: var(--xs); - max-width: calc(100% - 10px); - box-sizing: border-box; - padding-block: 8px; - padding-inline: $tabPadding; - border-radius: var(--radius-md) var(--radius-md) 0 0; - - body:not(.dragging) &.draggable { - cursor: move; - cursor: grab; - } - } - } - - .fld-tab__name { - margin-block: 0; - font-weight: var(--font-weight-regular); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .fld-tabcontent { - padding: $tabPadding; - border-start-start-radius: 0; - border-start-end-radius: var(--radius-md); - border-end-end-radius: var(--radius-md); - border-end-start-radius: var(--radius-md); - - & > .fld-element, - & > .fld-add-btn { - &:not(:first-child) { - margin-block-start: var(--s); - } - } - } - - &.fld-insertion { - .tabs .tab, - .fld-tabcontent { - margin: -2px; - border: 2px dashed var(--border-hairline); - box-shadow: none; - @include workspace-bg; - } - - .tabs .tab { - background-position: -1px -1px; - } - - .fld-tabcontent { - background-position: -1px -13px; - } - } -} - -.fld-tab-caboose { - min-height: 24px; -} - -.fld-element { - position: relative; - display: flex; - align-items: center; - padding: var(--s); - gap: var(--s); - box-shadow: inset 0 0 0 1px var(--border-hairline); - border-radius: var(--radius-md); - background-color: var(--white); - - body:not(.dragging) & { - cursor: move; - cursor: grab; - } - - &.fld-insertion { - box-sizing: border-box; - border: 2px dashed var(--border-hairline); - border-radius: var(--radius-md); - background: none; - box-shadow: none; - } - - &.draghelper { - @include mixins.shadow; - } - - &.fld-field { - color: var(--fg-subtle); - background-color: var(--gray-100); - - &:not(.draghelper, :focus) { - box-shadow: none; - } - - .field-name { - display: flex; - flex-direction: column; - gap: var(--xs); - } - } - - .fld-element-icon { - text-align: center; - - &, - svg { - width: 16px; - height: 16px; - } - - svg { - @include mixins.svg-mask(var(--ui-control-color)); - } - } - - .field-name { - flex: 1; - overflow: hidden; - - .fld-element-label, - .fld-attribute { - flex: 1; - display: flex; - align-items: center; - gap: var(--xs); - } - - .fld-element-label h4, - .fld-attribute .smalltext { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .fld-element-label h4 { - font-weight: normal; - color: var(--text-color); - margin: 0; - } - } -} - -.fld-hr, -.fld-br { - position: relative; - flex: 1; - display: flex; - justify-content: center; - - &::before { - position: absolute; - display: block; - inset-block-start: calc(50% - 2px); - inset-inline-start: 0; - width: 100%; - height: 4px; - content: ''; - font-size: 0; - border-radius: 2px; - } - - .smalltext { - position: relative; - display: flex; - justify-content: center; - align-items: center; - background-color: var(--gray-100); - border-radius: var(--radius-lg); - padding-block: 0; - padding-inline: var(--s); - height: var(--touch-target-size); - } -} - -.fld-hr::before { - background-color: var(--gray-100); -} - -.fld-br::before { - background-image: repeating-linear-gradient( - to right, - var(--gray-100), - var(--gray-100) calc(100% / 19), - transparent calc(100% / 19), - transparent calc(100% / 9.5), - var(--gray-100) calc(100% / 9.5) - ); -} - -.fld-element-settings-body { - flex: 1; - margin-block: -24px 0; - margin-inline: var(--neg-padding); - padding-block: 24px; - padding-inline: var(--padding); - overflow: hidden auto; - position: relative; - - hr { - margin-inline: -24px; - } -} - -.fld-element-settings-footer { - position: relative; - display: flex; - flex-direction: row; - margin-block: 0 -24px; - margin-inline: var(--neg-padding); - padding-block: 5px; - padding-inline: var(--padding); - @include mixins.pane; - background-color: var(--gray-050); - z-index: 3; - - & > .ee-site-select { - flex: 1; - } - - & > .btn { - margin-inline-start: 5px; - } - - & > .spinner { - margin-inline: 0 var(--neg-padding); - margin-block: 0; - } -} - -// thumbnail management -.thumb-management { - margin-block: var(--xl); - - .field:first-child { - margin-inline-end: var(--xl); - } -} - -// card view designer -.card-view-designer { - container: cvd / inline-size; -} - -.cvd-container { - display: grid; - position: relative; - overflow: hidden; - box-shadow: none; - gap: var(--xl); -} - -@container cvd (width > 37.5rem) { - .cvd-container { - grid-template-columns: 1fr 2fr; - } -} - -.cvd-library { - .draggable { - display: flex; - } -} - -.cvd-preview-container { - padding: var(--xl); - border: 1px solid #{color.adjust(mixins.$inputColor, $alpha: -0.75)} !important; - border-radius: var(--radius-sm); - display: grid; - height: 100%; - align-items: center; -} - -.cvd-preview { - &:not(.loading) .spinner { - display: none; - } -} - -.cvd-thumbnail { - --icon-size: 2rem; - --icon-color: var(--gray-300); - width: 100%; - aspect-ratio: 4/3; - display: flex; - justify-content: center; - align-items: center; - background-color: var(--gray-150); - border-radius: var(--radius-md); -} - -.card-placeholder { - display: inline-block; - border: 1px dashed mixins.$grey300; - border-radius: var(--radius-sm); - padding-block: 0.1em; - padding-inline: 0.5em; -} - -.field.cvd-field { - margin-block-start: 0.2em !important; - margin-inline-start: 0.5em; -} diff --git a/packages/craftcms-legacy/cp/src/css/craft.scss b/packages/craftcms-legacy/cp/src/css/craft.scss index ff419da226b..30fb155eeac 100644 --- a/packages/craftcms-legacy/cp/src/css/craft.scss +++ b/packages/craftcms-legacy/cp/src/css/craft.scss @@ -17,7 +17,6 @@ @import 'craft-tooltip'; @import 'preview'; @import 'entry-type-select'; - @import 'fld'; @import 'grouped-entry-type-select'; @import 'image_editor'; @import 'shame'; diff --git a/packages/craftcms-legacy/cp/src/js/FieldLayoutDesigner.js b/packages/craftcms-legacy/cp/src/js/FieldLayoutDesigner.js index cc9a7f4f81e..7f13ae4aa25 100644 --- a/packages/craftcms-legacy/cp/src/js/FieldLayoutDesigner.js +++ b/packages/craftcms-legacy/cp/src/js/FieldLayoutDesigner.js @@ -46,7 +46,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( this.$innerContainer = this.$container.children('.fld-container'); const $workspace = this.$innerContainer.children('.fld-workspace'); this.$tabContainer = $workspace.children('.fld-tabs'); - this.$newTabBtn = $workspace.children('.fld-new-tab-btn'); + this.$newTabBtn = $workspace.find('[command="--add-tab"]'); this.$libraryContainer = this.$innerContainer.children('.fld-library'); this.$fieldLibrary = this.$selectedLibrary = @@ -245,9 +245,16 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend(
- +
`); @@ -500,7 +507,7 @@ Craft.FieldLayoutDesigner.Tab = Garnish.Base.extend({ this.designer.tabGrid.refreshCols(true); }); - this.$addBtn = $tabContent.children('.fld-add-btn'); + this.$addBtn = $tabContent.find('[command="--add-field"]'); const hud = new Garnish.HUD(this.$addBtn, { hudClass: 'hud fld-library-hud cp-legacy', @@ -523,7 +530,9 @@ Craft.FieldLayoutDesigner.Tab = Garnish.Base.extend({ hud.show(); }); - const $elements = $tabContent.children().not(this.$addBtn); + // Match the drag system's item selector (ElementDrag.findItems) so every + // draggable element gets initialized, regardless of how it's nested. + const $elements = $tabContent.find('.fld-element'); for (let i = 0; i < $elements.length; i++) { this.initElement($($elements[i])); @@ -533,16 +542,23 @@ Craft.FieldLayoutDesigner.Tab = Garnish.Base.extend({ createMenu: function () { const $tab = this.$container.find('.tabs .tab'); const menuId = `actionmenu${Math.floor(Math.random() * 1000000)}`; - const $btn = $('