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

Migrate to Vue3 #51

Closed
kinow opened this issue Mar 11, 2023 · 4 comments · Fixed by #59
Closed

Migrate to Vue3 #51

kinow opened this issue Mar 11, 2023 · 4 comments · Fixed by #59
Labels
good first issue Good for newcomers help wanted Extra attention is needed
Milestone

Comments

@kinow
Copy link
Member

kinow commented Mar 11, 2023

PR's welcome, otherwise done whenever I have plenty spare time.

@kinow kinow added help wanted Extra attention is needed good first issue Good for newcomers labels Mar 11, 2023
@kinow kinow added this to the 1.2.0 milestone Mar 11, 2023
@nelsonpjunior
Copy link

I will check later this week if i can help with this migration
As of now i have created a separated wrapper using same api vue3 using same lumino-widget.js

<script setup lang="ts">
import { ref, nextTick, onMounted, onUpdated } from 'vue'
import LuminoWidget from './lumino-widget'
import { BoxPanel, DockPanel, Widget } from '@lumino/widgets'

interface Props {
  /**
   * Prop to customize the tab title. Defaults to name.
   * If a component does not have the $component.$tabTitleProp
   * set, then we still revert to the old default $component.name.
   */
  tabTitleProp?: string
}

const props = withDefaults(defineProps<Props>(), {
  tabTitleProp: 'name'
})

const emit = defineEmits(['lumino:activated', 'lumino:deleted'])

// define a ref to the slot element
const slot = ref<HTMLElement | null>(null)
// define a ref to the slot element
const container = ref<HTMLElement | null>(null)
const widgets = ref<string[]>([])

/**
 * Data for the Lumino component
 */
const main = new BoxPanel({ direction: 'left-to-right', spacing: 0 })
const dock = new DockPanel()

/**
 * React to a deleted event.
 *
 * @param customEvent {
 *   detail: {
 *     id: string,
 *     name: string,
 *     closable: boolean
 *   }
 * }}
 */
function onWidgetActivated(customEvent: CustomEvent) {
  emit('lumino:activated', customEvent.detail)
}

/**
 * React to a deleted event.
 *
 * @param customEvent {
 *   detail: {
 *     id: string,
 *     name: string,
 *     closable: boolean
 *   }
 * }}
 */
function onWidgetDeleted(customEvent: CustomEvent) {
  const id = customEvent.detail.id
  widgets.value.splice(widgets.value.indexOf(id), 1)

  document
    .getElementById(id)
    ?.removeEventListener('lumino:deleted', (event) => onWidgetDeleted(event as CustomEvent))
  document
    .getElementById(id)
    ?.removeEventListener('lumino:activated', (event) => onWidgetDeleted(event as CustomEvent))
  emit('lumino:deleted', customEvent.detail)
}

/**
 * Create a widget.
 *
 * @param id {String} - widget ID
 * @param name {String} - widget name
 */
function addWidget(id: string, name: string) {
  widgets.value.push(id)
  const luminoWidget = new LuminoWidget(id, name, /* closable */ true)
  dock.addWidget(luminoWidget)

  nextTick(() => {
    document
      .getElementById(id)
      ?.addEventListener('lumino:activated', (event) => onWidgetActivated(event as CustomEvent))
    document
      .getElementById(id)
      ?.addEventListener('lumino:deleted', (event) => onWidgetDeleted(event as CustomEvent))
  })
}

/**
 * Iterates through the component children, looking for newly created
 * components, and then creates a related Lumino Widget for this component.
 */
function syncWidgets() {
  const widgetList = slot.value?.children

  if (!widgetList) return
  for (const widget of widgetList) {
    const id = widget.id
    if (widgets.value.includes(id)) continue
    const name = widget.getAttribute(props.tabTitleProp as string) || ''
    addWidget(id, name)

    nextTick(() => {
      document.getElementById(id)?.appendChild(widget)
    })
  }
}

/**
 * Here we define the ID's for the Lumino DOM elements, and add the Dock panel to the main
 * Box panel. In the next tick of Vue, the DOM element and the Vue element/ref are attached.
 */
onMounted(() => {
  dock.id = 'dock'
  main.id = 'main'

  main.addWidget(dock)

  window.onresize = () => {
    main.update()
  }

  BoxPanel.setStretch(dock, 1)

  if (container.value) {
    Widget.attach(main, container.value)

    syncWidgets()
  }
})

/**
 * Every time a new child element is added to the slot, this method will
 * be called. It will iterate the children elements looking for new ones
 * to add.
 *
 * The removal is handled via event listeners from Lumino.
 */
onUpdated(() => {
  syncWidgets()
})
</script>

<template>
  <div id="workflow-panel">
    <div id="main" ref="container" class="pa-4 fill-height"></div>
    <div v-show="false" ref="slot">
      <slot></slot>
    </div>
  </div>
</template>

<styles lang="scss">
#workflow-panel {
  height: 100%;

  #main {
    height: 100%;

    .content {
      min-width: 300px;
      min-height: 300px;
      display: flex;
      flex-direction: column;
      padding: 0;
      border: 1px solid #c0c0c0;
      border-top: none;
      background: white;
      position: relative;
      overflow: auto;
    }

    .p-BoxPanel {
      flex: 1 1 auto;
    }
  }
}
</styles>

this should do the trick for until the migration is finished

@kinow
Copy link
Member Author

kinow commented Aug 10, 2023

Thank you @nelsonpjunior !

@novrain
Copy link

novrain commented Dec 13, 2023

@kinow @nelsonpjunior
Thank you for all your work!

In my project, I used this package and @nelsonpjunior's Vue3 component, but meet some problems:

  • need do somthing when widget close, even block the close
  • listen and process event in "Vue" way, and event can carray user's data
  • widget is not wrappered by a component

so, inspired by your guys, I wrote a Vue3 version:

vue3-lumino-widget

and I used it in this project:

wavy

Thank you all again.

@kinow
Copy link
Member Author

kinow commented Dec 13, 2023

Hi @novrain

If you'd like to send a PR we can merge your changes. Otherwise, this project was created to be used in Cylc. I left the project and haven't had time to update it.

But another dev updated it in Cylc, using Vue3 and the teleport. At some point I will have a look at his changes, at Vue 3 and upgrade this package (or someone could send a PR for that too).

Cheers

@kinow kinow mentioned this issue Jan 12, 2024
7 tasks
@kinow kinow closed this as completed in #59 Jan 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants