# MemoEditor

A multi-line text area component with advanced features including character counting, resizing options, and multiple display modes.

## Features

- **Multi-line Text Input**: Rich textarea with configurable rows and text wrapping
- **Character Counter**: Optional character count indicator with maximum length validation
- **Resize Options**: Configurable resize behavior (none, horizontal, vertical, both)
- **Multiple Variants**: Support for plain, outlined, and filled styles
- **Display Modes**: Edit and view modes with proper HTML formatting
- **Auto-resize Handling**: Automatic width calculation and resize observation
- **Clear Button**: Optional clear functionality for easy text removal
- **Form Integration**: Works seamlessly with forms and validation
- **Event Forwarding**: Comprehensive event handling and forwarding

## Installation

```bash
npm install @ticatec/uniface-element
```

## Usage

### Basic Usage

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let description = '';
</script>

<MemoEditor 
  bind:value={description}
  placeholder="Enter your description here..."
  variant="outlined"
  input$rows={4}
/>
```

### With Character Counter

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let notes = '';
</script>

<MemoEditor 
  bind:value={notes}
  variant="filled"
  input$rows={5}
  maxLength={500}
  showIndicator
  placeholder="Enter notes (max 500 characters)"
/>

<p>Current length: {notes.length}</p>
```

### Resizable Editor

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let content = '';
</script>

<MemoEditor 
  bind:value={content}
  variant="outlined"
  input$rows={6}
  resize="both"
  placeholder="This editor can be resized in both directions"
/>
```

### Read-only and View Modes

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  import { DisplayMode } from '@ticatec/uniface-element';
  
  let text = 'This is some sample text\nwith multiple lines\nfor demonstration.';
</script>

<!-- Read-only mode -->
<MemoEditor 
  value={text}
  readonly
  variant="outlined"
  input$rows={4}
/>

<!-- View mode (displays as formatted HTML) -->
<MemoEditor 
  value={text}
  displayMode={DisplayMode.View}
/>
```

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string` | `''` | The text content of the editor |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual style variant |
| `disabled` | `boolean` | `false` | Whether the editor is disabled |
| `readonly` | `boolean` | `false` | Whether the editor is read-only |
| `style` | `string` | `''` | Custom CSS styles |
| `class` | `string` | `''` | Additional CSS classes |
| `wrap` | `'hard' \| 'soft'` | `'hard'` | Text wrapping behavior |
| `resize` | `'none' \| 'horizontal' \| 'vertical' \| 'both'` | `'none'` | Resize handle behavior |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | Display mode (edit or view) |
| `input$rows` | `number` | `3` | Number of visible text lines |
| `showIndicator` | `boolean` | `false` | Whether to show character count indicator |
| `maxLength` | `number \| null` | `null` | Maximum number of characters allowed |

### Input Props

All props prefixed with `input$` are forwarded to the underlying textarea element:

| Prop | Type | Description |
|------|------|-------------|
| `input$placeholder` | `string` | Placeholder text |
| `input$maxLength` | `number` | Maximum character length |
| `input$minLength` | `number` | Minimum character length |
| `input$required` | `boolean` | Whether the field is required |
| `input$autofocus` | `boolean` | Whether to auto-focus on mount |
| `input$spellcheck` | `boolean` | Enable/disable spell checking |
| `input$autocomplete` | `string` | Autocomplete behavior |

### Events

The component forwards all standard textarea events:

- `on:input` - Text input events
- `on:change` - Value change events
- `on:focus` - Focus events
- `on:blur` - Blur events
- `on:keydown` - Keyboard events
- `on:keyup` - Keyboard events
- `on:keypress` - Keyboard events
- `on:compositionstart` - IME composition events
- `on:compositionend` - IME composition events
- `on:compositionupdate` - IME composition events

## Examples

### Comment Editor

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  import FormField from '@ticatec/uniface-element/form-field';
  
  let comment = '';
  let errors = {};
  
  function validateComment() {
    if (!comment.trim()) {
      errors.comment = 'Comment is required';
      return false;
    }
    
    if (comment.length < 10) {
      errors.comment = 'Comment must be at least 10 characters';
      return false;
    }
    
    errors.comment = null;
    return true;
  }
  
  function handleSubmit() {
    if (validateComment()) {
      console.log('Comment submitted:', comment);
    }
  }
</script>

<FormField label="Comment" required error={errors.comment}>
  <MemoEditor 
    bind:value={comment}
    variant="outlined"
    input$rows={4}
    maxLength={1000}
    showIndicator
    input$placeholder="Enter your comment here..."
    on:blur={validateComment}
  />
</FormField>

<button on:click={handleSubmit} disabled={!comment.trim()}>
  Submit Comment
</button>
```

