@medalsocial/meda 1.6.0 → 1.7.0
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/README.md +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/primitives/empty-state.d.ts +11 -0
- package/dist/primitives/empty-state.js +23 -0
- package/dist/primitives/filter-rail.d.ts +20 -0
- package/dist/primitives/filter-rail.js +16 -0
- package/dist/primitives/index.d.ts +6 -0
- package/dist/primitives/index.js +3 -0
- package/dist/primitives/skeleton.d.ts +3 -0
- package/dist/primitives/skeleton.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,6 +109,24 @@ Workspace shells also expose chrome-level composition slots:
|
|
|
109
109
|
|
|
110
110
|
`workspace.menuItems`, `workspace.menuFooter`, and the theme toggle are available from the mobile Menu drawer. Use `mainLayout`/`mainClassName` when a workspace shell needs the same mobile chrome but a custom main scroll region, such as a full-bleed marketing page. `useCommands()` works from workspace descendants without manually mounting `CommandPalette`; lower-level primitive compositions can still mount `CommandPalette` directly.
|
|
111
111
|
|
|
112
|
+
## Foundation primitives
|
|
113
|
+
|
|
114
|
+
Meda includes small foundation primitives for repeated loading, empty, and filtering surfaces:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { EmptyState, FilterRail, Skeleton } from '@medalsocial/meda';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`Skeleton` mirrors shadcn's simple loading placeholder shape. `EmptyState` standardizes zero/error states across panels and content areas. `FilterRail` provides a dense filter surface while leaving selected values, URL syncing, and query logic in the consuming app.
|
|
121
|
+
|
|
122
|
+
The same primitives are available from the shadcn-compatible registry when an app wants local source ownership:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx shadcn add https://meda.medalsocial.com/r/meda-skeleton.json
|
|
126
|
+
npx shadcn add https://meda.medalsocial.com/r/meda-empty-state.json
|
|
127
|
+
npx shadcn add https://meda.medalsocial.com/r/meda-filter-rail.json
|
|
128
|
+
```
|
|
129
|
+
|
|
112
130
|
For app-scoped brand tokens:
|
|
113
131
|
|
|
114
132
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './brand/public.js';
|
|
|
3
3
|
export * from './chat/public.js';
|
|
4
4
|
export * from './marketing/public.js';
|
|
5
5
|
export * from './panel/public.js';
|
|
6
|
+
export * from './primitives/index.js';
|
|
6
7
|
export * from './shell/index.js';
|
|
7
8
|
export * from './theme/index.js';
|
|
8
9
|
export * from './timeline/public.js';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './brand/public.js';
|
|
|
3
3
|
export * from './chat/public.js';
|
|
4
4
|
export * from './marketing/public.js';
|
|
5
5
|
export * from './panel/public.js';
|
|
6
|
+
export * from './primitives/index.js';
|
|
6
7
|
export * from './shell/index.js'; // v2 surface
|
|
7
8
|
export * from './theme/index.js';
|
|
8
9
|
export * from './timeline/public.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LucideIcon } from 'lucide-react';
|
|
2
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
3
|
+
export type EmptyStateVariant = 'default' | 'panel' | 'inline';
|
|
4
|
+
export interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title'> {
|
|
5
|
+
icon?: LucideIcon | ReactNode;
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
action?: ReactNode;
|
|
9
|
+
variant?: EmptyStateVariant;
|
|
10
|
+
}
|
|
11
|
+
export declare function EmptyState({ icon, title, description, action, variant, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { isValidElement } from 'react';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
|
+
function isIconComponent(icon) {
|
|
6
|
+
if (typeof icon === 'function')
|
|
7
|
+
return true;
|
|
8
|
+
if (typeof icon !== 'object')
|
|
9
|
+
return false;
|
|
10
|
+
return '$$typeof' in icon && !isValidElement(icon);
|
|
11
|
+
}
|
|
12
|
+
function renderIcon(icon) {
|
|
13
|
+
if (!icon)
|
|
14
|
+
return null;
|
|
15
|
+
if (isIconComponent(icon)) {
|
|
16
|
+
const Icon = icon;
|
|
17
|
+
return _jsx(Icon, { "data-testid": "empty-state-icon", className: "size-10", "aria-hidden": "true" });
|
|
18
|
+
}
|
|
19
|
+
return (_jsx("span", { "data-testid": "empty-state-icon", "aria-hidden": "true", className: "inline-flex", children: icon }));
|
|
20
|
+
}
|
|
21
|
+
export function EmptyState({ icon, title, description, action, variant = 'default', className, ...props }) {
|
|
22
|
+
return (_jsxs("div", { "data-slot": "empty-state", "data-variant": variant, className: cn('flex flex-col items-center justify-center text-center', variant === 'default' && 'px-6 py-16', variant === 'panel' && 'px-4 py-10', variant === 'inline' && 'px-3 py-6', className), ...props, children: [icon ? (_jsx("div", { "data-slot": "empty-state-icon", className: cn('mb-4 inline-flex items-center justify-center rounded-md text-muted-foreground', variant === 'inline' ? 'size-9' : 'size-12'), children: renderIcon(icon) })) : null, _jsx("h3", { "data-slot": "empty-state-title", className: cn('font-semibold text-foreground', variant === 'default' && 'text-lg', variant === 'panel' && 'text-base', variant === 'inline' && 'text-sm'), children: title }), description ? (_jsx("p", { "data-slot": "empty-state-description", className: cn('mt-1 max-w-sm text-muted-foreground', variant === 'inline' ? 'text-xs' : 'text-sm'), children: description })) : null, action ? (_jsx("div", { "data-slot": "empty-state-action", className: "mt-5", children: action })) : null] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
export interface FilterRailProps extends Omit<ComponentPropsWithoutRef<'aside'>, 'title'> {
|
|
3
|
+
title?: ReactNode;
|
|
4
|
+
description?: ReactNode;
|
|
5
|
+
search?: ReactNode;
|
|
6
|
+
actions?: ReactNode;
|
|
7
|
+
footer?: ReactNode;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export interface FilterRailGroupProps extends Omit<ComponentPropsWithoutRef<'fieldset'>, 'title'> {
|
|
11
|
+
title?: ReactNode;
|
|
12
|
+
description?: ReactNode;
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
declare function FilterRailGroup({ title, description, children, className, ...props }: FilterRailGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare function FilterRailRoot({ title, description, search, actions, footer, children, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: FilterRailProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const FilterRail: typeof FilterRailRoot & {
|
|
18
|
+
Group: typeof FilterRailGroup;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
|
+
function FilterRailGroup({ title, description, children, className, ...props }) {
|
|
6
|
+
return (_jsxs("fieldset", { "data-slot": "filter-rail-group", className: cn('space-y-2 border-0 p-0', className), ...props, children: [title ? (_jsx("legend", { "data-slot": "filter-rail-group-title", className: "text-xs font-semibold text-foreground", children: title })) : null, description ? (_jsx("p", { "data-slot": "filter-rail-group-description", className: "text-xs text-muted-foreground", children: description })) : null, _jsx("div", { "data-slot": "filter-rail-group-content", className: "space-y-1.5", children: children })] }));
|
|
7
|
+
}
|
|
8
|
+
function FilterRailRoot({ title, description, search, actions, footer, children, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }) {
|
|
9
|
+
const titleId = useId();
|
|
10
|
+
const label = ariaLabel ?? (ariaLabelledBy ? undefined : typeof title === 'string' ? title : undefined);
|
|
11
|
+
const labelledBy = ariaLabelledBy ?? (label ? undefined : title ? titleId : undefined);
|
|
12
|
+
return (_jsxs("aside", { "data-slot": "filter-rail", "aria-label": label, "aria-labelledby": labelledBy, className: cn('flex min-h-0 w-full flex-col border-border bg-card text-card-foreground', className), ...props, children: [title || description || actions ? (_jsxs("div", { "data-slot": "filter-rail-header", className: "flex shrink-0 items-start justify-between gap-3 border-b border-border px-4 py-3", children: [_jsxs("div", { className: "min-w-0", children: [title ? (_jsx("h2", { id: titleId, "data-slot": "filter-rail-title", className: "text-sm font-semibold text-foreground", children: title })) : null, description ? (_jsx("p", { "data-slot": "filter-rail-description", className: "mt-0.5 text-xs text-muted-foreground", children: description })) : null] }), actions ? (_jsx("div", { "data-slot": "filter-rail-actions", className: "shrink-0", children: actions })) : null] })) : null, search ? (_jsx("div", { "data-slot": "filter-rail-search", className: "shrink-0 border-b border-border p-3", children: search })) : null, _jsx("div", { "data-slot": "filter-rail-content", className: "min-h-0 flex-1 space-y-5 overflow-y-auto p-4", children: children }), footer ? (_jsx("div", { "data-slot": "filter-rail-footer", className: "shrink-0 border-t border-border p-3", children: footer })) : null] }));
|
|
13
|
+
}
|
|
14
|
+
export const FilterRail = Object.assign(FilterRailRoot, {
|
|
15
|
+
Group: FilterRailGroup,
|
|
16
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { EmptyStateProps, EmptyStateVariant } from './empty-state.js';
|
|
2
|
+
export { EmptyState } from './empty-state.js';
|
|
3
|
+
export type { FilterRailGroupProps, FilterRailProps } from './filter-rail.js';
|
|
4
|
+
export { FilterRail } from './filter-rail.js';
|
|
5
|
+
export type { SkeletonProps } from './skeleton.js';
|
|
6
|
+
export { Skeleton } from './skeleton.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
4
|
+
export function Skeleton({ className, 'aria-hidden': ariaHidden = true, ...props }) {
|
|
5
|
+
return (_jsx("div", { "data-slot": "skeleton", "aria-hidden": ariaHidden, className: cn('animate-pulse rounded-md bg-muted', className), ...props }));
|
|
6
|
+
}
|