Source RapidContext_Widget_TextField.js

1/*
2 * RapidContext <https://www.rapidcontext.com/>
3 * Copyright (c) 2007-2024 Per Cederberg. All rights reserved.
4 *
5 * This program is free software: you can redistribute it and/or
6 * modify it under the terms of the BSD license.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * See the RapidContext LICENSE for more details.
13 */
14
15// Namespace initialization
16if (typeof(RapidContext) == "undefined") {
17    RapidContext = {};
18}
19RapidContext.Widget = RapidContext.Widget || { Classes: {} };
20
21/**
22 * Creates a new text field widget.
23 *
24 * @constructor
25 * @param {Object} attrs the widget and node attributes
26 * @param {string} [attrs.name] the form field name
27 * @param {string} [attrs.value] the field value, defaults to ""
28 * @param {string} [attrs.helpText] the help text when empty (deprecated)
29 * @param {boolean} [attrs.disabled] the disabled widget flag, defaults to
30 *            false
31 * @param {boolean} [attrs.hidden] the hidden widget flag, defaults to false
32 * @param {...(string|Node)} [value] the initial text content
33 *
34 * @return {Widget} the widget DOM node
35 *
36 * @class The text field widget class. Used to provide a text input field for a
37 *     single line, using the `<input>` HTML element.
38 * @property {boolean} disabled The read-only widget disabled flag.
39 * @property {string} defaultValue The value to use on form reset.
40 * @extends RapidContext.Widget
41 *
42 * @example <caption>JavaScript</caption>
43 * var attrs = { name: "name", placeholder: "Your Name Here" };
44 * var field = RapidContext.Widget.TextField(attrs);
45 *
46 * @example <caption>User Interface XML</caption>
47 * <TextField name="name" placeholder="Your Name Here" />
48 */
49RapidContext.Widget.TextField = function (attrs/*, ...*/) {
50    function scrape(val) {
51        return String(val && val.textContent || val || "");
52    }
53    var type = (attrs && attrs.type) || "text";
54    var text = (attrs && attrs.value) || "";
55    text += Array.from(arguments).slice(1).map(scrape).join("");
56    var o = RapidContext.UI.INPUT({
57        autocapitalize: "off",
58        autocorrect: "off",
59        spellcheck: "off",
60        type: type,
61        value: text,
62    });
63    RapidContext.Widget._widgetMixin(o, RapidContext.Widget.TextField);
64    o.addClass("widgetTextField");
65    o.setAttrs(Object.assign({}, attrs, { value: text }));
66    o.on("input", o._handleChange);
67    return o;
68};
69
70// Register widget class
71RapidContext.Widget.Classes.TextField = RapidContext.Widget.TextField;
72
73/**
74 * Emitted when the text is modified. This event is triggered by either
75 * user events (keypress, paste, cut, blur) or by setting the value via
76 * setAttrs(). The DOM standard onchange event has no 'event.detail'
77 * data and is triggered on blur. The synthetic onchange events all
78 * contain an 'event.detail' object with 'before', 'after' and 'cause'
79 * properties.
80 *
81 * @name RapidContext.Widget.TextField#onchange
82 * @event
83 */
84
85/**
86 * Updates the widget or HTML DOM node attributes.
87 *
88 * @param {Object} attrs the widget and node attributes to set
89 * @param {string} [attrs.name] the form field name
90 * @param {string} [attrs.value] the field value
91 * @param {string} [attrs.helpText] the help text when empty (deprecated)
92 * @param {boolean} [attrs.disabled] the disabled widget flag
93 * @param {boolean} [attrs.hidden] the hidden widget flag
94 */
95RapidContext.Widget.TextField.prototype.setAttrs = function (attrs) {
96    attrs = Object.assign({}, attrs);
97    if ("helpText" in attrs) {
98        console.warn("deprecated: setting 'helpText' attribute, use 'placeholder' instead");
99        attrs.placeholder = attrs.placeholder || attrs.helpText;
100        delete attrs.helpText;
101    }
102    if ("value" in attrs) {
103        // FIXME: This is wrong, since we're setting an attribute here.
104        // But until Form.update() has some other way to set a field
105        // value and trigger changes, this will remain.
106        this.value = attrs.value || "";
107        this._handleChange(null);
108        delete attrs.value;
109    }
110    this.__setAttrs(attrs);
111};
112
113/**
114 * Resets the text area form value to the initial value.
115 */
116RapidContext.Widget.TextField.prototype.reset = function () {
117    this.setAttrs({ value: this.defaultValue });
118};
119
120/**
121 * Returns the text field value.
122 *
123 * @return {string} the field value
124 */
125RapidContext.Widget.TextField.prototype.getValue = function () {
126    return this.value;
127};
128
129/**
130 * Handles input events for this this widget.
131 *
132 * @param {Event} [evt] the DOM Event object or null for manual
133 */
134RapidContext.Widget.TextField.prototype._handleChange = function (evt) {
135    var cause = (evt && evt.inputType) || "set";
136    var detail = { before: this.storedValue || "", after: this.value, cause: cause };
137    this.emit("change", { detail: detail, bubbles: true });
138    this.storedValue = this.value;
139};
140