create for series
This commit is contained in:
parent
53991c7791
commit
95a2019956
@ -1,3 +1,5 @@
|
||||
import { Card } from './ui'
|
||||
|
||||
interface SeriesStatsProps {
|
||||
sponsoring: number
|
||||
purchases: number
|
||||
@ -17,10 +19,10 @@ export function SeriesStats({ sponsoring, purchases, reviewTips }: SeriesStatsPr
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
{items.map((item) => (
|
||||
<div key={item.label} className="border rounded-lg p-3 bg-white text-sm">
|
||||
<Card key={item.label} variant="default" className="bg-white text-sm">
|
||||
<div className="text-gray-600">{item.label}</div>
|
||||
<div className="font-semibold text-gray-900">{item.value}</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ArticleEditorForm } from './ArticleEditorForm'
|
||||
import type { ArticleDraft } from '@/lib/articlePublisher'
|
||||
import { Card } from './ui'
|
||||
|
||||
interface EditPanelProps {
|
||||
draft: ArticleDraft | null
|
||||
@ -24,7 +25,7 @@ export function EditPanel({
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className="border rounded-lg p-4 bg-white space-y-3">
|
||||
<Card variant="default" className="bg-white space-y-3">
|
||||
<h3 className="text-lg font-semibold">Edit article</h3>
|
||||
<ArticleEditorForm
|
||||
draft={draft}
|
||||
@ -37,6 +38,6 @@ export function EditPanel({
|
||||
error={error}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useNostrAuth } from '@/hooks/useNostrAuth'
|
||||
import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
|
||||
import { Button } from '../ui'
|
||||
import { Button, Card } from '../ui'
|
||||
import { t } from '@/lib/i18n'
|
||||
import { NoAccountView } from './NoAccountView'
|
||||
import { PresentationForm } from './PresentationForm'
|
||||
@ -10,7 +10,7 @@ import { useAuthorPresentationState } from './useAuthorPresentationState'
|
||||
|
||||
function SuccessNotice(params: { pubkey: string | null }): React.ReactElement {
|
||||
return (
|
||||
<div className="border border-neon-green/50 rounded-lg p-6 bg-neon-green/10">
|
||||
<Card variant="default" className="border-neon-green/50 bg-neon-green/10">
|
||||
<h3 className="text-lg font-semibold text-neon-green mb-2">{t('presentation.success')}</h3>
|
||||
<p className="text-cyber-accent mb-4">{t('presentation.successMessage')}</p>
|
||||
{params.pubkey ? (
|
||||
@ -22,7 +22,7 @@ function SuccessNotice(params: { pubkey: string | null }): React.ReactElement {
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Textarea } from '../ui'
|
||||
import { Button, Card, Textarea } from '../ui'
|
||||
import { t } from '@/lib/i18n'
|
||||
import type { Page } from '@/types/nostr'
|
||||
|
||||
@ -39,10 +39,10 @@ function PageEditor(params: {
|
||||
onImageUpload: (file: File) => Promise<void>
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<div className="border rounded-lg p-4 space-y-3">
|
||||
<Card variant="default" className="space-y-3">
|
||||
<PageEditorHeader page={params.page} onTypeChange={params.onTypeChange} onRemove={params.onRemove} />
|
||||
<PageEditorBody page={params.page} onContentChange={params.onContentChange} onImageUpload={params.onImageUpload} />
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ interface ButtonProps {
|
||||
'aria-expanded'?: boolean
|
||||
'aria-haspopup'?: boolean | 'true' | 'false' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
|
||||
'aria-selected'?: boolean
|
||||
'aria-controls'?: string
|
||||
role?: string
|
||||
id?: string
|
||||
}
|
||||
@ -95,6 +96,17 @@ function ButtonContent({
|
||||
)
|
||||
}
|
||||
|
||||
function getCombinedClasses(
|
||||
variant: ButtonVariant,
|
||||
size: ButtonSize,
|
||||
className: string
|
||||
): string {
|
||||
const variantClasses = getVariantClasses(variant)
|
||||
const sizeClasses = getSizeClasses(size)
|
||||
const baseClasses = 'inline-flex items-center justify-center rounded-lg font-medium transition-all border focus:outline-none focus:ring-2 focus:ring-neon-cyan disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
return `${baseClasses} ${variantClasses} ${sizeClasses} ${className}`.trim()
|
||||
}
|
||||
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref): React.ReactElement => {
|
||||
const {
|
||||
children,
|
||||
@ -109,14 +121,11 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, r
|
||||
'aria-expanded': ariaExpanded,
|
||||
'aria-haspopup': ariaHaspopup,
|
||||
'aria-selected': ariaSelected,
|
||||
'aria-controls': ariaControls,
|
||||
role: roleProp,
|
||||
id,
|
||||
} = props
|
||||
const variantClasses = getVariantClasses(variant)
|
||||
const sizeClasses = getSizeClasses(size)
|
||||
const baseClasses = 'inline-flex items-center justify-center rounded-lg font-medium transition-all border focus:outline-none focus:ring-2 focus:ring-neon-cyan disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
|
||||
const combinedClasses = `${baseClasses} ${variantClasses} ${sizeClasses} ${className}`.trim()
|
||||
const combinedClasses = getCombinedClasses(variant, size, className)
|
||||
|
||||
return (
|
||||
<button
|
||||
@ -131,6 +140,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, r
|
||||
aria-expanded={ariaExpanded}
|
||||
aria-haspopup={ariaHaspopup}
|
||||
aria-selected={ariaSelected}
|
||||
aria-controls={ariaControls}
|
||||
aria-busy={loading}
|
||||
>
|
||||
<ButtonContent loading={loading}>{children}</ButtonContent>
|
||||
|
||||
@ -8,6 +8,7 @@ interface CardProps {
|
||||
className?: string
|
||||
onClick?: () => void
|
||||
'aria-label'?: string
|
||||
role?: string
|
||||
}
|
||||
|
||||
function getVariantClasses(variant: CardVariant, hasOnClick: boolean): string {
|
||||
@ -40,17 +41,19 @@ export function Card({
|
||||
className = '',
|
||||
onClick,
|
||||
'aria-label': ariaLabel,
|
||||
role: roleProp,
|
||||
}: CardProps): React.ReactElement {
|
||||
const variantClasses = getVariantClasses(variant, onClick !== undefined)
|
||||
const paddingClasses = getPaddingClasses(variant)
|
||||
const combinedClasses = `${variantClasses} ${paddingClasses} ${className}`.trim()
|
||||
const role = roleProp ?? (onClick !== undefined ? 'button' : undefined)
|
||||
|
||||
if (onClick) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={combinedClasses}
|
||||
role="button"
|
||||
role={role}
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
@ -66,7 +69,7 @@ export function Card({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={combinedClasses} aria-label={ariaLabel}>
|
||||
<div className={combinedClasses} role={role} aria-label={ariaLabel}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { Card } from './Card'
|
||||
|
||||
interface ErrorStateProps {
|
||||
message: string
|
||||
@ -27,7 +28,7 @@ function ErrorIcon(): React.ReactElement {
|
||||
|
||||
export function ErrorState({ message, action, className = '' }: ErrorStateProps): React.ReactElement {
|
||||
return (
|
||||
<div className={`bg-red-900/20 border border-red-500/50 rounded-lg p-4 ${className}`} role="alert">
|
||||
<Card variant="default" className={`bg-red-900/20 border-red-500/50 ${className}`} role="alert">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 text-red-400">
|
||||
<ErrorIcon />
|
||||
@ -37,6 +38,6 @@ export function ErrorState({ message, action, className = '' }: ErrorStateProps)
|
||||
{action && <div className="mt-3">{action}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Button } from './Button'
|
||||
|
||||
interface MobileMenuProps {
|
||||
children: ReactNode
|
||||
@ -130,13 +131,15 @@ function MobileMenuContent({
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="flex justify-end mb-4">
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={onClose}
|
||||
className="text-cyber-accent hover:text-neon-cyan text-2xl transition-colors focus:outline-none focus:ring-2 focus:ring-neon-cyan rounded"
|
||||
className="text-cyber-accent hover:text-neon-cyan text-2xl p-0"
|
||||
aria-label="Close menu"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-4">{children}</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Button } from './Button'
|
||||
|
||||
interface ModalProps {
|
||||
children: ReactNode
|
||||
@ -28,13 +29,15 @@ function getSizeClasses(size: ModalProps['size']): string {
|
||||
|
||||
function CloseButton({ onClose }: { onClose: () => void }): React.ReactElement {
|
||||
return (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={onClose}
|
||||
className="text-cyber-accent hover:text-neon-cyan text-2xl transition-colors focus:outline-none focus:ring-2 focus:ring-neon-cyan rounded"
|
||||
className="text-cyber-accent hover:text-neon-cyan text-2xl p-0 w-auto h-auto min-w-0"
|
||||
aria-label="Close modal"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useEffect } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Button } from './Button'
|
||||
|
||||
export type ToastVariant = 'info' | 'success' | 'warning' | 'error'
|
||||
|
||||
@ -55,13 +56,15 @@ export function Toast({
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<div className="flex-1">{children}</div>
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={onClose}
|
||||
className="ml-4 text-current hover:opacity-70 transition-opacity focus:outline-none focus:ring-2 focus:ring-current rounded"
|
||||
className="ml-4 text-current hover:opacity-70 p-0"
|
||||
aria-label="Close notification"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -87,7 +87,16 @@ Aucun composant prioritaire restant. Tous les composants principaux ont été mi
|
||||
- ✅ `NotificationPanel.tsx` - Migration du conteneur principal vers Card
|
||||
- ✅ `AuthorFilterButton.tsx` - Migration vers Button avec support forwardRef
|
||||
- ✅ `AuthorFilterDropdown.tsx` - Migration de AuthorOption et AllAuthorsOption vers Button
|
||||
- ✅ `components/ui/Button.tsx` - Ajout du support forwardRef, aria-expanded, aria-haspopup, aria-selected, role, id
|
||||
- ✅ `components/ui/Button.tsx` - Ajout du support forwardRef, aria-expanded, aria-haspopup, aria-selected, aria-controls, role, id
|
||||
- ✅ `components/ui/MobileMenu.tsx` - Migration des boutons vers Button
|
||||
- ✅ `components/ui/Toast.tsx` - Migration du bouton de fermeture vers Button
|
||||
- ✅ `components/ui/ErrorState.tsx` - Migration du conteneur vers Card
|
||||
- ✅ `components/ui/Card.tsx` - Ajout du support role pour l'accessibilité
|
||||
- ✅ `components/ui/Modal.tsx` - Migration du bouton de fermeture vers Button
|
||||
- ✅ `components/SeriesStats.tsx` - Migration des conteneurs de statistiques vers Card
|
||||
- ✅ `components/UserArticlesEditPanel.tsx` - Migration du conteneur principal vers Card
|
||||
- ✅ `components/markdownEditorTwoColumns/PagesManager.tsx` - Migration de PageEditor vers Card
|
||||
- ✅ `components/authorPresentationEditor/AuthorPresentationEditor.tsx` - Migration de SuccessNotice vers Card
|
||||
|
||||
## Erreurs corrigées
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user