UTKInput Validation 기능 추가
This commit is contained in:
@@ -9,6 +9,15 @@ namespace UVC.UIToolkit
|
||||
/// 입력 필드 컴포넌트.
|
||||
/// Unity TextField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>플레이스홀더, 비밀번호, 멀티라인 지원</description></item>
|
||||
/// <item><description>스타일 변형 (Default, Filled, Outlined)</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (Submit/FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
@@ -17,39 +26,62 @@ namespace UVC.UIToolkit
|
||||
/// input.label = "이름";
|
||||
/// input.Placeholder = "이름을 입력하세요";
|
||||
/// input.OnValueChanged += (value) => Debug.Log($"입력값: {value}");
|
||||
///
|
||||
///
|
||||
/// // 비밀번호 입력 필드
|
||||
/// var password = new UTKInputField();
|
||||
/// password.label = "비밀번호";
|
||||
/// password.isPasswordField = true;
|
||||
///
|
||||
/// // 검증 오류 표시
|
||||
/// input.ErrorMessage = "이름은 필수입니다.";
|
||||
/// // 오류 제거
|
||||
/// input.ErrorMessage = "";
|
||||
///
|
||||
///
|
||||
/// // 변형 스타일
|
||||
/// input.Variant = UTKInputField.InputFieldVariant.Outlined;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var emailInput = new UTKInputField("이메일", "example@email.com");
|
||||
/// emailInput.ErrorMessage = "올바른 이메일 형식이 아닙니다.";
|
||||
/// emailInput.Validation = () => emailInput.Value.Contains("@");
|
||||
/// // → Submit(Enter) 또는 FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 필수 입력 검증
|
||||
/// var nameInput = new UTKInputField("이름");
|
||||
/// nameInput.ErrorMessage = "이름은 필수 항목입니다.";
|
||||
/// nameInput.Validation = () => !string.IsNullOrWhiteSpace(nameInput.Value);
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = nameInput.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// nameInput.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이)
|
||||
/// input.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// input.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 입력 필드 -->
|
||||
/// <utk:UTKInputField label="이름" />
|
||||
///
|
||||
///
|
||||
/// <!-- 플레이스홀더 -->
|
||||
/// <utk:UTKInputField label="이메일" placeholder="example@email.com" />
|
||||
///
|
||||
///
|
||||
/// <!-- 비밀번호 필드 -->
|
||||
/// <utk:UTKInputField label="비밀번호" is-password-field="true" />
|
||||
///
|
||||
///
|
||||
/// <!-- 여러 줄 입력 -->
|
||||
/// <utk:UTKInputField label="설명" multiline="true" />
|
||||
///
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKInputField label="이메일" error-message="올바른 이메일 형식이 아닙니다." />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKInputField label="읽기전용" is-enabled="false" value="수정 불가" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// ]]></code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKInputField : TextField, IDisposable
|
||||
@@ -63,6 +95,8 @@ namespace UVC.UIToolkit
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private InputFieldVariant _variant = InputFieldVariant.Default;
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -92,7 +126,7 @@ namespace UVC.UIToolkit
|
||||
set => textEdition.placeholder = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지</summary>
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
@@ -100,10 +134,19 @@ namespace UVC.UIToolkit
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
EnableInClassList("utk-input--error", !string.IsNullOrEmpty(value));
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-input--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. Submit/FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -199,26 +242,9 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<string>>(OnTextValueChanged);
|
||||
|
||||
RegisterCallback<FocusInEvent>(_ =>
|
||||
{
|
||||
EnableInClassList("utk-input--focused", true);
|
||||
OnFocused?.Invoke();
|
||||
});
|
||||
|
||||
RegisterCallback<FocusOutEvent>(_ =>
|
||||
{
|
||||
EnableInClassList("utk-input--focused", false);
|
||||
OnBlurred?.Invoke();
|
||||
});
|
||||
|
||||
RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return && !multiline)
|
||||
{
|
||||
OnSubmit?.Invoke(value);
|
||||
}
|
||||
}, TrickleDown.TrickleDown);
|
||||
RegisterCallback<FocusInEvent>(OnFocusIn);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -263,6 +289,28 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusIn(FocusInEvent evt)
|
||||
{
|
||||
EnableInClassList("utk-input--focused", true);
|
||||
OnFocused?.Invoke();
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
EnableInClassList("utk-input--focused", false);
|
||||
OnBlurred?.Invoke();
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return && !multiline)
|
||||
{
|
||||
OnSubmit?.Invoke(value);
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
@@ -283,6 +331,15 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택 영역 설정
|
||||
/// </summary>
|
||||
@@ -291,6 +348,57 @@ namespace UVC.UIToolkit
|
||||
textSelection.SelectAll();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-input--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-input--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-input__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private void UpdateVariant()
|
||||
{
|
||||
RemoveFromClassList("utk-input--default");
|
||||
@@ -318,10 +426,16 @@ namespace UVC.UIToolkit
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
UnregisterCallback<ChangeEvent<string>>(OnTextValueChanged);
|
||||
UnregisterCallback<FocusInEvent>(OnFocusIn);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
|
||||
OnValueChanged = null;
|
||||
OnFocused = null;
|
||||
OnBlurred = null;
|
||||
OnSubmit = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user