@hippy/vue-native-components
Version:
Native components middleware for Hippy-Vue, the components only for native, can't compatible with web.
1,052 lines (1,042 loc) • 40 kB
JavaScript
/*!
* @hippy/vue-native-components v3.2.0-beta
* (Using Vue v2.6.14 and Hippy-Vue v3.2.0-beta)
* Build at: Fri Apr 25 2025 20:27:30 GMT+0800 (中国标准时间)
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2025 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function registerAnimation(Vue) {
// Constants for animations
const DEFAULT_OPTION = {
valueType: undefined,
delay: 0,
startValue: 0,
toValue: 0,
duration: 0,
direction: 'center',
timingFunction: 'linear',
repeatCount: 0,
inputRange: [],
outputRange: [],
};
/**
* parse value of special value type
* @param {string} valueType
* @param {*} originalValue
*/
function parseValue(valueType, originalValue) {
if (valueType === 'color' && ['number', 'string'].indexOf(typeof originalValue) >= 0) {
return Vue.Native.parseColor(originalValue);
}
return originalValue;
}
/**
* Create the standalone animation
*/
function createAnimation(option) {
const { mode = 'timing', valueType, startValue, toValue, ...others } = option;
const fullOption = {
...DEFAULT_OPTION,
...others,
};
if (valueType !== undefined) {
fullOption.valueType = option.valueType;
}
fullOption.startValue = parseValue(fullOption.valueType, startValue);
fullOption.toValue = parseValue(fullOption.valueType, toValue);
fullOption.repeatCount = repeatCountDict(fullOption.repeatCount);
fullOption.mode = mode;
const animation = new global.Hippy.Animation(fullOption);
const animationId = animation.getId();
return {
animation,
animationId,
};
}
/**
* Create the animationSet
*/
function createAnimationSet(children, repeatCount = 0) {
const animation = new global.Hippy.AnimationSet({
children,
repeatCount,
});
const animationId = animation.getId();
return {
animation,
animationId,
};
}
function repeatCountDict(repeatCount) {
if (repeatCount === 'loop') {
return -1;
}
return repeatCount;
}
/**
* Generate the styles from animation and animationSet Ids.
*/
function createStyle(actions, animationIdsMap = {}) {
const style = {};
Object.keys(actions).forEach((key) => {
if (Array.isArray(actions[key])) {
// Process AnimationSet from Array.
const actionSet = actions[key];
const { repeatCount } = actionSet[actionSet.length - 1];
const animationSetActions = actionSet.map((animationChild) => {
const { animationId, animation } = createAnimation(Object.assign({}, animationChild, { repeatCount: 0 }));
Object.assign(animationIdsMap, {
[animationId]: animation,
});
return { animationId, follow: true };
});
const { animationId, animation } = createAnimationSet(animationSetActions, repeatCountDict(repeatCount));
style[key] = {
animationId,
};
Object.assign(animationIdsMap, {
[animationId]: animation,
});
}
else {
// Process standalone Animation.
const action = actions[key];
const { animationId, animation } = createAnimation(action);
Object.assign(animationIdsMap, {
[animationId]: animation,
});
style[key] = {
animationId,
};
}
});
return style;
}
/**
* Get animationIds from style for start/pause/destroy actions.
*/
function getAnimationIds(style) {
const { transform, ...otherStyles } = style;
let animationIds = Object.keys(otherStyles).map(key => style[key].animationId);
if (Array.isArray(transform) && transform.length > 0) {
const transformIds = [];
transform.forEach(entity => Object.keys(entity)
.forEach((key) => {
if (entity[key]) {
const { animationId } = entity[key];
if (typeof animationId === 'number' && animationId % 1 === 0) {
transformIds.push(animationId);
}
}
}));
animationIds = [...animationIds, ...transformIds];
}
return animationIds;
}
/**
* Register the animation component.
*/
Vue.component('Animation', {
inheritAttrs: false,
props: {
tag: {
type: String,
default: 'div',
},
playing: {
type: Boolean,
default: false,
},
actions: {
type: Object,
required: true,
},
props: Object,
},
data() {
return {
style: {},
animationIds: [],
animationIdsMap: {},
animationEventMap: {},
};
},
watch: {
playing(to, from) {
if (!from && to) {
this.start();
}
else if (from && !to) {
this.pause();
}
},
actions() {
this.destroy();
this.create();
// trigger actionsDidUpdate in setTimeout callback to make sure node style updated
setTimeout(() => {
if (typeof this.$listeners.actionsDidUpdate === 'function') {
this.$listeners.actionsDidUpdate();
}
});
},
},
created() {
this.animationEventMap = {
start: 'animationstart',
end: 'animationend',
repeat: 'animationrepeat',
cancel: 'animationcancel',
};
},
beforeMount() {
this.create();
},
mounted() {
const { playing } = this.$props;
if (playing) {
// make sure that start animation after node created
setTimeout(() => {
this.start();
}, 0);
}
},
beforeDestroy() {
this.destroy();
},
methods: {
create() {
const { actions: { transform, ...actions } } = this.$props;
this.animationIdsMap = {};
const style = createStyle(actions, this.animationIdsMap);
if (transform) {
const transformAnimations = createStyle(transform, this.animationIdsMap);
style.transform = Object.keys(transformAnimations).map(key => ({
[key]: transformAnimations[key],
}));
}
// Turn to be true at first startAnimation, and be false again when destroyed.
this.$alreadyStarted = false;
// Generated style
this.style = style;
},
removeAnimationEvent() {
this.animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
if (!animation)
return;
Object.keys(this.animationEventMap).forEach((key) => {
if (typeof this.$listeners[key] !== 'function')
return;
const eventName = this.animationEventMap[key];
if (!eventName)
return;
animation.removeEventListener(eventName);
});
});
},
addAnimationEvent() {
this.animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
if (!animation)
return;
Object.keys(this.animationEventMap).forEach((key) => {
if (typeof this.$listeners[key] !== 'function')
return;
const eventName = this.animationEventMap[key];
if (!eventName)
return;
animation.addEventListener(eventName, () => {
this.$emit(key);
});
});
});
},
reset() {
this.$alreadyStarted = false;
},
start() {
if (!this.$alreadyStarted) {
this.animationIds = getAnimationIds(this.style);
this.$alreadyStarted = true;
this.removeAnimationEvent();
this.addAnimationEvent();
this.animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
animation === null || animation === void 0 ? void 0 : animation.start();
});
}
else {
this.resume();
}
},
resume() {
const animationIds = getAnimationIds(this.style);
animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
animation === null || animation === void 0 ? void 0 : animation.resume();
});
},
pause() {
if (!this.$alreadyStarted) {
return;
}
const animationIds = getAnimationIds(this.style);
animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
animation === null || animation === void 0 ? void 0 : animation.pause();
});
},
destroy() {
this.removeAnimationEvent();
this.$alreadyStarted = false;
const animationIds = getAnimationIds(this.style);
animationIds.forEach((animationId) => {
const animation = this.animationIdsMap[animationId];
animation === null || animation === void 0 ? void 0 : animation.destroy();
});
},
},
render(h) {
return h(this.tag, {
attrs: {
useAnimation: true,
...this.props,
},
style: this.style,
}, this.$slots.default);
},
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const getFirstComponent = (elements) => {
if (!elements)
return null;
if (Array.isArray(elements))
return elements[0];
if (elements)
return elements;
};
function registerDialog(Vue) {
Vue.registerElement('hi-dialog', {
component: {
name: 'Modal',
defaultNativeStyle: {
position: 'absolute',
},
},
});
Vue.component('Dialog', {
inheritAttrs: false,
props: {
collapsable: {
type: Boolean,
default: false,
},
transparent: {
type: Boolean,
default: true,
},
immersionStatusBar: {
type: Boolean,
default: true,
},
autoHideStatusBar: {
type: Boolean,
default: false,
},
autoHideNavigationBar: {
type: Boolean,
default: false,
},
},
render(h) {
const firstChild = getFirstComponent(this.$slots.default);
if (firstChild) {
// __modalFirstChild__ marked to remove absolute position to be compatible with hippy 2.0
if (!firstChild.data.attrs) {
firstChild.data.attrs = {
__modalFirstChild__: true,
};
}
else {
Object.assign(firstChild.data.attrs, {
__modalFirstChild__: true,
});
}
}
const { collapsable, transparent, immersionStatusBar, autoHideStatusBar, autoHideNavigationBar } = this;
return h('hi-dialog', {
on: { ...this.$listeners },
attrs: {
collapsable,
transparent,
immersionStatusBar,
autoHideStatusBar,
autoHideNavigationBar,
},
}, this.$slots.default);
},
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Capitalize a word
*
* @param {string} str The word input
* @returns string
*/
function capitalize(str) {
if (typeof str !== 'string') {
return '';
}
return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
}
/**
* Get binding events redirector
*
* The function should be called with `getEventRedirector.call(this, [])`
* for binding this.
*
* @param {string[] | string[][]} events events will be redirect
* @returns Object
*/
function getEventRedirector(events) {
const on = {};
events.forEach((event) => {
if (Array.isArray(event)) {
// exposedEventName is used in vue declared, nativeEventName is used in native
const [exposedEventName, nativeEventName] = event;
if (Object.prototype.hasOwnProperty.call(this.$listeners, exposedEventName)) {
// Use event handler first if declared
if (this[`on${capitalize(nativeEventName)}`]) {
// event will be converted like "dropped,pageSelected" which assigned to "on" object
// @ts-expect-error TS(2538): Type 'any[]' cannot be used as an index type.
on[event] = this[`on${capitalize(nativeEventName)}`];
}
else {
// if no event handler found, emit default exposedEventName.
// @ts-expect-error TS(2538): Type 'any[]' cannot be used as an index type.
on[event] = (evt) => this.$emit(exposedEventName, evt);
}
}
}
else if (Object.prototype.hasOwnProperty.call(this.$listeners, event)) {
if (this[`on${capitalize(event)}`]) {
on[event] = this[`on${capitalize(event)}`];
}
else {
on[event] = (evt) => this.$emit(event, evt);
}
}
});
return on;
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function registerUlRefresh(Vue) {
Vue.registerElement('hi-ul-refresh-wrapper', {
component: {
name: 'RefreshWrapper',
},
});
Vue.registerElement('hi-refresh-wrapper-item', {
component: {
name: 'RefreshWrapperItemView',
},
});
Vue.component('UlRefreshWrapper', {
inheritAttrs: false,
props: {
bounceTime: {
type: Number,
defaultValue: 100,
},
},
methods: {
startRefresh() {
Vue.Native.callUIFunction(this.$refs.refreshWrapper, 'startRefresh', null);
},
refreshCompleted() {
// FIXME: Here's a typo mistake `refreshComplected` in native sdk.
Vue.Native.callUIFunction(this.$refs.refreshWrapper, 'refreshComplected', null);
},
},
render(h) {
const on = getEventRedirector.call(this, [
'refresh',
]);
return h('hi-ul-refresh-wrapper', {
on,
ref: 'refreshWrapper',
}, this.$slots.default);
},
});
Vue.component('UlRefresh', {
inheritAttrs: false,
render(h) {
return h('hi-refresh-wrapper-item', {
style: {
position: 'absolute',
left: 0,
right: 0,
},
}, [
h('div', this.$slots.default),
]);
},
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function registerSwiper(Vue) {
Vue.registerElement('hi-swiper', {
component: {
name: 'ViewPager',
processEventData(event, nativeEventName, nativeEventParams) {
switch (nativeEventName) {
case 'onPageSelected':
event.currentSlide = nativeEventParams.position;
break;
case 'onPageScroll':
event.nextSlide = nativeEventParams.position;
event.offset = nativeEventParams.offset;
break;
case 'onPageScrollStateChanged':
event.state = nativeEventParams.pageScrollState;
break;
}
return event;
},
},
});
Vue.registerElement('swiper-slide', {
component: {
name: 'ViewPagerItem',
defaultNativeStyle: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
},
});
Vue.component('Swiper', {
inheritAttrs: false,
props: {
current: {
type: Number,
defaultValue: 0,
},
needAnimation: {
type: Boolean,
defaultValue: true,
},
},
watch: {
current(to) {
if (this.$props.needAnimation) {
this.setSlide(to);
}
else {
this.setSlideWithoutAnimation(to);
}
},
},
beforeMount() {
this.$initialSlide = this.$props.current;
},
methods: {
setSlide(slideIndex) {
Vue.Native.callUIFunction(this.$refs.swiper, 'setPage', [slideIndex]);
},
setSlideWithoutAnimation(slideIndex) {
Vue.Native.callUIFunction(this.$refs.swiper, 'setPageWithoutAnimation', [slideIndex]);
},
},
render(h) {
const on = getEventRedirector.call(this, [
['dropped', 'pageSelected'],
['dragging', 'pageScroll'],
['stateChanged', 'pageScrollStateChanged'],
]);
return h('hi-swiper', {
on,
ref: 'swiper',
attrs: {
initialPage: this.$initialSlide,
},
}, this.$slots.default);
},
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const PULLING_EVENT = 'pulling';
const IDLE_EVENT = 'idle';
function registerPull(Vue) {
const { callUIFunction } = Vue.Native;
[
['Header', 'header'],
['Footer', 'footer'],
].forEach(([capitalCase, lowerCase]) => {
/**
* PullView native component
*
* Methods:
* expandPull() - Expand the PullView and display the content
* collapsePull() - collapse the PullView and hide the content
*
* Events:
* onReleased - Trigger when release the finger after pulling gap larger than the content height
* onPulling - Trigger when pulling, will use it to trigger idle and pulling method
*/
Vue.registerElement(`hi-pull-${lowerCase}`, {
component: {
name: `Pull${capitalCase}View`,
processEventData(event, nativeEventName, nativeEventParams) {
switch (nativeEventName) {
case `on${capitalCase}Released`:
case `on${capitalCase}Pulling`:
Object.assign(event, nativeEventParams);
break;
}
return event;
},
},
});
Vue.component(`pull-${lowerCase}`, {
methods: {
/**
* Expand the PullView and display the content
*/
[`expandPull${capitalCase}`]() {
callUIFunction(this.$refs.instance, `expandPull${capitalCase}`);
},
/**
* Collapse the PullView and hide the content
* @param {Object} [options] additional config for pull header
* @param {number} [options.time] time left to hide pullHeader after collapsePullHeader() called, unit is ms
*/
[`collapsePull${capitalCase}`](options) {
if (capitalCase === 'Header') {
// options: { time }
if (typeof options !== 'undefined') {
callUIFunction(this.$refs.instance, `collapsePull${capitalCase}WithOptions`, [options]);
}
else {
callUIFunction(this.$refs.instance, `collapsePull${capitalCase}`);
}
}
else {
callUIFunction(this.$refs.instance, `collapsePull${capitalCase}`);
}
},
/**
* Get the refresh height by @layout event
* @param {Object} evt
*/
onLayout(evt) {
this.$contentHeight = evt.height;
},
/**
* Trigger when release the finger after pulling gap larger than the content height
* Convert to `released` event.
*/
[`on${capitalCase}Released`](evt) {
// @ts-expect-error TS(2554): Expected 1 arguments, but got 2.
this.$emit('released', evt);
},
/**
* Trigger when pulling
* Convert to `idle` event if dragging gap less than content height
* Convert to `pulling` event if dragging gap larger than content height
*
* @param {Object} evt Event Object
* @param {number} evt.contentOffset Dragging gap, either horizion and vertical direction.
*/
[`on${capitalCase}Pulling`](evt) {
if (evt.contentOffset > this.$contentHeight) {
if (this.$lastEvent !== PULLING_EVENT) {
this.$lastEvent = PULLING_EVENT;
// @ts-expect-error TS(2554): Expected 1 arguments, but got 2.
this.$emit(PULLING_EVENT, evt);
}
}
else if (this.$lastEvent !== IDLE_EVENT) {
this.$lastEvent = IDLE_EVENT;
// @ts-expect-error TS(2554): Expected 1 arguments, but got 2.
this.$emit(IDLE_EVENT, evt);
}
},
},
render(h) {
const { released, pulling, idle } = this.$listeners;
const on = {
layout: this.onLayout,
};
if (typeof released === 'function') {
on[`${lowerCase}Released`] = this[`on${capitalCase}Released`];
}
if (typeof pulling === 'function' || typeof idle === 'function') {
on[`${lowerCase}Pulling`] = this[`on${capitalCase}Pulling`];
}
return h(`hi-pull-${lowerCase}`, {
on,
ref: 'instance',
}, this.$slots.default);
},
});
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function registerWaterfall(Vue) {
Vue.registerElement('hi-waterfall', {
component: {
name: 'WaterfallView',
processEventData(event, nativeEventName, nativeEventParams) {
switch (nativeEventName) {
case 'onExposureReport':
event.exposureInfo = nativeEventParams.exposureInfo;
break;
case 'onScroll': {
/**
* scroll event parameters
*
* @param {number} startEdgePos - Scrolled offset of List top edge
* @param {number} endEdgePos - Scrolled offset of List end edge
* @param {number} firstVisibleRowIndex - Index of the first list item at current visible screen
* @param {number} lastVisibleRowIndex - Index of the last list item at current visible screen
* @param {Object[]} visibleRowFrames - Frame info of current screen visible items
* @param {number} visibleRowFrames[].x - Current item's horizontal offset relative to ListView
* @param {number} visibleRowFrames[].y - Current item's vertical offset relative to ListView
* @param {number} visibleRowFrames[].width - Current item's width
* @param {number} visibleRowFrames[].height - Current item's height
*/
const { startEdgePos, endEdgePos, firstVisibleRowIndex, lastVisibleRowIndex, visibleRowFrames, } = nativeEventParams;
Object.assign(event, {
startEdgePos,
endEdgePos,
firstVisibleRowIndex,
lastVisibleRowIndex,
visibleRowFrames,
});
break;
}
}
return event;
},
},
});
Vue.registerElement('hi-waterfall-item', {
component: {
name: 'WaterfallItem',
},
});
Vue.component('Waterfall', {
inheritAttrs: false,
props: {
// specific number of waterfall column
numberOfColumns: {
type: Number,
default: 2,
},
// inner content padding
contentInset: {
type: Object,
default: () => ({ top: 0, left: 0, bottom: 0, right: 0 }),
},
// horizontal space between columns
columnSpacing: {
type: Number,
default: 0,
},
interItemSpacing: {
type: Number,
default: 0,
},
preloadItemNumber: {
type: Number,
default: 0,
},
containBannerView: {
type: Boolean,
default: false,
},
containPullHeader: {
type: Boolean,
default: false,
},
containPullFooter: {
type: Boolean,
default: false,
},
},
methods: {
// call native methods
call(action, params) {
Vue.Native.callUIFunction(this.$refs.waterfall, action, params);
},
startRefresh() {
// @ts-expect-error TS(2554): Expected 2 arguments, but got 1.
this.call('startRefresh');
},
/** @param {number} type 1.same as startRefresh */
startRefreshWithType(type) {
this.call('startRefreshWithType', [type]);
},
callExposureReport() {
this.call('callExposureReport', []);
},
/**
* Scrolls to a given index of item, either immediately, with a smooth animation.
*
* @param {Object} scrollToIndex params
* @param {number} scrollToIndex.index - Scroll to specific index.
* @param {boolean} scrollToIndex.animated - With smooth animation. By default is true.
*/
scrollToIndex({ index = 0, animated = true }) {
if (typeof index !== 'number' || typeof animated !== 'boolean') {
return;
}
this.call('scrollToIndex', [index, index, animated]);
},
/**
* Scrolls to a given x, y offset, either immediately, with a smooth animation.
*
* @param {Object} scrollToContentOffset params
* @param {number} scrollToContentOffset.xOffset - Scroll to horizon offset X.
* @param {number} scrollToContentOffset.yOffset - Scroll To vertical offset Y.
* @param {boolean} scrollToContentOffset.animated - With smooth animation. By default is true.
*/
scrollToContentOffset({ xOffset = 0, yOffset = 0, animated = true }) {
if (typeof xOffset !== 'number' || typeof yOffset !== 'number' || typeof animated !== 'boolean') {
return;
}
this.call('scrollToContentOffset', [xOffset, yOffset, animated]);
},
/**
* start to load more waterfall items
*/
startLoadMore() {
// @ts-expect-error TS(2554): Expected 2 arguments, but got 1.
this.call('startLoadMore');
},
},
render(h) {
const on = getEventRedirector.call(this, [
'headerReleased',
'headerPulling',
'endReached',
'exposureReport',
'initialListReady',
'scroll',
]);
return h('hi-waterfall', {
on,
ref: 'waterfall',
attrs: {
numberOfColumns: this.numberOfColumns,
contentInset: this.contentInset,
columnSpacing: this.columnSpacing,
interItemSpacing: this.interItemSpacing,
preloadItemNumber: this.preloadItemNumber,
containBannerView: this.containBannerView,
containPullHeader: this.containPullHeader,
containPullFooter: this.containPullFooter,
},
}, this.$slots.default);
},
});
Vue.component('WaterfallItem', {
inheritAttrs: false,
props: {
type: {
type: [String, Number],
default: '',
},
},
render(h) {
return h('hi-waterfall-item', {
on: { ...this.$listeners },
attrs: {
type: this.type,
},
}, this.$slots.default);
},
});
}
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Register all of native components
*/
const HippyVueNativeComponents = {
install(Vue) {
registerAnimation(Vue);
registerDialog(Vue);
registerUlRefresh(Vue);
registerSwiper(Vue);
registerPull(Vue);
registerWaterfall(Vue);
},
};
export { registerAnimation as AnimationComponent, registerDialog as DialogComponent, registerUlRefresh as ListRefreshComponent, registerPull as PullsComponents, registerSwiper as SwiperComponent, registerWaterfall as WaterfallComponent, HippyVueNativeComponents as default };