Skip to content

Commit

Permalink
feat(docs): rewrite sortablejs extension in TS (#406)
Browse files Browse the repository at this point in the history
Rewrites the documentation for the sortable extension in the
`src/pages/extensions/sortablejs` in TypeScript. Also includes
overlooked Vue 2 → 3 migration.

In `examples/ExSimple.vue`:
- Adapts the `createSortable` function so that it works well with the
  Vue 3 directive API:
  - Accepts an instance of `BTaginput` instead of `VNode`. The component
    instance should be obtained from the `binding` parameter rather than
    the `vnode` parameter in the Vue 3 directive API.
  - The `$buefy` global property is a part of `ComponentPublicInstance`;
    i.e., can be accessed via a `BTaginput` instance
- Refurbishes the `sortable` directive using the Vue 3 directive API:
  - `bind` → `beforeMount`
  - `update` → `updated`
  - `unbind` → `unmounted`
- Introduces a new interface `SortablejsContainer` which facilitate
  accessing the `_sortable` field attached to `HTMLElement`

In `Sortablejs.vue`:
- Wraps `ExSimple` in a `shallowRef` call to address the warning about
  making a component instance reactive

A tip for TypeScript migration:
- Explicitly import and register Buefy components so that they are
  type-checked. Note that no type-checking is performed for globally
  registered components.
  • Loading branch information
kikuomax authored Jan 17, 2025
1 parent c70ce94 commit ae26fab
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 27 deletions.
21 changes: 16 additions & 5 deletions packages/docs/src/pages/extensions/sortablejs/Sortablejs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,27 @@
</div>
</template>

<script>
import ExSimple from './examples/ExSimple'
<script lang="ts">
import { defineComponent, shallowRef } from 'vue'
import { BMessage } from '@ntohq/buefy-next'
import CodeView from '@/components/CodeView.vue'
import Example from '@/components/Example.vue'
import ExSimple from './examples/ExSimple.vue'
import ExSimpleCode from './examples/ExSimple.vue?raw'
export default {
export default defineComponent({
components: {
BMessage,
CodeView,
Example
},
data() {
return {
ExSimple,
ExSimple: shallowRef(ExSimple),
ExSimpleCode
}
}
}
})
</script>
70 changes: 48 additions & 22 deletions packages/docs/src/pages/extensions/sortablejs/examples/ExSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,44 @@
</section>
</template>

<script>
<script lang="ts">
import { defineComponent } from 'vue'
import type { Directive } from 'vue'
// You have to install sortable.js to use it:
// 'npm install sortablejs'
// 'npm install --save-dev @types/sortablejs'
import Sortable from 'sortablejs'
const createSortable = (el, options, vnode) => {
import { BField, BTaginput } from '@ntohq/buefy-next'
type BTaginputInstance = InstanceType<typeof BTaginput>
interface SortableContainer extends HTMLElement {
_sortable: Sortable
}
const createSortable = (
el: HTMLElement,
options: Sortable.Options,
taginput: BTaginputInstance
) => {
return Sortable.create(el, {
...options,
onEnd: function (evt) {
const data = vnode.componentInstance.$data.tags
const item = data[evt.oldIndex]
if (evt.newIndex > evt.oldIndex) {
for (let i = evt.oldIndex; i < evt.newIndex; i++) {
const data = taginput.$data.tags
const item = data[evt.oldIndex!]
if (evt.newIndex! > evt.oldIndex!) {
for (let i = evt.oldIndex!; i < evt.newIndex!; i++) {
data[i] = data[i + 1]
}
} else {
for (let i = evt.oldIndex; i > evt.newIndex; i--) {
for (let i = evt.oldIndex!; i > evt.newIndex!; i--) {
data[i] = data[i - 1]
}
}
data[evt.newIndex] = item
vnode.componentInstance.$emit('input', data)
vnode.context.$buefy.toast.open(`Moved ${item} from row ${evt.oldIndex + 1} to ${evt.newIndex + 1}`)
data[evt.newIndex!] = item
taginput.$emit('update:modelValue', data)
taginput.$buefy.toast.open(`Moved ${item} from row ${evt.oldIndex! + 1} to ${evt.newIndex! + 1}`)
}
})
}
Expand All @@ -45,24 +60,35 @@
* We add a new instance of Sortable when the element
* is bound or updated, and destroy it when it's unbound.
*/
const sortable = {
name: 'sortable',
bind(el, binding, vnode) {
const container = el.querySelector('.taginput-container')
container._sortable = createSortable(container, binding.value, vnode)
const sortable: Directive<HTMLElement, Sortable.Options> = {
beforeMount(el, binding) {
const container = el.querySelector('.taginput-container') as SortableContainer
container._sortable = createSortable(
container,
binding.value,
binding.instance as BTaginputInstance
)
},
update(el, binding, vnode) {
const container = el.querySelector('.taginput-container')
updated(el, binding) {
const container = el.querySelector('.taginput-container') as SortableContainer
container._sortable.destroy()
container._sortable = createSortable(container, binding.value, vnode)
container._sortable = createSortable(
container,
binding.value,
binding.instance as BTaginputInstance
)
},
unbind(el) {
const container = el.querySelector('.taginput-container')
unmounted(el) {
const container = el.querySelector('.taginput-container') as SortableContainer
container._sortable.destroy()
}
}
export default {
export default defineComponent({
components: {
BField,
BTaginput
},
directives: { sortable },
data() {
return {
Expand All @@ -77,7 +103,7 @@
]
}
}
}
})
</script>

<style>
Expand Down

0 comments on commit ae26fab

Please sign in to comment.