GrammarCheckerView.swift 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import SwiftUI
  2. struct GrammarCheckerView: View {
  3. @StateObject private var viewModel = GrammarCheckerViewModel()
  4. var body: some View {
  5. ScrollView(.vertical, showsIndicators: false) {
  6. VStack(alignment: .leading, spacing: 0) {
  7. header
  8. .padding(.bottom, 28)
  9. contentCard
  10. .padding(.bottom, 24)
  11. GradientButton(title: "Check Grammar") {
  12. viewModel.checkGrammar()
  13. }
  14. }
  15. .frame(maxWidth: .infinity, alignment: .topLeading)
  16. .padding(.horizontal, AppTheme.contentPadding)
  17. .padding(.top, 32)
  18. .padding(.bottom, 40)
  19. }
  20. .onChange(of: viewModel.text) { newValue in
  21. if newValue.count > GrammarCheckerViewModel.maxCharacters {
  22. viewModel.text = String(newValue.prefix(GrammarCheckerViewModel.maxCharacters))
  23. }
  24. }
  25. }
  26. private var header: some View {
  27. HStack(alignment: .top) {
  28. VStack(alignment: .leading, spacing: 8) {
  29. Text("Grammar Checker")
  30. .font(.system(size: 30, weight: .bold))
  31. .foregroundStyle(AppTheme.textPrimary)
  32. HStack(spacing: 0) {
  33. Text("Write ")
  34. .foregroundStyle(AppTheme.textSecondary)
  35. Text("better")
  36. .fontWeight(.semibold)
  37. .foregroundStyle(AppTheme.teal)
  38. Text(". Sound ")
  39. .foregroundStyle(AppTheme.textSecondary)
  40. Text("smarter")
  41. .fontWeight(.semibold)
  42. .foregroundStyle(AppTheme.teal)
  43. Text(".")
  44. .foregroundStyle(AppTheme.textSecondary)
  45. }
  46. .font(.system(size: 15))
  47. }
  48. Spacer()
  49. IconButton(iconName: "gearshape") {}
  50. }
  51. }
  52. private var contentCard: some View {
  53. VStack(alignment: .leading, spacing: 16) {
  54. Image("YourContentLogo")
  55. .resizable()
  56. .scaledToFit()
  57. .frame(height: 48)
  58. .accessibilityLabel("Your Content")
  59. ZStack(alignment: .bottomTrailing) {
  60. ThinCaretTextEditor(text: $viewModel.text)
  61. .font(.system(size: 14))
  62. .foregroundStyle(AppTheme.textPrimary)
  63. .padding(14)
  64. .frame(minHeight: 320)
  65. .background(Color.white)
  66. .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
  67. .overlay(
  68. RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius)
  69. .stroke(AppTheme.inputBorder, lineWidth: 1)
  70. )
  71. .overlay(alignment: .topLeading) {
  72. if viewModel.text.isEmpty {
  73. Text("Type or paste your text here and press the check button...")
  74. .font(.system(size: 14))
  75. .foregroundStyle(AppTheme.textMuted)
  76. .padding(.horizontal, 18)
  77. .padding(.vertical, 22)
  78. .allowsHitTesting(false)
  79. }
  80. }
  81. Text(viewModel.characterCountLabel)
  82. .font(.system(size: 11))
  83. .foregroundStyle(AppTheme.textMuted)
  84. .padding(.trailing, 16)
  85. .padding(.bottom, 12)
  86. }
  87. HStack(spacing: 8) {
  88. ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
  89. viewModel.pasteText()
  90. }
  91. ToolbarButton(title: "Upload File", iconName: "square.and.arrow.up") {}
  92. HStack(spacing: 6) {
  93. Image(systemName: "textformat")
  94. .font(.system(size: 12))
  95. Text(viewModel.wordCountLabel)
  96. .font(.system(size: 12, weight: .medium))
  97. }
  98. .foregroundStyle(AppTheme.textSecondary)
  99. .padding(.horizontal, 12)
  100. .padding(.vertical, 7)
  101. .background(Color.white)
  102. .clipShape(RoundedRectangle(cornerRadius: 8))
  103. .overlay(
  104. RoundedRectangle(cornerRadius: 8)
  105. .stroke(AppTheme.border, lineWidth: 1)
  106. )
  107. Spacer()
  108. IconButton(iconName: "eraser") {
  109. viewModel.clearText()
  110. }
  111. }
  112. }
  113. .padding(22)
  114. .background(AppTheme.cardBackground)
  115. .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
  116. .shadow(color: AppTheme.cardShadow, radius: 16, y: 6)
  117. }
  118. }