дизайн
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Save } from 'lucide-react'
|
||||
import { Save, Sigma } from 'lucide-react'
|
||||
import { memo } from 'react'
|
||||
import { ExcelUploadPanel } from '../../TemplateManager/ExcelUploadPanel'
|
||||
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="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">
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
<div className="flex min-w-[45px] items-center justify-center rounded border bg-muted px-2 py-1">
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{selectedCell
|
||||
? `${getColumnLabel(selectedCell.col)}${selectedCell.row + 1}`
|
||||
: 'A1'}
|
||||
@@ -40,19 +40,7 @@ export const FormulaBar = memo(
|
||||
{/* Строка формул */}
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<svg
|
||||
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>
|
||||
<Sigma className="h-5 w-5" />
|
||||
</div>
|
||||
<input
|
||||
ref={formulaBarRef}
|
||||
|
||||
@@ -456,10 +456,7 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
|
||||
onLayoutSettingsChange,
|
||||
}) => {
|
||||
const elements = template.elements
|
||||
const layoutSettings = template.layoutSettings || {
|
||||
gridSize: 20,
|
||||
showGrid: true,
|
||||
}
|
||||
const layoutSettings = template.layoutSettings || getDefaultLayoutSettings()
|
||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
|
||||
const [editingElement, setEditingElement] = useState<TemplateElement | null>(
|
||||
null
|
||||
@@ -611,6 +608,18 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
snapToGrid={layoutSettings.snapToGrid}
|
||||
onToggleSnapToGrid={
|
||||
onLayoutSettingsChange
|
||||
? () => {
|
||||
const newSettings = {
|
||||
...layoutSettings,
|
||||
snapToGrid: !layoutSettings.snapToGrid,
|
||||
}
|
||||
onLayoutSettingsChange(newSettings)
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
|
||||
@@ -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 { Button } from '../ui/button'
|
||||
|
||||
@@ -16,6 +16,8 @@ interface ElementFormulaBarProps {
|
||||
// Пропсы для управления сеткой
|
||||
showGrid?: boolean
|
||||
onToggleGrid?: () => void
|
||||
snapToGrid?: boolean
|
||||
onToggleSnapToGrid?: () => void
|
||||
}
|
||||
|
||||
export const ElementFormulaBar = memo(
|
||||
@@ -31,58 +33,73 @@ export const ElementFormulaBar = memo(
|
||||
onClearError,
|
||||
showGrid = true,
|
||||
onToggleGrid,
|
||||
snapToGrid = true,
|
||||
onToggleSnapToGrid,
|
||||
}: ElementFormulaBarProps) => {
|
||||
return (
|
||||
<div className="formula-bar flex-shrink-0 border-b bg-background px-3 py-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<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="relative flex items-center">
|
||||
<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
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onAddElement}
|
||||
disabled={disabled}
|
||||
className="h-7 px-2 text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
Добавить
|
||||
</Button>
|
||||
{onAutoArrange && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onAddElement}
|
||||
disabled={disabled}
|
||||
onClick={onAutoArrange}
|
||||
disabled={disabled || elementCount === 0}
|
||||
className="h-7 px-2 text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
Добавить
|
||||
<Wrench className="mr-1 h-3 w-3" />
|
||||
Упорядочить
|
||||
</Button>
|
||||
{onAutoArrange && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onAutoArrange}
|
||||
disabled={disabled || elementCount === 0}
|
||||
className="h-7 px-2 text-xs"
|
||||
>
|
||||
<Wrench className="mr-1 h-3 w-3" />
|
||||
Упорядочить
|
||||
</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 className="flex items-center gap-2">
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{/* Ошибка сохранения */}
|
||||
{lastSaveError && (
|
||||
<div className="flex items-center gap-1 rounded-md bg-destructive/10 px-2 py-1">
|
||||
|
||||
@@ -33,10 +33,20 @@ interface DraggableElementProps {
|
||||
onDelete: () => void
|
||||
onEdit?: () => void
|
||||
gridSize: number
|
||||
layoutSettings: FormLayoutSettings
|
||||
}
|
||||
|
||||
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 || {
|
||||
x: 50,
|
||||
y: 50,
|
||||
@@ -113,8 +123,8 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(
|
||||
|
||||
return (
|
||||
<Rnd
|
||||
dragGrid={[gridSize, gridSize]}
|
||||
resizeGrid={[gridSize, gridSize]}
|
||||
dragGrid={layoutSettings.snapToGrid ? [gridSize, gridSize] : [1, 1]}
|
||||
resizeGrid={layoutSettings.snapToGrid ? [gridSize, gridSize] : [1, 1]}
|
||||
default={{
|
||||
x: layout.x,
|
||||
y: layout.y,
|
||||
@@ -352,6 +362,7 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
||||
onDelete={() => onElementDelete(element.id)}
|
||||
onEdit={onElementEdit ? () => onElementEdit(element) : undefined}
|
||||
gridSize={layoutSettings.gridSize}
|
||||
layoutSettings={layoutSettings}
|
||||
/>
|
||||
))}
|
||||
{elements.length === 0 && (
|
||||
|
||||
@@ -47,6 +47,7 @@ export function getDefaultLayoutSettings(): FormLayoutSettings {
|
||||
return {
|
||||
gridSize: 20,
|
||||
showGrid: true,
|
||||
snapToGrid: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ElementConstructor } from '@/component/TemplateManager/ElementConstruct
|
||||
import { Button } from '@/component/ui/button'
|
||||
import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
||||
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 { FC, useCallback, useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
@@ -105,6 +105,21 @@ export const ElementsCreation: FC = () => {
|
||||
updateData(updatedTemplate)
|
||||
}
|
||||
|
||||
const handleLayoutSettingsChange = async (
|
||||
layoutSettings: FormLayoutSettings
|
||||
) => {
|
||||
if (!selectedTemplate) return
|
||||
|
||||
const updatedTemplate = {
|
||||
...selectedTemplate,
|
||||
layoutSettings,
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
setSelectedTemplate(updatedTemplate)
|
||||
updateData(updatedTemplate)
|
||||
}
|
||||
|
||||
// if (isLoading) {
|
||||
// return (
|
||||
// <div className="flex h-screen items-center justify-center">
|
||||
@@ -175,10 +190,10 @@ export const ElementsCreation: FC = () => {
|
||||
variant="link"
|
||||
size="sm"
|
||||
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
|
||||
variant="link"
|
||||
@@ -200,6 +215,7 @@ export const ElementsCreation: FC = () => {
|
||||
onElementAdd={handleElementAdd}
|
||||
onElementUpdate={handleElementUpdate}
|
||||
onElementDelete={handleElementDelete}
|
||||
onLayoutSettingsChange={handleLayoutSettingsChange}
|
||||
disabled={false}
|
||||
hasUnsavedChanges={hasUnsavedChanges}
|
||||
isSaving={isSaving}
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface ElementLayout {
|
||||
export interface FormLayoutSettings {
|
||||
gridSize: number // размер сетки для привязки (в пикселях)
|
||||
showGrid: boolean // показывать сетку
|
||||
snapToGrid: boolean // привязывать элементы к сетке
|
||||
}
|
||||
|
||||
export interface TemplateElement {
|
||||
|
||||
Reference in New Issue
Block a user