hazo_ui 4.9.0 → 4.10.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/dist/index.cjs CHANGED
@@ -208,6 +208,18 @@ var require_objectWithoutPropertiesLoose = __commonJS({
208
208
  function cn(...inputs) {
209
209
  return tailwindMerge.twMerge(clsx.clsx(inputs));
210
210
  }
211
+ function safeHref(url) {
212
+ if (!url) return void 0;
213
+ const trimmed = url.trim();
214
+ const lower = trimmed.toLowerCase();
215
+ if (lower.startsWith("http://") || lower.startsWith("https://") || lower.startsWith("mailto:")) {
216
+ return trimmed;
217
+ }
218
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
219
+ return trimmed;
220
+ }
221
+ return void 0;
222
+ }
211
223
  function HazoContextProvider({
212
224
  correlationId,
213
225
  userId,
@@ -6767,39 +6779,47 @@ function resolve_animation_classes(open_anim = "zoom", close_anim = "zoom") {
6767
6779
  }
6768
6780
  var VARIANT_PRESETS = {
6769
6781
  info: {
6770
- header_background_color: "rgb(191, 219, 254)",
6771
- description_background_color: "rgb(219, 234, 254)",
6772
- header_text_color: "rgb(30, 58, 138)",
6773
- border_color: "rgb(59, 130, 246)",
6774
- accent_color: "rgb(59, 130, 246)",
6782
+ header_background_color: "var(--dialog-info-bg)",
6783
+ description_background_color: "var(--dialog-info-bg)",
6784
+ header_text_color: "var(--dialog-info-fg)",
6785
+ border_color: "var(--dialog-info-fg)",
6786
+ accent_color: "var(--dialog-info-fg)",
6775
6787
  overlay_class_name: "bg-blue-950/50"
6776
6788
  },
6777
6789
  success: {
6778
- header_background_color: "rgb(187, 247, 208)",
6779
- description_background_color: "rgb(220, 252, 231)",
6780
- header_text_color: "rgb(22, 101, 52)",
6781
- border_color: "rgb(34, 197, 94)",
6782
- accent_color: "rgb(34, 197, 94)",
6790
+ header_background_color: "var(--dialog-success-bg)",
6791
+ description_background_color: "var(--dialog-success-bg)",
6792
+ header_text_color: "var(--dialog-success-fg)",
6793
+ border_color: "var(--dialog-success-fg)",
6794
+ accent_color: "var(--dialog-success-fg)",
6783
6795
  overlay_class_name: "bg-green-950/50"
6784
6796
  },
6785
6797
  warning: {
6786
- header_background_color: "rgb(253, 230, 138)",
6787
- description_background_color: "rgb(254, 249, 195)",
6788
- header_text_color: "rgb(113, 63, 18)",
6789
- border_color: "rgb(234, 179, 8)",
6790
- accent_color: "rgb(234, 179, 8)",
6798
+ header_background_color: "var(--dialog-warning-bg)",
6799
+ description_background_color: "var(--dialog-warning-bg)",
6800
+ header_text_color: "var(--dialog-warning-fg)",
6801
+ border_color: "var(--dialog-warning-fg)",
6802
+ accent_color: "var(--dialog-warning-fg)",
6791
6803
  overlay_class_name: "bg-yellow-950/50"
6792
6804
  },
6793
6805
  destructive: {
6794
- header_background_color: "rgb(254, 202, 202)",
6795
- description_background_color: "rgb(254, 226, 226)",
6796
- header_text_color: "rgb(127, 29, 29)",
6797
- border_color: "rgb(239, 68, 68)",
6798
- accent_color: "rgb(239, 68, 68)",
6806
+ header_background_color: "var(--dialog-destructive-bg)",
6807
+ description_background_color: "var(--dialog-destructive-bg)",
6808
+ header_text_color: "var(--dialog-destructive-fg)",
6809
+ border_color: "var(--dialog-destructive-fg)",
6810
+ accent_color: "var(--dialog-destructive-fg)",
6799
6811
  overlay_class_name: "bg-red-950/50",
6800
6812
  action_button_variant: "destructive"
6801
6813
  }
6802
6814
  };
6815
+ var NEUTRAL_VARIANT_PRESET = {
6816
+ header_background_color: "hsl(var(--dialog-border) / 0.12)",
6817
+ description_background_color: "hsl(var(--dialog-border) / 0.12)",
6818
+ header_text_color: "hsl(var(--dialog-fg))",
6819
+ border_color: "hsl(var(--dialog-border))",
6820
+ accent_color: "hsl(var(--dialog-border))",
6821
+ overlay_class_name: "bg-black/50"
6822
+ };
6803
6823
  function HazoUiDialog({
6804
6824
  open,
6805
6825
  onOpenChange,
@@ -6808,13 +6828,13 @@ function HazoUiDialog({
6808
6828
  onCancel,
6809
6829
  title = "Action required",
6810
6830
  description,
6811
- actionButtonText = "Confirm",
6831
+ actionButtonText: actionButtonTextProp,
6812
6832
  actionButtonVariant,
6813
6833
  cancelButtonText = "Cancel",
6814
- showCancelButton = true,
6834
+ showCancelButton: showCancelButtonProp,
6815
6835
  showActionButton = true,
6816
- closeOnConfirm = false,
6817
- actionButtonLoading = false,
6836
+ closeOnConfirm: closeOnConfirmProp,
6837
+ actionButtonLoading: actionButtonLoadingProp,
6818
6838
  actionButtonDisabled = false,
6819
6839
  actionButtonIcon,
6820
6840
  footerContent,
@@ -6834,18 +6854,46 @@ function HazoUiDialog({
6834
6854
  borderColor,
6835
6855
  accentColor,
6836
6856
  headerBar = false,
6837
- headerBarColor = "#1e293b",
6857
+ headerBarColor,
6838
6858
  className,
6839
6859
  contentClassName,
6840
6860
  overlayClassName,
6841
6861
  headerClassName,
6842
6862
  footerClassName,
6843
- showCloseButton = true
6863
+ showCloseButton = true,
6864
+ kind
6844
6865
  }) {
6845
6866
  const config = get_hazo_ui_config();
6846
- const variant_preset = variant !== "default" ? VARIANT_PRESETS[variant] : void 0;
6867
+ const is_confirm_kind = kind === "confirm";
6868
+ const is_status_kind = kind === "status";
6869
+ const [internal_confirm_loading, set_internal_confirm_loading] = React26__namespace.useState(false);
6870
+ const cancel_ref = React26__namespace.useRef(null);
6871
+ const variant_preset = variant !== "default" ? VARIANT_PRESETS[variant] : is_status_kind || is_confirm_kind ? NEUTRAL_VARIANT_PRESET : void 0;
6847
6872
  const resolved_action_button_variant = actionButtonVariant ?? variant_preset?.action_button_variant ?? "default";
6873
+ const showCancelButton = showCancelButtonProp ?? (is_status_kind ? false : true);
6874
+ const closeOnConfirm = closeOnConfirmProp ?? (is_status_kind || is_confirm_kind ? true : false);
6875
+ const actionButtonText = actionButtonTextProp ?? (is_status_kind ? "OK" : is_confirm_kind ? showCancelButton ? "Confirm" : "OK" : "Confirm");
6876
+ const actionButtonLoading = actionButtonLoadingProp ?? (is_confirm_kind ? internal_confirm_loading : false);
6848
6877
  const handleConfirm = () => {
6878
+ if (is_confirm_kind) {
6879
+ try {
6880
+ const result = onConfirm?.();
6881
+ if (result instanceof Promise) {
6882
+ if (actionButtonLoadingProp === void 0) set_internal_confirm_loading(true);
6883
+ result.then(() => {
6884
+ if (closeOnConfirm) onOpenChange?.(false);
6885
+ }).catch(() => {
6886
+ }).finally(() => {
6887
+ if (actionButtonLoadingProp === void 0) set_internal_confirm_loading(false);
6888
+ });
6889
+ return;
6890
+ }
6891
+ } catch {
6892
+ return;
6893
+ }
6894
+ if (closeOnConfirm) onOpenChange?.(false);
6895
+ return;
6896
+ }
6849
6897
  onConfirm?.();
6850
6898
  if (closeOnConfirm) {
6851
6899
  onOpenChange?.(false);
@@ -6865,12 +6913,14 @@ function HazoUiDialog({
6865
6913
  const finalHeaderBackgroundColor = headerBackgroundColor ?? variant_preset?.header_background_color ?? config.header_background_color;
6866
6914
  const finalDescriptionBgColor = descriptionBackgroundColor ?? variant_preset?.description_background_color;
6867
6915
  const finalHeaderTextColor = headerTextColor ?? variant_preset?.header_text_color ?? config.header_text_color;
6868
- const is_variant_active = variant !== "default";
6916
+ const is_variant_active = variant !== "default" || is_status_kind;
6869
6917
  const has_split_header = splitHeader && !headerBar && !!finalDescriptionBgColor && !!description;
6870
6918
  const has_merged_variant_header = !splitHeader && is_variant_active && !headerBar && !!description;
6919
+ const resolvedHeaderBarColor = headerBarColor ?? (variant !== "default" && variant_preset ? variant_preset.header_text_color : "var(--dialog-header-bar-bg)");
6871
6920
  const headerStyles = {
6872
6921
  ...headerBar && {
6873
- backgroundColor: headerBarColor,
6922
+ backgroundColor: resolvedHeaderBarColor,
6923
+ color: "var(--dialog-header-bar-fg)",
6874
6924
  paddingTop: compact ? "0.625rem" : "1.5rem",
6875
6925
  paddingBottom: compact ? "0.625rem" : "1.5rem",
6876
6926
  paddingLeft: "1.5rem",
@@ -6880,15 +6930,19 @@ function HazoUiDialog({
6880
6930
  ...!has_split_header && finalHeaderTextColor && { color: finalHeaderTextColor }
6881
6931
  };
6882
6932
  const titleClassName = cn(
6883
- "cls_dialog_title",
6884
- headerBar && "!text-white"
6933
+ "cls_dialog_title"
6885
6934
  );
6886
6935
  const descriptionClassName = cn(
6887
- "cls_dialog_description",
6888
- headerBar && "!text-white/80"
6936
+ "cls_dialog_description"
6889
6937
  );
6890
6938
  const bodyStyles = {
6891
- ...bodyBackgroundColor && { backgroundColor: bodyBackgroundColor }
6939
+ ...bodyBackgroundColor && { backgroundColor: bodyBackgroundColor },
6940
+ // kind="form" gets a comfortable body/label typography scale via the
6941
+ // hazo_theme --dialog-body-size token. --dialog-label-size is intentionally
6942
+ // not re-applied here: CSS custom properties inherit, so a consumer's own
6943
+ // <label> elements inside `children` can reference var(--dialog-label-size)
6944
+ // directly without any extra plumbing from this component.
6945
+ ...kind === "form" && { fontSize: "var(--dialog-body-size)" }
6892
6946
  };
6893
6947
  const footerStyles = {
6894
6948
  ...footerBackgroundColor && { backgroundColor: footerBackgroundColor }
@@ -6912,6 +6966,86 @@ function HazoUiDialog({
6912
6966
  ...finalCancelTextColor && { color: finalCancelTextColor },
6913
6967
  ...finalCancelBorderColor && { borderColor: finalCancelBorderColor }
6914
6968
  };
6969
+ if (is_confirm_kind) {
6970
+ const top_border_color = resolved_border_color ?? "hsl(var(--dialog-border))";
6971
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
6972
+ /* @__PURE__ */ jsxRuntime.jsx(
6973
+ DialogOverlay,
6974
+ {
6975
+ className: cn(
6976
+ "cls_confirm_dialog_overlay",
6977
+ overlayClassName ?? variant_preset?.overlay_class_name
6978
+ ),
6979
+ style: { backdropFilter: "blur(4px)" }
6980
+ }
6981
+ ),
6982
+ /* @__PURE__ */ jsxRuntime.jsxs(
6983
+ DialogPrimitive__namespace.Content,
6984
+ {
6985
+ className: cn(
6986
+ "cls_confirm_dialog_content",
6987
+ "fixed left-[50%] top-[50%] z-50",
6988
+ "translate-x-[-50%] translate-y-[-50%]",
6989
+ "bg-background shadow-lg",
6990
+ "duration-200",
6991
+ animationClasses,
6992
+ "overflow-hidden",
6993
+ className
6994
+ ),
6995
+ style: {
6996
+ width: "min(90vw, 420px)",
6997
+ borderRadius: "12px",
6998
+ borderTop: `4px solid ${top_border_color}`,
6999
+ boxShadow: "0 20px 60px rgba(0,0,0,0.15)",
7000
+ outline: "none"
7001
+ },
7002
+ onOpenAutoFocus: (e) => {
7003
+ if ((variant === "destructive" || variant === "warning") && showCancelButton && cancel_ref.current) {
7004
+ e.preventDefault();
7005
+ cancel_ref.current.focus();
7006
+ }
7007
+ },
7008
+ children: [
7009
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_body px-6 pt-7 pb-5", children: [
7010
+ /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Title, { className: "cls_confirm_dialog_title text-[17px] font-semibold text-foreground mb-2.5", children: title }),
7011
+ 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
7012
+ ] }),
7013
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_confirm_dialog_separator border-t border-border" }),
7014
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_actions px-6 py-4 flex justify-end gap-3", children: [
7015
+ showCancelButton && /* @__PURE__ */ jsxRuntime.jsx(
7016
+ Button,
7017
+ {
7018
+ ref: cancel_ref,
7019
+ type: "button",
7020
+ className: "cls_confirm_cancel_button focus-visible:ring-0 focus-visible:ring-offset-0",
7021
+ variant: "outline",
7022
+ onClick: handleCancel,
7023
+ disabled: actionButtonLoading,
7024
+ style: cancelButtonStyles,
7025
+ children: cancelButtonText
7026
+ }
7027
+ ),
7028
+ showActionButton && /* @__PURE__ */ jsxRuntime.jsxs(
7029
+ Button,
7030
+ {
7031
+ type: "button",
7032
+ className: "cls_confirm_action_button",
7033
+ variant: resolved_action_button_variant,
7034
+ onClick: handleConfirm,
7035
+ disabled: actionButtonLoading || actionButtonDisabled,
7036
+ style: actionButtonStyles,
7037
+ children: [
7038
+ actionButtonLoading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
7039
+ actionButtonText
7040
+ ]
7041
+ }
7042
+ )
7043
+ ] })
7044
+ ]
7045
+ }
7046
+ )
7047
+ ] }) });
7048
+ }
6915
7049
  return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
6916
7050
  /* @__PURE__ */ jsxRuntime.jsx(
6917
7051
  DialogOverlay,
@@ -6986,10 +7120,17 @@ function HazoUiDialog({
6986
7120
  style: headerStyles,
6987
7121
  children: [
6988
7122
  /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: titleClassName, children: title }),
6989
- description && /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { className: cn(
6990
- descriptionClassName,
6991
- has_merged_variant_header && "italic text-xs mt-1"
6992
- ), children: description })
7123
+ description && /* @__PURE__ */ jsxRuntime.jsx(
7124
+ DialogDescription,
7125
+ {
7126
+ className: cn(
7127
+ descriptionClassName,
7128
+ has_merged_variant_header && "italic text-xs mt-1"
7129
+ ),
7130
+ style: headerBar ? { opacity: 0.8 } : void 0,
7131
+ children: description
7132
+ }
7133
+ )
6993
7134
  ]
6994
7135
  }
6995
7136
  )
@@ -7055,7 +7196,7 @@ function HazoUiDialog({
7055
7196
  DialogPrimitive__namespace.Close,
7056
7197
  {
7057
7198
  className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground",
7058
- style: headerBar ? { color: "white" } : finalHeaderTextColor ? { color: finalHeaderTextColor } : void 0,
7199
+ style: headerBar ? { color: "var(--dialog-header-bar-fg)" } : finalHeaderTextColor ? { color: finalHeaderTextColor } : void 0,
7059
7200
  children: [
7060
7201
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }),
7061
7202
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
@@ -7067,28 +7208,6 @@ function HazoUiDialog({
7067
7208
  )
7068
7209
  ] }) });
7069
7210
  }