### Article Editor

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let article = {
    title: '',
    content: '',
    summary: ''
  };
  
  let wordCount = 0;
  
  $: wordCount = article.content.split(/\s+/).filter(word => word.length > 0).length;
</script>

<div class="article-editor">
  <h2>Article Editor</h2>
  
  <div class="field-group">
    <label>Title</label>
    <input 
      bind:value={article.title} 
      placeholder="Enter article title"
      maxlength="100"
    />
  </div>
  
  <div class="field-group">
    <label>Summary</label>
    <MemoEditor 
      bind:value={article.summary}
      variant="filled"
      input$rows={3}
      maxLength={300}
      showIndicator
      input$placeholder="Brief summary of the article..."
    />
  </div>
  
  <div class="field-group">
    <label>Content</label>
    <MemoEditor 
      bind:value={article.content}
      variant="outlined"
      input$rows={15}
      resize="vertical"
      input$placeholder="Write your article content here..."
    />
    <div class="word-count">
      Words: {wordCount}
    </div>
  </div>
</div>

<style>
  .article-editor {
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
  }
  
  .field-group {
    margin-bottom: 1.5rem;
  }
  
  .field-group label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
  }
  
  .field-group input {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid #d1d5db;
    border-radius: 0.375rem;
    font-size: 1rem;
  }
  
  .word-count {
    text-align: right;
    font-size: 0.875rem;
    color: #6b7280;
    margin-top: 0.25rem;
  }
</style>
```

### Feedback Form

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  import FormField from '@ticatec/uniface-element/form-field';
  
  let feedback = {
    category: '',
    description: '',
    suggestions: ''
  };
  
  let isSubmitting = false;
  
  async function submitFeedback() {
    isSubmitting = true;
    
    try {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 2000));
      
      console.log('Feedback submitted:', feedback);
      
      // Reset form
      feedback = { category: '', description: '', suggestions: '' };
      alert('Thank you for your feedback!');
    } catch (error) {
      console.error('Failed to submit feedback:', error);
      alert('Failed to submit feedback. Please try again.');
    } finally {
      isSubmitting = false;
    }
  }
</script>

<form on:submit|preventDefault={submitFeedback}>
  <h2>Feedback Form</h2>
  
  <FormField label="Category" required>
    <select bind:value={feedback.category} required>
      <option value="">Select a category</option>
      <option value="bug">Bug Report</option>
      <option value="feature">Feature Request</option>
      <option value="general">General Feedback</option>
    </select>
  </FormField>
  
  <FormField label="Description" required>
    <MemoEditor 
      bind:value={feedback.description}
      variant="outlined"
      input$rows={5}
      maxLength={1000}
      showIndicator
      input$placeholder="Please describe your feedback in detail..."
      input$required
    />
  </FormField>
  
  <FormField label="Suggestions">
    <MemoEditor 
      bind:value={feedback.suggestions}
      variant="filled"
      input$rows={3}
      maxLength={500}
      showIndicator
      input$placeholder="Any suggestions for improvement? (optional)"
    />
  </FormField>
  
  <div class="form-actions">
    <button 
      type="submit" 
      disabled={isSubmitting || !feedback.category || !feedback.description}
    >
      {isSubmitting ? 'Submitting...' : 'Submit Feedback'}
    </button>
  </div>
</form>

<style>
  form {
    max-width: 600px;
    margin: 0 auto;
    padding: 2rem;
  }
  
  h2 {
    margin-bottom: 2rem;
    color: #1f2937;
  }
  
  select {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid #d1d5db;
    border-radius: 0.375rem;
    font-size: 1rem;
    background: white;
  }
  
  .form-actions {
    margin-top: 2rem;
    text-align: right;
  }
  
  button {
    padding: 0.75rem 2rem;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 0.375rem;
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.2s;
  }
  
  button:hover:not(:disabled) {
    background: #2563eb;
  }
  
  button:disabled {
    background: #9ca3af;
    cursor: not-allowed;
  }
</style>
```

