@connectif/ui-components 2.4.3 → 3.0.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## [2.4.3] (2025-11-25)
3
+ ## [2.4.3] - 2025-11-25
4
4
 
5
5
  ### Performance Improvements
6
6
 
@@ -13,3 +13,28 @@
13
13
  - Added optional showTime prop in DateIntervalPicker component.
14
14
  - Added optional keepMounted prop in TabContent.
15
15
  - Added optional disableContentScroll prop in Tabs component.
16
+
17
+ ## [3.0.0] - 2025-12-11
18
+
19
+ ### Added
20
+
21
+ - `Tabs` now supports a `hideScrollButtons` and `hideLineTabs` prop.
22
+
23
+ ### Changed
24
+
25
+ - `Tabs` changes its styles to match the new design.
26
+ - `DateIntervalPicker` changes buttons styles.
27
+
28
+ ### Fixed
29
+
30
+ - Fixed an issue where reloading the `Tabs` component on a non-first tab caused an unwanted scroll effect.
31
+ - Fixed change interval when showTime is true.
32
+
33
+ ### Improvements
34
+
35
+ - Upgrade to Typescript 5.9
36
+ - Upgrade cspell to 6.0.0 (latest)
37
+
38
+ ### ⚠️ Breaking Changes
39
+
40
+ - `TabButton` no longer accepts the `disableUppercase` prop; Text always will be lowercase.
@@ -14,17 +14,11 @@ export type TabButtonProps = React.PropsWithChildren<{
14
14
  * Right margin of the tab
15
15
  */
16
16
  marginRight?: number | string;
17
- /**
18
- * Whether the tab button text is in uppercase or not.
19
- * If true, text will not be transformed to uppercase.
20
- * @default false
21
- */
22
- disableUppercase?: boolean;
23
17
  dataTestId?: string;
24
18
  }>;
25
19
  /**
26
20
  * A TabButton is a button that can be used to switch between tabs.
27
21
  * It is usually used in a Tabs component.
28
22
  */
29
- declare const TabButton: ({ children, disabled, textAlign, marginRight, dataTestId, disableUppercase, ...rest }: TabButtonProps) => import("react/jsx-runtime").JSX.Element;
23
+ declare const TabButton: ({ children, disabled, textAlign, marginRight, dataTestId, ...rest }: TabButtonProps) => import("react/jsx-runtime").JSX.Element;
30
24
  export default TabButton;
@@ -42,6 +42,14 @@ export type TabsProps = {
42
42
  * Hide scroll in tab content
43
43
  */
44
44
  disableContentScroll?: boolean;
45
+ /**
46
+ * Hide scroll in tab buttons
47
+ */
48
+ hideScrollButtons?: boolean;
49
+ /**
50
+ * Hide grey line in tabs
51
+ */
52
+ hideLineTabs?: boolean;
45
53
  };
46
54
  /**
47
55
  * Define a Tabs container, with a fixed header and a transitions on change between tabs.
@@ -52,5 +60,5 @@ export type TabsProps = {
52
60
  * define a callback function to be called when the tab is clicked, so you can do something
53
61
  * with the current tab index.
54
62
  */
55
- declare const Tabs: ({ tabButtons, children, currentTabIndex, onChangeTab, variant, fullHeight, contained, scrollbarGutter, disableContentScroll }: TabsProps) => import("react/jsx-runtime").JSX.Element;
63
+ declare const Tabs: ({ tabButtons, children, currentTabIndex, onChangeTab, variant, fullHeight, contained, scrollbarGutter, disableContentScroll, hideScrollButtons, hideLineTabs }: TabsProps) => import("react/jsx-runtime").JSX.Element;
56
64
  export default Tabs;
package/dist/index.js CHANGED
@@ -5329,9 +5329,9 @@ var tabsTheme = {
5329
5329
  minHeight: "40px"
5330
5330
  },
5331
5331
  indicator: {
5332
- bottom: "initial",
5333
- height: "4px",
5334
- borderRadius: "0 0 4px 4px"
5332
+ bottom: "0",
5333
+ height: "2px",
5334
+ borderRadius: "8px"
5335
5335
  },
