<commit_before>﻿import * as React from 'react'
import * as moment from 'moment'
import { Dic, areEqual, classes } from '../Globals'
import { FilterOptionParsed, QueryDescription, QueryToken, SubTokensOptions, filterOperations, isList, FilterOperation, FilterConditionOptionParsed, FilterGroupOptionParsed, isFilterGroupOptionParsed, hasAnyOrAll, getTokenParents, isPrefix, FilterConditionOption } from '../FindOptions'
import { SearchMessage } from '../Signum.Entities'
import { ValueLine, EntityLine, EntityCombo } from '../Lines'
import { Binding, IsByAll, getTypeInfos, toMomentFormat } from '../Reflection'
import { TypeContext } from '../TypeContext'
import QueryTokenBuilder from './QueryTokenBuilder'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FilterGroupOperation } from '../Signum.Entities.DynamicQuery';
import "./FilterBuilder.css"

interface FilterBuilderProps {
  filterOptions: FilterOptionParsed[];
  subTokensOptions: SubTokensOptions;
  queryDescription: QueryDescription;
  onTokenChanged?: (token: QueryToken | undefined) => void;
  lastToken?: QueryToken;
  onFiltersChanged?: (filters: FilterOptionParsed[]) => void;
  onHeightChanged?: () => void;
  readOnly?: boolean;
  title?: React.ReactNode;
  renderValue?: (fc: FilterConditionComponent) => React.ReactElement<any> | undefined;
}

export default class FilterBuilder extends React.Component<FilterBuilderProps>{
  handlerNewFilter = (e: React.MouseEvent<any>, isGroup: boolean) => {
    e.preventDefault();
    var lastToken = this.props.lastToken;

    this.props.filterOptions.push(isGroup ?
      {
        groupOperation: "Or",
        token: lastToken && hasAnyOrAll(lastToken) ? getTokenParents(lastToken).filter(a => a.queryTokenType == "AnyOrAll").lastOrNull() : undefined,
        filters: [],
        frozen: false,
      } as FilterGroupOptionParsed :
      {
        token: this.props.lastToken,
        operation: lastToken && (filterOperations[lastToken.filterType!] || []).firstOrNull() || undefined,
        value: undefined,
        frozen: false
      } as FilterConditionOptionParsed);

    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);

    this.forceUpdate(() => this.handleHeightChanged());
  };

  handlerDeleteFilter = (filter: FilterOptionParsed) => {
    this.props.filterOptions.remove(filter);
    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);
    this.forceUpdate(() => this.handleHeightChanged());
  };

  handleFilterChanged = () => {
    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);
  };

  handleHeightChanged = () => {
    if (this.props.onHeightChanged)
      this.props.onHeightChanged();
  }

  render() {


    return (
      <fieldset className="form-xs">
        {this.props.title && <legend>{this.props.title}</legend>}
        <div className="sf-filters-list table-responsive" style={{ overflowX: "visible" }}>
          <table className="table-sm">
            <thead>
              <tr>
                <th style={{ minWidth: "24px" }}></th>
                <th>{SearchMessage.Field.niceToString()}</th>
                <th>{SearchMessage.Operation.niceToString()}</th>
                <th style={{ paddingRight: "20px" }}>{SearchMessage.Value.niceToString()}</th>
              </tr>
            </thead>
            <tbody>
              {this.props.filterOptions.map((f, i) => isFilterGroupOptionParsed(f) ?
                <FilterGroupComponent key={i} filterGroup={f} readOnly={Boolean(this.props.readOnly)} onDeleteFilter={this.handlerDeleteFilter}
                  prefixToken={undefined}
                  subTokensOptions={this.props.subTokensOptions} queryDescription={this.props.queryDescription}
                  onTokenChanged={this.props.onTokenChanged} onFilterChanged={this.handleFilterChanged}
                  lastToken={this.props.lastToken} onHeightChanged={this.handleHeightChanged} renderValue={this.props.renderValue} /> :
                <FilterConditionComponent key={i} filter={f} readOnly={Boolean(this.props.readOnly)} onDeleteFilter={this.handlerDeleteFilter}
                  prefixToken={undefined}
                  subTokensOptions={this.props.subTokensOptions} queryDescription={this.props.queryDescription}
                  onTokenChanged={this.props.onTokenChanged} onFilterChanged={this.handleFilterChanged} renderValue={this.props.renderValue} />
              )}
              {!this.props.readOnly &&
                <tr >
                  <td colSpan={4}>
                    <a href="#" title={SearchMessage.AddFilter.niceToString()}
                      className="sf-line-button sf-create"
                      onClick={e => this.handlerNewFilter(e, false)}>
                      <FontAwesomeIcon icon="plus" className="sf-create" />&nbsp;{SearchMessage.AddFilter.niceToString()}
                    </a>
                    <a href="#" title={SearchMessage.AddGroup.niceToString()}
                      className="sf-line-button sf-create ml-3"
                      onClick={e => this.handlerNewFilter(e, true)}>
                      <FontAwesomeIcon icon="plus" className="sf-create" />&nbsp;{SearchMessage.AddGroup.niceToString()}
                    </a>
                  </td>
                </tr>
              }
            </tbody>
          </table>
        </div>
      </fieldset>
    );<commit_msg>use css margin instead of spaces.<commit_after>﻿import * as React from 'react'
import * as moment from 'moment'
import { Dic, areEqual, classes } from '../Globals'
import { FilterOptionParsed, QueryDescription, QueryToken, SubTokensOptions, filterOperations, isList, FilterOperation, FilterConditionOptionParsed, FilterGroupOptionParsed, isFilterGroupOptionParsed, hasAnyOrAll, getTokenParents, isPrefix, FilterConditionOption } from '../FindOptions'
import { SearchMessage } from '../Signum.Entities'
import { ValueLine, EntityLine, EntityCombo } from '../Lines'
import { Binding, IsByAll, getTypeInfos, toMomentFormat } from '../Reflection'
import { TypeContext } from '../TypeContext'
import QueryTokenBuilder from './QueryTokenBuilder'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FilterGroupOperation } from '../Signum.Entities.DynamicQuery';
import "./FilterBuilder.css"

