hazo_ui 2.6.5 → 2.7.0

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/README.md CHANGED
@@ -158,6 +158,7 @@ interface HazoUiConfig {
158
158
 
159
159
  The following components support both global config and prop-level color overrides:
160
160
  - `HazoUiDialog` - via `headerBackgroundColor`, `headerTextColor`, `accentColor` props
161
+ - `HazoUiConfirmDialog` - via `accentColor`, `confirmButtonColor` props
161
162
  - `HazoUiMultiFilterDialog` - via header/submit/cancel/clear color props
162
163
  - `HazoUiMultiSortDialog` - via header/submit/cancel/clear color props
163
164
 
@@ -183,6 +184,8 @@ The following components support both global config and prop-level color overrid
183
184
 
184
185
  - **[HazoUiDialog](#hazouidialog)** - A standardized dialog component with customizable animations, sizes, and theming. Features header/body/footer layout, color customization props, multiple size variants, and distinct animation presets (zoom, slide, fade).
185
186
 
187
+ - **[HazoUiConfirmDialog](#hazouiconfirmdialog)** - A compact, opinionated confirmation dialog with accent top border, variant system (destructive, warning, info, success), async loading support, and configurable buttons. Perfect for delete confirmations, unsaved changes warnings, and simple acknowledgments.
188
+
186
189
  ---
187
190
 
188
191
  ## HazoUiMultiFilterDialog
@@ -1801,6 +1804,133 @@ function CustomHeightExample() {
1801
1804
 
1802
1805
  ---
1803
1806
 
1807
+ ## HazoUiConfirmDialog
1808
+
1809
+ A compact, opinionated confirmation dialog for destructive actions, warnings, and simple acknowledgments. Features an accent top border colored by variant, configurable buttons, and automatic async loading state management.
1810
+
1811
+ ### Basic Usage
1812
+
1813
+ ```tsx
1814
+ import { HazoUiConfirmDialog } from 'hazo_ui';
1815
+
1816
+ // Destructive confirmation with two buttons
1817
+ <HazoUiConfirmDialog
1818
+ open={showClear}
1819
+ onOpenChange={setShowClear}
1820
+ variant="destructive"
1821
+ title="Clear Form Data"
1822
+ description="This will permanently delete all uploaded files and reset the status. This action cannot be undone."
1823
+ confirmLabel="Yes, clear everything"
1824
+ onConfirm={handleClear}
1825
+ />
1826
+
1827
+ // OK-only acknowledgment
1828
+ <HazoUiConfirmDialog
1829
+ open={showSuccess}
1830
+ onOpenChange={setShowSuccess}
1831
+ variant="info"
1832
+ title="Upload Complete"
1833
+ description="Your files have been uploaded successfully."
1834
+ showCancelButton={false}
1835
+ onConfirm={() => setShowSuccess(false)}
1836
+ />
1837
+
1838
+ // Async with auto-loading spinner
1839
+ <HazoUiConfirmDialog
1840
+ open={showDelete}
1841
+ onOpenChange={setShowDelete}
1842
+ variant="destructive"
1843
+ title="Delete Account"
1844
+ description="This will permanently delete your account."
1845
+ confirmLabel="Delete My Account"
1846
+ onConfirm={async () => {
1847
+ await deleteAccount();
1848
+ // dialog auto-closes on resolve
1849
+ }}
1850
+ />
1851
+
1852
+ // Custom children content
1853
+ <HazoUiConfirmDialog
1854
+ open={showConfirm}
1855
+ onOpenChange={setShowConfirm}
1856
+ title="Confirm Submission"
1857
+ onConfirm={handleSubmit}
1858
+ >
1859
+ <div>
1860
+ <p>You are about to submit:</p>
1861
+ <ul>
1862
+ <li>3 documents</li>
1863
+ <li>2 images</li>
1864
+ </ul>
1865
+ </div>
1866
+ </HazoUiConfirmDialog>
1867
+ ```
1868
+
1869
+ ### Variants
1870
+
1871
+ | Variant | Top Border | Button Color | Use Case |
1872
+ |---------|-----------|-------------|----------|
1873
+ | `default` | Slate | Primary | General confirmations |
1874
+ | `destructive` | Red | Red | Delete, clear, remove |
1875
+ | `warning` | Amber | Amber | Unsaved changes, caution |
1876
+ | `info` | Blue | Blue | Notices, acknowledgments |
1877
+ | `success` | Green | Green | Completions, confirmations |
1878
+
1879
+ ### Props
1880
+
1881
+ | Prop | Type | Default | Description |
1882
+ |------|------|---------|-------------|
1883
+ | `open` | `boolean` | - | **Required.** Controls dialog visibility |
1884
+ | `onOpenChange` | `(open: boolean) => void` | - | **Required.** Called when dialog open state changes |
1885
+ | `title` | `string` | - | **Required.** Dialog title text |
1886
+ | `description` | `string` | - | Simple text description (alternative to children) |
1887
+ | `children` | `ReactNode` | - | Custom content (overrides description if both provided) |
1888
+ | `variant` | `ConfirmDialogVariant` | `'default'` | Color variant preset |
1889
+ | `confirmLabel` | `string` | `'Confirm'` / `'OK'` | Confirm button text. Defaults to "OK" when `showCancelButton={false}` |
1890
+ | `cancelLabel` | `string` | `'Cancel'` | Cancel button text |
1891
+ | `showCancelButton` | `boolean` | `true` | Whether to show the cancel button |
1892
+ | `onConfirm` | `() => void \| Promise<void>` | - | **Required.** Called on confirm. If returns Promise, auto-shows loading spinner |
1893
+ | `onCancel` | `() => void` | - | Called on cancel (before dialog closes) |
1894
+ | `loading` | `boolean` | - | External loading state (overrides async auto-detection) |
1895
+ | `disabled` | `boolean` | `false` | Disable confirm button |
1896
+ | `accentColor` | `string` | - | Override top border color |
1897
+ | `confirmButtonColor` | `string` | - | Override confirm button background color |
1898
+ | `openAnimation` | `AnimationPreset \| string` | `'zoom'` | Dialog open animation |
1899
+ | `closeAnimation` | `AnimationPreset \| string` | `'zoom'` | Dialog close animation |
1900
+
1901
+ ### Async Loading
1902
+
1903
+ When `onConfirm` returns a Promise, the component automatically:
1904
+ 1. Shows a spinner on the confirm button
1905
+ 2. Disables both buttons
1906
+ 3. Closes dialog on resolve
1907
+ 4. Restores buttons on reject (dialog stays open)
1908
+
1909
+ Use the `loading` prop to override this behavior with external control.
1910
+
1911
+ ### Custom Colors
1912
+
1913
+ ```tsx
1914
+ <HazoUiConfirmDialog
1915
+ accentColor="#7c3aed"
1916
+ confirmButtonColor="#7c3aed"
1917
+ title="Custom Styled"
1918
+ description="Purple accent and button colors."
1919
+ onConfirm={handleConfirm}
1920
+ open={open}
1921
+ onOpenChange={setOpen}
1922
+ />
1923
+ ```
1924
+
1925
+ ### Accessibility
1926
+
1927
+ - Focus trap within dialog (Radix)
1928
+ - ESC closes dialog
1929
+ - Cancel button receives initial focus for destructive/warning variants (prevents accidental confirmation)
1930
+ - Proper `aria-labelledby` and `aria-describedby` via Radix primitives
1931
+
1932
+ ---
1933
+
1804
1934
  ## HazoUiDialog
1805
1935
 
1806
1936
  A flexible, standardized dialog component with customizable animations, sizes, and theming. Built on Radix UI Dialog primitives with a consistent header/body/footer layout.
package/dist/index.cjs CHANGED
@@ -341,6 +341,7 @@ var DialogTitle = React27__namespace.forwardRef(({ className, ...props }, ref) =
341
341
  ref,
342
342
  className: cn(
343
343
  "text-lg font-semibold leading-none tracking-tight",
344
+ "break-words [overflow-wrap:anywhere]",
344
345
  className
345
346
  ),
346
347
  ...props
@@ -6393,7 +6394,7 @@ function HazoUiDialog({
6393
6394
  /* @__PURE__ */ jsxRuntime.jsx(
6394
6395
  "div",
6395
6396
  {
6396
- className: "cls_dialog_header_title px-6 pt-6 pb-3",
6397
+ className: cn("cls_dialog_header_title px-6 pt-6 pb-3", showCloseButton && "pr-10"),
6397
6398
  style: {
6398
6399
  ...finalHeaderBackgroundColor && { backgroundColor: finalHeaderBackgroundColor },
6399
6400
  ...finalHeaderTextColor && { color: finalHeaderTextColor }
@@ -6424,6 +6425,7 @@ function HazoUiDialog({
6424
6425
  "cls_dialog_header shrink-0",
6425
6426
  !headerBar && "p-6 pb-4",
6426
6427
  has_merged_variant_header && "pb-3",
6428
+ showCloseButton && "pr-10",
6427
6429
  headerClassName
6428
6430
  ),
6429
6431
  style: headerStyles,
@@ -6506,10 +6508,183 @@ function HazoUiDialog({
6506
6508
  )
6507
6509
  ] }) });
6508
6510
  }
6511
+ var CONFIRM_VARIANT_PRESETS = {
6512
+ destructive: {
6513
+ accent_color: "rgb(220, 38, 38)",
6514
+ confirm_button_color: "rgb(220, 38, 38)",
6515
+ overlay_class_name: "bg-red-950/50"
6516
+ },
6517
+ warning: {
6518
+ accent_color: "rgb(245, 158, 11)",
6519
+ confirm_button_color: "rgb(245, 158, 11)",
6520
+ overlay_class_name: "bg-yellow-950/50"
6521
+ },
6522
+ info: {
6523
+ accent_color: "rgb(37, 99, 235)",
6524
+ confirm_button_color: "rgb(37, 99, 235)",
6525
+ overlay_class_name: "bg-blue-950/50"
6526
+ },
6527
+ success: {
6528
+ accent_color: "rgb(22, 163, 74)",
6529
+ confirm_button_color: "rgb(22, 163, 74)",
6530
+ overlay_class_name: "bg-green-950/50"
6531
+ }
6532
+ };
6533
+ var ANIMATION_PRESETS2 = {
6534
+ zoom: { open: "animate-dialog-zoom", close: "animate-dialog-zoom" },
6535
+ slide: { open: "animate-dialog-slide-bottom", close: "animate-dialog-slide-bottom" },
6536
+ fade: { open: "animate-dialog-fade", close: "animate-dialog-fade" },
6537
+ bounce: { open: "animate-dialog-bounce", close: "animate-dialog-bounce" },
6538
+ "scale-up": { open: "animate-dialog-scale", close: "animate-dialog-scale" },
6539
+ flip: { open: "animate-dialog-flip", close: "animate-dialog-flip" },
6540
+ "slide-left": { open: "animate-dialog-slide-left", close: "animate-dialog-slide-left" },
6541
+ "slide-right": { open: "animate-dialog-slide-right", close: "animate-dialog-slide-right" },
6542
+ "slide-top": { open: "animate-dialog-slide-top", close: "animate-dialog-slide-top" },
6543
+ none: { open: "", close: "" }
6544
+ };
6545
+ function resolve_animation_classes(open_anim = "zoom", close_anim = "zoom") {
6546
+ const open_classes = open_anim in ANIMATION_PRESETS2 ? ANIMATION_PRESETS2[open_anim].open : open_anim;
6547
+ const close_classes = close_anim in ANIMATION_PRESETS2 ? ANIMATION_PRESETS2[close_anim].close : close_anim;
6548
+ return `${open_classes} ${close_classes}`;
6549
+ }
6550
+ function HazoUiConfirmDialog({
6551
+ open,
6552
+ onOpenChange,
6553
+ title,
6554
+ description,
6555
+ children,
6556
+ variant = "default",
6557
+ confirmLabel,
6558
+ cancelLabel = "Cancel",
6559
+ showCancelButton = true,
6560
+ onConfirm,
6561
+ onCancel,
6562
+ loading: external_loading,
6563
+ disabled = false,
6564
+ accentColor,
6565
+ confirmButtonColor,
6566
+ openAnimation = "zoom",
6567
+ closeAnimation = "zoom"
6568
+ }) {
6569
+ const [internal_loading, set_internal_loading] = React27__namespace.useState(false);
6570
+ const config = get_hazo_ui_config();
6571
+ const variant_preset = variant !== "default" ? CONFIRM_VARIANT_PRESETS[variant] : void 0;
6572
+ const is_loading = external_loading ?? internal_loading;
6573
+ const resolved_confirm_label = confirmLabel ?? (showCancelButton ? "Confirm" : "OK");
6574
+ const resolved_accent_color = accentColor ?? variant_preset?.accent_color ?? "rgb(148, 163, 184)";
6575
+ const resolved_confirm_button_color = confirmButtonColor ?? variant_preset?.confirm_button_color ?? config.submit_button_background_color;
6576
+ const cancel_button_styles = {
6577
+ ...config.cancel_button_background_color && { backgroundColor: config.cancel_button_background_color },
6578
+ ...config.cancel_button_text_color && { color: config.cancel_button_text_color },
6579
+ ...config.cancel_button_border_color && { borderColor: config.cancel_button_border_color }
6580
+ };
6581
+ const confirm_button_styles = {
6582
+ ...resolved_confirm_button_color && {
6583
+ backgroundColor: resolved_confirm_button_color,
6584
+ borderColor: resolved_confirm_button_color,
6585
+ color: "white"
6586
+ }
6587
+ };
6588
+ const animation_classes = resolve_animation_classes(openAnimation, closeAnimation);
6589
+ const handle_confirm = async () => {
6590
+ try {
6591
+ const result = onConfirm();
6592
+ if (result instanceof Promise) {
6593
+ set_internal_loading(true);
6594
+ await result;
6595
+ }
6596
+ onOpenChange(false);
6597
+ } catch {
6598
+ } finally {
6599
+ set_internal_loading(false);
6600
+ }
6601
+ };
6602
+ const handle_cancel = () => {
6603
+ onCancel?.();
6604
+ onOpenChange(false);
6605
+ };
6606
+ const cancel_ref = React27__namespace.useRef(null);
6607
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
6608
+ /* @__PURE__ */ jsxRuntime.jsx(
6609
+ DialogOverlay,
6610
+ {
6611
+ className: cn(
6612
+ "cls_confirm_dialog_overlay",
6613
+ variant_preset?.overlay_class_name
6614
+ ),
6615
+ style: { backdropFilter: "blur(4px)" }
6616
+ }
6617
+ ),
6618
+ /* @__PURE__ */ jsxRuntime.jsxs(
6619
+ DialogPrimitive__namespace.Content,
6620
+ {
6621
+ className: cn(
6622
+ "cls_confirm_dialog_content",
6623
+ "fixed left-[50%] top-[50%] z-50",
6624
+ "translate-x-[-50%] translate-y-[-50%]",
6625
+ "bg-background shadow-lg",
6626
+ "duration-200",
6627
+ animation_classes,
6628
+ "overflow-hidden",
6629
+ "focus:outline-none"
6630
+ ),
6631
+ style: {
6632
+ width: "min(90vw, 420px)",
6633
+ borderRadius: "12px",
6634
+ borderTop: `4px solid ${resolved_accent_color}`,
6635
+ boxShadow: "0 20px 60px rgba(0,0,0,0.15)"
6636
+ },
6637
+ onOpenAutoFocus: (e) => {
6638
+ if ((variant === "destructive" || variant === "warning") && showCancelButton && cancel_ref.current) {
6639
+ e.preventDefault();
6640
+ cancel_ref.current.focus();
6641
+ }
6642
+ },
6643
+ children: [
6644
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_body px-6 pt-7 pb-5", children: [
6645
+ /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Title, { className: "cls_confirm_dialog_title text-[17px] font-semibold text-foreground mb-2.5", children: title }),
6646
+ children ? /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_confirm_dialog_children text-sm text-muted-foreground leading-relaxed", children }) }) : description ? /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Description, { className: "cls_confirm_dialog_description text-sm text-muted-foreground leading-relaxed", children: description }) : null
6647
+ ] }),
6648
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_confirm_dialog_separator border-t border-border" }),
6649
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_actions px-6 py-4 flex justify-end gap-3", children: [
6650
+ showCancelButton && /* @__PURE__ */ jsxRuntime.jsx(
6651
+ Button,
6652
+ {
6653
+ ref: cancel_ref,
6654
+ type: "button",
6655
+ className: "cls_confirm_cancel_button",
6656
+ variant: "outline",
6657
+ onClick: handle_cancel,
6658
+ disabled: is_loading,
6659
+ style: cancel_button_styles,
6660
+ children: cancelLabel
6661
+ }
6662
+ ),
6663
+ /* @__PURE__ */ jsxRuntime.jsxs(
6664
+ Button,
6665
+ {
6666
+ type: "button",
6667
+ className: "cls_confirm_action_button",
6668
+ onClick: handle_confirm,
6669
+ disabled: is_loading || disabled,
6670
+ style: confirm_button_styles,
6671
+ children: [
6672
+ is_loading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
6673
+ resolved_confirm_label
6674
+ ]
6675
+ }
6676
+ )
6677
+ ] })
6678
+ ]
6679
+ }
6680
+ )
6681
+ ] }) });
6682
+ }
6509
6683
 
6510
6684
  exports.CommandNodeExtension = CommandNodeExtension;
6511
6685
  exports.CommandPill = CommandPill;
6512
6686
  exports.CommandPopover = CommandPopover;
6687
+ exports.HazoUiConfirmDialog = HazoUiConfirmDialog;
6513
6688
  exports.HazoUiDialog = HazoUiDialog;
6514
6689
  exports.HazoUiDialogClose = DialogClose;
6515
6690
  exports.HazoUiDialogContent = DialogContent;