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

Adding more than one panel at once can cause incorrect/reversed panel order, resize behavior #202

Open
Billiam opened this issue Feb 11, 2024 · 0 comments

Comments

@Billiam
Copy link

Billiam commented Feb 11, 2024

When adding two or more panes at once, the internal order of panes can be reversed, leading to reversed splitter behavior (ex: drag left, splitter goes right), and a bunch of other weird issues.

When adding a new pane, Pane.onPaneAdd finds the position of the pane's dom element in the parent node.

This works when only one new panel is being added.

In my case, I have 3 panels, two of which are hidden when the page width is too small:

<Splitpanes>
  <Pane v-if="largePage"></Pane>
  <Pane v-if="largePage"></Pane>
  <Pane></Pane
</Splitpanes>

when largePage changes to true, both of the first two panes are added to the dom, and whenPaneAdd is fired twice by the Pane component's mount hook.

I don't think there's a guaranteed order for this, behavior, so the middle pane in my case fires first.

At this time, the this.panes array contains only one pane.

it finds the index correctly (1), but assumes that the rest of the pane array is otherwise complete, and inserts it into the panes array at position 1, with index 1, after the existing pane.

Then, whenPaneAdd fires for the left-most pane, and adds it at position 1

Now the pane orders are [0, 2, 1]. The indexes are then updated with:

this.panes.forEach((p, i) => (p.index = i))

And dragging the splitter between the second and third panes gives reversed behavior.

As a workaround, I'm doing the following:

Extending Pane to add the pane's ID to the dom element:

<template>
  <div class="splitpanes__pane" :data-uid="_.uid" @click="onPaneClick($event, _.uid)" :style="style">
    <slot/>
  </div>
</template>
<script>
import { Pane } from 'splitpanes'

export default {
  name: 'ModPane',
  extends: Pane
}
</script>

Extending Splitpanes to change the ordering behavior

<script>
import { Splitpanes } from 'splitpanes'

export default {
  name: 'ModSplitpanes',
  extends: Splitpanes,
  methods: {
    onPaneAdd (pane) {
      const paneEls = Array.from(pane.$el.parentNode.children).filter(el => {
        return el.className.includes('splitpanes__pane')
      })
// create a map of UIDs to index positions in the dom
      const indexMap = Object.fromEntries(paneEls.map((paneEl, index) => {
        return [paneEl.getAttribute('data-uid'), index]
      }))

      const index = indexMap[pane._.uid]
      const min = parseFloat(pane.minSize)
      const max = parseFloat(pane.maxSize)
// push instead of splice, since they'll be sorted
      this.panes.push({
        id: pane._.uid,
        index,
        min: isNaN(min) ? 0 : min,
        max: isNaN(max) ? 100 : max,
        size: pane.size === null ? null : parseFloat(pane.size),
        givenSize: pane.size,
        update: pane.update
      })

// Redo indexes after insertion for other shifted panes
      this.panes.forEach(p => (p.index = indexMap[p.id]))
// sort panes by index afterward
      this.panes.sort((a, b) => a.index - b.index)

      if (this.ready) {
        this.$nextTick(() => {
          // 2. Add the splitter.
          this.redoSplitters()

          // 3. Resize the panes.
          this.resetPaneSizes({ addedPane: this.panes[index] })

          // 4. Fire `pane-add` event.
          this.$emit('pane-add', { index, panes: this.panes.map(pane => ({ min: pane.min, max: pane.max, size: pane.size })) })
        })
      }
    }
}
</script>


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

1 participant