fis-component 0.0.79 → 0.0.81

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/cjs/index.js CHANGED
@@ -61835,13 +61835,139 @@ const DivTitleWrap = styled.div `
61835
61835
  `;
61836
61836
  const ToastWrapper = styled.div `
61837
61837
  position: fixed;
61838
- ${(props) => props.$position === "bottom-center" ? "bottom: 20px;" : "top: 20px;"}
61839
- left: 50%;
61840
- transform: translateX(-50%);
61841
- z-index: 1000;
61838
+ z-index: 1050; /* Cao hơn bootstrap modal (1040) các component khác */
61842
61839
  display: flex;
61843
61840
  flex-direction: column;
61844
- gap: 8px;
61841
+ gap: 12px; /* Tăng gap để toast không quá sát nhau */
61842
+ max-width: 420px; /* Tăng một chút cho nội dung dài */
61843
+ min-width: 320px; /* Tăng min-width cho đẹp hơn */
61844
+ pointer-events: none; /* Tránh chặn click vào các element phía sau */
61845
+
61846
+ /* Cho phép click vào các toast con */
61847
+ > * {
61848
+ pointer-events: auto;
61849
+ }
61850
+
61851
+ ${(props) => {
61852
+ switch (props.$position) {
61853
+ case "top-center":
61854
+ return styled.css `
61855
+ top: 24px;
61856
+ left: 50%;
61857
+ transform: translateX(-50%);
61858
+ `;
61859
+ case "bottom-center":
61860
+ return styled.css `
61861
+ bottom: 24px;
61862
+ left: 50%;
61863
+ transform: translateX(-50%);
61864
+ flex-direction: column-reverse; /* Toast mới xuất hiện từ dưới lên */
61865
+ `;
61866
+ case "top-right":
61867
+ return styled.css `
61868
+ top: 24px;
61869
+ right: 24px;
61870
+ `;
61871
+ case "bottom-right":
61872
+ return styled.css `
61873
+ bottom: 24px;
61874
+ right: 24px;
61875
+ flex-direction: column-reverse;
61876
+ `;
61877
+ case "top-left":
61878
+ return styled.css `
61879
+ top: 24px;
61880
+ left: 24px;
61881
+ `;
61882
+ case "bottom-left":
61883
+ return styled.css `
61884
+ bottom: 24px;
61885
+ left: 24px;
61886
+ flex-direction: column-reverse;
61887
+ `;
61888
+ default:
61889
+ return styled.css `
61890
+ top: 24px;
61891
+ left: 50%;
61892
+ transform: translateX(-50%);
61893
+ `;
61894
+ }
61895
+ }}
61896
+
61897
+ /* Animation cho toast xuất hiện */
61898
+ > div {
61899
+ animation: toastSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
61900
+ }
61901
+
61902
+ @keyframes toastSlideIn {
61903
+ from {
61904
+ opacity: 0;
61905
+ transform: translateY(-12px) scale(0.95);
61906
+ }
61907
+ to {
61908
+ opacity: 1;
61909
+ transform: translateY(0) scale(1);
61910
+ }
61911
+ }
61912
+
61913
+ /* Animation cho bottom positions */
61914
+ ${(props) => props.$position?.includes("bottom") &&
61915
+ styled.css `
61916
+ > div {
61917
+ animation: toastSlideInBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);
61918
+ }
61919
+
61920
+ @keyframes toastSlideInBottom {
61921
+ from {
61922
+ opacity: 0;
61923
+ transform: translateY(12px) scale(0.95);
61924
+ }
61925
+ to {
61926
+ opacity: 1;
61927
+ transform: translateY(0) scale(1);
61928
+ }
61929
+ }
61930
+ `}
61931
+
61932
+ /* Responsive cho mobile */
61933
+ @media (max-width: 768px) {
61934
+ max-width: calc(100vw - 32px);
61935
+ min-width: calc(100vw - 32px);
61936
+
61937
+ ${(props) => {
61938
+ if (props.$position?.includes("center")) {
61939
+ return styled.css `
61940
+ left: 16px;
61941
+ right: 16px;
61942
+ transform: none;
61943
+ `;
61944
+ }
61945
+ else if (props.$position?.includes("left")) {
61946
+ return styled.css `
61947
+ left: 16px;
61948
+ `;
61949
+ }
61950
+ else if (props.$position?.includes("right")) {
61951
+ return styled.css `
61952
+ right: 16px;
61953
+ `;
61954
+ }
61955
+ }}
61956
+
61957
+ ${(props) => props.$position?.includes("top")
61958
+ ? styled.css `
61959
+ top: 16px;
61960
+ `
61961
+ : styled.css `
61962
+ bottom: 16px;
61963
+ `}
61964
+ }
61965
+
61966
+ /* Tablet breakpoint */
61967
+ @media (max-width: 1024px) and (min-width: 769px) {
61968
+ max-width: 380px;
61969
+ min-width: 300px;
61970
+ }
61845
61971
  `;
