<commit_before>package doom.fs.renderer;

import haxe.ds.Option;
import js.html.InputElement;
import doom.html.Html.*;
import doom.core.VNode;
using thx.Arrays;
using thx.Options;

import fancy.search.Action;
import fancy.search.State;
import fancy.search.config.RendererConfig;

typedef Props<Sug, Value> = {
  state: State<Sug, String, Value>,
  cfg: RendererConfig<Sug, String, VNode>,
  dispatch: Action<Sug, String, Value> -> Void,
  placeholder: String
};

class Autocomplete {
  public static function render<Sug, Value>(props: Props<Sug, Value>) {
    var captureKeys = switch props.state.menu {
      case Open(Results(_), _): true;
      case _: false;
    };

    return div(["class" => "fs-search"], [
      renderInput(props.cfg.keys, props.cfg.classes, props.placeholder, props.dispatch, captureKeys, props.state.filter),
      renderMenu(props.cfg, props.dispatch, props.state)
    ]);
  }

  static function renderInput<Sug>(
    keys: KeyboardConfig,
    classes: ClassNameConfig,
    placeholder: String,
    dispatch,
    captureKeys: Bool,
    value: String) {
    return input([
      "class" => "fancify " + classes.input,
      "type" => "text",
      "placeholder" => placeholder,
      "value" => value,
      "focus" => function (el, _) {
        var inpt: InputElement = cast el;
        dispatch(SetFilter(inpt.value));
        dispatch(OpenMenu);
      },
      "blur" => function () dispatch(CloseMenu),
      "input" => function (el, _) {
        var inpt: InputElement = cast el;
        dispatch(SetFilter(inpt.value));
        dispatch(OpenMenu);
      },
      "keydown" => (captureKeys ? function (_, e: js.html.KeyboardEvent) {
        var keyWithModifier = dots.Keys.getKeyAndModifiers(e);

        if (keys.highlightUp(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChangeHighlight(Move(Up)));
        } else if (keys.highlightDown(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChangeHighlight(Move(Down)));
        } else if (keys.choose(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChooseCurrent);
        } else if (keys.close(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(CloseMenu);
        }
      } : null)
    ]);
  }

  // returns space-separated classes, like `"class-one class-two"`
  static function getContainerClassesForState<Sug>(classes: ClassNameConfig, state: MenuState<Sug>): String {
    var classArr: Array<String> = [classes.container].append(switch state {
      case Closed(Inactive): classes.containerClosed;
      case Closed(FailedCondition(_)): classes.containerNotAllowed;
      case Open(Loading, _): classes.containerLoading;
      case Open(NoResults, _): classes.containerNoResults;
      case Open(Failed, _): classes.containerFailed;
      case Open(Results(_), _): classes.containerOpen;
    });

    return classArr.join(" ");
  }

  static function renderMenu<Sug, Val>(cfg: RendererConfig<Sug, String, VNode>, dispatch, state: State<Sug, String, Val>) {
    var cls = getContainerClassesForState(cfg.classes, state.menu);

    return switch state.menu {
      case Closed(_): div(["class" => cls ]);
      case Open(Results(suggs), highlighted):
        div([ "class" => cls ], [
          ul([
            "class" => cfg.classes.list
          ], suggs.toArray().map(renderMenuItem.bind(state.config.sugEq, cfg, state.filter, dispatch, highlighted)))
        ]);
      case Open(_): div(["class" => cls ]);
    }
  }

  static function renderMenuItem<Sug>(eq, cfg: RendererConfig<Sug, String, VNode>, filter: String, dispatch, highlighted: Option<Sug>, item: Sug): VNode {
    var highlightClass = highlighted.cata("", function (h) {
      return eq(item, h) ? cfg.classes.itemHighlighted : "";
    });

    return li([
      "class" => cfg.classes.item + " " + highlightClass,<commit_msg>Add special rendering for failed-condition state<commit_after>package doom.fs.renderer;

import haxe.ds.Option;
import js.html.InputElement;
import doom.html.Html.*;
import doom.core.VNode;
using thx.Arrays;
using thx.Options;

import fancy.search.Action;
import fancy.search.State;
import fancy.search.config.RendererConfig;

typedef Props<Sug, Value> = {
  state: State<Sug, String, Value>,
  cfg: RendererConfig<Sug, String, VNode>,
  dispatch: Action<Sug, String, Value> -> Void,
  placeholder: String
};

class Autocomplete {
  public static function render<Sug, Value>(props: Props<Sug, Value>) {
    var captureKeys = switch props.state.menu {
      case Open(Results(_), _): true;
      case _: false;
    };

    return div(["class" => "fs-search"], [
      renderInput(props.cfg.keys, props.cfg.classes, props.placeholder, props.dispatch, captureKeys, props.state.filter),
      renderMenu(props.cfg, props.dispatch, props.state)
    ]);
  }

  static function renderInput<Sug>(
    keys: KeyboardConfig,
    classes: ClassNameConfig,
    placeholder: String,
    dispatch,
    captureKeys: Bool,
    value: String) {
    return input([
      "class" => "fancify " + classes.input,
      "type" => "text",
      "placeholder" => placeholder,
      "value" => value,
      "focus" => function (el, _) {
        var inpt: InputElement = cast el;
        dispatch(SetFilter(inpt.value));
        dispatch(OpenMenu);
      },
      "blur" => function () dispatch(CloseMenu),
      "input" => function (el, _) {
        var inpt: InputElement = cast el;
        dispatch(SetFilter(inpt.value));
        dispatch(OpenMenu);
      },
      "keydown" => (captureKeys ? function (_, e: js.html.KeyboardEvent) {
        var keyWithModifier = dots.Keys.getKeyAndModifiers(e);

        if (keys.highlightUp(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChangeHighlight(Move(Up)));
        } else if (keys.highlightDown(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChangeHighlight(Move(Down)));
        } else if (keys.choose(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(ChooseCurrent);
        } else if (keys.close(keyWithModifier)) {
          e.preventDefault();
          e.stopPropagation();
          dispatch(CloseMenu);
        }
      } : null)
    ]);
  }

  // returns space-separated classes, like `"class-one class-two"`
  static function getContainerClassesForState<Sug>(classes: ClassNameConfig, state: MenuState<Sug>): String {
    var classArr: Array<String> = [classes.container].append(switch state {
      case Closed(Inactive): classes.containerClosed;
      case Closed(FailedCondition(_)): classes.containerNotAllowed;
      case Open(Loading, _): classes.containerLoading;
      case Open(NoResults, _): classes.containerNoResults;
      case Open(Failed, _): classes.containerFailed;
      case Open(Results(_), _): classes.containerOpen;
    });

    return classArr.join(" ");
  }

  static function renderMenu<Sug, Val>(cfg: RendererConfig<Sug, String, VNode>, dispatch, state: State<Sug, String, Val>) {
    var cls = getContainerClassesForState(cfg.classes, state.menu);

    return switch state.menu {
      case Closed(Inactive): div(["class" => cls ]);
      case Closed(FailedCondition(msg)): div(["class" => cls ], [msg]);
      case Open(Results(suggs), highlighted):
        div([ "class" => cls ], [
          ul([
            "class" => cfg.classes.list
          ], suggs.toArray().map(renderMenuItem.bind(state.config.sugEq, cfg, state.filter, dispatch, highlighted)))
        ]);
      case Open(_): div(["class" => cls ]);
    }
  }

  static function renderMenuItem<Sug>(eq, cfg: RendererConfig<Sug, String, VNode>, filter: String, dispatch, highlighted: Option<Sug>, item: Sug): VNode {
    var highlightClass = highlighted.cata("", function (h) {
      return eq(item, h) ? cfg.classes.itemHighlighted : "";
    });

    return li([
      "class" => cfg.classes.item + " " + highlightClass,
{'fraction_non_alphanumeric': 0.1413016580054442, 'fraction_numerical': 0.0, 'mean_word_length': 3.1113936927772126, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 66, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1992983', 'n_tokens_mistral': 2428, 'n_tokens_neox': 2412, 'n_words': 521}