Project

General

Profile

1
import { Class } from '../mixin/index';
2
import { $, Animation, isNumeric, isString, offsetTop, query, requestAnimationFrame } from '../util/index';
3

    
4
export default function (UIkit) {
5

    
6
    UIkit.component('sticky', {
7

    
8
        mixins: [Class],
9

    
10
        attrs: true,
11

    
12
        props: {
13
            top: null,
14
            bottom: Boolean,
15
            offset: Number,
16
            animation: String,
17
            clsActive: String,
18
            clsInactive: String,
19
            clsFixed: String,
20
            widthElement: 'jQuery',
21
            showOnUp: Boolean,
22
            media: 'media',
23
            target: Number
24
        },
25

    
26
        defaults: {
27
            top: 0,
28
            bottom: false,
29
            offset: 0,
30
            animation: '',
31
            clsActive: 'uk-active',
32
            clsInactive: '',
33
            clsFixed: 'uk-sticky-fixed',
34
            widthElement: false,
35
            showOnUp: false,
36
            media: false,
37
            target: false
38
        },
39

    
40
        connected() {
41

    
42
            this.placeholder = $('<div class="uk-sticky-placeholder"></div>');
43
            this.widthElement = this.$props.widthElement || this.placeholder;
44

    
45
            if (!this.isActive) {
46
                this.$el.addClass(this.clsInactive);
47
            }
48
        },
49

    
50
        disconnected() {
51

    
52
            if (this.isActive) {
53
                this.isActive = false;
54
                this.hide();
55
                this.$el.removeClass(this.clsInactive);
56
            }
57

    
58
            this.placeholder.remove();
59
            this.placeholder = null;
60
            this.widthElement = null;
61
        },
62

    
63
        ready() {
64

    
65
            if (!(this.target && location.hash && window.pageYOffset > 0)) {
66
                return;
67
            }
68

    
69
            var target = query(location.hash);
70

    
71
            if (target) {
72
                requestAnimationFrame(() => {
73

    
74
                    var top = offsetTop(target),
75
                        elTop = offsetTop(this.$el),
76
                        elHeight = this.$el[0].offsetHeight,
77
                        elBottom = elTop + elHeight;
78

    
79
                    if (elBottom >= top && elTop <= top + target[0].offsetHeight) {
80
                        window.scrollTo(0, top - elHeight - this.target - this.offset);
81
                    }
82

    
83
                });
84
            }
85

    
86
        },
87

    
88
        update: [
89

    
90
            {
91

    
92
                write() {
93

    
94
                    var outerHeight = this.$el[0].offsetHeight, el;
95

    
96
                    this.placeholder
97
                        .css('height', this.$el.css('position') !== 'absolute' ? outerHeight : '')
98
                        .css(this.$el.css(['marginTop', 'marginBottom', 'marginLeft', 'marginRight']));
99

    
100
                    if (!document.documentElement.contains(this.placeholder[0])) {
101
                        this.placeholder.insertAfter(this.$el).attr('hidden', true);
102
                    }
103

    
104
                    this.width = this.widthElement.attr('hidden', null)[0].offsetWidth;
105
                    this.widthElement.attr('hidden', !this.isActive);
106

    
107
                    this.topOffset = offsetTop(this.isActive ? this.placeholder : this.$el);
108
                    this.bottomOffset = this.topOffset + outerHeight;
109

    
110
                    ['top', 'bottom'].forEach(prop => {
111

    
112
                        this[prop] = this.$props[prop];
113

    
114
                        if (!this[prop]) {
115
                            return;
116
                        }
117

    
118
                        if (isNumeric(this[prop])) {
119

    
120
                            this[prop] = this[`${prop}Offset`] + parseFloat(this[prop]);
121

    
122
                        } else {
123

    
124
                            if (isString(this[prop]) && this[prop].match(/^-?\d+vh$/)) {
125
                                this[prop] = window.innerHeight * parseFloat(this[prop]) / 100;
126
                            } else {
127

    
128
                                el = this[prop] === true ? this.$el.parent() : query(this[prop], this.$el);
129

    
130
                                if (el) {
131
                                    this[prop] = offsetTop(el) + el[0].offsetHeight;
132
                                }
133

    
134
                            }
135

    
136
                        }
137

    
138
                    });
139

    
140
                    this.top = Math.max(parseFloat(this.top), this.topOffset) - this.offset;
141
                    this.bottom = this.bottom && this.bottom - outerHeight;
142
                    this.inactive = this.media && !window.matchMedia(this.media).matches;
143

    
144
                    if (this.isActive) {
145
                        this.update();
146
                    }
147
                },
148

    
149
                events: ['load', 'resize']
150

    
151
            },
152

    
153
            {
154

    
155
                read() {
156
                    this.offsetTop = offsetTop(this.$el)
157
                },
158

    
159
                write({dir} = {}) {
160

    
161
                    var scroll = window.pageYOffset;
162

    
163
                    if (scroll < 0 || !this.$el.is(':visible') || this.disabled || this.showOnUp && !dir) {
164
                        return;
165
                    }
166

    
167
                    if (this.inactive
168
                        || scroll < this.top
169
                        || this.showOnUp && (scroll <= this.top || dir ==='down' || dir === 'up' && !this.isActive && scroll <= this.bottomOffset)
170
                    ) {
171

    
172
                        if (!this.isActive) {
173
                            return;
174
                        }
175

    
176
                        this.isActive = false;
177

    
178
                        if (this.animation && this.bottomOffset < this.offsetTop) {
179
                            Animation.cancel(this.$el).then(() => Animation.out(this.$el, this.animation).then(() => this.hide()));
180
                        } else {
181
                            this.hide();
182
                        }
183

    
184
                    } else if (this.isActive) {
185

    
186
                        this.update();
187

    
188
                    } else if (this.animation) {
189

    
190
                        Animation.cancel(this.$el).then(() => {
191
                            this.show();
192
                            Animation.in(this.$el, this.animation);
193
                        });
194

    
195
                    } else {
196
                        this.show();
197
                    }
198

    
199
                },
200

    
201
                events: ['scroll']
202

    
203
            },
204

    
205
        ],
206

    
207
        methods: {
208

    
209
            show() {
210

    
211
                this.isActive = true;
212
                this.update();
213
                this.$el.trigger('active');
214
                this.placeholder.attr('hidden', null);
215

    
216
            },
217

    
218
            hide() {
219

    
220
                this.$el
221
                    .addClass(this.clsInactive)
222
                    .removeClass(this.clsFixed)
223
                    .removeClass(this.clsActive)
224
                    .css({position: '', top: '', width: ''})
225
                    .trigger('inactive');
226

    
227
                this.placeholder.attr('hidden', true);
228

    
229
            },
230

    
231
            update() {
232

    
233
                var top = Math.max(0, this.offset), scroll = window.pageYOffset, active = scroll > this.top;
234

    
235
                if (this.bottom && scroll > this.bottom - this.offset) {
236
                    top = this.bottom - scroll;
237
                }
238

    
239
                this.$el
240
                    .css({
241
                        position: 'fixed',
242
                        top: `${top}px`,
243
                        width: this.width
244
                    })
245
                    .addClass(this.clsFixed)
246
                    .toggleClass(this.clsActive, active)
247
                    .toggleClass(this.clsInactive, !active);
248

    
249
            }
250

    
251
        }
252

    
253
    });
254

    
255
}
(24-24/28)