refine-mantine 1.2.1 → 1.3.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/package.json CHANGED
@@ -25,6 +25,7 @@
25
25
  "@tabler/icons-react": "^3.34.1",
26
26
  "@tanstack/react-query": "^5.87.4",
27
27
  "@tanstack/react-table": "^8.21.3",
28
+ "dayjs": "^1.11.19",
28
29
  "react": "^19.1.1",
29
30
  "react-dom": "^19.1.1",
30
31
  "react-router": "^7.0.2",
@@ -52,6 +53,7 @@
52
53
  "@types/react": "^19.2.2",
53
54
  "@types/react-dom": "^19.2.2",
54
55
  "@vitejs/plugin-react": "^5.1.0",
56
+ "dayjs": "^1.11.19",
55
57
  "husky": "^9.1.7",
56
58
  "postcss": "^8.5.6",
57
59
  "postcss-preset-mantine": "1.18.0",
@@ -87,5 +89,5 @@
87
89
  "dependencies": {
88
90
  "@refinedev/ui-types": "^2.0.1"
89
91
  },
90
- "version": "1.2.1"
92
+ "version": "1.3.0"
91
93
  }
@@ -0,0 +1,38 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import { IconCheck, IconX } from "@tabler/icons-react";
4
+ import { BooleanField } from "./BooleanField";
5
+
6
+ export default {
7
+ title: 'Fields/BooleanField',
8
+ component: BooleanField,
9
+ decorators: (Story) => (
10
+ <Stack h="100vh" align="center" justify="center">
11
+ <Story />
12
+ </Stack>
13
+ ),
14
+ } satisfies Meta<typeof BooleanField>;
15
+
16
+ export const Default = () =>
17
+ <BooleanField value={true} />;
18
+
19
+ export const CustomLabel = () =>
20
+ <BooleanField
21
+ value={false}
22
+ falseLabel="Value not selected"
23
+ trueLabel="Value selected"
24
+ />;
25
+
26
+ export const CustomIcon = () =>
27
+ <BooleanField
28
+ value={false}
29
+ falseIcon={<IconX />}
30
+ trueIcon={<IconCheck />}
31
+ />;
32
+
33
+ export const CustomColor = () =>
34
+ <BooleanField
35
+ value={false}
36
+ falseIconProps={{ fill: "red" }}
37
+ trueIconProps={{ fill: "green" }}
38
+ />;
@@ -0,0 +1,33 @@
1
+ import { Tooltip } from "@mantine/core";
2
+ import type { TooltipBaseProps } from "@mantine/core/lib/components/Tooltip/Tooltip.types";
3
+ import { IconCircleCheckFilled, IconCircleXFilled, ReactNode, type IconProps } from "@tabler/icons-react";
4
+
5
+ export interface BooleanFieldProps {
6
+ value: boolean;
7
+ trueLabel?: string;
8
+ falseLabel?: string;
9
+ trueIcon?: ReactNode;
10
+ falseIcon?: ReactNode;
11
+ trueIconProps?: IconProps;
12
+ falseIconProps?: IconProps;
13
+ tootlipProps?: TooltipBaseProps;
14
+ }
15
+
16
+ export const BooleanField: React.FC<BooleanFieldProps> = ({
17
+ value,
18
+ trueLabel = "true",
19
+ falseLabel = "false",
20
+ trueIcon,
21
+ falseIcon,
22
+ trueIconProps,
23
+ falseIconProps,
24
+ tootlipProps,
25
+ }) => (
26
+ <Tooltip label={value ? trueLabel : falseLabel} {...tootlipProps}>
27
+ <span>
28
+ {value
29
+ ? trueIcon ?? <IconCircleCheckFilled size={18} {...trueIconProps} />
30
+ : falseIcon ?? <IconCircleXFilled size={18} {...falseIconProps} />}
31
+ </span>
32
+ </Tooltip>
33
+ );
@@ -0,0 +1,46 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import "dayjs/locale/de";
4
+ import { DateField } from "./DateField";
5
+
6
+ export default {
7
+ title: 'Fields/DateField',
8
+ component: DateField,
9
+ decorators: (Story) => (
10
+ <Stack h="100vh" align="center" justify="center">
11
+ <Story />
12
+ </Stack>
13
+ ),
14
+ } satisfies Meta<typeof DateField>;
15
+
16
+ export const Default = () =>
17
+ <DateField value="2025-12-05T14:41:31" />;
18
+
19
+ export const Small = () =>
20
+ <DateField
21
+ value="2025-12-05T14:41:31"
22
+ textProps={{size: "xs"}}
23
+ />;
24
+
25
+ export const Color = () =>
26
+ <DateField
27
+ value="2025-12-05T14:41:31"
28
+ textProps={{ c: "teal" }}
29
+ />;
30
+
31
+ export const Format = () =>
32
+ <DateField
33
+ value="2025-12-05T14:41:31"
34
+ // https://day.js.org/docs/en/display/format
35
+ format="LLLL"
36
+ />;
37
+
38
+ export const Locale = () => (
39
+ <DateField
40
+ value="2025-12-05T14:41:31"
41
+ // https://day.js.org/docs/en/display/format
42
+ format="LLLL"
43
+ // importing "dayjs/locale/de" is required to work properly
44
+ locale="de"
45
+ />
46
+ );
@@ -0,0 +1,29 @@
1
+ import { Text, TextProps } from "@mantine/core";
2
+ import type { ConfigType } from "dayjs";
3
+ import dayjs from "dayjs";
4
+ import LocalizedFormat from "dayjs/plugin/localizedFormat";
5
+
6
+ export interface DateFieldProps {
7
+ value: ConfigType;
8
+ locale?: string;
9
+ format?: string;
10
+ textProps?: TextProps;
11
+ }
12
+
13
+ dayjs.extend(LocalizedFormat);
14
+ const defaultLocale = dayjs.locale();
15
+
16
+ export const DateField: React.FC<DateFieldProps> = ({
17
+ value,
18
+ locale,
19
+ format: dateFormat = "L",
20
+ textProps,
21
+ }) => (
22
+ <Text {...textProps}>
23
+ {value
24
+ ? dayjs(value)
25
+ .locale(locale ?? defaultLocale)
26
+ .format(dateFormat)
27
+ : ""}
28
+ </Text>
29
+ );
@@ -0,0 +1,84 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import { EmailField } from "./EmailField";
4
+
5
+ export default {
6
+ title: 'Fields/EmailField',
7
+ component: EmailField,
8
+ decorators: (Story) => (
9
+ <Stack h="100vh" align="center" justify="center">
10
+ <Story />
11
+ </Stack>
12
+ ),
13
+ } satisfies Meta<typeof EmailField>;
14
+
15
+ export const Default = () =>
16
+ <EmailField value="test@example.com" />;
17
+
18
+ export const Colors = () =>
19
+ <>
20
+ <EmailField
21
+ value="test@example.com"
22
+ anchorProps={{c: "indigo"}}
23
+ />
24
+ <EmailField
25
+ value="test@example.com"
26
+ anchorProps={{c: "cyan"}}
27
+ />
28
+ <EmailField
29
+ value="test@example.com"
30
+ anchorProps={{c: "teal"}}
31
+ />
32
+ <EmailField
33
+ value="test@example.com"
34
+ anchorProps={{c: "lime"}}
35
+ />
36
+ </>;
37
+
38
+ export const Sizes = () =>
39
+ <>
40
+ <EmailField
41
+ value="test@example.com"
42
+ textProps={{size: "xs"}}
43
+ />
44
+ <EmailField
45
+ value="test@example.com"
46
+ textProps={{size: "sm"}}
47
+ />
48
+ <EmailField
49
+ value="test@example.com"
50
+ textProps={{size: "md"}}
51
+ />
52
+ <EmailField
53
+ value="test@example.com"
54
+ textProps={{size: "lg"}}
55
+ />
56
+ <EmailField
57
+ value="test@example.com"
58
+ textProps={{size: "xl"}}
59
+ />
60
+ </>;
61
+
62
+ export const Truncated = () =>
63
+ <EmailField
64
+ value="firstname.lastname@verylongdomain.tld"
65
+ anchorProps={{maw: 200}}
66
+ textProps={{truncate: "end"}}
67
+ />;
68
+
69
+ export const Gradient = () =>
70
+ <EmailField
71
+ value="test@example.com"
72
+ anchorProps={{
73
+ variant: "gradient",
74
+ gradient: { from: 'pink', to: 'yellow' }
75
+ }}
76
+ />;
77
+
78
+ export const HiddenIcon = () =>
79
+ <EmailField
80
+ value="test@example.com"
81
+ iconProps={{
82
+ display: "none",
83
+ }}
84
+ />;
@@ -0,0 +1,9 @@
1
+ import { IconMailShare } from "@tabler/icons-react";
2
+ import { UrlField, UrlFieldProps } from "./UrlField";
3
+
4
+ export const EmailField: React.FC<UrlFieldProps> = (props) =>
5
+ <UrlField
6
+ icon={IconMailShare}
7
+ {...props}
8
+ anchorProps={{ href: `mailto:${props.value}`, ...props.anchorProps }}
9
+ />;
@@ -0,0 +1,91 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import { IconPhotoDown } from "@tabler/icons-react";
4
+ import { FileField } from "./FileField";
5
+
6
+ export default {
7
+ title: 'Fields/FileField',
8
+ component: FileField,
9
+ decorators: (Story) => (
10
+ <Stack h="100vh" align="center" justify="center">
11
+ <Story />
12
+ </Stack>
13
+ ),
14
+ } satisfies Meta<typeof FileField>;
15
+
16
+ export const Default = () =>
17
+ <FileField value="https://github.com/kruschid/refine-mantine">refine-mantine</FileField>;
18
+
19
+ export const Colors = () =>
20
+ <>
21
+ <FileField
22
+ value="https://github.com/kruschid/refine-mantine"
23
+ anchorProps={{c: "indigo"}}
24
+ />
25
+ <FileField
26
+ value="https://github.com/kruschid/refine-mantine"
27
+ anchorProps={{c: "cyan"}}
28
+ />
29
+ <FileField
30
+ value="https://github.com/kruschid/refine-mantine"
31
+ anchorProps={{c: "teal"}}
32
+ />
33
+ <FileField
34
+ value="https://github.com/kruschid/refine-mantine"
35
+ anchorProps={{c: "lime"}}
36
+ />
37
+ </>;
38
+
39
+ export const Sizes = () =>
40
+ <>
41
+ <FileField
42
+ value="https://github.com/kruschid/refine-mantine"
43
+ textProps={{size: "xs"}}
44
+ />
45
+ <FileField
46
+ value="https://github.com/kruschid/refine-mantine"
47
+ textProps={{size: "sm"}}
48
+ />
49
+ <FileField
50
+ value="https://github.com/kruschid/refine-mantine"
51
+ textProps={{size: "md"}}
52
+ />
53
+ <FileField
54
+ value="https://github.com/kruschid/refine-mantine"
55
+ textProps={{size: "lg"}}
56
+ />
57
+ <FileField
58
+ value="https://github.com/kruschid/refine-mantine"
59
+ textProps={{size: "xl"}}
60
+ />
61
+ </>;
62
+
63
+ export const Truncated = () =>
64
+ <FileField
65
+ value="https://github.com/kruschid/refine-mantine"
66
+ anchorProps={{maw: 200}}
67
+ textProps={{truncate: "end"}}
68
+ />;
69
+
70
+ export const Gradient = () =>
71
+ <FileField
72
+ value="https://github.com/kruschid/refine-mantine"
73
+ anchorProps={{
74
+ variant: "gradient",
75
+ gradient: { from: 'pink', to: 'yellow' }
76
+ }}
77
+ />;
78
+
79
+ export const HiddenIcon = () =>
80
+ <FileField
81
+ value="https://github.com/kruschid/refine-mantine"
82
+ iconProps={{
83
+ display: "none",
84
+ }}
85
+ />;
86
+
87
+ export const CustomIcon = () =>
88
+ <FileField
89
+ value="https://github.com/kruschid/refine-mantine"
90
+ icon={IconPhotoDown}
91
+ />;
@@ -0,0 +1,8 @@
1
+ import { IconFileDownloadFilled } from "@tabler/icons-react";
2
+ import { UrlField, UrlFieldProps } from "./UrlField";
3
+
4
+ export const FileField: React.FC<UrlFieldProps> = (props) =>
5
+ <UrlField
6
+ icon={IconFileDownloadFilled}
7
+ {...props}
8
+ />;
@@ -0,0 +1,96 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import { PhoneField } from "./PhoneField";
4
+
5
+ export default {
6
+ title: 'Fields/PhoneField',
7
+ component: PhoneField,
8
+ decorators: (Story) => (
9
+ <Stack h="100vh" align="center" justify="center">
10
+ <Story />
11
+ </Stack>
12
+ ),
13
+ } satisfies Meta<typeof PhoneField>;
14
+
15
+ export const Default = () =>
16
+ <PhoneField value="+05890000111">0-589-0000111</PhoneField>;
17
+
18
+ export const Colors = () =>
19
+ <>
20
+ <PhoneField
21
+ value="+05890000111"
22
+ anchorProps={{c: "indigo"}}
23
+ />
24
+ <PhoneField
25
+ value="+05890000111"
26
+ anchorProps={{c: "cyan"}}
27
+ />
28
+ <PhoneField
29
+ value="+05890000111"
30
+ anchorProps={{c: "teal"}}
31
+ />
32
+ <PhoneField
33
+ value="+05890000111"
34
+ anchorProps={{c: "lime"}}
35
+ />
36
+ </>;
37
+
38
+ export const Sizes = () =>
39
+ <>
40
+ <PhoneField
41
+ value="+05890000111"
42
+ textProps={{size: "xs"}}
43
+ />
44
+ <PhoneField
45
+ value="+05890000111"
46
+ textProps={{size: "sm"}}
47
+ />
48
+ <PhoneField
49
+ value="+05890000111"
50
+ textProps={{size: "md"}}
51
+ />
52
+ <PhoneField
53
+ value="+05890000111"
54
+ textProps={{size: "lg"}}
55
+ />
56
+ <PhoneField
57
+ value="+05890000111"
58
+ textProps={{size: "xl"}}
59
+ />
60
+ </>
61
+
62
+ export const WithTarget = () =>
63
+ <PhoneField
64
+ value="+05890000111"
65
+ anchorProps={{target: "_blank"}}
66
+ />;
67
+
68
+ export const Truncated = () =>
69
+ <PhoneField
70
+ value="+05890000111"
71
+ anchorProps={{maw: 200}}
72
+ textProps={{truncate: "end"}}
73
+ />;
74
+
75
+ export const WithTitle = () =>
76
+ <PhoneField
77
+ value="+05890000111"
78
+ anchorProps={{title: "refine-mantine"}}
79
+ />;
80
+
81
+ export const Gradient = () =>
82
+ <PhoneField
83
+ value="+05890000111"
84
+ anchorProps={{
85
+ variant: "gradient",
86
+ gradient: { from: 'pink', to: 'yellow' }
87
+ }}
88
+ />;
89
+
90
+ export const HiddenIcon = () =>
91
+ <PhoneField
92
+ value="+05890000111"
93
+ iconProps={{
94
+ display: "none",
95
+ }}
96
+ />;
@@ -0,0 +1,9 @@
1
+ import { IconPhoneOutgoing } from "@tabler/icons-react";
2
+ import { UrlField, UrlFieldProps } from "./UrlField";
3
+
4
+ export const PhoneField: React.FC<UrlFieldProps> = (props) =>
5
+ <UrlField
6
+ icon={IconPhoneOutgoing}
7
+ {...props}
8
+ anchorProps={{ href: `tel:${props.value}`, ...props.anchorProps }}
9
+ />;
@@ -0,0 +1,96 @@
1
+ import { Stack } from "@mantine/core";
2
+ import { Meta } from "@storybook/react";
3
+ import { UrlField } from "./UrlField";
4
+
5
+ export default {
6
+ title: 'Fields/UrlField',
7
+ component: UrlField,
8
+ decorators: (Story) => (
9
+ <Stack h="100vh" align="center" justify="center">
10
+ <Story />
11
+ </Stack>
12
+ ),
13
+ } satisfies Meta<typeof UrlField>;
14
+
15
+ export const Default = () =>
16
+ <UrlField value="https://github.com/kruschid/refine-mantine">refine-mantine</UrlField>;
17
+
18
+ export const Colors = () =>
19
+ <>
20
+ <UrlField
21
+ value="https://github.com/kruschid/refine-mantine"
22
+ anchorProps={{c: "indigo"}}
23
+ />
24
+ <UrlField
25
+ value="https://github.com/kruschid/refine-mantine"
26
+ anchorProps={{c: "cyan"}}
27
+ />
28
+ <UrlField
29
+ value="https://github.com/kruschid/refine-mantine"
30
+ anchorProps={{c: "teal"}}
31
+ />
32
+ <UrlField
33
+ value="https://github.com/kruschid/refine-mantine"
34
+ anchorProps={{c: "lime"}}
35
+ />
36
+ </>;
37
+
38
+ export const Sizes = () =>
39
+ <>
40
+ <UrlField
41
+ value="https://github.com/kruschid/refine-mantine"
42
+ textProps={{size: "xs"}}
43
+ />
44
+ <UrlField
45
+ value="https://github.com/kruschid/refine-mantine"
46
+ textProps={{size: "sm"}}
47
+ />
48
+ <UrlField
49
+ value="https://github.com/kruschid/refine-mantine"
50
+ textProps={{size: "md"}}
51
+ />
52
+ <UrlField
53
+ value="https://github.com/kruschid/refine-mantine"
54
+ textProps={{size: "lg"}}
55
+ />
56
+ <UrlField
57
+ value="https://github.com/kruschid/refine-mantine"
58
+ textProps={{size: "xl"}}
59
+ />
60
+ </>
61
+
62
+ export const WithTarget = () =>
63
+ <UrlField
64
+ value="https://github.com/kruschid/refine-mantine"
65
+ anchorProps={{target: "_blank"}}
66
+ />;
67
+
68
+ export const Truncated = () =>
69
+ <UrlField
70
+ value="https://github.com/kruschid/refine-mantine"
71
+ anchorProps={{maw: 200}}
72
+ textProps={{truncate: "end"}}
73
+ />;
74
+
75
+ export const WithTitle = () =>
76
+ <UrlField
77
+ value="https://github.com/kruschid/refine-mantine"
78
+ anchorProps={{title: "refine-mantine"}}
79
+ />;
80
+
81
+ export const Gradient = () =>
82
+ <UrlField
83
+ value="https://github.com/kruschid/refine-mantine"
84
+ anchorProps={{
85
+ variant: "gradient",
86
+ gradient: { from: 'pink', to: 'yellow' }
87
+ }}
88
+ />;
89
+
90
+ export const HiddenIcon = () =>
91
+ <UrlField
92
+ value="https://github.com/kruschid/refine-mantine"
93
+ iconProps={{
94
+ display: "none",
95
+ }}
96
+ />;
@@ -0,0 +1,47 @@
1
+ import { Anchor, AnchorProps, PolymorphicComponentProps, Text, TextProps } from "@mantine/core";
2
+ import { IconExternalLink, IconProps, ReactNode } from "@tabler/icons-react";
3
+
4
+ export interface UrlFieldProps {
5
+ value: string;
6
+ children?: ReactNode;
7
+ anchorProps?: PolymorphicComponentProps<"a", AnchorProps>;
8
+ textProps?: TextProps;
9
+ iconProps?: IconProps;
10
+ icon?: React.FC<IconProps>;
11
+ }
12
+
13
+ export const UrlField: React.FC<UrlFieldProps> = ({
14
+ children,
15
+ value,
16
+ anchorProps,
17
+ textProps,
18
+ icon,
19
+ iconProps,
20
+ }) => {
21
+ const Icon = icon ?? IconExternalLink;
22
+
23
+ return (
24
+ <Anchor
25
+ href={value}
26
+ style={{display: "inline-flex", alignItems: "center"}}
27
+ {...anchorProps}
28
+ >
29
+ <Text {...textProps}>
30
+ {children ?? value}
31
+ </Text>
32
+ <Icon
33
+ size={
34
+ textProps?.size === "xs" ? 12
35
+ : textProps?.size === "sm" ? 14
36
+ : textProps?.size === "md" ? 16
37
+ : textProps?.size === "lg" ? 18
38
+ : textProps?.size === "xl" ? 20
39
+ : 16
40
+ }
41
+ style={{ marginLeft: 4, flexShrink: 0 }}
42
+ aria-hidden
43
+ {...iconProps}
44
+ />
45
+ </Anchor>
46
+ );
47
+ }
@@ -1,9 +1,12 @@
1
- import { Badge, NumberFormatter, Select, Text } from "@mantine/core";
1
+ import { ActionIconGroup, Badge, NumberFormatter, Select, Text } from "@mantine/core";
2
2
  import { type CrudOperators, useSelect } from "@refinedev/core";
