<div id="input-container" class="input">
    <div class="input__line"> <input class="input__input" id="input" name="input" disabled data-input-type="text" value="This input is disabled" type="text" /></div>

</div>
{% set id = id ??? html_id('input') %}
{% set type = type ?? 'text' %}
{% set dateType = type == 'date' or type == 'datetime-local' or type == 'time' %}
{% set value = value ?? null %}
{% set placeholder = placeholder ?? null %}
{% set autocomplete = autocomplete ?? null %}
{% set required = required ?? false %}
{% set disabled = disabled ?? false %}
{% set invalid = invalid ?? false %}
{% set multiple = multiple ?? false %}
{% set readonly = readonly ?? false %}
{% set name = name ?? null %}
{% set tel = type == 'tel' ? tel|default %}
{% set limitType = limitType ?? null %}
{% set limit = limit ?? null -%}

{% set inputAttributes = {
  class: 'input__input',
  id: id,
  name: name,
  autocomplete: autocomplete,
  autofocus: autofocus ?? false,
  multiple: multiple,
  required: required ? true : false,
  'aria-describedby': describedBy ?? false,
  'aria-invalid': invalid ? 'true' : null,
  'aria-required': required ? 'true' : null,
  disabled: disabled ? true : false,
  readonly: readonly ? true : false,
  'data-input-type': type,
} -%}

<div {{ html_attributes({
  id: containerId ??? ('container' | namespaceInputId(id)),
  class: 'input',
}, attrs ?? {}) }}>
  <div class="input__line">
    {%- if icon|default %}
      <div class="input__icon">
        {% include '@icon' with {
          icon: icon,
        } only %}
      </div>
    {% endif %}

    {%- if prependText|default -%}
      <div class="input__text">
        {{- prependText -}}
      </div>
    {%- endif -%}

    {% switch type %}
      {%- case 'textarea' %}
        <textarea {{ html_attributes(inputAttributes | merge({
          rows: rows ?? 4,
          placeholder: placeholder,
          maxlength: maxlength ?? false,
          'data-input-limit': limit and limitType ? 'limit' | namespaceInputId(id),
          'data-input-limit-count': limit and limitType ? limit,
          'data-input-limit-type': limit and limitType ? limitType,
        }), inputAttrs ?? {}) }}>{{ value }}</textarea>

      {%- case 'select' %}
        <select {{ html_attributes(inputAttributes, inputAttrs ?? {}) }}>
          {% set hasOptgroups = false %}

          {%- if required and selectValue is defined and selectValue %}
            <option class="input__option input__option--select" disabled selected value="">
              {{- selectValue -}}
            </option>
          {% endif -%}

          {% if resetValue is defined and resetValue %}
            <option class="input__option input__option--reset" value="">
              {{- resetValue -}}
            </option>
          {% endif %}

          {%- for option in options %}
            {% if option.optgroup is defined and option.optgroup %}
              {% if hasOptgroups %}
                </optgroup>
              {% else %}
                {% set hasOptgroups = true %}
              {% endif %}

              <optgroup class="input__option-group" label="{{ option.optgroup }}">
            {% else %}
              <option {{ html_attributes({
                class: 'input__option',
                value: option.value,
                selected: option.selected ?? value == option.value,
                disabled: option.disabled ?? false,
              }) }}>
                {{- option.label -}}
              </option>
            {% endif %}
          {% endfor -%}

          {% if hasOptgroups %}
            </optgroup>
          {% endif %}
        </select>

        {%- if not multiple %}
          <div class="input__icon input__icon--select">
            {% include '@icon' with {
              icon: 'arrow-down',
            } only %}
          </div>
        {% endif %}

      {%- case 'tel-international' %}
        <input {{ html_attributes({
          type: 'hidden',
          name: 'country' | namespaceInputName(name),
          id: 'country' | namespaceInputId(id),
          value: country ?? null,
        }) }}>

        <input {{ html_attributes(inputAttributes | merge({
          name: 'phoneNumber' | namespaceInputName(name),
          value: phoneNumber ?? value ?? null,
          type: 'tel',
          placeholder: placeholder,
          inputmode: inputmode ?? 'tel',
          spellcheck: spellcheck ?? 'false',
          'data-input-tel': {
            initialCountry: country ?? null,
            countryOrder: countryOrder ?? null,
            onlyCountries: onlyCountries ?? null,
            countryInput: 'country' | namespaceInputId(id),
            i18n: {
              selectedCountryAriaLabel: 'Selected country' | t('site'),
              noCountrySelected: 'No country selected' | t('site'),
              countryListAriaLabel: 'List of countries' | t('site'),
              searchPlaceholder: 'Search for country' | t('site'),
              zeroSearchResults: 'No results found' | t('site'),
              oneSearchResult: '1 result found' | t('site'),
              multipleSearchResults: '${count} results found' | t('site'),
            },
          },
        }), inputAttrs ?? {}) }} />

      {%- default %}
        <input {{ html_attributes(inputAttributes | merge({
          value: type != 'file' ? value,
          type: type,
          placeholder: placeholder,
          maxlength: maxlength ?? false,
          min: min ?? false,
          max: max ?? false,
          step: step ?? false,
          multiple: multiple ?? false,
          accept: accept ?? false,
          inputmode: inputmode ?? false,
          spellcheck: type == 'password' ? 'false' : (spellcheck ?? false),
          'data-input-date': dateType,
          'data-input-empty': dateType ? not value or value == '',
          'data-input-no-spinners': type == 'number' and noSpinners|default,
          'data-input-limit': limit and limitType ? 'limit' | namespaceInputId(id),
          'data-input-limit-count': limit and limitType ? limit,
          'data-input-limit-type': limit and limitType ? limitType,
          'data-input-select-on-click': selectOnClick ?? false,
        }), inputAttrs ?? {}) }} />

        {%- if dateType %}
          <button class="input__picker" type="button">
            {% include '@icon' with {
              icon: 'calendar',
            } only %}
          </button>
        {% endif %}

    {%- endswitch -%}

    {%- if appendText|default -%}
      <div class="input__text">
        {{- appendText -}}
      </div>
    {%- endif -%}
  </div>

  {% if type == 'file' and files|default %}
    <ul class="input__uploads">
      {% for file in files %}
        <li class="input__pill">{{ file }}</li>
      {% endfor %}
    </ul>
  {% endif %}

  {%- if limit and limitType and (type == 'textarea' or type == 'text') -%}
    <div class="input__limit">
      <span class="input__pill" id="{{ 'limit' | namespaceInputId(id) }}">
        {%- if limitType == 'words' -%}
          {{ 'Words left: **{words}**' | t('site', {
            words: limit - (value | default('') | words | length),
          }) | markdown }}
        {%- elseif limitType == 'characters' -%}
          {{ 'Characters left: **{chars}**' | t('site', {
            chars: limit - (value | default('') | length),
          }) | markdown }}
        {%- endif -%}
      </span>
    </div>
  {%- endif -%}
