@underverse-ui/underverse 1.0.94 → 1.0.96

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.94",
3
+ "version": "1.0.96",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 225,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -2930,20 +2930,10 @@ function useHydrated() {
2930
2930
  return (0, import_react5.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
2931
2931
  }
2932
2932
 
2933
- // src/components/Tooltip.tsx
2934
- var import_jsx_runtime10 = require("react/jsx-runtime");
2935
- var variantStyles2 = {
2936
- default: "bg-popover text-popover-foreground border-border/50",
2937
- info: "bg-info text-info-foreground border-info/20",
2938
- warning: "bg-warning text-warning-foreground border-warning/20",
2939
- error: "bg-destructive text-destructive-foreground border-destructive/20",
2940
- success: "bg-success text-success-foreground border-success/20"
2941
- };
2942
- var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2943
- function composeEventHandlers(theirHandler, ourHandler) {
2933
+ // src/utils/react-compose.ts
2934
+ function chainEventHandlers(...handlers) {
2944
2935
  return (event) => {
2945
- theirHandler?.(event);
2946
- ourHandler(event);
2936
+ handlers.forEach((handler) => handler?.(event));
2947
2937
  };
2948
2938
  }
2949
2939
  function setRefValue(ref, value) {
@@ -2959,6 +2949,17 @@ function mergeRefs(...refs) {
2959
2949
  refs.forEach((ref) => setRefValue(ref, value));
2960
2950
  };
2961
2951
  }
2952
+
2953
+ // src/components/Tooltip.tsx
2954
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2955
+ var variantStyles2 = {
2956
+ default: "bg-popover text-popover-foreground border-border/50",
2957
+ info: "bg-info text-info-foreground border-info/20",
2958
+ warning: "bg-warning text-warning-foreground border-warning/20",
2959
+ error: "bg-destructive text-destructive-foreground border-destructive/20",
2960
+ success: "bg-success text-success-foreground border-success/20"
2961
+ };
2962
+ var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2962
2963
  function getTransformOrigin(side) {
2963
2964
  switch (side) {
2964
2965
  case "top":
@@ -3003,7 +3004,7 @@ function computeTooltipPosition(args) {
3003
3004
  top = clamp(top, padding, viewport.height - contentSize.height - padding);
3004
3005
  return { top, left, side };
3005
3006
  }
3006
- var Tooltip = ({
3007
+ var Tooltip = React9.forwardRef(({
3007
3008
  children,
3008
3009
  content,
3009
3010
  placement = "top",
@@ -3011,8 +3012,9 @@ var Tooltip = ({
3011
3012
  className,
3012
3013
  disabled = false,
3013
3014
  variant = "default",
3014
- asChild = true
3015
- }) => {
3015
+ asChild = true,
3016
+ ...triggerPassthroughProps
3017
+ }, forwardedRef) => {
3016
3018
  const [isOpen, setIsOpen] = React9.useState(false);
3017
3019
  const isMounted = useHydrated();
3018
3020
  const triggerRef = React9.useRef(null);
@@ -3127,31 +3129,53 @@ var Tooltip = ({
3127
3129
  if (panelRef.current) ro.observe(panelRef.current);
3128
3130
  return () => ro.disconnect();
3129
3131
  }, [isOpen, updatePosition]);
3132
+ const childProps = children.props;
3133
+ const childRef = childProps.ref;
3134
+ const passthroughRef = mergeRefs(forwardedRef, childRef, (node) => {
3135
+ triggerRef.current = node;
3136
+ });
3130
3137
  if (disabled || !content) {
3131
- return children;
3138
+ if (!asChild) return children;
3139
+ return React9.cloneElement(children, {
3140
+ ...triggerPassthroughProps,
3141
+ ref: passthroughRef
3142
+ });
3132
3143
  }
3133
- const childProps = children.props;
3134
- const childRef = children.ref ?? childProps.ref;
3135
3144
  const triggerProps = {
3136
- ref: mergeRefs(childRef, (node) => {
3137
- triggerRef.current = node;
3138
- }),
3145
+ ...triggerPassthroughProps,
3146
+ ref: passthroughRef,
3139
3147
  "data-underverse-tooltip-trigger": triggerSelector,
3140
- onMouseEnter: composeEventHandlers(childProps.onMouseEnter, (e) => {
3141
- triggerRef.current = e.currentTarget;
3142
- handleMouseEnter();
3143
- }),
3144
- onMouseLeave: composeEventHandlers(childProps.onMouseLeave, (e) => {
3145
- triggerRef.current = e.currentTarget;
3146
- handleMouseLeave();
3147
- }),
3148
- onFocus: composeEventHandlers(childProps.onFocus, (e) => {
3149
- triggerRef.current = e.currentTarget;
3150
- handleFocus();
3151
- }),
3152
- onBlur: composeEventHandlers(childProps.onBlur, () => {
3153
- handleBlur();
3154
- })
3148
+ onMouseEnter: chainEventHandlers(
3149
+ triggerPassthroughProps.onMouseEnter,
3150
+ childProps.onMouseEnter,
3151
+ (e) => {
3152
+ triggerRef.current = e.currentTarget;
3153
+ handleMouseEnter();
3154
+ }
3155
+ ),
3156
+ onMouseLeave: chainEventHandlers(
3157
+ triggerPassthroughProps.onMouseLeave,
3158
+ childProps.onMouseLeave,
3159
+ (e) => {
3160
+ triggerRef.current = e.currentTarget;
3161
+ handleMouseLeave();
3162
+ }
3163
+ ),
3164
+ onFocus: chainEventHandlers(
3165
+ triggerPassthroughProps.onFocus,
3166
+ childProps.onFocus,
3167
+ (e) => {
3168
+ triggerRef.current = e.currentTarget;
3169
+ handleFocus();
3170
+ }
3171
+ ),
3172
+ onBlur: chainEventHandlers(
3173
+ triggerPassthroughProps.onBlur,
3174
+ childProps.onBlur,
3175
+ (e) => {
3176
+ handleBlur();
3177
+ }
3178
+ )
3155
3179
  };
3156
3180
  const trigger = asChild ? React9.cloneElement(children, triggerProps) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { ...triggerProps, children });
3157
3181
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
@@ -3195,7 +3219,8 @@ var Tooltip = ({
3195
3219
  document.body
3196
3220
  )
3197
3221
  ] });
3198
- };
3222
+ });
3223
+ Tooltip.displayName = "Tooltip";
3199
3224
 
3200
3225
  // src/components/emoji-ui.tsx
3201
3226
  var import_jsx_runtime11 = require("react/jsx-runtime");
@@ -6120,36 +6145,32 @@ var Popover = ({
6120
6145
  ) : null;
6121
6146
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
6122
6147
  (() => {
6123
- const TriggerComponent = trigger.type;
6124
6148
  const triggerProps = trigger.props;
6125
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6126
- TriggerComponent,
6127
- {
6128
- ...triggerProps,
6129
- "data-underverse-popover-trigger": triggerSelector,
6130
- onClick: (e) => {
6149
+ const childRef = triggerProps.ref;
6150
+ return React18.cloneElement(trigger, {
6151
+ ...triggerProps,
6152
+ ref: mergeRefs(childRef, (node) => {
6153
+ triggerRef.current = node;
6154
+ }),
6155
+ "data-underverse-popover-trigger": triggerSelector,
6156
+ onClick: chainEventHandlers(
6157
+ (e) => {
6131
6158
  triggerRef.current = e.currentTarget;
6132
6159
  e.preventDefault();
6133
6160
  e.stopPropagation();
6134
6161
  handleTriggerClick();
6135
- if (typeof triggerProps.onClick === "function") {
6136
- triggerProps.onClick(e);
6137
- }
6138
6162
  },
6139
- onFocus: (e) => {
6163
+ triggerProps.onClick
6164
+ ),
6165
+ onFocus: chainEventHandlers(
6166
+ (e) => {
6140
6167
  triggerRef.current = e.currentTarget;
6141
- if (typeof triggerProps.onFocus === "function") {
6142
- triggerProps.onFocus(e);
6143
- }
6144
6168
  },
6145
- "aria-expanded": isOpen,
6146
- "aria-haspopup": triggerProps["aria-haspopup"] ?? "dialog",
6147
- className: cn(
6148
- triggerProps.className,
6149
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
6150
- )
6151
- }
6152
- );
6169
+ triggerProps.onFocus
6170
+ ),
6171
+ "aria-expanded": isOpen,
6172
+ "aria-haspopup": triggerProps["aria-haspopup"] ?? "dialog"
6173
+ });
6153
6174
  })(),
6154
6175
  popoverContent
6155
6176
  ] });
@@ -6772,6 +6793,32 @@ var variantStyles5 = {
6772
6793
  inactiveTab: "text-muted-foreground hover:text-foreground"
6773
6794
  }
6774
6795
  };
6796
+ function getTabsBaseId(tabs) {
6797
+ const key = tabs.map((t) => t.value).join("-");
6798
+ return `tabs-${key || "default"}`;
6799
+ }
6800
+ function getTabTriggerId(baseId, index) {
6801
+ return `${baseId}-tab-${index}`;
6802
+ }
6803
+ function getTabPanelId(baseId, index) {
6804
+ return `${baseId}-panel-${index}`;
6805
+ }
6806
+ function getTabHref(tab, panelId) {
6807
+ return tab.href ?? `#${panelId}`;
6808
+ }
6809
+ function resolveTabValueFromHash(hash, tabs, baseId) {
6810
+ const normalizedHash = hash.replace(/^#/, "");
6811
+ if (!normalizedHash) return null;
6812
+ const matchIndex = tabs.findIndex((tab, index) => {
6813
+ const tabId = getTabTriggerId(baseId, index);
6814
+ const panelId = getTabPanelId(baseId, index);
6815
+ return normalizedHash === tabId || normalizedHash === panelId;
6816
+ });
6817
+ return matchIndex >= 0 ? tabs[matchIndex]?.value ?? null : null;
6818
+ }
6819
+ function shouldHandleTabClickLocally(event, target) {
6820
+ return !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || target === "_blank");
6821
+ }
6775
6822
  var Tabs = ({
6776
6823
  tabs,
6777
6824
  defaultValue,
@@ -6789,14 +6836,33 @@ var Tabs = ({
6789
6836
  const [active, setActive] = React22.useState(defaultValue || tabs[0]?.value);
6790
6837
  const [underlineStyle, setUnderlineStyle] = React22.useState({});
6791
6838
  const tabRefs = React22.useRef([]);
6792
- const baseId = React22.useMemo(() => {
6793
- const key = tabs.map((t) => t.value).join("-");
6794
- return `tabs-${key || "default"}`;
6795
- }, [tabs]);
6839
+ const baseId = React22.useMemo(() => getTabsBaseId(tabs), [tabs]);
6796
6840
  const handleTabChange = (value) => {
6797
6841
  setActive(value);
6798
6842
  onTabChange?.(value);
6799
6843
  };
6844
+ const handleTabKeyDown = (event) => {
6845
+ const count = tabs.length;
6846
+ const idx = tabs.findIndex((t) => t.value === active);
6847
+ let next = idx;
6848
+ if (orientation === "horizontal") {
6849
+ if (event.key === "ArrowRight") next = (idx + 1) % count;
6850
+ if (event.key === "ArrowLeft") next = (idx - 1 + count) % count;
6851
+ } else {
6852
+ if (event.key === "ArrowDown") next = (idx + 1) % count;
6853
+ if (event.key === "ArrowUp") next = (idx - 1 + count) % count;
6854
+ }
6855
+ if (event.key === "Home") next = 0;
6856
+ if (event.key === "End") next = count - 1;
6857
+ if (next !== idx) {
6858
+ event.preventDefault();
6859
+ const nextTab = tabs[next];
6860
+ if (!nextTab?.disabled) {
6861
+ handleTabChange(nextTab.value);
6862
+ }
6863
+ tabRefs.current[next]?.focus();
6864
+ }
6865
+ };
6800
6866
  React22.useEffect(() => {
6801
6867
  if (variant === "underline" && orientation === "horizontal") {
6802
6868
  const activeIndex2 = tabs.findIndex((tab) => tab.value === active);
@@ -6810,6 +6876,18 @@ var Tabs = ({
6810
6876
  }
6811
6877
  }
6812
6878
  }, [active, variant, orientation, tabs]);
6879
+ React22.useEffect(() => {
6880
+ if (typeof window === "undefined") return;
6881
+ const syncFromHash = () => {
6882
+ const nextValue = resolveTabValueFromHash(window.location.hash, tabs, baseId);
6883
+ if (nextValue) {
6884
+ setActive(nextValue);
6885
+ }
6886
+ };
6887
+ syncFromHash();
6888
+ window.addEventListener("hashchange", syncFromHash);
6889
+ return () => window.removeEventListener("hashchange", syncFromHash);
6890
+ }, [baseId, tabs]);
6813
6891
  const containerClasses = cn(
6814
6892
  "relative",
6815
6893
  orientation === "horizontal" ? "w-full flex space-x-1 overflow-x-auto" : "flex flex-col space-y-1 shrink-0",
@@ -6832,59 +6910,69 @@ var Tabs = ({
6832
6910
  tabs.map((tab, index) => {
6833
6911
  const isActive = active === tab.value;
6834
6912
  const Icon = tab.icon;
6835
- const tabId2 = `${baseId}-tab-${index}`;
6836
- const panelId2 = `${baseId}-panel-${index}`;
6913
+ const tabId2 = getTabTriggerId(baseId, index);
6914
+ const panelId2 = getTabPanelId(baseId, index);
6915
+ const tabHref = getTabHref(tab, panelId2);
6916
+ const sharedClassName = cn(
6917
+ "font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
6918
+ "focus:outline-none focus-visible:outline-none",
6919
+ "disabled:opacity-50 disabled:cursor-not-allowed",
6920
+ sizeStyles6[size].tab,
6921
+ variantStyles5[variant].tab,
6922
+ isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
6923
+ orientation === "vertical" && "justify-start w-full",
6924
+ stretch && orientation === "horizontal" && "flex-1 justify-center",
6925
+ tab.href && tab.disabled && "pointer-events-none cursor-not-allowed opacity-50"
6926
+ );
6927
+ const sharedStyle = {
6928
+ boxShadow: "none",
6929
+ transform: "none",
6930
+ outline: "none",
6931
+ border: "none"
6932
+ };
6933
+ const sharedProps = {
6934
+ ref: (el) => {
6935
+ tabRefs.current[index] = el;
6936
+ },
6937
+ id: tabId2,
6938
+ role: "tab",
6939
+ "aria-selected": isActive,
6940
+ "aria-controls": panelId2,
6941
+ tabIndex: isActive ? 0 : -1,
6942
+ className: sharedClassName,
6943
+ style: sharedStyle,
6944
+ onKeyDown: handleTabKeyDown
6945
+ };
6946
+ if (!tab.disabled) {
6947
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6948
+ "a",
6949
+ {
6950
+ ...sharedProps,
6951
+ href: tabHref,
6952
+ target: tab.target,
6953
+ rel: tab.rel,
6954
+ onClick: (event) => {
6955
+ if (shouldHandleTabClickLocally(event, tab.target)) {
6956
+ event.preventDefault();
6957
+ handleTabChange(tab.value);
6958
+ }
6959
+ },
6960
+ className: cn(sharedClassName, "no-underline"),
6961
+ children: [
6962
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { className: "h-4 w-4" }),
6963
+ tab.label
6964
+ ]
6965
+ },
6966
+ tab.value
6967
+ );
6968
+ }
6837
6969
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6838
6970
  "button",
6839
6971
  {
6840
- ref: (el) => {
6841
- tabRefs.current[index] = el;
6842
- },
6972
+ ...sharedProps,
6843
6973
  onClick: () => !tab.disabled && handleTabChange(tab.value),
6844
6974
  disabled: tab.disabled,
6845
- style: {
6846
- boxShadow: "none",
6847
- transform: "none",
6848
- outline: "none",
6849
- border: "none"
6850
- },
6851
- className: cn(
6852
- "font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
6853
- "focus:outline-none focus-visible:outline-none",
6854
- "disabled:opacity-50 disabled:cursor-not-allowed",
6855
- "border-0 bg-transparent outline-none",
6856
- // Reset button default styles
6857
- sizeStyles6[size].tab,
6858
- variantStyles5[variant].tab,
6859
- isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
6860
- orientation === "vertical" && "justify-start w-full",
6861
- stretch && orientation === "horizontal" && "flex-1 justify-center"
6862
- ),
6863
- role: "tab",
6864
- id: tabId2,
6865
- "aria-selected": isActive,
6866
- "aria-controls": panelId2,
6867
- tabIndex: isActive ? 0 : -1,
6868
- onKeyDown: (e) => {
6869
- const count = tabs.length;
6870
- const idx = tabs.findIndex((t) => t.value === active);
6871
- let next = idx;
6872
- if (orientation === "horizontal") {
6873
- if (e.key === "ArrowRight") next = (idx + 1) % count;
6874
- if (e.key === "ArrowLeft") next = (idx - 1 + count) % count;
6875
- } else {
6876
- if (e.key === "ArrowDown") next = (idx + 1) % count;
6877
- if (e.key === "ArrowUp") next = (idx - 1 + count) % count;
6878
- }
6879
- if (e.key === "Home") next = 0;
6880
- if (e.key === "End") next = count - 1;
6881
- if (next !== idx) {
6882
- e.preventDefault();
6883
- const nextVal = tabs[next].value;
6884
- handleTabChange(nextVal);
6885
- tabRefs.current[next]?.focus();
6886
- }
6887
- },
6975
+ className: cn(sharedClassName, "border-0 bg-transparent outline-none"),
6888
6976
  children: [
6889
6977
  Icon && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { className: "h-4 w-4" }),
6890
6978
  tab.label
@@ -7028,17 +7116,18 @@ var DropdownMenu = ({
7028
7116
  index
7029
7117
  );
7030
7118
  }) : children });
7031
- const TriggerComponent = trigger.type;
7032
7119
  const triggerProps = trigger.props;
7033
- const enhancedTrigger = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
7034
- TriggerComponent,
7035
- {
7036
- ...triggerProps,
7037
- "aria-haspopup": "menu",
7038
- "aria-expanded": open,
7039
- onKeyDown: (e) => {
7040
- triggerRef.current = e.currentTarget;
7041
- if (disabled) return;
7120
+ const childRef = triggerProps.ref;
7121
+ const enhancedTrigger = import_react14.default.cloneElement(trigger, {
7122
+ ...triggerProps,
7123
+ ref: mergeRefs(childRef, (node) => {
7124
+ triggerRef.current = node;
7125
+ }),
7126
+ "aria-haspopup": "menu",
7127
+ "aria-expanded": open,
7128
+ onKeyDown: chainEventHandlers((e) => {
7129
+ triggerRef.current = e.currentTarget;
7130
+ if (!disabled) {
7042
7131
  if (e.key === "ArrowDown") {
7043
7132
  e.preventDefault();
7044
7133
  setOpen(true);
@@ -7054,22 +7143,12 @@ var DropdownMenu = ({
7054
7143
  e.preventDefault();
7055
7144
  setOpen(false);
7056
7145
  }
7057
- if (typeof triggerProps.onKeyDown === "function") {
7058
- triggerProps.onKeyDown(e);
7059
- }
7060
- },
7061
- onFocus: (e) => {
7062
- triggerRef.current = e.currentTarget;
7063
- if (typeof triggerProps.onFocus === "function") {
7064
- triggerProps.onFocus(e);
7065
- }
7066
- },
7067
- className: cn(
7068
- triggerProps.className,
7069
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
7070
- )
7071
- }
7072
- );
7146
+ }
7147
+ }, triggerProps.onKeyDown),
7148
+ onFocus: chainEventHandlers((e) => {
7149
+ triggerRef.current = e.currentTarget;
7150
+ }, triggerProps.onFocus)
7151
+ });
7073
7152
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
7074
7153
  Popover,
7075
7154
  {