@recursica/adapter-mantine-v8 1.3.1 → 1.3.3
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 +21 -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 +1117 -1086
- package/dist/adapter-mantine-v8.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/package.json +2 -2
- package/src/components/Card/Card.stories.tsx +2 -1
- package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +9 -0
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +8 -0
- package/src/components/Panel/Panel.stories.tsx +0 -8
- package/src/components/Panel/Panel.tsx +23 -6
- package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +10 -0
- package/src/components/Text/Text.headings.dom.test.tsx +51 -0
- package/src/components/Text/Text.module.css +6 -0
- package/src/components/Text/Text.stories.tsx +41 -17
- package/src/components/Text/Text.tsx +18 -0
- package/src/components/TimePicker/TimePicker.module.css +6 -22
- package/src/utils/typographyClass.ts +38 -0
package/dist/index.d.ts
CHANGED
|
@@ -1035,6 +1035,10 @@ export declare const Panel: PanelComponent;
|
|
|
1035
1035
|
* </Panel>
|
|
1036
1036
|
* ```
|
|
1037
1037
|
*
|
|
1038
|
+
* Always non-modal, with no props to change it: the page behind stays usable
|
|
1039
|
+
* (no overlay, focus trap or scroll lock), focus returns to the opener on close,
|
|
1040
|
+
* Escape always closes, and outside clicks never do.
|
|
1041
|
+
*
|
|
1038
1042
|
* Mantine Drawer sub-components available via dot-notation:
|
|
1039
1043
|
* - `Panel.Header` — Top section with title and close button
|
|
1040
1044
|
* - `Panel.Title` — Title text within the header
|
|
@@ -1240,7 +1244,7 @@ export declare interface RecursicaNumberInputProps extends Omit<NumberInputProps
|
|
|
1240
1244
|
/**
|
|
1241
1245
|
* Recursica Panel root props. Extends Mantine Drawer.
|
|
1242
1246
|
*/
|
|
1243
|
-
export declare interface RecursicaPanelProps extends Omit<DrawerProps, "size" | "styles" | "classNames" | "style" | "position" | "opened">, RecursicaPanelProps_2 {
|
|
1247
|
+
export declare interface RecursicaPanelProps extends Omit<DrawerProps, "size" | "styles" | "classNames" | "style" | "position" | "opened" | "withOverlay" | "trapFocus" | "lockScroll" | "returnFocus" | "closeOnEscape" | "closeOnClickOutside">, RecursicaPanelProps_2 {
|
|
1244
1248
|
}
|
|
1245
1249
|
|
|
1246
1250
|
export declare interface RecursicaRadioGroupProps extends Omit<RadioGroupProps_2, "size" | "labelProps" | "defaultValue" | "value">, Omit<RecursicaFormControlWrapperProps, "controlMaxWidth" | "controlMinWidth">, ReadOnlyControlProps, RecursicaRadioGroupProps_2 {
|
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.3",
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
@@ -78,7 +78,7 @@
|
|
|
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",
|
|
@@ -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
|
|
@@ -16,3 +16,12 @@ line — headings are the primary intended use case for this property. Not a des
|
|
|
16
16
|
layout algorithm choice, so it's hardcoded rather than pulled from `recursica_variables_scoped.css`.
|
|
17
17
|
Chromium/Firefox only balance up to ~6 lines, which comfortably covers heading text. No fallback
|
|
18
18
|
needed — browsers that don't support the value just ignore the declaration.
|
|
19
|
+
|
|
20
|
+
## Heading owns semantic `h1`-`h6` (Matt Massey, 2026-10-08)
|
|
21
|
+
|
|
22
|
+
**Decision:** `Heading` is the only component that renders semantic `<h1>`-`<h6>` elements. Each
|
|
23
|
+
`order` maps to a heading level that Recursica and Forge define and style
|
|
24
|
+
(`recursica_brand_typography_h{order}`).
|
|
25
|
+
|
|
26
|
+
**Implementation:** Heading's styles are fixed by the Recursica JSON/CSS. Do not add variants to
|
|
27
|
+
Heading. Any other kind of text belongs in `Text`.
|
|
@@ -122,3 +122,11 @@ Project-wide fix; see Chip's `CHIP_IMPLEMENTATION_NOTES.md` for the original dis
|
|
|
122
122
|
**Decision:** The `border-radius` token is applied only to the corners on the edge that faces the page, not all four.
|
|
123
123
|
|
|
124
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).
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 10. Always non-modal (Matt Massey, 2026-10-08)
|
|
129
|
+
|
|
130
|
+
**Decision:** Panel is never modal, and there are no props to change that.
|
|
131
|
+
|
|
132
|
+
**Implementation:** The house rule is that the page behind a panel stays usable. `Panel.tsx` always passes `withOverlay={false}`, `trapFocus={false}`, `lockScroll={false}`, `returnFocus`, `closeOnEscape` and `closeOnClickOutside={false}` to Mantine's Drawer, after the caller's props, and omits those keys from `RecursicaPanelProps`. Escape is independent of outside clicks and always closes the panel; clicking the page behind never does. Every adapter must match: no overlay, no focus trap, no scroll lock, no `aria-modal`, no `aria-hidden` on the rest of the page.
|
|
@@ -21,10 +21,6 @@ const meta: Meta = {
|
|
|
21
21
|
control: "text",
|
|
22
22
|
description: "Panel title displayed in the header.",
|
|
23
23
|
},
|
|
24
|
-
withOverlay: {
|
|
25
|
-
control: "boolean",
|
|
26
|
-
description: "Whether to display a background overlay.",
|
|
27
|
-
},
|
|
28
24
|
withCloseButton: {
|
|
29
25
|
control: "boolean",
|
|
30
26
|
description: "Whether to display the close button in the header.",
|
|
@@ -80,7 +76,6 @@ export const Default: Story = {
|
|
|
80
76
|
args: {
|
|
81
77
|
placement: "right",
|
|
82
78
|
title: "Panel Title",
|
|
83
|
-
withOverlay: true,
|
|
84
79
|
withCloseButton: true,
|
|
85
80
|
wrapHeaderText: false,
|
|
86
81
|
},
|
|
@@ -122,7 +117,6 @@ export const LeftPlacement: Story = {
|
|
|
122
117
|
args: {
|
|
123
118
|
placement: "left",
|
|
124
119
|
title: "Navigation",
|
|
125
|
-
withOverlay: true,
|
|
126
120
|
withCloseButton: true,
|
|
127
121
|
wrapHeaderText: false,
|
|
128
122
|
},
|
|
@@ -155,7 +149,6 @@ export const ScrollableContent: Story = {
|
|
|
155
149
|
args: {
|
|
156
150
|
placement: "right",
|
|
157
151
|
title: "Scrollable Panel",
|
|
158
|
-
withOverlay: true,
|
|
159
152
|
withCloseButton: true,
|
|
160
153
|
wrapHeaderText: false,
|
|
161
154
|
},
|
|
@@ -198,7 +191,6 @@ export const LongTitle: Story = {
|
|
|
198
191
|
placement: "right",
|
|
199
192
|
title:
|
|
200
193
|
"This is a ridiculously long panel title designed to test how the header CSS handles text overflow and whether it truncates correctly or breaks the layout",
|
|
201
|
-
withOverlay: true,
|
|
202
194
|
withCloseButton: true,
|
|
203
195
|
wrapHeaderText: true,
|
|
204
196
|
},
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
filterStylingProps,
|
|
15
15
|
mergeClassNames,
|
|
16
|
-
withCallerOverride,
|
|
17
16
|
type RecursicaOverStyled,
|
|
18
17
|
} from "../../utils/filterStylingProps";
|
|
19
18
|
import styles from "./Panel.module.css";
|
|
@@ -30,7 +29,18 @@ import { type RecursicaPanelProps as BaseRecursicaPanelProps } from "@recursica/
|
|
|
30
29
|
export interface RecursicaPanelProps
|
|
31
30
|
extends Omit<
|
|
32
31
|
MantineDrawerProps,
|
|
33
|
-
|
|
32
|
+
| "size"
|
|
33
|
+
| "styles"
|
|
34
|
+
| "classNames"
|
|
35
|
+
| "style"
|
|
36
|
+
| "position"
|
|
37
|
+
| "opened"
|
|
38
|
+
| "withOverlay"
|
|
39
|
+
| "trapFocus"
|
|
40
|
+
| "lockScroll"
|
|
41
|
+
| "returnFocus"
|
|
42
|
+
| "closeOnEscape"
|
|
43
|
+
| "closeOnClickOutside"
|
|
34
44
|
>,
|
|
35
45
|
BaseRecursicaPanelProps {}
|
|
36
46
|
|
|
@@ -52,6 +62,10 @@ export type PanelProps = RecursicaOverStyled<RecursicaPanelProps>;
|
|
|
52
62
|
* </Panel>
|
|
53
63
|
* ```
|
|
54
64
|
*
|
|
65
|
+
* Always non-modal, with no props to change it: the page behind stays usable
|
|
66
|
+
* (no overlay, focus trap or scroll lock), focus returns to the opener on close,
|
|
67
|
+
* Escape always closes, and outside clicks never do.
|
|
68
|
+
*
|
|
55
69
|
* Mantine Drawer sub-components available via dot-notation:
|
|
56
70
|
* - `Panel.Header` — Top section with title and close button
|
|
57
71
|
* - `Panel.Title` — Title text within the header
|
|
@@ -98,10 +112,13 @@ const PanelBase = function Panel({
|
|
|
98
112
|
size="var(--recursica_ui-kit_components_panel_properties_max-width)"
|
|
99
113
|
position={placement} /* Recursica default: right; Mantine default: left */
|
|
100
114
|
keepMounted={keepMounted}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
/* Always non-modal: the page behind stays usable, Escape always closes */
|
|
116
|
+
withOverlay={false}
|
|
117
|
+
trapFocus={false}
|
|
118
|
+
lockScroll={false}
|
|
119
|
+
returnFocus
|
|
120
|
+
closeOnEscape
|
|
121
|
+
closeOnClickOutside={false}
|
|
105
122
|
classNames={mergedClassNames}
|
|
106
123
|
/>
|
|
107
124
|
);
|
|
@@ -10,3 +10,13 @@ line. Not a design token — it's a layout algorithm choice, so it's hardcoded r
|
|
|
10
10
|
from `recursica_variables_scoped.css`. Chromium/Firefox only balance up to ~6 lines; longer
|
|
11
11
|
paragraphs silently fall back to normal wrapping past that point. No fallback needed — browsers
|
|
12
12
|
that don't support the value just ignore the declaration.
|
|
13
|
+
|
|
14
|
+
## Text must never render `h1`-`h6` (Matt Massey, 2026-10-08)
|
|
15
|
+
|
|
16
|
+
**Decision:** `Text` must never render an `<h1>`-`<h6>` element. It is not allowed. Semantic
|
|
17
|
+
headings use `Heading`.
|
|
18
|
+
|
|
19
|
+
**Implementation:** `Text` covers every other kind of text in the system (body, labels, captions,
|
|
20
|
+
etc.). Its variants can extend what the Recursica JSON and CSS define, so a variant need not
|
|
21
|
+
exist in the Recursica tokens. Heading levels are the one thing `Text` can't be: they have a
|
|
22
|
+
fixed definition and styling from Recursica and Forge, owned by `Heading`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { describe, it, expect, vi } from "vitest";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { flushSync } from "react-dom";
|
|
5
|
+
import { MantineProvider } from "@mantine/core";
|
|
6
|
+
import { Text } from "./Text";
|
|
7
|
+
|
|
8
|
+
class Boundary extends React.Component<
|
|
9
|
+
{ children: React.ReactNode },
|
|
10
|
+
{ message: string | null }
|
|
11
|
+
> {
|
|
12
|
+
state = { message: null as string | null };
|
|
13
|
+
static getDerivedStateFromError(error: Error) {
|
|
14
|
+
return { message: error.message };
|
|
15
|
+
}
|
|
16
|
+
render() {
|
|
17
|
+
return this.state.message ?? this.props.children;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function render(component: string): HTMLElement {
|
|
22
|
+
const container = document.createElement("div");
|
|
23
|
+
document.body.appendChild(container);
|
|
24
|
+
const props = { component } as Record<string, unknown>;
|
|
25
|
+
flushSync(() =>
|
|
26
|
+
createRoot(container).render(
|
|
27
|
+
<MantineProvider>
|
|
28
|
+
<Boundary>
|
|
29
|
+
<Text {...(props as object)}>Content</Text>
|
|
30
|
+
</Boundary>
|
|
31
|
+
</MantineProvider>,
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
return container;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("Text heading elements", () => {
|
|
38
|
+
it.each(["h1", "h2", "h3", "h4", "h5", "h6"])(
|
|
39
|
+
"throws when component is %s",
|
|
40
|
+
(component) => {
|
|
41
|
+
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
42
|
+
expect(render(component).textContent).toContain(
|
|
43
|
+
`Text cannot render <${component}>. Use <Heading> for semantic h1-h6.`,
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
it.each(["p", "span", "label"])("renders component=%s", (component) => {
|
|
49
|
+
expect(render(component).querySelector(component)).not.toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -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
|
};
|
|
@@ -122,3 +121,28 @@ export const Emphasis: Story = {
|
|
|
122
121
|
</div>
|
|
123
122
|
),
|
|
124
123
|
};
|
|
124
|
+
|
|
125
|
+
export const AsElement: Story = {
|
|
126
|
+
args: {},
|
|
127
|
+
parameters: {
|
|
128
|
+
docs: {
|
|
129
|
+
description: {
|
|
130
|
+
story:
|
|
131
|
+
"`component` renders Text as an inline `span`, a `label` or a `div`. `h1` to `h6` throw; use `<Heading>`.",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
render: () => (
|
|
136
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
137
|
+
<Text>Default is a block paragraph.</Text>
|
|
138
|
+
<div>
|
|
139
|
+
Inline text:{" "}
|
|
140
|
+
<Text component="span" emphasis="low">
|
|
141
|
+
a span inside a line
|
|
142
|
+
</Text>
|
|
143
|
+
.
|
|
144
|
+
</div>
|
|
145
|
+
<Text component="label">A label</Text>
|
|
146
|
+
</div>
|
|
147
|
+
),
|
|
148
|
+
};
|
|
@@ -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<
|
|
@@ -17,6 +18,8 @@ export type TextProps = RecursicaOverStyled<
|
|
|
17
18
|
"color"
|
|
18
19
|
>;
|
|
19
20
|
|
|
21
|
+
const HEADING_ELEMENTS = ["h1", "h2", "h3", "h4", "h5", "h6"];
|
|
22
|
+
|
|
20
23
|
const _Text = forwardRef<HTMLDivElement, TextProps>(function Text(
|
|
21
24
|
{
|
|
22
25
|
overStyled = false,
|
|
@@ -27,10 +30,24 @@ const _Text = forwardRef<HTMLDivElement, TextProps>(function Text(
|
|
|
27
30
|
},
|
|
28
31
|
ref,
|
|
29
32
|
) {
|
|
33
|
+
const component = (rest as { component?: unknown }).component;
|
|
34
|
+
if (typeof component === "string" && HEADING_ELEMENTS.includes(component)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Text cannot render <${component}>. Use <Heading> for semantic h1-h6.`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
31
41
|
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
32
42
|
.className as string | undefined;
|
|
33
43
|
|
|
44
|
+
// Dev only: an unknown variant hides the text (and logs) instead of silently rendering with the
|
|
45
|
+
// UI kit's default styling.
|
|
46
|
+
const typographyMissing =
|
|
47
|
+
process.env.NODE_ENV !== "production" &&
|
|
48
|
+
typeof document !== "undefined" &&
|
|
49
|
+
!isTypographyStyleDefined(variant);
|
|
50
|
+
|
|
34
51
|
const typographyClass = `recursica_brand_typography_${variant}`;
|
|
35
52
|
const mergedClassName = [typographyClass, styles.root, classNameProp]
|
|
36
53
|
.filter(Boolean)
|
|
@@ -45,6 +62,7 @@ const _Text = forwardRef<HTMLDivElement, TextProps>(function Text(
|
|
|
45
62
|
{...(sanitizedProps as unknown as MantineTextProps)}
|
|
46
63
|
data-color={color}
|
|
47
64
|
data-emphasis={emphasis}
|
|
65
|
+
data-typography-missing={typographyMissing || undefined}
|
|
48
66
|
className={mergedClassName}
|
|
49
67
|
/>
|
|
50
68
|
);
|
|
@@ -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
|
+
}
|