### Chat Message Editor

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let message = '';
  let messages = [];
  
  function sendMessage() {
    if (message.trim()) {
      messages = [...messages, {
        id: Date.now(),
        text: message,
        timestamp: new Date().toLocaleTimeString()
      }];
      message = '';
    }
  }
  
  function handleKeydown(event) {
    if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
      event.preventDefault();
      sendMessage();
    }
  }
</script>

<div class="chat-container">
  <div class="messages">
    {#each messages as msg}
      <div class="message">
        <div class="message-text">{msg.text}</div>
        <div class="message-time">{msg.timestamp}</div>
      </div>
    {/each}
  </div>
  
  <div class="message-input">
    <MemoEditor 
      bind:value={message}
      variant="outlined"
      input$rows={3}
      maxLength={500}
      showIndicator
      input$placeholder="Type your message... (Ctrl+Enter to send)"
      on:keydown={handleKeydown}
    />
    
    <div class="input-actions">
      <button 
        on:click={sendMessage}
        disabled={!message.trim()}
      >
        Send
      </button>
    </div>
  </div>
</div>

<style>
  .chat-container {
    max-width: 500px;
    margin: 0 auto;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    overflow: hidden;
  }
  
  .messages {
    height: 300px;
    overflow-y: auto;
    padding: 1rem;
    background: #f9fafb;
  }
  
  .message {
    margin-bottom: 1rem;
    padding: 0.75rem;
    background: white;
    border-radius: 0.375rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  }
  
  .message-text {
    white-space: pre-wrap;
    margin-bottom: 0.25rem;
  }
  
  .message-time {
    font-size: 0.75rem;
    color: #6b7280;
    text-align: right;
  }
  
  .message-input {
    padding: 1rem;
    background: white;
    border-top: 1px solid #e5e7eb;
  }
  
  .input-actions {
    margin-top: 0.5rem;
    text-align: right;
  }
  
  button {
    padding: 0.5rem 1rem;
    background: #10b981;
    color: white;
    border: none;
    border-radius: 0.25rem;
    cursor: pointer;
    font-size: 0.875rem;
  }
  
  button:hover:not(:disabled) {
    background: #059669;
  }
  
  button:disabled {
    background: #d1d5db;
    cursor: not-allowed;
  }
</style>
```

## Styling

### CSS Variables

The MemoEditor component uses CSS variables for consistent theming:

```css
:root {
  --uniface-editor-border-color: #d1d5db;
  --uniface-editor-focus-border-color: #3b82f6;
  --uniface-editor-background: white;
  --uniface-editor-text-color: #374151;
  --uniface-editor-placeholder-color: #9ca3af;
  --uniface-editor-indicator-color: #6b7280;
  --uniface-editor-indicator-background: rgba(255, 255, 255, 0.9);
}
```

### Custom Styling

```css
.custom-memo-editor {
  --uniface-editor-border-color: #e2e8f0;
  --uniface-editor-focus-border-color: #2563eb;
  --uniface-editor-background: #f8fafc;
  border-radius: 8px;
  font-family: 'Monaco', 'Menlo', monospace;
}

.custom-memo-editor textarea {
  line-height: 1.6;
  font-size: 14px;
}

.custom-memo-editor .uniface-memo-indicator {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 4px;
  padding: 2px 8px;
}
```

### Variant Styles

```svelte
<!-- Outlined variant -->
<MemoEditor variant="outlined" />

<!-- Filled variant -->
<MemoEditor variant="filled" />

<!-- Plain variant -->
<MemoEditor variant="plain" />

<!-- Default variant -->
<MemoEditor />
```

## Advanced Features

### Auto-save Functionality

```svelte
<script>
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let content = '';
  let lastSaved = null;
  let saveStatus = 'saved'; // 'saving', 'saved', 'error'
  
  let saveTimeout;
  
  async function saveContent() {
    saveStatus = 'saving';
    
    try {
      // Simulate API save
      await new Promise(resolve => setTimeout(resolve, 1000));
      
      localStorage.setItem('draft-content', content);
      lastSaved = new Date();
      saveStatus = 'saved';
    } catch (error) {
      console.error('Save failed:', error);
      saveStatus = 'error';
    }
  }
  
  function handleInput() {
    saveStatus = 'saving';
    
    // Debounce auto-save
    clearTimeout(saveTimeout);
    saveTimeout = setTimeout(saveContent, 2000);
  }
  
  // Load saved content on mount
  import { onMount } from 'svelte';
  
  onMount(() => {
    const saved = localStorage.getItem('draft-content');
    if (saved) {
      content = saved;
    }
  });
</script>

<div class="auto-save-editor">
  <div class="editor-header">
    <h3>Document Editor</h3>
    <div class="save-status">
      {#if saveStatus === 'saving'}
        <span class="status saving">Saving...</span>
      {:else if saveStatus === 'saved'}
        <span class="status saved">
          Saved {lastSaved ? lastSaved.toLocaleTimeString() : ''}
        </span>
      {:else if saveStatus === 'error'}
        <span class="status error">Save failed</span>
      {/if}
    </div>
  </div>
  
  <MemoEditor 
    bind:value={content}
    variant="outlined"
    input$rows={15}
    resize="vertical"
    input$placeholder="Start typing your document..."
    on:input={handleInput}
  />
</div>

<style>
  .auto-save-editor {
    max-width: 800px;
    margin: 0 auto;
  }
  
  .editor-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1rem;
    padding: 0 0.5rem;
  }
  
  .editor-header h3 {
    margin: 0;
    color: #1f2937;
  }
  
  .save-status .status {
    font-size: 0.875rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
  }
  
  .status.saving {
    background: #fef3c7;
    color: #92400e;
  }
  
  .status.saved {
    background: #d1fae5;
    color: #065f46;
  }
  
  .status.error {
    background: #fee2e2;
    color: #991b1b;
  }
</style>
```

## Best Practices

### 1. Use Appropriate Row Counts

Set `input$rows` based on expected content length:

```svelte
<!-- Short descriptions -->
<MemoEditor input$rows={3} maxLength={200} />

<!-- Medium content -->
<MemoEditor input$rows={6} maxLength={1000} />

<!-- Long articles -->
<MemoEditor input$rows={15} resize="vertical" />
```

### 2. Implement Character Limits

Always set reasonable character limits with indicators:

```svelte
<MemoEditor 
  maxLength={500}
  showIndicator
  variant="outlined"
/>
```

### 3. Handle Validation

Provide clear validation feedback:

```svelte
<script>
  let content = '';
  let error = '';
  
  function validate() {
    if (content.length < 10) {
      error = 'Content must be at least 10 characters';
    } else {
      error = '';
    }
  }
</script>

<MemoEditor 
  bind:value={content}
  on:blur={validate}
  style={error ? 'border-color: #ef4444;' : ''}
/>

{#if error}
  <div class="error">{error}</div>
{/if}
```

### 4. Enable Resize for Long Content

Allow users to resize editors for better usability:

```svelte
<MemoEditor 
  resize="vertical"
  input$rows={8}
  placeholder="This editor can be resized vertically"
/>
```

## Accessibility Features

- **Keyboard Navigation**: Full keyboard support for text editing
- **Screen Reader Support**: Proper labeling and announcements
- **Focus Management**: Clear focus indicators and tab navigation
- **ARIA Attributes**: Appropriate roles and properties
- **Character Limit Announcements**: Screen reader feedback for character limits

## Browser Support

- **Modern Browsers**: Chrome, Firefox, Safari, Edge (latest versions)
- **Mobile Browsers**: iOS Safari, Chrome Mobile, Firefox Mobile
- **ResizeObserver**: Required for auto-width calculation (polyfill available)
- **Textarea Features**: All standard textarea functionality supported

## Related Components

- **TextEditor**: Single-line text input component
- **FormField**: Form field wrapper for labels and validation
- **ListBox**: List selection for text templates or snippets

## License

MIT License - see LICENSE file for details.