@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.
- package/dist/primitives/card.d.ts +15 -0
- package/dist/primitives/card.js +20 -0
- package/dist/primitives/index.d.ts +4 -0
- package/dist/primitives/index.js +2 -0
- package/dist/primitives/markdown-view-entry.d.ts +2 -0
- package/dist/primitives/markdown-view-entry.js +7 -0
- package/dist/primitives/markdown-view.d.ts +22 -0
- package/dist/primitives/markdown-view.js +56 -0
- package/dist/primitives/status-pill.d.ts +12 -0
- package/dist/primitives/status-pill.js +26 -0
- package/package.json +22 -3
|
@@ -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';
|
package/dist/primitives/index.js
CHANGED
|
@@ -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
|
+
"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.
|
|
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",
|