@mailstep/design-system 0.10.6 → 0.11.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.
Files changed (48) hide show
  1. package/package.json +1 -1
  2. package/ui/Blocks/LoginPage/LoginPage.js +3 -2
  3. package/ui/Blocks/LoginPage/components/LoginForm.d.ts +4 -3
  4. package/ui/Blocks/LoginPage/components/LoginForm.js +7 -3
  5. package/ui/Blocks/LoginPage/types.d.ts +1 -0
  6. package/ui/Blocks/Tabs/hooks/useTabs.d.ts +1 -1
  7. package/ui/Blocks/Tabs/hooks/useTabs.js +16 -4
  8. package/ui/Blocks/Tabs/index.d.ts +1 -1
  9. package/ui/Blocks/Tabs/types.d.ts +5 -1
  10. package/ui/Blocks/Tabs/utils/constants.d.ts +1 -0
  11. package/ui/Blocks/Tabs/utils/constants.js +4 -0
  12. package/ui/Blocks/Tabs/utils/getDefaultTab.d.ts +4 -0
  13. package/ui/Blocks/Tabs/utils/getDefaultTab.js +7 -0
  14. package/ui/Blocks/Tabs/utils/getInitialTab.d.ts +9 -0
  15. package/ui/Blocks/Tabs/utils/getInitialTab.js +12 -0
  16. package/ui/Blocks/Tabs/utils/index.d.ts +6 -0
  17. package/ui/Blocks/Tabs/utils/index.js +7 -0
  18. package/ui/Blocks/Tabs/utils/isSelectableTab.d.ts +8 -0
  19. package/ui/Blocks/Tabs/utils/isSelectableTab.js +7 -0
  20. package/ui/Blocks/Tabs/utils/readTabFromUrl.d.ts +8 -0
  21. package/ui/Blocks/Tabs/utils/readTabFromUrl.js +16 -0
  22. package/ui/Blocks/Tabs/utils/writeTabToUrl.d.ts +7 -0
  23. package/ui/Blocks/Tabs/utils/writeTabToUrl.js +10 -0
  24. package/ui/Elements/DatePicker/DatePicker.d.ts +1 -1
  25. package/ui/Elements/DatePicker/DatePicker.js +18 -25
  26. package/ui/Elements/DatePicker/Datetime/components/{Timepicker.d.ts → Timepicker/Timepicker.d.ts} +2 -2
  27. package/ui/Elements/DatePicker/Datetime/components/Timepicker/Timepicker.js +181 -0
  28. package/ui/Elements/DatePicker/Datetime/components/Timepicker/constants.d.ts +5 -0
  29. package/ui/Elements/DatePicker/Datetime/components/Timepicker/constants.js +8 -0
  30. package/ui/Elements/DatePicker/Datetime/components/Timepicker/index.d.ts +1 -0
  31. package/ui/Elements/DatePicker/Datetime/components/Timepicker/index.js +2 -0
  32. package/ui/Elements/DatePicker/Datetime/components/Timepicker/styles.d.ts +3 -0
  33. package/ui/Elements/DatePicker/Datetime/components/Timepicker/styles.js +48 -0
  34. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/index.d.ts +3 -0
  35. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/index.js +4 -0
  36. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/padTime.d.ts +3 -0
  37. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/padTime.js +4 -0
  38. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/parseTimeField.d.ts +3 -0
  39. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/parseTimeField.js +8 -0
  40. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/sanitizeTimeField.d.ts +3 -0
  41. package/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/sanitizeTimeField.js +4 -0
  42. package/ui/Elements/DatePicker/Datetime/views/DaysView.js +2 -1
  43. package/ui/Elements/DatePicker/components/DatePickerInput/DatePickerInput.d.ts +15 -0
  44. package/ui/Elements/DatePicker/components/DatePickerInput/DatePickerInput.js +33 -0
  45. package/ui/Elements/DatePicker/components/DatePickerInput/index.d.ts +1 -0
  46. package/ui/Elements/DatePicker/components/DatePickerInput/index.js +2 -0
  47. package/ui/index.js +1 -1
  48. package/ui/Elements/DatePicker/Datetime/components/Timepicker.js +0 -168
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailstep/design-system",
3
- "version": "0.10.6",
3
+ "version": "0.11.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "main": "./ui/index.js",
@@ -9,7 +9,7 @@ import { x } from "@xstyled/styled-components";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Trans } from "@lingui/react";
11
11
  //#region packages/ui/Blocks/LoginPage/LoginPage.tsx
