@medalsocial/meda 2.3.0 → 2.4.1

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.
@@ -0,0 +1,15 @@
1
+ import type { ComponentPropsWithoutRef } from 'react';
2
+ export type CardProps = ComponentPropsWithoutRef<'div'>;
3
+ export type CardHeaderProps = ComponentPropsWithoutRef<'div'>;
4
+ export type CardBodyProps = ComponentPropsWithoutRef<'div'>;
5
+ export type CardFooterProps = ComponentPropsWithoutRef<'div'>;
6
+ declare function CardRoot({ children, className, ...props }: CardProps): import("react/jsx-runtime").JSX.Element;
7
+ declare function CardHeader({ children, className, ...props }: CardHeaderProps): import("react/jsx-runtime").JSX.Element;
8
+ declare function CardBody({ children, className, ...props }: CardBodyProps): import("react/jsx-runtime").JSX.Element;
9
+ declare function CardFooter({ children, className, ...props }: CardFooterProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare const Card: typeof CardRoot & {
11
+ Header: typeof CardHeader;
12
+ Body: typeof CardBody;
13
+ Footer: typeof CardFooter;
14
+ };
15
+ export {};
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../lib/utils.js';
4
+ function CardRoot({ children, className, ...props }) {
5
+ return (_jsx("div", { "data-slot": "card", className: cn('flex flex-col rounded-xl border border-border bg-card text-card-foreground', className), ...props, children: children }));
6
+ }
7
+ function CardHeader({ children, className, ...props }) {
8
+ return (_jsx("div", { "data-slot": "card-header", className: cn('flex flex-col gap-1 border-b border-border px-4 py-3 last:border-b-0', className), ...props, children: children }));
9
+ }
10
+ function CardBody({ children, className, ...props }) {
11
+ return (_jsx("div", { "data-slot": "card-body", className: cn('px-4 py-3', className), ...props, children: children }));
12
+ }
13
+ function CardFooter({ children, className, ...props }) {
14
+ return (_jsx("div", { "data-slot": "card-footer", className: cn('flex items-center justify-end gap-2 border-t border-border px-4 py-3 first:border-t-0', className), ...props, children: children }));
15
+ }
16
+ export const Card = Object.assign(CardRoot, {
17
+ Header: CardHeader,
18
+ Body: CardBody,
19
+ Footer: CardFooter,
20
+ });
@@ -1,6 +1,10 @@
1
+ export type { CardBodyProps, CardFooterProps, CardHeaderProps, CardProps, } from './card.js';
2
+ export { Card } from './card.js';
1
3
  export type { EmptyStateProps, EmptyStateVariant } from './empty-state.js';
2
4
  export { EmptyState } from './empty-state.js';
3
5
  export type { FilterRailGroupProps, FilterRailProps } from './filter-rail.js';
4
6
  export { FilterRail } from './filter-rail.js';
5
7
  export type { SkeletonProps } from './skeleton.js';
6
8
  export { Skeleton } from './skeleton.js';
9
+ export type { StatusPillProps, StatusPillSize, StatusPillTone } from './status-pill.js';
10
+ export { StatusPill } from './status-pill.js';
@@ -1,3 +1,5 @@
1
+ export { Card } from './card.js';
1
2
  export { EmptyState } from './empty-state.js';
2
3
  export { FilterRail } from './filter-rail.js';
3
4
  export { Skeleton } from './skeleton.js';
5
+ export { StatusPill } from './status-pill.js';
@@ -0,0 +1,2 @@
1
+ export type { MarkdownViewProps } from './markdown-view.js';
2
+ export { MarkdownView } from './markdown-view.js';
@@ -0,0 +1,7 @@
1
+ // Dedicated subpath entry for MarkdownView.
2
+ //
3
+ // Importing `@medalsocial/meda/markdown-view` opts the consumer into the
4
+ // react-markdown / remark-gfm / rehype-highlight peer chain. Consumers who
5
+ // don't need markdown rendering should import from `@medalsocial/meda` /
6
+ // `@medalsocial/meda/primitives` instead — neither pulls these peers in.
7
+ export { MarkdownView } from './markdown-view.js';
@@ -0,0 +1,22 @@
1
+ import type { ComponentPropsWithoutRef } from 'react';
2
+ import { type Components } from 'react-markdown';
3
+ export interface MarkdownViewProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
4
+ children: string;
5
+ /**
6
+ * Override or extend the default react-markdown component map.
7
+ * Spread defaults are applied first, so caller overrides win.
8
+ */
9
+ components?: Components;
10
+ }
11
+ /**
12
+ * MarkdownView wraps `react-markdown` with `remark-gfm` and `rehype-highlight`,
13
+ * and applies a consistent prose stylesheet using meda design tokens.
14
+ *
15
+ * The markdown libraries are listed as **optional peer dependencies**. Install
16
+ * them in your app if you import this component:
17
+ *
18
+ * ```bash
19
+ * pnpm add react-markdown remark-gfm rehype-highlight
20
+ * ```
21
+ */
22
+ export declare function MarkdownView({ children, className, components, ...props }: MarkdownViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import ReactMarkdown from 'react-markdown';
4
+ import rehypeHighlight from 'rehype-highlight';
5
+ import remarkGfm from 'remark-gfm';
6
+ import { cn } from '../lib/utils.js';
7
+ // remark-gfm renders task-list `[ ]` / `[x]` as unlabelled disabled checkboxes,
8
+ // which fails the axe `label` rule. Override the `input` renderer to add an
9
+ // aria-label derived from the checked state — keeps the checkbox semantics
10
+ // for AT users while satisfying the contrast/label gate.
11
+ const DEFAULT_COMPONENTS = {
12
+ input: ({ type, checked, ...rest }) => {
13
+ if (type === 'checkbox') {
14
+ return (_jsx("input", { type: "checkbox", checked: checked, "aria-label": checked ? 'task complete' : 'task incomplete', ...rest }));
15
+ }
16
+ return _jsx("input", { type: type, ...rest });
17
+ },
18
+ };
19
+ /**
20
+ * MarkdownView wraps `react-markdown` with `remark-gfm` and `rehype-highlight`,
21
+ * and applies a consistent prose stylesheet using meda design tokens.
22
+ *
23
+ * The markdown libraries are listed as **optional peer dependencies**. Install
24
+ * them in your app if you import this component:
25
+ *
26
+ * ```bash
27
+ * pnpm add react-markdown remark-gfm rehype-highlight
28
+ * ```
29
+ */
30
+ export function MarkdownView({ children, className, components, ...props }) {
31
+ return (_jsx("div", { "data-slot": "markdown-view", className: cn(
32
+ // Base prose
33
+ 'text-sm leading-relaxed text-foreground',
34
+ // Trim outer block margins: only the first/last *child* of the
35
+ // wrapper should lose its top/bottom margin. The earlier shape
36
+ // `first:[&_h1]:mt-0` selects all descendant h1s when the wrapper
37
+ // itself is :first-child of its parent — which then collapses the
38
+ // margin on every nested h1, not just the leading one.
39
+ '[&>*:first-child]:mt-0 [&>*:last-child]:mb-0',
40
+ // Headings
41
+ '[&_h1]:mt-6 [&_h1]:mb-3 [&_h1]:text-xl [&_h1]:font-semibold [&_h1]:text-foreground', '[&_h2]:mt-5 [&_h2]:mb-2 [&_h2]:text-lg [&_h2]:font-semibold [&_h2]:text-foreground', '[&_h3]:mt-4 [&_h3]:mb-2 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:text-foreground', '[&_h4]:mt-3 [&_h4]:mb-1.5 [&_h4]:text-sm [&_h4]:font-semibold [&_h4]:text-foreground',
42
+ // Paragraphs and inline
43
+ '[&_p]:my-3', '[&_strong]:font-semibold [&_strong]:text-foreground', '[&_em]:italic', '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2 hover:[&_a]:no-underline',
44
+ // Lists
45
+ '[&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:space-y-1', '[&_ol]:my-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:space-y-1', '[&_li]:text-foreground',
46
+ // Blockquote
47
+ '[&_blockquote]:my-3 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',
48
+ // Code
49
+ '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.875em] [&_code]:text-foreground', '[&_pre]:my-3 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-3', '[&_pre_code]:bg-transparent [&_pre_code]:p-0',
50
+ // Tables (GFM)
51
+ '[&_table]:my-3 [&_table]:w-full [&_table]:border-collapse [&_table]:text-sm', '[&_th]:border [&_th]:border-border [&_th]:bg-muted [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold', '[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',
52
+ // Horizontal rule
53
+ '[&_hr]:my-6 [&_hr]:border-border',
54
+ // Images
55
+ '[&_img]:my-3 [&_img]:rounded-md [&_img]:border [&_img]:border-border', className), ...props, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeHighlight], components: { ...DEFAULT_COMPONENTS, ...components }, children: children }) }));
56
+ }
@@ -0,0 +1,12 @@
1
+ import type { ComponentPropsWithoutRef } from 'react';
2
+ export type StatusPillTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
3
+ export type StatusPillSize = 'sm' | 'md';
4
+ export interface StatusPillProps extends ComponentPropsWithoutRef<'span'> {
5
+ tone?: StatusPillTone;
6
+ size?: StatusPillSize;
7
+ /**
8
+ * Show a leading dot. Defaults to true; pass `false` to hide.
9
+ */
10
+ dot?: boolean;
11
+ }
12
+ export declare function StatusPill({ tone, size, dot, className, children, ...props }: StatusPillProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '../lib/utils.js';
4
+ // Tone classes use solid backgrounds + their semantic foreground tokens.
5
+ // Tinted backgrounds (e.g. `bg-info/15 text-info`) failed WCAG AA contrast on
6
+ // the 11px size (~3:1 vs. the 4.5:1 minimum) — see vitest-axe a11y gate. The
7
+ // solid pairing is what existing meda status pills (e.g. voice-status-pill)
8
+ // use, and theme tokens already guarantee AA in both light and dark modes.
9
+ const TONE_CLASSES = {
10
+ neutral: 'bg-muted text-muted-foreground',
11
+ info: 'bg-info text-info-foreground',
12
+ success: 'bg-success text-success-foreground',
13
+ warning: 'bg-warning text-warning-foreground',
14
+ danger: 'bg-destructive text-destructive-foreground',
15
+ };
16
+ const SIZE_CLASSES = {
17
+ sm: 'gap-1 px-2 py-0.5 text-[11px]',
18
+ md: 'gap-1.5 px-2.5 py-1 text-xs',
19
+ };
20
+ const DOT_SIZE = {
21
+ sm: 'size-1.5',
22
+ md: 'size-2',
23
+ };
24
+ export function StatusPill({ tone = 'neutral', size = 'sm', dot = true, className, children, ...props }) {
25
+ return (_jsxs("span", { "data-slot": "status-pill", "data-tone": tone, "data-size": size, className: cn('inline-flex items-center rounded-full font-medium', TONE_CLASSES[tone], SIZE_CLASSES[size], className), ...props, children: [dot ? (_jsx("span", { "data-slot": "status-pill-dot", "aria-hidden": "true", className: cn('rounded-full bg-current', DOT_SIZE[size]) })) : null, children] }));
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/meda",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "Shared Meda UI shell and runtime package.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -107,6 +107,10 @@
107
107
  "./theme": {
108
108
  "types": "./dist/theme/index.d.ts",
109
109
  "default": "./dist/theme/index.js"
110
+ },
111
+ "./markdown-view": {
112
+ "types": "./dist/primitives/markdown-view-entry.d.ts",
113
+ "default": "./dist/primitives/markdown-view-entry.js"
110
114
  }
111
115
  },
112
116
  "sideEffects": [
@@ -125,7 +129,10 @@
125
129
  "lucide-react": "^1.8.0",
126
130
  "next-themes": "^0.4.0",
127
131
  "react": ">=19",
128
- "react-dom": ">=19"
132
+ "react-dom": ">=19",
133
+ "react-markdown": "^10.0.0",
134
+ "rehype-highlight": "^7.0.0",
135
+ "remark-gfm": "^4.0.0"
129
136
  },
130
137
  "peerDependenciesMeta": {
131
138
  "@dnd-kit/core": {
@@ -145,10 +152,19 @@
145
152
  },
146
153
  "next-themes": {
147
154
  "optional": true
155
+ },
156
+ "react-markdown": {
157
+ "optional": true
158
+ },
159
+ "rehype-highlight": {
160
+ "optional": true
161
+ },
162
+ "remark-gfm": {
163
+ "optional": true
148
164
  }
149
165
  },
150
166
  "dependencies": {
151
- "@base-ui/react": "1.4.1",
167
+ "@base-ui/react": "1.5.0",
152
168
  "@fontsource-variable/geist": "5.2.8",
153
169
  "@fontsource-variable/geist-mono": "5.2.7",
154
170
  "class-variance-authority": "0.7.1",
@@ -192,6 +208,9 @@
192
208
  "playwright": "1.59.1",
193
209
  "react": "19.2.5",
194
210
  "react-dom": "19.2.5",
211
+ "react-markdown": "10.1.0",
212
+ "rehype-highlight": "7.0.2",
213
+ "remark-gfm": "4.0.1",
195
214
  "size-limit": "12.1.0",
196
215
  "storybook": "10.3.6",
197
216
  "tailwindcss": "4.2.4",