import SwiftUI // MARK: - General Text Input Component struct TextInputView: View { @Binding var content: String @FocusState var isContentFieldFocused: Bool let placeholder: String let maxCharacters: Int var body: some View { VStack(spacing: 8) { ZStack { // Input field body TextEditor(text: $content) .frame(minHeight: 120) .padding(8) .background(Color(.systemBackground)) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(isContentFieldFocused ? Color.blue : Color(.systemGray4), lineWidth: 1) ) .focused($isContentFieldFocused) .onChange(of: content) { newValue in // Limit maximum characters if newValue.count > maxCharacters { content = String(newValue.prefix(maxCharacters)) } } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("done".localized) { isContentFieldFocused = false } .foregroundColor(.blue) .font(.system(size: 16, weight: .medium)) } } // Placeholder text - top-left aligned if content.isEmpty && !isContentFieldFocused { VStack { HStack { Text(placeholder) .foregroundColor(.secondary) .font(.body) Spacer() } Spacer() } .padding(16) .allowsHitTesting(false) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } } // Character count and limit hints - below input field HStack { Spacer() // Pushes content to the right VStack(alignment: .trailing, spacing: 4) { // Character limit hint if content.count >= maxCharacters { HStack(spacing: 4) { Image(systemName: "exclamationmark.triangle") .font(.caption) .foregroundColor(.orange) Text("max_characters_reached".localized) .font(.caption) .foregroundColor(.orange) } } else if content.count >= Int(Double(maxCharacters) * 0.93) { HStack(spacing: 4) { Image(systemName: "info.circle") .font(.caption) .foregroundColor(.blue) Text("near_character_limit".localized) .font(.caption) .foregroundColor(.blue) } } // Character count Text(String(format: "character_count".localized, content.count, maxCharacters)) .font(.caption) .foregroundColor(getCharacterCountColor()) } } } } private func getCharacterCountColor() -> Color { if content.count >= maxCharacters { return .orange } else if content.count >= Int(Double(maxCharacters) * 0.93) { return .blue } else { return .secondary } } } #Preview { TextInputView( content: .constant(""), placeholder: "text_placeholder".localized, maxCharacters: 150 ) }