diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index f0f85da5651..204112b0db6 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -847,10 +847,16 @@ Craft 6 now uses [Laravel's authorization system](https://laravel.com/docs/12.x/ - The legacy element index JavaScript is now provided by the Yii2 adapter: `Craft.createElementIndex()`, `Craft.registerElementIndexClass()`, `Craft.BaseElementIndex`, `Craft.BaseElementIndexView` and its subclasses, `Craft.AssetIndex`, `Craft.CategoryIndex`, `Craft.EntryIndex`, `Craft.UserIndex`, `Craft.Structure`, `Craft.StructureDrag`, `Craft.ProgressBar`, `Craft.ElementTableSorter`, `Craft.IntervalManager`, `Craft.PromptHandler`, and `Craft.openCustomizeSourcesModal()`. - `Craft.AdminTable` and `Craft.DataTableSorter` are now provided by the Yii2 adapter. - `Craft.ui` and `Craft.EditableTable` are now provided by the Yii2 adapter. +- `Craft.CpScreenSlideout`, `Craft.ElementActionTrigger`, `Craft.Tabs`, and `Craft.createElementEditor()` are now provided by the Yii2 adapter. `Craft.Tabs`’ `selectTab` and `deselectTab` events now include the tab element as `tab`. +- The control panel no longer loads jQuery, Garnish, jQuery Touch Events, or Velocity. The Yii2 adapter still loads them for plugins, along with `Craft.js`. Asset bundles that use jQuery should depend on `craft\web\assets\jquery\JqueryAsset` rather than relying on `craft\web\assets\cp\CpAsset` to load it. +- Server-rendered menus (the `disclosureMenu()` Twig function, screen action and context menus, and breadcrumb menus) now render as ``. With the Yii2 adapter installed, they keep the legacy disclosure menu markup. Without it, items’ `liAttributes` and groups’ `headingTag`, `headingAttributes`, `listAttributes`, and `padded` settings no longer apply, and menus with `withButton: false` get a default button unless an `invoker` is passed. +- Element select inputs now boot through ``, ``, and `` rather than inline JavaScript. `Craft.BaseElementSelectInput`, `Craft.EntrySelectInput`, and `Craft.AssetSelectInput` are now provided by the Yii2 adapter, and a relation field’s custom `$inputJsClass` is booted by name via the element’s `input-class` attribute. +- `Craft.Slideout`, `Craft.Grid`, `Craft.PreviewFileModal`, `Craft.ElementDeletionManager`, and the `Craft.charts` classes now store plain elements (`container`, `items`, etc.) instead of jQuery collections, and no longer have `$container`, `$items`, `$outerContainer`, `$shade`, `$chart`, or other `$`-prefixed properties. `Craft.Grid` reads its items’ column spans from their `data-colspan` attributes rather than jQuery data. - `Craft.ElementEditor` and `Craft.ElementEditorSlideout` are now provided by the Yii2 adapter. Core’s element editors, slideouts included, all use the Vue editor. - `Craft.CP` and `Craft.cp` are now provided by the Yii2 adapter. Control panel messages, the element clipboard and screen reader announcements no longer use them. `showNotice()`, `showSuccess()`, `showError()`, `copyElements()` and `announce()` are exported from `@craftcms/cp`. - The legacy `Craft` helpers (`Craft.t()`, `Craft.sendActionRequest()`, `Craft.getUrl()`, `Craft.initUiElements()` and the rest of `Craft.js`), its jQuery plugins (`$.fn.disclosureMenu()`, `$.fn.menubtn()`, `$.fn.formsubmit()`…) and `Craft.queue` are now provided by the Yii2 adapter. Core uses `@craftcms/cp` and `@craftcms/ui` modules instead. - Removed the `Craft.FocalPoint` and `Craft.Updater` control panel JavaScript classes. The image preview and the updater now start themselves from their markup. +- `craft\web\assets\focalpoint\FocalPointAsset` now does nothing. - Added `AuthChallengeForm`, `AssetSelectorController`, `ElementSelectorController`, `Uploads`, `createElementSelectorModal`, `highlighter`, and `registerElementSelectorController` to the `@craftcms/cp` import-map module. Control panel plugin code should import them from there; they’re no longer `Craft.*` globals, except `Craft.createElementSelectorModal` and `Craft.highlighter`, which the Yii2 adapter still provides. - Removed the classic (pre-3.2) `Craft.LivePreview` control panel JavaScript class, which Craft hasn’t used since 3.2. ([#19964](https://github.com/craftcms/cms/pull/19964)) - Deprecated the `Craft.LightSwitch`, `Craft.InfoIcon`, `Craft.ColorInput`, `Craft.PasswordInput`, `Craft.IconPicker`, `Craft.SlidePicker`, `Craft.SlideRuleInput`, and `Craft.Tooltip` control panel JavaScript classes, along with the `.infoicon` jQuery plugin. The corresponding `@craftcms/ui` web components should be used instead. diff --git a/packages/craftcms-legacy/dashboard/src/Dashboard.js b/packages/craftcms-legacy/dashboard/src/Dashboard.js index 8ecf330e843..bdfcd4bc0ac 100644 --- a/packages/craftcms-legacy/dashboard/src/Dashboard.js +++ b/packages/craftcms-legacy/dashboard/src/Dashboard.js @@ -199,10 +199,10 @@ import './dashboard.scss'; // Append the new widget after the last one // (can't simply append it to the grid container, since that will place it after the resize listener object) - if (this.grid.$items.length) { - $gridItem.insertAfter(this.grid.$items.last()); + if (this.grid.items.length) { + $gridItem.insertAfter(this.grid.items[this.grid.items.length - 1]); } else { - $gridItem.prependTo(this.grid.$container); + $gridItem.prependTo(this.grid.container); } this.grid.addItems($gridItem); diff --git a/packages/craftcms-ui/src/utilities/dom.ts b/packages/craftcms-ui/src/utilities/dom.ts index 970b348b2ca..50634d555c6 100644 --- a/packages/craftcms-ui/src/utilities/dom.ts +++ b/packages/craftcms-ui/src/utilities/dom.ts @@ -89,6 +89,7 @@ export async function appendElementHtml( } else { // Just inserted into `parent`, so it's a child node. appended.push(node as ChildNode); + await runNestedScripts(node, rejectOnError); } if (loaded) { @@ -104,6 +105,31 @@ export async function appendElementHtml( } } +/** + * Scripts nested in a wrapper clone as already started, so they're inert once + * the wrapper is inserted. Recreate them one at a time, waiting for each + * external script to load, so they run in order, as jQuery's `.html()` did. + */ +async function runNestedScripts( + node: Node, + rejectOnError: boolean +): Promise { + if (!(node instanceof Element)) { + return; + } + + for (const inert of node.querySelectorAll('script')) { + const script = cloneNode(inert) as HTMLScriptElement; + const loaded = script.src ? waitForScript(script, script.src) : null; + + inert.replaceWith(script); + + if (loaded) { + await awaitAsset(loaded, rejectOnError); + } + } +} + function cloneNode(source: Node): Node { if (!(source instanceof HTMLScriptElement)) { return source.cloneNode(true); diff --git a/resources/js/common/craft/chip-actions.test.ts b/resources/js/common/craft/chip-actions.test.ts index a54aea92776..b4f623a33d0 100644 --- a/resources/js/common/craft/chip-actions.test.ts +++ b/resources/js/common/craft/chip-actions.test.ts @@ -67,3 +67,12 @@ it('puts prepended actions ahead of the existing ones', async () => { const menu = el.querySelector('craft-action-menu')!; await vi.waitFor(() => expect(itemLabels(menu)).toEqual(['Replace', 'Edit'])); }); + +it('leaves a chip without an action container alone when the legacy menu plugin isn’t loaded', () => { + const el = document.createElement('div'); + el.className = 'chip'; + document.body.append(el); + + expect(() => addActionsToChip(el, [{label: 'Replace'}])).not.toThrow(); + expect(el.children).toHaveLength(0); +}); diff --git a/resources/js/common/craft/chip-actions.ts b/resources/js/common/craft/chip-actions.ts index f04e51cdb97..94e3cde385d 100644 --- a/resources/js/common/craft/chip-actions.ts +++ b/resources/js/common/craft/chip-actions.ts @@ -2,8 +2,6 @@ import {t} from '@craftcms/ui/utilities/translate'; import {setElementAttributes} from './attributes'; import {initLegacyUi} from './legacy-ui'; -declare const $: any; - /** An action in the shape the legacy `disclosureMenu()` plugin accepts. */ export interface ChipAction { label?: string; @@ -172,9 +170,16 @@ function addActionsToDisclosureMenu( actions: ChipAction[], prepend: boolean ): void { - let $trigger = $(trigger); + // The legacy menu plugin comes with the Yii2 adapter + // oxlint-disable-next-line typescript/no-explicit-any + const $ = (window as {jQuery?: any}).jQuery; + if (typeof $?.fn.disclosureMenu !== 'function') { + return; + } + + let triggerElement = trigger; - if (!trigger) { + if (!triggerElement) { console.warn( 'A chip was given actions without an action container, so a deprecated ' + 'disclosure menu was built for it. Render the chip with a ' + @@ -185,34 +190,31 @@ function addActionsToDisclosureMenu( const menuId = `actions-${Math.floor(Math.random() * 1000000)}`; const labelId = `${menuId}-label`; - const $label = $('