7070
- var CONFIRM_VARIANT_PRESETS = {
7071
- destructive: {
7072
- accent_color: "rgb(220, 38, 38)",
7073
- confirm_button_color: "rgb(220, 38, 38)",
7074
- overlay_class_name: "bg-red-950/50"
7075
- },
7076
- warning: {
7077
- accent_color: "rgb(245, 158, 11)",
7078
- confirm_button_color: "rgb(245, 158, 11)",
7079
- overlay_class_name: "bg-yellow-950/50"
7080
- },
7081
- info: {
7082
- accent_color: "rgb(37, 99, 235)",
7083
- confirm_button_color: "rgb(37, 99, 235)",
7084
- overlay_class_name: "bg-blue-950/50"
7085
- },
7086
- success: {
7087
- accent_color: "rgb(22, 163, 74)",
7088
- confirm_button_color: "rgb(22, 163, 74)",
7089
- overlay_class_name: "bg-green-950/50"
7090
- }
7091
- };
7092
7211
  function HazoUiConfirmDialog({
7093
7212
  open,
7094
7213
  onOpenChange,
@@ -7101,126 +7220,37 @@ function HazoUiConfirmDialog({
7101
7220
  showCancelButton = true,
7102
7221
  onConfirm,
7103
7222
  onCancel,
7104
- loading: external_loading,
7223
+ loading,
7105
7224
  disabled = false,
7106
7225
  accentColor,
7107
7226
  confirmButtonColor,
7108
7227
  openAnimation = "zoom",
7109
7228
  closeAnimation = "zoom"
7110
7229
  }) {
7111
- const [internal_loading, set_internal_loading] = React26__namespace.useState(false);
7112
- const config = get_hazo_ui_config();
7113
- const variant_preset = variant !== "default" ? CONFIRM_VARIANT_PRESETS[variant] : void 0;
7114
- const is_loading = external_loading ?? internal_loading;
7115
- const resolved_confirm_label = confirmLabel ?? (showCancelButton ? "Confirm" : "OK");
7116
- const resolved_accent_color = accentColor ?? variant_preset?.accent_color ?? "rgb(148, 163, 184)";
7117
- const resolved_confirm_button_color = confirmButtonColor ?? variant_preset?.confirm_button_color ?? config.submit_button_background_color;
7118
- const cancel_button_styles = {
7119
- ...config.cancel_button_background_color && { backgroundColor: config.cancel_button_background_color },
7120
- ...config.cancel_button_text_color && { color: config.cancel_button_text_color },
7121
- ...config.cancel_button_border_color && { borderColor: config.cancel_button_border_color }
7122
- };
7123
- const confirm_button_styles = {
7124
- ...resolved_confirm_button_color && {
7125
- backgroundColor: resolved_confirm_button_color,
7126
- borderColor: resolved_confirm_button_color,
7127
- color: "white"
7128
- }
7129
- };
7130
- const animation_classes = resolve_animation_classes(openAnimation, closeAnimation);
7131
- const handle_confirm = async () => {
7132
- try {
7133
- const result = onConfirm();
7134
- if (result instanceof Promise) {
7135
- set_internal_loading(true);
7136
- await result;
7137
- }
7138
- onOpenChange(false);
7139
- } catch {
7140
- } finally {
7141
- set_internal_loading(false);
7230
+ const resolved_accent_color = confirmButtonColor ?? accentColor;
7231
+ return /* @__PURE__ */ jsxRuntime.jsx(
7232
+ HazoUiDialog,
7233
+ {
7234
+ kind: "confirm",
7235
+ open,
7236
+ onOpenChange,
7237
+ title,
7238
+ description,
7239
+ variant,
7240
+ actionButtonText: confirmLabel,
7241
+ cancelButtonText: cancelLabel,
7242
+ showCancelButton,
7243
+ onConfirm,
7244
+ onCancel,
7245
+ actionButtonLoading: loading,
7246
+ actionButtonDisabled: disabled,
7247
+ accentColor: resolved_accent_color,
7248
+ openAnimation,
7249
+ closeAnimation,
7250
+ closeOnConfirm: true,
7251
+ children
7142
7252
  }
7143
- };
7144
- const handle_cancel = () => {
7145
- onCancel?.();
7146
- onOpenChange(false);
7147
- };
7148
- const cancel_ref = React26__namespace.useRef(null);
7149
- return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
7150
- /* @__PURE__ */ jsxRuntime.jsx(
7151
- DialogOverlay,
7152
- {
7153
- className: cn(
7154
- "cls_confirm_dialog_overlay",
7155
- variant_preset?.overlay_class_name
7156
- ),
7157
- style: { backdropFilter: "blur(4px)" }
7158
- }
7159
- ),
7160
- /* @__PURE__ */ jsxRuntime.jsxs(
7161
- DialogPrimitive__namespace.Content,
7162
- {
7163
- className: cn(
7164
- "cls_confirm_dialog_content",
7165
- "fixed left-[50%] top-[50%] z-50",
7166
- "translate-x-[-50%] translate-y-[-50%]",
7167
- "bg-background shadow-lg",
7168
- "duration-200",
7169
- animation_classes,
7170
- "overflow-hidden"
7171
- ),
7172
- style: {
7173
- width: "min(90vw, 420px)",
7174
- borderRadius: "12px",
7175
- borderTop: `4px solid ${resolved_accent_color}`,
7176
- boxShadow: "0 20px 60px rgba(0,0,0,0.15)",
7177
- outline: "none"
7178
- },
7179
- onOpenAutoFocus: (e) => {
7180
- if ((variant === "destructive" || variant === "warning") && showCancelButton && cancel_ref.current) {
7181
- e.preventDefault();
7182
- cancel_ref.current.focus();
7183
- }
7184
- },
7185
- children: [
7186
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_body px-6 pt-7 pb-5", children: [
7187
- /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Title, { className: "cls_confirm_dialog_title text-[17px] font-semibold text-foreground mb-2.5", children: title }),
7188
- 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
7189
- ] }),
7190
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cls_confirm_dialog_separator border-t border-border" }),
7191
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cls_confirm_dialog_actions px-6 py-4 flex justify-end gap-3", children: [
7192
- showCancelButton && /* @__PURE__ */ jsxRuntime.jsx(
7193
- Button,
7194
- {
7195
- ref: cancel_ref,
7196
- type: "button",
7197
- className: "cls_confirm_cancel_button focus-visible:ring-0 focus-visible:ring-offset-0",
7198
- variant: "outline",
7199
- onClick: handle_cancel,
7200
- disabled: is_loading,
7201
- style: cancel_button_styles,
7202
- children: cancelLabel
7203
- }
7204
- ),
7205
- /* @__PURE__ */ jsxRuntime.jsxs(
7206
- Button,
7207
- {
7208
- type: "button",
7209
- className: "cls_confirm_action_button",
7210
- onClick: handle_confirm,
7211
- disabled: is_loading || disabled,
7212
- style: confirm_button_styles,
7213
- children: [
7214
- is_loading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
7215
- resolved_confirm_label
7216
- ]
7217
- }
7218
- )
7219
- ] })
7220
- ]
7221
- }
7222
- )
7223
- ] }) });
7253
+ );
7224
7254
  }
