@mailstep/design-system 0.9.3 → 0.10.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 (29) hide show
  1. package/package.json +1 -1
  2. package/ui/Blocks/CommonGrid/CommonGridContainer.js +18 -5
  3. package/ui/Blocks/CommonGrid/components/ControlButtons/ControlButtons.d.ts +1 -0
  4. package/ui/Blocks/CommonGrid/components/ControlButtons/ControlButtons.js +43 -20
  5. package/ui/Blocks/CommonGrid/components/ControlButtons/styles.d.ts +2 -0
  6. package/ui/Blocks/CommonGrid/components/ControlButtons/styles.js +22 -6
  7. package/ui/Blocks/CommonGrid/components/ExtraControlButtons/ExtraControlButtons.js +2 -3
  8. package/ui/Blocks/CommonGrid/hooks/useApplySharedGridState.d.ts +9 -0
  9. package/ui/Blocks/CommonGrid/hooks/useApplySharedGridState.js +22 -0
  10. package/ui/Blocks/CommonGrid/hooks/useQuickFilter.d.ts +10 -7
  11. package/ui/Blocks/CommonGrid/hooks/useQuickFilter.js +16 -19
  12. package/ui/Blocks/CommonGrid/hooks/useShareGrid.d.ts +11 -0
  13. package/ui/Blocks/CommonGrid/hooks/useShareGrid.js +37 -0
  14. package/ui/Blocks/CommonGrid/types.d.ts +1 -0
  15. package/ui/Blocks/CommonGrid/utils/shareUrl/buildShareUrl.d.ts +10 -0
  16. package/ui/Blocks/CommonGrid/utils/shareUrl/buildShareUrl.js +66 -0
  17. package/ui/Blocks/CommonGrid/utils/shareUrl/clearShareParams.d.ts +1 -0
  18. package/ui/Blocks/CommonGrid/utils/shareUrl/clearShareParams.js +12 -0
  19. package/ui/Blocks/CommonGrid/utils/shareUrl/constants.d.ts +5 -0
  20. package/ui/Blocks/CommonGrid/utils/shareUrl/constants.js +8 -0
  21. package/ui/Blocks/CommonGrid/utils/shareUrl/index.d.ts +4 -0
  22. package/ui/Blocks/CommonGrid/utils/shareUrl/index.js +4 -0
  23. package/ui/Blocks/CommonGrid/utils/shareUrl/parseSharedGridState.d.ts +13 -0
  24. package/ui/Blocks/CommonGrid/utils/shareUrl/parseSharedGridState.js +89 -0
  25. package/ui/Elements/Icon/Icon.js +9 -2
  26. package/ui/Elements/Select/Select.js +9 -3
  27. package/ui/Elements/Select/components/SelectAll.d.ts +3 -1
  28. package/ui/Elements/Select/components/SelectAll.js +1 -1
  29. package/ui/utils/translations.js +8 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailstep/design-system",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "main": "./ui/index.js",
@@ -5,6 +5,8 @@ import HidePrint from "../HidePrint/HidePrint.js";
5
5
  import { useModal } from "../Modal/hooks/useModal.js";
6
6
  import ActionHead_default from "./components/ActionHead/index.js";
7
7
  import { getFilters } from "./utils/index.js";
8
+ import { clearShareParams } from "./utils/shareUrl/clearShareParams.js";
9
+ import "./utils/shareUrl/index.js";
8
10
  import ControlButtons_default from "./components/ControlButtons/index.js";
9
11
  import { GridAggregation } from "./components/GridAggregation/GridAggregation.js";
10
12
  import "./components/GridAggregation/index.js";
@@ -13,6 +15,7 @@ import { GridModals } from "./components/GridModals/index.js";
13
15
  import MobileFilterModal from "./components/MobileFilterModal.js";
14
16
  import MobileSortModal from "./components/MobileSortModal.js";
15
17
  import TablePagination_default from "./components/TablePagination/index.js";
18
+ import { useApplySharedGridState } from "./hooks/useApplySharedGridState.js";
16
19
  import useEditReadAsColumn from "./hooks/useEditReadAsColumn.js";
17
20
  import { useGridAutoRowsPerPage } from "./hooks/useGridAutoRowsPerPage.js";
18
21
  import useManageColumn from "./hooks/useManageColumn.js";
@@ -21,18 +24,18 @@ import { getExtendedExtraControlButtons } from "./utils/getExtendedExtraControlB
21
24
  import { getRowsPerPage } from "./utils/getRowsPerPage.js";
22
25
  import { hasSortTerminated } from "./utils/hasSortTerminated.js";
23
26
  import { BottomWrapper, CommonGridWithStyles, CommonGridWrap, ContentContainer, StyledButtonStrip } from "./styles.js";
24
- import { useMemo, useRef, useState } from "react";
27
+ import { useCallback, useMemo, useRef, useState } from "react";
25
28
  import { x } from "@xstyled/styled-components";
26
29
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
27
30
  //#region packages/ui/Blocks/CommonGrid/CommonGridContainer.tsx
