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 +194 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/src/components/Toast/index.d.ts +1 -1
- package/dist/cjs/types/src/components/Toast/styles.d.ts +1 -1
- package/dist/cjs/types/src/components/Tooltip/index.d.ts +2 -0
- package/dist/cjs/types/src/components/Tooltip/useSmartTooltipPlacement.d.ts +5 -0
- package/dist/esm/index.js +194 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/src/components/Toast/index.d.ts +1 -1
- package/dist/esm/types/src/components/Toast/styles.d.ts +1 -1
- package/dist/esm/types/src/components/Tooltip/index.d.ts +2 -0
- package/dist/esm/types/src/components/Tooltip/useSmartTooltipPlacement.d.ts +5 -0
- package/dist/index.d.ts +3 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
export type ThemeType = "neutral" | "info" | "positive" | "caution" | "negative";
|
|
3
3
|
export type ToastType = "link" | "button" | "no-action";
|
|
4
4
|
export type PositionType = "inline" | "bottom";
|
|
5
|
-
export type ToastPositionType = "top-center" | "bottom-center";
|
|
5
|
+
export type ToastPositionType = "top-center" | "bottom-center" | "top-right" | "bottom-right" | "top-left" | "bottom-left";
|
|
6
6
|
export type ToastProps = {
|
|
7
7
|
className?: string;
|
|
8
8
|
theme?: ThemeType;
|
|
@@ -10,6 +10,6 @@ export declare const PTitleSC: import("styled-components/dist/types").IStyledCom
|
|
|
10
10
|
export declare const LinkSC: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, never>> & string;
|
|
11
11
|
export declare const DivTitleWrap: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, ToastProps>> & string;
|
|
12
12
|
export declare const ToastWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
13
|
-
$position?: "top-center" | "bottom-center";
|
|
13
|
+
$position?: "top-center" | "bottom-center" | "top-right" | "bottom-right" | "top-left" | "bottom-left";
|
|
14
14
|
}>> & string;
|
|
15
15
|
export {};
|
|
@@ -23,6 +23,8 @@ interface TooltipProps extends Omit<TooltipPropsAntd, "variant" | "title"> {
|
|
|
23
23
|
size?: TooltipSize;
|
|
24
24
|
/** Child elements that the tooltip wraps (optional) */
|
|
25
25
|
children?: React.ReactNode;
|
|
26
|
+
/** Enable smart placement (auto left/right) */
|
|
27
|
+
smartPlacement?: boolean;
|
|
26
28
|
}
|
|
27
29
|
declare const FISTooltip: FC<TooltipProps>;
|
|
28
30
|
export default FISTooltip;
|
package/dist/esm/index.js
CHANGED
|
@@ -61815,13 +61815,139 @@ const DivTitleWrap = styled.div `
|
|
|
61815
61815
|
`;
|
|
61816
61816
|
const ToastWrapper = styled.div `
|
|
61817
61817
|
position: fixed;
|
|
61818
|
-
|
|
61819
|
-
left: 50%;
|
|
61820
|
-
transform: translateX(-50%);
|
|
61821
|
-
z-index: 1000;
|
|
61818
|
+
z-index: 1050; /* Cao hơn bootstrap modal (1040) và các component khác */
|
|
61822
61819
|
display: flex;
|
|
61823
61820
|
flex-direction: column;
|
|
61824
|
-
gap:
|
|
61821
|
+
gap: 12px; /* Tăng gap để toast không quá sát nhau */
|
|
61822
|
+
max-width: 420px; /* Tăng một chút cho nội dung dài */
|
|
61823
|
+
min-width: 320px; /* Tăng min-width cho đẹp hơn */
|
|
61824
|
+
pointer-events: none; /* Tránh chặn click vào các element phía sau */
|
|
61825
|
+
|
|
61826
|
+
/* Cho phép click vào các toast con */
|
|
61827
|
+
> * {
|
|
61828
|
+
pointer-events: auto;
|
|
61829
|
+
}
|
|
61830
|
+
|
|
61831
|
+
${(props) => {
|
|
61832
|
+
switch (props.$position) {
|
|
61833
|
+
case "top-center":
|
|
61834
|
+
return css `
|
|
61835
|
+
top: 24px;
|
|
61836
|
+
left: 50%;
|
|
61837
|
+
transform: translateX(-50%);
|
|
61838
|
+
`;
|
|
61839
|
+
case "bottom-center":
|
|
61840
|
+
return css `
|
|
61841
|
+
bottom: 24px;
|
|
61842
|
+
left: 50%;
|
|
61843
|
+
transform: translateX(-50%);
|
|
61844
|
+
flex-direction: column-reverse; /* Toast mới xuất hiện từ dưới lên */
|
|
61845
|
+
`;
|
|
61846
|
+
case "top-right":
|
|
61847
|
+
return css `
|
|
61848
|
+
top: 24px;
|
|
61849
|
+
right: 24px;
|
|
61850
|
+
`;
|
|
61851
|
+
case "bottom-right":
|
|
61852
|
+
return css `
|
|
61853
|
+
bottom: 24px;
|
|
61854
|
+
right: 24px;
|
|
61855
|
+
flex-direction: column-reverse;
|
|
61856
|
+
`;
|
|
61857
|
+
case "top-left":
|
|
61858
|
+
return css `
|
|
61859
|
+
top: 24px;
|
|
61860
|
+
left: 24px;
|
|
61861
|
+
`;
|
|
61862
|
+
case "bottom-left":
|
|
61863
|
+
return css `
|
|
61864
|
+
bottom: 24px;
|
|
61865
|
+
left: 24px;
|
|
61866
|
+
flex-direction: column-reverse;
|
|
61867
|
+
`;
|
|
61868
|
+
default:
|
|
61869
|
+
return css `
|
|
61870
|
+
top: 24px;
|
|
61871
|
+
left: 50%;
|
|
61872
|
+
transform: translateX(-50%);
|
|
61873
|
+
`;
|
|
61874
|
+
}
|
|
61875
|
+
}}
|
|
61876
|
+
|
|
61877
|
+
/* Animation cho toast xuất hiện */
|
|
61878
|
+
> div {
|
|
61879
|
+
animation: toastSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
61880
|
+
}
|
|
61881
|
+
|
|
61882
|
+
@keyframes toastSlideIn {
|
|
61883
|
+
from {
|
|
61884
|
+
opacity: 0;
|
|
61885
|
+
transform: translateY(-12px) scale(0.95);
|
|
61886
|
+
}
|
|
61887
|
+
to {
|
|
61888
|
+
opacity: 1;
|
|
61889
|
+
transform: translateY(0) scale(1);
|
|
61890
|
+
}
|
|
61891
|
+
}
|
|
61892
|
+
|
|
61893
|
+
/* Animation cho bottom positions */
|
|
61894
|
+
${(props) => props.$position?.includes("bottom") &&
|
|
61895
|
+
css `
|
|
61896
|
+
> div {
|
|
61897
|
+
animation: toastSlideInBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
61898
|
+
}
|
|
61899
|
+
|
|
61900
|
+
@keyframes toastSlideInBottom {
|
|
61901
|
+
from {
|
|
61902
|
+
opacity: 0;
|
|
61903
|
+
transform: translateY(12px) scale(0.95);
|
|
61904
|
+
}
|
|
61905
|
+
to {
|
|
61906
|
+
opacity: 1;
|
|
61907
|
+
transform: translateY(0) scale(1);
|
|
61908
|
+
}
|
|
61909
|
+
}
|
|
61910
|
+
`}
|
|
61911
|
+
|
|
61912
|
+
/* Responsive cho mobile */
|
|
61913
|
+
@media (max-width: 768px) {
|
|
61914
|
+
max-width: calc(100vw - 32px);
|
|
61915
|
+
min-width: calc(100vw - 32px);
|
|
61916
|
+
|
|
61917
|
+
${(props) => {
|
|
61918
|
+
if (props.$position?.includes("center")) {
|
|
61919
|
+
return css `
|
|
61920
|
+
left: 16px;
|
|
61921
|
+
right: 16px;
|
|
61922
|
+
transform: none;
|
|
61923
|
+
`;
|
|
61924
|
+
}
|
|
61925
|
+
else if (props.$position?.includes("left")) {
|
|
61926
|
+
return css `
|
|
61927
|
+
left: 16px;
|
|
61928
|
+
`;
|
|
61929
|
+
}
|
|
61930
|
+
else if (props.$position?.includes("right")) {
|
|
61931
|
+
return css `
|
|
61932
|
+
right: 16px;
|
|
61933
|
+
`;
|
|
61934
|
+
}
|
|
61935
|
+
}}
|
|
61936
|
+
|
|
61937
|
+
${(props) => props.$position?.includes("top")
|
|
61938
|
+
? css `
|
|
61939
|
+
top: 16px;
|
|
61940
|
+
`
|
|
61941
|
+
: css `
|
|
61942
|
+
bottom: 16px;
|
|
61943
|
+
`}
|
|
61944
|
+
}
|
|
61945
|
+
|
|
61946
|
+
/* Tablet breakpoint */
|
|
61947
|
+
@media (max-width: 1024px) and (min-width: 769px) {
|
|
61948
|
+
max-width: 380px;
|
|
61949
|
+
min-width: 300px;
|
|
61950
|
+
}
|
|
61825
61951
|
`;
|
|
61826
61952
|
|
|
61827
61953
|
const getPadding = (size, borderPath) => {
|
|
@@ -62547,7 +62673,7 @@ const ToastProvider = ({ children, }) => {
|
|
|
62547
62673
|
setToasts((prev) => prev.slice(1));
|
|
62548
62674
|
}, duration);
|
|
62549
62675
|
}, []);
|
|
62550
|
-
return (jsxs(ToastContext.Provider, { value: { showToast }, children: [children, toasts.length > 0 && (jsx(ToastWrapper, { "$position": toasts[0].position || "top-
|
|
62676
|
+
return (jsxs(ToastContext.Provider, { value: { showToast }, children: [children, toasts.length > 0 && (jsx(ToastWrapper, { "$position": toasts[0].position || "top-right", children: toasts.map((toast, index) => (jsx(FISToast, { ...toast }, index))) }))] }));
|
|
62551
62677
|
};
|
|
62552
62678
|
|
|
62553
62679
|
const FISThemeProvider = ({ theme: theme$1 = theme, children, }) => (jsxs(ThemeProvider, { theme: theme$1, children: [jsx(GlobalStyle, { theme: theme$1 }), jsx(NotificationProvider, { children: jsx(ToastProvider, { children: children }) })] }));
|
|
@@ -66297,18 +66423,58 @@ const DivTooltipSC = styled.div `
|
|
|
66297
66423
|
}
|
|
66298
66424
|
`;
|
|
66299
66425
|
|
|
66300
|
-
const
|
|
66426
|
+
const useSmartTooltipPlacement = () => {
|
|
66427
|
+
const [placement, setPlacement] = useState("right");
|
|
66428
|
+
const calculatePlacement = useCallback((triggerEl) => {
|
|
66429
|
+
if (!triggerEl)
|
|
66430
|
+
return;
|
|
66431
|
+
const rect = triggerEl.getBoundingClientRect();
|
|
66432
|
+
const viewportWidth = window.innerWidth;
|
|
66433
|
+
const leftSpace = rect.left;
|
|
66434
|
+
const rightSpace = viewportWidth - rect.right;
|
|
66435
|
+
if (rightSpace > leftSpace) {
|
|
66436
|
+
setPlacement("right");
|
|
66437
|
+
}
|
|
66438
|
+
else if (leftSpace > rightSpace) {
|
|
66439
|
+
setPlacement("left");
|
|
66440
|
+
}
|
|
66441
|
+
else {
|
|
66442
|
+
setPlacement("right");
|
|
66443
|
+
}
|
|
66444
|
+
}, []);
|
|
66445
|
+
return { placement, calculatePlacement };
|
|
66446
|
+
};
|
|
66447
|
+
|
|
66448
|
+
const FISTooltip = ({ className, variant = "primary", size = "md", title: titleProp, children, smartPlacement = false, ...rest }) => {
|
|
66301
66449
|
const theme = useTheme$1();
|
|
66450
|
+
const triggerRef = useRef(null);
|
|
66451
|
+
const { placement, calculatePlacement } = useSmartTooltipPlacement();
|
|
66452
|
+
useEffect(() => {
|
|
66453
|
+
if (!smartPlacement)
|
|
66454
|
+
return;
|
|
66455
|
+
const el = triggerRef.current;
|
|
66456
|
+
if (!el)
|
|
66457
|
+
return;
|
|
66458
|
+
const handleMouseEnter = () => calculatePlacement(el);
|
|
66459
|
+
el.addEventListener("mouseenter", handleMouseEnter);
|
|
66460
|
+
calculatePlacement(el);
|
|
66461
|
+
return () => {
|
|
66462
|
+
el.removeEventListener("mouseenter", handleMouseEnter);
|
|
66463
|
+
};
|
|
66464
|
+
}, [smartPlacement, calculatePlacement]);
|
|
66465
|
+
const tooltipPlacement = smartPlacement
|
|
66466
|
+
? placement
|
|
66467
|
+
: rest.placement || "right";
|
|
66302
66468
|
if (isTooltipTitleCustom(titleProp)) {
|
|
66303
66469
|
// Create properly structured custom title
|
|
66304
66470
|
const title = (jsxs(DivTooltipContentWrapperSC, { children: [jsxs(DivTooltipContentSC, { children: [jsx(DivTooltipLabelSC, { children: titleProp.label }), titleProp.description && (jsx(DivTooltipDescriptionSC, { children: titleProp.description }))] }), titleProp.link && (jsx(ATooltipLinkSC, { href: titleProp.linkUrl, target: "_blank", rel: "noopener noreferrer", children: titleProp.link }))] }));
|
|
66305
|
-
return (jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsx(Tooltip, { color: variant === "primary"
|
|
66306
|
-
|
|
66307
|
-
|
|
66471
|
+
return (jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsx("span", { ref: smartPlacement ? triggerRef : undefined, children: jsx(Tooltip, { color: variant === "primary"
|
|
66472
|
+
? theme["com/tooltip/size-md/theme-black/background-color"]
|
|
66473
|
+
: theme["com/tooltip/size-md/theme-white/background-color"], title: title, getTooltipContainer: () => document.body, placement: tooltipPlacement, ...rest, children: children || jsx(Button$2, { children: "RT" }) }) }) }));
|
|
66308
66474
|
}
|
|
66309
|
-
return (jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsx(Tooltip, { color: variant === "primary"
|
|
66310
|
-
|
|
66311
|
-
|
|
66475
|
+
return (jsx(DivTooltipSC, { className: className, "$size": size, "$variant": variant, children: jsx("span", { ref: smartPlacement ? triggerRef : undefined, children: jsx(Tooltip, { color: variant === "primary"
|
|
66476
|
+
? theme["com/tooltip/size-md/theme-black/background-color"]
|
|
66477
|
+
: theme["com/tooltip/size-md/theme-white/background-color"], title: titleProp, getTooltipContainer: () => document.body, placement: tooltipPlacement, ...rest, children: children || jsx(Button$2, { children: "RT" }) }) }) }));
|
|
66312
66478
|
};
|
|
66313
66479
|
FISTooltip.displayName = "FISTooltip";
|
|
66314
66480
|
|
|
@@ -67329,6 +67495,14 @@ const FISBadge = ({ className, icon, label, size = "sm", color, status, type = "
|
|
|
67329
67495
|
FISBadge.displayName = "FISBadge";
|
|
67330
67496
|
|
|
67331
67497
|
const FISMenuItem = ({ title, description, iconPrefix, type = "action", size = "md", selected = false, negative = false, disable, onClickMenu, }) => {
|
|
67498
|
+
const titleRef = useRef(null);
|
|
67499
|
+
const [isTruncated, setIsTruncated] = useState(false);
|
|
67500
|
+
useEffect(() => {
|
|
67501
|
+
const el = titleRef.current;
|
|
67502
|
+
if (el) {
|
|
67503
|
+
setIsTruncated(el.scrollWidth > el.clientWidth);
|
|
67504
|
+
}
|
|
67505
|
+
}, [title]);
|
|
67332
67506
|
const renderSuffixIcon = () => {
|
|
67333
67507
|
switch (type) {
|
|
67334
67508
|
case "action":
|
|
@@ -67345,9 +67519,12 @@ const FISMenuItem = ({ title, description, iconPrefix, type = "action", size = "
|
|
|
67345
67519
|
negative: negative,
|
|
67346
67520
|
}), "$size": size, "$selected": selected, "$type": type, "$disable": disable, onClick: onClickMenu, children: [iconPrefix && (jsx(DivPrefixIconSC, { className: classNames({
|
|
67347
67521
|
negative: negative,
|
|
67348
|
-
}), "$size": size, "$type": type, "$selected": selected, "$disable": disable, children: iconPrefix })), jsxs(DivContentSC$2, { children: [title &&
|
|
67349
|
-
|
|
67350
|
-
|
|
67522
|
+
}), "$size": size, "$type": type, "$selected": selected, "$disable": disable, children: iconPrefix })), jsxs(DivContentSC$2, { children: [title &&
|
|
67523
|
+
(isTruncated ? (jsx(FISTooltip, { title: title, variant: "primary", smartPlacement: true, children: jsx(PTitleSC$1, { ref: titleRef, className: classNames({
|
|
67524
|
+
negative: negative,
|
|
67525
|
+
}), "$selected": selected, "$type": type, "$disable": disable, children: title }) })) : (jsx(PTitleSC$1, { ref: titleRef, className: classNames({
|
|
67526
|
+
negative: negative,
|
|
67527
|
+
}), "$selected": selected, "$type": type, "$disable": disable, children: title }))), description && (jsx(PDescriptionSC, { className: classNames({
|
|
67351
67528
|
negative: negative,
|
|
67352
67529
|
}), "$type": type, "$selected": selected, "$disable": disable, children: description }))] }), renderSuffixIcon()] }));
|
|
67353
67530
|
};
|
|
@@ -67477,7 +67654,7 @@ const FISMenuSelect = ({ placeholder, groups, size = "md", multi = false, select
|
|
|
67477
67654
|
onClickMenu?.();
|
|
67478
67655
|
}
|
|
67479
67656
|
};
|
|
67480
|
-
return (jsxs(DivContainerSC$4, { "$maxHeight": maxHeight, className: className, children: [showInput && !combobox && (jsx(DivSearchSC, { children: jsx(FISInputText, { iconPrefix: jsx(SearchIcon, {}), placeholder: placeholder, value: search, onChange: handleSearchChange }) })), (shouldShowNoResult || noResult) && (jsxs(DivLoaderSC, { children: [jsx(DivIconDataSC, { children: jsx(NoResultIcon, {}) }), jsx(PTitleSC$2, { children: noResultText })] })), noData && (jsxs(DivLoaderSC, { children: [jsx(DivIconDataSC, { children: jsx(NoDataIcon, {}) }), jsx(PTitleSC$2, { children: noDataText })] })), loading && (jsxs(DivLoaderSC, { children: [jsx(FISProgressCircular, { size: size, variant: "indeterminate" }), jsx(PTitleSC$2, { children: loadingText })] })), !shouldShowNoResult && !noData && !loading && (jsxs(Fragment, { children: [jsx(MenuContent, { "$removeSelectedGroup": !!removeSelectedGroup, children: groupsToRender.map((group, index) => (jsxs(DivWrapperSC$4, { children: [index !== 0 && jsx(FISMenuSection, { withDivider: true }), group?.groupLabel && (jsx(FISMenuSection, { label: group?.groupLabel })), group.items.map((item, idx) => (jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => handleItemClick(item), selected: selectedValues.includes(item.value), type: "select" }, idx)))] }, index))) }), removeSelectedGroup && (jsx(FixedBottomSection, { children: jsx(DivWrapperSC$4, { children: removeSelectedGroup.items.map((item, idx) => (jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => onChangeSelected?.([]), type: "select", iconPrefix: jsx(RemoveIcon, {}), negative: true }, idx))) }) }))] }))] }));
|
|
67657
|
+
return (jsxs(DivContainerSC$4, { "$maxHeight": maxHeight, className: className, children: [showInput && !combobox && (jsx(DivSearchSC, { children: jsx(FISInputText, { iconPrefix: jsx(SearchIcon, {}), placeholder: placeholder, value: search, onChange: handleSearchChange }) })), (shouldShowNoResult || noResult) && (jsxs(DivLoaderSC, { children: [jsx(DivIconDataSC, { children: jsx(NoResultIcon, {}) }), jsx(PTitleSC$2, { children: noResultText })] })), noData && (jsxs(DivLoaderSC, { children: [jsx(DivIconDataSC, { children: jsx(NoDataIcon, {}) }), jsx(PTitleSC$2, { children: noDataText })] })), loading && (jsxs(DivLoaderSC, { children: [jsx(FISProgressCircular, { size: size, variant: "indeterminate" }), jsx(PTitleSC$2, { children: loadingText })] })), !shouldShowNoResult && !noData && !loading && (jsxs(Fragment, { children: [jsx(MenuContent, { "$removeSelectedGroup": !!removeSelectedGroup, children: groupsToRender.map((group, index) => (jsxs(DivWrapperSC$4, { children: [index !== 0 && jsx(FISMenuSection, { withDivider: true }), group?.groupLabel && (jsx(FISMenuSection, { label: group?.groupLabel })), group.items.map((item, idx) => (jsx(FISTooltip, { title: item.label, variant: "primary", children: jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => handleItemClick(item), selected: selectedValues.includes(item.value), type: "select" }) }, idx)))] }, index))) }), removeSelectedGroup && (jsx(FixedBottomSection, { children: jsx(DivWrapperSC$4, { children: removeSelectedGroup.items.map((item, idx) => (jsx(FISTooltip, { title: item.label, variant: "primary", children: jsx(FISMenuItem, { title: item.label, description: item.description, size: size, onClickMenu: () => onChangeSelected?.([]), type: "select", iconPrefix: jsx(RemoveIcon, {}), negative: true }) }, idx))) }) }))] }))] }));
|
|
67481
67658
|
};
|
|
67482
67659
|
FISMenuSelect.displayName = "FISMenuSelect";
|
|
67483
67660
|
|