61846
61972
 
61847
61973
  const getPadding = (size, borderPath) => {
@@ -62567,7 +62693,7 @@ const ToastProvider = ({ children, }) => {
62567
62693
  setToasts((prev) => prev.slice(1));
62568
62694
  }, duration);
62569
62695
  }, []);
62570
- return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast }, children: [children, toasts.length > 0 && (jsxRuntime.jsx(ToastWrapper, { "$position": toasts[0].position || "top-center", children: toasts.map((toast, index) => (jsxRuntime.jsx(FISToast, { ...toast }, index))) }))] }));
62696
+ return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast }, children: [children, toasts.length > 0 && (jsxRuntime.jsx(ToastWrapper, { "$position": toasts[0].position || "top-right", children: toasts.map((toast, index) => (jsxRuntime.jsx(FISToast, { ...toast }, index))) }))] }));
62571
62697
  };
62572
62698
 
62573
62699
  const FISThemeProvider = ({ theme: theme$1 = theme, children, }) => (jsxRuntime.jsxs(styled.ThemeProvider, { theme: theme$1, children: [jsxRuntime.jsx(GlobalStyle, { theme: theme$1 }), jsxRuntime.jsx(NotificationProvider, { children: jsxRuntime.jsx(ToastProvider, { children: children }) })] }));
@@ -66317,18 +66443,58 @@ const DivTooltipSC = styled.div `
66317
66443
  }
66318
66444
  `;
66319
66445
 
66320
- const FISTooltip = ({ className, variant = "primary", size = "md", title: titleProp, children, ...rest }) => {
66446
+ const useSmartTooltipPlacement = () => {
66447
+ const [placement, setPlacement] = React.useState("right");
66448
+ const calculatePlacement = React.useCallback((triggerEl) => {
66449
+ if (!triggerEl)
66450
+ return;
66451
+ const rect = triggerEl.getBoundingClientRect();
66452
+ const viewportWidth = window.innerWidth;
66453
+ const leftSpace = rect.left;
66454
+ const rightSpace = viewportWidth - rect.right;
66455
+ if (rightSpace > leftSpace) {
66456
+ setPlacement("right");
66457
+ }
66458
+ else if (leftSpace > rightSpace) {
66459
+ setPlacement("left");
66460
+ }
66461
+ else {
66462
+ setPlacement("right");
66463
+ }
66464
+ }, []);
66465
+ return { placement, calculatePlacement };
66466
+ };
66467
+
66468
+ const FISTooltip = ({ className, variant = "primary", size = "md", title: titleProp, children, smartPlacement = false, ...rest }) => {
66321
66469
  const theme = styled.useTheme();
66470
+ const triggerRef = React.useRef(null);
66471
+ const { placement, calculatePlacement } = useSmartTooltipPlacement();
66472
+ React.useEffect(() => {
66473
+ if (!smartPlacement)
66474
+ return;
66475
+ const el = triggerRef.current;
66476
+ if (!el)
66477
+ return;
66478
+ const handleMouseEnter = () => calculatePlacement(el);
66479
+ el.addEventListener("mouseenter", handleMouseEnter);
66480
+ calculatePlacement(el);
66481
+ return () => {
66482
+ el.removeEventListener("mouseenter", handleMouseEnter);
66483
+ };
66484
+ }, [smartPlacement, calculatePlacement]);
66485
+ const tooltipPlacement = smartPlacement
66486
+ ? placement
66487
+ : rest.placement || "right";
66322
66488
  if (isTooltipTitleCustom(titleProp)) {
66323
66489
  // Create properly structured custom title
66324
66490
  const title = (jsxRuntime.jsxs(DivTooltipContentWrapperSC, { children: [jsxRuntime.jsxs(DivTooltipContentSC, { children: [jsxRuntime.jsx(DivTooltipLabelSC, { children: titleProp.label }), titleProp.description && (jsxRuntime.jsx(DivTooltipDescriptionSC, { children: titleProp.description }))] }), titleProp.link && (jsxRuntime.jsx(ATooltipLinkSC, { href: titleProp.linkUrl, target: "_blank", rel: "noopener noreferrer", children: titleProp.link }))] }));
66325
- return (jsxRuntime.jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsxRuntime.jsx(Tooltip, { color: variant === "primary"
66326
- ? theme["com/tooltip/size-md/theme-black/background-color"]
66327
- : theme["com/tooltip/size-md/theme-white/background-color"], title: title, getTooltipContainer: (trigger) => trigger, ...rest, children: children || jsxRuntime.jsx(Button$2, { children: "RT" }) }) }));
66491
+ return (jsxRuntime.jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsxRuntime.jsx("span", { ref: smartPlacement ? triggerRef : undefined, children: jsxRuntime.jsx(Tooltip, { color: variant === "primary"
66492
+ ? theme["com/tooltip/size-md/theme-black/background-color"]
66493
+ : theme["com/tooltip/size-md/theme-white/background-color"], title: title, getTooltipContainer: () => document.body, placement: tooltipPlacement, ...rest, children: children || jsxRuntime.jsx(Button$2, { children: "RT" }) }) }) }));
66328
66494
  }
