UNPKG

decap-cms-core

Version:

Decap CMS core application, see decap-cms package for the main distribution.

222 lines (192 loc) 5.79 kB
import PropTypes from 'prop-types'; import { Component } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import styled from '@emotion/styled'; import { List } from 'immutable'; import { colors, Icon } from 'decap-cms-ui-default'; import NotesList from './NotesList'; import AddNoteForm from './AddNoteForm'; const NotesContainer = styled.div` height: 100%; display: flex; flex-direction: column; background-color: ${colors.background}; border-left: 1px solid ${colors.textFieldBorder}; `; const NotesHeader = styled.div` padding: 16px 24px; border-bottom: 1px solid ${colors.textFieldBorder}; background-color: ${colors.inputBackground}; display: flex; align-items: center; min-height: 60px; `; const NotesTitleGroup = styled.div` display: flex; align-items: center; gap: 8px; `; const NotesTitle = styled.h3` margin: 0; font-size: 16px; font-weight: 600; color: ${colors.text}; `; const NotesCount = styled.span` background-color: ${colors.controlLabel}; color: white; border-radius: 12px; padding: 2px 8px; font-size: 12px; font-weight: 500; `; const SourceLink = styled.a` color: ${colors.controlLabel}; text-decoration: none; font-size: 14px; display: flex; align-items: center; gap: 6px; &:hover { color: ${colors.text}; text-decoration: underline; } `; const SourceIcon = styled(Icon)` width: 16px; height: 16px; color: currentColor; `; const NotesContent = styled.div` flex: 1; display: flex; flex-direction: column; overflow: hidden; `; const EmptyState = styled.div` flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 40px 20px; color: ${colors.controlLabel}; text-align: center; `; const EmptyStateIcon = styled.div` font-size: 48px; margin-bottom: 16px; opacity: 0.5; `; const EmptyStateText = styled.p` font-size: 14px; margin: 0; line-height: 1.4; `; class EditorNotesPane extends Component { static propTypes = { notes: ImmutablePropTypes.list, onChange: PropTypes.func.isRequired, entry: ImmutablePropTypes.map.isRequired, collection: ImmutablePropTypes.map.isRequired, user: PropTypes.object, t: PropTypes.func, sourceUrl: PropTypes.string, // Link to the GitHub issue or source }; static defaultProps = { notes: List(), t: key => key, // Fallback translation function }; handleAddNote = content => { const { onChange, user } = this.props; const newNote = { content: content.trim(), author: user?.login || user?.name || 'Anonymous', resolved: false, }; onChange('ADD_NOTE', newNote); }; handleUpdateNote = (noteId, updates) => { const { onChange } = this.props; onChange('UPDATE_NOTE', { id: noteId, updates }); }; handleDeleteNote = noteId => { const { onChange } = this.props; onChange('DELETE_NOTE', { id: noteId }); }; handleToggleNoteResolution = noteId => { const { notes } = this.props; const notesList = notes && notes.size !== undefined ? notes : List(notes || []); const note = notesList.find(n => n.get('id') === noteId); const currentResolved = note ? note.get('resolved') : false; this.handleUpdateNote(noteId, { resolved: !currentResolved }); }; // Helper method to get the appropriate link text and icon type based on the source getSourceInfo = url => { // Check if URL is from GitHub if (url.includes('github.com')) { return { text: 'View in GitHub', iconType: 'github', // Using GitHub icon type }; } // TODO: Add support for other Git providers // Example for future contributors: // if (url.includes('gitlab.com')) { // return { text: 'View in GitLab', iconType: 'gitlab' }; // } // if (url.includes('bitbucket.org')) { // return { text: 'View in Bitbucket', iconType: 'bitbucket' }; // } // Default fallback return { text: 'View source', iconType: 'link', }; }; render() { const { notes, t } = this.props; const notesList = notes && notes.size !== undefined ? notes : List(notes || []); const notesCount = notesList.size; const unresolvedCount = notesList.filter(note => !note.get('resolved')).size; const sourceUrl = notesCount > 0 ? notesList.first()?.get('issueUrl') : null; const sourceInfo = sourceUrl ? this.getSourceInfo(sourceUrl) : null; return ( <NotesContainer> <NotesHeader> <NotesTitleGroup> <NotesTitle>{t('editor.editorNotesPane.title')}</NotesTitle> {notesCount > 0 && ( <NotesCount>{unresolvedCount > 0 ? unresolvedCount : notesCount}</NotesCount> )} {sourceInfo && ( <SourceLink href={sourceUrl} target="_blank" rel="noopener noreferrer"> <span>{sourceInfo.text}</span> <SourceIcon type={sourceInfo.iconType} /> </SourceLink> )} </NotesTitleGroup> </NotesHeader> <NotesContent> {notesCount === 0 ? ( <EmptyState> <EmptyStateIcon>📝</EmptyStateIcon> <EmptyStateText>{t('editor.editorNotesPane.emptyState')}</EmptyStateText> </EmptyState> ) : ( <NotesList notes={notesList} onUpdate={this.handleUpdateNote} onDelete={this.handleDeleteNote} onToggleResolution={this.handleToggleNoteResolution} user={this.props.user} t={t} /> )} <AddNoteForm onAdd={this.handleAddNote} t={t} /> </NotesContent> </NotesContainer> ); } } export default EditorNotesPane;