discourse/app/assets/javascripts/float-kit/addon/components/d-menu.gjs
Jan Cernik 8dd883d4e5
DEV: Refactor topic admin menu to use <DMenu> (#26678)
* DEV: Refactor topic admin menu to use `<DMenu>`

This PR also introduces a new plugin API to add buttons to the topic admin menu

```javascript
api.addTopicAdminMenuButton((topic) => {
  return {
    action: () => {
      alert('Sunrise!');
    },
    icon: 'sun',
    className: 'sunrise-button',
    label: 'actions.rise',
  };
});
```

The plugins that needed to be updated are:

- [discourse-zoom](https://github.com/discourse/discourse-zoom/pull/73)
- [discourse-salesforce](https://github.com/discourse/discourse-salesforce/pull/74)
- [discourse-topic-noindex](https://github.com/discourse/discourse-topic-noindex/pull/11)
2024-04-29 12:44:38 -03:00

153 lines
4.3 KiB
Plaintext

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/application";
import { concat } from "@ember/helper";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { modifier } from "ember-modifier";
import { and } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import concatClass from "discourse/helpers/concat-class";
import { isTesting } from "discourse-common/config/environment";
import DFloatBody from "float-kit/components/d-float-body";
import { MENU } from "float-kit/lib/constants";
import DMenuInstance from "float-kit/lib/d-menu-instance";
export default class DMenu extends Component {
@service menu;
@service site;
@tracked menuInstance = null;
registerTrigger = modifier((element, [properties]) => {
const options = {
...properties,
...{
autoUpdate: true,
listeners: true,
beforeTrigger: () => {
this.menu.close();
},
},
};
const instance = new DMenuInstance(getOwner(this), element, options);
this.menuInstance = instance;
this.options.onRegisterApi?.(this.menuInstance);
return () => {
instance.destroy();
if (this.isDestroying) {
this.menuInstance = null;
}
};
});
get menuId() {
return `d-menu-${this.menuInstance.id}`;
}
get options() {
return this.menuInstance?.options ?? {};
}
get componentArgs() {
return {
close: this.menuInstance.close,
data: this.options.data,
};
}
@action
allowedProperties() {
const properties = {};
Object.keys(MENU.options).forEach((key) => {
const value = MENU.options[key];
properties[key] = this.args[key] ?? value;
});
return properties;
}
<template>
<DButton
class={{concatClass
"fk-d-menu__trigger"
(if this.menuInstance.expanded "-expanded")
(concat this.options.identifier "-trigger")
@triggerClass
}}
id={{this.menuInstance.id}}
data-identifier={{this.options.identifier}}
data-trigger
@icon={{@icon}}
@translatedAriaLabel={{@ariaLabel}}
@translatedLabel={{@label}}
@translatedTitle={{@title}}
@disabled={{@disabled}}
aria-expanded={{if this.menuInstance.expanded "true" "false"}}
{{this.registerTrigger (this.allowedProperties)}}
...attributes
>
{{#if (has-block "trigger")}}
{{yield this.componentArgs to="trigger"}}
{{/if}}
</DButton>
{{#if this.menuInstance.expanded}}
{{#if (and this.site.mobileView this.options.modalForMobile)}}
<DModal
@closeModal={{this.menuInstance.close}}
@hideHeader={{true}}
class={{concatClass
"fk-d-menu-modal"
(concat this.options.identifier "-content")
}}
@inline={{(isTesting)}}
>
{{#if (has-block)}}
{{yield this.componentArgs}}
{{else if (has-block "content")}}
{{yield this.componentArgs to="content"}}
{{else if this.options.component}}
<this.options.component
@data={{this.options.data}}
@close={{this.menuInstance.close}}
/>
{{else if this.options.content}}
{{this.options.content}}
{{/if}}
</DModal>
{{else}}
<DFloatBody
@instance={{this.menuInstance}}
@trapTab={{this.options.trapTab}}
@mainClass={{concatClass
"fk-d-menu"
(concat this.options.identifier "-content")
}}
@innerClass="fk-d-menu__inner-content"
@role="dialog"
@inline={{this.options.inline}}
@portalOutletElement={{this.menu.portalOutletElement}}
>
{{#if (has-block)}}
{{yield this.componentArgs}}
{{else if (has-block "content")}}
{{yield this.componentArgs to="content"}}
{{else if this.options.component}}
<this.options.component
@data={{this.options.data}}
@close={{this.menuInstance.close}}
/>
{{else if this.options.content}}
{{this.options.content}}
{{/if}}
</DFloatBody>
{{/if}}
{{/if}}
</template>
}