sag_components 2.0.0-beta305 → 2.0.0-beta307

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.esm.js CHANGED
@@ -3796,11 +3796,14 @@ const SearchInput$1 = props => {
3796
3796
  position = 'left',
3797
3797
  iconColor = '#212121',
3798
3798
  onTyping = () => {},
3799
+ onClear = () => {},
3800
+ clearTrigger,
3799
3801
  className
3800
3802
  } = props;
3801
3803
 
3802
3804
  // Use internal state for uncontrolled mode
3803
3805
  const [internalValue, setInternalValue] = useState(defaultValue);
3806
+ const previousValueRef = useRef(defaultValue);
3804
3807
 
3805
3808
  // Determine if component is controlled
3806
3809
  const isControlled = controlledValue !== undefined;
@@ -3812,12 +3815,33 @@ const SearchInput$1 = props => {
3812
3815
  setInternalValue(defaultValue);
3813
3816
  }
3814
3817
  }, [defaultValue, isControlled]);
3818
+
3819
+ // Clear the input when clearTrigger changes (external clear trigger)
3820
+ useEffect(() => {
3821
+ if (clearTrigger !== undefined) {
3822
+ setInternalValue('');
3823
+ }
3824
+ }, [clearTrigger]);
3815
3825
  const handleInputChange = e => {
3826
+ const newValue = e.target.value;
3827
+
3828
+ // Detect native clear button click (value was non-empty, now empty, via input event)
3829
+ if (previousValueRef.current && newValue === '') {
3830
+ onClear();
3831
+ }
3832
+ previousValueRef.current = newValue;
3816
3833
  if (!isControlled) {
3817
- setInternalValue(e.target.value);
3834
+ setInternalValue(newValue);
3818
3835
  }
3819
3836
  onTyping(e);
3820
3837
  };
3838
+
3839
+ // Handle the 'search' event which fires when native clear button is clicked
3840
+ const handleSearch = e => {
3841
+ if (e.target.value === '') {
3842
+ onClear();
3843
+ }
3844
+ };
3821
3845
  return /*#__PURE__*/React$1.createElement(TextFieldContainer, {
3822
3846
  className: className,
3823
3847
  width: width
@@ -3832,6 +3856,7 @@ const SearchInput$1 = props => {
3832
3856
  height: height,
3833
3857
  placeholder: placeholder,
3834
3858
  onChange: handleInputChange,
3859
+ onSearch: handleSearch,
3835
3860
  position: position
3836
3861
  }));
3837
3862
  };
@@ -37714,8 +37739,16 @@ const SearchInput = styled.input`
37714
37739
  }
37715
37740
  `;
37716
37741
  const ListWrapper = styled.div`
37717
- max-height: ${props => props.maxHeight || 'unset'};
37718
- overflow-y: auto;
37742
+ max-height: ${props => {
37743
+ const maxH = parseInt(props.maxHeight) || 400;
37744
+ const actualH = (props.actualHeight || maxH) + 16;
37745
+ return actualH < maxH ? `${actualH}px` : props.maxHeight;
37746
+ }};
37747
+ overflow-y: ${props => {
37748
+ const maxH = parseInt(props.maxHeight) || 400;
37749
+ const actualH = (props.actualHeight || maxH) + 16;
37750
+ return actualH < maxH ? 'hidden' : 'auto';
37751
+ }};
37719
37752
  position: relative;
37720
37753
  `;
37721
37754
  const CheckboxGroup = styled.div`
@@ -37760,6 +37793,7 @@ const CheckboxLabel = styled.label`
37760
37793
  color: #212121;
37761
37794
  cursor: pointer;
37762
37795
  flex-shrink: 0;
37796
+ box-sizing: border-box;
37763
37797
 