3
3
  import { useTable } from "@refinedev/react-table";
4
4
  import type { Meta } from "@storybook/react";
5
5
  import type { ColumnDef } from "@tanstack/react-table";
6
- import { use, useCallback, useMemo } from "react";
6
+ import { useCallback, useMemo } from "react";
7
+ import { DeleteButton } from "../buttons/DeleteButton";
8
+ import { EditButton } from "../buttons/EditButton";
9
+ import { ShowButton } from "../buttons/ShowButton";
7
10
  import { Table } from "./Table";
8
11
 
9
12
  export default {
@@ -30,7 +33,7 @@ interface CategoryRecord {
30
33
  type ProductRecordKey = keyof ProductRecord;
31
34
 
32
35
  const useTableProps = () => {
33
- const categories = useSelect<CategoryRecord>({
36
+ const categories = useSelect<CategoryRecord>({
34
37
  resource: "categories",
35
38
  optionLabel: category => category.title,
36
39
  optionValue: category => category.id.toString(),
@@ -117,6 +120,22 @@ const categories = useSelect<CategoryRecord>({
117
120
  {categoryMap[row.original.category.id] ?? "uncategorized"}
118
121
  </Badge>
119
122
  },
123
+ {
124
+ id: "id" satisfies ProductRecordKey,
125
+ enableColumnFilter: false,
126
+ enableSorting: false,
127
+ header: "Actions",
128
+ cell: ({ row }) => (
129
+ <ActionIconGroup>
130
+ <ShowButton hideText recordItemId={row.original.id} />
131
+ <EditButton
132
+ hideText
133
+ recordItemId={row.original.id}
134
+ />
135
+ <DeleteButton hideText recordItemId={row.original.id} />
136
+ </ActionIconGroup>
137
+ ),
138
+ },
120
139
  ],
121
140
  [categoryMap, CategoryFilter],
122
141
  );
@@ -1,4 +1,4 @@
1
- import { Box, Group, Table as MantineTable, Pagination, TableProps, TableScrollContainerProps } from "@mantine/core";
1
+ import { Box, Group, Table as MantineTable, Pagination, type TableProps } from "@mantine/core";
2
2
  import type { BaseRecord, HttpError } from "@refinedev/core";
3
3
  import type { UseTableReturnType } from "@refinedev/react-table";
4
4
  import { flexRender } from "@tanstack/react-table";