5336
5336
  scrollButtons: {
5337
5337
  width: "24px"
@@ -16078,64 +16078,101 @@ function parseDateForLocale(dateString, locale, timezone) {
16078
16078
  const m = tz({ year, month, date }, timezone);
16079
16079
  return m.isValid() ? m.toDate() : /* @__PURE__ */ new Date(NaN);
16080
16080
  }
16081
- function getDatesFromDateInterval(interval, maxSelectableDays, timezone) {
16081
+ function getDatesFromDateInterval(interval, maxSelectableDays, timezone, options) {
16082
16082
  const end = tz(timezone).startOf("day");
16083
16083
  const endDate = end.toDate();
16084
+ let finalStartDate;
16085
+ let finalEndDate;
16086
+ switch (interval) {
16087
+ case "today": {
16088
+ finalStartDate = end.toDate();
16089
+ finalEndDate = end.add(1, "day").toDate();
16090
+ break;
16091
+ }
16092
+ case "yesterday": {
16093
+ finalStartDate = end.subtract(1, "day").toDate();
16094
+ finalEndDate = endDate;
16095
+ break;
16096
+ }
16097
+ case "thisWeekFromSunday": {
16098
+ finalStartDate = end.clone().day(0).toDate();
16099
+ finalEndDate = end.add(1, "day").toDate();
16100
+ break;
16101
+ }
16102
+ case "thisWeekFromMonday": {
16103
+ finalStartDate = end.clone().day(1).toDate();
16104
+ finalEndDate = end.add(1, "day").toDate();
16105
+ break;
16106
+ }
16107
+ case "thisMonth": {
16108
+ finalStartDate = end.clone().date(1).toDate();
16109
+ finalEndDate = end.add(1, "day").toDate();
16110
+ break;
16111
+ }
16112
+ case "thisYear": {
16113
+ finalStartDate = end.clone().month(0).date(1).toDate();
16114
+ finalEndDate = end.add(1, "day").toDate();
16115
+ break;
16116
+ }
16117
+ case "last7Days": {
16118
+ finalStartDate = end.subtract(7, "days").toDate();
16119
+ finalEndDate = endDate;
16120
+ break;
16121
+ }
16122
+ case "last30Days": {
16123
+ finalStartDate = end.subtract(30, "day").toDate();
16124
+ finalEndDate = endDate;
16125
+ break;
16126
+ }
16127
+ case "last90Days": {
16128
+ finalStartDate = end.subtract(90, "day").toDate();
16129
+ finalEndDate = endDate;
16130
+ break;
16131
+ }
16132
+ case "last12Months": {
16133
+ finalStartDate = end.subtract(12, "month").toDate();
16134
+ finalEndDate = endDate;
16135
+ break;
16136
+ }
16137
+ case "all": {
16138
+ finalStartDate = end.subtract(maxSelectableDays - 1, "day").toDate();
16139
+ finalEndDate = endDate;
16140
+ break;
16141
+ }
16142
+ }
16143
+ if (!options?.includeTime) {
16144
+ return { startDate: finalStartDate, endDate: finalEndDate };
16145
+ }
16146
+ const now = tz(timezone);
16147
+ const hour = now.hour();
16148
+ const minute = now.minute();
16149
+ const second = now.second();
16150
+ const millisecond = now.millisecond();
16151
+ const startMoment = tz(finalStartDate, timezone).hour(hour).minute(minute).second(second).millisecond(millisecond);
16152
+ const endMoment = tz(finalEndDate, timezone).hour(hour).minute(minute).second(second).millisecond(millisecond);
16084
16153
  switch (interval) {
16085
16154
  case "today":
16086
16155
  return {
16087
- startDate: end.toDate(),
16088
- endDate: end.add(1, "day").toDate()
16156
+ startDate: finalStartDate,
16157
+ endDate: endMoment.toDate()
16089
16158
  };
16090
16159
  case "yesterday":
16091
16160
  return {
16092
- startDate: end.subtract(1, "day").toDate(),
16093
- endDate
16094
- };
16095
- case "thisWeekFromSunday":
16096
- return {
16097
- startDate: end.clone().day(0).toDate(),
16098
- endDate: end.add(1, "day").toDate()
16099
- };
16100
- case "thisWeekFromMonday":
16101
- return {
16102
- startDate: end.clone().day(1).toDate(),
16103
- endDate: end.add(1, "day").toDate()
16104
- };
16105
- case "thisMonth":
16106
- return {
16107
- startDate: end.clone().date(1).toDate(),
16108
- endDate: end.add(1, "day").toDate()
16161
+ startDate: finalStartDate,
16162
+ endDate: tz(finalEndDate, timezone).endOf("day").toDate()
16109
16163
  };
16110
16164
  case "thisYear":
16165
+ case "thisMonth":
16166
+ case "thisWeekFromMonday":
16167
+ case "thisWeekFromSunday":
16111
16168
  return {
16112
- startDate: end.clone().month(0).date(1).toDate(),
16113
- endDate: end.add(1, "day").toDate()
16114
- };
16115
- case "last7Days":
16116
- return {
16117
- startDate: end.subtract(7, "days").toDate(),
16118
- endDate
16119
- };
16120
- case "last30Days":
16121
- return {
16122
- startDate: end.subtract(30, "day").toDate(),
16123
- endDate
16124
- };
16125
- case "last90Days":
16126
- return {
16127
- startDate: end.subtract(90, "day").toDate(),
16128
- endDate
16129
- };
16130
- case "last12Months":
16131
- return {
16132
- startDate: end.subtract(12, "month").toDate(),
16133
- endDate
16169
+ startDate: finalStartDate,
16170
+ endDate: endMoment.toDate()
16134
16171
  };
16135
- case "all":
16172
+ default:
16136
16173
  return {
16137
- startDate: end.subtract(maxSelectableDays - 1, "day").toDate(),
16138
- endDate
16174
+ startDate: startMoment.toDate(),
16175
+ endDate: endMoment.toDate()
16139
16176
  };
16140
16177
  }
16141
16178
  }
@@ -19731,14 +19768,19 @@ var DateIntervalPickerPopover = ({
19731
19768
  const result = getDatesFromDateInterval(
19732
19769
  interval2,
19733
19770
  maxSelectableDays,
19734
- timezone
19771
+ timezone,
19772
+ { includeTime: !!showTime }
19735
19773
  );
19736
19774
  const displayEndDate2 = getDisplayEndDate(result.endDate, timezone);
19737
19775
  return {
19738
19776
  startDate: dateInputFormatter(result.startDate),
19739
19777
  endDate: dateInputFormatter(displayEndDate2),
19740
19778
  startSimpleDate: dateToSimpleDate(result.startDate, timezone),
19741
- endSimpleDate: dateToSimpleDate(displayEndDate2, timezone)
19779
+ endSimpleDate: dateToSimpleDate(displayEndDate2, timezone),
19780
+ startTime: result.startDate,
19781
+ endTime: result.endDate,
19782
+ comparisonStartTime: result.startDate,
19783
+ comparisonEndTime: result.endDate
19742
19784
  };
19743
19785
  };
19744
19786
  const getStateFieldsForComparisonInterval = (comparisonInterval2, startSimpleDate, endSimpleDate) => {
@@ -19900,7 +19942,8 @@ var DateIntervalPickerPopover = ({
19900
19942
  const handleChangeTime = (value) => {
19901
19943
  setState({
19902
19944
  ...state,
19903
- [getTimePropertyName(state.highlightedInput)]: value
19945
+ [getTimePropertyName(state.highlightedInput)]: value,
19946
+ interval: "custom"
19904
19947
  });
19905
19948
  };
19906
19949
  const handleFocusInputDate = (inputDate) => {
@@ -20199,21 +20242,23 @@ var DateIntervalPickerPopover = ({
20199
20242
  /* @__PURE__ */ jsx108(
20200
20243
  PopoverActions_default,
20201
20244
  {
20245
+ sx: { padding: "16px 24px" },
20202
20246
  leftContent: /* @__PURE__ */ jsx108(
20203
20247
  Button_default,
20204
20248
  {
20205
- variant: "text",
20206
- onClick: handleApply,
20207
- text: t("DATE_INTERVAL_PICKER.APPLY"),
20208
- disabled: isApplyButtonDisabled()
20249
+ variant: "contained",
20250
+ color: "white",
20251
+ onClick: handleCancel,
20252
+ text: t("DATE_INTERVAL_PICKER.CANCEL")
20209
20253
  }
20210
20254
  ),
20211
20255
  rightContent: /* @__PURE__ */ jsx108(
20212
20256
  Button_default,
20213
20257
  {
20214
- variant: "text",
20215
- onClick: handleCancel,
20216
- text: t("DATE_INTERVAL_PICKER.CANCEL")
20258
+ variant: "contained",
20259
+ onClick: handleApply,
20260
+ text: t("DATE_INTERVAL_PICKER.APPLY"),
20261
+ disabled: isApplyButtonDisabled()
20217
20262
  }
20218
20263
  )
20219
20264
  }
@@ -25723,10 +25768,9 @@ import { jsx as jsx148 } from "react/jsx-runtime";
25723
25768
  var TabButton = ({
25724
25769
  children,
25725
25770
  disabled = false,
25726
- textAlign = "left",
25771
+ textAlign = "center",
25727
25772
  marginRight = "24px",
25728
25773
  dataTestId,
25729
- disableUppercase = false,
25730
25774
  ...rest
25731
25775
  }) => /* @__PURE__ */ jsx148(
25732
25776
  MuiTab,
@@ -25740,10 +25784,8 @@ var TabButton = ({
25740
25784
  alignItems: textAlign === "left" ? "start" : "center",
25741
25785
  width: textAlign === "center" ? "100%" : void 0,
25742
25786
  marginRight: "24px",
25743
- ...disableUppercase ? {
25744
- textTransform: "none",
25745
- ...variants.body1
25746
- } : variants.caption
25787
+ textTransform: "none",
25788
+ ...variants["body1-semibold"]
25747
25789
  },
25748
25790
  children
25749
25791
  }
@@ -25751,9 +25793,15 @@ var TabButton = ({
25751
25793
  sx: {
25752
25794
  minHeight: "36px",
25753
25795
  alignItems: "start",
25754
- justifyContent: "start",
25796
+ justifyContent: "center",
25755
25797
  padding: "10px 0",
25756
- marginRight
25798
+ marginRight,
25799
+ borderRadius: "8px",
25800
+ transition: "background 0.2s",
25801
+ color: grey700,
25802
+ "&:not(.Mui-selected):hover": {
25803
+ backgroundColor: grey100
25804
+ }
25757
25805
  },
25758
25806
  disabled,
25759
25807
  "data-testid": dataTestId,
@@ -25806,6 +25854,7 @@ function SwipeableViews({
25806
25854
  const scrollTimeout = useRef23();
25807
25855
  const scrollingMethod = useRef23("none");
25808
25856
  const [previousIndex, setPreviousIndex] = useState32(index);
25857
+ const hideScrollAnimation = useRef23(true);
25809
25858
  useEffect22(() => {
25810
25859
  if (containerRef.current) {
25811
25860
  if (scrollingMethod.current === "manual") {
@@ -25835,8 +25884,9 @@ function SwipeableViews({
25835
25884
  };
25836
25885
  container.scrollTo({
25837
25886
  left: scrollPosition,
25838
- behavior: "smooth"
25887
+ behavior: !hideScrollAnimation.current ? "smooth" : "instant"
25839
25888
  });
25889
+ hideScrollAnimation.current = false;
25840
25890
  animationFrame = requestAnimationFrame(checkScrollCompletion);
25841
25891
  return () => cancelAnimationFrame(animationFrame);
25842
25892
  }
@@ -25875,7 +25925,10 @@ function SwipeableViews({
25875
25925
  {
25876
25926
  style: Object.assign(
25877
25927
  {
25878
- ...!hasShowTab(childIndex) && mountedChild && { display: "none" }
25928
+ ...!hasShowTab(childIndex) && mountedChild && {
25929
+ visibility: "hidden",
25930
+ pointerEvents: "none"
25931
+ }
25879
25932
  },
25880
25933
  styles.slide,
25881
25934
  slideStyle
@@ -25902,9 +25955,84 @@ var Tabs = ({
25902
25955
  fullHeight = false,
25903
25956
  contained = false,
25904
25957
  scrollbarGutter,
25905
- disableContentScroll = false
25958
+ disableContentScroll = false,
25959
+ hideScrollButtons = false,
25960
+ hideLineTabs = false
25906
25961
  }) => {
25962
+ const tabsRef = React84.useRef(null);
25907
25963
  const [value, setValue] = React84.useState(0);
25964
+ const measureTextWidthRange = (child) => {
25965
+ const walker = document.createTreeWalker(child, NodeFilter.SHOW_TEXT, {
25966
+ acceptNode: (node) => node.textContent?.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
25967
+ });
25968
+ const textNode = walker.nextNode();
25969
+ if (!textNode) {
25970
+ return Math.round(child.getBoundingClientRect().width);
25971
+ }
25972
+ const range = document.createRange();
25973
+ range.selectNodeContents(textNode);
25974
+ const rect = range.getBoundingClientRect();
25975
+ return Math.round(rect.width);
25976
+ };
25977
+ const waitForSmoothScrollEnd = React84.useCallback(
25978
+ (container, onEnd) => {
25979
+ let prev = container.scrollLeft;
25980
+ let idleFrames = 0;
25981
+ const check = () => {
25982
+ const current = container.scrollLeft;
25983
+ const isMoving = current !== prev;
25984
+ prev = current;
25985
+ if (isMoving) {
25986
+ idleFrames = 0;
25987
+ requestAnimationFrame(check);
25988
+ return;
25989
+ }
25990
+ if (idleFrames < 5) {
25991
+ idleFrames++;
25992
+ requestAnimationFrame(check);
25993
+ return;
25994
+ }
25995
+ onEnd();
25996
+ };
25997
+ requestAnimationFrame(check);
25998
+ },
25999
+ []
26000
+ );
26001
+ const calculateIndicatorTabWidth = React84.useCallback((index) => {
26002
+ if (!tabsRef.current) {
26003
+ return null;
26004
+ }
26005
+ const tabsContainer = tabsRef.current.querySelector(
26006
+ ".MuiTabs-list"
26007
+ );
26008
+ const indicator = tabsRef.current.querySelector(
26009
+ ".MuiTabs-indicator"
26010
+ );
26011
+ const child = Array.from(tabsContainer.children)[index];
26012
+ const textWidth = measureTextWidthRange(child);
26013
+ const childRect = child.getBoundingClientRect();
26014
+ const listRect = tabsContainer.getBoundingClientRect();
26015
+ const left = childRect.left + (childRect.width - textWidth) / 2 - listRect.left;
26016
+ indicator.style.display = "block";
26017
+ indicator.style.width = `${textWidth}px`;
26018
+ indicator.style.left = `${left}px`;
26019
+ }, []);
26020
+ React84.useEffect(() => {
26021
+ if (!tabsRef.current) {
26022
+ return;
26023
+ }
26024
+ const tabsScroller = tabsRef.current.querySelector(
26025
+ ".MuiTabs-scroller"
26026
+ );
26027
+ waitForSmoothScrollEnd(tabsScroller, () => {
26028
+ calculateIndicatorTabWidth(currentTabIndex ?? value);
26029
+ });
26030
+ }, [
26031
+ calculateIndicatorTabWidth,
26032
+ currentTabIndex,
26033
+ value,
26034
+ waitForSmoothScrollEnd
26035
+ ]);
25908
26036
  const handleChangeIndex = (index) => {
25909
26037
  onChangeTab?.(index);
25910
26038
  setValue(index);
@@ -25913,7 +26041,7 @@ var Tabs = ({
25913
26041
  top: 0
25914
26042
  } : {
25915
26043
  bottom: 0,
25916
- borderRadius: "8px 8px 0 0"
26044
+ borderRadius: "8px"
25917
26045
  };
25918
26046
  return /* @__PURE__ */ jsxs77(
25919
26047
  Box_default2,
@@ -25932,25 +26060,32 @@ var Tabs = ({
25932
26060
  /* @__PURE__ */ jsx150(
25933
26061
  MuiTabs,
25934
26062
  {
26063
+ ref: tabsRef,
25935
26064
  value: currentTabIndex ?? value,
25936
26065
  onChange: (_event, index) => {
25937
26066
  handleChangeIndex(index);
25938
26067
  },
25939
26068
  variant,
25940
- scrollButtons: true,
26069
+ scrollButtons: !hideScrollButtons,
25941
26070
  allowScrollButtonsMobile: true,
25942
- TabScrollButtonProps: {
25943
- sx: {
25944
- "&.Mui-disabled": {
25945
- display: contained ? "none" : void 0
26071
+ slotProps: {
26072
+ indicator: {
26073
+ sx: {
26074
+ ...highlightedPositionSx,
26075
+ display: "none"
26076
+ }
26077
+ },
26078
+ scrollButtons: {
26079
+ sx: {
26080
+ "&.Mui-disabled": {
26081
+ display: contained ? "none" : void 0
26082
+ }
25946
26083
  }
25947
26084
  }
25948
26085
  },
25949
- TabIndicatorProps: {
25950
- sx: highlightedPositionSx
25951
- },
25952
26086
  sx: {
25953
- borderBottom: contained ? `1px solid ${grey200}` : void 0
26087
+ borderBottom: contained && !hideLineTabs ? `1px solid ${grey200}` : void 0,
26088
+ borderTop: !contained && !hideLineTabs ? `1px solid ${grey200}` : void 0
25954
26089
  },
25955
26090
  children: tabButtons
25956
26091
  }
@@ -26870,27 +27005,27 @@ var MinimizableWindow = React88.forwardRef(function MinimizableWindow2({
26870
27005
  var MinimizableWindow_default = MinimizableWindow;
26871
27006
 
26872
27007
  // src/hooks/useFormatters.ts
26873
- import { useCallback as useCallback20, useContext as useContext16 } from "react";
27008
+ import { useCallback as useCallback21, useContext as useContext16 } from "react";
26874
27009
  var useFormatters = () => {
26875
27010
  const { locale, currency, timezone } = useContext16(IntlContext);
26876
27011
  return {
26877
- formatCompactNumber: useCallback20(
27012
+ formatCompactNumber: useCallback21(
26878
27013
  (value) => formatCompactNumber(value, locale),
26879
27014
  [locale]
26880
27015
  ),
26881
- formatNumber: useCallback20(
27016
+ formatNumber: useCallback21(
26882
27017
  (value, fractionSize) => formatNumber(value, locale, fractionSize),
26883
27018
  [locale]
26884
27019
  ),
26885
- formatPercentage: useCallback20(
27020
+ formatPercentage: useCallback21(
26886
27021
  (value, fractionSize) => formatPercentage(value, locale, fractionSize),
26887
27022
  [locale]
26888
27023
  ),
26889
- formatCurrency: useCallback20(
27024
+ formatCurrency: useCallback21(
26890
27025
  (value, notation) => formatCurrency(value, locale, currency, notation),
26891
27026
  [currency, locale]
26892
27027
  ),
26893
- formatDate: useCallback20(
27028
+ formatDate: useCallback21(
26894
27029
  (date, format2) => formatDate(date, locale, timezone, format2),
26895
27030
  [locale, timezone]
26896
27031
  )