<commit_before>import { Actions, helper } from '@appbaseio/reactivecore';
import VueTypes from 'vue-types';
import { componentTypes } from '@appbaseio/reactivecore/lib/utils/constants';
import Pagination from './addons/Pagination.jsx';
import PoweredBy from './addons/PoweredBy.jsx';
import ResultListWrapper from './addons/ResultListWrapper.jsx';
import ResultCardsWrapper from './addons/ResultCardsWrapper.jsx';
import {
	connect,
	isFunction,
	hasCustomRenderer,
	getComponent,
	getValidPropsKeys,
	updateDefaultQuery,
	isQueryIdentical,
} from '../../utils/index';
import Flex from '../../styles/Flex';
import types from '../../utils/vueTypes';
import { resultStats, sortOptions } from '../../styles/results';

const {
	addComponent,
	removeComponent,
	setStreaming,
	watchComponent,
	setQueryOptions,
	updateQuery,
	loadMore,
	setValue,
	setQueryListener,
	setComponentProps,
	updateComponentProps,
	setDefaultQuery,
} = Actions;

const {
	isEqual,
	getQueryOptions,
	pushToAndClause,
	getClassName,
	parseHits,
	getOptionsFromQuery,
	getCompositeAggsQuery,
	checkSomePropChange,
	getResultStats,
} = helper;

