122 lines
3.1 KiB
Python
122 lines
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any, Optional
|
|
|
|
from ._input_handler import TextInputHandler
|
|
from .element import CursorOffset, Element
|
|
|
|
if TYPE_CHECKING:
|
|
from pydantic import TypeAdapter
|
|
|
|
from .styles.base import BaseStyle
|
|
|
|
|
|
class Input(TextInputHandler, Element):
|
|
label: Optional[str] = None
|
|
|
|
def __init__(
|
|
self,
|
|
label: Optional[str] = None,
|
|
placeholder: Optional[str] = None,
|
|
default: Optional[str] = None,
|
|
default_as_placeholder: bool = True,
|
|
required: bool = False,
|
|
required_message: Optional[str] = None,
|
|
password: bool = False,
|
|
inline: bool = False,
|
|
name: Optional[str] = None,
|
|
style: Optional[BaseStyle] = None,
|
|
validator: Optional[TypeAdapter] = None,
|
|
**metadata: Any,
|
|
):
|
|
self.name = name
|
|
self.label = label
|
|
self._placeholder = placeholder
|
|
self.default = default
|
|
self.default_as_placeholder = default_as_placeholder
|
|
self.required = required
|
|
self.password = password
|
|
self.inline = inline
|
|
|
|
self.text = ""
|
|
self.valid = None
|
|
self.required_message = required_message
|
|
self._validation_message: Optional[str] = None
|
|
self._validator: Optional[TypeAdapter] = validator
|
|
|
|
Element.__init__(self, style=style, metadata=metadata)
|
|
super().__init__()
|
|
|
|
@property
|
|
def placeholder(self) -> str:
|
|
if self.default_as_placeholder and self.default:
|
|
return self.default
|
|
|
|
return self._placeholder or ""
|
|
|
|
@property
|
|
def validation_message(self) -> Optional[str]:
|
|
if self._validation_message:
|
|
return self._validation_message
|
|
|
|
assert self.valid
|
|
|
|
return None
|
|
|
|
@property
|
|
def cursor_offset(self) -> CursorOffset:
|
|
top = 1 if self.inline else 2
|
|
|
|
left_offset = 0
|
|
|
|
if self.inline and self.label:
|
|
left_offset = len(self.label) + 1
|
|
|
|
return CursorOffset(top=top, left=self.cursor_left + left_offset)
|
|
|
|
@property
|
|
def should_show_cursor(self) -> bool:
|
|
return True
|
|
|
|
def on_blur(self):
|
|
self.on_validate()
|
|
|
|
def on_validate(self):
|
|
value = self.value.strip()
|
|
|
|
if not value and self.required:
|
|
self.valid = False
|
|
self._validation_message = self.required_message or "This field is required"
|
|
|
|
return
|
|
|
|
if self._validator:
|
|
from pydantic import ValidationError
|
|
|
|
try:
|
|
self._validator.validate_python(value)
|
|
except ValidationError as e:
|
|
self.valid = False
|
|
|
|
self._validation_message = e.errors()[0]["ctx"]["reason"]
|
|
|
|
return
|
|
|
|
self._validation_message = None
|
|
self.valid = True
|
|
|
|
@property
|
|
def value(self) -> str:
|
|
return self.text or self.default or ""
|
|
|
|
def ask(self) -> str:
|
|
from .container import Container
|
|
|
|
container = Container(style=self.style)
|
|
|
|
container.elements = [self]
|
|
|
|
container.run()
|
|
|
|
return self.value
|