<commit_before>define(
  'ephox.alloy.api.ui.FormInput',

  [
    'ephox.alloy.api.behaviour.Composing',
    'ephox.alloy.api.behaviour.Representing',
    'ephox.alloy.api.ui.CompositeBuilder',
    'ephox.alloy.api.ui.Input',
    'ephox.alloy.parts.PartType',
    'ephox.boulder.api.FieldSchema',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option'
  ],

  function (Composing, Representing, CompositeBuilder, Input, PartType, FieldSchema, Fun, Option) {
    var schema = [
      // FieldSchema.strict('components'),
      // FieldSchema.option('placeholder')
      // If the current value is empty, on focus, take the value of the placeholder
      // FieldSchema.defaulted('stickyPlaceholder', false),
      // FieldSchema.defaulted('inline', true)
    ];

    var partTypes = [
      PartType.optional(
        { build: Fun.identity },
        'label',
        '<alloy.input.label>',
        Fun.constant({ }),
        Fun.constant({ })
      ),
      PartType.internal(
        Input,
        'field',
        '<alloy.input.field>',
        Fun.constant({ }),
        Fun.constant({ })
      )
    ];

    var build = function (spec) {
      console.log('spec', spec);
      return CompositeBuilder.build('form-input', schema, partTypes, make, spec);
    };

    var make = function (detail, components, spec, externals) {
      return {
        uiType: 'custom',
        dom: {
          tag: 'span'
        },
        components: components,
        behaviours: {
          composing: {
            find: function (container) {
              return container.getSystem().getByUid(detail.partUids().field).fold(Option.none, Option.some);
            }
          },
          representing:  {
            initialValue: { value: '', text: '' },
            store: {
              mode: 'manual',
              getValue: function (container) {
                return Composing.getCurrent(container).bind(function (current) {
                  return Representing.getValue(current);
                });
              },
              setValue: function (container, newValue) {
                Composing.getCurrent(container).each(function (current) {
                  Representing.setValue(current, newValue);
                });
              }
            }
          }
        }
      };
    };

    var parts = PartType.generate('form-input', partTypes);

    return {
      build: build,
      parts: Fun.constant(parts)
    };
  }
);<commit_msg>VAN-44-wheel-hackery-apis-2: form input with labels<commit_after>define(
  'ephox.alloy.api.ui.FormInput',

  [
    'ephox.alloy.api.SystemEvents',
    'ephox.alloy.api.behaviour.Composing',
    'ephox.alloy.api.behaviour.Representing',
    'ephox.alloy.api.ui.CompositeBuilder',
    'ephox.alloy.api.ui.Input',
    'ephox.alloy.construct.EventHandler',
    'ephox.alloy.parts.PartType',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.Objects',
    'ephox.epithet.Id',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option',
    'ephox.sugar.api.Attr'
  ],

  function (SystemEvents, Composing, Representing, CompositeBuilder, Input, EventHandler, PartType, FieldSchema, Objects, Id, Fun, Option, Attr) {
    var schema = [
      FieldSchema.defaulted('prefix', 'form-input')
      // FieldSchema.strict('components'),
      // FieldSchema.option('placeholder')
      // If the current value is empty, on focus, take the value of the placeholder
      // FieldSchema.defaulted('stickyPlaceholder', false),
      // FieldSchema.defaulted('inline', true)
    ];

    var partTypes = [
      PartType.optional(
        { build: Fun.identity },
        'label',
        '<alloy.input.label>',
        Fun.constant({ }),
        Fun.constant({ })
      ),
      PartType.internal(
        Input,
        'field',
        '<alloy.input.field>',
        Fun.constant({ }),
        Fun.constant({ })
      )
    ];

    var build = function (spec) {
      console.log('spec', spec);
      return CompositeBuilder.build('form-input', schema, partTypes, make, spec);
    };

    var make = function (detail, components, spec, externals) {
      return {
        uiType: 'custom',
        dom: {
          tag: 'span'
        },
        components: components,
        behaviours: {
          composing: {
            find: function (container) {
              return container.getSystem().getByUid(detail.partUids().field).fold(Option.none, Option.some);
            }
          },
          representing:  {
            initialValue: { value: '', text: '' },
            store: {
              mode: 'manual',
              getValue: function (container) {
                return Composing.getCurrent(container).bind(function (current) {
                  return Representing.getValue(current);
                });
              },
              setValue: function (container, newValue) {
                Composing.getCurrent(container).each(function (current) {
                  Representing.setValue(current, newValue);
                });
              }
            }
          }
        },

        events: Objects.wrap(
          SystemEvents.systemInit(),
          EventHandler.nu({
            run: function (component) {
              var system = component.getSystem();
              system.getByUid(detail.partUids().label).each(function (label) {
                system.getByUid(detail.partUids().field).each(function (field) {
                  var id = Id.generate(detail.prefix());
                              
                  // TODO: Find a nicer way of doing this.
                  Attr.set(label.element(), 'for', id);
                  Attr.set(field.element(), 'id', id);    
                });
              });          
            }
          })
        )
      };
    };

    var parts = PartType.generate('form-input', partTypes);

    return {
      build: build,
      parts: Fun.constant(parts)
    };
  }
);
{'fraction_non_alphanumeric': 0.1368260427263479, 'fraction_numerical': 0.000508646998982706, 'mean_word_length': 1.79441023211748, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, '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': '967033', 'n_tokens_mistral': 1704, 'n_tokens_neox': 1694, 'n_words': 293}