@recursica/mantine-adapter 0.7.0 → 0.8.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 +6 -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 +963 -639
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Container/Container.d.ts +14 -0
- package/dist/src/components/Dropdown/Dropdown.d.ts +10 -2
- package/dist/src/components/Flex/Flex.d.ts +17 -0
- package/dist/src/components/Group/Group.d.ts +17 -0
- package/dist/src/components/Stack/Stack.d.ts +15 -0
- package/dist/src/components/TextArea/TextArea.d.ts +14 -2
- package/dist/src/components/index.d.ts +6 -0
- package/package.json +1 -1
- package/src/components/Container/CONTAINER_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Container/Container.module.css +7 -0
- package/src/components/Container/Container.stories.tsx +102 -0
- package/src/components/Container/Container.tsx +63 -0
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +7 -0
- package/src/components/Dropdown/Dropdown.module.css +272 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +77 -5
- package/src/components/Dropdown/Dropdown.tsx +184 -5
- package/src/components/Flex/FLEX_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Flex/Flex.module.css +7 -0
- package/src/components/Flex/Flex.stories.tsx +125 -0
- package/src/components/Flex/Flex.tsx +68 -0
- package/src/components/Group/GROUP_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Group/Group.module.css +7 -0
- package/src/components/Group/Group.stories.tsx +117 -0
- package/src/components/Group/Group.tsx +68 -0
- package/src/components/Stack/STACK_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Stack/Stack.module.css +7 -0
- package/src/components/Stack/Stack.stories.tsx +105 -0
- package/src/components/Stack/Stack.tsx +66 -0
- package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +7 -0
- package/src/components/TextArea/TextArea.module.css +225 -0
- package/src/components/TextArea/TextArea.stories.tsx +77 -5
- package/src/components/TextArea/TextArea.tsx +162 -5
- package/src/components/index.ts +6 -0
|
@@ -1,22 +1,94 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
3
|
import { Dropdown } from "./Dropdown";
|
|
3
|
-
import {
|
|
4
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
type DropdownStoryProps = React.ComponentProps<typeof Dropdown>;
|
|
7
|
+
|
|
8
|
+
const meta: Meta<DropdownStoryProps> = {
|
|
9
|
+
title: "UI-Kit/Dropdown",
|
|
7
10
|
component: Dropdown,
|
|
8
11
|
tags: ["autodocs"],
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"Dropdown provides a selectable list of options, mapping natively over Mantine's Select component encapsulated within the standardized FormControlWrapper.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
label: "Country Selection",
|
|
22
|
+
assistiveText: "Select your country of origin.",
|
|
23
|
+
placeholder: "Pick value",
|
|
24
|
+
data: ["United States", "Canada", "Mexico", "United Kingdom", "France"],
|
|
25
|
+
disabled: false,
|
|
26
|
+
required: false,
|
|
27
|
+
readOnly: false,
|
|
28
|
+
searchable: false,
|
|
29
|
+
clearable: false,
|
|
30
|
+
},
|
|
9
31
|
argTypes: {
|
|
32
|
+
...formControlArgTypes,
|
|
10
33
|
disabled: {
|
|
11
34
|
control: "boolean",
|
|
12
35
|
},
|
|
36
|
+
readOnly: {
|
|
37
|
+
control: "boolean",
|
|
38
|
+
},
|
|
39
|
+
searchable: {
|
|
40
|
+
control: "boolean",
|
|
41
|
+
},
|
|
42
|
+
clearable: {
|
|
43
|
+
control: "boolean",
|
|
44
|
+
},
|
|
45
|
+
checked: {
|
|
46
|
+
table: { disable: true },
|
|
47
|
+
},
|
|
48
|
+
defaultChecked: {
|
|
49
|
+
table: { disable: true },
|
|
50
|
+
},
|
|
51
|
+
containerWidth: {
|
|
52
|
+
table: { disable: true },
|
|
53
|
+
},
|
|
13
54
|
},
|
|
14
55
|
};
|
|
15
56
|
|
|
16
57
|
export default meta;
|
|
17
58
|
|
|
18
|
-
type Story = StoryObj<
|
|
59
|
+
type Story = StoryObj<DropdownStoryProps>;
|
|
19
60
|
|
|
20
61
|
export const Default: Story = {
|
|
21
|
-
|
|
62
|
+
args: {},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const SearchableClearable: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
label: "Search & Clear Options",
|
|
68
|
+
searchable: true,
|
|
69
|
+
clearable: true,
|
|
70
|
+
placeholder: "Start typing...",
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const StaticError: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
error: "You must choose a valid destination.",
|
|
77
|
+
value: "Invalid Island",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const StaticDisabled: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
disabled: true,
|
|
84
|
+
value: "United States",
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const StaticReadOnly: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
label: "Read Only View",
|
|
91
|
+
readOnly: true,
|
|
92
|
+
value: "Canada",
|
|
93
|
+
},
|
|
22
94
|
};
|
|
@@ -1,7 +1,186 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Select as MantineSelect,
|
|
4
|
+
type SelectProps as MantineSelectProps,
|
|
5
|
+
} from "@mantine/core";
|
|
6
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
7
|
+
import {
|
|
8
|
+
filterStylingProps,
|
|
9
|
+
type RecursicaOverStyled,
|
|
10
|
+
} from "../../utils/filterStylingProps";
|
|
11
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
12
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
13
|
+
import styles from "./Dropdown.module.css";
|
|
2
14
|
|
|
3
|
-
export
|
|
15
|
+
export interface RecursicaDropdownProps
|
|
16
|
+
extends Omit<
|
|
17
|
+
MantineSelectProps,
|
|
18
|
+
| "size"
|
|
19
|
+
| "variant"
|
|
20
|
+
| "radius"
|
|
21
|
+
| "wrapperProps"
|
|
22
|
+
| "label"
|
|
23
|
+
| "description"
|
|
24
|
+
| "error"
|
|
25
|
+
| "required"
|
|
26
|
+
| "withAsterisk"
|
|
27
|
+
>,
|
|
28
|
+
Pick<
|
|
29
|
+
RecursicaFormControlWrapperProps,
|
|
30
|
+
| "label"
|
|
31
|
+
| "error"
|
|
32
|
+
| "required"
|
|
33
|
+
| "withAsterisk"
|
|
34
|
+
| "id"
|
|
35
|
+
| "assistiveText"
|
|
36
|
+
| "assistiveWithIcon"
|
|
37
|
+
| "formLayout"
|
|
38
|
+
| "labelSize"
|
|
39
|
+
| "labelAlignment"
|
|
40
|
+
| "labelOptionalText"
|
|
41
|
+
| "labelWithEditIcon"
|
|
42
|
+
| "onLabelEditClick"
|
|
43
|
+
>,
|
|
44
|
+
ReadOnlyControlProps {
|
|
45
|
+
/** Internal hook for Selects to override raw layout width properties when necessary */
|
|
46
|
+
containerWidth?: React.CSSProperties["width"];
|
|
47
|
+
}
|
|
4
48
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
49
|
+
export type DropdownProps = RecursicaOverStyled<RecursicaDropdownProps>;
|
|
50
|
+
|
|
51
|
+
export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
52
|
+
function Dropdown(
|
|
53
|
+
{
|
|
54
|
+
overStyled = false,
|
|
55
|
+
formLayout = "stacked",
|
|
56
|
+
containerWidth,
|
|
57
|
+
|
|
58
|
+
// Label & Wrapper Maps
|
|
59
|
+
labelSize,
|
|
60
|
+
labelAlignment,
|
|
61
|
+
labelOptionalText,
|
|
62
|
+
labelWithEditIcon,
|
|
63
|
+
onLabelEditClick,
|
|
64
|
+
|
|
65
|
+
label,
|
|
66
|
+
assistiveText,
|
|
67
|
+
assistiveWithIcon,
|
|
68
|
+
error,
|
|
69
|
+
required,
|
|
70
|
+
withAsterisk,
|
|
71
|
+
id,
|
|
72
|
+
className,
|
|
73
|
+
style,
|
|
74
|
+
disabled,
|
|
75
|
+
readOnly,
|
|
76
|
+
readOnlyComponent,
|
|
77
|
+
value,
|
|
78
|
+
defaultValue,
|
|
79
|
+
data,
|
|
80
|
+
...rest
|
|
81
|
+
},
|
|
82
|
+
ref,
|
|
83
|
+
) {
|
|
84
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
85
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
86
|
+
|
|
87
|
+
// Delete prohibited sizing hooks from bypassing variables natively
|
|
88
|
+
delete restRecord["size"];
|
|
89
|
+
delete restRecord["variant"];
|
|
90
|
+
delete restRecord["radius"];
|
|
91
|
+
|
|
92
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
93
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
94
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
95
|
+
input: styles.input,
|
|
96
|
+
section: styles.section,
|
|
97
|
+
dropdown: styles.dropdown,
|
|
98
|
+
option: styles.option,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const classNamesProp = restRecord.classNames;
|
|
102
|
+
if (
|
|
103
|
+
classNamesProp &&
|
|
104
|
+
typeof classNamesProp === "object" &&
|
|
105
|
+
!Array.isArray(classNamesProp)
|
|
106
|
+
) {
|
|
107
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
108
|
+
mergedClassNames.wrapper = o.wrapper
|
|
109
|
+
? `${styles.root} ${o.wrapper}`
|
|
110
|
+
: styles.root;
|
|
111
|
+
mergedClassNames.input = o.input
|
|
112
|
+
? `${styles.input} ${o.input}`
|
|
113
|
+
: styles.input;
|
|
114
|
+
mergedClassNames.section = o.section
|
|
115
|
+
? `${styles.section} ${o.section}`
|
|
116
|
+
: styles.section;
|
|
117
|
+
mergedClassNames.dropdown = o.dropdown
|
|
118
|
+
? `${styles.dropdown} ${o.dropdown}`
|
|
119
|
+
: styles.dropdown;
|
|
120
|
+
mergedClassNames.option = o.option
|
|
121
|
+
? `${styles.option} ${o.option}`
|
|
122
|
+
: styles.option;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const injectedStyles = {
|
|
126
|
+
...((style as React.CSSProperties) || {}),
|
|
127
|
+
width: containerWidth || "100%",
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<WithReadOnlyWrapper
|
|
132
|
+
className={className}
|
|
133
|
+
style={injectedStyles}
|
|
134
|
+
controlMaxWidth="var(--recursica_ui-kit_components_dropdown_properties_max-width)"
|
|
135
|
+
controlMinWidth="var(--recursica_ui-kit_components_dropdown_properties_min-width)"
|
|
136
|
+
overStyled={overStyled as true}
|
|
137
|
+
formLayout={formLayout}
|
|
138
|
+
labelSize={labelSize}
|
|
139
|
+
labelAlignment={labelAlignment}
|
|
140
|
+
labelOptionalText={labelOptionalText}
|
|
141
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
142
|
+
onLabelEditClick={onLabelEditClick}
|
|
143
|
+
label={label}
|
|
144
|
+
assistiveText={assistiveText}
|
|
145
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
146
|
+
error={error}
|
|
147
|
+
required={required}
|
|
148
|
+
withAsterisk={withAsterisk}
|
|
149
|
+
id={id}
|
|
150
|
+
readOnly={readOnly}
|
|
151
|
+
readOnlyComponent={readOnlyComponent}
|
|
152
|
+
readOnlyType="text"
|
|
153
|
+
readOnlyValue={
|
|
154
|
+
value !== undefined
|
|
155
|
+
? String(value)
|
|
156
|
+
: defaultValue
|
|
157
|
+
? String(defaultValue)
|
|
158
|
+
: undefined
|
|
159
|
+
}
|
|
160
|
+
activeComponent={
|
|
161
|
+
/* Naked Select execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
|
|
162
|
+
<MantineSelect
|
|
163
|
+
ref={ref}
|
|
164
|
+
classNames={mergedClassNames}
|
|
165
|
+
disabled={disabled}
|
|
166
|
+
value={value}
|
|
167
|
+
defaultValue={defaultValue}
|
|
168
|
+
data={data || []}
|
|
169
|
+
label={undefined}
|
|
170
|
+
description={undefined}
|
|
171
|
+
error={undefined}
|
|
172
|
+
required={undefined}
|
|
173
|
+
withAsterisk={undefined}
|
|
174
|
+
wrapperProps={{
|
|
175
|
+
"data-disabled": disabled ? "true" : undefined,
|
|
176
|
+
"data-error": error ? "true" : undefined,
|
|
177
|
+
}}
|
|
178
|
+
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
179
|
+
/>
|
|
180
|
+
}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
Dropdown.displayName = "Dropdown";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Flex Implementation Notes
|
|
2
|
+
|
|
3
|
+
The `Flex` component is a generic unopinionated flex layout wrapper mapped directly to Mantine's `Flex`.
|
|
4
|
+
It currently does not require any custom logical layouts or CSS workarounds since it serves only to provide absolute, raw manipulation of standard CSS flex properties. All spacing props (gap, align, justify, direction, wrap) pass safely through via the `filterStylingProps` layout-property allowance, with `rec-` dimension tokens scaling transparently mapped to standard gap limits.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Flex } from "./Flex";
|
|
4
|
+
import { Button } from "../Button";
|
|
5
|
+
import { Text } from "../Text/Text";
|
|
6
|
+
|
|
7
|
+
type FlexStoryProps = React.ComponentProps<typeof Flex>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta<FlexStoryProps> = {
|
|
10
|
+
title: "UI-Kit/Flex",
|
|
11
|
+
component: Flex,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
"Flex is a bare-metal flex container that maps directly to Mantine's Flex component, providing unopinionated control over direction, alignment, and wrapping.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
gap: "rec-default",
|
|
23
|
+
align: "center",
|
|
24
|
+
justify: "flex-start",
|
|
25
|
+
direction: "row",
|
|
26
|
+
wrap: "wrap",
|
|
27
|
+
},
|
|
28
|
+
argTypes: {
|
|
29
|
+
gap: {
|
|
30
|
+
control: "select",
|
|
31
|
+
options: [
|
|
32
|
+
"rec-none",
|
|
33
|
+
"rec-sm",
|
|
34
|
+
"rec-default",
|
|
35
|
+
"rec-md",
|
|
36
|
+
"rec-lg",
|
|
37
|
+
"rec-xl",
|
|
38
|
+
"rec-2xl",
|
|
39
|
+
"xs",
|
|
40
|
+
"sm",
|
|
41
|
+
"md",
|
|
42
|
+
"lg",
|
|
43
|
+
"xl",
|
|
44
|
+
],
|
|
45
|
+
description: "Gap between elements",
|
|
46
|
+
},
|
|
47
|
+
direction: {
|
|
48
|
+
control: "select",
|
|
49
|
+
options: ["row", "column", "row-reverse", "column-reverse"],
|
|
50
|
+
description: "Flex-direction property",
|
|
51
|
+
},
|
|
52
|
+
align: {
|
|
53
|
+
control: "select",
|
|
54
|
+
options: ["flex-start", "center", "flex-end", "stretch"],
|
|
55
|
+
description: "Align-items property",
|
|
56
|
+
},
|
|
57
|
+
justify: {
|
|
58
|
+
control: "select",
|
|
59
|
+
options: [
|
|
60
|
+
"flex-start",
|
|
61
|
+
"center",
|
|
62
|
+
"flex-end",
|
|
63
|
+
"space-between",
|
|
64
|
+
"space-around",
|
|
65
|
+
],
|
|
66
|
+
description: "Justify-content property",
|
|
67
|
+
},
|
|
68
|
+
wrap: {
|
|
69
|
+
control: "select",
|
|
70
|
+
options: ["wrap", "nowrap", "wrap-reverse"],
|
|
71
|
+
description: "Flex-wrap property",
|
|
72
|
+
},
|
|
73
|
+
defaultChecked: {
|
|
74
|
+
table: { disable: true },
|
|
75
|
+
},
|
|
76
|
+
rowGap: {
|
|
77
|
+
table: { disable: true },
|
|
78
|
+
},
|
|
79
|
+
columnGap: {
|
|
80
|
+
table: { disable: true },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default meta;
|
|
86
|
+
|
|
87
|
+
type Story = StoryObj<FlexStoryProps>;
|
|
88
|
+
|
|
89
|
+
export const Default: Story = {
|
|
90
|
+
render: (args) => (
|
|
91
|
+
<Flex {...args}>
|
|
92
|
+
<Button variant="solid">Block A</Button>
|
|
93
|
+
<Button variant="outline">Block B</Button>
|
|
94
|
+
<Text>Text inside Flex</Text>
|
|
95
|
+
</Flex>
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const StaticGapSmallColumn: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
gap: "rec-sm",
|
|
102
|
+
direction: "column",
|
|
103
|
+
},
|
|
104
|
+
render: (args) => (
|
|
105
|
+
<Flex {...args}>
|
|
106
|
+
<Button variant="solid">Item 1</Button>
|
|
107
|
+
<Button variant="solid">Item 2</Button>
|
|
108
|
+
<Button variant="solid">Item 3</Button>
|
|
109
|
+
</Flex>
|
|
110
|
+
),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const StaticGapLargeRow: Story = {
|
|
114
|
+
args: {
|
|
115
|
+
gap: "rec-xl",
|
|
116
|
+
direction: "row",
|
|
117
|
+
},
|
|
118
|
+
render: (args) => (
|
|
119
|
+
<Flex {...args}>
|
|
120
|
+
<Button variant="solid">Item 1</Button>
|
|
121
|
+
<Button variant="solid">Item 2</Button>
|
|
122
|
+
<Button variant="solid">Item 3</Button>
|
|
123
|
+
</Flex>
|
|
124
|
+
),
|
|
125
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Flex as MantineFlex,
|
|
4
|
+
type FlexProps as MantineFlexProps,
|
|
5
|
+
} from "@mantine/core";
|
|
6
|
+
import {
|
|
7
|
+
filterStylingProps,
|
|
8
|
+
type RecursicaOverStyled,
|
|
9
|
+
type RecursicaSpacing,
|
|
10
|
+
} from "../../utils/filterStylingProps";
|
|
11
|
+
import styles from "./Flex.module.css";
|
|
12
|
+
|
|
13
|
+
export interface RecursicaFlexProps {
|
|
14
|
+
/**
|
|
15
|
+
* Children components inside the Flex container
|
|
16
|
+
*/
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
gap?: MantineFlexProps["gap"] | RecursicaSpacing;
|
|
19
|
+
rowGap?: MantineFlexProps["gap"] | RecursicaSpacing;
|
|
20
|
+
columnGap?: MantineFlexProps["gap"] | RecursicaSpacing;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Flex layout wrapper
|
|
25
|
+
*/
|
|
26
|
+
export type FlexProps = RecursicaOverStyled<
|
|
27
|
+
MantineFlexProps & RecursicaFlexProps
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
export const Flex = forwardRef<HTMLDivElement, FlexProps>(function Flex(
|
|
31
|
+
{ children, overStyled = false, gap = "rec-default", ...rest },
|
|
32
|
+
ref,
|
|
33
|
+
) {
|
|
34
|
+
const sanitizedProps = filterStylingProps({ ...rest, gap }, overStyled);
|
|
35
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
36
|
+
|
|
37
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
38
|
+
root: styles.root,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const classNamesProp = restRecord.classNames;
|
|
42
|
+
if (
|
|
43
|
+
classNamesProp &&
|
|
44
|
+
typeof classNamesProp === "object" &&
|
|
45
|
+
!Array.isArray(classNamesProp)
|
|
46
|
+
) {
|
|
47
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
48
|
+
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
52
|
+
const finalClass = classNameProp
|
|
53
|
+
? `${styles.root} ${classNameProp}`
|
|
54
|
+
: styles.root;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<MantineFlex
|
|
58
|
+
ref={ref}
|
|
59
|
+
className={finalClass}
|
|
60
|
+
classNames={mergedClassNames}
|
|
61
|
+
{...sanitizedProps}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</MantineFlex>
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
Flex.displayName = "Flex";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Group Implementation Notes
|
|
2
|
+
|
|
3
|
+
The `Group` component is a generic flex layout wrapper mapped directly to Mantine's `Group`.
|
|
4
|
+
It currently does not require any custom logical layouts or CSS workarounds since it serves only to organize layout structure, and doesn't enforce any strict design-system token styling itself. All gap, align, wrap, and justify properties pass safely through via the `filterStylingProps` layout-property allowance.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Group } from "./Group";
|
|
4
|
+
import { Button } from "../Button";
|
|
5
|
+
import { Text } from "../Text/Text";
|
|
6
|
+
|
|
7
|
+
type GroupStoryProps = React.ComponentProps<typeof Group>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta<GroupStoryProps> = {
|
|
10
|
+
title: "UI-Kit/Group",
|
|
11
|
+
component: Group,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
"Group is a flex horizontal layout container that maps directly to Mantine's Group component allowing safe layout property passing.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
gap: "rec-default",
|
|
23
|
+
align: "center",
|
|
24
|
+
justify: "flex-start",
|
|
25
|
+
wrap: "wrap",
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
gap: {
|
|
29
|
+
control: "select",
|
|
30
|
+
options: [
|
|
31
|
+
"rec-none",
|
|
32
|
+
"rec-sm",
|
|
33
|
+
"rec-default",
|
|
34
|
+
"rec-md",
|
|
35
|
+
"rec-lg",
|
|
36
|
+
"rec-xl",
|
|
37
|
+
"rec-2xl",
|
|
38
|
+
"xs",
|
|
39
|
+
"sm",
|
|
40
|
+
"md",
|
|
41
|
+
"lg",
|
|
42
|
+
"xl",
|
|
43
|
+
],
|
|
44
|
+
description: "Gap between elements",
|
|
45
|
+
},
|
|
46
|
+
align: {
|
|
47
|
+
control: "select",
|
|
48
|
+
options: ["flex-start", "center", "flex-end", "stretch"],
|
|
49
|
+
description: "Align-items property",
|
|
50
|
+
},
|
|
51
|
+
justify: {
|
|
52
|
+
control: "select",
|
|
53
|
+
options: [
|
|
54
|
+
"flex-start",
|
|
55
|
+
"center",
|
|
56
|
+
"flex-end",
|
|
57
|
+
"space-between",
|
|
58
|
+
"space-around",
|
|
59
|
+
],
|
|
60
|
+
description: "Justify-content property",
|
|
61
|
+
},
|
|
62
|
+
wrap: {
|
|
63
|
+
control: "select",
|
|
64
|
+
options: ["wrap", "nowrap", "wrap-reverse"],
|
|
65
|
+
description: "Flex-wrap property",
|
|
66
|
+
},
|
|
67
|
+
defaultChecked: {
|
|
68
|
+
table: { disable: true },
|
|
69
|
+
},
|
|
70
|
+
rowGap: {
|
|
71
|
+
table: { disable: true },
|
|
72
|
+
},
|
|
73
|
+
columnGap: {
|
|
74
|
+
table: { disable: true },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default meta;
|
|
80
|
+
|
|
81
|
+
type Story = StoryObj<GroupStoryProps>;
|
|
82
|
+
|
|
83
|
+
export const Default: Story = {
|
|
84
|
+
render: (args) => (
|
|
85
|
+
<Group {...args}>
|
|
86
|
+
<Button variant="solid">Primary</Button>
|
|
87
|
+
<Button variant="outline">Secondary</Button>
|
|
88
|
+
<Text>Text element within Group</Text>
|
|
89
|
+
</Group>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const StaticGapSmall: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
gap: "rec-sm",
|
|
96
|
+
},
|
|
97
|
+
render: (args) => (
|
|
98
|
+
<Group {...args}>
|
|
99
|
+
<Button variant="solid">Item 1</Button>
|
|
100
|
+
<Button variant="solid">Item 2</Button>
|
|
101
|
+
<Button variant="solid">Item 3</Button>
|
|
102
|
+
</Group>
|
|
103
|
+
),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const StaticGapLarge: Story = {
|
|
107
|
+
args: {
|
|
108
|
+
gap: "rec-xl",
|
|
109
|
+
},
|
|
110
|
+
render: (args) => (
|
|
111
|
+
<Group {...args}>
|
|
112
|
+
<Button variant="solid">Item 1</Button>
|
|
113
|
+
<Button variant="solid">Item 2</Button>
|
|
114
|
+
<Button variant="solid">Item 3</Button>
|
|
115
|
+
</Group>
|
|
116
|
+
),
|
|
117
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Group as MantineGroup,
|
|
4
|
+
type GroupProps as MantineGroupProps,
|
|
5
|
+
} from "@mantine/core";
|
|
6
|
+
import {
|
|
7
|
+
filterStylingProps,
|
|
8
|
+
type RecursicaOverStyled,
|
|
9
|
+
type RecursicaSpacing,
|
|
10
|
+
} from "../../utils/filterStylingProps";
|
|
11
|
+
import styles from "./Group.module.css";
|
|
12
|
+
|
|
13
|
+
export interface RecursicaGroupProps {
|
|
14
|
+
/**
|
|
15
|
+
* Children components inside the group
|
|
16
|
+
*/
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
gap?: MantineGroupProps["gap"] | RecursicaSpacing;
|
|
19
|
+
rowGap?: MantineGroupProps["gap"] | RecursicaSpacing;
|
|
20
|
+
columnGap?: MantineGroupProps["gap"] | RecursicaSpacing;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Group flex layout wrapper
|
|
25
|
+
*/
|
|
26
|
+
export type GroupProps = RecursicaOverStyled<
|
|
27
|
+
MantineGroupProps & RecursicaGroupProps
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
export const Group = forwardRef<HTMLDivElement, GroupProps>(function Group(
|
|
31
|
+
{ children, overStyled = false, gap = "rec-default", ...rest },
|
|
32
|
+
ref,
|
|
33
|
+
) {
|
|
34
|
+
const sanitizedProps = filterStylingProps({ ...rest, gap }, overStyled);
|
|
35
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
36
|
+
|
|
37
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
38
|
+
root: styles.root,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const classNamesProp = restRecord.classNames;
|
|
42
|
+
if (
|
|
43
|
+
classNamesProp &&
|
|
44
|
+
typeof classNamesProp === "object" &&
|
|
45
|
+
!Array.isArray(classNamesProp)
|
|
46
|
+
) {
|
|
47
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
48
|
+
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
52
|
+
const finalClass = classNameProp
|
|
53
|
+
? `${styles.root} ${classNameProp}`
|
|
54
|
+
: styles.root;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<MantineGroup
|
|
58
|
+
ref={ref}
|
|
59
|
+
className={finalClass}
|
|
60
|
+
classNames={mergedClassNames}
|
|
61
|
+
{...sanitizedProps}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</MantineGroup>
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
Group.displayName = "Group";
|