66329
- return (jsxRuntime.jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsxRuntime.jsx(Tooltip, { color: variant === "primary"
66330
- ? theme["com/tooltip/size-md/theme-black/background-color"]
66331
- : theme["com/tooltip/size-md/theme-white/background-color"], title: titleProp, getTooltipContainer: (trigger) => trigger, ...rest, children: children || jsxRuntime.jsx(Button$2, { children: "RT" }) }) }));
66495
+ return (jsxRuntime.jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsxRuntime.jsx("span", { ref: smartPlacement ? triggerRef : undefined, children: jsxRuntime.jsx(Tooltip, { color: variant === "primary"
66496
+ ? theme["com/tooltip/size-md/theme-black/background-color"]
66497
+ : theme["com/tooltip/size-md/theme-white/background-color"], title: titleProp, getTooltipContainer: () => document.body, placement: tooltipPlacement, ...rest, children: children || jsxRuntime.jsx(Button$2, { children: "RT" }) }) }) }));
66332
66498
  };
66333
66499
  FISTooltip.displayName = "FISTooltip";
66334
66500
 
@@ -67349,6 +67515,14 @@ const FISBadge = ({ className, icon, label, size = "sm", color, status, type = "
67349
67515
  FISBadge.displayName = "FISBadge";
67350
67516
 
67351
67517
  const FISMenuItem = ({ title, description, iconPrefix, type = "action", size = "md", selected = false, negative = false, disable, onClickMenu, }) => {
67518
+ const titleRef = React.useRef(null);
67519
+ const [isTruncated, setIsTruncated] = React.useState(false);
67520
+ React.useEffect(() => {
67521
+ const el = titleRef.current;
67522
+ if (el) {
67523
+ setIsTruncated(el.scrollWidth > el.clientWidth);
67524
+ }
67525
+ }, [title]);
67352
67526
  const renderSuffixIcon = () => {
67353
67527
  switch (type) {
67354
67528
  case "action":
@@ -67365,9 +67539,12 @@ const FISMenuItem = ({ title, description, iconPrefix, type = "action", size = "
67365
67539
  negative: negative,
67366
67540
  }), "$size": size, "$selected": selected, "$type": type, "$disable": disable, onClick: onClickMenu, children: [iconPrefix && (jsxRuntime.jsx(DivPrefixIconSC, { className: classNames({
67367
67541
  negative: negative,
67368
- }), "$size": size, "$type": type, "$selected": selected, "$disable": disable, children: iconPrefix })), jsxRuntime.jsxs(DivContentSC$2, { children: [title && (jsxRuntime.jsx(PTitleSC$1, { className: classNames({
67369
- negative: negative,
67370
- }), "$selected": selected, "$type": type, "$disable": disable, children: title })), description && (jsxRuntime.jsx(PDescriptionSC, { className: classNames({
67542
+ }), "$size": size, "$type": type, "$selected": selected, "$disable": disable, children: iconPrefix })), jsxRuntime.jsxs(DivContentSC$2, { children: [title &&
67543
+ (isTruncated ? (jsxRuntime.jsx(FISTooltip, { title: title, variant: "primary", smartPlacement: true, children: jsxRuntime.jsx(PTitleSC$1, { ref: titleRef, className: classNames({
67544
+ negative: negative,
67545
+ }), "$selected": selected, "$type": type, "$disable": disable, children: title }) })) : (jsxRuntime.jsx(PTitleSC$1, { ref: titleRef, className: classNames({
67546
+ negative: negative,
67547
+ }), "$selected": selected, "$type": type, "$disable": disable, children: title }))), description && (jsxRuntime.jsx(PDescriptionSC, { className: classNames({
67371
67548
  negative: negative,
67372
67549
  }), "$type": type, "$selected": selected, "$disable": disable, children: description }))] }), renderSuffixIcon()] }));
67373
67550
  };
@@ -67497,7 +67674,7 @@ const FISMenuSelect = ({ placeholder, groups, size = "md", multi = false, select
67497
67674
  onClickMenu?.();
67498
67675
  }
67499
67676
  };
67500
- return (jsxRuntime.jsxs(DivContainerSC$4, { "$maxHeight": maxHeight, className: className, children: [showInput && !combobox && (jsxRuntime.jsx(DivSearchSC, { children: jsxRuntime.jsx(FISInputText, { iconPrefix: jsxRuntime.jsx(SearchIcon, {}), placeholder: placeholder, value: search, onChange: handleSearchChange }) })), (shouldShowNoResult || noResult) && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(DivIconDataSC, { children: jsxRuntime.jsx(NoResultIcon, {}) }), jsxRuntime.jsx(PTitleSC$2, { children: noResultText })] })), noData && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(DivIconDataSC, { children: jsxRuntime.jsx(NoDataIcon, {}) }), jsxRuntime.jsx(PTitleSC$2, { children: noDataText })] })), loading && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(FISProgressCircular, { size: size, variant: "indeterminate" }), jsxRuntime.jsx(PTitleSC$2, { children: loadingText })] })), !shouldShowNoResult && !noData && !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(MenuContent, { "$removeSelectedGroup": !!removeSelectedGroup, children: groupsToRender.map((group, index) => (jsxRuntime.jsxs(DivWrapperSC$4, { children: [index !== 0 && jsxRuntime.jsx(FISMenuSection, { withDivider: true }), group?.groupLabel && (jsxRuntime.jsx(FISMenuSection, { label: group?.groupLabel })), group.items.map((item, idx) => (jsxRuntime.jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => handleItemClick(item), selected: selectedValues.includes(item.value), type: "select" }, idx)))] }, index))) }), removeSelectedGroup && (jsxRuntime.jsx(FixedBottomSection, { children: jsxRuntime.jsx(DivWrapperSC$4, { children: removeSelectedGroup.items.map((item, idx) => (jsxRuntime.jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => onChangeSelected?.([]), type: "select", iconPrefix: jsxRuntime.jsx(RemoveIcon, {}), negative: true }, idx))) }) }))] }))] }));
67677
+ return (jsxRuntime.jsxs(DivContainerSC$4, { "$maxHeight": maxHeight, className: className, children: [showInput && !combobox && (jsxRuntime.jsx(DivSearchSC, { children: jsxRuntime.jsx(FISInputText, { iconPrefix: jsxRuntime.jsx(SearchIcon, {}), placeholder: placeholder, value: search, onChange: handleSearchChange }) })), (shouldShowNoResult || noResult) && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(DivIconDataSC, { children: jsxRuntime.jsx(NoResultIcon, {}) }), jsxRuntime.jsx(PTitleSC$2, { children: noResultText })] })), noData && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(DivIconDataSC, { children: jsxRuntime.jsx(NoDataIcon, {}) }), jsxRuntime.jsx(PTitleSC$2, { children: noDataText })] })), loading && (jsxRuntime.jsxs(DivLoaderSC, { children: [jsxRuntime.jsx(FISProgressCircular, { size: size, variant: "indeterminate" }), jsxRuntime.jsx(PTitleSC$2, { children: loadingText })] })), !shouldShowNoResult && !noData && !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(MenuContent, { "$removeSelectedGroup": !!removeSelectedGroup, children: groupsToRender.map((group, index) => (jsxRuntime.jsxs(DivWrapperSC$4, { children: [index !== 0 && jsxRuntime.jsx(FISMenuSection, { withDivider: true }), group?.groupLabel && (jsxRuntime.jsx(FISMenuSection, { label: group?.groupLabel })), group.items.map((item, idx) => (jsxRuntime.jsx(FISTooltip, { title: item.label, variant: "primary", children: jsxRuntime.jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => handleItemClick(item), selected: selectedValues.includes(item.value), type: "select" }) }, idx)))] }, index))) }), removeSelectedGroup && (jsxRuntime.jsx(FixedBottomSection, { children: jsxRuntime.jsx(DivWrapperSC$4, { children: removeSelectedGroup.items.map((item, idx) => (jsxRuntime.jsx(FISTooltip, { title: item.label, variant: "primary", children: jsxRuntime.jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => onChangeSelected?.([]), type: "select", iconPrefix: jsxRuntime.jsx(RemoveIcon, {}), negative: true }) }, idx))) }) }))] }))] }));
67501
67678
  };
67502
67679
  FISMenuSelect.displayName = "FISMenuSelect";
67503
67680