@mailstep/design-system 0.11.0 → 0.12.0-beta.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailstep/design-system",
3
- "version": "0.11.0",
3
+ "version": "0.12.0-beta.1",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "main": "./ui/index.js",
@@ -5,8 +5,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region packages/ui/Blocks/CommonGrid/components/Filters/ActionsFilter/ActionsFilter.tsx
6
6
  const ActionsFilter = ({ actionColumn, rowsData, uxState, handleUxChange }) => {
7
7
  const [allChecked, toggleCheckbox] = useToggleAllCheckbox(rowsData, uxState, handleUxChange);
8
- const displayRowNumberLabel = actionColumn?.addRowNumbers;
9
- const rowsNumberLabel = actionColumn?.addRowNumbersLabel ?? "#";
8
+ const rowsNumberLabel = actionColumn?.addRowNumbersLabel;
10
9
  const displayCheckbox = actionColumn?.actionOptions?.length || actionColumn?.forceCheckboxes;
11
10
  return /* @__PURE__ */ jsxs(x.div, {
12
11
  display: "flex",
@@ -21,7 +20,7 @@ const ActionsFilter = ({ actionColumn, rowsData, uxState, handleUxChange }) => {
21
20
  onChange: toggleCheckbox,
22
21
  label: ""
23
22
  }, "check-all")
24
- }), displayRowNumberLabel && /* @__PURE__ */ jsx(x.div, {
23
+ }), actionColumn?.addRowNumbersLabel && /* @__PURE__ */ jsx(x.div, {
25
24
  w: "100%",
26
25
  pr: "12px",
27
26
  display: "flex",
@@ -19,7 +19,6 @@ const IconButtonInCell = ({ icon, isLoading, onClick, linkTo }) => {
19
19
  "data-cy": `${icon ?? ""}IconBtn`,
20
20
  cursor: "pointer",
21
21
  h: "100%",
22
- w: "100%",
23
22
  display: "flex",
24
23
  alignItems: "center",
25
24
  justifyContent: "center",
@@ -5,6 +5,7 @@ type Props = DataCellProps<{
5
5
  }> & {
6
6
  addRowNumbers?: boolean;
7
7
  rowIndex?: number;
8
+ rowNumberWidth?: number;
8
9
  actionColumnDefinition?: ActionColumn;
9
10
  checkedRows?: UxState['checkedRows'];
10
11
  onUxChange?: GridActionsType['handleUxChange'];
@@ -3,9 +3,9 @@ import { IconButtonInCell } from "./IconButtonInCell.js";
3
3
  import { useCallback, useMemo } from "react";
4
4
  import { x } from "@xstyled/styled-components";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
- import { isFunction } from "lodash/fp";
6
+ import { isFunction, omit } from "lodash/fp";
7
7
  //#region packages/ui/Blocks/CommonGrid/components/gridCells/RowActionsCell.tsx
8
- const RowActionsCell = ({ actionColumnDefinition, rowData, addRowNumbers, rowIndex, checkedRows, withCheckboxes, onUxChange, onRowAction }) => {
8
+ const RowActionsCell = ({ actionColumnDefinition, rowData, addRowNumbers, rowIndex, rowNumberWidth = 0, checkedRows, withCheckboxes, onUxChange, onRowAction }) => {
9
9
  const rowDataId = rowData?.id;
10
10
  const canEdit = isFunction(actionColumnDefinition?.canRowEdit) ? actionColumnDefinition?.canRowEdit(rowData) : actionColumnDefinition?.canRowEdit;
11
11
  const canRead = isFunction(actionColumnDefinition?.canRowRead) ? actionColumnDefinition?.canRowRead(rowData) : actionColumnDefinition?.canRowRead;
@@ -26,9 +26,10 @@ const RowActionsCell = ({ actionColumnDefinition, rowData, addRowNumbers, rowInd
26
26
  }, [canEdit, rowData?.id]);
27
27
  const isChecked = !!checkedRows?.[rowDataId];
28
28
  const onCheckboxChange = useCallback(() => {
29
- const newCheckedRows = { ...checkedRows || {} };
30
- if (!isChecked) newCheckedRows[rowDataId] = true;
31
- else delete newCheckedRows[rowDataId];
29
+ const newCheckedRows = isChecked ? omit(rowDataId, checkedRows || {}) : {
30
+ ...checkedRows || {},
31
+ [rowDataId]: true
32
+ };
32
33
  onUxChange?.("checkedRows", newCheckedRows);
33
34
  }, [
34
35
  onUxChange,
@@ -40,14 +41,13 @@ const RowActionsCell = ({ actionColumnDefinition, rowData, addRowNumbers, rowInd
40
41
  if (isFunction(onClick)) onClick();
41
42
  }, [onClick]);
42
43
  return /* @__PURE__ */ jsxs(x.div, {
43
- w: "100%",
44
44
  display: "flex",
45
- gap: "16px",
45
+ gap: "12px",
46
46
  pl: "4px",
47
47
  pr: "4px",
48
48
  children: [
49
49
  withCheckboxes && /* @__PURE__ */ jsx(x.div, {
50
- w: "100%",
50
+ w: "18px",
51
51
  children: /* @__PURE__ */ jsx(Checkbox_default, {
52
52
  checked: isChecked,
53
53
  onChange: onCheckboxChange,
@@ -58,8 +58,8 @@ const RowActionsCell = ({ actionColumnDefinition, rowData, addRowNumbers, rowInd
58
58
  display: "flex",
59
59
  alignItems: "center",
60
60
  justifyContent: "center",
61
- pr: "8px",
62
- w: "100%",
61
+ w: `${rowNumberWidth}px`,
62
+ flexShrink: 0,
63
63
  children: rowIndex
64
64
  }),
65
65
  canEdit && /* @__PURE__ */ jsx(IconButtonInCell, {
@@ -1,3 +1,4 @@
1
+ import { getActionsColumnExtraWidth, getRowNumbersExtraWidth } from "../utils/index.js";
1
2
  import "../types.js";
2
3
  import withProps from "../HoC/withProps.js";
3
4
  import { RowActionsCell } from "../components/gridCells/RowActionsCell.js";
@@ -5,25 +6,35 @@ import "../components/gridCells/index.js";
5
6
  import { useCallback, useMemo } from "react";
6
7
  //#region packages/ui/Blocks/CommonGrid/hooks/useEditReadAsColumn.tsx
7
8
  const useEditReadAsColumn = (props) => {
8
- const { onRowAction, actionColumnDefinition, columnsDefinitions, gridSelectors, gridActions, onRowEditClick, onRowReadClick } = props;
9
+ const { onRowAction, actionColumnDefinition, columnsDefinitions, gridSelectors, gridActions, onRowEditClick, onRowReadClick, rowsData } = props;
9
10
  const modifiedOnRowAction = useCallback((id, field, value, rowData) => {
10
11
  if (field === "row_edit" || field === "row_read") {
11
12
  if (field === "row_edit" && onRowEditClick) onRowEditClick(id, value);
12
13
  if (field === "row_read" && onRowReadClick) onRowReadClick(id, value);
13
- } else if (onRowAction) return onRowAction(id, field, value, rowData);
14
+ } else if (onRowAction) onRowAction(id, field, value, rowData);
14
15
  }, [
15
16
  onRowAction,
16
17
  onRowEditClick,
17
18
  onRowReadClick
18
19
  ]);
19
- const { canRowEdit, canRowRead, addRowNumbers, stickTo = "left", flexBasis = 20 } = actionColumnDefinition || {};
20
+ const { canRowEdit, canRowRead, addRowNumbers, stickTo = "left", flexBasis = 0 } = actionColumnDefinition || {};
20
21
  const withCheckboxes = !!actionColumnDefinition?.actionOptions?.length || actionColumnDefinition?.forceCheckboxes;
21
22
  const hasActions = canRowEdit || canRowRead || withCheckboxes || addRowNumbers;
23
+ const page = gridSelectors?.page || 1;
24
+ const maxRowNumber = (gridSelectors?.rowsPerPage || 10) * (page - 1) + (rowsData?.length || 0);
25
+ const actionColumnFlexBasis = flexBasis + getActionsColumnExtraWidth({
26
+ addRowNumbers,
27
+ withCheckboxes,
28
+ canRowEdit,
29
+ canRowRead,
30
+ maxRowNumber
31
+ });
32
+ const rowNumberWidth = getRowNumbersExtraWidth(maxRowNumber);
22
33
  return {
23
34
  columnsDefinitions: useMemo(() => {
24
35
  const editReadColumnDefinition = {
25
36
  name: "actions-cell-cg",
26
- flexBasis,
37
+ flexBasis: actionColumnFlexBasis,
27
38
  filtering: withCheckboxes || addRowNumbers,
28
39
  filteringType: "actions",
29
40
  sorting: false,
@@ -34,6 +45,7 @@ const useEditReadAsColumn = (props) => {
34
45
  actionColumnDefinition,
35
46
  withCheckboxes,
36
47
  addRowNumbers: actionColumnDefinition?.addRowNumbers,
48
+ rowNumberWidth,
37
49
  checkedRows: gridSelectors?.uxState?.checkedRows,
38
50
  onUxChange: gridActions?.handleUxChange
39
51
  })(RowActionsCell)
@@ -42,7 +54,8 @@ const useEditReadAsColumn = (props) => {
42
54
  else return [...columnsDefinitions, editReadColumnDefinition];
43
55
  }, [
44
56
  addRowNumbers,
45
- flexBasis,
57
+ actionColumnFlexBasis,
58
+ rowNumberWidth,
46
59
  stickTo,
47
60
  hasActions,
48
61
  columnsDefinitions,
@@ -86,11 +86,9 @@ export type RowProps = {
86
86
  id: string;
87
87
  [rowClassSymbol]?: string;
88
88
  };
89
- type ColumnBaseProps = {
90
- flexBasis: number;
91
- };
92
89
  export type RowValueType = string | React.ReactNode | number | void;
93
- export type ColumnDefinition<ColumnName extends string = string> = ColumnBaseProps & {
90
+ export type ColumnDefinition<ColumnName extends string = string> = {
91
+ flexBasis: number;
94
92
  asyncLoadKey?: string;
95
93
  name: ColumnName;
96
94
  group?: Group['name'];
@@ -140,7 +138,8 @@ type batchAction = {
140
138
  isEnabled?: (checkedRows: any[]) => boolean;
141
139
  };
142
140
  export type OnBatchActionFn<T = RowProps> = (action: batchAction, affectedRows: T[]) => void;
143
- export type ActionColumn = ColumnBaseProps & {
141
+ export type ActionColumn = {
142
+ flexBasis?: number;
144
143
  actionOptions?: batchAction[];
145
144
  canRowEdit?: boolean | ((row: any) => boolean);
146
145
  canRowRead?: boolean | ((row: any) => boolean);
@@ -1,4 +1,4 @@
1
- import type { ColumnDefinition, ColumnConfig, FilterProps, Group, StickTo, FiltersConfig } from '../types';
1
+ import type { ColumnDefinition, ColumnConfig, FilterProps, Group, StickTo, FiltersConfig, ActionColumn } from '../types';
2
2
  type CellSizePropsResult = {
3
3
  flexBasis?: number;
4
4
  flexGrow?: number;
@@ -13,6 +13,15 @@ export declare const calculatePaginator: (page: number, rowsPerPage: number) =>
13
13
  offset: number;
14
14
  limit: number;
15
15
  };
16
+ export declare const getRowNumbersExtraWidth: (maxRowNumber?: number) => number;
17
+ type ActionsColumnExtraWidthParams = {
18
+ addRowNumbers?: boolean;
19
+ withCheckboxes?: boolean;
20
+ canRowEdit?: ActionColumn['canRowEdit'];
21
+ canRowRead?: ActionColumn['canRowRead'];
22
+ maxRowNumber?: number;
23
+ };
24
+ export declare const getActionsColumnExtraWidth: ({ addRowNumbers, withCheckboxes, canRowEdit, canRowRead, maxRowNumber }: ActionsColumnExtraWidthParams) => number;
16
25
  export declare const createFilterType: (columnDefinition: ColumnDefinition) => FilterProps["type"];
17
26
  export declare const getGroups: (columns: ColumnDefinition[]) => Group[];
18
27
  export declare const getGroupClassNames: (group?: Group) => string;
@@ -4,6 +4,7 @@ import BooleanSelect_default from "../components/Filters/BooleanSelect/index.js"
4
4
  import DatePickerRange_default from "../components/Filters/DatePickerRange/index.js";
5
5
  import NumberRange_default from "../components/Filters/NumberRange/index.js";
6
6
  import TextRange_default from "../components/Filters/TextRange/index.js";
7
+ import sum from "lodash/sum";
7
8
  //#region packages/ui/Blocks/CommonGrid/utils/index.ts
8
9
  const isDefined = (val) => typeof val !== "undefined";
9
10
  const getCellSizeProps = (column, columnWidth) => {
@@ -34,6 +35,25 @@ const calculatePaginator = (page, rowsPerPage) => ({
34
35
  offset: rowsPerPage ? rowsPerPage * (page - 1) : rowsPerPage,
35
36
  limit: rowsPerPage
36
37
  });
38
+ const ROW_NUMBER_DIGIT_WIDTH = 8;
39
+ const CHECKBOX_WIDTH = 18;
40
+ const ICON_BUTTON_WIDTH = 20;
41
+ const ACTIONS_CELL_GAP = 12;
42
+ const ACTIONS_CELL_PADDING = 16;
43
+ const getRowNumbersExtraWidth = (maxRowNumber = 0) => {
44
+ return String(maxRowNumber).length * ROW_NUMBER_DIGIT_WIDTH;
45
+ };
46
+ const getActionsColumnExtraWidth = ({ addRowNumbers, withCheckboxes, canRowEdit, canRowRead, maxRowNumber = 0 }) => {
47
+ const hasActionIcon = !!canRowEdit || !!canRowRead;
48
+ const parts = [];
49
+ if (withCheckboxes) parts.push(CHECKBOX_WIDTH);
50
+ if (addRowNumbers) parts.push(getRowNumbersExtraWidth(maxRowNumber));
51
+ if (hasActionIcon) parts.push(ICON_BUTTON_WIDTH);
52
+ if (!parts.length) return 0;
53
+ const gaps = (parts.length - 1) * ACTIONS_CELL_GAP;
54
+ parts.push(ACTIONS_CELL_PADDING);
55
+ return sum(parts) + gaps;
56
+ };
37
57
  const createFilterType = (columnDefinition) => {
38
58
  if (columnDefinition.filterOptions || columnDefinition.asyncLoadKey) return "options";
39
59
  else return columnDefinition.filteringType || "text";
@@ -75,4 +95,4 @@ const checksColumnsOrder = (colsOrder, columnsDefinitions) => {
75
95
  if (stickyRightColls.length && colsOrder.indexOf(stickyRightColls[0].name) !== colsOrder.length - 1 && stickyRightColls[0].name !== "actions-cell-cg") console.error("The Right sticky column should be the last one in a row");
76
96
  };
77
97
  //#endregion
78
- export { calculatePaginator, checksColumnsOrder, createFilterType, getCellSizeProps, getFilterName, getFilters, getGroupClassNames, getGroups, getSortName, getStickyCollClassNames, isColumnOn, isFilterActive };
98
+ export { calculatePaginator, checksColumnsOrder, createFilterType, getActionsColumnExtraWidth, getCellSizeProps, getFilterName, getFilters, getGroupClassNames, getGroups, getRowNumbersExtraWidth, getSortName, getStickyCollClassNames, isColumnOn, isFilterActive };
@@ -0,0 +1,2 @@
1
+ export declare const IMAGE_MIME_PREFIX = "image/";
2
+ export declare const INVALID_FORMAT_MESSAGE = "Unsupported file format";
@@ -0,0 +1,5 @@
1
+ //#region packages/ui/Blocks/ImageList/components/AddPhoto/constants.ts
2
+ const IMAGE_MIME_PREFIX = "image/";
3
+ const INVALID_FORMAT_MESSAGE = "Unsupported file format";
4
+ //#endregion
5
+ export { IMAGE_MIME_PREFIX, INVALID_FORMAT_MESSAGE };
@@ -0,0 +1,11 @@
1
+ import { type ChangeEvent } from 'react';
2
+ import { type ImageUploadProps } from '../../../types';
3
+ type Props = {
4
+ onImageUpload?: (props: ImageUploadProps) => Promise<void>;
5
+ };
6
+ type HookType = (props: Props) => {
7
+ handleImageUpload: (event: ChangeEvent<HTMLInputElement>) => void;
8
+ hasInvalidFormat: boolean;
9
+ };
10
+ export declare const useImageUpload: HookType;
11
+ export {};
@@ -0,0 +1,34 @@
1
+ import "../constants.js";
2
+ import { useCallback, useState } from "react";
3
+ //#region packages/ui/Blocks/ImageList/components/AddPhoto/hooks/useImageUpload.ts
4
+ const useImageUpload = ({ onImageUpload }) => {
5
+ const [hasInvalidFormat, setHasInvalidFormat] = useState(false);
6
+ return {
7
+ handleImageUpload: useCallback((event) => {
8
+ const input = event.target;
9
+ const file = input?.files?.[0];
10
+ if (!file) {
11
+ setHasInvalidFormat(false);
12
+ return;
13
+ }
14
+ if (!file.type.startsWith("image/")) {
15
+ input.value = "";
16
+ setHasInvalidFormat(true);
17
+ return;
18
+ }
19
+ setHasInvalidFormat(false);
20
+ const reader = new FileReader();
21
+ reader.readAsDataURL(file);
22
+ reader.onload = async () => {
23
+ await onImageUpload?.({
24
+ base64image: reader.result?.split(",").pop(),
25
+ base64WithDataDeclaration: reader.result,
26
+ file
27
+ });
28
+ };
29
+ }, [onImageUpload]),
30
+ hasInvalidFormat
31
+ };
32
+ };
33
+ //#endregion
34
+ export { useImageUpload };
@@ -1,9 +1,9 @@
1
1
  import { type FC } from 'react';
2
2
  import { type ImageUploadProps } from '../../types';
3
- interface AddPhotoProps {
3
+ type AddPhotoProps = {
4
4
  onImageUpload?: (props: ImageUploadProps) => Promise<void>;
5
5
  isDisabled?: boolean;
6
6
  hasRemove?: boolean;
7
- }
7
+ };
8
8
  export declare const AddPhoto: FC<AddPhotoProps>;
9
9
  export {};
@@ -1,29 +1,15 @@
1
1
  import { AddPhoto as AddPhoto$1 } from "../../../../Elements/Icon/icons/AddPhoto.js";
2
- import { Container, HiddenInput } from "./styles.js";
3
- import { useCallback } from "react";
2
+ import ErrorMessage from "../../../../Elements/ErrorMessage/ErrorMessage.js";
3
+ import "../../../../Elements/ErrorMessage/index.js";
4
+ import "./constants.js";
5
+ import { useImageUpload } from "./hooks/useImageUpload.js";
6
+ import { Container, ErrorWrapper, HiddenInput, Wrapper } from "./styles.js";
4
7
  import { jsx, jsxs } from "react/jsx-runtime";
5
8
  //#region packages/ui/Blocks/ImageList/components/AddPhoto/index.tsx
6
- const IMAGE_MIME_PREFIX = "image/";
7
9
  const AddPhoto = ({ onImageUpload, isDisabled, hasRemove }) => {
8
- const handleImageUpload = useCallback((event) => {
9
- const input = event.target;
10
- const file = input?.files?.[0];
11
- if (!file?.type.startsWith(IMAGE_MIME_PREFIX)) {
12
- input.value = "";
13
- return;
14
- }
15
- const reader = new FileReader();
16
- reader.readAsDataURL(file);
17
- reader.onload = async () => {
18
- await onImageUpload?.({
19
- base64image: reader.result?.split(",").pop(),
20
- base64WithDataDeclaration: reader.result,
21
- file
22
- });
23
- };
24
- }, [onImageUpload]);
10
+ const { handleImageUpload, hasInvalidFormat } = useImageUpload({ onImageUpload });
25
11
  if (!onImageUpload) return null;
26
- return /* @__PURE__ */ jsxs(Container, {
12
+ return /* @__PURE__ */ jsxs(Wrapper, { children: [/* @__PURE__ */ jsxs(Container, {
27
13
  isDisabled,
28
14
  hasRemove,
29
15
  children: [/* @__PURE__ */ jsx(HiddenInput, {
@@ -36,7 +22,13 @@ const AddPhoto = ({ onImageUpload, isDisabled, hasRemove }) => {
36
22
  width: "36",
37
23
  height: "36"
38
24
  })]
39
- });
25
+ }), hasInvalidFormat && /* @__PURE__ */ jsx(ErrorWrapper, {
26
+ hasRemove,
27
+ children: /* @__PURE__ */ jsx(ErrorMessage, {
28
+ appearance: "inline",
29
+ children: "Unsupported file format"
30
+ })
31
+ })] });
40
32
  };
41
33
  //#endregion
42
34
  export { AddPhoto };
@@ -3,3 +3,7 @@ export declare const Container: import("styled-components").StyledComponent<"div
3
3
  hasRemove?: boolean;
4
4
  }, never>;
5
5
  export declare const HiddenInput: import("styled-components").StyledComponent<"input", import("@xstyled/styled-components").Theme, {}, never>;
6
+ export declare const Wrapper: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
7
+ export declare const ErrorWrapper: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {
8
+ hasRemove?: boolean;
9
+ }, never>;
@@ -26,5 +26,17 @@ const HiddenInput = styled$1.input`
26
26
  height: 100%;
27
27
  cursor: pointer;
28
28
  `;
29
+ const Wrapper = styled$1.div`
30
+ position: relative;
31
+ flex-shrink: 0;
32
+ `;
33
+ const ErrorWrapper = styled$1.div`
34
+ position: absolute;
35
+ top: ${({ hasRemove }) => hasRemove ? "100px" : "85px"};
36
+ left: 0;
37
+ width: 85px;
38
+ line-height: 1.2;
39
+ text-align: center;
40
+ `;
29
41
  //#endregion
30
- export { Container, HiddenInput };
42
+ export { Container, ErrorWrapper, HiddenInput, Wrapper };