summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/abstract_renderer_owl.js
blob: 0c97159e2941a4c2dc72b9ffff6fa5b4599585fc (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
odoo.define('web.AbstractRendererOwl', function () {
    "use strict";

    // Renderers may display sample data when there is no real data to display. In
    // this case the data is displayed with opacity and can't be clicked. Moreover,
    // we also want to prevent the user from accessing DOM elements with TAB
    // navigation. This is the list of elements we won't allow to focus.
    const FOCUSABLE_ELEMENTS = [
        // focusable by default
        'a', 'button', 'input', 'select', 'textarea',
        // manually set
        '[tabindex="0"]'
    ].map((sel) => `:scope ${sel}`).join(', ');

    class AbstractRenderer extends owl.Component {

        constructor() {
            super(...arguments);
            // Defines the elements suppressed when in demo data. This must be a list
            // of DOM selectors matching view elements that will:
            // 1. receive the 'o_sample_data_disabled' class (greyd out & no user events)
            // 2. have themselves and any of their focusable children removed from the
            //    tab navigation
            this.sampleDataTargets = [];
        }

        mounted() {
            this._suppressFocusableElements();
        }

        patched() {
            this._suppressFocusableElements();
        }

        /**
         * Suppresses 'tabindex' property on any focusable element located inside
         * root elements defined in the `this.sampleDataTargets` object and assigns
         * the 'o_sample_data_disabled' class to these root elements.
         *
         * @private
         * @see sampleDataTargets
         */
        _suppressFocusableElements() {
            if (!this.props.isSample || this.props.isEmbedded) {
                const disabledEls = this.el.querySelectorAll(`.o_sample_data_disabled`);
                disabledEls.forEach(el => el.classList.remove('o_sample_data_disabled'));
                return;
            }
            const rootEls = [];
            for (const selector of this.sampleDataTargets) {
                rootEls.push(...this.el.querySelectorAll(`:scope ${selector}`));
            }
            const focusableEls = new Set(rootEls);
            for (const rootEl of rootEls) {
                rootEl.classList.add('o_sample_data_disabled');
                for (const focusableEl of rootEl.querySelectorAll(FOCUSABLE_ELEMENTS)) {
                    focusableEls.add(focusableEl);
                }
            }
            for (const focusableEl of focusableEls) {
                focusableEl.setAttribute('tabindex', -1);
                if (focusableEl.classList.contains('dropdown-item')) {
                    // Tells Bootstrap to ignore the dropdown item in keynav
                    focusableEl.classList.add('disabled');
                }
            }
        }
    }

    return AbstractRenderer;

});