Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle 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.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Page utility helpers.
18
 *
19
 * @module core/pagehelpers
20
 * @copyright  2023 Ferran Recio <ferran@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
/**
25
 * Maximum sizes for breakpoints. This needs to correspond with Bootstrap
26
 * Breakpoints
27
 *
28
 * @private
29
 */
30
const Sizes = {
31
    small: 576,
32
    medium: 991,
33
    large: 1400
34
};
35
 
36
const Selectors = {
37
    focusable: 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])',
38
};
39
 
40
const Classes = {
41
    behatSite: 'behat-site',
42
};
43
 
44
/**
45
 * Check fi the current page is a Behat site.
46
 * @returns {boolean} true if the current page is a Behat site.
47
 */
48
export const isBehatSite = () => {
49
    return document.body.classList.contains(Classes.behatSite);
50
};
51
 
52
/**
53
 * Get the current body width.
54
 * @returns {number} the current body width.
55
 */
56
export const getCurrentWidth = () => {
57
    const DomRect = document.body.getBoundingClientRect();
58
    return DomRect.x + DomRect.width;
59
};
60
 
61
/**
62
 * Check if the user uses an extra small size browser.
63
 *
64
 * @returns {boolean} true if the body is smaller than sizes.small max size.
65
 */
66
export const isExtraSmall = () => {
67
    const browserWidth = getCurrentWidth();
68
    return browserWidth < Sizes.small;
69
};
70
 
71
/**
72
 * Check if the user uses a small size browser.
73
 *
74
 * @returns {boolean} true if the body is smaller than sizes.medium max size.
75
 */
76
export const isSmall = () => {
77
    const browserWidth = getCurrentWidth();
78
    return browserWidth < Sizes.medium;
79
};
80
 
81
/**
82
 * Check if the user uses a large size browser.
83
 *
84
 * @returns {boolean} true if the body is smaller than sizes.large max size.
85
 */
86
export const isLarge = () => {
87
    const browserWidth = getCurrentWidth();
88
    return browserWidth >= Sizes.large;
89
};
90
 
91
/**
92
 * Get the first focusable element inside a container.
93
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
94
 * @returns {HTMLElement|null}
95
 */
96
export const firstFocusableElement = (container) => {
97
    const containerElement = container || document;
98
    return containerElement.querySelector(Selectors.focusable);
99
};
100
 
101
/**
102
 * Get the last focusable element inside a container.
103
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
104
 * @returns {HTMLElement|null}
105
 */
106
export const lastFocusableElement = (container) => {
107
    const containerElement = container || document;
108
    const focusableElements = containerElement.querySelectorAll(Selectors.focusable);
109
    return focusableElements[focusableElements.length - 1] ?? null;
110
};
111
 
112
/**
113
 * Get all focusable elements inside a container.
114
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
115
 * @returns {HTMLElement[]}
116
 */
117
export const focusableElements = (container) => {
118
    const containerElement = container || document;
119
    return containerElement.querySelectorAll(Selectors.focusable);
120
};
121
 
122
/**
123
 * Get the previous focusable element in a container.
124
 * It uses the current focused element to know where to start the search.
125
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
126
 * @param {Boolean} [loopSelection] Whether to loop selection or not. Default to false.
127
 * @returns {HTMLElement|null}
128
 */
129
export const previousFocusableElement = (container, loopSelection) => {
130
    return getRelativeFocusableElement(container, loopSelection, -1);
131
};
132
 
133
/**
134
 * Get the next focusable element in a container.
135
 * It uses the current focused element to know where to start the search.
136
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
137
 * @param {Boolean} [loopSelection] Whether to loop selection or not. Default to false.
138
 * @returns {HTMLElement|null}
139
 */
140
export const nextFocusableElement = (container, loopSelection) => {
141
    return getRelativeFocusableElement(container, loopSelection, 1);
142
};
143
 
144
/**
145
 * Internal function to get the next or previous focusable element.
146
 * @param {HTMLElement} [container] Container to search in. Defaults to document.
147
 * @param {Boolean} [loopSelection] Whether to loop selection or not.
148
 * @param {Number} [direction] Direction to search in. 1 for next, -1 for previous.
149
 * @returns {HTMLElement|null}
150
 * @private
151
 */
152
const getRelativeFocusableElement = (container, loopSelection, direction) => {
153
    const focusedElement = document.activeElement;
154
    const focusables = [...focusableElements(container)];
155
    const focusedIndex = focusables.indexOf(focusedElement);
156
 
157
    if (focusedIndex === -1) {
158
        return null;
159
    }
160
 
161
    const newIndex = focusedIndex + direction;
162
 
163
    if (focusables[newIndex] !== undefined) {
164
        return focusables[newIndex];
165
    }
166
    if (loopSelection != true) {
167
        return null;
168
    }
169
    if (direction > 0) {
170
        return focusables[0] ?? null;
171
    }
172
    return focusables[focusables.length - 1] ?? null;
173
};