Newer
Older
iuav-ui / src / components / form / form.ts
  1. import { LitElement, html, css } from 'lit';
  2. import { customElement, property, state, queryAssignedNodes } from 'lit/decorators.js';
  3.  
  4. interface FormField {
  5. name: string;
  6. type: string;
  7. label?: string;
  8. placeholder?: string;
  9. }
  10.  
  11. @customElement('iu-form')
  12.  
  13. export class Form extends LitElement {
  14. static override styles = css`
  15. input {
  16. padding: 8px;
  17. font-size: 1rem;
  18. width: 100%;
  19. box-sizing: border-box;
  20. }
  21. div{
  22. margin-bottom: 20px;
  23. }
  24. label{
  25. font-weight: bold;
  26. margin-bottom: 4px;
  27. display: block;
  28. }
  29. button {
  30. position: relative;
  31. display: inline-flex;
  32. align-items: center;
  33. background: var(--iu-color-grey-100);
  34. padding: 0.375rem 0.75rem;
  35. color: var(--iu-color-black);
  36. text-decoration: none;
  37. border: 0;
  38. font: var(--iu-f-sm);
  39. cursor: pointer;
  40. margin-top: 20px;
  41. &:hover{
  42. background: var(--iu-color-black);
  43. color: var(--iu-color-white);
  44. }
  45. margin-top: var(--iu-spacing-4);
  46. }
  47. ::slotted(iu-heading){
  48. --iu-heading-margin-top: var(--iu-spacing-7);
  49. }
  50. `;
  51.  
  52. // Define the structure of form fields
  53. @property({ type: Array }) fields: FormField[] = [];
  54.  
  55. // Reactive state to hold form values
  56. @state() private formData: Record<string, string> = {};
  57.  
  58. // Query slot content to check if sections are provided
  59. @queryAssignedNodes() private slottedElements?: NodeList;
  60.  
  61. private handleInput(event: Event) {
  62. const target = event.target as HTMLInputElement;
  63. this.formData = { ...this.formData, [target.name]: target.value };
  64. }
  65.  
  66. private handleSubmit(event: Event) {
  67. event.preventDefault();
  68. console.log('Form Data:', this.formData);
  69.  
  70. // Dispatch custom event with form data
  71. this.dispatchEvent(new CustomEvent('form-submit', {
  72. detail: this.formData,
  73. bubbles: true,
  74. composed: true,
  75. }));
  76. }
  77.  
  78. // make sure that every component inside this has <iu-container> set
  79. // to nested to disable padding left on the container itself
  80. private updateSlottedElements() {
  81. if (!this.slottedElements) return;
  82.  
  83. this.slottedElements.forEach((slot) => {
  84. if (slot instanceof HTMLElement) {
  85. // Set the `nested` attribute or property
  86. slot.setAttribute('nested', 'true');
  87. }
  88. });
  89. }
  90. override firstUpdated() {
  91. super.firstUpdated();
  92. this.updateSlottedElements();
  93. }
  94.  
  95. constructor() {
  96. super();
  97. // Initialize form data with empty values
  98. this.fields.forEach(field => {
  99. this.formData[field.name] = '';
  100. });
  101. }
  102.  
  103.  
  104. override render() {
  105. const hasSections = this.slottedElements && this.slottedElements.length > 0;
  106. return html`
  107. <iu-container columns-md="2">
  108. <form @submit="${this.handleSubmit}">
  109. <slot></slot>
  110. ${!hasSections && this.fields.length > 0
  111. ? this.fields.map(
  112. field => html`
  113. <div>
  114. <label for="${field.name}">${field.label}</label>
  115. <input
  116. type="${field.type}"
  117. name="${field.name}"
  118. placeholder="${field.placeholder || ''}"
  119. @input="${this.handleInput}"
  120. .value="${this.formData[field.name] || ''}"
  121. required
  122. />
  123. </div>
  124. `)
  125. : ''
  126. }
  127. <button type="submit">Invia</button>
  128. </form>
  129. </iu-container>
  130. `;
  131. }
  132. }
  133.  
  134. declare global {
  135. interface HTMLElementTagNameMap {
  136. 'iu-form': Form;
  137. }
  138. }