дизайн

This commit is contained in:
2025-07-28 14:39:56 +03:00
parent 8e5f993cd8
commit d8e6dcd7d5
7 changed files with 112 additions and 69 deletions

View File

@@ -1,4 +1,4 @@
import { Save } from 'lucide-react' import { Save, Sigma } from 'lucide-react'
import { memo } from 'react' import { memo } from 'react'
import { ExcelUploadPanel } from '../../TemplateManager/ExcelUploadPanel' import { ExcelUploadPanel } from '../../TemplateManager/ExcelUploadPanel'
import { FormulaBarProps } from '../types' import { FormulaBarProps } from '../types'
@@ -29,8 +29,8 @@ export const FormulaBar = memo(
<div className="formula-bar flex-shrink-0 border-b bg-background px-3 py-1.5"> <div className="formula-bar flex-shrink-0 border-b bg-background px-3 py-1.5">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
{/* Адрес ячейки */} {/* Адрес ячейки */}
<div className="flex min-w-[45px] items-center justify-center rounded border bg-muted px-1.5 py-0.5"> <div className="flex min-w-[45px] items-center justify-center rounded border bg-muted px-2 py-1">
<span className="text-xs font-medium text-muted-foreground"> <span className="text-sm font-medium text-muted-foreground">
{selectedCell {selectedCell
? `${getColumnLabel(selectedCell.col)}${selectedCell.row + 1}` ? `${getColumnLabel(selectedCell.col)}${selectedCell.row + 1}`
: 'A1'} : 'A1'}
@@ -40,19 +40,7 @@ export const FormulaBar = memo(
{/* Строка формул */} {/* Строка формул */}
<div className="flex flex-1 items-center space-x-2"> <div className="flex flex-1 items-center space-x-2">
<div className="flex items-center text-muted-foreground"> <div className="flex items-center text-muted-foreground">
<svg <Sigma className="h-5 w-5" />
className="h-3.5 w-3.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 002 2z"
/>
</svg>
</div> </div>
<input <input
ref={formulaBarRef} ref={formulaBarRef}

View File

@@ -456,10 +456,7 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
onLayoutSettingsChange, onLayoutSettingsChange,
}) => { }) => {
const elements = template.elements const elements = template.elements
const layoutSettings = template.layoutSettings || { const layoutSettings = template.layoutSettings || getDefaultLayoutSettings()
gridSize: 20,
showGrid: true,
}
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
const [editingElement, setEditingElement] = useState<TemplateElement | null>( const [editingElement, setEditingElement] = useState<TemplateElement | null>(
null null
@@ -611,6 +608,18 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
} }
: undefined : undefined
} }
snapToGrid={layoutSettings.snapToGrid}
onToggleSnapToGrid={
onLayoutSettingsChange
? () => {
const newSettings = {
...layoutSettings,
snapToGrid: !layoutSettings.snapToGrid,
}
onLayoutSettingsChange(newSettings)
}
: undefined
}
/> />
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">

View File

