@recursica/adapter-mantine-v8 1.3.1 → 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/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.1",
15
+ "version": "1.3.2",
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.1.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
- <Text variant="subtitle">Notice</Text>
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
@@ -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
- <Text variant="subtitle">
75
- Subtitle (Minor sub-headers avoiding heavy display weights)
76
- </Text>
77
- <Text variant="subtitle-small">
78
- Subtitle Small (Section anchors deep in hierarchy)
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
- /* min-width, not width: the `width` token was authored for a single plain input (confirmed against
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: auto;
213
- min-width: 5.5rem;
209
+ width: var(--recursica_ui-kit_components_time-picker_properties_width);
214
210
  }
215
211
 
216
- /* Dropdown.module.css's own .input sets width: 100% (correct for a standalone Dropdown filling
217
- its own column) — resetting that to `auto` still isn't enough here: a native <input>'s intrinsic
218
- ("auto"/"fit-content") width is a fixed UA default (~20 characters wide), not based on its actual
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: 6rem;
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
+ }