</div>
{
  "id": "input",
  "name": "input",
  "type": "text",
  "value": "This input is disabled",
  "disabled": true
}
  • Content:
    @use 'layers';
    @use 'a11y';
    
    .input {
      --_input-border-width: var(--input-border-width, 1px);
      --_input-min-block-size: var(--input-min-block-size, 4.4rem);
      --_input-padding-block: calc((var(--_input-min-block-size) - 1lh) / 2 - var(--_input-border-width));
      --_input-padding-inline: var(--input-padding-inline, 2.2rem);
    
      background-color: var(--input-background-color, var(--color-white));
      border-color: var(--input-border-color, var(--color-gray-400));
      border-radius: var(--input-border-radius, calc(var(--_input-min-block-size) / 2));
      border-style: var(--input-border-style, solid);
      border-width: var(--_input-border-width);
      color: var(--input-color, var(--color-steel-700));
      display: flex;
      flex-direction: column;
      font-size: var(--input-font-size, 1.6rem);
      line-height: var(--input-line-height, var(--line-height-regular));
      min-inline-size: var(--input-min-inline-size, 20rem);
      opacity: var(--input-opacity, 1);
      touch-action: manipulation;
      transition-property: background-color, border-color, color, opacity;
    
      &:has(.input__input:focus-visible) {
        --a11y-outline-offset: -1px;
        --a11y-outline-color: var(--color-gray-800);
    
        background-color: var(--input-background-color--engaged, var(--color-white));
        border-color: var(--input-border-color--engaged, var(--color-gray-800));
        color: var(--input-color--engaged, var(--color-steel-700));
    
        @include a11y.outline();
      }
    
      &:has(.input__input[disabled]) {
        filter: grayscale(1);
        opacity: 0.2;
        pointer-events: none;
        user-select: none;
      }
    
      &:has(.input__input[data-input-type='select'], .input__input[data-input-type='color']) {
        cursor: pointer;
      }
    }
    
    .input__line {
      align-items: center;
      column-gap: calc(var(--_input-padding-inline) / 2);
      display: flex;
      padding-inline: var(--_input-padding-inline);
    
      &:has(.iti) {
        padding-inline-start: 0;
      }
    
      :is(.iti) {
        position: relative;
        z-index: #{layers.position('dropdown')};
      }
    }
    
    .input__icon {
      --icon-size: var(--input-icon-size, 2rem);
    
      color: var(--input-icon-color, inherit);
      display: flex;
      flex-shrink: 0;
      inline-size: var(--icon-size);
      pointer-events: none;
    }
    
    .input__text {
      color: var(--input-text-color, var(--color-steel-700));
      flex-shrink: 0;
      pointer-events: none;
      touch-action: manipulation;
      user-select: none;
      white-space: nowrap;
    }
    
    .input__input {
      appearance: var(--input-appearance, initial);
      background-color: transparent;
      cursor: var(--input-cursor, default);
      display: block;
      flex-grow: 1;
      inline-size: auto;
      inline-size: var(--input-inline-size, 100%);
      line-height: inherit;
      min-block-size: calc(var(--_input-min-block-size) - var(--_input-border-width) * 2 * 1px);
      padding-block: var(--_input-padding-block);
      padding-inline: 0;
      text-align: var(--input-text-align, left);
    
      &:is([data-input-type='select']) {
        --input-color-invalid: var(--input-placeholder-color);
    
        cursor: pointer;
      }
    
      &:is([data-input-type='color']) {
        cursor: pointer;
      }
    
      &:is([data-input-type='tags']) {
        display: flex;
        flex-wrap: wrap;
        gap: 1rem;
      }
    
      &:is([data-input-no-spinners]) {
        appearance: textfield;
    
        &::-webkit-outer-spin-button,
        &::-webkit-inner-spin-button {
          appearance: none;
          margin: 0;
        }
      }
    
      &:is(:focus) {
        outline: 0;
      }
    
      &:is(:invalid) {
        color: var(--input-color-invalid, inherit);
      }
    
      &:is([data-input-empty]) {
        color: var(--input-placeholder-color, var(--color-gray-400));
        -webkit-text-fill-color: var(--input-placeholder-color, var(--color-gray-400));
      }
    
      &::placeholder {
        color: var(--input-placeholder-color, var(--color-gray-400));
        opacity: 1;
      }
    
      &::file-selector-button {
        background-color: var(--input-file-selector-button-background-color, var(--color-gray-800));
        border-radius: var(--border-radius-small);
        border-width: 0;
        color: var(--input-file-selector-button-color, var(--color-white));
        font-family: var(--font-family-exo);
        font-size: 1.4rem;
        font-weight: var(--font-weight-semibold);
        line-height: var(--line-height-narrow);
        padding-block: 0.5em;
        padding-inline: 1em;
        transition-property: background-color, color;
    
        &:hover {
          background-color: var(--input-file-selector-button-background-color--enter, var(--color-gray-950));
          color: var(--input-file-selector-button-color--enter, var(--color-white));
        }
      }
    
      &::-webkit-calendar-picker-indicator {
        display: none;
      }
    
      &::-webkit-datetime-edit {
        block-size: auto;
        display: block;
        line-height: 1;
        padding-block: calc((1em * var(--input-line-height, var(--line-height-regular)) - 1em - var(--_input-border-width)) / 2);
        padding-inline: 0;
      }
    
      &::-webkit-datetime-edit-ampm-field:focus,
      &::-webkit-datetime-edit-day-field:focus,
      &::-webkit-datetime-edit-hour-field:focus,
      &::-webkit-datetime-edit-millisecond-field:focus,
      &::-webkit-datetime-edit-minute-field:focus,
      &::-webkit-datetime-edit-month-field:focus,
      &::-webkit-datetime-edit-second-field:focus,
      &::-webkit-datetime-edit-week-field:focus,
      &::-webkit-datetime-edit-year-field:focus {
        background-color: var(--tertiary-background-color);
        color: var(--primary-form-color);
      }
    }
    
    .input__limit {
      margin-block-end: var(--_input-padding-block);
      margin-inline-start: var(--_input-padding-inline);
      pointer-events: none;
      user-select: none;
    }
    
    .input__pill {
      background-color: var(--input-limit-background-color, var(--color-gray-100));
      border-radius: calc((var(--input-limit-padding-block, 0.5em) + 1lh) / 2);
      color: var(--input-limit-color, var(--color-gray-800));
      display: inline-block;
      font-size: var(--input-limit-font-size, 1.2rem);
      font-weight: var(--font-weight-bold);
      inline-size: min-content;
      line-height: var(--input-limit-line-height, var(--line-height-narrow));
      padding-block: var(--input-limit-padding-block, 0.5em);
      padding-inline: var(--input-limit-padding-inline, 1em);
      white-space: nowrap;
    
      :is(strong) {
        font-weight: inherit;
      }
    }
    
    .input__option {
      color: var(--input-color);
      font-family: inherit;
      font-weight: var(--font-weight-regular);
    }
    
    .input__option-group {
      color: var(--input-color);
      font-family: inherit;
      font-weight: var(--font-weight-semibold);
    }
    
    .input__option-group .input__option {
      padding-inline-start: 1.6rem;
    }
    
    .input__option--select {
      color: var(--input-placeholder-color);
    }
    
    .input__uploads {
      display: flex;
      flex-wrap: wrap;
      gap: var(--input-uploads-gap, 1rem);
      margin-block-end: var(--_input-padding-block);
      margin-inline-start: var(--_input-padding-inline);
      pointer-events: none;
      user-select: none;
    }
    
    .input__picker {
      --icon-size: var(--input-icon-size, 2rem);
    
      block-size: 100%;
      color: var(--input-icon-color, inherit);
      display: flex;
      flex-shrink: 0;
      inline-size: var(--icon-size);
    
      // stylelint-disable-next-line at-rule-no-vendor-prefix
      @-moz-document url-prefix() {
        display: none;
      }
    }
    
  • URL: /components/raw/input/input.scss
  • Filesystem Path: src/components/2-atoms/input/input.scss
  • Size: 7 KB
  • Content:
    import { on } from 'delegated-events';
    import abort from '../../../javascripts/utils/abort';
    import onLoad from '../../../javascripts/utils/onLoad';
    
    const wordsCount = (value: string) => value.split(/\S+/).length - 1;
    
    const charsCount = (value: string) => [...value]
      .map((char) => {
        const codePoint = char.codePointAt(0);
        if (codePoint && codePoint > 127) {
          return `&#${codePoint};`;
        }
    
        return char;
      })
      .join('').length;
    
    const updateCount = ($input: HTMLInputElement | HTMLTextAreaElement) => {
      const {
        inputLimit: limit,
        inputLimitType: limitType = 'characters',
        inputLimitCount: limitCount = false,
      } = $input.dataset;
    
      if (!limit || !limitCount) {
        return;
      }
    
      requestIdleCallback(() => {
        const $limit = document.getElementById(limit) ?? abort();
        const $limitDisplay = $limit.querySelector('strong') ?? abort();
        const value = $input.isContentEditable ? $input.innerHTML : $input.value;
        const count = limitType === 'words' ? wordsCount(value) : charsCount(value);
        const left = parseInt(limitCount, 10) - count;
    
        // Update display
        $limitDisplay.innerText = left.toString();
      });
    };
    
    const useInputLimits = ($input: HTMLInputElement | HTMLTextAreaElement) => {
      // Update on load
      updateCount($input);
    
      // Update on keydown, change and paste
      const eventListener = () => updateCount($input);
      $input.addEventListener('keydown', eventListener);
      $input.addEventListener('change', eventListener);
      $input.addEventListener('paste', eventListener);
    
      // Deregister function
      return () => {
        $input.removeEventListener('keydown', eventListener);
        $input.removeEventListener('change', eventListener);
        $input.removeEventListener('paste', eventListener);
      };
    };
    
    on('click', '.input', (event) => {
      const { currentTarget: $container } = event;
    
      if (event.target instanceof Element) {
        if (event.target.closest('.input__input') || event.target.closest('.input__picker')) {
          return;
        }
      }
    
      const $input = $container.querySelector<HTMLElement>('.input__input');
    
      $input?.focus({
        preventScroll: true,
      });
    
      if ($input instanceof HTMLSelectElement) {
        $input.showPicker();
      }
    });
    
    on('click', '.input__picker', (event) => {
      const { currentTarget: $picker } = event;
    
      if ('showPicker' in HTMLInputElement.prototype) {
        try {
          const $input = $picker.parentElement?.querySelector<HTMLInputElement>('input.input__input');
    
          $input?.focus({ preventScroll: true });
          $input?.showPicker();
        } catch {
          // Do nothing.
        }
      }
    });
    
    on('click', 'input.input__input[data-input-select-on-click]', (event) => {
      const { currentTarget: $input } = event;
    
      $input.select();
      $input.setSelectionRange(0, $input.value.length);
    });
    
    on('change', 'input.input__input[type^=date], input.input__input[type^=time]', (event) => {
      const { currentTarget: $input } = event;
      $input.toggleAttribute('data-input-empty', $input.value === '');
    });
    
    onLoad<HTMLInputElement>('input.input__input[type^=date], input.input__input[type^=time]', ($input) => {
      $input.toggleAttribute('data-input-empty', $input.value === '');
    });
    
    onLoad<HTMLInputElement | HTMLTextAreaElement>('.input__input[data-input-limit]', ($input) => {
      useInputLimits($input);
    });
    
  • URL: /components/raw/input/input.ts
  • Filesystem Path: src/components/2-atoms/input/input.ts
  • Size: 3.3 KB

No notes defined.