Source RapidContext_Widget_Pane.js

1/*
2 * RapidContext <https://www.rapidcontext.com/>
3 * Copyright (c) 2007-2023 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 pane widget.
23 *
24 * @constructor
25 * @param {Object} attrs the widget and node attributes
26 * @param {string} [attrs.pageTitle] the page title used when inside
27 *            a page container, defaults to "Page"
28 * @param {string|Object} [attrs.pageStatus] the page status used
29 *            when inside a page container, use one of the predefined
30 *            status constants in this class, defaults to `ANY`
31 * @param {boolean} [attrs.pageCloseable] the page closeable flag
32 *            used when inside some page containers, defaults to
33 *            `false`
34 * @param {boolean} [attrs.hidden] the hidden widget flag, defaults to false
35 * @param {...(string|Node|Array)} [child] the child widgets or DOM nodes
36 *
37 * @return {Widget} the widget DOM node
38 *
39 * @class The pane widget class. Used to create the simplest form of element
40 *     container. It is also used inside various types of paged containers,
41 *     such as a `TabContainer`, a `Wizard` or similar. A pane only uses a
42 *     `<div>` HTML element, and supports being hidden and shown according to
43 *     any page transitions required by a parent container.
44 * @property {string} pageTitle [read-only] The current page title.
45 * @property {Object} pageStatus [read-only] The current page status.
46 * @property {boolean} pageCloseable [read-only] The current page
47 *               closeable flag value.
48 * @extends RapidContext.Widget
49 *
50 * @example <caption>JavaScript</caption>
51 * let h1 = MochiKit.DOM.H1({}, "Hello, world!");
52 * let helloPane = RapidContext.Widget.Pane({}, h1);
53 *
54 * @example <caption>User Interface XML</caption>
55 * <Pane id="helloPane">
56 *   <h1>Hello, world!</h1>
57 * </Pane>
58 */
59RapidContext.Widget.Pane = function (attrs/*, ... */) {
60    let o = document.createElement("div");
61    RapidContext.Widget._widgetMixin(o, RapidContext.Widget.Pane);
62    o.addClass("widgetPane");
63    o.setAttrs(Object.assign({ pageTitle: "Page", pageStatus: "ANY", pageCloseable: false }, attrs));
64    o.addAll(Array.from(arguments).slice(1));
65    return o;
66};
67
68// Register widget class
69RapidContext.Widget.Classes.Pane = RapidContext.Widget.Pane;
70
71/**
72 * Emitted when the pane is shown for viewing in a container widget.
73 *
74 * @name RapidContext.Widget.Pane#onenter
75 * @event
76 */
77
78/**
79 * Emitted when the pane is hidden from view in a container widget.
80 *
81 * @name RapidContext.Widget.Pane#onexit
82 * @event
83 */
84
85/**
86 * Emitted when the pane is closed (removed) in a `TabContainer`.
87 *
88 * @name RapidContext.Widget.Pane#onclose
89 * @event
90 */
91
92/**
93 * The default page status. Allows page transitions both to the
94 * previous and the next page.
95 */
96RapidContext.Widget.Pane.ANY = { previous: true, next: true };
97
98/**
99 * The forward-only page status. Allows transitions only to the next
100 * page.
101 */
102RapidContext.Widget.Pane.FORWARD = { previous: false, next: true };
103
104/**
105 * The backward-only page status. Allows transitions only to the
106 * previous page.
107 */
108RapidContext.Widget.Pane.BACKWARD = { previous: true, next: false };
109
110/**
111 * The working page status. Will disable transitions both to the
112 * previous and the next page. The page container may also display a
113 * cancel button to allow user cancellation of the ongoing operation.
114 */
115RapidContext.Widget.Pane.WORKING = { previous: false, next: false };
116
117/**
118 * Updates the widget or HTML DOM node attributes.
119 *
120 * @param {Object} attrs the widget and node attributes to set
121 * @param {string} [attrs.pageTitle] the page title used when inside
122 *            a page container
123 * @param {string|Object} [attrs.pageStatus] the page status used
124 *            when inside a page container, use one of the predefined
125 *            status constants in this class
126 * @param {boolean} [attrs.pageCloseable] the page closeable flag
127 *            used when inside some page containers
128 * @param {boolean} [attrs.hidden] the hidden widget flag
129 */
130RapidContext.Widget.Pane.prototype.setAttrs = function (attrs) {
131    attrs = Object.assign({}, attrs);
132    if ("pageStatus" in attrs && typeof(attrs.pageStatus) != "object") {
133        attrs.pageStatus = RapidContext.Widget.Pane[attrs.pageStatus] || RapidContext.Widget.Pane.ANY;
134    }
135    if ("pageCloseable" in attrs) {
136        attrs.pageCloseable = RapidContext.Data.bool(attrs.pageCloseable);
137    }
138    this.__setAttrs(attrs);
139    if (this.parentNode && typeof(this.parentNode._updateStatus) == "function") {
140        this.parentNode._updateStatus();
141    }
142};
143
144/**
145 * Handles the page enter event. This method is called by a parent
146 * page container widget, such as a `TabContainer` or a `Wizard`. It will
147 * reset the form validations (optional), show the pane (optional),
148 * and finally trigger the "onenter" event.
149 *
150 * @param {Object} opts the page transition options
151 * @param {boolean} [opts.show] the show pane flag, defaults to `true`
152 * @param {boolean} [opts.validateReset] the form validation reset
153 *            flag, used to clear all form validations in the pane
154 */
155RapidContext.Widget.Pane.prototype._handleEnter = function (opts) {
156    opts = Object.assign({ show: true, validateReset: false }, opts);
157    if (RapidContext.Data.bool(opts.validateReset)) {
158        let forms = this.getElementsByTagName("FORM");
159        for (let i = 0; i < forms.length; i++) {
160            if (typeof(forms[i].validateReset) == "function") {
161                forms[i].validateReset();
162            }
163        }
164    }
165    if (RapidContext.Data.bool(opts.show)) {
166        this.show();
167        RapidContext.Util.resizeElements(this);
168    }
169    this.emit("enter");
170};
171
172/**
173 * Handles the page exit event. This method is called by a parent
174 * page container widget, such as a `TabContainer` or a `Wizard`. It will
175 * validate the form (optional), unfocus all form fields, hide the
176 * pane (optional), and finally trigger the "onexit" event.
177 *
178 * @param {Object} opts the page transition options
179 * @param {boolean} [opts.hide] the hide pane flag, defaults to `true`
180 * @param {boolean} [opts.validate] the form validation flag, used to
181 *            check all forms in the page for valid entries before
182 *            proceeding, defaults to `false`
183 *
184 * @return {boolean} `true` if the page exit event completed, or
185 *         `false` if it was cancelled (due to validation errors)
186 */
187RapidContext.Widget.Pane.prototype._handleExit = function (opts) {
188    function callFirst(obj, methods) {
189        for (let i = 0; i < methods.length; i++) {
190            let k = methods[i];
191            if (typeof(obj[k]) === "function") {
192                return obj[k]();
193            }
194        }
195        return undefined;
196    }
197    opts = Object.assign({ hide: true, validate: false }, opts);
198    if (RapidContext.Data.bool(opts.validate)) {
199        let forms = this.getElementsByTagName("FORM");
200        for (let i = 0; i < forms.length; i++) {
201            if (callFirst(forms[i], ["validate", "checkValidity"]) === false) {
202                return false;
203            }
204        }
205    }
206    this.blurAll();
207    if (RapidContext.Data.bool(opts.hide)) {
208        this.hide();
209    }
210    this.emit("exit");
211    return true;
212};
213