@abydahana/aksara-ui 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/aksara.d.ts CHANGED
@@ -226,6 +226,26 @@ export declare class Offcanvas extends Modal {
226
226
  show(): this;
227
227
  hide(): this;
228
228
  }
229
+ export interface ConfirmModalOptions {
230
+ title: string;
231
+ message: string;
232
+ tone?: "danger" | "warning" | "primary" | "info" | "success" | string;
233
+ confirmLabel?: string;
234
+ cancelLabel?: string;
235
+ icon?: string;
236
+ }
237
+ export interface AlertModalOptions {
238
+ title: string;
239
+ message: string;
240
+ tone?: "primary" | "danger" | "warning" | "info" | "success" | string;
241
+ buttonLabel?: string;
242
+ icon?: string;
243
+ }
244
+ export interface ToastNotifyOptions {
245
+ icon?: string;
246
+ variant?: "primary" | "secondary" | "success" | "danger" | "warning" | "info" | string;
247
+ duration?: number;
248
+ }
229
249
  export interface AksaraNamespace {
230
250
  Modal: typeof Modal;
231
251
  Tooltip: typeof Tooltip;
@@ -275,6 +295,9 @@ export interface AksaraNamespace {
275
295
  setTheme(theme: "light" | "dark" | "system"): "light" | "dark" | "system";
276
296
  getTheme(): "light" | "dark" | "system";
277
297
  initTheme(): "light" | "dark" | "system";
298
+ confirm(options: ConfirmModalOptions): Promise<boolean>;
299
+ alert(options: AlertModalOptions): Promise<void>;
300
+ notify(message: string, options?: ToastNotifyOptions): void;
278
301
  clipboard(target: string | HTMLElement, text?: string): Promise<boolean>;
279
302
  init(root?: ParentNode | Document | HTMLElement): AksaraNamespace;
280
303
  destroy(): AksaraNamespace;
@@ -431,6 +431,12 @@ export class Dropdown extends Component {
431
431
  this.menu.style.position = "absolute";
432
432
  this.menu.style.zIndex = "1000";
433
433
  const triggerRect = this.trigger.getBoundingClientRect();
434
+ if (this.trigger.classList.contains("form-select") ||
435
+ this.element.classList.contains("variant-form-select") ||
436
+ this.element.hasAttribute("data-dropdown-select")) {
437
+ this.menu.style.width = `${triggerRect.width}px`;
438
+ this.menu.style.minWidth = `${triggerRect.width}px`;
439
+ }
434
440
  const menuRect = this.menu.getBoundingClientRect();
435
441
  const gap = this.offset;
436
442
  let top;
@@ -1679,6 +1685,147 @@ export const Aksara = {
1679
1685
  }
1680
1686
  return currentTheme;
1681
1687
  },
1688
+ confirm(options) {
1689
+ if (typeof document === "undefined")
1690
+ return Promise.resolve(false);
1691
+ let host = document.getElementById("aksara-dialog-host");
1692
+ if (!host) {
1693
+ host = document.createElement("div");
1694
+ host.id = "aksara-dialog-host";
1695
+ document.body.appendChild(host);
1696
+ }
1697
+ const tone = options.tone ?? "danger";
1698
+ const confirmLabel = options.confirmLabel ?? "Confirm";
1699
+ const cancelLabel = options.cancelLabel ?? "Cancel";
1700
+ const icon = options.icon ?? (tone === "danger" ? "mdi-alert-circle-outline" : "mdi-alert-outline");
1701
+ host.innerHTML = `
1702
+ <div class="modal fade modal-sm" id="aksaraConfirmModal" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="aksara-confirm-title">
1703
+ <div class="modal-dialog modal-dialog-centered">
1704
+ <div class="modal-content overflow-hidden border-0 shadow-lg">
1705
+ <div class="modal-body text-center p-4">
1706
+ <i class="mdi ${escapeHtmlStr(icon)} text-${escapeHtmlStr(tone)} fs-1 d-block mb-2" aria-hidden="true"></i>
1707
+ <h5 id="aksara-confirm-title" class="fs-5 font-bold mb-2 text-body">${escapeHtmlStr(options.title)}</h5>
1708
+ <p class="text-muted mb-0">${escapeHtmlStr(options.message)}</p>
1709
+ </div>
1710
+ <div class="modal-footer p-0 overflow-hidden m-0 w-100 gap-0 d-flex border-top">
1711
+ <button class="btn btn-ghost text-body rounded-0 py-3 m-0 text-center w-[50%]" type="button" data-aksara-confirm-cancel>
1712
+ ${escapeHtmlStr(cancelLabel)}
1713
+ </button>
1714
+ <button class="btn btn-ghost border-start text-${escapeHtmlStr(tone)} rounded-0 py-3 m-0 text-center w-[50%]" type="button" data-aksara-confirm-accept>
1715
+ <i class="mdi mdi-check me-1" aria-hidden="true"></i>
1716
+ ${escapeHtmlStr(confirmLabel)}
1717
+ </button>
1718
+ </div>
1719
+ </div>
1720
+ </div>
1721
+ </div>
1722
+ `;
1723
+ const modalEl = host.querySelector("#aksaraConfirmModal");
1724
+ if (!modalEl)
1725
+ return Promise.resolve(false);
1726
+ const modalInstance = Aksara.modal(modalEl);
1727
+ modalInstance.show();
1728
+ return new Promise((resolve) => {
1729
+ let settled = false;
1730
+ const finish = (value) => {
1731
+ if (settled)
1732
+ return;
1733
+ settled = true;
1734
+ modalInstance.hide();
1735
+ setTimeout(() => {
1736
+ if (host)
1737
+ host.innerHTML = "";
1738
+ }, 200);
1739
+ resolve(value);
1740
+ };
1741
+ modalEl.addEventListener("modal:hide", () => finish(false), { once: true });
1742
+ host
1743
+ ?.querySelector("[data-aksara-confirm-cancel]")
1744
+ ?.addEventListener("click", () => finish(false), { once: true });
1745
+ host
1746
+ ?.querySelector("[data-aksara-confirm-accept]")
1747
+ ?.addEventListener("click", () => finish(true), { once: true });
1748
+ });
1749
+ },
1750
+ alert(options) {
1751
+ if (typeof document === "undefined")
1752
+ return Promise.resolve();
1753
+ let host = document.getElementById("aksara-dialog-host");
1754
+ if (!host) {
1755
+ host = document.createElement("div");
1756
+ host.id = "aksara-dialog-host";
1757
+ document.body.appendChild(host);
1758
+ }
1759
+ const tone = options.tone ?? "primary";
1760
+ const buttonLabel = options.buttonLabel ?? "OK";
1761
+ const icon = options.icon ??
1762
+ (tone === "danger"
1763
+ ? "mdi-alert-circle-outline"
1764
+ : tone === "warning"
1765
+ ? "mdi-alert-outline"
1766
+ : "mdi-information-outline");
1767
+ host.innerHTML = `
1768
+ <div class="modal fade modal-sm" id="aksaraAlertModal" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="aksara-alert-title">
1769
+ <div class="modal-dialog modal-dialog-centered">
1770
+ <div class="modal-content overflow-hidden border-0 shadow-lg">
1771
+ <div class="modal-body text-center p-4">
1772
+ <i class="mdi ${escapeHtmlStr(icon)} text-${escapeHtmlStr(tone)} fs-1 d-block mb-2" aria-hidden="true"></i>
1773
+ <h5 id="aksara-alert-title" class="fs-5 font-bold mb-2 text-body">${escapeHtmlStr(options.title)}</h5>
1774
+ <p class="text-muted mb-0">${escapeHtmlStr(options.message)}</p>
1775
+ </div>
1776
+ <div class="modal-footer p-0 overflow-hidden m-0 w-100 gap-0 d-flex border-top">
1777
+ <button class="btn btn-ghost text-body rounded-0 py-3 m-0 text-center w-100" type="button" data-aksara-alert-dismiss>
1778
+ ${escapeHtmlStr(buttonLabel)}
1779
+ </button>
1780
+ </div>
1781
+ </div>
1782
+ </div>
1783
+ </div>
1784
+ `;
1785
+ const modalEl = host.querySelector("#aksaraAlertModal");
1786
+ if (!modalEl)
1787
+ return Promise.resolve();
1788
+ const modalInstance = Aksara.modal(modalEl);
1789
+ modalInstance.show();
1790
+ return new Promise((resolve) => {
1791
+ let settled = false;
1792
+ const finish = () => {
1793
+ if (settled)
1794
+ return;
1795
+ settled = true;
1796
+ modalInstance.hide();
1797
+ setTimeout(() => {
1798
+ if (host)
1799
+ host.innerHTML = "";
1800
+ }, 200);
1801
+ resolve();
1802
+ };
1803
+ modalEl.addEventListener("modal:hide", () => finish(), { once: true });
1804
+ host?.querySelector("[data-aksara-alert-dismiss]")?.addEventListener("click", () => finish(), { once: true });
1805
+ });
1806
+ },
1807
+ notify(message, options = {}) {
1808
+ if (typeof document === "undefined")
1809
+ return;
1810
+ let container = document.getElementById("aksara-toast-container");
1811
+ if (!container) {
1812
+ container = document.createElement("div");
1813
+ container.id = "aksara-toast-container";
1814
+ container.className = "aksara-toast-container";
1815
+ document.body.appendChild(container);
1816
+ }
1817
+ const toast = document.createElement("div");
1818
+ toast.className = "aksara-toast-pill";
1819
+ const iconClass = options.icon ?? "mdi-information-outline";
1820
+ toast.innerHTML = `<i class="mdi ${escapeHtmlStr(iconClass)} text-primary" style="font-size: 1.15rem; line-height: 1;"></i><span>${escapeHtmlStr(message)}</span>`;
1821
+ container.appendChild(toast);
1822
+ const duration = options.duration ?? 2500;
1823
+ setTimeout(() => {
1824
+ toast.style.opacity = "0";
1825
+ toast.style.transform = "translateY(8px)";
1826
+ setTimeout(() => toast.remove(), 250);
1827
+ }, duration);
1828
+ },
1682
1829
  async clipboard(target, text) {
1683
1830
  if (typeof navigator === "undefined" || !navigator.clipboard)
1684
1831
  return false;
@@ -1753,7 +1900,7 @@ function toAttributes(attributes) {
1753
1900
  parts.push(key);
1754
1901
  }
1755
1902
  else if (value !== false && value !== null && value !== undefined) {
1756
- parts.push(`${key}="${escapeHtml(value)}"`);
1903
+ parts.push(`${key}="${escapeHtmlStr(value)}"`);
1757
1904
  }
1758
1905
  }
1759
1906
  return parts.length > 0 ? ` ${parts.join(" ")}` : "";
package/dist/aksara.js CHANGED
@@ -432,6 +432,12 @@ class Dropdown extends Component {
432
432
  this.menu.style.position = "absolute";
433
433
  this.menu.style.zIndex = "1000";
434
434
  const triggerRect = this.trigger.getBoundingClientRect();
435
+ if (this.trigger.classList.contains("form-select") ||
436
+ this.element.classList.contains("variant-form-select") ||
437
+ this.element.hasAttribute("data-dropdown-select")) {
438
+ this.menu.style.width = `${triggerRect.width}px`;
439
+ this.menu.style.minWidth = `${triggerRect.width}px`;
440
+ }
435
441
  const menuRect = this.menu.getBoundingClientRect();
436
442
  const gap = this.offset;
437
443
  let top;
@@ -1680,6 +1686,147 @@ const Aksara = {
1680
1686
  }
1681
1687
  return currentTheme;
1682
1688
  },
1689
+ confirm(options) {
1690
+ if (typeof document === "undefined")
1691
+ return Promise.resolve(false);
1692
+ let host = document.getElementById("aksara-dialog-host");
1693
+ if (!host) {
1694
+ host = document.createElement("div");
1695
+ host.id = "aksara-dialog-host";
1696
+ document.body.appendChild(host);
1697
+ }
1698
+ const tone = options.tone ?? "danger";
1699
+ const confirmLabel = options.confirmLabel ?? "Confirm";
1700
+ const cancelLabel = options.cancelLabel ?? "Cancel";
1701
+ const icon = options.icon ?? (tone === "danger" ? "mdi-alert-circle-outline" : "mdi-alert-outline");
1702
+ host.innerHTML = `
1703
+ <div class="modal fade modal-sm" id="aksaraConfirmModal" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="aksara-confirm-title">
1704
+ <div class="modal-dialog modal-dialog-centered">
1705
+ <div class="modal-content overflow-hidden border-0 shadow-lg">
1706
+ <div class="modal-body text-center p-4">
1707
+ <i class="mdi ${escapeHtmlStr(icon)} text-${escapeHtmlStr(tone)} fs-1 d-block mb-2" aria-hidden="true"></i>
1708
+ <h5 id="aksara-confirm-title" class="fs-5 font-bold mb-2 text-body">${escapeHtmlStr(options.title)}</h5>
1709
+ <p class="text-muted mb-0">${escapeHtmlStr(options.message)}</p>
1710
+ </div>
1711
+ <div class="modal-footer p-0 overflow-hidden m-0 w-100 gap-0 d-flex border-top">
1712
+ <button class="btn btn-ghost text-body rounded-0 py-3 m-0 text-center w-[50%]" type="button" data-aksara-confirm-cancel>
1713
+ ${escapeHtmlStr(cancelLabel)}
1714
+ </button>
1715
+ <button class="btn btn-ghost border-start text-${escapeHtmlStr(tone)} rounded-0 py-3 m-0 text-center w-[50%]" type="button" data-aksara-confirm-accept>
1716
+ <i class="mdi mdi-check me-1" aria-hidden="true"></i>
1717
+ ${escapeHtmlStr(confirmLabel)}
1718
+ </button>
1719
+ </div>
1720
+ </div>
1721
+ </div>
1722
+ </div>
1723
+ `;
1724
+ const modalEl = host.querySelector("#aksaraConfirmModal");
1725
+ if (!modalEl)
1726
+ return Promise.resolve(false);
1727
+ const modalInstance = Aksara.modal(modalEl);
1728
+ modalInstance.show();
1729
+ return new Promise((resolve) => {
1730
+ let settled = false;
1731
+ const finish = (value) => {
1732
+ if (settled)
1733
+ return;
1734
+ settled = true;
1735
+ modalInstance.hide();
1736
+ setTimeout(() => {
1737
+ if (host)
1738
+ host.innerHTML = "";
1739
+ }, 200);
1740
+ resolve(value);
1741
+ };
1742
+ modalEl.addEventListener("modal:hide", () => finish(false), { once: true });
1743
+ host
1744
+ ?.querySelector("[data-aksara-confirm-cancel]")
1745
+ ?.addEventListener("click", () => finish(false), { once: true });
1746
+ host
1747
+ ?.querySelector("[data-aksara-confirm-accept]")
1748
+ ?.addEventListener("click", () => finish(true), { once: true });
1749
+ });
1750
+ },
1751
+ alert(options) {
1752
+ if (typeof document === "undefined")
1753
+ return Promise.resolve();
1754
+ let host = document.getElementById("aksara-dialog-host");
1755
+ if (!host) {
1756
+ host = document.createElement("div");
1757
+ host.id = "aksara-dialog-host";
1758
+ document.body.appendChild(host);
1759
+ }
1760
+ const tone = options.tone ?? "primary";
1761
+ const buttonLabel = options.buttonLabel ?? "OK";
1762
+ const icon = options.icon ??
1763
+ (tone === "danger"
1764
+ ? "mdi-alert-circle-outline"
1765
+ : tone === "warning"
1766
+ ? "mdi-alert-outline"
1767
+ : "mdi-information-outline");
1768
+ host.innerHTML = `
1769
+ <div class="modal fade modal-sm" id="aksaraAlertModal" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="aksara-alert-title">
1770
+ <div class="modal-dialog modal-dialog-centered">
1771
+ <div class="modal-content overflow-hidden border-0 shadow-lg">
1772
+ <div class="modal-body text-center p-4">
1773
+ <i class="mdi ${escapeHtmlStr(icon)} text-${escapeHtmlStr(tone)} fs-1 d-block mb-2" aria-hidden="true"></i>
1774
+ <h5 id="aksara-alert-title" class="fs-5 font-bold mb-2 text-body">${escapeHtmlStr(options.title)}</h5>
1775
+ <p class="text-muted mb-0">${escapeHtmlStr(options.message)}</p>
1776
+ </div>
1777
+ <div class="modal-footer p-0 overflow-hidden m-0 w-100 gap-0 d-flex border-top">
1778
+ <button class="btn btn-ghost text-body rounded-0 py-3 m-0 text-center w-100" type="button" data-aksara-alert-dismiss>
1779
+ ${escapeHtmlStr(buttonLabel)}
1780
+ </button>
1781
+ </div>
1782
+ </div>
1783
+ </div>
1784
+ </div>
1785
+ `;
1786
+ const modalEl = host.querySelector("#aksaraAlertModal");
1787
+ if (!modalEl)
1788
+ return Promise.resolve();
1789
+ const modalInstance = Aksara.modal(modalEl);
1790
+ modalInstance.show();
1791
+ return new Promise((resolve) => {
1792
+ let settled = false;
1793
+ const finish = () => {
1794
+ if (settled)
1795
+ return;
1796
+ settled = true;
1797
+ modalInstance.hide();
1798
+ setTimeout(() => {
1799
+ if (host)
1800
+ host.innerHTML = "";
1801
+ }, 200);
1802
+ resolve();
1803
+ };
1804
+ modalEl.addEventListener("modal:hide", () => finish(), { once: true });
1805
+ host?.querySelector("[data-aksara-alert-dismiss]")?.addEventListener("click", () => finish(), { once: true });
1806
+ });
1807
+ },
1808
+ notify(message, options = {}) {
1809
+ if (typeof document === "undefined")
1810
+ return;
1811
+ let container = document.getElementById("aksara-toast-container");
1812
+ if (!container) {
1813
+ container = document.createElement("div");
1814
+ container.id = "aksara-toast-container";
1815
+ container.className = "aksara-toast-container";
1816
+ document.body.appendChild(container);
1817
+ }
1818
+ const toast = document.createElement("div");
1819
+ toast.className = "aksara-toast-pill";
1820
+ const iconClass = options.icon ?? "mdi-information-outline";
1821
+ toast.innerHTML = `<i class="mdi ${escapeHtmlStr(iconClass)} text-primary" style="font-size: 1.15rem; line-height: 1;"></i><span>${escapeHtmlStr(message)}</span>`;
1822
+ container.appendChild(toast);
1823
+ const duration = options.duration ?? 2500;
1824
+ setTimeout(() => {
1825
+ toast.style.opacity = "0";
1826
+ toast.style.transform = "translateY(8px)";
1827
+ setTimeout(() => toast.remove(), 250);
1828
+ }, duration);
1829
+ },
1683
1830
  async clipboard(target, text) {
1684
1831
  if (typeof navigator === "undefined" || !navigator.clipboard)
1685
1832
  return false;
@@ -1754,7 +1901,7 @@ function toAttributes(attributes) {
1754
1901
  parts.push(key);
1755
1902
  }
1756
1903
  else if (value !== false && value !== null && value !== undefined) {
1757
- parts.push(`${key}="${escapeHtml(value)}"`);
1904
+ parts.push(`${key}="${escapeHtmlStr(value)}"`);
1758
1905
  }
1759
1906
  }
1760
1907
  return parts.length > 0 ? ` ${parts.join(" ")}` : "";