Newer
Older
iuav-ui / src / components / form / form.ts
import { LitElement, html, css } from 'lit';
import { customElement, property, state, queryAssignedNodes } from 'lit/decorators.js';

interface FormField {
  name: string;
  type: string;
  label?: string;
  placeholder?: string;
}

@customElement('iu-form')

export class Form extends LitElement {
    static override styles = css`
        input {
            padding: 8px;
            font-size: 1rem;
            width: 100%;
            box-sizing: border-box;
        }
        div{
            margin-bottom: 20px;
        }
        label{
            font-weight: bold;
            margin-bottom: 4px;
            display: block;
        }
        button {
            position: relative;
            display: inline-flex;
            align-items: center;
            background: var(--iu-color-grey-100);
            padding: 0.375rem 0.75rem;
            color: var(--iu-color-black);
            text-decoration: none;
            border: 0;
            font: var(--iu-f-sm);
            cursor: pointer;
            margin-top: 20px;
            &:hover{
              background: var(--iu-color-black);
              color: var(--iu-color-white);
            }
            margin-top: var(--iu-spacing-4);
        }
        ::slotted(iu-heading){
          --iu-heading-margin-top: var(--iu-spacing-7);
        }
    `;

  // Define the structure of form fields
  @property({ type: Array }) fields: FormField[] = [];

  // Reactive state to hold form values
  @state() private formData: Record<string, string> = {};

  // Query slot content to check if sections are provided
  @queryAssignedNodes() private slottedElements?: NodeList;

  private handleInput(event: Event) {
    const target = event.target as HTMLInputElement;
    this.formData = { ...this.formData, [target.name]: target.value };
  }

  private handleSubmit(event: Event) {
    event.preventDefault();
    console.log('Form Data:', this.formData);

    // Dispatch custom event with form data
    this.dispatchEvent(new CustomEvent('form-submit', {
      detail: this.formData,
      bubbles: true,
      composed: true,
    }));
  }

  // make sure that every component inside this has <iu-container> set 
  // to nested to disable padding left on the container itself
  private updateSlottedElements() {
    if (!this.slottedElements) return;

    this.slottedElements.forEach((slot) => {
      if (slot instanceof HTMLElement) {
        // Set the `nested` attribute or property
        slot.setAttribute('nested', 'true');
      }
    });
  }
  
  override firstUpdated() {
    super.firstUpdated();
    this.updateSlottedElements();
  }

  constructor() {
    super();
    // Initialize form data with empty values
    this.fields.forEach(field => {
      this.formData[field.name] = '';
    });
  }


  override render() {
    const hasSections = this.slottedElements && this.slottedElements.length > 0;
    return html`
      <iu-container columns-md="2">
        <form @submit="${this.handleSubmit}">
          <slot></slot>
          ${!hasSections && this.fields.length > 0
            ? this.fields.map(
              field => html`
                <div>
                  <label for="${field.name}">${field.label}</label>
                  <input
                    type="${field.type}"
                    name="${field.name}"
                    placeholder="${field.placeholder || ''}"
                    @input="${this.handleInput}"
                    .value="${this.formData[field.name] || ''}"
                    required
                  />
                </div>
              `)
            : ''
          }
          <button type="submit">Invia</button>
        </form>
      </iu-container>
    `;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'iu-form': Form;
  }
}