const ReactiveList = {
	name: 'ReactiveList',
	components: {
		ResultListWrapper,
		ResultCardsWrapper,
	},
	data() {
		const props = this.$props;
		let currentPageState = 0;
		if (props.currentPage) {
			currentPageState = Math.max(props.currentPage - 1, 0);
		}

		this.__state = {
			from: currentPageState * props.size,
			isLoading: true,
			currentPageState,
		};
		return this.__state;
	},
	created() {
		// no support for pagination and aggregationField together
		if (this.pagination && this.aggregationField) {
			console.warn(
				'Pagination is not supported when aggregationField is present. The list will be rendered with infinite scroll',
			);
		}
		if (this.defaultPage >= 0) {
			this.currentPageState = this.defaultPage;
			this.from = this.currentPageState * this.$props.size;
		}
		this.isLoading = true;
		this.internalComponent = `${this.$props.componentId}__internal`;
		const onQueryChange = (...args) => {
			this.$emit('queryChange', ...args);
		};
		const onError = e => {
			this.$emit('error', e);
		};
		this.sortOptionIndex = this.defaultSortOption
			? this.sortOptions.findIndex(s => s.label === this.defaultSortOption)
			: 0;
		this.setQueryListener(this.$props.componentId, onQueryChange, onError);
		this.setComponentProps(this.componentId, { ...this.$props, from: this.from}, componentTypes.reactiveList);
		this.setComponentProps(this.internalComponent, { ...this.$props, from: this.from}, componentTypes.reactiveList);
	},
	props: {
		currentPage: VueTypes.number.def(0),
		includeFields: types.includeFields.def(['*']),
		// component props
		className: types.string,
		componentId: types.stringRequired,
		dataField: types.stringRequired,
		aggregationField: types.string,
		defaultQuery: types.func,
		defaultSortOption: types.string,
		excludeFields: types.excludeFields.def([]),
		innerClass: types.style,
		listClass: VueTypes.string.def(''),
		loader: types.title,
		render: types.func,
		renderItem: types.func,
		renderNoResults: VueTypes.any.def('No Results found.'),
		renderError: types.title,
		renderResultStats: types.func,
		pages: VueTypes.number.def(5),
		pagination: VueTypes.bool.def(false),
		paginationAt: types.paginationAt.def('bottom'),
		react: types.react,
		showResultStats: VueTypes.bool.def(true),
		showEndPage: VueTypes.bool.def(false),
		size: VueTypes.number.def(10),
		sortBy: types.sortBy,
		sortOptions: types.sortOptions,
		stream: types.bool,
		URLParams: VueTypes.bool.def(false),
	},
	computed: {
		shouldRenderPagination() {
			return this.pagination && !this.aggregationField;
		},
		totalPages() {
			return Math.ceil(this.total / this.$props.size) || 0;
		},
		hasPageChangeListener() {
			return this.$listeners && this.$listeners.pageChange;
		},
		hasResultStatsListener() {
			return this.$listeners && this.$listeners.resultStats;
		},
		stats() {
			const { filteredResults } = this.getAllData();
			return {
				...getResultStats(this),
				currentPage: this.currentPageState,
				displayedResults: filteredResults.length,
			};
		},
		hasCustomRender() {
			return hasCustomRenderer(this);
		},
	},
	watch: {
		$props: {
			deep: true,
			handler(newVal) {
				const propsKeys = getValidPropsKeys(newVal);
				checkSomePropChange(newVal, this.componentProps, propsKeys, () => {
					this.updateComponentProps(
						this.componentId,
						newVal,
						componentTypes.reactiveList,
					);
					this.updateComponentProps(
						this.internalComponent,
						newVal,
						componentTypes.reactiveList,
					);
				});
			},
		},
		sortOptions(newVal, oldVal) {
			if (!isEqual(oldVal, newVal)) {
				this.updateQueryOptions(this.$props);
			}
		},
		sortBy(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		size(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		dataField(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		includeFields(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		excludeFields(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		defaultQuery(newVal, oldVal) {
			if (!isQueryIdentical(newVal, oldVal, null, this.$props)) {
				let options = getQueryOptions(this.$props);
				options.from = 0;
				this.$defaultQuery = newVal(null, this.$props);
				const { sort, ...query } = this.$defaultQuery;

				if (sort) {
					options.sort = this.$defaultQuery.sort;
				}
				const queryOptions = getOptionsFromQuery(this.$defaultQuery);
				if (queryOptions) {
					options = { ...options, ...getOptionsFromQuery(this.$defaultQuery) };
				}
				this.setQueryOptions(
					this.$props.componentId,
					{ ...options, ...this.getAggsQuery() },
					!query,
				);

				this.updateQuery(
					{
						componentId: this.internalComponent,
						query,
					},
					true,
				); // reset page because of query change
				// Update calculated default query in store
				updateDefaultQuery(this.componentId, this.setDefaultQuery, this.$props);
				this.currentPageState = 0;
				this.from = 0;
			}
		},
		stream(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.setStreaming(this.$props.componentId, newVal);
			}
		},
		react(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.setReact(this.$props);
			}
		},
		streamHits() {
			this.$emit('data', this.getData());
		},
		promotedResults(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		hidden(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		time(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		hits(newVal, oldVal) {
			this.$emit('data', this.getAllData());
			if (this.shouldRenderPagination) {
				// called when page is changed
				if (this.isLoading && (oldVal || newVal)) {
					if (this.hasPageChangeListener) {
						this.$emit('pageChange', this.currentPageState + 1, this.totalPages);
					} else {
						window.scrollTo(0, 0);
					}
					this.isLoading = false;
				}
			} else if (oldVal && newVal) {
				if (oldVal.length !== newVal.length || newVal.length === this.$props.total) {
					this.isLoading = false;

					if (newVal.length < oldVal.length) {
						// query has changed
						window.scrollTo(0, 0);
						this.from = 0;
					}
				}
			} else if ((!oldVal || !oldVal.length) && newVal) {
				this.isLoading = false;
			}
		},
		rawData(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		total(newVal, oldVal) {
			if (this.shouldRenderPagination && newVal !== oldVal) {
				let currentPage = this.$data.total ? 0 : this.currentPageState;
				if (this.defaultPage >= 0) currentPage = this.defaultPage;<commit_msg>feat(vue): add scrollOnChange option to prevent scrolling to top (#1505)<commit_after>import { Actions, helper } from '@appbaseio/reactivecore';
import VueTypes from 'vue-types';
import { componentTypes } from '@appbaseio/reactivecore/lib/utils/constants';
import Pagination from './addons/Pagination.jsx';
import PoweredBy from './addons/PoweredBy.jsx';
import ResultListWrapper from './addons/ResultListWrapper.jsx';
import ResultCardsWrapper from './addons/ResultCardsWrapper.jsx';
import {
	connect,
	isFunction,
	hasCustomRenderer,
	getComponent,
	getValidPropsKeys,
	updateDefaultQuery,
	isQueryIdentical,
} from '../../utils/index';
import Flex from '../../styles/Flex';
import types from '../../utils/vueTypes';
import { resultStats, sortOptions } from '../../styles/results';

const {
	addComponent,
	removeComponent,
	setStreaming,
	watchComponent,
	setQueryOptions,
	updateQuery,
	loadMore,
	setValue,
	setQueryListener,
	setComponentProps,
	updateComponentProps,
	setDefaultQuery,
} = Actions;

const {
	isEqual,
	getQueryOptions,
	pushToAndClause,
	getClassName,
	parseHits,
	getOptionsFromQuery,
	getCompositeAggsQuery,
	checkSomePropChange,
	getResultStats,
} = helper;

const ReactiveList = {
	name: 'ReactiveList',
	components: {
		ResultListWrapper,
		ResultCardsWrapper,
	},
	data() {
		const props = this.$props;
		let currentPageState = 0;
		if (props.currentPage) {
			currentPageState = Math.max(props.currentPage - 1, 0);
		}

		this.__state = {
			from: currentPageState * props.size,
			isLoading: true,
			currentPageState,
		};
		return this.__state;
	},
	created() {
		// no support for pagination and aggregationField together
		if (this.pagination && this.aggregationField) {
			console.warn(
				'Pagination is not supported when aggregationField is present. The list will be rendered with infinite scroll',
			);
		}
		if (this.defaultPage >= 0) {
			this.currentPageState = this.defaultPage;
			this.from = this.currentPageState * this.$props.size;
		}
		this.isLoading = true;
		this.internalComponent = `${this.$props.componentId}__internal`;
		const onQueryChange = (...args) => {
			this.$emit('queryChange', ...args);
		};
		const onError = e => {
			this.$emit('error', e);
		};
		this.sortOptionIndex = this.defaultSortOption
			? this.sortOptions.findIndex(s => s.label === this.defaultSortOption)
			: 0;
		this.setQueryListener(this.$props.componentId, onQueryChange, onError);
		this.setComponentProps(this.componentId, { ...this.$props, from: this.from}, componentTypes.reactiveList);
		this.setComponentProps(this.internalComponent, { ...this.$props, from: this.from}, componentTypes.reactiveList);
	},
	props: {
		currentPage: VueTypes.number.def(0),
		includeFields: types.includeFields.def(['*']),
		// component props
		className: types.string,
		componentId: types.stringRequired,
		dataField: types.stringRequired,
		aggregationField: types.string,
		defaultQuery: types.func,
		defaultSortOption: types.string,
		excludeFields: types.excludeFields.def([]),
		innerClass: types.style,
		listClass: VueTypes.string.def(''),
		loader: types.title,
		render: types.func,
		renderItem: types.func,
		renderNoResults: VueTypes.any.def('No Results found.'),
		renderError: types.title,
		renderResultStats: types.func,
		pages: VueTypes.number.def(5),
		pagination: VueTypes.bool.def(false),
		paginationAt: types.paginationAt.def('bottom'),
		react: types.react,
		scrollOnChange: VueTypes.bool.def(true),
		showResultStats: VueTypes.bool.def(true),
		showEndPage: VueTypes.bool.def(false),
		size: VueTypes.number.def(10),
		sortBy: types.sortBy,
		sortOptions: types.sortOptions,
		stream: types.bool,
		URLParams: VueTypes.bool.def(false),
	},
	computed: {
		shouldRenderPagination() {
			return this.pagination && !this.aggregationField;
		},
		totalPages() {
			return Math.ceil(this.total / this.$props.size) || 0;
		},
		hasPageChangeListener() {
			return this.$listeners && this.$listeners.pageChange;
		},
		hasResultStatsListener() {
			return this.$listeners && this.$listeners.resultStats;
		},
		stats() {
			const { filteredResults } = this.getAllData();
			return {
				...getResultStats(this),
				currentPage: this.currentPageState,
				displayedResults: filteredResults.length,
			};
		},
		hasCustomRender() {
			return hasCustomRenderer(this);
		},
	},
	watch: {
		$props: {
			deep: true,
			handler(newVal) {
				const propsKeys = getValidPropsKeys(newVal);
				checkSomePropChange(newVal, this.componentProps, propsKeys, () => {
					this.updateComponentProps(
						this.componentId,
						newVal,
						componentTypes.reactiveList,
					);
					this.updateComponentProps(
						this.internalComponent,
						newVal,
						componentTypes.reactiveList,
					);
				});
			},
		},
		sortOptions(newVal, oldVal) {
			if (!isEqual(oldVal, newVal)) {
				this.updateQueryOptions(this.$props);
			}
		},
		sortBy(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		size(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		dataField(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		includeFields(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		excludeFields(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.updateQueryOptions(this.$props);
			}
		},
		defaultQuery(newVal, oldVal) {
			if (!isQueryIdentical(newVal, oldVal, null, this.$props)) {
				let options = getQueryOptions(this.$props);
				options.from = 0;
				this.$defaultQuery = newVal(null, this.$props);
				const { sort, ...query } = this.$defaultQuery;

				if (sort) {
					options.sort = this.$defaultQuery.sort;
				}
				const queryOptions = getOptionsFromQuery(this.$defaultQuery);
				if (queryOptions) {
					options = { ...options, ...getOptionsFromQuery(this.$defaultQuery) };
				}
				this.setQueryOptions(
					this.$props.componentId,
					{ ...options, ...this.getAggsQuery() },
					!query,
				);

				this.updateQuery(
					{
						componentId: this.internalComponent,
						query,
					},
					true,
				); // reset page because of query change
				// Update calculated default query in store
				updateDefaultQuery(this.componentId, this.setDefaultQuery, this.$props);
				this.currentPageState = 0;
				this.from = 0;
			}
		},
		stream(newVal, oldVal) {
			if (oldVal !== newVal) {
				this.setStreaming(this.$props.componentId, newVal);
			}
		},
		react(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.setReact(this.$props);
			}
		},
		streamHits() {
			this.$emit('data', this.getData());
		},
		promotedResults(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		hidden(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		time(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		hits(newVal, oldVal) {
			this.$emit('data', this.getAllData());
			if (this.shouldRenderPagination) {
				// called when page is changed
				if (this.isLoading && (oldVal || newVal)) {
					if (this.hasPageChangeListener) {
						this.$emit('pageChange', this.currentPageState + 1, this.totalPages);
					} else {
						if (this.scrollOnChange) {
							window.scrollTo(0, 0);
						}
					}
					this.isLoading = false;
				}
			} else if (oldVal && newVal) {
				if (oldVal.length !== newVal.length || newVal.length === this.$props.total) {
					this.isLoading = false;

					if (newVal.length < oldVal.length) {
						// query has changed
						if (this.scrollOnChange) {
							window.scrollTo(0, 0);
						}
						this.from = 0;
					}
				}
			} else if ((!oldVal || !oldVal.length) && newVal) {
				this.isLoading = false;
			}
		},
		rawData(newVal, oldVal) {
			if (!isEqual(newVal, oldVal)) {
				this.$emit('data', this.getData());
			}
		},
		total(newVal, oldVal) {
			if (this.shouldRenderPagination && newVal !== oldVal) {
				let currentPage = this.$data.total ? 0 : this.currentPageState;
				if (this.defaultPage >= 0) currentPage = this.defaultPage;
{'fraction_non_alphanumeric': 0.1468298109010011, 'fraction_numerical': 0.002842664689160796, 'mean_word_length': 3.5330532212885153, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6535353', 'n_tokens_mistral': 6383, 'n_tokens_neox': 5349, 'n_words': 1006}