infinity-ui-elements 1.8.63 → 1.9.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.js CHANGED
@@ -974,7 +974,7 @@ const alertTextVariants = classVarianceAuthority.cva("", {
974
974
  intent: "info",
975
975
  },
976
976
  });
977
- const getDefaultIcon = (intent, emphasis) => {
977
+ const getDefaultIcon$1 = (intent, emphasis) => {
978
978
  const iconSize = 16;
979
979
  switch (emphasis) {
980
980
  case "intense":
@@ -1022,7 +1022,7 @@ const getActionLink = (actionButtonText, onActionLinkClick, emphasis, intent) =>
1022
1022
  return (jsxRuntime.jsx(Link, { type: "action", color: intent, size: "small", onClick: onActionLinkClick, children: actionButtonText }));
1023
1023
  };
1024
1024
  const Alert = React__namespace.forwardRef(({ className, emphasis = "subtle", intent = "info", title, children, isFullWidth = false, onClose, icon, actionButtonText, onActionButtonClick, actionLinkText, onActionLinkClick, ...props }, ref) => {
1025
- const displayIcon = icon ?? getDefaultIcon(intent, emphasis);
1025
+ const displayIcon = icon ?? getDefaultIcon$1(intent, emphasis);
1026
1026
  const actionButton = getActionButton(actionButtonText, onActionButtonClick, emphasis, intent);
1027
1027
  const actionLink = getActionLink(actionLinkText, onActionLinkClick, emphasis, intent);
1028
1028
  const hasActions = actionButton || actionLink;
@@ -2026,19 +2026,19 @@ const FormHeader = React__namespace.forwardRef(({ label, size = "medium", isOpti
2026
2026
  // Size-based configurations
2027
2027
  const sizeConfig = {
2028
2028
  small: {
2029
- textClassName: "text-body-xsmall-semibold",
2029
+ textClassName: "text-body-xsmall-medium",
2030
2030
  textClassNameRegular: "text-caption-small-regular",
2031
2031
  iconSize: 12,
2032
2032
  gap: "gap-1.5",
2033
2033
  },
2034
2034
  medium: {
2035
- textClassName: "text-body-small-semibold",
2035
+ textClassName: "text-body-small-medium",
2036
2036
  textClassNameRegular: "text-caption-medium-regular",
2037
2037
  iconSize: 14,
2038
2038
  gap: "gap-2",
2039
2039
  },
2040
2040
  large: {
2041
- textClassName: "text-body-medium-semibold",
2041
+ textClassName: "text-body-medium-medium",
2042
2042
  textClassNameRegular: "text-caption-large-regular",
2043
2043
  iconSize: 16,
2044
2044
  gap: "gap-2.5",
@@ -4867,6 +4867,339 @@ const TextArea = React__namespace.forwardRef(({ label, helperText, errorText, su
4867
4867
  });
4868
4868
  TextArea.displayName = "TextArea";
4869
4869
 
4870
+ const toastVariants = classVarianceAuthority.cva("relative flex items-center gap-3 p-4 rounded-large shadow-lg min-w-[360px] max-w-[480px] transition-all", {
4871
+ variants: {
4872
+ type: {
4873
+ information: "",
4874
+ promotional: "bg-surface-fill-neutral-intense",
4875
+ },
4876
+ intent: {
4877
+ neutral: "",
4878
+ positive: "",
4879
+ negative: "",
4880
+ notice: "",
4881
+ info: "",
4882
+ },
4883
+ },
4884
+ compoundVariants: [
4885
+ // Information type with different intents
4886
+ {
4887
+ type: "information",
4888
+ intent: "neutral",
4889
+ class: "bg-feedback-fill-neutral-intense",
4890
+ },
4891
+ {
4892
+ type: "information",
4893
+ intent: "positive",
4894
+ class: "bg-feedback-fill-positive-intense",
4895
+ },
4896
+ {
4897
+ type: "information",
4898
+ intent: "negative",
4899
+ class: "bg-feedback-fill-negative-intense",
4900
+ },
4901
+ {
4902
+ type: "information",
4903
+ intent: "notice",
4904
+ class: "bg-feedback-fill-notice-intense",
4905
+ },
4906
+ {
4907
+ type: "information",
4908
+ intent: "info",
4909
+ class: "bg-feedback-fill-info-intense",
4910
+ },
4911
+ ],
4912
+ defaultVariants: {
4913
+ type: "information",
4914
+ intent: "neutral",
4915
+ },
4916
+ });
4917
+ const toastIconVariants = classVarianceAuthority.cva("flex-shrink-0 flex items-center justify-center", {
4918
+ variants: {
4919
+ type: {
4920
+ information: "text-white",
4921
+ promotional: "text-action-ink-neutral-normal",
4922
+ },
4923
+ },
4924
+ defaultVariants: {
4925
+ type: "information",
4926
+ },
4927
+ });
4928
+ const toastTextVariants = classVarianceAuthority.cva("text-body-small-regular", {
4929
+ variants: {
4930
+ type: {
4931
+ information: "text-white",
4932
+ promotional: "text-surface-ink-neutral-normal",
4933
+ },
4934
+ },
4935
+ defaultVariants: {
4936
+ type: "information",
4937
+ },
4938
+ });
4939
+ const toastLinkVariants = classVarianceAuthority.cva("text-body-small-medium cursor-pointer hover:underline", {
4940
+ variants: {
4941
+ type: {
4942
+ information: "text-white",
4943
+ promotional: "text-action-ink-primary-normal hover:text-action-ink-primary-subtle",
4944
+ },
4945
+ },
4946
+ defaultVariants: {
4947
+ type: "information",
4948
+ },
4949
+ });
4950
+ const toastCloseButtonVariants = classVarianceAuthority.cva("flex-shrink-0 cursor-pointer hover:opacity-75 transition-opacity", {
4951
+ variants: {
4952
+ type: {
4953
+ information: "text-white",
4954
+ promotional: "text-action-ink-neutral-subtle",
4955
+ },
4956
+ },
4957
+ defaultVariants: {
4958
+ type: "information",
4959
+ },
4960
+ });
4961
+ const getDefaultIcon = (type, intent) => {
4962
+ const iconSize = 16;
4963
+ if (type === "promotional") {
4964
+ return jsxRuntime.jsx(Icon, { name: "info", size: iconSize, "aria-hidden": "true" });
4965
+ }
4966
+ // Information type icons based on intent
4967
+ switch (intent) {
4968
+ case "positive":
4969
+ return jsxRuntime.jsx(Icon, { name: "check", size: iconSize, "aria-hidden": "true" });
4970
+ case "negative":
4971
+ return jsxRuntime.jsx(Icon, { name: "exclamation", size: iconSize, "aria-hidden": "true" });
4972
+ case "notice":
4973
+ return jsxRuntime.jsx(Icon, { name: "info", size: iconSize, "aria-hidden": "true" });
4974
+ case "info":
4975
+ return jsxRuntime.jsx(Icon, { name: "info", size: iconSize, "aria-hidden": "true" });
4976
+ case "neutral":
4977
+ default:
4978
+ return jsxRuntime.jsx(Icon, { name: "check", size: iconSize, "aria-hidden": "true" });
4979
+ }
4980
+ };
4981
+ const Toast = React__namespace.forwardRef(({ className, type = "information", intent = "neutral", icon, message, heading, description, linkText, onLinkClick, buttonText, onButtonClick, onClose, duration = 5000, isVisible = true, ...props }, ref) => {
4982
+ const [visible, setVisible] = React__namespace.useState(isVisible);
4983
+ const timerRef = React__namespace.useRef(null);
4984
+ React__namespace.useEffect(() => {
4985
+ setVisible(isVisible);
4986
+ }, [isVisible]);
4987
+ React__namespace.useEffect(() => {
4988
+ // Auto-dismiss only for information type
4989
+ if (type === "information" && visible && duration > 0) {
4990
+ timerRef.current = window.setTimeout(() => {
4991
+ setVisible(false);
4992
+ onClose?.();
4993
+ }, duration);
4994
+ }
4995
+ return () => {
4996
+ if (timerRef.current) {
4997
+ window.clearTimeout(timerRef.current);
4998
+ }
4999
+ };
5000
+ }, [type, visible, duration, onClose]);
5001
+ const handleClose = () => {
5002
+ setVisible(false);
5003
+ onClose?.();
5004
+ };
5005
+ const displayIcon = icon ?? getDefaultIcon(type, intent);
5006
+ if (!visible) {
5007
+ return null;
5008
+ }
5009
+ // Promotional type layout
5010
+ if (type === "promotional") {
5011
+ return (jsxRuntime.jsxs("div", { ref: ref, className: cn(toastVariants({ type, intent }), "flex-row items-start gap-3", className), role: "alert", ...props, children: [jsxRuntime.jsx("div", { className: "py-1", children: jsxRuntime.jsx("div", { className: cn(toastIconVariants({ type }), "h-5 w-5 shrink-0"), children: displayIcon }) }), jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: heading && (jsxRuntime.jsx("div", { className: "text-body-small-semibold text-surface-ink-neutral-normal", children: heading })) }), onClose && (jsxRuntime.jsx("div", { className: cn(toastCloseButtonVariants({ type }), "h-5 w-5"), onClick: handleClose, role: "button", "aria-label": "Close toast", children: jsxRuntime.jsx(Icon, { name: "close", size: 16 }) }))] }), description && (jsxRuntime.jsx("div", { className: "text-body-small-regular text-surface-ink-neutral-subtle", children: description })), buttonText && (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(Button, { variant: "secondary", color: "primary", size: "xsmall", onClick: onButtonClick, children: buttonText }) }))] })] }));
5012
+ }
5013
+ // Information type layout
5014
+ return (jsxRuntime.jsxs("div", { ref: ref, className: cn(toastVariants({ type, intent }), className), role: "alert", ...props, children: [jsxRuntime.jsx("div", { className: cn(toastIconVariants({ type }), "h-5 w-5 shrink-0"), children: displayIcon }), jsxRuntime.jsx("div", { className: cn(toastTextVariants({ type }), "flex-1 min-w-0"), children: message }), linkText && (jsxRuntime.jsx("div", { className: cn(toastLinkVariants({ type }), "shrink-0"), onClick: onLinkClick, role: "button", tabIndex: 0, onKeyDown: (e) => {
5015
+ if (e.key === "Enter" || e.key === " ") {
5016
+ e.preventDefault();
5017
+ onLinkClick?.();
5018
+ }
5019
+ }, children: linkText })), onClose && (jsxRuntime.jsx("div", { className: cn(toastCloseButtonVariants({ type }), "h-5 w-5"), onClick: handleClose, role: "button", "aria-label": "Close toast", tabIndex: 0, onKeyDown: (e) => {
5020
+ if (e.key === "Enter" || e.key === " ") {
5021
+ e.preventDefault();
5022
+ handleClose();
5023
+ }
5024
+ }, children: jsxRuntime.jsx(Icon, { name: "close", size: 16 }) }))] }));
5025
+ });
5026
+ Toast.displayName = "Toast";
5027
+
5028
+ const ToastContext = React__namespace.createContext(undefined);
5029
+ const positionClasses = {
5030
+ "top-left": "top-4 left-4",
5031
+ "top-center": "top-4 left-1/2 -translate-x-1/2",
5032
+ "top-right": "top-4 right-4",
5033
+ "bottom-left": "bottom-4 left-4",
5034
+ "bottom-center": "bottom-4 left-1/2 -translate-x-1/2",
5035
+ "bottom-right": "bottom-4 right-4",
5036
+ };
5037
+ const getDefaultPosition = (type) => {
5038
+ return type === "promotional" ? "bottom-right" : "bottom-center";
5039
+ };
5040
+ const getAnimationClasses = (position, isVisible) => {
5041
+ const isTop = position.startsWith("top");
5042
+ const isBottom = position.startsWith("bottom");
5043
+ if (!isVisible) {
5044
+ // Exit/hidden state - slide out
5045
+ if (isTop) {
5046
+ return "-translate-y-[120%] pointer-events-none";
5047
+ }
5048
+ if (isBottom) {
5049
+ return "translate-y-[120%] pointer-events-none";
5050
+ }
5051
+ // Fallback
5052
+ return "translate-y-[120%] pointer-events-none";
5053
+ }
5054
+ // Visible state - slide in
5055
+ return "translate-y-0";
5056
+ };
5057
+ const ToastProvider = ({ children, maxToasts = 5, defaultDuration = 5000, }) => {
5058
+ const [toasts, setToasts] = React__namespace.useState([]);
5059
+ const toastCounter = React__namespace.useRef(0);
5060
+ const generateId = () => {
5061
+ toastCounter.current += 1;
5062
+ return `toast-${Date.now()}-${toastCounter.current}`;
5063
+ };
5064
+ const showToast = React__namespace.useCallback((options) => {
5065
+ const id = options.id || generateId();
5066
+ const toastType = options.type || "information";
5067
+ const newToast = {
5068
+ ...options,
5069
+ id,
5070
+ isVisible: false, // Start hidden to trigger entry animation
5071
+ type: toastType,
5072
+ position: options.position || getDefaultPosition(toastType),
5073
+ duration: options.duration ??
5074
+ (toastType === "promotional" ? 0 : defaultDuration),
5075
+ };
5076
+ setToasts((prevToasts) => {
5077
+ const filtered = prevToasts.filter((t) => t.id !== id);
5078
+ const updated = [...filtered, newToast];
5079
+ // Keep only the most recent maxToasts
5080
+ return updated.slice(-maxToasts);
5081
+ });
5082
+ // Trigger entry animation after DOM update
5083
+ // Use setTimeout to ensure the initial hidden state is rendered first
5084
+ setTimeout(() => {
5085
+ setToasts((prevToasts) => prevToasts.map((toast) => toast.id === id ? { ...toast, isVisible: true } : toast));
5086
+ }, 10);
5087
+ return id;
5088
+ }, [maxToasts, defaultDuration]);
5089
+ const dismiss = React__namespace.useCallback((id) => {
5090
+ setToasts((prevToasts) => prevToasts.map((toast) => toast.id === id ? { ...toast, isVisible: false } : toast));
5091
+ // Remove toast after animation completes (500ms animation + 50ms buffer)
5092
+ setTimeout(() => {
5093
+ setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
5094
+ }, 550);
5095
+ }, []);
5096
+ const dismissAll = React__namespace.useCallback(() => {
5097
+ setToasts((prevToasts) => prevToasts.map((toast) => ({ ...toast, isVisible: false })));
5098
+ // Remove all toasts after animation completes (500ms animation + 50ms buffer)
5099
+ setTimeout(() => {
5100
+ setToasts([]);
5101
+ }, 550);
5102
+ }, []);
5103
+ const success = React__namespace.useCallback((message, options) => {
5104
+ return showToast({
5105
+ type: "information",
5106
+ intent: "positive",
5107
+ message,
5108
+ ...options,
5109
+ });
5110
+ }, [showToast]);
5111
+ const error = React__namespace.useCallback((message, options) => {
5112
+ return showToast({
5113
+ type: "information",
5114
+ intent: "negative",
5115
+ message,
5116
+ ...options,
5117
+ });
5118
+ }, [showToast]);
5119
+ const warning = React__namespace.useCallback((message, options) => {
5120
+ return showToast({
5121
+ type: "information",
5122
+ intent: "notice",
5123
+ message,
5124
+ ...options,
5125
+ });
5126
+ }, [showToast]);
5127
+ const info = React__namespace.useCallback((message, options) => {
5128
+ return showToast({
5129
+ type: "information",
5130
+ intent: "info",
5131
+ message,
5132
+ ...options,
5133
+ });
5134
+ }, [showToast]);
5135
+ const promotional = React__namespace.useCallback((heading, description, options) => {
5136
+ return showToast({
5137
+ type: "promotional",
5138
+ heading,
5139
+ description,
5140
+ ...options,
5141
+ });
5142
+ }, [showToast]);
5143
+ const contextValue = React__namespace.useMemo(() => ({
5144
+ showToast,
5145
+ success,
5146
+ error,
5147
+ warning,
5148
+ info,
5149
+ promotional,
5150
+ dismiss,
5151
+ dismissAll,
5152
+ }), [showToast, success, error, warning, info, promotional, dismiss, dismissAll]);
5153
+ // Group toasts by position
5154
+ const toastsByPosition = React__namespace.useMemo(() => {
5155
+ const grouped = {};
5156
+ toasts.forEach((toast) => {
5157
+ const pos = toast.position || getDefaultPosition(toast.type);
5158
+ if (!grouped[pos]) {
5159
+ grouped[pos] = [];
5160
+ }
5161
+ grouped[pos].push(toast);
5162
+ });
5163
+ return grouped;
5164
+ }, [toasts]);
5165
+ return (jsxRuntime.jsxs(ToastContext.Provider, { value: contextValue, children: [children, Object.keys(toastsByPosition).map((position) => {
5166
+ const positionToasts = toastsByPosition[position];
5167
+ if (!positionToasts)
5168
+ return null;
5169
+ return (jsxRuntime.jsx("div", { className: `fixed z-50 flex flex-col gap-3 ${positionClasses[position]}`, "aria-live": "polite", "aria-atomic": "false", children: positionToasts.map((toast) => {
5170
+ const toastPosition = toast.position || getDefaultPosition(toast.type);
5171
+ const animationClasses = getAnimationClasses(toastPosition, toast.isVisible);
5172
+ return (jsxRuntime.jsx("div", { className: cn("will-change-transform transition-transform duration-200 ease-out", animationClasses), children: jsxRuntime.jsx(Toast, { ...toast, isVisible: toast.isVisible, onClose: () => {
5173
+ toast.onClose?.();
5174
+ dismiss(toast.id);
5175
+ } }) }, toast.id));
5176
+ }) }, position));
5177
+ })] }));
5178
+ };
5179
+ /**
5180
+ * Hook to access toast functionality
5181
+ *
5182
+ * @example
5183
+ * ```tsx
5184
+ * function MyComponent() {
5185
+ * const toast = useToast();
5186
+ *
5187
+ * return (
5188
+ * <button onClick={() => toast.success("Saved successfully!")}>
5189
+ * Save
5190
+ * </button>
5191
+ * );
5192
+ * }
5193
+ * ```
5194
+ */
5195
+ const useToast = () => {
5196
+ const context = React__namespace.useContext(ToastContext);
5197
+ if (!context) {
5198
+ throw new Error("useToast must be used within a ToastProvider");
5199
+ }
5200
+ return context;
5201
+ };
5202
+
4870
5203
  const uploadBoxVariants = classVarianceAuthority.cva("relative flex flex-col items-center justify-center border-2 border-dashed rounded-large transition-all font-display", {
4871
5204
  variants: {
4872
5205
  size: {
@@ -5258,6 +5591,8 @@ exports.Tabs = Tabs;
5258
5591
  exports.Text = Text;
5259
5592
  exports.TextArea = TextArea;
5260
5593
  exports.TextField = TextField;
5594
+ exports.Toast = Toast;
5595
+ exports.ToastProvider = ToastProvider;
5261
5596
  exports.Tooltip = Tooltip;
5262
5597
  exports.UploadBox = UploadBox;
5263
5598
  exports.alertVariants = alertVariants;
@@ -5287,4 +5622,5 @@ exports.textAreaVariants = textAreaVariants;
5287
5622
  exports.textFieldVariants = textFieldVariants;
5288
5623
  exports.tooltipVariants = tooltipVariants;
5289
5624
  exports.uploadBoxVariants = uploadBoxVariants;
5625
+ exports.useToast = useToast;
5290
5626
  //# sourceMappingURL=index.js.map