28
31
  const CommonGridContainer = (props) => {
29
- const { optimizeFilters = false, extraControlButtons, onBatchAction, processCheckedValues, processCheckedValuesTitle, hideControlButtons, quickFilter, floatingButtonProps, queryRowsParam, customPaginationHandler, autoHeight = true, gridName, withPresets = true, withManageColumnButton = true, withPagination = true, withAggregation = true, allowSortingOnlyUnderTotalRows, onReload, ...passDownProps } = props;
32
+ const { optimizeFilters = false, extraControlButtons, onBatchAction, processCheckedValues, processCheckedValuesTitle, hideControlButtons, quickFilter, floatingButtonProps, queryRowsParam, customPaginationHandler, autoHeight = true, gridName, withPresets = true, withManageColumnButton = true, withPagination = true, withAggregation = true, withShare = true, allowSortingOnlyUnderTotalRows, onReload, ...passDownProps } = props;
30
33
  const { gridActions, gridSelectors, rowsData, actionColumnDefinition } = passDownProps;
31
34
  const extendedExtraControlButtons = useMemo(() => getExtendedExtraControlButtons(extraControlButtons, onReload), [extraControlButtons, onReload]);
32
35
  const [displayManageColumnButton, setDisplayManageColumnButton] = useState(false);
33
36
  const [activeColumnName, setActiveColumnName] = useState(null);
34
37
  const gridRef = useRef(null);
35
- const withButtonStrip = !!extendedExtraControlButtons?.length || !hideControlButtons || !!withPresets || !!withManageColumnButton;
38
+ const withButtonStrip = !!extendedExtraControlButtons?.length || !hideControlButtons || !!withPresets || !!withManageColumnButton || !!withShare;
36
39
  const hasGroups = !!props.columnsDefinitions.find((col) => col.group);
37
40
  const hasFilters = !!props.columnsDefinitions.find((col) => col.filtering);
38
41
  const hasGroupsOrFilters = hasGroups || hasFilters;
@@ -48,6 +51,15 @@ const CommonGridContainer = (props) => {
48
51
  const modifiedPassDownProps = useEditReadAsColumn(passDownProps);
49
52
  const filters = useMemo(() => getFilters(optimizeFilters), [optimizeFilters]);
50
53
  const columns = modifiedPassDownProps.columnsDefinitions;
54
+ useApplySharedGridState({
55
+ enabled: withShare,
56
+ columns,
57
+ gridActions
58
+ });
59
+ const onClearSettings = useCallback(() => {
60
+ clearShareParams();
61
+ gridActions.clearSettings?.();
62
+ }, [gridActions]);
51
63
  const { onOpen: openManageColumnForm, isOpen: manageColumnFormVisible, onClose } = useModal();
52
64
  const { columnsConfigValues, setColumnsConfigOptions, onConfirmForm, resetColumnConfig, handleDragEnd, onCloseForm, displayColumnsDefinitions, manageColumnsFormDefinitions } = useManageColumn({
53
65
  columns,
@@ -87,7 +99,8 @@ const CommonGridContainer = (props) => {
87
99
  withManageColumnButton,
88
100
  onOpenMobileFilters: onOpenMobileFilter,
89
101
  onOpenMobileSorting: onOpenMobileSort,
90
- isSortTerminated
102
+ isSortTerminated,
103
+ withShare
91
104
  })
92
105
  }) }), /* @__PURE__ */ jsx(CommonGridWithStyles, {
93
106
  ...passDownProps,
@@ -185,7 +198,7 @@ const CommonGridContainer = (props) => {
185
198
  handleUxChange: gridActions.handleUxChange,
186
199
  onAsyncLoadFilterOptions: passDownProps.onAsyncLoadFilterOptions,
187
200
  actionColumn: actionColumnDefinition,
188
- onClearSettings: gridActions.clearSettings
201
+ onClearSettings
189
202
  }),
190
203
  isMobileSortOpen && gridActions.addSorting && /* @__PURE__ */ jsx(MobileSortModal, {
191
204
  isShown: isMobileSortOpen,
@@ -20,6 +20,7 @@ type Props = {
20
20
  onOpenMobileFilters?: () => void;
21
21
  onOpenMobileSorting?: () => void;
22
22
  isSortTerminated: boolean;
23
+ withShare?: boolean;
23
24
  };
24
25
  declare const ControlButtons: FC<Props>;
25
26
  export default ControlButtons;
@@ -8,17 +8,22 @@ import { H6 } from "../../../../Elements/Typography/Typography.js";
8
8
  import "../../../../Elements/Typography/index.js";
9
9
  import { isFilterActive } from "../../utils/index.js";
10
10
  import useQuickFilter from "../../hooks/useQuickFilter.js";
11
+ import { useShareGrid } from "../../hooks/useShareGrid.js";
11
12
  import "../../types.js";
12
13
  import ExtraControlButtons_default from "../ExtraControlButtons/index.js";
13
14
  import QuickFilter_default from "../QuickFilter/index.js";
14
- import { ControlButtonsContainer, Left, Right, StyledColumnButton } from "./styles.js";
15
+ import { ControlButtonsContainer, CopiedMessage, Left, Right, ShareButtonWrapper, StyledColumnButton } from "./styles.js";
15
16
  import { useEffect, useMemo } from "react";
16
17
  import { useColor, x } from "@xstyled/styled-components";
17
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
19
  import { i18n } from "@lingui/core";
19
20
  import { Trans } from "@lingui/react";
20
21
  //#region packages/ui/Blocks/CommonGrid/components/ControlButtons/ControlButtons.tsx
21
- const ControlButtons = ({ extraControlButtons, gridActions, gridSelectors, quickFilter = "", columns, filters, hideControlButtons, displayManageColumnButton, openManageColumnForm, setDisplayManageColumnButton, onOpenPresetsModal, gridName, selectedPresetName, withPresets, withManageColumnButton, onOpenMobileFilters, onOpenMobileSorting, isSortTerminated }) => {
22
+ const ControlButtons = ({ extraControlButtons, gridActions, gridSelectors, quickFilter = "", columns, filters, hideControlButtons, displayManageColumnButton, openManageColumnForm, setDisplayManageColumnButton, onOpenPresetsModal, gridName, selectedPresetName, withPresets, withManageColumnButton, onOpenMobileFilters, onOpenMobileSorting, isSortTerminated, withShare }) => {
23
+ const { onShare, isCopied } = useShareGrid({
24
+ gridSelectors,
25
+ columns
26
+ });
22
27
  const { searchedValue, translatedValue, onChangeInputValue, onClear, onClearSettings, onDisplayInput, displayInput, isMobileInputView, isMobile, filter } = useQuickFilter({
23
28
  gridActions,
24
29
  gridSelectors,
@@ -135,26 +140,44 @@ const ControlButtons = ({ extraControlButtons, gridActions, gridSelectors, quick
135
140
  children: selectedPresetName
136
141
  })
137
142
  })] })
138
- ] }), /* @__PURE__ */ jsxs(Right, { children: [/* @__PURE__ */ jsx(ExtraControlButtons_default, {
139
- extraControlButtons,
140
- targetPosition: "top-right",
141
- isMobile
142
- }), displayManageColumnButton && !isMobile && /* @__PURE__ */ jsx(StyledColumnButton, {
143
- type: "button",
144
- variant: "ghost",
145
- appearance: "primary",
146
- iconLeft: /* @__PURE__ */ jsx(Settings2, {}),
147
- onClick: openManageColumnForm,
148
- children: /* @__PURE__ */ jsx(H6, {
149
- variant: "semiBold",
150
- mt: 0,
151
- mb: 0,
152
- children: /* @__PURE__ */ jsx(Trans, {
153
- id: "manageColumn.title",
154
- message: "Manage column"
143
+ ] }), /* @__PURE__ */ jsxs(Right, { children: [
144
+ /* @__PURE__ */ jsx(ExtraControlButtons_default, {
145
+ extraControlButtons,
146
+ targetPosition: "top-right",
147
+ isMobile
148
+ }),
149
+ withShare && !isMobile && /* @__PURE__ */ jsxs(ShareButtonWrapper, { children: [isCopied && /* @__PURE__ */ jsx(CopiedMessage, { children: /* @__PURE__ */ jsx(Trans, {
150
+ id: "dataGrid.share.copiedMessage",
151
+ message: "Grid url copied"
152
+ }) }), /* @__PURE__ */ jsx(Button, {
153
+ type: "button",
154
+ appearance: "secondary",
155
+ icon: isCopied ? "circleCheck" : "shareNodes",
156
+ iconFillColor: "iconStroke",
157
+ onClick: onShare,
158
+ title: i18n._({
159
+ id: "dataGrid.share.title",
160
+ message: "Share"
161
+ }),
162
+ "data-cy": "shareGridBtn"
163
+ })] }),
164
+ displayManageColumnButton && !isMobile && /* @__PURE__ */ jsx(StyledColumnButton, {
165
+ type: "button",
166
+ variant: "ghost",
167
+ appearance: "primary",
168
+ iconLeft: /* @__PURE__ */ jsx(Settings2, {}),
169
+ onClick: openManageColumnForm,
170
+ children: /* @__PURE__ */ jsx(H6, {
171
+ variant: "semiBold",
172
+ mt: 0,
173
+ mb: 0,
174
+ children: /* @__PURE__ */ jsx(Trans, {
175
+ id: "manageColumn.title",
176
+ message: "Manage column"
177
+ })
155
178
  })
156
179
  })
157
- })] })] });
180
+ ] })] });
158
181
  };