interface FilterBuilderProps {
  filterOptions: FilterOptionParsed[];
  subTokensOptions: SubTokensOptions;
  queryDescription: QueryDescription;
  onTokenChanged?: (token: QueryToken | undefined) => void;
  lastToken?: QueryToken;
  onFiltersChanged?: (filters: FilterOptionParsed[]) => void;
  onHeightChanged?: () => void;
  readOnly?: boolean;
  title?: React.ReactNode;
  renderValue?: (fc: FilterConditionComponent) => React.ReactElement<any> | undefined;
}

export default class FilterBuilder extends React.Component<FilterBuilderProps>{
  handlerNewFilter = (e: React.MouseEvent<any>, isGroup: boolean) => {
    e.preventDefault();
    var lastToken = this.props.lastToken;

    this.props.filterOptions.push(isGroup ?
      {
        groupOperation: "Or",
        token: lastToken && hasAnyOrAll(lastToken) ? getTokenParents(lastToken).filter(a => a.queryTokenType == "AnyOrAll").lastOrNull() : undefined,
        filters: [],
        frozen: false,
      } as FilterGroupOptionParsed :
      {
        token: this.props.lastToken,
        operation: lastToken && (filterOperations[lastToken.filterType!] || []).firstOrNull() || undefined,
        value: undefined,
        frozen: false
      } as FilterConditionOptionParsed);

    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);

    this.forceUpdate(() => this.handleHeightChanged());
  };

  handlerDeleteFilter = (filter: FilterOptionParsed) => {
    this.props.filterOptions.remove(filter);
    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);
    this.forceUpdate(() => this.handleHeightChanged());
  };

  handleFilterChanged = () => {
    if (this.props.onFiltersChanged)
      this.props.onFiltersChanged(this.props.filterOptions);
  };

  handleHeightChanged = () => {
    if (this.props.onHeightChanged)
      this.props.onHeightChanged();
  }

  render() {


    return (
      <fieldset className="form-xs">
        {this.props.title && <legend>{this.props.title}</legend>}
        <div className="sf-filters-list table-responsive" style={{ overflowX: "visible" }}>
          <table className="table-sm">
            <thead>
              <tr>
                <th style={{ minWidth: "24px" }}></th>
                <th>{SearchMessage.Field.niceToString()}</th>
                <th>{SearchMessage.Operation.niceToString()}</th>
                <th style={{ paddingRight: "20px" }}>{SearchMessage.Value.niceToString()}</th>
              </tr>
            </thead>
            <tbody>
              {this.props.filterOptions.map((f, i) => isFilterGroupOptionParsed(f) ?
                <FilterGroupComponent key={i} filterGroup={f} readOnly={Boolean(this.props.readOnly)} onDeleteFilter={this.handlerDeleteFilter}
                  prefixToken={undefined}
                  subTokensOptions={this.props.subTokensOptions} queryDescription={this.props.queryDescription}
                  onTokenChanged={this.props.onTokenChanged} onFilterChanged={this.handleFilterChanged}
                  lastToken={this.props.lastToken} onHeightChanged={this.handleHeightChanged} renderValue={this.props.renderValue} /> :
                <FilterConditionComponent key={i} filter={f} readOnly={Boolean(this.props.readOnly)} onDeleteFilter={this.handlerDeleteFilter}
                  prefixToken={undefined}
                  subTokensOptions={this.props.subTokensOptions} queryDescription={this.props.queryDescription}
                  onTokenChanged={this.props.onTokenChanged} onFilterChanged={this.handleFilterChanged} renderValue={this.props.renderValue} />
              )}
              {!this.props.readOnly &&
                <tr >
                  <td colSpan={4}>
                    <a href="#" title={SearchMessage.AddFilter.niceToString()}
                      className="sf-line-button sf-create"
                      onClick={e => this.handlerNewFilter(e, false)}>
                      <FontAwesomeIcon icon="plus" className="sf-create mr-1" />{SearchMessage.AddFilter.niceToString()}
                    </a>
                    <a href="#" title={SearchMessage.AddGroup.niceToString()}
                      className="sf-line-button sf-create ml-3"
                      onClick={e => this.handlerNewFilter(e, true)}>
                      <FontAwesomeIcon icon="plus" className="sf-create mr-1" />{SearchMessage.AddGroup.niceToString()}
                    </a>
                  </td>
                </tr>
              }
            </tbody>
          </table>
        </div>
      </fieldset>
    );
{'fraction_non_alphanumeric': 0.13031963470319635, 'fraction_numerical': 0.0012785388127853881, 'mean_word_length': 2.9040998217468807, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 105, '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': '7244201', 'n_tokens_mistral': 2974, 'n_tokens_neox': 2943, 'n_words': 523}