@recursica/adapter-mantine-v8 1.3.0 → 1.3.2
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 +26 -0
- package/dist/adapter-mantine-v8.cjs +2 -2
- package/dist/adapter-mantine-v8.cjs.map +1 -1
- package/dist/adapter-mantine-v8.css +1 -1
- package/dist/adapter-mantine-v8.js +1237 -1207
- package/dist/adapter-mantine-v8.js.map +1 -1
- package/package.json +4 -4
- package/src/components/Card/Card.stories.tsx +2 -1
- package/src/components/Checkbox/Checkbox.module.css +1 -3
- package/src/components/FileInput/FileInput.module.css +8 -0
- package/src/components/FileUpload/FileUpload.module.css +1 -0
- package/src/components/FileUpload/FileUpload.tsx +1 -0
- package/src/components/Menu/Menu.stories.tsx +0 -1
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +8 -0
- package/src/components/Panel/Panel.module.css +27 -2
- package/src/components/Panel/Panel.stories.tsx +4 -4
- package/src/components/Panel/Panel.tsx +10 -1
- package/src/components/Radio/Radio.module.css +2 -2
- package/src/components/SegmentedControl/SegmentedControl.module.css +26 -0
- package/src/components/Stepper/Stepper.module.css +15 -0
- package/src/components/Switch/Switch.module.css +5 -0
- package/src/components/Tabs/Tabs.module.css +5 -0
- package/src/components/Tabs/Tabs.tsx +1 -0
- package/src/components/Text/Text.module.css +6 -0
- package/src/components/Text/Text.stories.tsx +16 -17
- package/src/components/Text/Text.tsx +9 -0
- package/src/components/TimePicker/TimePicker.module.css +6 -22
- package/src/utils/typographyClass.ts +38 -0
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "git",
|
|
13
13
|
"url": "git+https://github.com/borderux/recursica-adapter-mantine-v8.git"
|
|
14
14
|
},
|
|
15
|
-
"version": "1.3.
|
|
15
|
+
"version": "1.3.2",
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
@@ -78,15 +78,15 @@
|
|
|
78
78
|
"release": "changeset publish"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@recursica/adapter-common": "^1.
|
|
81
|
+
"@recursica/adapter-common": "^1.2.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@changesets/cli": "^2.29.7",
|
|
85
85
|
"@eslint/js": "^9.25.0",
|
|
86
86
|
"@playwright/test": "^1.62.1",
|
|
87
|
-
"@recursica/adapter-tester": "^5.3.
|
|
87
|
+
"@recursica/adapter-tester": "^5.3.1",
|
|
88
88
|
"@recursica/recursica-postcss-vars": "^1.5.0",
|
|
89
|
-
"@recursica/storybook-template": "^0.7.
|
|
89
|
+
"@recursica/storybook-template": "^0.7.15",
|
|
90
90
|
"@recursica/token-analyzer": "^1.8.0",
|
|
91
91
|
"@storybook/addon-a11y": "^10.3.3",
|
|
92
92
|
"@storybook/addon-docs": "^10.3.3",
|
|
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
|
|
|
2
2
|
import { Card } from "./Card";
|
|
3
3
|
import { Button } from "../Button/Button";
|
|
4
4
|
import { Group } from "../Group/Group";
|
|
5
|
+
import { Heading } from "../Heading/Heading";
|
|
5
6
|
import { Text } from "../Text/Text";
|
|
6
7
|
|
|
7
8
|
const meta: Meta<typeof Card> = {
|
|
@@ -61,7 +62,7 @@ export const HeaderlessAndFooterless: Story = {
|
|
|
61
62
|
return (
|
|
62
63
|
<Card {...args}>
|
|
63
64
|
<Card.Content>
|
|
64
|
-
<
|
|
65
|
+
<Heading order={6}>Notice</Heading>
|
|
65
66
|
<Text>
|
|
66
67
|
This is a completely generic card payload dropping the Header and
|
|
67
68
|
Footer specific elements, simply acting as a padded elevation
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
/* SideBySide Variant */
|
|
22
22
|
.groupRoot[data-layout="side-by-side"] {
|
|
23
23
|
flex-direction: column;
|
|
24
|
-
flex-wrap:
|
|
24
|
+
flex-wrap: nowrap;
|
|
25
25
|
padding-top: var(
|
|
26
26
|
--recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_vertical-padding
|
|
27
27
|
);
|
|
@@ -259,8 +259,6 @@
|
|
|
259
259
|
);
|
|
260
260
|
}
|
|
261
261
|
.groupRoot[data-layout="side-by-side"] {
|
|
262
|
-
flex-direction: row;
|
|
263
|
-
flex-wrap: wrap;
|
|
264
262
|
gap: var(
|
|
265
263
|
--recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_gutter
|
|
266
264
|
);
|
|
@@ -58,6 +58,14 @@
|
|
|
58
58
|
min-height: var(
|
|
59
59
|
--recursica_ui-kit_components_file-input_properties_min-height
|
|
60
60
|
);
|
|
61
|
+
/* Fixed height (icon + padding + border) so the nested clear Button overflows the row instead of growing the control. */
|
|
62
|
+
height: calc(
|
|
63
|
+
var(--recursica_ui-kit_components_file-input_properties_icon-size) + 2 *
|
|
64
|
+
var(
|
|
65
|
+
--recursica_ui-kit_components_file-input_properties_vertical-padding
|
|
66
|
+
) +
|
|
67
|
+
2 * var(--recursica_ui-kit_components_file-input_properties_border-size)
|
|
68
|
+
);
|
|
61
69
|
gap: var(--recursica_ui-kit_components_file-input_properties_icon-text-gap);
|
|
62
70
|
padding-top: var(
|
|
63
71
|
--recursica_ui-kit_components_file-input_properties_vertical-padding
|
|
@@ -267,7 +267,6 @@ export const WithDisabledItems: Story = {
|
|
|
267
267
|
export const WithSubmenus: Story = {
|
|
268
268
|
args: {
|
|
269
269
|
position: "bottom-start",
|
|
270
|
-
width: 200,
|
|
271
270
|
// Rendered open by default so this story is diffable against the MUI adapter
|
|
272
271
|
// without an interaction step.
|
|
273
272
|
opened: true,
|
|
@@ -114,3 +114,11 @@ clips descenders (e.g. the "g" in a long title) whenever `text_line-height` is t
|
|
|
114
114
|
font's natural ascent+descent. Switched to `overflow: clip; overflow-clip-margin: 0.35em;` — same
|
|
115
115
|
truncation, but ink can bleed slightly past the line box before it's actually clipped.
|
|
116
116
|
Project-wide fix; see Chip's `CHIP_IMPLEMENTATION_NOTES.md` for the original discovery.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 9. Placement-aware border radius (Matt Massey, 2026-10-05)
|
|
121
|
+
|
|
122
|
+
**Decision:** The `border-radius` token is applied only to the corners on the edge that faces the page, not all four.
|
|
123
|
+
|
|
124
|
+
**Implementation:** A panel with `placement="right"` (the default) rounds its upper-left and lower-left corners only; its right corners are square against the screen edge. A panel with `placement="left"` rounds its upper-right and lower-right corners only. `Panel.tsx` adds `.contentRight` / `.contentLeft` to the `content` class, and those zero the two outer corners. `top` and `bottom` placements are unchanged (all four corners rounded).
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/* HARDCODED VALUES:
|
|
2
2
|
- border-style: solid: Structural rendering rule for the panel border.
|
|
3
|
-
- .
|
|
3
|
+
- .contentTop {
|
|
4
|
+
border-top-left-radius: 0;
|
|
5
|
+
border-top-right-radius: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.contentBottom {
|
|
9
|
+
border-bottom-left-radius: 0;
|
|
10
|
+
border-bottom-right-radius: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.inner { left: 0 }: Prevents the fixed Drawer portal from being pushed off-screen in centered flex environments (like Storybook Canvas).
|
|
4
14
|
- flexbox layout on .content & .body: Required to create a vertical flex context that pushes the footer to the bottom.
|
|
5
15
|
- 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
16
|
- z-index: 10 on .footer: Ensures the footer covers the body content as it scrolls underneath.
|
|
@@ -24,13 +34,28 @@
|
|
|
24
34
|
);
|
|
25
35
|
box-shadow: var(--recursica_ui-kit_components_panel_properties_elevation);
|
|
26
36
|
min-width: var(--recursica_ui-kit_components_panel_properties_min-width);
|
|
27
|
-
max-width:
|
|
37
|
+
max-width: min(
|
|
38
|
+
var(--recursica_ui-kit_components_panel_properties_max-width),
|
|
39
|
+
100vw
|
|
40
|
+
);
|
|
28
41
|
|
|
29
42
|
display: flex;
|
|
30
43
|
flex-direction: column;
|
|
31
44
|
height: 100%;
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
/* Round only the edge facing the page: a panel sliding in from the right rounds
|
|
48
|
+
its left corners; from the left, its right corners. */
|
|
49
|
+
.contentRight {
|
|
50
|
+
border-top-right-radius: 0;
|
|
51
|
+
border-bottom-right-radius: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.contentLeft {
|
|
55
|
+
border-top-left-radius: 0;
|
|
56
|
+
border-bottom-left-radius: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
.inner {
|
|
35
60
|
/* Forces the Drawer Inner container to the left edge, preventing it from inheriting
|
|
36
61
|
a centered static position in flex environments like Storybook's Canvas wrapper */
|
|
@@ -86,7 +86,7 @@ export const Default: Story = {
|
|
|
86
86
|
},
|
|
87
87
|
render: ({ wrapHeaderText, ...args }: PanelStoryArgs) => {
|
|
88
88
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
89
|
-
const [opened, setOpened] = useState(
|
|
89
|
+
const [opened, setOpened] = useState(true);
|
|
90
90
|
|
|
91
91
|
return (
|
|
92
92
|
<>
|
|
@@ -129,7 +129,7 @@ export const LeftPlacement: Story = {
|
|
|
129
129
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
130
130
|
render: ({ withLayer, layer, ...args }: PanelStoryArgs) => {
|
|
131
131
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
132
|
-
const [opened, setOpened] = useState(
|
|
132
|
+
const [opened, setOpened] = useState(true);
|
|
133
133
|
|
|
134
134
|
return (
|
|
135
135
|
<>
|
|
@@ -162,7 +162,7 @@ export const ScrollableContent: Story = {
|
|
|
162
162
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
163
163
|
render: ({ withLayer, layer, ...args }: PanelStoryArgs) => {
|
|
164
164
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
165
|
-
const [opened, setOpened] = useState(
|
|
165
|
+
const [opened, setOpened] = useState(true);
|
|
166
166
|
|
|
167
167
|
return (
|
|
168
168
|
<>
|
|
@@ -204,7 +204,7 @@ export const LongTitle: Story = {
|
|
|
204
204
|
},
|
|
205
205
|
render: ({ ...args }: PanelStoryArgs) => {
|
|
206
206
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
207
|
-
const [opened, setOpened] = useState(
|
|
207
|
+
const [opened, setOpened] = useState(true);
|
|
208
208
|
|
|
209
209
|
return (
|
|
210
210
|
<>
|
|
@@ -75,7 +75,15 @@ const PanelBase = function Panel({
|
|
|
75
75
|
// Bind CSS module classes to Mantine's internal classNames API
|
|
76
76
|
const mergedClassNames = mergeClassNames(
|
|
77
77
|
{
|
|
78
|
-
content: styles.content
|
|
78
|
+
content: `${styles.content} ${
|
|
79
|
+
placement === "right"
|
|
80
|
+
? styles.contentRight
|
|
81
|
+
: placement === "left"
|
|
82
|
+
? styles.contentLeft
|
|
83
|
+
: placement === "top"
|
|
84
|
+
? styles.contentTop
|
|
85
|
+
: styles.contentBottom
|
|
86
|
+
}`,
|
|
79
87
|
header: styles.header,
|
|
80
88
|
title: wrapHeaderText ? styles.titleTruncate : styles.title,
|
|
81
89
|
body: styles.body,
|
|
@@ -87,6 +95,7 @@ const PanelBase = function Panel({
|
|
|
87
95
|
return (
|
|
88
96
|
<MantineDrawer
|
|
89
97
|
{...(sanitizedProps as unknown as MantineDrawerProps)}
|
|
98
|
+
size="var(--recursica_ui-kit_components_panel_properties_max-width)"
|
|
90
99
|
position={placement} /* Recursica default: right; Mantine default: left */
|
|
91
100
|
keepMounted={keepMounted}
|
|
92
101
|
closeOnClickOutside={withCallerOverride(
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
/* SideBySide Variant */
|
|
26
26
|
.groupRoot[data-layout="side-by-side"] {
|
|
27
|
-
flex-direction:
|
|
28
|
-
flex-wrap:
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
flex-wrap: nowrap;
|
|
29
29
|
gap: var(
|
|
30
30
|
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_gutter
|
|
31
31
|
);
|
|
@@ -133,6 +133,32 @@
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/* Vertical: Mantine's separator is a horizontal line across the item, so the divider token
|
|
137
|
+
sets its height; and the container radius is capped to stay concentric with the pill items. */
|
|
138
|
+
.root[data-orientation="vertical"] .control::before {
|
|
139
|
+
width: auto;
|
|
140
|
+
height: var(
|
|
141
|
+
--recursica_ui-kit_components_segmented-control_properties_divider-size
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.root[data-orientation="vertical"] {
|
|
146
|
+
border-radius: min(
|
|
147
|
+
var(
|
|
148
|
+
--recursica_ui-kit_components_segmented-control_properties_border-radius
|
|
149
|
+
),
|
|
150
|
+
calc(
|
|
151
|
+
var(
|
|
152
|
+
--recursica_ui-kit_components_segmented-control-item_properties_item_height
|
|
153
|
+
) /
|
|
154
|
+
2 +
|
|
155
|
+
var(
|
|
156
|
+
--recursica_ui-kit_components_segmented-control_properties_padding-vertical
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
136
162
|
/* Indicator (the moving selected block) */
|
|
137
163
|
.root .indicator {
|
|
138
164
|
background-color: var(
|
|
@@ -84,6 +84,21 @@
|
|
|
84
84
|
); /* Move gap inside the step so the separator line can stretch through it */
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/* Mantine core adds a min-height, step-body inline margin and description margins on top of the
|
|
88
|
+
step-gap / indicator-label-gap tokens; zero them so spacing comes only from Recursica tokens. */
|
|
89
|
+
.root.vertical .step {
|
|
90
|
+
min-height: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.root.vertical .stepBody {
|
|
94
|
+
margin-inline: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.root.vertical .stepDescription {
|
|
98
|
+
margin-top: 0;
|
|
99
|
+
margin-bottom: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
.root.vertical .step:last-of-type {
|
|
88
103
|
padding-bottom: 0;
|
|
89
104
|
}
|
|
@@ -185,6 +185,11 @@
|
|
|
185
185
|
cursor: inherit;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/* Mantine's InlineInput adds label-offset padding on top of the label-gap token. */
|
|
189
|
+
.root .label {
|
|
190
|
+
padding-inline: 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
188
193
|
.root:has(input:disabled) .label {
|
|
189
194
|
color: var(
|
|
190
195
|
--recursica_ui-kit_components_switch-item_properties_colors_disabled-text
|
|
@@ -265,6 +265,11 @@
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
/* Icon support */
|
|
268
|
+
/* Mantine core adds an xs margin between the section and label on top of the element-gap token. */
|
|
269
|
+
.root .tabSection {
|
|
270
|
+
margin-inline: 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
268
273
|
.root .tab svg {
|
|
269
274
|
width: var(--tabs-icon-size);
|
|
270
275
|
height: var(--tabs-icon-size);
|
|
@@ -37,6 +37,7 @@ const _Tabs = forwardRef<HTMLDivElement, TabsProps>(function Tabs(props, ref) {
|
|
|
37
37
|
{
|
|
38
38
|
list: styles.list,
|
|
39
39
|
tab: styles.tab,
|
|
40
|
+
tabSection: styles.tabSection,
|
|
40
41
|
panel: styles.panel,
|
|
41
42
|
},
|
|
42
43
|
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
@@ -50,3 +50,9 @@
|
|
|
50
50
|
.root[data-emphasis="low"] {
|
|
51
51
|
opacity: var(--recursica_brand_text-emphasis_low);
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/* Dev only (the attribute is never set in production): hides text whose typography variant has no
|
|
55
|
+
exported style, so a typo is obvious instead of rendering with the UI kit's defaults. */
|
|
56
|
+
.root[data-typography-missing] {
|
|
57
|
+
visibility: hidden;
|
|
58
|
+
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { Text } from "./Text";
|
|
3
|
+
import brand from "../../../recursica_brand.json";
|
|
4
|
+
|
|
5
|
+
const BUILT_IN_VARIANTS = ["body", "caption", "overline"];
|
|
6
|
+
|
|
7
|
+
// Custom styles defined under `brand.typography` (beyond h1-h6 and the built-ins above); each has
|
|
8
|
+
// an exported `recursica_brand_typography_<name>` class, so `Text` renders it by name.
|
|
9
|
+
// Sorted so the render order is stable across exports (the visual diff depends on it).
|
|
10
|
+
const customVariants = Object.keys(brand.brand.typography)
|
|
11
|
+
.filter((name) => !/^h[1-6]$/.test(name) && !BUILT_IN_VARIANTS.includes(name))
|
|
12
|
+
.sort();
|
|
3
13
|
|
|
4
14
|
const meta: Meta<typeof Text> = {
|
|
5
15
|
title: "UI-Kit/Text",
|
|
@@ -16,14 +26,7 @@ const meta: Meta<typeof Text> = {
|
|
|
16
26
|
argTypes: {
|
|
17
27
|
variant: {
|
|
18
28
|
control: "select",
|
|
19
|
-
options: [
|
|
20
|
-
"body",
|
|
21
|
-
"body-small",
|
|
22
|
-
"caption",
|
|
23
|
-
"overline",
|
|
24
|
-
"subtitle",
|
|
25
|
-
"subtitle-small",
|
|
26
|
-
],
|
|
29
|
+
options: [...BUILT_IN_VARIANTS, ...customVariants],
|
|
27
30
|
description:
|
|
28
31
|
"Controls the standard logical boundary definitions natively extracted from Figma.",
|
|
29
32
|
},
|
|
@@ -62,21 +65,17 @@ export const StaticVariations: Story = {
|
|
|
62
65
|
<Text variant="body">
|
|
63
66
|
Body (Base paragraph and generic information flow)
|
|
64
67
|
</Text>
|
|
65
|
-
<Text variant="body-small">
|
|
66
|
-
Body Small (Compacted list items and helper blocks)
|
|
67
|
-
</Text>
|
|
68
68
|
<Text variant="caption">
|
|
69
69
|
Caption (Data table descriptions or micro-labels)
|
|
70
70
|
</Text>
|
|
71
71
|
<Text variant="overline">
|
|
72
72
|
Overline (Card contextual pre-headers and categorical tags)
|
|
73
73
|
</Text>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</Text>
|
|
74
|
+
{customVariants.map((name) => (
|
|
75
|
+
<Text key={name} variant={name}>
|
|
76
|
+
{name} (Custom style from brand.typography)
|
|
77
|
+
</Text>
|
|
78
|
+
))}
|
|
80
79
|
</div>
|
|
81
80
|
),
|
|
82
81
|
};
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../utils/filterStylingProps";
|
|
11
11
|
|
|
12
12
|
import { type RecursicaTextProps } from "@recursica/adapter-common";
|
|
13
|
+
import { isTypographyStyleDefined } from "../../utils/typographyClass";
|
|
13
14
|
import styles from "./Text.module.css";
|
|
14
15
|
|
|
15
16
|
export type TextProps = RecursicaOverStyled<
|
|
@@ -31,6 +32,13 @@ const _Text = forwardRef<HTMLDivElement, TextProps>(function Text(
|
|
|
31
32
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
32
33
|
.className as string | undefined;
|
|
33
34
|
|
|
35
|
+
// Dev only: an unknown variant hides the text (and logs) instead of silently rendering with the
|
|
36
|
+
// UI kit's default styling.
|
|
37
|
+
const typographyMissing =
|
|
38
|
+
process.env.NODE_ENV !== "production" &&
|
|
39
|
+
typeof document !== "undefined" &&
|
|
40
|
+
!isTypographyStyleDefined(variant);
|
|
41
|
+
|
|
34
42
|
const typographyClass = `recursica_brand_typography_${variant}`;
|
|
35
43
|
const mergedClassName = [typographyClass, styles.root, classNameProp]
|
|
36
44
|
.filter(Boolean)
|
|
@@ -45,6 +53,7 @@ const _Text = forwardRef<HTMLDivElement, TextProps>(function Text(
|
|
|
45
53
|
{...(sanitizedProps as unknown as MantineTextProps)}
|
|
46
54
|
data-color={color}
|
|
47
55
|
data-emphasis={emphasis}
|
|
56
|
+
data-typography-missing={typographyMissing || undefined}
|
|
48
57
|
className={mergedClassName}
|
|
49
58
|
/>
|
|
50
59
|
);
|
|
@@ -58,10 +58,7 @@
|
|
|
58
58
|
gap: var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap);
|
|
59
59
|
position: relative;
|
|
60
60
|
box-sizing: border-box;
|
|
61
|
-
|
|
62
|
-
text-field's identical token shape); this is now only the time-entry half of the composite. */
|
|
63
|
-
min-width: var(--recursica_ui-kit_components_time-picker_properties_width);
|
|
64
|
-
width: fit-content;
|
|
61
|
+
width: var(--recursica_ui-kit_components_time-picker_properties_width);
|
|
65
62
|
/* time-picker has no dedicated min-height token (unlike text-field/date-picker); this global is
|
|
66
63
|
the one text-field's own min-height token resolves to, so both controls land on the same 48px
|
|
67
64
|
height without binding to another component's own namespace. See
|
|
@@ -209,27 +206,14 @@
|
|
|
209
206
|
this only controls its size/alignment within the flex row. */
|
|
210
207
|
.amPmSelect {
|
|
211
208
|
flex: 0 0 auto;
|
|
212
|
-
width:
|
|
213
|
-
min-width: 5.5rem;
|
|
209
|
+
width: var(--recursica_ui-kit_components_time-picker_properties_width);
|
|
214
210
|
}
|
|
215
211
|
|
|
216
|
-
/*
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
value text, so it doesn't naturally shrink to "AM"/"PM" either way. Give it an explicit small
|
|
220
|
-
width sized for a couple of characters instead — the wrapper (.amPmSelect, whose own rendered
|
|
221
|
-
width just follows this input, since Dropdown.module.css's .section icon is positioned
|
|
222
|
-
absolutely and doesn't add to flex flow) ends up hugging the content as a result. Target
|
|
223
|
-
Mantine's own stable global class (not the Dropdown.module.css hash, which this module can't
|
|
224
|
-
reference directly). See TIMEPICKER_IMPLEMENTATION_NOTES.md.
|
|
225
|
-
Width math (box-sizing: border-box, so padding eats into this number): Dropdown's own
|
|
226
|
-
horizontal-padding (16px) on the left + the chevron's reserved right-section padding
|
|
227
|
-
(horizontal-padding + icon-size + icon-text-gap = 16+24+8 = 48px) on the right leaves very
|
|
228
|
-
little room for the "AM"/"PM" text itself — 2.5rem (40px) put the padding in a negative content
|
|
229
|
-
box, which is why the value was invisible and the chevron looked jammed against the border. 6rem
|
|
230
|
-
leaves a real ~32px content area. */
|
|
212
|
+
/* Fill .amPmSelect, which carries the time-picker width token (Dropdown's own .input is
|
|
213
|
+
otherwise a native input with a fixed UA-default intrinsic width). Target Mantine's stable
|
|
214
|
+
global class; this module can't reference Dropdown.module.css's hashed class. */
|
|
231
215
|
.amPmSelect :global(.mantine-Select-input) {
|
|
232
|
-
width:
|
|
216
|
+
width: 100%;
|
|
233
217
|
}
|
|
234
218
|
|
|
235
219
|
/* -------------------------------------
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const found = new Set<string>();
|
|
2
|
+
const reported = new Set<string>();
|
|
3
|
+
|
|
4
|
+
function ruleListDefines(rules: CSSRuleList, selector: string): boolean {
|
|
5
|
+
return Array.from(rules).some((rule) => {
|
|
6
|
+
if (rule instanceof CSSStyleRule) {
|
|
7
|
+
return rule.selectorText.split(",").some((s) => s.trim() === selector);
|
|
8
|
+
}
|
|
9
|
+
const nested = (rule as CSSGroupingRule).cssRules;
|
|
10
|
+
return nested ? ruleListDefines(nested, selector) : false;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Dev-only: whether a `.recursica_brand_typography_<name>` rule exists in any readable stylesheet.
|
|
16
|
+
* Logs a console error once per missing name. Cross-origin stylesheets can't be read and are
|
|
17
|
+
* skipped, so they never count as defining the class.
|
|
18
|
+
*/
|
|
19
|
+
export function isTypographyStyleDefined(name: string): boolean {
|
|
20
|
+
const selector = `.recursica_brand_typography_${name}`;
|
|
21
|
+
if (found.has(selector)) return true;
|
|
22
|
+
const defined = Array.from(document.styleSheets).some((sheet) => {
|
|
23
|
+
try {
|
|
24
|
+
return ruleListDefines(sheet.cssRules, selector);
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (defined) {
|
|
30
|
+
found.add(selector);
|
|
31
|
+
} else if (!reported.has(selector)) {
|
|
32
|
+
reported.add(selector);
|
|
33
|
+
console.error(
|
|
34
|
+
`Text: no "${selector}" rule found. "${name}" is not defined under brand.typography in the exported Recursica CSS, so the text is hidden.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return defined;
|
|
38
|
+
}
|