Skip to content
This repository has been archived by the owner on Nov 24, 2021. It is now read-only.

Commit

Permalink
Merge branch 'release/5.0.0' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
budnix committed Aug 5, 2020
2 parents c501b07 + 4d3ae4b commit 458ae75
Show file tree
Hide file tree
Showing 11 changed files with 2,465 additions and 2,183 deletions.
5 changes: 5 additions & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sandboxes": [
"github/handsontable/examples/tree/master/vue/pull-request"
]
}
4 changes: 2 additions & 2 deletions .config/minified.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { baseConfig } from './base';
import { addLicenseBanner } from './helpers/licenseBanner';
import { uglify } from 'rollup-plugin-uglify';
import { terser } from 'rollup-plugin-terser';

const minFilename = 'vue-handsontable.min.js';

Expand All @@ -18,7 +18,7 @@ const minConfig = {
}
},
plugins: baseConfig.plugins.concat([
uglify({
terser({
output: {
comments: /^!/
},
Expand Down
4,214 changes: 2,073 additions & 2,141 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@handsontable/vue",
"version": "4.1.1",
"version": "5.0.0",
"description": "Best Data Grid for Vue with Spreadsheet Look and Feel.",
"author": "Handsoncode <hello@handsoncode.net> (https://handsoncode.net)",
"homepage": "https://handsontable.com",
Expand Down Expand Up @@ -44,7 +44,7 @@
"url": "https://github.com/handsontable/vue-handsontable-official/issues"
},
"peerDependencies": {
"handsontable": ">=7.2.2",
"handsontable": ">=8.0.0",
"vue": "^2.5.0"
},
"devDependencies": {
Expand All @@ -56,13 +56,13 @@
"@babel/preset-typescript": "^7.3.3",
"@babel/runtime": "^7.4.5",
"@types/jest": "^24.0.14",
"@vue/test-utils": "^1.0.0-beta.29",
"@vue/test-utils": "^1.0.3",
"babel-core": "^7.0.0-bridge.0",
"cpy-cli": "^2.0.0",
"cpy-cli": "^3.1.1",
"cross-env": "^5.2.0",
"css-loader": "^3.0.0",
"del-cli": "^2.0.0",
"handsontable": "^7.2.2",
"del-cli": "^3.0.1",
"handsontable": "^8.0.0",
"jest": "^24.8.0",
"rollup": "^1.15.4",
"rollup-plugin-babel": "^4.3.2",
Expand All @@ -71,11 +71,11 @@
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.0.2",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-terser": "^5.3.0",
"rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-typescript2": "^0.21.1",
"rollup-plugin-uglify": "^6.0.2",
"rollup-plugin-vue": "^5.0.0",
"typescript": "^3.5.2",
"typescript": "^3.7.3",
"vue": "^2.6.10",
"vue-class-component": "^7.1.0",
"vue-jest": "^3.0.4",
Expand Down
91 changes: 82 additions & 9 deletions src/HotTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
propFactory,
preventInternalEditWatch,
prepareSettings,
filterPassedProps,
createVueComponent,
findVNodeByType,
getHotColumnComponents
Expand All @@ -20,7 +19,6 @@
HotTableMethods,
HotTableProps,
HotTableComponent,
VueProps,
EditorComponent
} from './types';
import * as packageJson from '../package.json';
Expand All @@ -31,8 +29,32 @@
name: 'HotTable',
props: propFactory('HotTable'),
watch: {
mergedHotSettings: function(value) {
this.hotInstance.updateSettings(value);
mergedHotSettings: function (value) {
if (value.data) {
if (
this.hotInstance.isColumnModificationAllowed() ||
(
!this.hotInstance.isColumnModificationAllowed() &&
this.hotInstance.countSourceCols() === this.miscCache.currentSourceColumns
)
) {
// If the dataset dimensions change, update the index mappers.
this.matchHotMappersSize(value.data);
// Data is automatically synchronized by reference.
delete value.data;
}
}
// If there are another options changed, update the HOT settings, render the table otherwise.
if (Object.keys(value).length) {
this.hotInstance.updateSettings(value);
} else {
this.hotInstance.render();
}
this.miscCache.currentSourceColumns = this.hotInstance.countSourceCols();
}
},
data: function () {
Expand All @@ -48,15 +70,18 @@
return {
__internalEdit: false,
miscCache: {
currentSourceColumns: null
},
hotInstance: null,
columnSettings: null,
rendererCache: rendererCache,
editorCache: new Map()
}
};
},
computed: {
mergedHotSettings: function(): Handsontable.GridSettings {
return prepareSettings(this.$props);
mergedHotSettings: function (): Handsontable.GridSettings {
return prepareSettings(this.$props, this.hotInstance ? this.hotInstance.getSettings() : void 0);
}
},
methods: {
Expand Down Expand Up @@ -87,6 +112,54 @@
this.hotInstance.init();
preventInternalEditWatch(this);
this.miscCache.currentSourceColumns = this.hotInstance.countSourceCols();
},
matchHotMappersSize: function (data: any[][]): void {
const rowsToRemove: number[] = [];
const columnsToRemove: number[] = [];
const indexMapperRowCount = this.hotInstance.rowIndexMapper.getNumberOfIndexes();
const isColumnModificationAllowed = this.hotInstance.isColumnModificationAllowed();
let indexMapperColumnCount = 0;
if (data && data.length !== indexMapperRowCount) {
if (data.length < indexMapperRowCount) {
for (let r = data.length; r < indexMapperRowCount; r++) {
rowsToRemove.push(r);
}
}
}
if (isColumnModificationAllowed) {
indexMapperColumnCount = this.hotInstance.columnIndexMapper.getNumberOfIndexes();
if (data && data[0] && data[0]?.length !==
indexMapperColumnCount) {
if (data[0].length < indexMapperColumnCount) {
for (let c = data[0].length; c < indexMapperColumnCount; c++) {
columnsToRemove.push(c);
}
}
}
}
this.hotInstance.batch(() => {
if (rowsToRemove.length > 0) {
this.hotInstance.rowIndexMapper.removeIndexes(rowsToRemove);
} else {
this.hotInstance.rowIndexMapper.insertIndexes(indexMapperRowCount - 1, data.length - indexMapperRowCount);
}
if (isColumnModificationAllowed) {
if (columnsToRemove.length > 0) {
this.hotInstance.columnIndexMapper.removeIndexes(columnsToRemove);
} else {
this.hotInstance.columnIndexMapper.insertIndexes(indexMapperColumnCount - 1, data[0].length - indexMapperColumnCount);
}
}
});
},
getGlobalRendererVNode: function (): VNode | null {
const hotTableSlots: VNode[] = this.$slots.default || [];
Expand Down Expand Up @@ -145,7 +218,7 @@
};
if (rendererCache && !rendererCache.has(`${row}-${col}`)) {
const mountedComponent: Vue = createVueComponent(vNode, containerComponent, {}, rendererArgs);
const mountedComponent: Vue = createVueComponent(vNode, containerComponent, vNode.componentOptions.propsData, rendererArgs);
rendererCache.set(`${row}-${col}`, {
component: mountedComponent,
Expand Down Expand Up @@ -187,7 +260,7 @@
let mountedComponent: EditorComponent = null;
if (!editorCache.has(componentName)) {
mountedComponent = createVueComponent(vNode, containerComponent, {}, {isEditor: true});
mountedComponent = createVueComponent(vNode, containerComponent, vNode.componentOptions.propsData, {isEditor: true});
editorCache.set(componentName, mountedComponent);
Expand Down
38 changes: 32 additions & 6 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ export function preventInternalEditWatch(component) {
export function propFactory(source): VueProps<HotTableProps> {
const registeredHooks: string[] = Handsontable.hooks.getRegistered();

const propSchema: VueProps<HotTableProps> = new Handsontable.DefaultSettings();
let propSchema: VueProps<HotTableProps> = {};
Object.assign(propSchema, Handsontable.DefaultSettings);

for (let prop in propSchema) {
propSchema[prop] = {
Expand Down Expand Up @@ -137,23 +138,35 @@ export function filterPassedProps(props) {
/**
* Prepare the settings object to be used as the settings for Handsontable, based on the props provided to the component.
*
* @param {Object} props The props passed to the component.
* @returns {Object} An object containing the properties, ready to be used within Handsontable.
* @param {HotTableProps} props The props passed to the component.
* @param {Handsontable.GridSettings} currentSettings The current Handsontable settings.
* @returns {Handsontable.GridSettings} An object containing the properties, ready to be used within Handsontable.
*/
export function prepareSettings(props: HotTableProps): Handsontable.GridSettings {
export function prepareSettings(props: HotTableProps, currentSettings?: Handsontable.GridSettings): Handsontable.GridSettings {
const assignedProps: VueProps<HotTableProps> = filterPassedProps(props);
const hotSettingsInProps: {} = props.settings ? props.settings : assignedProps;
const additionalHotSettingsInProps: Handsontable.GridSettings = props.settings ? assignedProps : null;
const newSettings = {};

for (const key in hotSettingsInProps) {
if (hotSettingsInProps.hasOwnProperty(key) && hotSettingsInProps[key] !== void 0) {
if (
hotSettingsInProps.hasOwnProperty(key) &&
hotSettingsInProps[key] !== void 0 &&
((currentSettings && key !== 'data') ? !simpleEqual(currentSettings[key], hotSettingsInProps[key]) : true)
) {
newSettings[key] = hotSettingsInProps[key];
}
}

for (const key in additionalHotSettingsInProps) {
if (key !== 'id' && key !== 'settings' && key !== 'wrapperRendererCacheSize' && additionalHotSettingsInProps.hasOwnProperty(key) && additionalHotSettingsInProps[key] !== void 0) {
if (
additionalHotSettingsInProps.hasOwnProperty(key) &&
key !== 'id' &&
key !== 'settings' &&
key !== 'wrapperRendererCacheSize' &&
additionalHotSettingsInProps[key] !== void 0 &&
((currentSettings && key !== 'data') ? !simpleEqual(currentSettings[key], additionalHotSettingsInProps[key]) : true)
) {
newSettings[key] = additionalHotSettingsInProps[key];
}
}
Expand Down Expand Up @@ -221,3 +234,16 @@ export function createVueComponent(vNode: VNode, parent: Vue, props: object, dat

return (new (vNode.componentOptions as any).Ctor(settings)).$mount(componentContainer);
}

/**
* Compare two objects using `JSON.stringify`.
* *Note: * As it's using the stringify function to compare objects, the property order in both objects is
* important. It will return `false` for the same objects, if they're defined in a different order.
*
* @param {object} objectA First object to compare.
* @param {object} objectB Second object to compare.
* @returns {boolean} `true` if they're the same, `false` otherwise.
*/
function simpleEqual(objectA, objectB) {
return JSON.stringify(objectA) === JSON.stringify(objectB);
}
13 changes: 9 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import Handsontable from 'handsontable';
import Vue, { VNode } from 'vue';
import { ThisTypedComponentOptionsWithRecordProps } from 'vue/types/options';
import { HotTableData, HotTableMethods, HotTableProps } from './types';

export interface HotTableData {
__internalEdit: boolean,
miscCache?: {
currentSourceColumns?: number
},
hotInstance?: Handsontable,
columnSettings: HotTableProps[]
columnSettings: HotTableProps[],
rendererCache: any, // temporary `any`, TODO: use the LRU definition here
editorCache: Map<string, EditorComponent>
}

export interface HotTableMethods {
Expand All @@ -15,12 +19,13 @@ export interface HotTableMethods {
getGlobalRendererVNode: () => VNode | void,
getGlobalEditorVNode: () => VNode | void,
getRendererWrapper: (vNode: VNode, containerComponent: Vue) => (...args) => HTMLElement,
getEditorClass: (vNode: VNode, containerComponent: Vue) => typeof Handsontable.editors.BaseEditor
getEditorClass: (vNode: VNode, containerComponent: Vue) => typeof Handsontable.editors.BaseEditor,
matchHotMappersSize: (data: any[][]) => void
}

export interface HotTableProps extends Handsontable.GridSettings {
id?: string,
settings?: Handsontable.DefaultSettings,
settings?: Handsontable.GridSettings,
wrapperRendererCacheSize?: number
}

Expand Down
15 changes: 15 additions & 0 deletions test/_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,18 @@ export function mockClientDimensions(element, width, height) {
value: height
});
}

/**
* Create a temporary DOM container for the components to be placed in.
* It should be replaced with the actual mounted component from `@vue/test-utils`, so there's no need to remove it
* after the test case is finished.
*/
export function createDomContainer() {
const container = document.createElement('div');

if (document.body) {
document.body.appendChild(container);
}

return container;
}
14 changes: 9 additions & 5 deletions test/hotColumn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import HotTable from '../src/HotTable.vue';
import HotColumn from '../src/HotColumn.vue';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import { createSampleData, mockClientDimensions } from './_helpers';
import {
createDomContainer,
createSampleData,
mockClientDimensions
} from './_helpers';

describe('createColumnSettings', () => {
it('should create the column settings based on the data provided to the `hot-column` component and its child components', () => {
Expand Down Expand Up @@ -84,7 +88,7 @@ describe('createColumnSettings', () => {
});

let testWrapper = mount(App, {
attachToDocument: true
attachTo: createDomContainer()
});
const hotTableComponent = testWrapper.vm.$children[0];

Expand Down Expand Up @@ -163,7 +167,7 @@ describe('renderer cache', () => {
});

let testWrapper = mount(App, {
attachToDocument: true
attachTo: createDomContainer()
});
const hotTableComponent = testWrapper.vm.$children[0];

Expand Down Expand Up @@ -221,7 +225,7 @@ describe('renderer cache', () => {
});

let testWrapper = mount(App, {
attachToDocument: true
attachTo: createDomContainer()
});
const hotTableComponent = testWrapper.vm.$children[0];

Expand Down Expand Up @@ -290,7 +294,7 @@ describe('hot-column children', () => {
});

let testWrapper = mount(App, {
attachToDocument: true
attachTo: createDomContainer()
});
const hotTableComponent = testWrapper.vm.$children[0];

Expand Down

0 comments on commit 458ae75

Please sign in to comment.