Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How about if the table gets resized ? #3

Open
turigeza opened this issue May 30, 2019 · 7 comments
Open

How about if the table gets resized ? #3

turigeza opened this issue May 30, 2019 · 7 comments

Comments

@turigeza
Copy link

turigeza commented May 30, 2019

While I like how simple it is : )

  1. If the orientation of the device changes or the window size changes with it the table size changes too and the columns-resize-bar(s) will be out of position.

  2. Also it is not possible to programatically apply a column size for the same reason.

  3. Column order changes also not possible for the same reason.

@turigeza
Copy link
Author

turigeza commented May 31, 2019

After a bit of messing with the code I ended up rewriting it so it matches my requirements a bit closer. I am posting it here because it relates to the issue above and you might use some of the ideas in your package.

The biggest compromise I made was that I limited the resize handle to the header. I add the handle to the th elements. I can live with it like that : )

I needed the following.

  1. Handle position should be adjusted to header regardless of orientation, window resize or reordering of the columns.
  2. Option to adjust table size instead of the neighbouring columns.
  3. Some custom options like z-index and handleWidth etc ...
  4. Callback function afterResize so one can save the data if needed. Maybe it should have been a custom event instead.
  5. Ability to revert to original column width.
  6. Attache and remove that event listener mousemove so it uses less cpu when not in use.
  7. Column sizes can be changed directly on the columns and it doesn't upset the resizing.

In retrospect it should possibly be a component instead of a custom directive but hey ...
Thank you for the original code.

export default ({ Vue }) => {
    Vue.directive('columns-resizable', {
        inserted (el, binding) {
            if (el.nodeName !== 'TABLE') { return console.error('This directive is only valid on a table!'); }

            const opt = binding.value || {};
            const table = el;
            const thead = table.querySelector('thead');
            const ths = thead.querySelectorAll('th');
            const calcTableWidth = () => {
                let tableWidth = 0;
                let width = 0;

                ths.forEach((th) => {
                    if (th.style.width) {
                        width = Number(parseInt(th.style.width));
                    } else {
                        width = th.offsetWidth;
                    }
                    tableWidth += width;
                }, 0);

                return tableWidth;
            };
            const applyTableWidth = () => {
                if (opt.fixedWidthTable) {
                    table.style.width = calcTableWidth();
                    table.style.maxWidth = 'none';
                } else if (!table.style.width) {
                    table.style.width = '100%';
                    table.style.maxWidth = '100%';
                }
            };
            const handleResize = e => {
                if (opt.resizable === false) return;

                if (!opt.fixedWidthTable) {
                    activeTh.style.width = parseInt(activeTh.style.width) + e.movementX + 'px';
                    neighbourghTh.style.width = parseInt(neighbourghTh.style.width) - e.movementX + 'px';
                } else {
                    activeTh.style.width = parseInt(activeTh.style.width) + e.movementX + 'px';
                    table.style.width = parseInt(table.style.width) + e.movementX + 'px';
                }
            };

            let activeTh = null; // the th being resized
            let neighbourghTh = null; // the next except when the last column is being resized in that case it is the previous
            let resizing = false; // a resize started needed because we can not detect event handler was attached or not
            table.style.position = 'relative';

            applyTableWidth();

            ths.forEach((th, index) => {
                // initilise the width if th does not already have it
                if (!th.style.width) {
                    th.style.width = th.offsetWidth + 'px';
                }

                th.originalWidth = th.style.width;
                const bar = document.createElement('div');

                bar.style.position = 'absolute';
                bar.style.right = 0;
                bar.style.top = 0;
                bar.style.bottom = 0;
                bar.style.cursor = 'col-resize';

                // customisable options
                bar.style.width = opt.handleWidth || '8px';
                bar.style.zIndex = opt.zIndex || 1;
                bar.className = opt.handleClass || 'columns-resize-bar';

                bar.addEventListener('mousedown', (e) => {
                    // element with a fixedsize attribute will be ignored
                    if (e.target.parentElement.getAttribute('fixedsize')) {
                        return;
                    }
                    resizing = true;
                    document.body.addEventListener('mousemove', handleResize);
                    document.body.style.cursor = 'col-resize';
                    document.body.style.userSelect = 'none';

                    activeTh = e.target.parentElement;
                    neighbourghTh = activeTh.nextElementSibling;
                    if (!neighbourghTh) {
                        neighbourghTh = activeTh.previousElementSibling;
                    }

                    // calculate table size if the table width is fixed
                    if (!opt.fixedWidthTable) {
                        let tableWidth = ths.reduce((a, th) => {
                            return a + Number(parseInt(th.style.width));
                        }, 0);
                        table.style.width = tableWidth + 'px';
                    }
                });

                th.appendChild(bar);
            });

            document.addEventListener('mouseup', () => {
                if (!resizing) return;
                resizing = false;
                document.body.removeEventListener('mousemove', handleResize);
                document.body.style.cursor = '';
                document.body.style.userSelect = '';
                if (typeof opt.afterResize === 'function') {
                    opt.afterResize(ths);
                }
            });

            // resets the column sizes
            el.$resetColumnSizes = () => {
                ths.forEach((th) => {
                    th.style.width = th.originalWidth;
                }, 0);
                applyTableWidth();
            };
        }
    });
};

@astanley86
Copy link

astanley86 commented Aug 26, 2021

Thank you for posting this. I have the same use case.

When using your above code, the resize only appears on the last column. I am using your code + https://github.com/SortableJS/Vue.Draggable/blob/master/example/components/table-column-example.vue so that the columns can be re-ordered.

Do you have any thoughts why the only TH that is resizable is the last one?

@astanley86
Copy link

Fixed it! I needed to add style="position:relative;" to my elements. Your code works beautifully.

@IngmarVoigt2
Copy link

Thx so much @turigeza and @astanley86 for posting these things here! Actually I would love to re-use your solution, however I have some trouble to make it work (I pasted into src/plugins/vue-columns-resizable/index.js which keeps throwing errors). Do you have a simple working example to try? Thx a lot in advance!

@IngmarVoigt2
Copy link

Actually I wonder whether this could be contributed back upstream into https://github.com/vuetifyjs/vuetify/tree/master/packages/vuetify/src/components/VDataTable or so?

@IngmarVoigt2
Copy link

@astanley86 with a bit of debugging & fixes I actually got to exactly your question:

Do you have any thoughts why the only TH that is resizable is the last one?

However I do not understand your answer

Fixed it! I needed to add style="position:relative;" to my elements

Which elements do you refer to exactly? The table?

@IngmarVoigt2
Copy link

nevermind @astanley86 @turigeza I realized this was abt the table header elements

IngmarVoigt2 pushed a commit to IngmarVoigt2/vue-columns-resizable that referenced this issue Apr 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants