mirror of
https://github.com/discourse/discourse.git
synced 2024-12-04 08:33:40 +08:00
abcdd8d367
This PR introduces three new UI elements to Discourse codebase through an addon called "FloatKit": - menu - tooltip - toast Simple cases can be express with an API similar to DButton: ```hbs <DTooltip @label={{i18n "foo.bar"}} @icon="check" @content="Something" /> ``` More complex cases can use blocks: ```hbs <DTooltip> <:trigger> {{d-icon "check"}} <span>{{i18n "foo.bar"}}</span> </:trigger> <:content> Something </:content> </DTooltip> ``` You can manually show a tooltip using the `tooltip` service: ```javascript const tooltipInstance = await this.tooltip.show( document.querySelector(".my-span"), options ) // and later manually close or destroy it tooltipInstance.close(); tooltipInstance.destroy(); // you can also just close any open tooltip through the service this.tooltip.close(); ``` The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service: ```javascript const tooltipInstance = this.tooltip.register( document.querySelector(".my-span"), options ) // when done you can destroy the instance to remove the listeners tooltipInstance.destroy(); ``` Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args: ```javascript const tooltipInstance = await this.tooltip.show( document.querySelector(".my-span"), { component: MyComponent, data: { foo: 1 } } ) ``` Menus are very similar to tooltips and provide the same kind of APIs: ```hbs <DMenu @icon="plus" @label={{i18n "foo.bar"}}> <ul> <li>Foo</li> <li>Bat</li> <li>Baz</li> </ul> </DMenu> ``` They also support blocks: ```hbs <DMenu> <:trigger> {{d-icon "plus"}} <span>{{i18n "foo.bar"}}</span> </:trigger> <:content> <ul> <li>Foo</li> <li>Bat</li> <li>Baz</li> </ul> </:content> </DMenu> ``` You can manually show a menu using the `menu` service: ```javascript const menuInstance = await this.menu.show( document.querySelector(".my-span"), options ) // and later manually close or destroy it menuInstance.close(); menuInstance.destroy(); // you can also just close any open tooltip through the service this.menu.close(); ``` The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service: ```javascript const menuInstance = this.menu.register( document.querySelector(".my-span"), options ) // when done you can destroy the instance to remove the listeners menuInstance.destroy(); ``` Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args: ```javascript const menuInstance = await this.menu.show( document.querySelector(".my-span"), { component: MyComponent, data: { foo: 1 } } ) ``` Interacting with toasts is made only through the `toasts` service. A default component is provided (DDefaultToast) and can be used through dedicated service methods: - this.toasts.success({ ... }); - this.toasts.warning({ ... }); - this.toasts.info({ ... }); - this.toasts.error({ ... }); - this.toasts.default({ ... }); ```javascript this.toasts.success({ data: { title: "Foo", message: "Bar", actions: [ { label: "Ok", class: "btn-primary", action: (componentArgs) => { // eslint-disable-next-line no-alert alert("Closing toast:" + componentArgs.data.title); componentArgs.close(); }, } ] }, }); ``` You can also provide your own component: ```javascript this.toasts.show(MyComponent, { autoClose: false, class: "foo", data: { baz: 1 }, }) ``` Co-authored-by: Martin Brennan <mjrbrennan@gmail.com> Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com> Co-authored-by: David Taylor <david@taylorhq.com> Co-authored-by: Jarek Radosz <jradosz@gmail.com>
114 lines
3.1 KiB
JavaScript
114 lines
3.1 KiB
JavaScript
import Service from "@ember/service";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
|
import { action } from "@ember/object";
|
|
import DDefaultToast from "float-kit/components/d-default-toast";
|
|
import DToastInstance from "float-kit/lib/d-toast-instance";
|
|
import { getOwner } from "discourse-common/lib/get-owner";
|
|
|
|
export default class Toasts extends Service {
|
|
@tracked activeToasts = new TrackedArray();
|
|
|
|
/**
|
|
* Render a toast
|
|
*
|
|
* @param {Object} [options] - options passed to the toast component as `@toast` argument
|
|
* @param {String} [options.duration] - The duration (ms) of the toast, will be closed after this time
|
|
* @param {ComponentClass} [options.component] - A component to render, will use `DDefaultToast` if not provided
|
|
* @param {String} [options.class] - A class added to the d-toast element
|
|
* @param {Object} [options.data] - An object which will be passed as the `@data` argument to the component
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
show(options = {}) {
|
|
const instance = new DToastInstance(getOwner(this), options);
|
|
this.activeToasts.push(instance);
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* Render a DDefaultToast toast with the default theme
|
|
*
|
|
* @param {Object} [options] - @see show
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
default(options = {}) {
|
|
options.data.theme = "default";
|
|
|
|
return this.show({ ...options, component: DDefaultToast });
|
|
}
|
|
|
|
/**
|
|
* Render a DDefaultToast toast with the success theme
|
|
*
|
|
* @param {Object} [options] - @see show
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
success(options = {}) {
|
|
options.data.theme = "success";
|
|
options.data.icon = "check";
|
|
|
|
return this.show({ ...options, component: DDefaultToast });
|
|
}
|
|
|
|
/**
|
|
* Render a DDefaultToast toast with the error theme
|
|
*
|
|
* @param {Object} [options] - @see show
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
error(options = {}) {
|
|
options.data.theme = "error";
|
|
options.data.icon = "exclamation-triangle";
|
|
|
|
return this.show({ ...options, component: DDefaultToast });
|
|
}
|
|
|
|
/**
|
|
* Render a DDefaultToast toast with the warning theme
|
|
*
|
|
* @param {Object} [options] - @see show
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
warning(options = {}) {
|
|
options.data.theme = "warning";
|
|
options.data.icon = "exclamation-circle";
|
|
|
|
return this.show({ ...options, component: DDefaultToast });
|
|
}
|
|
|
|
/**
|
|
* Render a DDefaultToast toast with the info theme
|
|
*
|
|
* @param {Object} [options] - @see show
|
|
*
|
|
* @returns {DToastInstance} - a toast instance
|
|
*/
|
|
@action
|
|
info(options = {}) {
|
|
options.data.theme = "info";
|
|
options.data.icon = "info-circle";
|
|
|
|
return this.show({ ...options, component: DDefaultToast });
|
|
}
|
|
|
|
/**
|
|
* Close a toast. Any object containg a valid `id` property can be used as a toast parameter.
|
|
*/
|
|
@action
|
|
close(toast) {
|
|
this.activeToasts = new TrackedArray(
|
|
this.activeToasts.filter((activeToast) => activeToast.id !== toast.id)
|
|
);
|
|
}
|
|
}
|