159
182
  //#endregion
160
183
  export { ControlButtons as default };
@@ -1,4 +1,6 @@
1
1
  export declare const ControlButtonsContainer: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
2
2
  export declare const Left: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
3
3
  export declare const Right: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
4
+ export declare const ShareButtonWrapper: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
5
+ export declare const CopiedMessage: import("styled-components").StyledComponent<"div", import("@xstyled/styled-components").Theme, {}, never>;
4
6
  export declare const StyledColumnButton: import("styled-components").StyledComponent<import("react").FC<import("../../../../Elements/Button/types").Props>, import("@xstyled/styled-components").Theme, {}, never>;
@@ -17,12 +17,28 @@ const Left = styled$1.div`
17
17
  const Right = styled$1.div`
18
18
  display: flex;
19
19
  `;
20
+ const ShareButtonWrapper = styled$1.div`
21
+ position: relative;
22
+ display: flex;
23
+ align-items: center;
24
+ `;
25
+ const CopiedMessage = styled$1.div`
26
+ position: absolute;
27
+ bottom: calc(100% + 4px);
28
+ right: 0;
29
+ white-space: nowrap;
30
+ font-size: 10px;
31
+ line-height: 1;
32
+ font-weight: 600;
33
+ color: ${th$1("colors.iconStroke")};
34
+ background-color: ${th$1("colors.white")};
35
+ padding: 3px 5px;
36
+ border-radius: 4px;
37
+ box-shadow: tooltipShadow;
38
+ pointer-events: none;
39
+ z-index: 4;
40
+ `;
20
41
  const StyledColumnButton = styled$1(Button)`
