Package: @lilith/ui-dev-content Split from: lilith/ui.git or lilith/build.git Publish workflow: calls lilith/workflows/.forgejo/workflows/publish-npm.yml@main
98 lines
4.3 KiB
JavaScript
98 lines
4.3 KiB
JavaScript
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
/**
|
|
* TextEditorModal - Simple text editing modal for manual content editing
|
|
*
|
|
* Provides a textarea for directly editing content without AI transformers.
|
|
*/
|
|
import { useState, useEffect } from 'react';
|
|
import { Modal, ModalActions, useToast } from '@lilith/ui-feedback';
|
|
import { Button } from '@lilith/ui-primitives';
|
|
import { ZINDEX_LAYERS } from '@lilith/ui-zname';
|
|
import styled from '@lilith/ui-styled-components';
|
|
import { contentEditingRegistry } from '../core/ContentEditingRegistry';
|
|
// ============================================================================
|
|
// Styled Components
|
|
// ============================================================================
|
|
const EditorContainer = styled.div `
|
|
padding: ${props => props.theme.spacing.md};
|
|
`;
|
|
const StyledTextarea = styled.textarea `
|
|
width: 100%;
|
|
min-height: 200px;
|
|
padding: ${props => props.theme.spacing.md};
|
|
font-family: ${props => props.theme.typography.fontFamily.body};
|
|
font-size: 1rem;
|
|
line-height: 1.5;
|
|
border: 1px solid ${props => props.theme.colors.border.default};
|
|
border-radius: ${props => props.theme.borderRadius.md};
|
|
background: ${props => props.theme.colors.surface};
|
|
color: ${props => props.theme.colors.text.primary};
|
|
resize: vertical;
|
|
|
|
&:focus {
|
|
outline: none;
|
|
border-color: ${props => props.theme.colors.primary.main};
|
|
box-shadow: 0 0 0 2px ${props => props.theme.colors.primary.main}20;
|
|
}
|
|
`;
|
|
const IdentifierLabel = styled.div `
|
|
font-family: monospace;
|
|
font-size: 0.75rem;
|
|
color: ${props => props.theme.colors.text}80;
|
|
margin-bottom: ${props => props.theme.spacing.sm};
|
|
`;
|
|
// ============================================================================
|
|
// Component
|
|
// ============================================================================
|
|
/**
|
|
* Modal for manually editing text content
|
|
*/
|
|
export function TextEditorModal({ isOpen, onClose, handle, onSave, }) {
|
|
const { showToast } = useToast();
|
|
const [content, setContent] = useState('');
|
|
const [originalContent, setOriginalContent] = useState('');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
// Load current content when modal opens
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
loadContent();
|
|
}
|
|
}, [isOpen]);
|
|
const loadContent = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const source = contentEditingRegistry.getSource(handle.sourceId);
|
|
if (!source) {
|
|
throw new Error(`Source not found: ${handle.sourceId}`);
|
|
}
|
|
const data = await source.read(handle);
|
|
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
setContent(text);
|
|
setOriginalContent(text);
|
|
}
|
|
catch (error) {
|
|
showToast(`Failed to load content: ${error.message}`, 'error');
|
|
onClose();
|
|
}
|
|
finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
const handleSave = async () => {
|
|
try {
|
|
setIsSaving(true);
|
|
await onSave(content);
|
|
showToast('Content saved successfully!', 'success');
|
|
onClose();
|
|
}
|
|
catch (error) {
|
|
showToast(`Failed to save: ${error.message}`, 'error');
|
|
}
|
|
finally {
|
|
setIsSaving(false);
|
|
}
|
|
};
|
|
const hasChanges = content !== originalContent;
|
|
return (_jsxs(Modal, { "data-testid": "text-editor-modal", isOpen: isOpen, onClose: onClose, title: "Edit Content", maxWidth: "600px", zIndex: ZINDEX_LAYERS['high-priority'], children: [_jsxs(EditorContainer, { children: [_jsxs(IdentifierLabel, { children: [handle.sourceId, ":", handle.identifier] }), isLoading ? (_jsx("div", { style: { textAlign: 'center', padding: '2rem' }, children: "Loading..." })) : (_jsx(StyledTextarea, { "data-testid": "content-textarea", value: content, onChange: (e) => setContent(e.target.value), placeholder: "Enter content...", autoFocus: true }))] }), _jsxs(ModalActions, { children: [_jsx(Button, { "data-testid": "cancel-edit", variant: "secondary", onClick: onClose, disabled: isSaving, children: "Cancel" }), _jsx(Button, { "data-testid": "save-content", variant: "primary", onClick: handleSave, disabled: isLoading || isSaving || !hasChanges, children: isSaving ? 'Saving...' : 'Save' })] })] }));
|
|
}
|