// // LNCommonInputPanel.swift // Gami // // Created by OneeChan on 2026/3/3. // import Foundation import UIKit import SnapKit class LNCommonInputPanel: LNPopupView { var maxInput = 0 private let textView = LNAutoSizeTextView() private var hideSend: Constraint? private let sendView = UIView() var handler: ((String) -> Void)? override init(frame: CGRect) { super.init(frame: frame) setupViews() } override func popup(_ targetView: UIView? = nil, animated: Bool = true) { super.popup(targetView, animated: animated) textView.becomeFirstResponder() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNCommonInputPanel { private func toSendText() { dismiss() let cleanedText = (textView.text ?? "").replacingOccurrences(of: "\n", with: "").trimmingCharacters(in: .whitespacesAndNewlines) guard !cleanedText.isEmpty else { return } handler?(cleanedText) } } extension LNCommonInputPanel: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { if maxInput > 0, textView.text.count > maxInput { textView.text = String(textView.text.prefix(maxInput)) } let showSend = !textView.text.isEmpty hideSend?.update(priority: showSend ? .low : .high) UIView.animate(withDuration: 0.2) { [weak self] in guard let self else { return } layoutIfNeeded() } } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { if text == "\n" { toSendText() return false } if maxInput > 0 { let currentText = textView.text ?? "" guard let swiftRange = Range(range, in: currentText) else { return true } let newText = currentText.replacingCharacters(in: swiftRange, with: text) if newText.count > maxInput { return false } } return true } } extension LNCommonInputPanel { private func setupViews() { container.layer.cornerRadius = 0 ignoreKeyboardToDismiss = true let inputContainer = UIView() container.addSubview(inputContainer) inputContainer.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalToSuperview().offset(10) make.bottom.equalToSuperview().offset(-5 + commonBottomInset) } let holder = UIView() holder.backgroundColor = .fill_2 holder.layer.cornerRadius = 19 holder.snp.makeConstraints { make in make.height.greaterThanOrEqualTo(38) } inputContainer.addSubview(holder) holder.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.leading.equalToSuperview().offset(12) hideSend = make.trailing.equalToSuperview().offset(-12).priority(.high).constraint } textView.backgroundColor = .clear textView.delegate = self textView.font = .body_m textView.textColor = .text_5 textView.returnKeyType = .send holder.addSubview(textView) textView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(10) make.centerY.equalToSuperview() make.top.greaterThanOrEqualToSuperview() } inputContainer.addSubview(sendView) sendView.snp.makeConstraints { make in make.leading.equalTo(holder.snp.trailing).offset(12) make.trailing.equalToSuperview().offset(-12).priority(.medium) make.bottom.equalToSuperview() } let sendButton = UIButton() sendButton.layer.cornerRadius = 16 sendButton.clipsToBounds = true sendButton.setTitle(.init(key: "A00305"), for: .normal) sendButton.setTitleColor(.text_1, for: .normal) sendButton.titleLabel?.font = .heading_h4 sendButton.setBackgroundImage(.primary_8, for: .normal) sendButton.contentEdgeInsets = .init(top: 0, left: 16, bottom: 0, right: 16) sendButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } toSendText() }), for: .touchUpInside) sendView.addSubview(sendButton) sendButton.snp.makeConstraints { make in make.height.equalTo(32) make.horizontalEdges.equalToSuperview() make.bottom.equalToSuperview().offset(-2) make.top.equalToSuperview() } } }