""" Textbox Layout Supports enhanced rationale/justification features: - min_chars: Minimum character count with counter display - show_char_count: Show character counter below textarea - collapsible: Start collapsed, expand on click - target_schema: Visual grouping with a preceding schema - placeholder: Placeholder text in the input """ import logging from potato.ai.ai_help_wrapper import get_ai_wrapper, get_dynamic_ai_help from .identifier_utils import ( safe_generate_layout, generate_element_identifier, generate_validation_attribute, escape_html_content, generate_layout_attributes ) logger = logging.getLogger(__name__) def generate_textbox_layout(annotation_scheme): """ Generate HTML for a textbox input interface. Args: annotation_scheme (dict): Configuration including: - name: Schema identifier - description: Display description - labels: Optional list of labels for multiple textboxes - label_requirement (dict): Optional validation settings - required (bool): Whether input is mandatory - textarea (dict): Optional textarea configuration - on (bool): Whether to use textarea instead of input - rows (int): Number of rows for textarea - cols (int): Number of columns for textarea - allow_paste (bool): Whether to allow pasting text - custom_css (dict): Optional CSS styling - min_chars (int): Minimum characters required - show_char_count (bool): Show character counter - collapsible (bool): Start collapsed, expand on click - target_schema (str): Name of schema this is a rationale for - placeholder (str): Placeholder text Returns: tuple: (html_string, key_bindings) html_string: Complete HTML for the textbox interface key_bindings: Empty list (no keyboard shortcuts) """ return safe_generate_layout(annotation_scheme, _generate_textbox_layout_internal) def _generate_textbox_layout_internal(annotation_scheme): """ Internal function to generate textbox layout after validation. """ logger.debug(f"Generating textbox layout for schema: {annotation_scheme['name']}") # Layout attributes for grid positioning layout_attrs = generate_layout_attributes(annotation_scheme) # Enhanced features min_chars = annotation_scheme.get("min_chars", 0) show_char_count = annotation_scheme.get("show_char_count", False) collapsible = annotation_scheme.get("collapsible", False) target_schema = annotation_scheme.get("target_schema", "") placeholder = escape_html_content(annotation_scheme.get("placeholder", "")) # CSS classes for target_schema grouping target_class = "shadcn-textbox-target-grouped" if target_schema else "" # Initialize form wrapper schema_name = annotation_scheme['name'] schematic = f"""
{get_ai_wrapper()}
""" # Collapsible wrapper if collapsible: schematic += f""" {escape_html_content(annotation_scheme["description"])}
""" else: schematic += f""" {escape_html_content(annotation_scheme["description"])} """ # Handle custom CSS if provided display_info = annotation_scheme.get("display_config", {}) custom_css = "" if "custom_css" in display_info: custom_css_parts = [] for k, v in display_info["custom_css"].items(): custom_css_parts.append(f"{k}: {v}") custom_css = "; ".join(custom_css_parts) # Set paste settings paste_setting = '' if "allow_paste" in annotation_scheme and annotation_scheme["allow_paste"] == False: paste_setting = 'onpaste="alert(\'Pasting is not allowed for the current study\');return false;"' # Handle multiple textboxes with different labels if "labels" not in annotation_scheme or annotation_scheme["labels"] == None: labels = ["text_box"] else: labels = annotation_scheme["labels"] # Generate input field(s) for each label for label in labels: # Generate consistent identifiers identifiers = generate_element_identifier(annotation_scheme["name"], label, "text") validation = generate_validation_attribute(annotation_scheme) # Determine if using textarea is_textarea = False textarea_attrs = "" # Check for multiline flag (new format) or textarea.on (old format) if annotation_scheme.get("multiline") or annotation_scheme.get("textarea", {}).get("on"): is_textarea = True # Use multiline config or fall back to textarea config if annotation_scheme.get("multiline"): rows = annotation_scheme.get("rows", "3") cols = annotation_scheme.get("cols", "40") else: rows = annotation_scheme["textarea"].get("rows", "3") cols = annotation_scheme["textarea"].get("cols", "40") textarea_attrs = f"rows='{rows}' cols='{cols}'" # Show label if not the default text_box label label_text = "" if label == "text_box" else label # Placeholder attribute placeholder_attr = f'placeholder="{placeholder}"' if placeholder else '' schematic += f"""
{f'' if label_text else ''} """ if is_textarea: # Render textarea for multiline input schematic += f""" """ else: # Render input for single-line text schematic += f""" """ # Character counter if show_char_count or min_chars: min_label = f"/{min_chars} min" if min_chars else "" schematic += f"""
0{min_label} characters
""" schematic += "
" # Close collapsible body if needed if collapsible: schematic += "
" schematic += "
" logger.debug(f"Generated textbox schematic for {annotation_scheme['name']}") logger.info(f"Successfully generated textbox layout for {annotation_scheme['name']} with {len(labels)} fields") return schematic, []