mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-10 05:53:05 +09:00
Plan 79 §H + ratchet 7. <html dir> already follows the language manifest and the locale probe asserts it. What was missing is a stylesheet that could follow it: 468 direction-anchored declarations (margin/padding/border-left|right, bare left:/right:, text-align and float) against 5 logical properties. An RTL locale today would render a mirrored language inside an unmirrored layout, so the plan ships plumbing and a ratchet, not a locale. The count is 468 rather than the plan's grepped 412 because float and text-align are equally anchored. Also splits `src/i18n/t.js` out of the i18n barrel. The API client's error mapper imports the bare `t`, and the client is reachable from a node test -- which then loaded i18n/index.js and its `import.meta.glob`, a Vite compile-time transform that throws under plain node. The obvious fix was the runtime guard that had just been removed for silently discarding every locale, so the module is split instead: t.js touches only the i18next singleton, and index.js keeps initialisation and the glob for browser-only callers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
50 lines
1.6 KiB
React
50 lines
1.6 KiB
React
import { Inbox } from 'lucide-react';
|
|
import { PageSkeleton } from './PageSkeleton';
|
|
import { t } from '../i18n/t';
|
|
|
|
/**
|
|
* `loadingVariant` names the shape of the page that is loading — see
|
|
* PageSkeleton for the archetypes (table, feed, cards, detail, console, form,
|
|
* chart, tree, split). It defaults to the generic `panel`, which is a safe
|
|
* fallback but predicts nothing: prefer naming the real shape, so the skeleton
|
|
* resolves into the content instead of being replaced by it.
|
|
*/
|
|
export default function EmptyState({
|
|
icon: Icon = Inbox,
|
|
title = 'No items found',
|
|
description = '',
|
|
action = null,
|
|
size = 'default',
|
|
loading = false,
|
|
loadingVariant = 'panel',
|
|
loadingRows,
|
|
}) {
|
|
if (loading) {
|
|
return (
|
|
<div
|
|
className={`empty-state empty-state--${size} empty-state--loading`}
|
|
role="status"
|
|
aria-busy="true"
|
|
aria-label={title || t('common.state.loading', 'Loading')}
|
|
>
|
|
<PageSkeleton variant={loadingVariant} rows={loadingRows} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`empty-state empty-state--${size}`}>
|
|
<div className="empty-state__icon">
|
|
<Icon size={size === 'lg' ? 64 : 48} />
|
|
</div>
|
|
<h3 className="empty-state__title">{title}</h3>
|
|
{description && (
|
|
<p className="empty-state__description">{description}</p>
|
|
)}
|
|
{action && (
|
|
<div className="empty-state__action">{action}</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|