diff --git a/package-lock.json b/package-lock.json
index 565fea0..dacfa21 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -101,6 +101,104 @@
"@babel/types": "^7.0.0"
}
},
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.2.tgz",
+ "integrity": "sha512-tdW8+V8ceh2US4GsYdNVNoohq5uVwOf9k6krjwW4E1lINcHgttnWcNqgdoessn12dAy8QkbezlbQh2nXISNY+A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.2.3"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz",
+ "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.3.3",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.11",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz",
+ "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz",
+ "integrity": "sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.2.3",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz",
+ "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==",
+ "dev": true
+ },
+ "@babel/traverse": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz",
+ "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.2.2",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "@babel/parser": "^7.2.3",
+ "@babel/types": "^7.2.2",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.10"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz",
+ "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
"@babel/helper-define-map": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz",
@@ -302,6 +400,16 @@
"@babel/plugin-syntax-async-generators": "^7.2.0"
}
},
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.3.tgz",
+ "integrity": "sha512-XO9eeU1/UwGPM8L+TjnQCykuVcXqaO5J1bkRPIygqZ/A2L1xVMJ9aZXrY31c0U4H2/LHKL4lbFQLsxktSrc/Ng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.3.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
"@babel/plugin-proposal-json-strings": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz",
diff --git a/package.json b/package.json
index 4584c2b..999ea52 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
},
"devDependencies": {
"@babel/core": "7.1.2",
+ "@babel/plugin-proposal-class-properties": "7.3.3",
"@babel/plugin-transform-async-to-generator": "7.1.0",
"@babel/polyfill": "7.0.0",
"@babel/preset-env": "7.1.0",
diff --git a/src/linked-scroll/index.js b/src/linked-scroll/index.js
new file mode 100644
index 0000000..5e5ded1
--- /dev/null
+++ b/src/linked-scroll/index.js
@@ -0,0 +1,2 @@
+export { default as LinkedScrollWrapper } from './linked-scroll-wrapper.jsx';
+export { default as LinkedScrollSection } from './linked-scroll-section.jsx';
diff --git a/src/linked-scroll/linked-scroll-section.jsx b/src/linked-scroll/linked-scroll-section.jsx
new file mode 100644
index 0000000..e77aaeb
--- /dev/null
+++ b/src/linked-scroll/linked-scroll-section.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { LinkedScrollContext } from './linked-scroll-wrapper.jsx';
+
+class LinkedScrollSection extends React.PureComponent {
+ static contextType = LinkedScrollContext;
+
+ componentDidMount () {
+ const { link } = this.context;
+ link(this);
+ }
+
+ componentWillUnmount () {
+ const { unlink } = this.context;
+ unlink(this);
+ }
+
+ render () {
+ const { children } = this.props;
+
+ return children;
+ }
+}
+
+LinkedScrollSection.propTypes = {
+ children: PropTypes.any
+};
+
+export default LinkedScrollSection;
diff --git a/src/linked-scroll/linked-scroll-wrapper.jsx b/src/linked-scroll/linked-scroll-wrapper.jsx
new file mode 100644
index 0000000..be95b55
--- /dev/null
+++ b/src/linked-scroll/linked-scroll-wrapper.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
+
+export const LinkedScrollContext = React.createContext();
+
+class LinkedScrollWrapper extends React.PureComponent {
+ constructor (props) {
+ super(props);
+
+ this.linkComponent = this.linkComponent.bind(this);
+ this.unlinkComponent = this.unlinkComponent.bind(this);
+ this.handleScroll = this.handleScroll.bind(this);
+ this.scrollElements = [];
+
+ this.linkActions = {
+ link: this.linkComponent,
+ unlink: this.unlinkComponent
+ };
+ }
+
+ linkComponent (component) {
+ // eslint-disable-next-line react/no-find-dom-node
+ const node = ReactDOM.findDOMNode(component);
+ const element = {
+ component,
+ node
+ };
+ this.scrollElements.push(element);
+ node.onscroll = this.handleScroll.bind(this, element);
+ }
+
+ unlinkComponent (component) {
+ const componentIndex = this.scrollElements.map(element => element.component).indexOf(component);
+ if (componentIndex !== -1) {
+ this.scrollElements.removeAt(componentIndex);
+ // eslint-disable-next-line react/no-find-dom-node
+ const node = ReactDOM.findDOMNode(component);
+ node.onscroll = null;
+ }
+ }
+
+ handleScroll (element) {
+ window.requestAnimationFrame(() => {
+ this.sync(element);
+ });
+ }
+
+ sync (scrollElement) {
+ this.scrollElements.forEach(element => {
+ if (scrollElement === element) {
+ return;
+ }
+ element.node.onscroll = null;
+ if (element.component.props.linkHorizontal) {
+ element.node.scrollLeft = scrollElement.node.scrollLeft;
+ }
+
+ if (element.component.props.linkVertical) {
+ element.node.scrollTop = scrollElement.node.scrollTop;
+ }
+ window.requestAnimationFrame(() => {
+ element.node.onscroll = this.handleScroll.bind(this, element);
+ });
+ });
+ }
+
+ render () {
+ const { children } = this.props;
+ return (
+
+ {children}
+
+ );
+ }
+}
+
+LinkedScrollWrapper.propTypes = {
+ children: PropTypes.any
+};
+
+export default LinkedScrollWrapper;
diff --git a/src/main.less b/src/main.less
index e836cbb..b43d649 100644
--- a/src/main.less
+++ b/src/main.less
@@ -12,8 +12,6 @@
}
div.qv-object-content-container {
- overflow-x: scroll;
- overflow-y: hidden;
z-index: 110;
}
@@ -151,12 +149,6 @@
width: 350px;
}
- .header-wrapper {
- position: absolute;
- top: 0;
- z-index: 1;
- }
-
/*popups for headers*/
.tooltip {
position: fixed !important;
@@ -168,11 +160,7 @@
/*end popups*/
.row-wrapper {
- position: absolute;
- top: 97px;
height: calc(~"100% - 97px");
- overflow-x: hidden;
- overflow-y: scroll;
padding: 0;
margin-top: 0;
}
@@ -188,30 +176,54 @@
.kpi-table {
width: @KpiTableWidth !important;
overflow: hidden !important;
- display: table;
- height: 100%;
+ height: 100%;
margin: 0;
padding: 0;
- z-index: 100;
position: absolute;
top: 0;
left: 0;
border-right: 1px solid white;
box-shadow: 4px 2px 8px #e1e1e1;
- }
- .kpi-table .row-wrapper {
- overflow: hidden;
+ .row-wrapper {
+ height: calc(~"100% - 97px");
+ overflow: scroll;
+ position: absolute;
+ padding: 0;
+ margin-top: 0;
+ }
}
.data-table {
- width: 272px !important;
- float: left;
- display: table;
height: 100%;
- z-index: 90;
+ width: calc(100% - 243px);
position: absolute;
margin-left: @KpiTableWidth + 13px;
- -ms-overflow-style: none;
+
+ .header-wrapper {
+ overflow: scroll;
+ width: 100%;
+ }
+
+ .row-wrapper {
+ height: calc(~"100% - 97px");
+ width: 100%;
+ overflow: scroll;
+ padding: 0;
+ margin-top: 0;
+ }
+ }
+
+ // hide scrollbars
+ .kpi-table .header-wrapper,
+ .kpi-table .row-wrapper,
+ .data-table .header-wrapper,
+ .data-table .row-wrapper {
+ -ms-overflow-style: none; // IE 10+
+ -moz-overflow: -moz-scrollbars-none; // Firefox
+
+ &::-webkit-scrollbar {
+ display: none; // Safari and Chrome
+ }
}
}
diff --git a/src/paint.jsx b/src/paint.jsx
index 2255a89..29ec860 100644
--- a/src/paint.jsx
+++ b/src/paint.jsx
@@ -4,6 +4,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import HeadersTable from './headers-table/index.jsx';
import DataTable from './data-table/index.jsx';
+import { LinkedScrollWrapper, LinkedScrollSection } from './linked-scroll';
export default async function paint ($element, layout, component) {
const state = await initializeStore({
@@ -13,7 +14,7 @@ export default async function paint ($element, layout, component) {
});
const editmodeClass = component.inAnalysisState() ? '' : 'edit-mode';
const jsx = (
-
+
-
+
+
+
-
-
+
+
+
+
+
+
-
+
);
ReactDOM.render(jsx, $element[0]);
- // TODO: skipped the following as they weren't blockers for letting react handle rendering,
- // they are however the only reason we still depend on jQuery and should be removed as part of unnecessary dependencies issue
- $(`[tid="${layout.qInfo.qId}"] .data-table .row-wrapper`).on('scroll', function () {
- $(`[tid="${layout.qInfo.qId}"] .kpi-table .row-wrapper`).scrollTop($(this).scrollTop());
- });
-
- // freeze first column
- $(`[tid="${layout.qInfo.qId}"] .qv-object-content-container`).on('scroll', (t) => {
- $(`[tid="${layout.qInfo.qId}"] .kpi-table`).css('left', `${Math.round(t.target.scrollLeft)}px`);
- });
-
// TODO: fixing tooltips has a seperate issue, make sure to remove this as part of that issue
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).hover(function () {
$(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(500)
diff --git a/webpack.config.js b/webpack.config.js
index a70a9b1..83f5ec2 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -6,23 +6,16 @@ console.log('Webpack mode:', settings.mode); // eslint-disable-line no-console
const config = {
devtool: 'source-map',
- entry: [
- './src/index.js'
- ],
- mode: settings.mode,
- output: {
- path: settings.buildDestination,
- filename: settings.name + '.js',
- libraryTarget: 'amd'
- },
+ entry: ['./src/index.js'],
externals: {
jquery: {
amd: 'jquery',
commonjs: 'jquery',
commonjs2: 'jquery',
root: '_'
- },
+ }
},
+ mode: settings.mode,
// TODO: breaks core-js for some reason
// resolve: {
// extensions: ['js', 'jsx']
@@ -31,20 +24,23 @@ const config = {
rules: [
{
enforce: 'pre',
- test: /\.(js|jsx)$/,
exclude: /(node_modules|Library)/,
loader: 'eslint-loader',
options: {
failOnError: true
- }
+ },
+ test: /\.(js|jsx)$/
},
{
- test: /\.(js|jsx)$/,
exclude: /node_modules/,
+ test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
- plugins: ['@babel/plugin-transform-async-to-generator'],
+ plugins: [
+ '@babel/plugin-transform-async-to-generator',
+ '@babel/plugin-proposal-class-properties'
+ ],
presets: [
'@babel/preset-env',
'@babel/preset-react'
@@ -54,21 +50,30 @@ const config = {
},
{
test: /.less$/,
- use: ['style-loader', 'css-loader', 'less-loader']
+ use: [
+ 'style-loader',
+ 'css-loader',
+ 'less-loader'
+ ]
}
]
},
+ output: {
+ filename: `${settings.name}.js`,
+ libraryTarget: 'amd',
+ path: settings.buildDestination
+ },
plugins: [
new CopyWebpackPlugin([
- 'assets/' + settings.name + '.qext',
- 'assets/' + settings.name + '.png',
+ `assets/${settings.name}.qext`,
+ `assets/${settings.name}.png`,
'assets/wbfolder.wbl',
+ 'resources/Excel.png',
// TODO: remove entries below this line
'resources/Accounts.csv',
'resources/Accounts2.csv',
- 'resources/QlikLook.csv',
- 'resources/Excel.png',
+ 'resources/QlikLook.csv'
], {}),
new StyleLintPlugin()
]