@recursica/mantine-adapter 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/mantine-adapter.cjs +1 -1
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +1688 -1330
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Button/Button.d.ts +7 -1
- package/dist/src/components/Container/Container.d.ts +8 -5
- package/dist/src/components/Flex/Flex.d.ts +10 -7
- package/dist/src/components/Group/Group.d.ts +8 -5
- package/dist/src/components/Link/Link.d.ts +32 -2
- package/dist/src/components/Link/index.d.ts +1 -0
- package/dist/src/components/Pagination/Pagination.d.ts +50 -2
- package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
- package/dist/src/components/Pagination/index.d.ts +1 -0
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +16 -3
- package/dist/src/components/Stack/Stack.d.ts +8 -5
- package/dist/src/components/Stepper/Stepper.d.ts +12 -2
- package/dist/src/components/Tabs/Tabs.d.ts +19 -3
- package/dist/src/components/index.d.ts +0 -2
- package/package.json +1 -1
- package/src/OverStyling.stories.tsx +22 -0
- package/src/components/Button/Button.stories.tsx +11 -0
- package/src/components/Button/Button.tsx +7 -1
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Container/Container.tsx +13 -17
- package/src/components/Flex/Flex.tsx +13 -17
- package/src/components/Group/Group.tsx +13 -17
- package/src/components/Link/IMPLEMENTATION_NOTES.md +26 -0
- package/src/components/Link/Link.module.css +133 -0
- package/src/components/Link/Link.stories.tsx +46 -4
- package/src/components/Link/Link.tsx +82 -5
- package/src/components/Link/index.ts +1 -0
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +16 -0
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +225 -0
- package/src/components/Pagination/Pagination.stories.tsx +43 -4
- package/src/components/Pagination/Pagination.tsx +214 -4
- package/src/components/Pagination/index.ts +5 -0
- package/src/components/Panel/Panel.stories.tsx +1 -1
- package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +17 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +138 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +108 -6
- package/src/components/SegmentedControl/SegmentedControl.tsx +96 -5
- package/src/components/Stack/Stack.tsx +13 -17
- package/src/components/Stepper/Stepper.module.css +353 -0
- package/src/components/Stepper/Stepper.stories.tsx +135 -4
- package/src/components/Stepper/Stepper.tsx +68 -5
- package/src/components/Tabs/Tabs.module.css +401 -0
- package/src/components/Tabs/Tabs.stories.tsx +141 -4
- package/src/components/Tabs/Tabs.tsx +107 -5
- package/src/components/index.ts +0 -2
- package/dist/src/components/Popover/Popover.d.ts +0 -3
- package/dist/src/components/Search/Search.d.ts +0 -3
- package/src/components/Popover/Popover.stories.tsx +0 -17
- package/src/components/Popover/Popover.tsx +0 -7
- package/src/components/Search/Search.stories.tsx +0 -17
- package/src/components/Search/Search.tsx +0 -7
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Group as MantineGroup,
|
|
4
4
|
type GroupProps as MantineGroupProps,
|
|
5
5
|
} from "@mantine/core";
|
|
6
|
-
import {
|
|
7
|
-
filterStylingProps,
|
|
8
|
-
type RecursicaOverStyled,
|
|
9
|
-
type RecursicaSpacing,
|
|
10
|
-
} from "../../utils/filterStylingProps";
|
|
6
|
+
import { type RecursicaSpacing } from "../../utils/filterStylingProps";
|
|
11
7
|
import styles from "./Group.module.css";
|
|
12
8
|
|
|
13
9
|
export interface RecursicaGroupProps {
|
|
@@ -21,24 +17,23 @@ export interface RecursicaGroupProps {
|
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
/**
|
|
24
|
-
* Group flex layout wrapper
|
|
20
|
+
* Group flex layout wrapper.
|
|
21
|
+
*
|
|
22
|
+
* Note: Unlike complex UI components, primitive layout components (Flex, Stack, Group, Container)
|
|
23
|
+
* DO NOT use the `RecursicaOverStyled` gatekeeper. Developers must be able to freely pass
|
|
24
|
+
* width, height, padding, margins, and flexbox alignment props to construct structural layouts.
|
|
25
25
|
*/
|
|
26
|
-
export type GroupProps =
|
|
27
|
-
MantineGroupProps & RecursicaGroupProps
|
|
28
|
-
>;
|
|
26
|
+
export type GroupProps = MantineGroupProps & RecursicaGroupProps;
|
|
29
27
|
|
|
30
28
|
export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
31
|
-
{ children,
|
|
29
|
+
{ children, gap = "rec-default", ...rest },
|
|
32
30
|
ref,
|
|
33
31
|
) {
|
|
34
|
-
const sanitizedProps = filterStylingProps({ ...rest, gap }, overStyled);
|
|
35
|
-
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
36
|
-
|
|
37
32
|
const mergedClassNames: Partial<Record<string, string>> = {
|
|
38
33
|
root: styles.root,
|
|
39
34
|
};
|
|
40
35
|
|
|
41
|
-
const classNamesProp =
|
|
36
|
+
const classNamesProp = rest.classNames;
|
|
42
37
|
if (
|
|
43
38
|
classNamesProp &&
|
|
44
39
|
typeof classNamesProp === "object" &&
|
|
@@ -48,7 +43,7 @@ export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
|
48
43
|
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
const classNameProp =
|
|
46
|
+
const classNameProp = rest.className as string | undefined;
|
|
52
47
|
const finalClass = classNameProp
|
|
53
48
|
? `${styles.root} ${classNameProp}`
|
|
54
49
|
: styles.root;
|
|
@@ -58,7 +53,8 @@ export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
|
58
53
|
ref={ref}
|
|
59
54
|
className={finalClass}
|
|
60
55
|
classNames={mergedClassNames}
|
|
61
|
-
{
|
|
56
|
+
gap={gap}
|
|
57
|
+
{...rest}
|
|
62
58
|
>
|
|
63
59
|
{children}
|
|
64
60
|
</MantineGroup>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Link Component Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Missing States in Design Tokens
|
|
4
|
+
|
|
5
|
+
Currently, the design tokens (`recursica_variables_scoped.css`) only provide states for:
|
|
6
|
+
|
|
7
|
+
- `default`
|
|
8
|
+
- `hover`
|
|
9
|
+
- `visited`
|
|
10
|
+
- `visited-hover`
|
|
11
|
+
|
|
12
|
+
There are no tokens for the `active` or `focus` states. If these are needed, they must be added to the Figma variables and exported via the token pipeline. The component relies on the browser's default focus outline for accessibility unless overriden by a global reset.
|
|
13
|
+
|
|
14
|
+
## Overriding Mantine's underline Prop
|
|
15
|
+
|
|
16
|
+
Mantine's `Anchor` component uses `underline="hover"` by default. Because our design system specifies exact `text-decoration` styles via CSS tokens, we explicitly pass `underline="never"` to the underlying Mantine component. This prevents Mantine from injecting its own text-decoration inline or via generic classnames, ensuring our scoped CSS remains the single source of truth.
|
|
17
|
+
|
|
18
|
+
## Base Layout
|
|
19
|
+
|
|
20
|
+
Mantine's `Anchor` renders an inline element by default and does not natively support `leftSection` like the `Button` component. To support an optional `icon` alongside the text, we enforce a baseline layout of `display: inline-flex` and `align-items: center` in `Link.module.css`.
|
|
21
|
+
|
|
22
|
+
When an icon is present, the component conditionally passes a `data-has-icon` attribute to the root element. The CSS module uses this attribute to apply the `icon-text-gap` token via the CSS `gap` property.
|
|
23
|
+
|
|
24
|
+
## Inner Wrappers
|
|
25
|
+
|
|
26
|
+
The icon and children are wrapped in internal `<span>` tags (`.iconWrapper` and `.labelText` respectively). This follows the component development guide for structural robustness, allowing us to enforce specific sizing on the icon and intrinsic text truncation behavior if the link is placed in a bounded container.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/*
|
|
2
|
+
HARDCODED VALUES:
|
|
3
|
+
- display: inline-flex; (Base layout to align icon and text correctly)
|
|
4
|
+
- align-items: center;
|
|
5
|
+
- cursor: pointer;
|
|
6
|
+
- text-decoration: none; (Overridden by our design tokens instead of using Mantine's underline prop)
|
|
7
|
+
- color: inherit; (Reset before applying token)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.root {
|
|
11
|
+
/* HARDCODE: base layout to support optional icon gaps */
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
|
|
16
|
+
/* HARDCODE: Reset underline and color so we can explicitly use our tokens */
|
|
17
|
+
text-decoration: none;
|
|
18
|
+
color: inherit;
|
|
19
|
+
|
|
20
|
+
/* Typography Default Tokens */
|
|
21
|
+
font-family: var(
|
|
22
|
+
--recursica_ui-kit_components_link_properties_text_font-family
|
|
23
|
+
);
|
|
24
|
+
font-size: var(--recursica_ui-kit_components_link_properties_text_font-size);
|
|
25
|
+
letter-spacing: var(
|
|
26
|
+
--recursica_ui-kit_components_link_properties_text_letter-spacing
|
|
27
|
+
);
|
|
28
|
+
line-height: var(
|
|
29
|
+
--recursica_ui-kit_components_link_properties_text_line-height
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
/* Default State Tokens */
|
|
33
|
+
font-style: var(
|
|
34
|
+
--recursica_ui-kit_components_link_variants_states_default_properties_text_font-style
|
|
35
|
+
);
|
|
36
|
+
font-weight: var(
|
|
37
|
+
--recursica_ui-kit_components_link_variants_states_default_properties_text_font-weight
|
|
38
|
+
);
|
|
39
|
+
text-decoration: var(
|
|
40
|
+
--recursica_ui-kit_components_link_variants_states_default_properties_text_text-decoration
|
|
41
|
+
);
|
|
42
|
+
color: var(
|
|
43
|
+
--recursica_ui-kit_components_link_variants_states_default_properties_colors_text
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.root[data-has-icon] {
|
|
48
|
+
gap: var(--recursica_ui-kit_components_link_properties_icon-text-gap);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.root:hover {
|
|
52
|
+
/* Reset Mantine's potential hover override */
|
|
53
|
+
text-decoration: var(
|
|
54
|
+
--recursica_ui-kit_components_link_variants_states_hover_properties_text_text-decoration
|
|
55
|
+
);
|
|
56
|
+
font-style: var(
|
|
57
|
+
--recursica_ui-kit_components_link_variants_states_hover_properties_text_font-style
|
|
58
|
+
);
|
|
59
|
+
font-weight: var(
|
|
60
|
+
--recursica_ui-kit_components_link_variants_states_hover_properties_text_font-weight
|
|
61
|
+
);
|
|
62
|
+
color: var(
|
|
63
|
+
--recursica_ui-kit_components_link_variants_states_hover_properties_colors_text
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.root:visited {
|
|
68
|
+
text-decoration: var(
|
|
69
|
+
--recursica_ui-kit_components_link_variants_states_visited_properties_text_text-decoration
|
|
70
|
+
);
|
|
71
|
+
font-style: var(
|
|
72
|
+
--recursica_ui-kit_components_link_variants_states_visited_properties_text_font-style
|
|
73
|
+
);
|
|
74
|
+
font-weight: var(
|
|
75
|
+
--recursica_ui-kit_components_link_variants_states_visited_properties_text_font-weight
|
|
76
|
+
);
|
|
77
|
+
color: var(
|
|
78
|
+
--recursica_ui-kit_components_link_variants_states_visited_properties_colors_text
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.root:visited:hover {
|
|
83
|
+
text-decoration: var(
|
|
84
|
+
--recursica_ui-kit_components_link_variants_states_visited-hover_properties_text_text-decoration
|
|
85
|
+
);
|
|
86
|
+
font-style: var(
|
|
87
|
+
--recursica_ui-kit_components_link_variants_states_visited-hover_properties_text_font-style
|
|
88
|
+
);
|
|
89
|
+
font-weight: var(
|
|
90
|
+
--recursica_ui-kit_components_link_variants_states_visited-hover_properties_text_font-weight
|
|
91
|
+
);
|
|
92
|
+
color: var(
|
|
93
|
+
--recursica_ui-kit_components_link_variants_states_visited-hover_properties_colors_text
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Icon Support */
|
|
98
|
+
.iconWrapper {
|
|
99
|
+
/* HARDCODE: maintain intrinsic icon bounds */
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
|
|
104
|
+
width: var(--recursica_ui-kit_components_link_properties_icon-size);
|
|
105
|
+
height: var(--recursica_ui-kit_components_link_properties_icon-size);
|
|
106
|
+
color: var(
|
|
107
|
+
--recursica_ui-kit_components_link_variants_states_default_properties_colors_icon
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.root:hover .iconWrapper {
|
|
112
|
+
color: var(
|
|
113
|
+
--recursica_ui-kit_components_link_variants_states_hover_properties_colors_icon
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.root:visited .iconWrapper {
|
|
118
|
+
color: var(
|
|
119
|
+
--recursica_ui-kit_components_link_variants_states_visited_properties_colors_icon
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.root:visited:hover .iconWrapper {
|
|
124
|
+
color: var(
|
|
125
|
+
--recursica_ui-kit_components_link_variants_states_visited-hover_properties_colors_icon
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Label text wrapper */
|
|
130
|
+
.labelText {
|
|
131
|
+
/* HARDCODE: allows text truncation if bounded */
|
|
132
|
+
min-width: 0;
|
|
133
|
+
}
|
|
@@ -1,17 +1,59 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1
3
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
4
|
import { Link } from "./Link";
|
|
3
|
-
import {
|
|
5
|
+
import { Text } from "@mantine/core";
|
|
4
6
|
|
|
5
7
|
const meta: Meta<typeof Link> = {
|
|
6
|
-
title: "UI-Kit
|
|
8
|
+
title: "UI-Kit/Link",
|
|
7
9
|
component: Link,
|
|
8
10
|
tags: ["autodocs"],
|
|
11
|
+
args: {
|
|
12
|
+
children: "Link text",
|
|
13
|
+
href: "#",
|
|
14
|
+
},
|
|
15
|
+
render: ({ withLayer, layer, ...args }: any) => <Link {...args} />,
|
|
9
16
|
};
|
|
10
17
|
|
|
11
18
|
export default meta;
|
|
12
19
|
|
|
13
20
|
type Story = StoryObj<typeof Link>;
|
|
14
21
|
|
|
15
|
-
export const Default: Story = {
|
|
16
|
-
|
|
22
|
+
export const Default: Story = {};
|
|
23
|
+
|
|
24
|
+
export const WithIcon: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
icon: (
|
|
27
|
+
<svg
|
|
28
|
+
width="24"
|
|
29
|
+
height="24"
|
|
30
|
+
viewBox="0 0 24 24"
|
|
31
|
+
fill="none"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
strokeWidth="2"
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
>
|
|
37
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
|
38
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
|
39
|
+
</svg>
|
|
40
|
+
),
|
|
41
|
+
children: "Link with Icon",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Polymorphic: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
component: "button",
|
|
48
|
+
onClick: () => alert("Button clicked!"),
|
|
49
|
+
children: "Rendered as <button>",
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const InlineText: Story = {
|
|
54
|
+
render: () => (
|
|
55
|
+
<Text>
|
|
56
|
+
Here is some text with an <Link href="#">inline link</Link> inside it.
|
|
57
|
+
</Text>
|
|
58
|
+
),
|
|
17
59
|
};
|
|
@@ -1,7 +1,84 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Anchor as MantineAnchor,
|
|
4
|
+
type AnchorProps as MantineAnchorProps,
|
|
5
|
+
createPolymorphicComponent,
|
|
6
|
+
} from "@mantine/core";
|
|
7
|
+
import {
|
|
8
|
+
filterStylingProps,
|
|
9
|
+
type RecursicaOverStyled,
|
|
10
|
+
} from "../../utils/filterStylingProps";
|
|
11
|
+
import styles from "./Link.module.css";
|
|
2
12
|
|
|
3
|
-
export
|
|
13
|
+
export interface RecursicaLinkProps {
|
|
14
|
+
/**
|
|
15
|
+
* Optional leading icon to display next to the link text.
|
|
16
|
+
*/
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* The text or elements to display inside the link.
|
|
20
|
+
*/
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
}
|
|
4
23
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
24
|
+
export type LinkProps = RecursicaOverStyled<
|
|
25
|
+
Omit<MantineAnchorProps, "underline"> & RecursicaLinkProps
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
const _Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
|
|
29
|
+
{ icon, children, overStyled = false, ...rest },
|
|
30
|
+
ref,
|
|
31
|
+
) {
|
|
32
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
33
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
34
|
+
|
|
35
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
36
|
+
root: styles.root,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const classNamesProp = restRecord.classNames;
|
|
40
|
+
if (
|
|
41
|
+
classNamesProp &&
|
|
42
|
+
typeof classNamesProp === "object" &&
|
|
43
|
+
!Array.isArray(classNamesProp)
|
|
44
|
+
) {
|
|
45
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
46
|
+
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
50
|
+
const finalClass = classNameProp
|
|
51
|
+
? `${styles.root} ${classNameProp}`
|
|
52
|
+
: styles.root;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<MantineAnchor
|
|
56
|
+
ref={ref}
|
|
57
|
+
className={finalClass}
|
|
58
|
+
classNames={mergedClassNames}
|
|
59
|
+
underline="never"
|
|
60
|
+
{...(icon ? { "data-has-icon": "" } : {})}
|
|
61
|
+
{...sanitizedProps}
|
|
62
|
+
>
|
|
63
|
+
{icon && (
|
|
64
|
+
<span className={styles.iconWrapper} aria-hidden>
|
|
65
|
+
{icon}
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
<span className={styles.labelText}>{children}</span>
|
|
69
|
+
</MantineAnchor>
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
_Link.displayName = "Link";
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Recursica Link component wrapping Mantine's Anchor.
|
|
76
|
+
*
|
|
77
|
+
* Supports polymorphism via the `component` prop or `renderRoot` for custom element rendering (e.g. react-router Link).
|
|
78
|
+
* @example
|
|
79
|
+
* ```tsx
|
|
80
|
+
* <Link component="a" href="https://example.com">External Link</Link>
|
|
81
|
+
* <Link renderRoot={(props) => <RouterLink to="/home" {...props} />}>Home</Link>
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const Link = createPolymorphicComponent<"a", LinkProps>(_Link);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Link";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Pagination Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Architecture Decision: CSS Inheritance vs. Composition
|
|
4
|
+
|
|
5
|
+
The Figma design tokens for the `Pagination` component dictate that pagination pages perfectly mimic `Button` variants (e.g. `active-pages_style: "solid"`, `inactive-pages_style: "outline"`, `navigation-controls_style: "text"`).
|
|
6
|
+
|
|
7
|
+
As of the `1.2.0` scoped CSS update, the Figma exporter automatically handles these variants. It flattens and aliases the referenced Button properties directly into the `Pagination` component's variable namespace.
|
|
8
|
+
|
|
9
|
+
We opted to use Mantine's native `PaginationControl` components to ensure all DOM structure, focus management, and accessibility attributes are preserved natively without us needing to carefully rebuild `Pagination.Items` mappings.
|
|
10
|
+
|
|
11
|
+
To honor the Figma design intents while using Mantine's raw `<button>` elements:
|
|
12
|
+
|
|
13
|
+
1. We inherit and map all natively scoped `Pagination` variant styles (small typography, outline/solid/text colors, and hover overlays) directly into `.control` and `.control[data-active]` within `Pagination.module.css`. We do not need to manually reference `Button` variables cross-component.
|
|
14
|
+
2. We inject `data-variant="text"` via `getControlProps` onto the navigation buttons so that they inherit the explicitly aliased text style variables (`navigation-controls`) defined by the UI Kit for pagination.
|
|
15
|
+
|
|
16
|
+
This achieves exact optical alignment with the tokens while maintaining Mantine's robust internal event handling for pagination.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import styles from "./Pagination.module.css";
|
|
2
|
+
|
|
3
|
+
const NextPath =
|
|
4
|
+
"M8.781 8l-3.3-3.3.943-.943L10.667 8l-4.243 4.243-.943-.943 3.3-3.3z";
|
|
5
|
+
const PrevPath =
|
|
6
|
+
"M7.219 8l3.3 3.3-.943.943L5.333 8l4.243-4.243.943.943-3.3 3.3z";
|
|
7
|
+
const FirstPath =
|
|
8
|
+
"M6.85355 3.85355C7.04882 3.65829 7.04882 3.34171 6.85355 3.14645C6.65829 2.95118 6.34171 2.95118 6.14645 3.14645L2.14645 7.14645C1.95118 7.34171 1.95118 7.65829 2.14645 7.85355L6.14645 11.8536C6.34171 12.0488 6.65829 12.0488 6.85355 11.8536C7.04882 11.6583 7.04882 11.3417 6.85355 11.1464L3.20711 7.5L6.85355 3.85355ZM12.8536 3.85355C13.0488 3.65829 13.0488 3.34171 12.8536 3.14645C12.6583 2.95118 12.3417 2.95118 12.1464 3.14645L8.14645 7.14645C7.95118 7.34171 7.95118 7.65829 8.14645 7.85355L12.1464 11.8536C12.3417 12.0488 12.6583 12.0488 12.8536 11.8536C13.0488 11.6583 13.0488 11.3417 12.8536 11.1464L9.20711 7.5L12.8536 3.85355Z";
|
|
9
|
+
const LastPath =
|
|
10
|
+
"M2.14645 11.1464C1.95118 11.3417 1.95118 11.6583 2.14645 11.8536C2.34171 12.0488 2.65829 12.0488 2.85355 11.8536L6.85355 7.85355C7.04882 7.65829 7.04882 7.34171 6.85355 7.14645L2.85355 3.14645C2.65829 2.95118 2.34171 2.95118 2.14645 3.14645C1.95118 3.34171 1.95118 3.65829 2.14645 3.85355L5.79289 7.5L2.14645 11.1464ZM8.14645 11.1464C7.95118 11.3417 7.95118 11.6583 8.14645 11.8536C8.34171 12.0488 8.65829 12.0488 8.85355 11.8536L12.8536 7.85355C13.0488 7.65829 13.0488 7.34171 12.8536 7.14645L8.85355 3.14645C8.65829 2.95118 8.34171 2.95118 8.14645 3.14645C7.95118 3.34171 7.95118 3.65829 8.14645 3.85355L11.7929 7.5L8.14645 11.1464Z";
|
|
11
|
+
|
|
12
|
+
export type PaginationIconType = "next" | "prev" | "first" | "last";
|
|
13
|
+
|
|
14
|
+
const paths: Record<PaginationIconType, string> = {
|
|
15
|
+
next: NextPath,
|
|
16
|
+
prev: PrevPath,
|
|
17
|
+
first: FirstPath,
|
|
18
|
+
last: LastPath,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const PaginationIcon = ({
|
|
22
|
+
type,
|
|
23
|
+
className,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<"svg"> & { type: PaginationIconType }) => (
|
|
26
|
+
<svg
|
|
27
|
+
viewBox="0 0 16 16"
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
className={`${styles.baseIcon} ${className || ""}`.trim()}
|
|
30
|
+
{...props}
|
|
31
|
+
>
|
|
32
|
+
<path d={paths[type as PaginationIconType]} fill="currentColor" />
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
export const NextWithLabel = (props: any) => (
|
|
38
|
+
<div className={styles.iconWithLabel}>
|
|
39
|
+
<span>Next</span>
|
|
40
|
+
<PaginationIcon type="next" {...props} />
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
export const PrevWithLabel = (props: any) => (
|
|
46
|
+
<div className={styles.iconWithLabel}>
|
|
47
|
+
<PaginationIcon type="prev" {...props} />
|
|
48
|
+
<span>Prev</span>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
export const FirstWithLabel = (props: any) => (
|
|
54
|
+
<div className={styles.iconWithLabel}>
|
|
55
|
+
<PaginationIcon type="first" {...props} />
|
|
56
|
+
<span>First</span>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
export const LastWithLabel = (props: any) => (
|
|
62
|
+
<div className={styles.iconWithLabel}>
|
|
63
|
+
<span>Last</span>
|
|
64
|
+
<PaginationIcon type="last" {...props} />
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--recursica_ui-kit_components_pagination_properties_item-gap);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.control {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
margin: 0;
|
|
10
|
+
border-style: solid;
|
|
11
|
+
position: relative;
|
|
12
|
+
transition: all 0.2s ease;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
|
|
18
|
+
/* Typography */
|
|
19
|
+
font-family: var(
|
|
20
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_font-family,
|
|
21
|
+
var(--recursica_brand_fonts_primary)
|
|
22
|
+
);
|
|
23
|
+
font-size: var(
|
|
24
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_font-size,
|
|
25
|
+
var(--recursica_brand_dimensions_text-size_default)
|
|
26
|
+
);
|
|
27
|
+
font-style: var(
|
|
28
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_font-style,
|
|
29
|
+
normal
|
|
30
|
+
);
|
|
31
|
+
font-weight: var(
|
|
32
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_font-weight,
|
|
33
|
+
500
|
|
34
|
+
);
|
|
35
|
+
letter-spacing: var(
|
|
36
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_letter-spacing,
|
|
37
|
+
0
|
|
38
|
+
);
|
|
39
|
+
line-height: var(
|
|
40
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_line-height,
|
|
41
|
+
1.5
|
|
42
|
+
);
|
|
43
|
+
text-decoration: var(
|
|
44
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_text-decoration,
|
|
45
|
+
none
|
|
46
|
+
);
|
|
47
|
+
text-transform: var(
|
|
48
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_text_text-transform,
|
|
49
|
+
none
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/* Dimensions */
|
|
53
|
+
border-radius: var(
|
|
54
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_border-radius
|
|
55
|
+
);
|
|
56
|
+
height: var(
|
|
57
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_height
|
|
58
|
+
);
|
|
59
|
+
min-width: var(
|
|
60
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_min-width
|
|
61
|
+
);
|
|
62
|
+
padding: 0
|
|
63
|
+
var(
|
|
64
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_horizontal-padding
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
/* Base Inactive Style: Outline */
|
|
68
|
+
border-width: var(
|
|
69
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_border-size
|
|
70
|
+
);
|
|
71
|
+
box-shadow: var(
|
|
72
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_elevation
|
|
73
|
+
);
|
|
74
|
+
background-color: var(
|
|
75
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_colors_background
|
|
76
|
+
);
|
|
77
|
+
border-color: var(
|
|
78
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_colors_border-color
|
|
79
|
+
);
|
|
80
|
+
color: var(
|
|
81
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_colors_text
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.control:disabled {
|
|
86
|
+
opacity: var(--recursica_brand_states_disabled);
|
|
87
|
+
cursor: not-allowed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.control:hover:not(:disabled) {
|
|
91
|
+
color: var(
|
|
92
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_style_properties_colors_text-hover
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Hover Overlay */
|
|
97
|
+
.control::after {
|
|
98
|
+
content: "";
|
|
99
|
+
position: absolute;
|
|
100
|
+
inset: 0;
|
|
101
|
+
border-radius: inherit;
|
|
102
|
+
z-index: 0;
|
|
103
|
+
pointer-events: none;
|
|
104
|
+
transition: opacity 150ms ease;
|
|
105
|
+
opacity: 0;
|
|
106
|
+
background-color: var(
|
|
107
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_hover-color
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.control:hover:not(:disabled)::after {
|
|
112
|
+
opacity: var(
|
|
113
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_hover-opacity,
|
|
114
|
+
0.1
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Active Style: Solid */
|
|
119
|
+
.control[data-active] {
|
|
120
|
+
border-width: var(
|
|
121
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_border-size
|
|
122
|
+
);
|
|
123
|
+
box-shadow: var(
|
|
124
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_elevation
|
|
125
|
+
);
|
|
126
|
+
background-color: var(
|
|
127
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_colors_background
|
|
128
|
+
);
|
|
129
|
+
border-color: var(
|
|
130
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_colors_border-color
|
|
131
|
+
);
|
|
132
|
+
color: var(
|
|
133
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_colors_text
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.control[data-active]:hover:not(:disabled) {
|
|
138
|
+
color: var(
|
|
139
|
+
--recursica_ui-kit_components_pagination_properties_active-pages_style_properties_colors_text-hover
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Navigation Style: Text */
|
|
144
|
+
.control[data-variant="text"] {
|
|
145
|
+
border-width: var(
|
|
146
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_border-size
|
|
147
|
+
);
|
|
148
|
+
box-shadow: var(
|
|
149
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_elevation
|
|
150
|
+
);
|
|
151
|
+
background-color: var(
|
|
152
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_colors_background
|
|
153
|
+
);
|
|
154
|
+
border-color: var(
|
|
155
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_colors_border-color
|
|
156
|
+
);
|
|
157
|
+
color: var(
|
|
158
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_colors_text
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
/* Also explicitly inherit navigation sizes incase they differ from pages */
|
|
162
|
+
border-radius: var(
|
|
163
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_border-radius
|
|
164
|
+
);
|
|
165
|
+
height: var(
|
|
166
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_height
|
|
167
|
+
);
|
|
168
|
+
min-width: var(
|
|
169
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_min-width
|
|
170
|
+
);
|
|
171
|
+
padding: 0
|
|
172
|
+
var(
|
|
173
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_horizontal-padding
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.control[data-variant="text"]:hover:not(:disabled) {
|
|
178
|
+
color: var(
|
|
179
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_style_properties_colors_text-hover
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Hover Overlay for Navigation */
|
|
184
|
+
.control[data-variant="text"]::after {
|
|
185
|
+
background-color: var(
|
|
186
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_hover-color
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
.control[data-variant="text"]:hover:not(:disabled)::after {
|
|
190
|
+
opacity: var(
|
|
191
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_hover-opacity,
|
|
192
|
+
0.1
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Dots */
|
|
197
|
+
.dots {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
height: var(
|
|
202
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_height
|
|
203
|
+
);
|
|
204
|
+
min-width: var(
|
|
205
|
+
--recursica_ui-kit_components_pagination_properties_inactive-pages_size_properties_min-width
|
|
206
|
+
);
|
|
207
|
+
color: var(
|
|
208
|
+
--recursica_ui-kit_components_pagination_properties_colors_dots-color
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Icon Styles */
|
|
213
|
+
.iconWithLabel {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: var(
|
|
217
|
+
--recursica_ui-kit_components_pagination_properties_navigation-controls_size_properties_icon-text-gap,
|
|
218
|
+
4px
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.baseIcon {
|
|
223
|
+
width: calc(var(--pagination-control-size) / 1.8);
|
|
224
|
+
height: calc(var(--pagination-control-size) / 1.8);
|
|
225
|
+
}
|