Welcome to mirror list, hosted at ThFree Co, Russian Federation.

form-field.js « forms « src « js - github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f2c3a59b536c13267d6961890d0f0eb96b551539 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
 * --------------------------------------------------------------------------
 * Bootstrap (v5.3.0): forms/field.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */

import { getUID, isElement } from '../util/index'
import EventHandler from '../dom/event-handler'
import BaseComponent from '../base-component'
import SelectorEngine from '../dom/selector-engine'
import TemplateFactory from '../util/template-factory'

const NAME = 'formField'
const DATA_KEY = 'bs.field'
const EVENT_KEY = `.${DATA_KEY}`
const EVENT_INPUT = `input${EVENT_KEY}`
const CLASS_FIELD_ERROR = 'is-invalid'
const CLASS_FIELD_SUCCESS = 'is-valid'

const ARIA_DESCRIBED_BY = 'aria-describedby'
const Default = {
  invalid: '', // invalid message to append
  valid: '', // valid message to append
  type: 'feedback' // or tooltip
}

const DefaultType = {
  invalid: 'string',
  valid: 'string',
  type: 'string'
}

const MessageTypes = {
  ERROR: { prefix: 'invalid', class: CLASS_FIELD_ERROR },
  INFO: { prefix: 'info', class: '' },
  SUCCESS: { prefix: 'valid', class: CLASS_FIELD_SUCCESS }
}

class FormField extends BaseComponent {
  constructor(element, config) {
    super(element, config)
    if (!isElement(this._element)) {
      throw new TypeError(`field "${this._config.name}" not found`)
    }

    this._tipId = getUID(`${this._config.name}-formTip-`)
    this._initialDescribedBy = this._element.getAttribute(ARIA_DESCRIBED_BY) || ''

    EventHandler.on(this._element, EVENT_INPUT, () => {
      this.clearAppended()
    })
  }

  static get NAME() {
    return NAME
  }

  static get Default() {
    return Default
  }

  static get DefaultType() {
    return DefaultType
  }

  static get MessageTypes() {
    return MessageTypes
  }

  clearAppended() {
    const appendedFeedback = SelectorEngine.findOne(`#${this._tipId}`, this._element.parentNode)
    if (!appendedFeedback) {
      return
    }

    appendedFeedback.remove()

    this._element.classList.remove(CLASS_FIELD_ERROR, CLASS_FIELD_SUCCESS)

    if (this._initialDescribedBy) {
      this._element.setAttribute(ARIA_DESCRIBED_BY, this._initialDescribedBy)
      return
    }

    this._element.removeAttribute(ARIA_DESCRIBED_BY)
  }

  appendError(message = this._config.invalid) {
    return this.appendFeedback(message, this.constructor.MessageTypes.ERROR)
  }

  appendSuccess(message = this._config.valid) {
    return this.appendFeedback(message, this.constructor.MessageTypes.SUCCESS)
  }

  appendFeedback(feedback, classes = this.constructor.MessageTypes.INFO) {
    if (!feedback) {
      return false
    }

    this.clearAppended()

    const config = {
      extraClass: `${classes.prefix}-${this._config.type} ${classes.class}`,
      content: { div: feedback }
    }
    feedback = new TemplateFactory(config)

    const feedbackElement = feedback.toHtml()
    feedbackElement.id = this._tipId

    this._element.parentNode.append(feedbackElement)

    const describedBy = `${this._initialDescribedBy} ${feedbackElement.id}`.trim()
    this._element.setAttribute(ARIA_DESCRIBED_BY, describedBy)
    return true
  }

  name() {
    return this._element.name || this._element.id
  }
}

export default FormField