12
- const Login = ({ onSubmit, activeLanguage, onLanguageChange, children, brand, brandVariant, isSubmitting, isSubmitDisabled, withDistributionCenterSelect, distributionCenterOptions, selectedDistributionCenter, onChangeDistributionCenter, languageItems, version, backgroundImage, admin, hasSupport = true, hasSocialLinks = true, withPassword = true, withLogin = true, loginLabel, passwordLabel }) => {
12
+ const Login = ({ onSubmit, activeLanguage, onLanguageChange, children, brand, brandVariant, isSubmitting, isSubmitDisabled, withDistributionCenterSelect, distributionCenterOptions, selectedDistributionCenter, onChangeDistributionCenter, languageItems, version, backgroundImage, admin, hasSupport = true, hasSocialLinks = true, withPassword = true, withLogin = true, loginLabel, passwordLabel, formHeader }) => {
13
13
  return /* @__PURE__ */ jsxs(Wrapper, {
14
14
  backgroundImage,
15
15
  children: [
@@ -50,7 +50,8 @@ const Login = ({ onSubmit, activeLanguage, onLanguageChange, children, brand, br
50
50
  withPassword,
51
51
  withLogin,
52
52
  loginLabel,
53
- passwordLabel
53
+ passwordLabel,
54
+ formHeader
54
55
  })
55
56
  ] }),
56
57
  children,
@@ -1,6 +1,6 @@
1
- import { FC } from 'react';
2
- import { Option } from '../../../Elements/Select/types';
3
- import { LoginData } from '../types';
1
+ import { type FC, type ReactNode } from 'react';
2
+ import { type Option } from '../../../Elements/Select/types';
3
+ import { type LoginData } from '../types';
4
4
  type Props = {
5
5
  onSubmit: (loginData: LoginData) => Promise<void>;
6
6
  isSubmitting?: boolean;
@@ -13,6 +13,7 @@ type Props = {
13
13
  loginLabel?: string;
14
14
  passwordLabel?: string;
15
15
  withLogin?: boolean;
16
+ formHeader?: ReactNode;
16
17
  };
17
18
  export declare const LoginForm: FC<Props>;
18
19
  export {};
@@ -16,7 +16,7 @@ const Container = styled$1.div`
16
16
  display: none;
17
17
  }
18
18
  @media (min-width: 1024px) {
19
- height: 300px;
19
+ min-height: 300px;
20
20
  min-width: 300px;
21
21
  margin-bottom: 50px;
22
22
  & > div > H4 {
@@ -33,7 +33,7 @@ const ButtonsContainer = styled$1.div`
33
33
  margin-top: 40px;
34
34
  }
35
35
  `;
36
- const LoginForm = ({ onSubmit, isSubmitting, isSubmitDisabled, distributionCenterOptions, withDistributionCenterSelect, onChangeDistributionCenter, selectedDistributionCenter, withPassword, withLogin, loginLabel, passwordLabel }) => {
36
+ const LoginForm = ({ onSubmit, isSubmitting, isSubmitDisabled, distributionCenterOptions, withDistributionCenterSelect, onChangeDistributionCenter, selectedDistributionCenter, withPassword, withLogin, loginLabel, passwordLabel, formHeader }) => {
37
37
  const [login, setLogin] = useState("");
38
38
  const [password, setPassword] = useState("");
39
39
  const [submitError, setSubmitError] = useState("");
@@ -76,7 +76,7 @@ const LoginForm = ({ onSubmit, isSubmitting, isSubmitDisabled, distributionCente
76
76
  login,
77
77
  password
78
78
  }).catch((error) => {
79
- error && setSubmitError(String(error));
79
+ error && setSubmitError(error instanceof Error ? error.message : String(error));
80
80
  }).finally(() => {
81
81
  setPassword("");
82
82
  });
@@ -92,6 +92,10 @@ const LoginForm = ({ onSubmit, isSubmitting, isSubmitDisabled, distributionCente
92
92
  onChangeDistributionCenter?.(e.target.value);
93
93
  }, [onChangeDistributionCenter]);
94
94
  return /* @__PURE__ */ jsxs(Container, { children: [
95
+ formHeader && /* @__PURE__ */ jsx(x.div, {
96
+ mt: "24px",
97
+ children: formHeader
98
+ }),
95
99
  withLogin && /* @__PURE__ */ jsx(x.div, {
96
100
  mt: "24px",
97
101
  children: /* @__PURE__ */ jsx(Input, {
@@ -31,4 +31,5 @@ export type LoginPageProps = {
31
31
  loginLabel?: string;
32
32
  passwordLabel?: string;
33
33
  admin?: boolean;
34
+ formHeader?: ReactNode;
34
35
  };
@@ -1,2 +1,2 @@
1
- import { UseTabsHook } from '../types';
1
+ import type { UseTabsHook } from '../types';
2
2
  export declare const useTabs: UseTabsHook;
@@ -1,13 +1,25 @@
1
+ import "../utils/constants.js";
2
+ import { getInitialTab } from "../utils/getInitialTab.js";
3
+ import { writeTabToUrl } from "../utils/writeTabToUrl.js";
1
4
  import { useCallback, useState } from "react";
2
5
  //#region packages/ui/Blocks/Tabs/hooks/useTabs.ts
3
- const useTabs = (tabDefinitions) => {
4
- const index = tabDefinitions.map((item) => item.default).indexOf(true);
5
- const [activeTab, setActiveTab] = useState(index === -1 ? 0 : index);
6
+ const useTabs = (tabsDefinition, options = {}) => {
7
+ const { syncWithUrl = false, queryParam = "tab" } = options;
8
+ const [activeTab, setActiveTab] = useState(() => getInitialTab({
9
+ tabsDefinition,
10
+ syncWithUrl,
11
+ queryParam
12
+ }));
6
13
  return {
7
14
  activeTab,
8
15
  onTabSwitch: useCallback((value) => {
9
16
  setActiveTab(value);
10
- }, [])
17
+ if (!syncWithUrl) return;
18
+ writeTabToUrl({
19
+ value,
20
+ queryParam
21
+ });
22
+ }, [syncWithUrl, queryParam])
11
23
  };
12
24
  };
13
25
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { Tabs } from './Tabs';
2
2
  export { TabContent } from './TabContent';
3
3
  export { useTabs } from './hooks/useTabs';
4
- export type { TabDefinition } from './types';
4
+ export type { TabDefinition, UseTabsOptions } from './types';
5
5
  export default Tabs;
@@ -21,7 +21,11 @@ export type TabElementProps = {
21
21
  onTabSwitch: (tab: number) => void;
22
22
  minWidth?: string | string[];
23
23
  };
24
- export type UseTabsHook = (tabDefinitions: TabDefinition[]) => {
24
+ export type UseTabsOptions = {
25
+ syncWithUrl?: boolean;
26
+ queryParam?: string;
27
+ };
28
+ export type UseTabsHook = (tabsDefinition: TabDefinition[], options?: UseTabsOptions) => {
25
29
  activeTab: number;
26
30
  onTabSwitch: (tab: number) => void;
27
31
  };
@@ -0,0 +1 @@
1
+ export declare const TAB_PARAM = "tab";
@@ -0,0 +1,4 @@
1
+ //#region packages/ui/Blocks/Tabs/utils/constants.ts
2
+ const TAB_PARAM = "tab";
3
+ //#endregion
4
+ export { TAB_PARAM };
@@ -0,0 +1,4 @@
1
+ import type { TabDefinition } from '../types';
2
+ type GetDefaultTabType = (tabsDefinition: TabDefinition[]) => number;
3
+ export declare const getDefaultTab: GetDefaultTabType;
4
+ export {};
@@ -0,0 +1,7 @@
1
+ //#region packages/ui/Blocks/Tabs/utils/getDefaultTab.ts
2
+ const getDefaultTab = (tabsDefinition) => {
3
+ const index = tabsDefinition.map((item) => item.default).indexOf(true);
4
+ return index === -1 ? 0 : index;
5
+ };
6
+ //#endregion
7
+ export { getDefaultTab };
@@ -0,0 +1,9 @@
1
+ import type { TabDefinition } from '../types';
2
+ type GetInitialTabProps = {
3
+ tabsDefinition: TabDefinition[];
4
+ syncWithUrl: boolean;
5
+ queryParam: string;
6
+ };
7
+ type GetInitialTabType = (props: GetInitialTabProps) => number;
8
+ export declare const getInitialTab: GetInitialTabType;
9
+ export {};
@@ -0,0 +1,12 @@
1
+ import { getDefaultTab } from "./getDefaultTab.js";
2
+ import { readTabFromUrl } from "./readTabFromUrl.js";
3
+ //#region packages/ui/Blocks/Tabs/utils/getInitialTab.ts
4
+ const getInitialTab = ({ tabsDefinition, syncWithUrl, queryParam }) => {
5
+ if (!syncWithUrl) return getDefaultTab(tabsDefinition);
6
+ return readTabFromUrl({
7
+ tabsDefinition,
8
+ queryParam
9
+ }) ?? getDefaultTab(tabsDefinition);
10
+ };
11
+ //#endregion
12
+ export { getInitialTab };
@@ -0,0 +1,6 @@
1
+ export { TAB_PARAM } from './constants';
2
+ export { getDefaultTab } from './getDefaultTab';
3
+ export { getInitialTab } from './getInitialTab';
4
+ export { isSelectableTab } from './isSelectableTab';
5
+ export { readTabFromUrl } from './readTabFromUrl';
6
+ export { writeTabToUrl } from './writeTabToUrl';
@@ -0,0 +1,7 @@
1
+ import { TAB_PARAM } from "./constants.js";
2
+ import { getDefaultTab } from "./getDefaultTab.js";
3
+ import { isSelectableTab } from "./isSelectableTab.js";
4
+ import { readTabFromUrl } from "./readTabFromUrl.js";
5
+ import { getInitialTab } from "./getInitialTab.js";
6
+ import { writeTabToUrl } from "./writeTabToUrl.js";
7
+ export { TAB_PARAM, getDefaultTab, getInitialTab, isSelectableTab, readTabFromUrl, writeTabToUrl };
@@ -0,0 +1,8 @@
1
+ import type { TabDefinition } from '../types';
2
+ type IsSelectableTabProps = {
3
+ tabsDefinition: TabDefinition[];
4
+ value: number;
5
+ };
6
+ type IsSelectableTabType = (props: IsSelectableTabProps) => boolean;
7
+ export declare const isSelectableTab: IsSelectableTabType;
8
+ export {};
@@ -0,0 +1,7 @@
1
+ //#region packages/ui/Blocks/Tabs/utils/isSelectableTab.ts
2
+ const isSelectableTab = ({ tabsDefinition, value }) => {
3
+ const tab = tabsDefinition[value];
4
+ return Boolean(tab) && !tab.disabled;
5
+ };
6
+ //#endregion
7
+ export { isSelectableTab };
@@ -0,0 +1,8 @@
1
+ import type { TabDefinition } from '../types';
2
+ type ReadTabFromUrlProps = {
3
+ tabsDefinition: TabDefinition[];
4
+ queryParam: string;
5
+ };
6
+ type ReadTabFromUrlType = (props: ReadTabFromUrlProps) => number | null;
7
+ export declare const readTabFromUrl: ReadTabFromUrlType;
8
+ export {};
@@ -0,0 +1,16 @@
1
+ import { isSelectableTab } from "./isSelectableTab.js";
2
+ //#region packages/ui/Blocks/Tabs/utils/readTabFromUrl.ts
3
+ const readTabFromUrl = ({ tabsDefinition, queryParam }) => {
4
+ if (typeof window === "undefined") return null;
5
+ const raw = new URLSearchParams(window.location.search).get(queryParam);
6
+ if (raw === null || raw.trim() === "") return null;
7
+ const value = Number(raw);
8
+ if (!Number.isInteger(value)) return null;
9
+ if (!isSelectableTab({
10
+ tabsDefinition,
11
+ value
12
+ })) return null;
13
+ return value;
14
+ };
15
+ //#endregion
16
+ export { readTabFromUrl };
@@ -0,0 +1,7 @@
1
+ type WriteTabToUrlProps = {
2
+ value: number;
3
+ queryParam: string;
4
+ };
5
+ type WriteTabToUrlType = (props: WriteTabToUrlProps) => void;
6
+ export declare const writeTabToUrl: WriteTabToUrlType;
7
+ export {};
@@ -0,0 +1,10 @@
1
+ //#region packages/ui/Blocks/Tabs/utils/writeTabToUrl.ts
2
+ const writeTabToUrl = ({ value, queryParam }) => {
3
+ if (typeof window === "undefined") return;
4
+ const url = new URL(window.location.href);
5
+ if (url.searchParams.get(queryParam) === String(value)) return;
6
+ url.searchParams.set(queryParam, String(value));
7
+ window.history.replaceState(null, "", `${url.pathname}${url.search}${url.hash}`);
8
+ };
9
+ //#endregion
10
+ export { writeTabToUrl };
@@ -22,7 +22,7 @@ export type DatePickerType = DatetimepickerProps & {
22
22
  isSecondDatePicker?: boolean;
23
23
  others?: DateTimeOthers;
24
24
  onClear?: () => void;
25
- onInputChange?: () => void;
25
+ onInputChange?: (value: string) => void;
26
26
  onChangeOthers?: (others: DateTimeOthers) => void;
27
27
  };
28
28
  declare const _default: React.NamedExoticComponent<DatePickerType>;
@@ -1,12 +1,12 @@
1
1
  import { LanguageContext } from "../../ThemeProvider/ThemeProvider.js";
2
2
  import SpaceAround_default from "../SpaceAround/index.js";
3
- import Input_default from "../../Forms/Input/index.js";
4
3
  import Datetime from "./Datetime/DateTime.js";
4
+ import { DatePickerInput } from "./components/DatePickerInput/DatePickerInput.js";
5
+ import "./components/DatePickerInput/index.js";
5
6
  import { DatePickerBase } from "./styles.js";
6
7
  import React, { useCallback, useContext } from "react";
7
8
  import { x } from "@xstyled/styled-components";
8
9
  import { jsx } from "react/jsx-runtime";
9
- import moment from "moment";
10
10
  import "moment/dist/locale/cs";
11
11
  import "moment/dist/locale/el";
12
12
  import "moment/dist/locale/it";
@@ -15,34 +15,27 @@ import "moment/dist/locale/sk";
15
15
  //#region packages/ui/Elements/DatePicker/DatePicker.tsx
16
16
  const defaultDateFormat = "DD.MM.YYYY";
17
17
  const DatePicker = ({ name, value, initialViewDate, error, label, disabled, isValidDate, onChange, dateFormat = defaultDateFormat, timeFormat = false, open, secondValue = null, input, icon = true, showInputLabel = false, showCalendarLabel = true, spaceAround = false, className, onClear, onInputChange, ...passProps }) => {
18
- const format = `${dateFormat}${timeFormat ? ` ${timeFormat}` : ``}`;
19
- const isValid = !!value && moment(value, format, true).isValid();
20
- const stringValue = isValid ? moment(value, format).format(format) : value;
21
18
  const locale = useContext(LanguageContext);
22
- const handleRenderInput = useCallback((_, openCalendar) => {
23
- return /* @__PURE__ */ jsx(Input_default, {
24
- type: "text",
25
- icon: icon ? "calendar" : void 0,
26
- name,
27
- error,
28
- disabled,
29
- isInvalid: !!error,
30
- onChange: onInputChange,
31
- label: showInputLabel ? label : void 0,
32
- value: stringValue,
33
- onFocus: openCalendar,
34
- iconOnClick: openCalendar,
35
- onClear,
36
- autoComplete: "off"
37
- });
38
- }, [
19
+ const handleRenderInput = useCallback((inputProps, openCalendar) => /* @__PURE__ */ jsx(DatePickerInput, {
20
+ inputProps,
21
+ openCalendar,
39
22
  name,
40
- stringValue,
23
+ label,
41
24
  error,
25
+ disabled,
26
+ icon,
27
+ showInputLabel,
28
+ onClear,
29
+ onInputChange
30
+ }), [
31
+ name,
42
32
  label,
33
+ error,
34
+ disabled,
35
+ icon,
43
36
  showInputLabel,
44
- isValid,
45
- disabled
37
+ onClear,
38
+ onInputChange
46
39
  ]);
47
40
  const handleRenderView = useCallback((_, renderCalendar) => {
48
41
  if (label && !showInputLabel) return /* @__PURE__ */ jsx(x.div, {
@@ -1,8 +1,8 @@
1
1
  import { type FC } from 'react';
2
- interface TimepickerProps {
2
+ type TimepickerProps = {
3
3
  setTime: (type: string, value: number) => void;
4
4
  value: Date | null;
5
5
  disabled?: boolean;
6
- }
6
+ };
7
7
  export declare const Timepicker: FC<TimepickerProps>;
8
8
  export {};
@@ -0,0 +1,181 @@
1
+ import Icon from "../../../../Icon/Icon.js";
2
+ import { FooterRow } from "../FooterRow.js";
3
+ import { FooterRowLabel } from "../FooterRowLabel.js";
4
+ import "./constants.js";
5
+ import { TimeButton, TimeField, TimeWrapper } from "./styles.js";
6
+ import { padTime } from "./utils/padTime.js";
7
+ import { parseTimeField } from "./utils/parseTimeField.js";
8
+ import { sanitizeTimeField } from "./utils/sanitizeTimeField.js";
9
+ import { useCallback, useEffect, useRef, useState } from "react";
10
+ import { x } from "@xstyled/styled-components";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { i18n } from "@lingui/core";
13
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/Timepicker.tsx
14
+ const Timepicker = ({ setTime, value, disabled }) => {
15
+ const hours = value?.getHours?.() ?? 0;
16
+ const minutes = value?.getMinutes?.() ?? 0;
17
+ const [hoursValue, setHoursValue] = useState(padTime(hours));
18
+ const [minutesValue, setMinutesValue] = useState(padTime(minutes));
19
+ const isHoursEdited = useRef(false);
20
+ const isMinutesEdited = useRef(false);
21
+ useEffect(() => {
22
+ if (isHoursEdited.current) return;
23
+ setHoursValue(padTime(hours));
24
+ }, [hours]);
25
+ useEffect(() => {
26
+ if (isMinutesEdited.current) return;
27
+ setMinutesValue(padTime(minutes));
28
+ }, [minutes]);
29
+ const commitTime = useCallback((type, nextValue) => {
30
+ if (!value) return;
31
+ setTime(type, nextValue);
32
+ }, [setTime, value]);
33
+ const changeHours = useCallback((nextValue) => {
34
+ setHoursValue(padTime(nextValue));
35
+ commitTime("hours", nextValue);
36
+ }, [commitTime]);
37
+ const changeMinutes = useCallback((nextValue) => {
38
+ setMinutesValue(padTime(nextValue));
39
+ commitTime("minutes", nextValue);
40
+ }, [commitTime]);
41
+ const increaseHours = useCallback(() => {
42
+ changeHours((parseTimeField(hoursValue, hours) + 1) % 24);
43
+ }, [
44
+ changeHours,
45
+ hoursValue,
46
+ hours
47
+ ]);
48
+ const decreaseHours = useCallback(() => {
49
+ changeHours((parseTimeField(hoursValue, hours) - 1 + 24) % 24);
50
+ }, [
51
+ changeHours,
52
+ hoursValue,
53
+ hours
54
+ ]);
55
+ const increaseMinutes = useCallback(() => {
56
+ changeMinutes((parseTimeField(minutesValue, minutes) + 5) % 60);
57
+ }, [
58
+ changeMinutes,
59
+ minutesValue,
60
+ minutes
61
+ ]);
62
+ const decreaseMinutes = useCallback(() => {
63
+ changeMinutes((parseTimeField(minutesValue, minutes) - 5 + 60) % 60);
64
+ }, [
65
+ changeMinutes,
66
+ minutesValue,
67
+ minutes
68
+ ]);
69
+ const onHoursChange = useCallback((e) => {
70
+ const nextValue = sanitizeTimeField(e.target.value);
71
+ setHoursValue(nextValue);
72
+ const parsed = parseTimeField(nextValue, NaN);
73
+ if (Number.isNaN(parsed) || parsed > 23) return;
74
+ commitTime("hours", parsed);
75
+ }, [commitTime]);
76
+ const onMinutesChange = useCallback((e) => {
77
+ const nextValue = sanitizeTimeField(e.target.value);
78
+ setMinutesValue(nextValue);
79
+ const parsed = parseTimeField(nextValue, NaN);
80
+ if (Number.isNaN(parsed) || parsed > 59) return;
81
+ commitTime("minutes", parsed);
82
+ }, [commitTime]);
83
+ const onFieldClick = useCallback((e) => {
84
+ e.currentTarget.select();
85
+ }, []);
86
+ const onHoursFocus = useCallback((e) => {
87
+ isHoursEdited.current = true;
88
+ e.target.select();
89
+ }, []);
90
+ const onMinutesFocus = useCallback((e) => {
91
+ isMinutesEdited.current = true;
92
+ e.target.select();
93
+ }, []);
94
+ const onHoursBlur = useCallback(() => {
95
+ isHoursEdited.current = false;
96
+ const parsed = parseTimeField(hoursValue, NaN);
97
+ setHoursValue(padTime(!Number.isNaN(parsed) && parsed <= 23 ? parsed : hours));
98
+ }, [hoursValue, hours]);
99
+ const onMinutesBlur = useCallback(() => {
100
+ isMinutesEdited.current = false;
101
+ const parsed = parseTimeField(minutesValue, NaN);
102
+ setMinutesValue(padTime(!Number.isNaN(parsed) && parsed <= 59 ? parsed : minutes));
103
+ }, [minutesValue, minutes]);
104
+ return /* @__PURE__ */ jsxs(FooterRow, {
105
+ disabled,
106
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Icon, { icon: "clock" }), /* @__PURE__ */ jsx(FooterRowLabel, { children: i18n._({
107
+ id: "dataGrid.filterCell.time",
108
+ message: "Time"
109
+ }) })] }), /* @__PURE__ */ jsx(x.div, {
110
+ display: "flex",
111
+ flexDirection: "column",
112
+ alignItems: "flex-start",
113
+ pt: "4px",
114
+ w: "fit-content",
115
+ children: /* @__PURE__ */ jsxs(x.div, {
116
+ display: "flex",
117
+ alignItems: "center",
118
+ children: [
119
+ /* @__PURE__ */ jsxs(x.div, {
120
+ display: "flex",
121
+ flexDirection: "column",
122
+ children: [/* @__PURE__ */ jsx(TimeButton, {
123
+ disabled,
124
+ onClick: increaseHours,
125
+ type: "button",
126
+ children: "‹"
127
+ }), /* @__PURE__ */ jsx(TimeButton, {
128
+ disabled,
129
+ onClick: decreaseHours,
130
+ type: "button",
131
+ children: "›"
132
+ })]
133
+ }),
134
+ /* @__PURE__ */ jsxs(TimeWrapper, { children: [
135
+ /* @__PURE__ */ jsx(TimeField, {
136
+ value: hoursValue,
137
+ disabled,
138
+ inputMode: "numeric",
139
+ maxLength: 2,
140
+ onChange: onHoursChange,
141
+ onFocus: onHoursFocus,
142
+ onClick: onFieldClick,
143
+ onBlur: onHoursBlur
144
+ }),
145
+ /* @__PURE__ */ jsx(x.div, {
146
+ mx: "1px",
147
+ children: ":"
148
+ }),
149
+ /* @__PURE__ */ jsx(TimeField, {
150
+ value: minutesValue,
151
+ disabled,
152
+ inputMode: "numeric",
153
+ maxLength: 2,
154
+ onChange: onMinutesChange,
155
+ onFocus: onMinutesFocus,
156
+ onClick: onFieldClick,
157
+ onBlur: onMinutesBlur
158
+ })
159
+ ] }),
160
+ /* @__PURE__ */ jsxs(x.div, {
161
+ display: "flex",
162
+ flexDirection: "column",
163
+ children: [/* @__PURE__ */ jsx(TimeButton, {
164
+ disabled,
165
+ onClick: increaseMinutes,
166
+ type: "button",
167
+ children: "‹"
168
+ }), /* @__PURE__ */ jsx(TimeButton, {
169
+ disabled,
170
+ onClick: decreaseMinutes,
171
+ type: "button",
172
+ children: "›"
173
+ })]
174
+ })
175
+ ]
176
+ })
177
+ })]
178
+ });
179
+ };
180
+ //#endregion
181
+ export { Timepicker };
@@ -0,0 +1,5 @@
1
+ export declare const HOURS_COUNT = 24;
2
+ export declare const MINUTES_COUNT = 60;
3
+ export declare const MINUTES_STEP = 5;
4
+ export declare const MAX_HOURS: number;
5
+ export declare const MAX_MINUTES: number;
@@ -0,0 +1,8 @@
1
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/constants.ts
2
+ const HOURS_COUNT = 24;
3
+ const MINUTES_COUNT = 60;
4
+ const MINUTES_STEP = 5;
5
+ const MAX_HOURS = 23;
6
+ const MAX_MINUTES = 59;
7
+ //#endregion
8
+ export { HOURS_COUNT, MAX_HOURS, MAX_MINUTES, MINUTES_COUNT, MINUTES_STEP };
@@ -0,0 +1 @@
1
+ export { Timepicker } from './Timepicker';
@@ -0,0 +1,2 @@
1
+ import { Timepicker } from "./Timepicker.js";
2
+ export { Timepicker };
@@ -0,0 +1,3 @@
1
+ export declare const TimeButton: import("styled-components").StyledComponent<"button", import("@xstyled/styled-components").Theme, {}, never>;
2
+ export declare const TimeWrapper: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
3
+ export declare const TimeField: import("styled-components").StyledComponent<"input", import("@xstyled/styled-components").Theme, {}, never>;
@@ -0,0 +1,48 @@
1
+ import styled$1 from "@xstyled/styled-components";
2
+ import { th as th$1 } from "@xstyled/system";
3
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/styles.ts
4
+ const TimeButton = styled$1.button`
5
+ transform: rotate(90deg);
6
+ font-size: 20px;
7
+ line-height: 1;
8
+ margin: -3px 0;
9
+ padding: 0;
10
+ background-color: transparent !important;
11
+ border: none;
12
+ color: ${th$1("colors.lightGray4")};
13
+ &:disabled:hover {
14
+ color: ${th$1("colors.lightGray4")};
15
+ cursor: default;
16
+ }
17
+ &:hover {
18
+ color: ${th$1("colors.red2")};
19
+ background-color: transparent !important;
20
+ }
21
+ `;
22
+ const TimeWrapper = styled$1.div`
23
+ display: flex;
24
+ align-items: baseline;
25
+ justify-content: center;
26
+ border: 1px solid ${th$1("colors.lightGray2")};
27
+ border-radius: 6px;
28
+ padding: 3px 8px;
29
+ margin: 0 4px;
30
+ `;
31
+ const TimeField = styled$1.input`
32
+ font-size: 16px;
33
+ font-weight: 600;
34
+ line-height: 20px;
35
+ width: 24px;
36
+ text-align: center;
37
+ margin: 0;
38
+ padding: 0;
39
+ border: none;
40
+ outline: none;
41
+ background-color: transparent;
42
+ color: ${th$1("colors.blue2")};
43
+ &:disabled {
44
+ opacity: 0.5;
45
+ }
46
+ `;
47
+ //#endregion
48
+ export { TimeButton, TimeField, TimeWrapper };
@@ -0,0 +1,3 @@
1
+ export { padTime } from './padTime';
2
+ export { parseTimeField } from './parseTimeField';
3
+ export { sanitizeTimeField } from './sanitizeTimeField';
@@ -0,0 +1,4 @@
1
+ import { padTime } from "./padTime.js";
2
+ import { parseTimeField } from "./parseTimeField.js";
3
+ import { sanitizeTimeField } from "./sanitizeTimeField.js";
4
+ export { padTime, parseTimeField, sanitizeTimeField };
@@ -0,0 +1,3 @@
1
+ type PadTimeType = (value: number) => string;
2
+ export declare const padTime: PadTimeType;
3
+ export {};
@@ -0,0 +1,4 @@
1
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/padTime.ts
2
+ const padTime = (value) => String(value).padStart(2, "0");
3
+ //#endregion
4
+ export { padTime };
@@ -0,0 +1,3 @@
1
+ type ParseTimeFieldType = (text: string, fallback: number) => number;
2
+ export declare const parseTimeField: ParseTimeFieldType;
3
+ export {};
@@ -0,0 +1,8 @@
1
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/parseTimeField.ts
2
+ const parseTimeField = (text, fallback) => {
3
+ if (text === "") return fallback;
4
+ const parsed = Number(text);
5
+ return Number.isNaN(parsed) ? fallback : parsed;
6
+ };
7
+ //#endregion
8
+ export { parseTimeField };
@@ -0,0 +1,3 @@
1
+ type SanitizeTimeFieldType = (text: string) => string;
2
+ export declare const sanitizeTimeField: SanitizeTimeFieldType;
3
+ export {};
@@ -0,0 +1,4 @@
1
+ //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker/utils/sanitizeTimeField.ts
2
+ const sanitizeTimeField = (text) => text.replace(/\D/g, "").slice(0, 2);
3
+ //#endregion
4
+ export { sanitizeTimeField };
@@ -1,5 +1,6 @@
1
1
  import { DateRepeater } from "../components/DateRepeater.js";
2
- import { Timepicker } from "../components/Timepicker.js";
2
+ import { Timepicker } from "../components/Timepicker/Timepicker.js";
3
+ import "../components/Timepicker/index.js";
3
4
  import ViewNavigation from "../components/ViewNavigation.js";
4
5
  import React from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -0,0 +1,15 @@
1
+ import { type FC, type InputHTMLAttributes } from 'react';
2
+ type DatePickerInputProps = {
3
+ inputProps: InputHTMLAttributes<HTMLInputElement>;
4
+ openCalendar: () => void;
5
+ name?: string;
6
+ label?: string;
7
+ error?: string;
8
+ disabled?: boolean;
9
+ icon?: boolean;
10
+ showInputLabel?: boolean;
11
+ onClear?: () => void;
12
+ onInputChange?: (value: string) => void;
13
+ };
14
+ export declare const DatePickerInput: FC<DatePickerInputProps>;
15
+ export {};
@@ -0,0 +1,33 @@
1
+ import Input_default from "../../../../Forms/Input/index.js";
2
+ import { useCallback, useState } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region packages/ui/Elements/DatePicker/components/DatePickerInput/DatePickerInput.tsx
5
+ const DatePickerInput = ({ inputProps, openCalendar, name, label, error, disabled, icon, showInputLabel, onClear, onInputChange }) => {
6
+ const [typedValue, setTypedValue] = useState(null);
7
+ const handleChange = useCallback((e) => {
8
+ setTypedValue(e.target.value);
9
+ inputProps.onChange?.(e);
10
+ onInputChange?.(e.target.value);
11
+ }, [inputProps, onInputChange]);
12
+ const handleBlur = useCallback(() => {
13
+ setTypedValue(null);
14
+ }, []);
15
+ return /* @__PURE__ */ jsx(Input_default, {
16
+ type: "text",
17
+ icon: icon ? "calendar" : void 0,
18
+ name,
19
+ error,
20
+ disabled,
21
+ isInvalid: !!error,
22
+ onChange: handleChange,
23
+ onBlur: handleBlur,
24
+ label: showInputLabel ? label : void 0,
25
+ value: typedValue ?? inputProps.value,
26
+ onFocus: openCalendar,
27
+ iconOnClick: openCalendar,
28
+ onClear,
29
+ autoComplete: "off"
30
+ });
31
+ };
32
+ //#endregion
33
+ export { DatePickerInput };
@@ -0,0 +1 @@
1
+ export { DatePickerInput } from './DatePickerInput';
@@ -0,0 +1,2 @@
1
+ import { DatePickerInput } from "./DatePickerInput.js";
2
+ export { DatePickerInput };
package/ui/index.js CHANGED
@@ -133,8 +133,8 @@ import SpaceAroundWrap from "./Elements/SpaceAround/SpaceAround.js";
133
133
  import SpaceAround_default from "./Elements/SpaceAround/index.js";
134
134
  import Select_default from "./Elements/Select/index.js";
135
135
  import SingleSelect_default from "./Elements/SingleSelect/index.js";
136
- import Input_default from "./Forms/Input/index.js";
137
136
  import { DatePickerRepeat, TimePickerRepeat } from "./Elements/DatePicker/Datetime/types.js";
137
+ import Input_default from "./Forms/Input/index.js";
138
138
  import DatePicker_default from "./Elements/DatePicker/index.js";
139
139
  import { useEvent } from "./Blocks/CommonGrid/hooks/useEvent.js";
140
140
  import { useAddFilter } from "./Blocks/CommonGrid/hooks/useAddFilter.js";
@@ -1,168 +0,0 @@
1
- import Icon from "../../../Icon/Icon.js";
2
- import { FooterRow } from "./FooterRow.js";
3
- import { FooterRowLabel } from "./FooterRowLabel.js";
4
- import { useCallback, useEffect, useState } from "react";
5
- import styled$1, { x } from "@xstyled/styled-components";
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
- import { th as th$1 } from "@xstyled/system";
8
- import { i18n } from "@lingui/core";
9
- //#region packages/ui/Elements/DatePicker/Datetime/components/Timepicker.tsx
10
- const Btn = styled$1.button`
11
- transform: rotate(90deg);
12
- font-size: 18px;
13
- margin: -4px 0;
14
- background-color: transparent !important;
15
- border: none;
16
- color: ${th$1("colors.lightGray4")};
17
- &:disabled:hover {
18
- color: ${th$1("colors.lightGray4")};
19
- cursor: default;
20
- }
21
- &:hover {
22
- color: ${th$1("colors.red2")};
23
- background-color: transparent !important;
24
- }
25
- `;
26
- const TimeWrapper = styled$1.div`
27
- display: flex;
28
- align-items: baseline;
29
- justify-content: center;
30
- border: 1px solid ${th$1("colors.lightGray2")};
31
- border-radius: 6px;
32
- padding: 0 4px;
33
- margin: 0 4px;
34
- `;
35
- const Hours = styled$1.input`
36
- font-size: 14px;
37
- font-weight: 600;
38
- width: 17px;
39
- margin: 0;
40
- padding: 0;
41
- border: none;
42
- outline: none;
43
- background-color: transparent;
44
- color: #22394E;
45
- &:disabled {
46
- opacity: 0.5;
47
- }
48
- `;
49
- const Minutes = styled$1.input`
50
- font-weight: 600;
51
- font-size: 14px;
52
- width: 18px;
53
- margin: 0;
54
- padding: 0;
55
- border: none;
56
- outline: none;
57
- background-color: transparent;
58
- color: #22394E;
59
- &:disabled {
60
- opacity: 0.5;
61
- }
62
- `;
63
- const regExp = "^[0-9]*$";
64
- const Timepicker = ({ setTime, value, disabled }) => {
65
- const initialHours = value?.getHours?.() ?? 0;
66
- const initialMinutes = value?.getMinutes?.() ?? 0;
67
- const [hours, setHours] = useState(initialHours);
68
- const [minutes, setMinutes] = useState(initialMinutes);
69
- const increaseHour = useCallback(() => {
70
- setHours((prev) => (prev + 1) % 24);
71
- }, []);
72
- const decreaseHour = useCallback(() => {
73
- setHours((prev) => prev === 0 ? 23 : prev - 1);
74
- }, []);
75
- const increaseMinutes = useCallback(() => {
76
- setMinutes((prev) => (prev + 5) % 60);
77
- }, []);
78
- const decreaseMinutes = useCallback(() => {
79
- setMinutes((prev) => (prev - 5 + 60) % 60);
80
- }, []);
81
- const onHoursChange = useCallback((e) => {
82
- if (e.target.value.match(regExp) && Number(e.target.value) <= 23) setHours(Number(e.target.value));
83
- else setHours(0);
84
- }, []);
85
- const onMinutesChange = useCallback((e) => {
86
- if (e.target.value.match(regExp) && Number(e.target.value) <= 59) setMinutes(Number(e.target.value));
87
- else setMinutes(0);
88
- }, []);
89
- useEffect(() => {
90
- value && setTime("hours", hours);
91
- }, [hours]);
92
- useEffect(() => {
93
- value && setTime("minutes", minutes);
94
- }, [minutes]);
95
- useEffect(() => {
96
- setHours(initialHours);
97
- }, [initialHours]);
98
- useEffect(() => {
99
- setMinutes(initialMinutes);
100
- }, [initialMinutes]);
101
- return /* @__PURE__ */ jsxs(FooterRow, {
102
- disabled,
103
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Icon, { icon: "clock" }), /* @__PURE__ */ jsx(FooterRowLabel, { children: i18n._({
104
- id: "dataGrid.filterCell.time",
105
- message: "Time"
106
- }) })] }), /* @__PURE__ */ jsx(x.div, {
107
- display: "flex",
108
- flexDirection: "column",
109
- alignItems: "flex-start",
110
- pt: "4px",
111
- w: "fit-content",
112
- children: /* @__PURE__ */ jsxs(x.div, {
113
- display: "flex",
114
- alignItems: "center",
115
- children: [
116
- /* @__PURE__ */ jsxs(x.div, {
117
- display: "flex",
118
- flexDirection: "column",
119
- children: [/* @__PURE__ */ jsx(Btn, {
120
- disabled,
121
- onClick: increaseHour,
122
- type: "button",
123
- children: "‹"
124
- }), /* @__PURE__ */ jsx(Btn, {
125
- disabled,
126
- onClick: decreaseHour,
127
- type: "button",
128
- children: "›"
129
- })]
130
- }),
131
- /* @__PURE__ */ jsxs(TimeWrapper, { children: [
132
- /* @__PURE__ */ jsx(Hours, {
133
- value: String(hours).padStart(2, "0"),
134
- disabled,
135
- onChange: onHoursChange
136
- }),
137
- /* @__PURE__ */ jsx(x.div, {
138
- mx: "1px",
139
- children: ":"
140
- }),
141
- /* @__PURE__ */ jsx(Minutes, {
142
- value: String(minutes).padStart(2, "0"),
143
- disabled,
144
- onChange: onMinutesChange
145
- })
146
- ] }),
147
- /* @__PURE__ */ jsxs(x.div, {
148
- display: "flex",
149
- flexDirection: "column",
150
- children: [/* @__PURE__ */ jsx(Btn, {
151
- disabled,
152
- onClick: increaseMinutes,
153
- type: "button",
154
- children: "‹"
155
- }), /* @__PURE__ */ jsx(Btn, {
156
- disabled,
157
- onClick: decreaseMinutes,
158
- type: "button",
159
- children: "›"
160
- })]
161
- })
162
- ]
163
- })
164
- })]
165
- });
166
- };
167
- //#endregion
168
- export { Timepicker };