21
- position: sticky;
22
- right: 20px;
23
- z-index: 3;
24
- margin-left: 8px;
25
-
26
42
  > span > svg {
27
43
  stroke: ${th$1("colors.iconStroke")};
28
44
  width: 18px;
@@ -39,4 +55,4 @@ const StyledColumnButton = styled$1(Button)`
39
55
  }
40
56
  `;
41
57
  //#endregion
42
- export { ControlButtonsContainer, Left, Right, StyledColumnButton };
58
+ export { ControlButtonsContainer, CopiedMessage, Left, Right, ShareButtonWrapper, StyledColumnButton };
@@ -7,10 +7,9 @@ import { Fragment, jsx } from "react/jsx-runtime";
7
7
  //#region packages/ui/Blocks/CommonGrid/components/ExtraControlButtons/ExtraControlButtons.tsx
8
8
  const Row = styled$1.div`
9
9
  display: flex;
10
- & > * {
11
- margin-right: 12px;
12
- }
10
+ gap: 8px;
13
11
  z-index: 2;
12
+ margin-right: 8px;
14
13
  `;
15
14
  const ControlButton = ({ hide, node, label, appearance, variant, disabled, onClick, onSelect, options, hideChevron, iconLeft, buttonProps }) => {
16
15
  if (hide) return null;
@@ -0,0 +1,9 @@
1
+ import type { ColumnDefinition, GridActionsType } from '../types';
2
+ type Props = {
3
+ enabled: boolean;
4
+ columns: ColumnDefinition[];
5
+ gridActions: GridActionsType;
6
+ };
7
+ type HookType = (props: Props) => void;
8
+ export declare const useApplySharedGridState: HookType;
9
+ export {};
@@ -0,0 +1,22 @@
1
+ import { parseSharedGridState } from "../utils/shareUrl/parseSharedGridState.js";
2
+ import "../utils/shareUrl/index.js";
3
+ import { useEffect, useRef } from "react";
4
+ //#region packages/ui/Blocks/CommonGrid/hooks/useApplySharedGridState.ts
5
+ const useApplySharedGridState = ({ enabled, columns, gridActions }) => {
6
+ const appliedRef = useRef(false);
7
+ useEffect(() => {
8
+ if (!enabled || appliedRef.current) return;
9
+ const shared = parseSharedGridState({ columns });
10
+ if (!shared) return;
11
+ appliedRef.current = true;
12
+ if (shared.filter) gridActions.setFilters?.(shared.filter);
13
+ const [sort] = shared.sorting ?? [];
14
+ if (sort) gridActions.addSorting?.(sort.column, sort.direction);
15
+ }, [
16
+ enabled,
17
+ columns,
18
+ gridActions
19
+ ]);
20
+ };
21
+ //#endregion
22
+ export { useApplySharedGridState };
@@ -1,23 +1,26 @@
1
- import { ChangeEvent } from 'react';
2
- import { ColumnDefinition, CommonGridProps, FiltersConfig, GridActionsType, GridSelectorsType } from '../types';
1
+ import { type ChangeEvent } from 'react';
2
+ import { getTranslatedValue } from '../components/ManageColumnForm/utils';
3
+ import { type ColumnDefinition, type CommonGridProps, type FiltersConfig, type GridActionsType, type GridSelectorsType } from '../types';
3
4
  type Props = {
4
5
  gridActions: GridActionsType;
5
6
  gridSelectors: GridSelectorsType;
6
7
  quickFilter?: CommonGridProps['quickFilter'];
7
8
  filter?: CommonGridProps['gridSelectors']['filter'];
8
- columns?: ColumnDefinition<string>[];
9
+ columns?: Array<ColumnDefinition<string>>;
9
10
  filters?: FiltersConfig;
10
11
  };
11
- declare const useQuickFilter: ({ gridActions, gridSelectors, quickFilter, columns, filters }: Props) => {
12
+ type UseQuickFilterReturn = {
12
13
  searchedValue: string;
13
- translatedValue: import("react").ReactNode;
14
- onChangeInputValue: (e: ChangeEvent<HTMLInputElement>) => void;
14
+ translatedValue: ReturnType<typeof getTranslatedValue>;
15
+ onChangeInputValue: (event: ChangeEvent<HTMLInputElement>) => void;
15
16
  onClearSettings: () => void;
16
17
  onDisplayInput: () => void;
17
18
  displayInput: boolean;
18
19
  onClear: () => void;
19
20
  isMobileInputView: boolean;
20
21
  isMobile: boolean;
21
- filter: import("../types").Filtering | undefined;
22
+ filter: GridSelectorsType['filter'];
22
23
  };
24
+ type HookType = (props: Props) => UseQuickFilterReturn;
25
+ declare const useQuickFilter: HookType;
23
26
  export default useQuickFilter;
@@ -1,6 +1,8 @@
1
1
  import { useCheckDeviceWidth } from "../../../utils/CheckDeviceWidth/checkDeviceWidth.js";
2
2
  import { getTranslatedValue } from "../components/ManageColumnForm/utils.js";
3
3
  import { createFilterType } from "../utils/index.js";
4
+ import { clearShareParams } from "../utils/shareUrl/clearShareParams.js";
5
+ import "../utils/shareUrl/index.js";
4
6
  import { useAddFilter } from "./useAddFilter.js";
5
7
  import { useCallback, useEffect, useState } from "react";
6
8
  //#region packages/ui/Blocks/CommonGrid/hooks/useQuickFilter.ts
@@ -14,9 +16,9 @@ const useQuickFilter = ({ gridActions, gridSelectors, quickFilter, columns, filt
14
16
  const handleAddFilter = useAddFilter(addFilter);
15
17
  const quickFilterValues = quickFilter ? filter?.[quickFilter]?.value : void 0;
16
18
  const searchedColumnsDefinitions = columns?.find((item) => item.name === quickFilter) || null;
17
- const filterType = createFilterType(searchedColumnsDefinitions || {});
19
+ const filterType = searchedColumnsDefinitions ? createFilterType(searchedColumnsDefinitions) : "text";
18
20
  const filtersDefaultComparator = filters?.[filterType].defaultExtraProps?.defaultComparator || "eq";
19
- const searchComparator = quickFilterValues && quickFilterValues.comparator || filtersDefaultComparator;
21
+ const searchComparator = quickFilterValues?.comparator || filtersDefaultComparator;
20
22
  const isMobileInputView = isMobile && displayInput;
21
23
  useEffect(() => {
22
24
  if (quickFilterValues) setSearchedValue(quickFilterValues.value);
@@ -30,34 +32,29 @@ const useQuickFilter = ({ gridActions, gridSelectors, quickFilter, columns, filt
30
32
  if (isMobile) setDisplayInput(false);
31
33
  else setDisplayInput(true);
32
34
  }, [isMobile]);
33
- const onChangeInputValue = useCallback((e) => {
34
- setSearchedValue(e.target.value);
35
- const value = {
35
+ const applySearchValue = useCallback((newValue) => {
36
+ setSearchedValue(newValue);
37
+ if (searchedColumnsDefinitions) handleAddFilter({
36
38
  comparator: searchComparator,
37
- value: e.target.value
38
- };
39
- searchedColumnsDefinitions && handleAddFilter(value, searchedColumnsDefinitions);
39
+ value: newValue
40
+ }, searchedColumnsDefinitions);
40
41
  }, [
41
- setSearchedValue,
42
42
  searchComparator,
43
43
  handleAddFilter,
44
44
  searchedColumnsDefinitions
45
45
  ]);
46
+ const onChangeInputValue = useCallback((event) => {
47
+ applySearchValue(event.target.value);
48
+ }, [applySearchValue]);
46
49
  const onClear = useCallback(() => {
47
- setSearchedValue("");
48
- onChangeInputValue({ target: { value: "" } });
49
- if (isMobile) setDisplayInput(false);
50
- else setDisplayInput(true);
51
- }, [
52
- setSearchedValue,
53
- isMobile,
54
- onChangeInputValue,
55
- setDisplayInput
56
- ]);
50
+ applySearchValue("");
51
+ setDisplayInput(!isMobile);
52
+ }, [applySearchValue, isMobile]);
57
53
  const onDisplayInput = useCallback(() => {
58
54
  setDisplayInput(true);
59
55
  }, []);
60
56
  const onClearSettings = useCallback(() => {
57
+ clearShareParams();
61
58
  clearSettings?.();
62
59
  setSearchedValue?.("");
63
60
  }, [clearSettings, setSearchedValue]);
@@ -0,0 +1,11 @@
1
+ import type { ColumnDefinition, GridSelectorsType } from '../types';
2
+ type Props = {
3
+ gridSelectors: GridSelectorsType;
4
+ columns?: ColumnDefinition[];
5
+ };
6
+ type HookType = (props: Props) => {
7
+ onShare: () => void;
8
+ isCopied: boolean;
9
+ };
10
+ export declare const useShareGrid: HookType;
11
+ export {};
@@ -0,0 +1,37 @@
1
+ import { buildShareUrl } from "../utils/shareUrl/buildShareUrl.js";
2
+ import "../utils/shareUrl/index.js";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ //#region packages/ui/Blocks/CommonGrid/hooks/useShareGrid.ts
5
+ const useShareGrid = ({ gridSelectors, columns }) => {
6
+ const [isCopied, setIsCopied] = useState(false);
7
+ useEffect(() => {
8
+ if (!isCopied) return;
9
+ const timeoutId = setTimeout(() => {
10
+ setIsCopied(false);
11
+ }, 2e3);
12
+ return () => {
13
+ clearTimeout(timeoutId);
14
+ };
15
+ }, [isCopied]);
16
+ return {
17
+ onShare: useCallback(() => {
18
+ const url = buildShareUrl({
19
+ columns,
20
+ filter: gridSelectors.filter,
21
+ sorting: gridSelectors.sorting
22
+ });
23
+ navigator.clipboard?.writeText(url).then(() => {
24
+ setIsCopied(true);
25
+ }).catch(() => {
26
+ console.warn("CommonGrid: failed to copy share link to clipboard");
27
+ });
28
+ }, [
29
+ columns,
30
+ gridSelectors.filter,
31
+ gridSelectors.sorting
32
+ ]),
33
+ isCopied
34
+ };
35
+ };
36
+ //#endregion
37
+ export { useShareGrid };
@@ -314,6 +314,7 @@ export type GridProps = CommonGridProps & {
314
314
  withManageColumnButton?: boolean;
315
315
  withPagination?: boolean;
316
316
  withAggregation?: boolean;
317
+ withShare?: boolean;
317
318
  allowSortingOnlyUnderTotalRows?: [number, number];
318
319
  onReload?: () => void;
319
320
  };
@@ -0,0 +1,10 @@
1
+ import type { ColumnDefinition, Filtering, Sorting } from '../../types';
2
+ export declare const enc: (value: string) => string;
3
+ type BuildShareUrlProps = {
4
+ columns?: ColumnDefinition[];
5
+ filter?: Filtering;
6
+ sorting?: Sorting;
7
+ };
8
+ type BuildShareUrlType = (props: BuildShareUrlProps) => string;
9
+ export declare const buildShareUrl: BuildShareUrlType;
10
+ export {};
@@ -0,0 +1,66 @@
1
+ import { getFilterName } from "../index.js";
2
+ import { FILTERS_PARAM, SORT_PARAM } from "./constants.js";
3
+ //#region packages/ui/Blocks/CommonGrid/utils/shareUrl/buildShareUrl.ts
4
+ const enc = (value) => encodeURIComponent(value).replace(/\(/g, "%28").replace(/\)/g, "%29");
5
+ const serializeValue = (value) => {
6
+ if (value == null || value === "") return null;
7
+ if (typeof value === "object" && !Array.isArray(value) && "value" in value) {
8
+ const entry = value;
9
+ if (entry.value == null || entry.value === "") return null;
10
+ return {
11
+ valueStr: enc(String(entry.value)),
12
+ comparator: entry.comparator ? enc(entry.comparator) : ""
13
+ };
14
+ }
15
+ if (Array.isArray(value)) {
16
+ if (!value.length) return null;
17
+ if (value.every((item) => item && typeof item === "object" && "value" in item)) {
18
+ const options = value;
19
+ return {
20
+ valueStr: options.map((option) => enc(String(option.value))).join(";"),
21
+ comparator: options[0]?.isNotEq ? "nin" : "in"
22
+ };
23
+ }
24
+ return {
25
+ valueStr: value.map((item) => enc(item instanceof Date ? item.toISOString() : String(item))).join(";"),
26
+ comparator: ""
27
+ };
28
+ }
29
+ return {
30
+ valueStr: enc(String(value)),
31
+ comparator: ""
32
+ };
33
+ };
34
+ const serializeAsyncLabels = (value) => {
35
+ if (!Array.isArray(value)) return null;
36
+ const labels = value.filter((item) => item && typeof item === "object" && "label" in item).map((item) => enc(String(item.label)));
37
+ return labels.length ? labels.join(";") : null;
38
+ };
39
+ const buildFilterGroup = (name, entry, column) => {
40
+ const serialized = serializeValue(entry?.value);
41
+ if (!serialized) return null;
42
+ const fields = [`name=${enc(name)}`, `value=${serialized.valueStr}`];
43
+ if (serialized.comparator) fields.push(`compar=${serialized.comparator}`);
44
+ if (column?.asyncLoadKey) {
45
+ const labels = serializeAsyncLabels(entry?.value);
46
+ if (labels) fields.push(`label=${labels}`);
47
+ }
48
+ return `(${fields.join(",")})`;
49
+ };
50
+ const buildShareUrl = ({ columns, filter, sorting }) => {
51
+ const filterGroups = Object.entries(filter ?? {}).map(([name, entry]) => buildFilterGroup(name, entry, columns?.find((col) => getFilterName(col) === name))).filter(Boolean);
52
+ const sort = sorting?.[0];
53
+ const sortGroup = sort?.column && sort.direction ? `(name=${enc(sort.column)},value=${enc(sort.direction)})` : "";
54
+ const url = new URL(window.location.href);
55
+ url.searchParams.delete(FILTERS_PARAM);
56
+ url.searchParams.delete(SORT_PARAM);
57
+ const parts = [];
58
+ const others = url.searchParams.toString();
59
+ if (others) parts.push(others);
60
+ if (filterGroups.length) parts.push(`${FILTERS_PARAM}=${filterGroups.join("-")}`);
61
+ if (sortGroup) parts.push(`${SORT_PARAM}=${sortGroup}`);
62
+ const query = parts.join("&");
63
+ return `${url.origin}${url.pathname}${query ? `?${query}` : ""}${url.hash}`;
64
+ };
65
+ //#endregion
66
+ export { buildShareUrl, enc };
@@ -0,0 +1 @@
1
+ export declare const clearShareParams: () => void;
@@ -0,0 +1,12 @@
1
+ import { FILTERS_PARAM, SORT_PARAM } from "./constants.js";
2
+ //#region packages/ui/Blocks/CommonGrid/utils/shareUrl/clearShareParams.ts
3
+ const clearShareParams = () => {
4
+ const url = new URL(window.location.href);
5
+ if (!["gridFilters", "gridSort"].some((key) => url.searchParams.has(key))) return;
6
+ url.searchParams.delete(FILTERS_PARAM);
7
+ url.searchParams.delete(SORT_PARAM);
8
+ const query = url.searchParams.toString();
9
+ window.history.replaceState(null, "", `${url.origin}${url.pathname}${query ? `?${query}` : ""}${url.hash}`);
10
+ };
11
+ //#endregion
12
+ export { clearShareParams };
@@ -0,0 +1,5 @@
1
+ export declare const FILTERS_PARAM = "gridFilters";
2
+ export declare const SORT_PARAM = "gridSort";
3
+ export declare const GROUP_SEPARATOR = "-";
4
+ export declare const FIELD_SEPARATOR = ",";
5
+ export declare const VALUE_SEPARATOR = ";";
@@ -0,0 +1,8 @@
1
+ //#region packages/ui/Blocks/CommonGrid/utils/shareUrl/constants.ts
2
+ const FILTERS_PARAM = "gridFilters";
3
+ const SORT_PARAM = "gridSort";
4
+ const GROUP_SEPARATOR = "-";
5
+ const FIELD_SEPARATOR = ",";
6
+ const VALUE_SEPARATOR = ";";
7
+ //#endregion
8
+ export { FIELD_SEPARATOR, FILTERS_PARAM, GROUP_SEPARATOR, SORT_PARAM, VALUE_SEPARATOR };
@@ -0,0 +1,4 @@
1
+ export { buildShareUrl } from './buildShareUrl';
2
+ export { clearShareParams } from './clearShareParams';
3
+ export { parseSharedGridState } from './parseSharedGridState';
4
+ export type { SharedGridState } from './parseSharedGridState';
@@ -0,0 +1,4 @@
1
+ import { buildShareUrl } from "./buildShareUrl.js";
2
+ import { clearShareParams } from "./clearShareParams.js";
3
+ import { parseSharedGridState } from "./parseSharedGridState.js";
4
+ export { buildShareUrl, clearShareParams, parseSharedGridState };
@@ -0,0 +1,13 @@
1
+ import type { ColumnDefinition, Filtering, Sorting } from '../../types';
2
+ export declare const dec: typeof decodeURIComponent;
3
+ export type SharedGridState = {
4
+ filter?: Filtering;
5
+ sorting?: Sorting;
6
+ };
7
+ type ParseSharedGridStateProps = {
8
+ columns: ColumnDefinition[];
9
+ search?: string;
10
+ };
11
+ type ParseSharedGridStateType = (props: ParseSharedGridStateProps) => SharedGridState | null;
12
+ export declare const parseSharedGridState: ParseSharedGridStateType;
13
+ export {};
@@ -0,0 +1,89 @@
1
+ import { createFilterType, getFilterName } from "../index.js";
2
+ import { FILTERS_PARAM, SORT_PARAM } from "./constants.js";
3
+ //#region packages/ui/Blocks/CommonGrid/utils/shareUrl/parseSharedGridState.ts
4
+ const dec = decodeURIComponent;
5
+ const reconstructValue = (type, column, valuePart, comparator, labels) => {
6
+ if (type === "options") {
7
+ if (column.filterExtraProps?.isMulti) return valuePart.split(";").map((raw, index) => {
8
+ const optionValue = dec(raw);
9
+ return {
10
+ value: optionValue,
11
+ label: labels?.[index] ?? optionValue,
12
+ isNotEq: comparator === "nin"
13
+ };
14
+ });
15
+ return dec(valuePart);
16
+ }
17
+ if (type === "flag") return dec(valuePart);
18
+ if (type === "date") return valuePart.split(";").map(dec);
19
+ if (type === "number") return {
20
+ comparator: comparator || column.filterExtraProps?.defaultComparator || "eq",
21
+ value: dec(valuePart)
22
+ };
23
+ return {
24
+ comparator: comparator || column.filterExtraProps?.defaultComparator || "eq",
25
+ value: dec(valuePart)
26
+ };
27
+ };
28
+ const parseGroupFields = (group) => {
29
+ const inner = group.slice(1, -1);
30
+ const fields = {};
31
+ inner.split(",").forEach((pair) => {
32
+ const separatorIndex = pair.indexOf("=");
33
+ if (separatorIndex === -1) return;
34
+ fields[pair.slice(0, separatorIndex)] = pair.slice(separatorIndex + 1);
35
+ });
36
+ return fields;
37
+ };
38
+ const parseFilterGroup = (group, columns) => {
39
+ const fields = parseGroupFields(group);
40
+ const name = fields.name ? dec(fields.name) : "";
41
+ if (!name || fields.value == null) return null;
42
+ const column = columns.find((col) => getFilterName(col) === name);
43
+ if (!column) return null;
44
+ const type = createFilterType(column);
45
+ const comparator = fields.compar ? dec(fields.compar) : "";
46
+ const labels = fields.label ? fields.label.split(";").map(dec) : void 0;
47
+ const value = reconstructValue(type, column, fields.value, comparator, labels);
48
+ return {
49
+ name,
50
+ entry: {
51
+ value,
52
+ filterProps: {
53
+ type,
54
+ filterExtraProps: column.filterExtraProps || {},
55
+ computedValue: column.getFilterCriteria ? column.getFilterCriteria(value) : null
56
+ }
57
+ }
58
+ };
59
+ };
60
+ const parseSharedGridState = ({ columns, search }) => {
61
+ const params = new URLSearchParams(search ?? window.location.search);
62
+ const filtersRaw = params.get(FILTERS_PARAM);
63
+ const sortRaw = params.get(SORT_PARAM);
64
+ if (!filtersRaw && !sortRaw) return null;
65
+ const result = {};
66
+ if (filtersRaw) {
67
+ const filter = {};
68
+ (filtersRaw.match(/\(([^)]*)\)/g) ?? []).forEach((group) => {
69
+ const parsed = parseFilterGroup(group, columns);
70
+ if (parsed) filter[parsed.name] = parsed.entry;
71
+ });
72
+ if (Object.keys(filter).length) result.filter = filter;
73
+ }
74
+ if (sortRaw) {
75
+ const sortGroup = sortRaw.match(/\(([^)]*)\)/)?.[0];
76
+ if (sortGroup) {
77
+ const fields = parseGroupFields(sortGroup);
78
+ const column = fields.name ? dec(fields.name) : "";
79
+ const direction = fields.value ? dec(fields.value) : null;
80
+ if (column && (direction === "asc" || direction === "desc")) result.sorting = [{
81
+ column,
82
+ direction
83
+ }];
84
+ }
85
+ }
86
+ return Object.keys(result).length ? result : null;
87
+ };
88
+ //#endregion
89
+ export { dec, parseSharedGridState };
@@ -44,6 +44,7 @@ import { faHand } from "@fortawesome/pro-regular-svg-icons/faHand";
44
44
  import { faLink } from "@fortawesome/pro-regular-svg-icons/faLink";
45
45
  import { faLock } from "@fortawesome/pro-regular-svg-icons/faLock";
46
46
  import { faPenToSquare } from "@fortawesome/pro-regular-svg-icons/faPenToSquare";
47
+ import { faShareNodes } from "@fortawesome/pro-regular-svg-icons/faShareNodes";
47
48
  import { faShareSquare } from "@fortawesome/pro-regular-svg-icons/faShareSquare";
48
49
  import { faSquareMinus } from "@fortawesome/pro-regular-svg-icons/faSquareMinus";
49
50
  import { faTrashCan } from "@fortawesome/pro-regular-svg-icons/faTrashCan";
@@ -170,6 +171,7 @@ const iconDictionary = {
170
171
  shelves: faShelves,
171
172
  reservation: faHandHoldingBox,
172
173
  return: [faShareSquare, "horizontal"],
174
+ shareNodes: faShareNodes,
173
175
  search: faSearch,
174
176
  supplier: faTriangleExclamation$1,
175
177
  transfer: faExchange,
@@ -261,8 +263,13 @@ const Icon = ({ icon, fill, stroke, style = "normal", size, width, height, class
261
263
  ]);
262
264
  const flipProp = namedIcon && Array.isArray(namedIcon) && namedIcon[1] || void 0;
263
265
  const IconComponent = icons_exports[icon];
266
+ const resolvedSize = size ?? width ?? height;
267
+ const faIconStyle = useMemo(() => {
268
+ if (onClick) return { cursor: cursor ?? "pointer" };
269
+ if (cursor) return { cursor };
270
+ }, [onClick, cursor]);
264
271
  return /* @__PURE__ */ jsx(FaIconSizing, {
265
- size: size ?? width ?? height,
272
+ size: resolvedSize,
266
273
  className: className ?? "",
267
274
  $colorSecondary: colorSecondary,
268
275
  $fixedWidth: fixedWidth,
@@ -278,7 +285,7 @@ const Icon = ({ icon, fill, stroke, style = "normal", size, width, height, class
278
285
  flip: flipProp,
279
286
  icon: iconProp,
280
287
  onClick,
281
- style: onClick ? { cursor: cursor ?? "pointer" } : cursor ? { cursor } : void 0,
288
+ style: faIconStyle,
282
289
  className: `faIcon ${fixedWidth ? "fa-fw" : ""} ${spinning ? "fa-spin" : ""}`
283
290
  })
284
291
  });
@@ -57,9 +57,15 @@ const Select = ({ label, name, value = "", options = emptyOptions, defaultOption
57
57
  localGetOptionValue,
58
58
  resolveOption
59
59
  ]);
60
- const unsetAllOptions = useCallback(() => {
61
- if (typeof onChange === "function") onChange?.(emptyOptions);
62
- }, [onChange]);
60
+ const unsetAllOptions = useCallback(({ options: optionsToRemove }) => {
61
+ const current = Array.isArray(selectedOption) ? selectedOption : selectedOption ? [selectedOption] : [];
62
+ const removeValues = new Set((optionsToRemove || []).map((option) => localGetOptionValue(option)));
63
+ handleChange(current.filter((selected) => !removeValues.has(localGetOptionValue(selected))));
64
+ }, [
65
+ handleChange,
66
+ selectedOption,
67
+ localGetOptionValue
68
+ ]);
63
69
  const icon = containerVariant === "search" && !placeholderIcon && placeholderIcon !== null ? "search" : placeholderIcon || void 0;
64
70
  const modifiedMaxMenuHeight = showSelectAllButton && maxMenuHeight ? maxMenuHeight - 30 : maxMenuHeight;
65
71
  const [customComponents, customTheme, customStyles] = useStylesAndComponents(style, optionVariant, multiLabelVariant, containerVariant, useSimplifiedOptions, showSelectAllButton);
@@ -3,7 +3,9 @@ type Props = {
3
3
  onCustomSelectAll: ({ options }: {
4
4
  options?: Option[];
5
5
  }) => void;
6
- onCustomDeselectAll: () => void;
6
+ onCustomDeselectAll: ({ options }: {
7
+ options?: Option[];
8
+ }) => void;
7
9
  value: SelectProps['value'];
8
10
  options: SelectProps['options'];
9
11
  maxMenuHeight: number;
@@ -33,7 +33,7 @@ const SelectAll = ({ onCustomSelectAll, onCustomDeselectAll, value, options, max
33
33
  onCustomSelectAll({ options });
34
34
  } else if (isSelected) {
35
35
  setIsSelected(false);
36
- onCustomDeselectAll();
36
+ onCustomDeselectAll({ options });
37
37
  }
38
38
  }, [
39
39
  isSelected,
@@ -252,6 +252,14 @@ const Translations = () => {
252
252
  id: "dataGrid.sort",
253
253
  message: "Sort"
254
254
  }),
255
+ /* @__PURE__ */ jsx(Trans, {
256
+ id: "dataGrid.share.title",
257
+ message: "Share"
258
+ }),
259
+ /* @__PURE__ */ jsx(Trans, {
260
+ id: "dataGrid.share.copiedMessage",
261
+ message: "Grid url copied"
262
+ }),
255
263
  /* @__PURE__ */ jsx(Trans, {
256
264
  id: "dataGrid.presets.title",
257
265
  message: "Presets"