7225
7255
  var Slider = React26__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
7226
7256
  SliderPrimitive__namespace.Root,
@@ -11452,6 +11482,173 @@ var ThemeToggle = React26__namespace.forwardRef(
11452
11482
  }
11453
11483
  );
11454
11484
  ThemeToggle.displayName = "ThemeToggle";
11485
+ function Logo({
11486
+ logoUrl,
11487
+ logoUrlDark,
11488
+ appTitle,
11489
+ href,
11490
+ height = 28,
11491
+ className,
11492
+ imgClassName
11493
+ }) {
11494
+ const alt = appTitle ?? "Logo";
11495
+ let content = null;
11496
+ if (logoUrl) {
11497
+ content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11498
+ /* @__PURE__ */ jsxRuntime.jsx(
11499
+ "img",
11500
+ {
11501
+ src: logoUrl,
11502
+ alt,
11503
+ height,
11504
+ style: { height },
11505
+ className: cn("object-contain", logoUrlDark && "dark:hidden", imgClassName)
11506
+ }
11507
+ ),
11508
+ logoUrlDark ? /* @__PURE__ */ jsxRuntime.jsx(
11509
+ "img",
11510
+ {
11511
+ src: logoUrlDark,
11512
+ alt,
11513
+ height,
11514
+ style: { height },
11515
+ className: cn("hidden object-contain dark:block", imgClassName)
11516
+ }
11517
+ ) : null
11518
+ ] });
11519
+ } else if (appTitle) {
11520
+ content = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-navbar-fg", children: appTitle });
11521
+ }
11522
+ if (content === null) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
11523
+ const resolvedHref = safeHref(href);
11524
+ if (resolvedHref) {
11525
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: resolvedHref, className: cn("inline-flex items-center gap-2", className), children: content });
11526
+ }
11527
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("inline-flex items-center gap-2", className), children: content });
11528
+ }
11529
+ function AppHeader({ identity, homeHref, navItems, actions, className }) {
11530
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11531
+ "header",
11532
+ {
11533
+ "aria-label": "Application header",
11534
+ className: cn(
11535
+ "flex w-full items-center justify-between gap-4 border-b bg-navbar text-navbar-fg border-navbar-border px-4 py-2.5",
11536
+ className
11537
+ ),
11538
+ children: [
11539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
11540
+ /* @__PURE__ */ jsxRuntime.jsx(
11541
+ Logo,
11542
+ {
11543
+ logoUrl: identity?.logoUrl,
11544
+ logoUrlDark: identity?.logoUrlDark,
11545
+ appTitle: identity?.appTitle,
11546
+ href: homeHref
11547
+ }
11548
+ ),
11549
+ navItems
11550
+ ] }),
11551
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: actions })
11552
+ ]
11553
+ }
11554
+ );
11555
+ }
11556
+ function AppFooter({ variant, footer, identity, className }) {
11557
+ const cfg = footer ?? identity?.footer?.[variant];
11558
+ if (variant === "app") {
11559
+ const links = cfg?.columns?.[0]?.links;
11560
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11561
+ "footer",
11562
+ {
11563
+ className: cn(
11564
+ "flex w-full items-center justify-between gap-4 border-t bg-footer-app text-footer-app-fg border-footer-app-border px-4 py-2 text-sm",
11565
+ className
11566
+ ),
11567
+ children: [
11568
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-footer-app-fg-muted", children: cfg?.copyright }),
11569
+ links && links.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: links.map((link, i) => /* @__PURE__ */ jsxRuntime.jsx(
11570
+ "a",
11571
+ {
11572
+ href: safeHref(link.href),
11573
+ className: "text-footer-app-fg-muted hover:text-footer-app-fg",
11574
+ children: link.label
11575
+ },
11576
+ `${link.href}-${i}`
11577
+ )) }) : null
11578
+ ]
11579
+ }
11580
+ );
11581
+ }
11582
+ const showBrandRow = Boolean(identity?.logoUrl || identity?.appTitle);
11583
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11584
+ "footer",
11585
+ {
11586
+ className: cn(
11587
+ "w-full border-t bg-footer-public text-footer-public-fg border-footer-public-border px-6 py-10",
11588
+ className
11589
+ ),
11590
+ children: [
11591
+ showBrandRow ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-8", children: /* @__PURE__ */ jsxRuntime.jsx(
11592
+ Logo,
11593
+ {
11594
+ logoUrl: identity?.logoUrl,
11595
+ logoUrlDark: identity?.logoUrlDark,
11596
+ appTitle: identity?.appTitle
11597
+ }
11598
+ ) }) : null,
11599
+ cfg?.columns && cfg.columns.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-8 md:grid-cols-4", children: cfg.columns.map((column, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
11600
+ column.heading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-footer-public-fg", children: column.heading }) : null,
11601
+ column.links.map((link, i) => /* @__PURE__ */ jsxRuntime.jsx(
11602
+ "a",
11603
+ {
11604
+ href: safeHref(link.href),
11605
+ className: "text-footer-public-fg-muted hover:text-footer-public-fg",
11606
+ children: link.label
11607
+ },
11608
+ `${link.href}-${i}`
11609
+ ))
11610
+ ] }, column.heading ?? idx)) }) : null,
11611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-between gap-4", children: [
11612
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-footer-public-fg-muted", children: cfg?.copyright }),
11613
+ cfg?.social ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
11614
+ cfg.social.github ? /* @__PURE__ */ jsxRuntime.jsx(
11615
+ "a",
11616
+ {
11617
+ href: safeHref(cfg.social.github),
11618
+ className: "text-footer-public-fg-muted hover:text-footer-public-fg",
11619
+ children: "GitHub"
11620
+ }
11621
+ ) : null,
11622
+ cfg.social.twitter ? /* @__PURE__ */ jsxRuntime.jsx(
11623
+ "a",
11624
+ {
11625
+ href: safeHref(cfg.social.twitter),
11626
+ className: "text-footer-public-fg-muted hover:text-footer-public-fg",
11627
+ children: "Twitter"
11628
+ }
11629
+ ) : null,
11630
+ cfg.social.linkedin ? /* @__PURE__ */ jsxRuntime.jsx(
11631
+ "a",
11632
+ {
11633
+ href: safeHref(cfg.social.linkedin),
11634
+ className: "text-footer-public-fg-muted hover:text-footer-public-fg",
11635
+ children: "LinkedIn"
11636
+ }
11637
+ ) : null,
11638
+ cfg.social.website ? /* @__PURE__ */ jsxRuntime.jsx(
11639
+ "a",
11640
+ {
11641
+ href: safeHref(cfg.social.website),
11642
+ className: "text-footer-public-fg-muted hover:text-footer-public-fg",
11643
+ children: "Website"
11644
+ }
11645
+ ) : null
11646
+ ] }) : null
11647
+ ] })
11648
+ ]
11649
+ }
11650
+ );
11651
+ }
11455
11652
 
