@recursica/mantine-adapter 0.13.1 → 0.15.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 (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/mantine-adapter.cjs +1 -1
  3. package/dist/mantine-adapter.cjs.map +1 -1
  4. package/dist/mantine-adapter.css +1 -1
  5. package/dist/mantine-adapter.js +1329 -1082
  6. package/dist/mantine-adapter.js.map +1 -1
  7. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  8. package/dist/src/components/NumberInput/NumberInput.d.ts +10 -2
  9. package/dist/src/components/Panel/Panel.d.ts +65 -3
  10. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  11. package/package.json +1 -1
  12. package/src/components/Card/Card.module.css +1 -0
  13. package/src/components/Card/Card.stories.tsx +48 -58
  14. package/src/components/Dropdown/Dropdown.module.css +0 -3
  15. package/src/components/HoverCard/HOVERCARD_IMPLEMENTATION_NOTES.md +77 -0
  16. package/src/components/HoverCard/HoverCard.module.css +84 -0
  17. package/src/components/HoverCard/HoverCard.stories.tsx +160 -5
  18. package/src/components/HoverCard/HoverCard.tsx +153 -4
  19. package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +18 -0
  20. package/src/components/NumberInput/NumberInput.module.css +250 -0
  21. package/src/components/NumberInput/NumberInput.stories.tsx +84 -4
  22. package/src/components/NumberInput/NumberInput.tsx +144 -5
  23. package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +106 -0
  24. package/src/components/Panel/Panel.module.css +154 -0
  25. package/src/components/Panel/Panel.stories.tsx +216 -6
  26. package/src/components/Panel/Panel.tsx +153 -4
  27. package/src/components/TextArea/TextArea.module.css +0 -3
  28. package/src/components/TextField/TextField.module.css +0 -3
  29. package/src/components/Tooltip/TOOLTIP_IMPLEMENTATION_NOTES.md +85 -0
  30. package/src/components/Tooltip/Tooltip.module.css +81 -0
  31. package/src/components/Tooltip/Tooltip.stories.tsx +154 -6
  32. package/src/components/Tooltip/Tooltip.tsx +114 -4
@@ -1,12 +1,34 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { NumberInput } from "./NumberInput";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
3
 
5
4
  const meta: Meta<typeof NumberInput> = {
6
- title: "UI-Kit/🚧 NumberInput",
5
+ title: "UI-Kit/NumberInput",
7
6
  component: NumberInput,
8
7
  tags: ["autodocs"],
9
- argTypes: {},
8
+ argTypes: {
9
+ label: { control: "text" },
10
+ assistiveText: { control: "text" },
11
+ disabled: { control: "boolean" },
12
+ error: { control: "boolean" },
13
+ readOnly: { control: "boolean" },
14
+ required: { control: "boolean" },
15
+ hideControls: { control: "boolean" },
16
+ formLayout: {
17
+ control: "select",
18
+ options: ["stacked", "side-by-side"],
19
+ },
20
+ labelSize: {
21
+ control: "select",
22
+ options: ["small", "default", "large"],
23
+ },
24
+ checked: { table: { disable: true } },
25
+ defaultChecked: { table: { disable: true } },
26
+ assistiveWithIcon: { table: { disable: true } },
27
+ labelOptionalText: { table: { disable: true } },
28
+ labelWithEditIcon: { table: { disable: true } },
29
+ onLabelEditClick: { table: { disable: true } },
30
+ emptyValueComponent: { table: { disable: true } },
31
+ },
10
32
  };
11
33
 
12
34
  export default meta;
@@ -14,5 +36,63 @@ export default meta;
14
36
  type Story = StoryObj<typeof NumberInput>;
15
37
 
16
38
  export const Default: Story = {
17
- render: () => <ComingSoon componentName="NumberInput" />,
39
+ args: {
40
+ label: "Amount",
41
+ placeholder: "Enter an amount",
42
+ assistiveText: "Must be greater than 0",
43
+ defaultValue: 10,
44
+ min: 0,
45
+ max: 100,
46
+ },
47
+ };
48
+
49
+ export const SideBySideLayout: Story = {
50
+ args: {
51
+ ...Default.args,
52
+ formLayout: "side-by-side",
53
+ },
54
+ };
55
+
56
+ export const States: Story = {
57
+ render: () => (
58
+ <div
59
+ style={{
60
+ display: "flex",
61
+ flexDirection: "column",
62
+ gap: "1rem",
63
+ maxWidth: 400,
64
+ }}
65
+ >
66
+ <NumberInput label="Default" placeholder="Enter a number" />
67
+ <NumberInput label="Disabled" placeholder="Disabled input" disabled />
68
+ <NumberInput label="Error" placeholder="Error state" error />
69
+ <NumberInput label="Read Only" value={42} readOnly />
70
+ <NumberInput label="Required" required />
71
+ </div>
72
+ ),
73
+ };
74
+
75
+ export const WithLeftIcon: Story = {
76
+ args: {
77
+ label: "Price",
78
+ placeholder: "0.00",
79
+ leftSection: <span>$</span>,
80
+ },
81
+ };
82
+
83
+ export const WithRightIcon: Story = {
84
+ args: {
85
+ label: "Percentage",
86
+ placeholder: "0",
87
+ rightSection: <span>%</span>,
88
+ hideControls: true, // Typically hiding controls if rightSection is occupied
89
+ },
90
+ };
91
+
92
+ export const HiddenControls: Story = {
93
+ args: {
94
+ label: "Zip Code",
95
+ placeholder: "Enter zip code",
96
+ hideControls: true,
97
+ },
18
98
  };
@@ -1,7 +1,146 @@
1
- import React from "react";
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ NumberInput as MantineNumberInput,
4
+ type NumberInputProps as MantineNumberInputProps,
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 "./NumberInput.module.css";
2
14
 
3
- export type NumberInputProps = React.HTMLAttributes<HTMLDivElement>;
15
+ export interface RecursicaNumberInputProps
16
+ extends Omit<
17
+ MantineNumberInputProps,
18
+ "size" | "variant" | "radius" | "wrapperProps" | "classNames" | "styles"
19
+ >,
20
+ Pick<
21
+ RecursicaFormControlWrapperProps,
22
+ | "assistiveText"
23
+ | "assistiveWithIcon"
24
+ | "formLayout"
25
+ | "labelSize"
26
+ | "labelAlignment"
27
+ | "labelOptionalText"
28
+ | "labelWithEditIcon"
29
+ | "onLabelEditClick"
30
+ >,
31
+ ReadOnlyControlProps {
32
+ /** Enables/disables the controls to increment and decrement the value */
33
+ hideControls?: boolean;
34
+ }
4
35
 
5
- export const NumberInput: React.FC<NumberInputProps> = (props) => {
6
- return <div {...props}>NumberInput</div>;
7
- };
36
+ export type NumberInputProps = RecursicaOverStyled<RecursicaNumberInputProps>;
37
+
38
+ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
39
+ function NumberInput(props, ref) {
40
+ const {
41
+ overStyled = false,
42
+ formLayout = "stacked",
43
+
44
+ // Label & Wrapper Maps
45
+ labelSize,
46
+ labelAlignment,
47
+ labelOptionalText,
48
+ labelWithEditIcon,
49
+ onLabelEditClick,
50
+
51
+ label,
52
+ assistiveText,
53
+ assistiveWithIcon,
54
+ error,
55
+ required,
56
+ withAsterisk,
57
+ id,
58
+ className,
59
+ style,
60
+ disabled,
61
+ readOnly,
62
+ readOnlyComponent,
63
+ emptyValueComponent,
64
+ value,
65
+ defaultValue,
66
+ hideControls = false,
67
+ ...rest
68
+ } = props;
69
+
70
+ const sanitizedProps = filterStylingProps(rest, overStyled);
71
+ const restRecord = sanitizedProps as Record<string, unknown>;
72
+
73
+ // Delete prohibited sizing hooks from bypassing variables natively
74
+ delete restRecord["size"];
75
+ delete restRecord["variant"];
76
+ delete restRecord["radius"];
77
+
78
+ // Securely map core native blocks down ensuring nested CSS modules map precisely
79
+ const mergedClassNames: Partial<Record<string, string>> = {
80
+ wrapper: styles.root, // The nested Input internal relative wrapper bounding box
81
+ input: styles.input,
82
+ section: styles.section,
83
+ controls: styles.controls,
84
+ control: styles.control,
85
+ };
86
+
87
+ return (
88
+ <WithReadOnlyWrapper
89
+ className={className}
90
+ style={style as React.CSSProperties}
91
+ controlMaxWidth="var(--recursica_ui-kit_components_number-input_properties_max-width)"
92
+ controlMinWidth="var(--recursica_ui-kit_components_number-input_properties_min-width)"
93
+ overStyled={overStyled as true}
94
+ formLayout={formLayout}
95
+ labelSize={labelSize}
96
+ labelAlignment={labelAlignment}
97
+ labelOptionalText={labelOptionalText}
98
+ labelWithEditIcon={labelWithEditIcon}
99
+ onLabelEditClick={onLabelEditClick}
100
+ label={label}
101
+ assistiveText={assistiveText}
102
+ assistiveWithIcon={assistiveWithIcon}
103
+ error={error}
104
+ required={required}
105
+ withAsterisk={withAsterisk}
106
+ id={id}
107
+ readOnly={readOnly}
108
+ readOnlyComponent={readOnlyComponent}
109
+ emptyValueComponent={emptyValueComponent}
110
+ readOnlyType="text"
111
+ readOnlyValue={
112
+ value !== undefined ? value?.toString() : defaultValue?.toString()
113
+ }
114
+ readOnlyNativeProps={props}
115
+ activeComponent={
116
+ /* Naked Input execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
117
+ <MantineNumberInput
118
+ ref={ref}
119
+ classNames={mergedClassNames}
120
+ disabled={disabled}
121
+ value={value}
122
+ defaultValue={defaultValue}
123
+ hideControls={hideControls}
124
+ label={undefined}
125
+ description={undefined}
126
+ error={undefined}
127
+ required={undefined}
128
+ withAsterisk={undefined}
129
+ wrapperProps={{
130
+ "data-disabled": disabled ? "true" : undefined,
131
+ "data-error": error ? "true" : undefined,
132
+ "data-with-left-section": restRecord.leftSection
133
+ ? "true"
134
+ : undefined,
135
+ "data-with-right-section":
136
+ restRecord.rightSection || !hideControls ? "true" : undefined,
137
+ }}
138
+ {...(sanitizedProps as unknown as MantineNumberInputProps)}
139
+ />
140
+ }
141
+ />
142
+ );
143
+ },
144
+ );
145
+
146
+ NumberInput.displayName = "NumberInput";
@@ -0,0 +1,106 @@
1
+ # Panel – Implementation Notes
2
+
3
+ Decisions and design tweaks strictly tailored for the UI Kit's Panel wrapped against `@mantine/core`. This is a living document that tracks _why_ specific logic decisions exist.
4
+
5
+ ---
6
+
7
+ ## 1. Mapping to Mantine Drawer
8
+
9
+ **Decision:** Panel maps to Mantine's `Drawer` component, not `Paper` or `Card`.
10
+
11
+ **Implementation:** Per the Recursica design system specification, "Panels slide in or expand from the edge of the screen to reveal additional content or functionality." This is the exact behavior of Mantine's `Drawer` component, which provides:
12
+
13
+ - Slide-in animation from any screen edge
14
+ - Overlay/backdrop
15
+ - Portal rendering
16
+ - Focus trapping
17
+ - Scroll locking
18
+ - Close button and title in header
19
+
20
+ Paper and Card are static containers; Drawer is an overlay that matches Panel's defined behavior.
21
+
22
+ ---
23
+
24
+ ## 2. Token Namespace: `panel`
25
+
26
+ **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_panel_*` namespace.
27
+
28
+ **Implementation:** The Recursica token system defines the `panel` namespace covering:
29
+
30
+ - Geometry: border-radius, border-size, min-width (200px), max-width (960px)
31
+ - Content padding: content-horizontal-padding (xl), content-vertical-padding (lg)
32
+ - Header/Footer padding: header-footer-horizontal-padding (xl), header-footer-vertical-padding (md)
33
+ - Spacing: header-close-gap (md), footer-button-gap (md)
34
+ - Divider: divider-size (1px), divider-color
35
+ - Elevation: elevation-3
36
+ - Colors (layer-aware): background, border-color, content, divider-color, header-footer-background, title
37
+ - Non-CSS: header-style ("h3") — see §7
38
+
39
+ No tokens from other component namespaces are referenced.
40
+
41
+ ---
42
+
43
+ ## 3. ClassNames Mapping to Drawer
44
+
45
+ **Decision:** Panel maps CSS module classes to Mantine Drawer's stylesNames.
46
+
47
+ **Implementation:** The Drawer stylesNames used are:
48
+
49
+ - `content` — Outer container (background, border, elevation)
50
+ - `header` — Title bar with close button (padding, divider, gap)
51
+ - `title` — Title text color
52
+ - `body` — Scrollable content area (padding)
53
+
54
+ Other stylesNames (`overlay`, `root`, `inner`, `close`) are left to Mantine defaults.
55
+
56
+ ---
57
+
58
+ ## 4. Default Position Override
59
+
60
+ **Decision:** Recursica defaults `position` to `"right"`. Mantine defaults to `"left"`.
61
+
62
+ **Implementation:** The `position="right"` default is set before the prop spread, so developer-provided `position` values still take precedence. Right-side panels are the most common pattern for supplementary content, settings, and detail views.
63
+
64
+ ---
65
+
66
+ ## 5. Custom Panel.Footer
67
+
68
+ **Decision:** A custom `Panel.Footer` sub-component is provided. Mantine's Drawer does not have a native footer.
69
+
70
+ **Implementation:** `Panel.Footer` is a `<div>` with inline styles referencing Recursica CSS variables for:
71
+
72
+ - `header-footer-background` and `header-footer-padding` tokens
73
+ - Top divider using `divider-size` and `divider-color`
74
+ - `footer-button-gap` for action button spacing
75
+ - `margin-top: auto` to push the footer to the bottom
76
+
77
+ Inline styles are used instead of a CSS module class because the footer is rendered inside the Drawer's `<body>` element, and the CSS variables are applied to the body's parent container. The inline styles ensure the footer correctly references the panel tokens regardless of DOM position.
78
+
79
+ ---
80
+
81
+ ## 6. Hardcoded Values
82
+
83
+ ### `border-style: solid` (CSS module, `.content`)
84
+
85
+ Mantine's Drawer content does not set `border-style` natively. Without this, the border-width and border-color tokens have no visible effect. Same pattern as Card, Menu, HoverCard, Tooltip.
86
+
87
+ ---
88
+
89
+ ## 7. `header-style` Typography Utility Class
90
+
91
+ **Decision:** The `header-style` token exports as the string `"h3"`, so we use the generated global utility class `.recursica_brand_typography_h3`.
92
+
93
+ **Implementation:** The PostCSS compiler generates `.recursica_brand_typography_<typeName>` classes at the bottom of the scoped variables file to apply full typography definitions without assigning variables inline. We apply this to the `.title` class via `composes: recursica_brand_typography_h3 from global;`.
94
+
95
+ ---
96
+
97
+ ## 8. Panel Types: Standard vs Scrollable
98
+
99
+ **Decision:** Both standard and scrollable types are supported natively.
100
+
101
+ **Implementation:** Per the Recursica specification:
102
+
103
+ - **Standard** — All content visible without scrolling. The default behavior when content fits.
104
+ - **Scrollable** — Internal scrollbar enabled when content exceeds the panel height. Header and footer CTAs remain pinned.
105
+
106
+ Mantine's Drawer handles this automatically — the `body` section scrolls when content overflows, while the `header` remains fixed. The `Panel.Footer` uses `margin-top: auto` to stay at the bottom.
@@ -0,0 +1,154 @@
1
+ /* HARDCODED VALUES:
2
+ - border-style: solid: Structural rendering rule for the panel border.
3
+ - .inner { left: 0 }: Prevents the fixed Drawer portal from being pushed off-screen in centered flex environments (like Storybook Canvas).
4
+ - flexbox layout on .content & .body: Required to create a vertical flex context that pushes the footer to the bottom.
5
+ - position: sticky & negative margins on .footer: A CSS technique used to break the footer out of the .body padding constraints so it stretches edge-to-edge, while remaining fixed at the bottom of the scroll container.
6
+ - z-index: 10 on .footer: Ensures the footer covers the body content as it scrolls underneath.
7
+ */
8
+
9
+ /* ======================================
10
+ PANEL CONTENT (outer drawer container)
11
+ ====================================== */
12
+
13
+ .content {
14
+ background-color: var(
15
+ --recursica_ui-kit_components_panel_properties_colors_background
16
+ );
17
+ border-style: solid;
18
+ border-width: var(--recursica_ui-kit_components_panel_properties_border-size);
19
+ border-color: var(
20
+ --recursica_ui-kit_components_panel_properties_colors_border-color
21
+ );
22
+ border-radius: var(
23
+ --recursica_ui-kit_components_panel_properties_border-radius
24
+ );
25
+ box-shadow: var(--recursica_ui-kit_components_panel_properties_elevation);
26
+ color: var(--recursica_ui-kit_components_panel_properties_colors_content);
27
+
28
+ display: flex;
29
+ flex-direction: column;
30
+ height: 100%;
31
+ }
32
+
33
+ .inner {
34
+ /* Forces the Drawer Inner container to the left edge, preventing it from inheriting
35
+ a centered static position in flex environments like Storybook's Canvas wrapper */
36
+ left: 0;
37
+ }
38
+
39
+ /* ======================================
40
+ PANEL HEADER
41
+ ====================================== */
42
+
43
+ .header {
44
+ background-color: var(
45
+ --recursica_ui-kit_components_panel_properties_colors_header-footer-background
46
+ );
47
+ padding: var(
48
+ --recursica_ui-kit_components_panel_properties_header-footer-vertical-padding
49
+ )
50
+ var(
51
+ --recursica_ui-kit_components_panel_properties_header-footer-horizontal-padding
52
+ );
53
+ border-bottom: var(
54
+ --recursica_ui-kit_components_panel_properties_divider-size
55
+ )
56
+ solid
57
+ var(--recursica_ui-kit_components_panel_properties_colors_divider-color);
58
+ gap: var(--recursica_ui-kit_components_panel_properties_header-close-gap);
59
+ }
60
+
61
+ /* ======================================
62
+ PANEL TITLE
63
+ ====================================== */
64
+
65
+ .title {
66
+ color: var(--recursica_ui-kit_components_panel_properties_colors_title);
67
+ composes: recursica_brand_typography_h3 from global;
68
+ }
69
+
70
+ .titleTruncate {
71
+ composes: title;
72
+ white-space: nowrap;
73
+ overflow: hidden;
74
+ text-overflow: ellipsis;
75
+ display: block;
76
+ flex: 1;
77
+ min-width: 0;
78
+ }
79
+
80
+ /* ======================================
81
+ PANEL BODY
82
+ ====================================== */
83
+
84
+ .body {
85
+ padding: var(
86
+ --recursica_ui-kit_components_panel_properties_content-vertical-padding
87
+ )
88
+ var(
89
+ --recursica_ui-kit_components_panel_properties_content-horizontal-padding
90
+ );
91
+ color: var(--recursica_ui-kit_components_panel_properties_colors_content);
92
+
93
+ flex: 1;
94
+ display: flex;
95
+ flex-direction: column;
96
+ overflow-y: auto;
97
+ }
98
+
99
+ /* ======================================
100
+ PANEL FOOTER
101
+ ====================================== */
102
+
103
+ .footer {
104
+ background-color: var(
105
+ --recursica_ui-kit_components_panel_properties_colors_header-footer-background
106
+ );
107
+ padding: var(
108
+ --recursica_ui-kit_components_panel_properties_header-footer-vertical-padding
109
+ )
110
+ var(
111
+ --recursica_ui-kit_components_panel_properties_header-footer-horizontal-padding
112
+ );
113
+ border-top: var(--recursica_ui-kit_components_panel_properties_divider-size)
114
+ solid
115
+ var(--recursica_ui-kit_components_panel_properties_colors_divider-color);
116
+
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: flex-end;
120
+ gap: var(--recursica_ui-kit_components_panel_properties_footer-button-gap);
121
+
122
+ /* Ensure footer sticks to the bottom of the scroll container */
123
+ margin-top: auto;
124
+ position: sticky;
125
+
126
+ /* Break out of .body padding constraints to stretch edge-to-edge */
127
+ bottom: calc(
128
+ var(
129
+ --recursica_ui-kit_components_panel_properties_content-vertical-padding
130
+ ) *
131
+ -1
132
+ );
133
+ margin-left: calc(
134
+ var(
135
+ --recursica_ui-kit_components_panel_properties_content-horizontal-padding
136
+ ) *
137
+ -1
138
+ );
139
+ margin-right: calc(
140
+ var(
141
+ --recursica_ui-kit_components_panel_properties_content-horizontal-padding
142
+ ) *
143
+ -1
144
+ );
145
+ margin-bottom: calc(
146
+ var(
147
+ --recursica_ui-kit_components_panel_properties_content-vertical-padding
148
+ ) *
149
+ -1
150
+ );
151
+
152
+ /* Render over scrolling content */
153
+ z-index: 10;
154
+ }