37764
37798
  &:hover {
37765
37799
  background-color: #E6F0F0;
@@ -37832,7 +37866,7 @@ const FilterPop = props => {
37832
37866
  selectedAttributes: propSelectedAttributes = {},
37833
37867
  showSearch = true,
37834
37868
  searchPlaceholder = "Search...",
37835
- itemHeight = 32,
37869
+ itemHeight = 40,
37836
37870
  overscan = 5,
37837
37871
  hasActiveFilter = false // NEW: Indicates if this column has an active filter
37838
37872
  } = props;
@@ -37921,16 +37955,18 @@ const FilterPop = props => {
37921
37955
  offsetY
37922
37956
  } = useMemo(() => {
37923
37957
  const containerHeight = parseInt(maxHeight) || 400;
37924
- const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
37925
- const endIndex = Math.min(sortedList.length, Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan);
37958
+ const gap = 8;
37959
+ const itemWithGap = itemHeight + gap;
37960
+ const startIndex = Math.max(0, Math.floor(scrollTop / itemWithGap) - overscan);
37961
+ const endIndex = Math.min(sortedList.length, Math.ceil((scrollTop + containerHeight) / itemWithGap) + overscan);
37926
37962
  const visible = sortedList.slice(startIndex, endIndex).map((item, idx) => ({
37927
37963
  ...item,
37928
37964
  index: startIndex + idx
37929
37965
  }));
37930
37966
  return {
37931
37967
  visibleItems: visible,
37932
- totalHeight: (sortedList.length + 1) * (itemHeight + 8),
37933
- offsetY: startIndex * itemHeight
37968
+ totalHeight: sortedList.length * itemHeight + Math.max(0, sortedList.length - 1) * gap,
37969
+ offsetY: startIndex * itemWithGap
37934
37970
  };
37935
37971
  }, [sortedList, scrollTop, itemHeight, overscan, maxHeight]);
37936
37972
  const areAllNonAllItemsSelected = function () {
@@ -38130,7 +38166,8 @@ const FilterPop = props => {
38130
38166
  }), /*#__PURE__*/React$1.createElement(ListWrapper, {
38131
38167
  ref: scrollContainerRef,
38132
38168
  onScroll: handleScroll,
38133
- maxHeight: maxHeight
38169
+ maxHeight: maxHeight,
38170
+ actualHeight: totalHeight
38134
38171
  }, sortedList.length === 1 ? /*#__PURE__*/React$1.createElement(NoResultsMessage, null, "No items match your search") : /*#__PURE__*/React$1.createElement("div", {
38135
38172
  style: {
38136
38173
  height: `${totalHeight}px`,
@@ -40982,10 +41019,12 @@ const TableHeader = ({
40982
41019
  const debouncedFilterState = useDebounce(filterState, debounceDelay);
40983
41020
  const iconRefs = useRef({});
40984
41021
  const popupRef = useRef(null);
41022
+ const prevResetKeyRef = useRef(resetFiltersKey);
40985
41023
 
40986
- // Reset internal state when resetFiltersKey changes
41024
+ // Reset internal state when resetFiltersKey changes (not when columns change)
40987
41025
  useEffect(() => {
40988
- if (resetFiltersKey > 0) {
41026
+ // Only run reset when resetFiltersKey actually changed
41027
+ if (resetFiltersKey !== prevResetKeyRef.current && resetFiltersKey > 0) {
40989
41028
  const resetSelections = {};
40990
41029
  columns.forEach(column => {
40991
41030
  if (column.filter && column.filterOptions) {
@@ -41007,6 +41046,7 @@ const TableHeader = ({
41007
41046
  setVisibleFilterPopWrapper(null);
41008
41047
  setVisibleSortPopWrapper(null);
41009
41048
  }
41049
+ prevResetKeyRef.current = resetFiltersKey;
41010
41050
  }, [resetFiltersKey, columns]);
41011
41051
 
41012
41052
  // Track if we've already initialized from saved state
@@ -57679,6 +57719,21 @@ function ModalDrawer(_ref) {
57679
57719
  }, "\xD7"), children));
57680
57720
  }
57681
57721
 
57722
+ // Keyframes for rolling/marquee animation
57723
+ const scrollTextAnimation = keyframes`
57724
+ 0% {
57725
+ transform: translateX(0);
57726
+ }
57727
+ 10% {
57728
+ transform: translateX(0);
57729
+ }
57730
+ 90% {
57731
+ transform: translateX(var(--scroll-distance, -30%));
57732
+ }
57733
+ 100% {
57734
+ transform: translateX(var(--scroll-distance, -30%));
57735
+ }
57736
+ `;
57682
57737
  const scrollableStyles = `
57683
57738
  &::-webkit-scrollbar {
57684
57739
  height: 8px;
@@ -57697,6 +57752,9 @@ const scrollableStyles = `
57697
57752
  `;
57698
57753
  const TooltipWrapper = styled(Tooltip$2)`
57699
57754
  width: 100%;
57755
+ .tooltip-wrapper {
57756
+ width: 100%;
57757
+ }
57700
57758
  `;
57701
57759
  const DropdownContainer = styled.div`
57702
57760
  position: relative;
@@ -57794,6 +57852,41 @@ const Wrapper = styled.div`
57794
57852
  align-items: center;
57795
57853
  width: 100%;
57796
57854
  `;
57855
+
57856
+ // Wrapper for scrollable truncated text
57857
+ const ScrollableTextWrapper = styled.div`
57858
+ flex: 1;
57859
+ display: flex;
57860
+ align-items: center;
57861
+ min-width: 0;
57862
+ overflow: hidden;
57863
+ position: relative;
57864
+ `;
57865
+
57866
+ // Inner text that scrolls when truncated
57867
+ const ScrollableTextInner = styled.span`
57868
+ display: inline-block;
57869
+ white-space: nowrap;
57870
+ line-height: 21px;
57871
+ font-family: "Poppins", sans-serif;
57872
+ font-size: 14px;
57873
+ font-weight: 400;
57874
+ color: ${props => props.color || 'inherit'};
57875
+ max-width: 100%;
57876
+
57877
+ /* Default state: show ellipsis */
57878
+ overflow: hidden;
57879
+ text-overflow: ellipsis;
57880
+
57881
+ ${props => props.$isTruncated && css`
57882
+ ${ScrollableTextWrapper}:hover & {
57883
+ /* On hover: remove ellipsis and start animation */
57884
+ overflow: visible;
57885
+ text-overflow: clip;
57886
+ animation: ${scrollTextAnimation} ${props.$animationDuration || 3}s linear infinite;
57887
+ }
57888
+ `}
57889
+ `;
57797
57890
  const TruncatedText = styled.span`
57798
57891
  flex: 1;
57799
57892
  min-width: 0;
@@ -58110,7 +58203,54 @@ OverlayTemplateDialog.propTypes = {
58110
58203
  buttonHoverColor: PropTypes.string
58111
58204
  };
58112
58205
 
58113
- const OverlayDropdown = _ref => {
58206
+ // Component for scrolling truncated text on hover
58207
+ const ScrollingText = _ref => {
58208
+ let {
58209
+ children,
58210
+ color
58211
+ } = _ref;
58212
+ const wrapperRef = useRef(null);
58213
+ const textRef = useRef(null);
58214
+ const [isTruncated, setIsTruncated] = useState(false);
58215
+ const [scrollDistance, setScrollDistance] = useState(0);
58216
+ const [animationDuration, setAnimationDuration] = useState(3);
58217
+ const checkTruncation = useCallback(() => {
58218
+ if (wrapperRef.current && textRef.current) {
58219
+ const wrapperWidth = wrapperRef.current.offsetWidth;
58220
+ const textWidth = textRef.current.scrollWidth;
58221
+ const isOverflowing = textWidth > wrapperWidth;
58222
+ setIsTruncated(isOverflowing);
58223
+ if (isOverflowing) {
58224
+ // Calculate how much we need to scroll (extra width + small padding)
58225
+ const extraWidth = textWidth - wrapperWidth + 20;
58226
+ setScrollDistance(-extraWidth);
58227
+
58228
+ // Calculate animation duration based on text length (roughly 50px per second)
58229
+ const duration = Math.max(2, Math.min(8, extraWidth / 50));
58230
+ setAnimationDuration(duration);
58231
+ }
58232
+ }
58233
+ }, []);
58234
+ useEffect(() => {
58235
+ checkTruncation();
58236
+
58237
+ // Recheck on window resize
58238
+ window.addEventListener('resize', checkTruncation);
58239
+ return () => window.removeEventListener('resize', checkTruncation);
58240
+ }, [children, checkTruncation]);
58241
+ return /*#__PURE__*/React$1.createElement(ScrollableTextWrapper, {
58242
+ ref: wrapperRef
58243
+ }, /*#__PURE__*/React$1.createElement(ScrollableTextInner, {
58244
+ ref: textRef,
58245
+ color: color,
58246
+ $isTruncated: isTruncated,
58247
+ $animationDuration: animationDuration,
58248
+ style: {
58249
+ '--scroll-distance': `${scrollDistance}px`
58250
+ }
58251
+ }, children));
58252
+ };
58253
+ const OverlayDropdown = _ref2 => {
58114
58254
  let {
58115
58255
  data = [],
58116
58256
  value,
@@ -58136,7 +58276,7 @@ const OverlayDropdown = _ref => {
58136
58276
  height = "auto",
58137
58277
  margin = "8px",
58138
58278
  ...props
58139
- } = _ref;
58279
+ } = _ref2;
58140
58280
  const [open, setOpen] = useState(false);
58141
58281
  const [hoveredText, setHoveredText] = useState(null);
58142
58282
  const [templateDialog, setTemplateDialog] = useState(null);
@@ -58423,9 +58563,8 @@ const OverlayDropdown = _ref => {
58423
58563
  }
58424
58564
  }
58425
58565
  }
58426
- }, /*#__PURE__*/React$1.createElement(TruncatedText, {
58427
- onMouseEnter: () => setHoveredText(displayText),
58428
- onMouseLeave: () => setHoveredText(null)
58566
+ }, /*#__PURE__*/React$1.createElement(ScrollingText, {
58567
+ color: item.value === value ? '#fff' : '#212121'
58429
58568
  }, displayText), iconToShow);
58430
58569
  })))), templateDialog && /*#__PURE__*/React$1.createElement(OverlayTemplateDialog, {
58431
58570
  open: true,