11456
11653
  Object.defineProperty(exports, "rawToast", {
11457
11654
  enumerable: true,
@@ -11473,6 +11670,8 @@ exports.AlertDialogOverlay = AlertDialogOverlay;
11473
11670
  exports.AlertDialogPortal = AlertDialogPortal;
11474
11671
  exports.AlertDialogTitle = AlertDialogTitle;
11475
11672
  exports.AlertDialogTrigger = AlertDialogTrigger;
11673
+ exports.AppFooter = AppFooter;
11674
+ exports.AppHeader = AppHeader;
11476
11675
  exports.Button = Button;
11477
11676
  exports.ButtonGroup = ButtonGroup;
11478
11677
  exports.ButtonGroupSeparator = ButtonGroupSeparator;
@@ -11563,6 +11762,7 @@ exports.InputAffix = InputAffix;
11563
11762
  exports.InverseSparkline = InverseSparkline;
11564
11763
  exports.Label = Label3;
11565
11764
  exports.LoadingTimeout = LoadingTimeout;
11765
+ exports.Logo = Logo;
11566
11766
  exports.MarkdownEditor = MarkdownEditor;
11567
11767
  exports.NotificationCountBadge = NotificationCountBadge;
11568
11768
  exports.NotificationItem = NotificationItem;
@@ -11641,6 +11841,7 @@ exports.parse_commands_from_text = parse_commands_from_text;
11641
11841
  exports.pick_x_label_indices = pick_x_label_indices;
11642
11842
  exports.reset_hazo_ui_config = reset_hazo_ui_config;
11643
11843
  exports.resolve_animation_classes = resolve_animation_classes;
11844
+ exports.safeHref = safeHref;
11644
11845
  exports.set_hazo_ui_config = set_hazo_ui_config;
11645
11846
  exports.set_logger = set_logger;
11646
11847
  exports.successToast = successToast;