@@ -1,4 +1,4 @@
import { Eye, EyeOff, Plus, Save, Wrench } from 'lucide-react' import { Eye, EyeOff, Magnet, Plus, Save, Wrench } from 'lucide-react'
import { memo } from 'react' import { memo } from 'react'
import { Button } from '../ui/button' import { Button } from '../ui/button'
@@ -16,6 +16,8 @@ interface ElementFormulaBarProps {
// Пропсы для управления сеткой // Пропсы для управления сеткой
showGrid?: boolean showGrid?: boolean
onToggleGrid?: () => void onToggleGrid?: () => void
snapToGrid?: boolean
onToggleSnapToGrid?: () => void
} }
export const ElementFormulaBar = memo( export const ElementFormulaBar = memo(
@@ -31,17 +33,48 @@ export const ElementFormulaBar = memo(
onClearError, onClearError,
showGrid = true, showGrid = true,
onToggleGrid, onToggleGrid,
snapToGrid = true,
onToggleSnapToGrid,
}: ElementFormulaBarProps) => { }: ElementFormulaBarProps) => {
return ( return (
<div className="formula-bar flex-shrink-0 border-b bg-background px-3 py-1"> <div className="formula-bar flex-shrink-0 border-b bg-background px-3 py-1">
<div className="flex items-center justify-between"> <div className="relative flex items-center">
<div className="flex items-center space-x-2">
<div className="flex min-w-[100px] items-center justify-center rounded-md border bg-muted px-2 py-1">
<span className="text-xs font-medium text-muted-foreground">
{elementCount} элементов
</span>
</div>
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
{onToggleGrid && (
<Button
variant={showGrid ? 'default' : 'outline'}
size="sm"
onClick={onToggleGrid}
className="h-7 px-2 text-xs"
>
{showGrid ? (
<EyeOff className="mr-1 h-3 w-3" />
) : (
<Eye className="mr-1 h-3 w-3" />
)}
{showGrid ? 'Скрыть' : 'Показать'}
</Button>
)}
{onToggleSnapToGrid && (
<Button
variant={snapToGrid ? 'default' : 'outline'}
size="sm"
onClick={onToggleSnapToGrid}
className="h-7 px-2 text-xs"
title={
snapToGrid
? 'Отключить привязку к сетке'
: 'Включить привязку к сетке'
}
>
<Magnet className="mr-1 h-3 w-3" />
Привязка
</Button>
)}
</div>
{/* Центральные кнопки добавления - абсолютное позиционирование */}
<div className="absolute left-1/2 flex -translate-x-1/2 transform items-center space-x-1">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -64,25 +97,9 @@ export const ElementFormulaBar = memo(
Упорядочить Упорядочить
</Button> </Button>
)} )}
{onToggleGrid && (
<Button
variant="outline"
size="sm"
onClick={onToggleGrid}
className="h-7 px-2 text-xs"
>
{showGrid ? (
<EyeOff className="mr-1 h-3 w-3" />
) : (
<Eye className="mr-1 h-3 w-3" />
)}
{showGrid ? 'Скрыть сетку' : 'Показать'}
</Button>
)}
</div>
</div> </div>
<div className="flex items-center gap-2"> <div className="ml-auto flex items-center gap-2">
{/* Ошибка сохранения */} {/* Ошибка сохранения */}
{lastSaveError && ( {lastSaveError && (
<div className="flex items-center gap-1 rounded-md bg-destructive/10 px-2 py-1"> <div className="flex items-center gap-1 rounded-md bg-destructive/10 px-2 py-1">

View File

@@ -33,10 +33,20 @@ interface DraggableElementProps {
onDelete: () => void onDelete: () => void
onEdit?: () => void onEdit?: () => void
gridSize: number gridSize: number
layoutSettings: FormLayoutSettings
} }
const DraggableElement: React.FC<DraggableElementProps> = React.memo( const DraggableElement: React.FC<DraggableElementProps> = React.memo(
({ element, isSelected, onSelect, onUpdate, onDelete, onEdit, gridSize }) => { ({
element,
isSelected,
onSelect,
onUpdate,
onDelete,
onEdit,
gridSize,
layoutSettings,
}) => {
const layout = element.layout || { const layout = element.layout || {
x: 50, x: 50,
y: 50, y: 50,
@@ -113,8 +123,8 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(
return ( return (
<Rnd <Rnd
dragGrid={[gridSize, gridSize]} dragGrid={layoutSettings.snapToGrid ? [gridSize, gridSize] : [1, 1]}
resizeGrid={[gridSize, gridSize]} resizeGrid={layoutSettings.snapToGrid ? [gridSize, gridSize] : [1, 1]}
default={{ default={{
x: layout.x, x: layout.x,
y: layout.y, y: layout.y,
@@ -352,6 +362,7 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
onDelete={() => onElementDelete(element.id)} onDelete={() => onElementDelete(element.id)}
onEdit={onElementEdit ? () => onElementEdit(element) : undefined} onEdit={onElementEdit ? () => onElementEdit(element) : undefined}
gridSize={layoutSettings.gridSize} gridSize={layoutSettings.gridSize}
layoutSettings={layoutSettings}
/> />
))} ))}
{elements.length === 0 && ( {elements.length === 0 && (

View File

@@ -47,6 +47,7 @@ export function getDefaultLayoutSettings(): FormLayoutSettings {
return { return {
gridSize: 20, gridSize: 20,
showGrid: true, showGrid: true,
snapToGrid: true,
} }
} }

View File

@@ -2,7 +2,7 @@ import { ElementConstructor } from '@/component/TemplateManager/ElementConstruct
import { Button } from '@/component/ui/button' import { Button } from '@/component/ui/button'
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext' import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
import { useDelayedSave } from '@/hook/useDelayedSave' import { useDelayedSave } from '@/hook/useDelayedSave'
import { Template, TemplateElement } from '@/type/template' import { FormLayoutSettings, Template, TemplateElement } from '@/type/template'
import { ArrowLeft, FileText, Settings, Wrench } from 'lucide-react' import { ArrowLeft, FileText, Settings, Wrench } from 'lucide-react'
import { FC, useCallback, useEffect, useState } from 'react' import { FC, useCallback, useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
@@ -105,6 +105,21 @@ export const ElementsCreation: FC = () => {
updateData(updatedTemplate) updateData(updatedTemplate)
} }
const handleLayoutSettingsChange = async (
layoutSettings: FormLayoutSettings
) => {
if (!selectedTemplate) return
const updatedTemplate = {
...selectedTemplate,
layoutSettings,
updatedAt: new Date(),
}
setSelectedTemplate(updatedTemplate)
updateData(updatedTemplate)
}
// if (isLoading) { // if (isLoading) {
// return ( // return (
// <div className="flex h-screen items-center justify-center"> // <div className="flex h-screen items-center justify-center">
@@ -175,10 +190,10 @@ export const ElementsCreation: FC = () => {
variant="link" variant="link"
size="sm" size="sm"
onClick={() => navigate(`/templates/${selectedTemplate.id}/edit`)} onClick={() => navigate(`/templates/${selectedTemplate.id}/edit`)}
className="flex items-center gap-2 hover:bg-muted" className="flex items-center gap-1 hover:bg-muted"
> >
<Settings className="h-3.5 w-3.5" /> <Settings className="h-4 w-4" />
Настройки шаблона <span className="text-sm">Настройки шаблона</span>
</Button> </Button>
<Button <Button
variant="link" variant="link"
@@ -200,6 +215,7 @@ export const ElementsCreation: FC = () => {
onElementAdd={handleElementAdd} onElementAdd={handleElementAdd}
onElementUpdate={handleElementUpdate} onElementUpdate={handleElementUpdate}
onElementDelete={handleElementDelete} onElementDelete={handleElementDelete}
onLayoutSettingsChange={handleLayoutSettingsChange}
disabled={false} disabled={false}
hasUnsavedChanges={hasUnsavedChanges} hasUnsavedChanges={hasUnsavedChanges}
isSaving={isSaving} isSaving={isSaving}

View File

@@ -31,6 +31,7 @@ export interface ElementLayout {
export interface FormLayoutSettings { export interface FormLayoutSettings {
gridSize: number // размер сетки для привязки (в пикселях) gridSize: number // размер сетки для привязки (в пикселях)
showGrid: boolean // показывать сетку showGrid: boolean // показывать сетку
snapToGrid: boolean // привязывать элементы к сетке
} }
export interface TemplateElement { export interface TemplateElement {