@nexim/snackbar
Version:
Snackbar component with signal capability.
211 lines (202 loc) • 6.95 kB
JavaScript
/* @nexim/snackbar v2.1.0 */
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result) __defProp(target, key, result);
return result;
};
// src/main.ts
import { packageTracer } from "@alwatr/package-tracer";
// src/lib/handler.ts
import { delay } from "@alwatr/delay";
import { createLogger } from "@alwatr/logger";
import debounce from "debounce";
// src/lib/signal.ts
import { AlwatrSignal, AlwatrTrigger } from "@alwatr/flux";
var snackbarActionButtonClickedSignal = /* @__PURE__ */ new AlwatrTrigger({
name: "snackbar-action-button-click"
});
var snackbarCloseButtonClickedSignal = /* @__PURE__ */ new AlwatrTrigger({
name: "snackbar-close-button-click"
});
var snackbarSignal = /* @__PURE__ */ new AlwatrSignal({ name: "snackbar" });
// src/lib/handler.ts
var logger = createLogger("@nexim/snackbar");
var closeLastSnackbar = null;
var unsubscribeActionButtonHandler = null;
var unsubscribeCloseButtonHandler = null;
function createSnackbarElement(options) {
const element = document.createElement("snack-bar");
element.setAttribute("content", options.content);
if (options.addCloseButton === true) {
element.setAttribute("add-close-button", "");
}
if (options.action != null) {
element.setAttribute("action-button-label", options.action.label);
}
return element;
}
function handleActionButtonClick(closeSnackbar, handler) {
logger.logMethod?.("handleActionButtonClick");
(async () => {
try {
await handler();
} catch (error) {
logger.error("handleActionButtonClick", "call_handler_failed", error);
}
})();
return closeSnackbar();
}
async function showSnackbar(options) {
logger.logMethodArgs?.("showSnackbar", { options });
options.duration ?? (options.duration = "5s");
const element = createSnackbarElement(options);
let closed = false;
const closeSnackbar = async () => {
if (closed) return;
logger.logMethodArgs?.("closeSnackbar", { options });
await element.close();
unsubscribeActionButtonHandler?.();
unsubscribeCloseButtonHandler?.();
closed = true;
};
await closeLastSnackbar?.();
closeLastSnackbar = closeSnackbar;
if (options.action != null) {
unsubscribeActionButtonHandler = snackbarActionButtonClickedSignal.subscribe(
handleActionButtonClick.bind(null, closeSnackbar, options.action.handler)
).unsubscribe;
}
if (options.addCloseButton === true) {
unsubscribeCloseButtonHandler = snackbarCloseButtonClickedSignal.subscribe(closeSnackbar.bind(null)).unsubscribe;
}
document.body.appendChild(element);
if (options.duration !== "infinite") {
delay.by(options.duration).then(closeSnackbar);
}
}
var debouncedShowSnackbar = debounce((options) => {
showSnackbar(options);
}, 50);
snackbarSignal.subscribe((options) => {
debouncedShowSnackbar(options);
});
// src/lib/element.ts
import { delay as delay3 } from "@alwatr/delay";
import { lightDomMixin, loggerMixin } from "@nexim/element";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
// src/lib/utils.ts
import { delay as delay2 } from "@alwatr/delay";
function waitForNextFrame() {
return new Promise((resolve) => {
delay2.untilNextAnimationFrame().then(() => {
delay2.immediate().then(resolve);
});
});
}
// src/lib/element.ts
var SnackbarElement = class extends lightDomMixin(loggerMixin(LitElement)) {
constructor() {
super(...arguments);
this.content = "";
this.actionButtonLabel = null;
this.addCloseButton = false;
}
// ms
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
waitForNextFrame().then(() => {
this.setAttribute("open", "");
});
}
/**
* Close the snackbar and remove it from the DOM.
* Waits for the closing animation to end before removing the element.
*
* @internal
* This method should be used by the package API, not directly, to ensure proper signal unsubscription.
*/
async close() {
this.logger_.logMethod?.("close");
this.removeAttribute("open");
await delay3.by(SnackbarElement.openAndCloseAnimationDuration__);
this.remove();
}
/**
* Handle the close button click event.
* Sends a signal when the action button is clicked.
*/
closeButtonClickHandler__() {
this.logger_.logMethod?.("closeButtonClickHandler__");
snackbarCloseButtonClickedSignal.notify();
}
/**
* Handle the action button click event.
* Sends a signal when the action button is clicked.
*/
actionButtonClickHandler__() {
this.logger_.logMethod?.("actionButtonClickHandler__");
snackbarActionButtonClickedSignal.notify();
}
/**
* Render the snackbar component.
*/
render() {
super.render();
const actionButtonHtml = this.renderActionButton__();
const closeButtonHtml = this.renderCloseButton__();
let actionButtonHandler = nothing;
if (actionButtonHtml != nothing || closeButtonHtml != nothing) {
actionButtonHandler = html`<div>${actionButtonHtml} ${closeButtonHtml}</div>`;
}
return [html`<span>${this.content}</span>`, actionButtonHandler];
}
/**
* Render the action button.
*/
renderActionButton__() {
if (this.actionButtonLabel == null) return nothing;
this.logger_.logMethodArgs?.("renderActionButton__", { actionLabel: this.actionButtonLabel });
return html` <button class="action-button" @click=${this.actionButtonClickHandler__.bind(this)}>${this.actionButtonLabel}</button> `;
}
/**
* Render the close button.
*/
renderCloseButton__() {
if (!this.addCloseButton) return nothing;
this.logger_.logMethod?.("renderCloseButton__");
return html`
<button class="close-button" @click=${this.closeButtonClickHandler__.bind(this)}>
<span class="icon">close</span>
</button>
`;
}
};
/**
* Duration for the open and close animation in milliseconds.
*/
SnackbarElement.openAndCloseAnimationDuration__ = 200;
__decorateClass([
property({ type: String })
], SnackbarElement.prototype, "content", 2);
__decorateClass([
property({ type: String, attribute: "action-button-label" })
], SnackbarElement.prototype, "actionButtonLabel", 2);
__decorateClass([
property({ type: Boolean, attribute: "add-close-button" })
], SnackbarElement.prototype, "addCloseButton", 2);
SnackbarElement = __decorateClass([
customElement("snack-bar")
], SnackbarElement);
// src/main.ts
__dev_mode__: packageTracer.add("@nexim/snackbar", "2.1.0");
export {
SnackbarElement,
snackbarSignal
};
//# sourceMappingURL=main.mjs.map