Skip to content

Commit

Permalink
Merge pull request #6207 from nextcloud-libraries/backport/6195/next
Browse files Browse the repository at this point in the history
[next] feat: Add `spawnDialog` function
  • Loading branch information
susnux authored Nov 14, 2024
2 parents bea056c + 5fda5b3 commit 1cd8309
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 4 deletions.
111 changes: 111 additions & 0 deletions docs/functions/spawnDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
```ts static
import {
spawnDialog,
} from '@nextcloud/vue/dist/Functions/dialog.js'
```

## Definitions

```ts static
/**
* Helper to spawn a Vue dialog without having to mount it from a component
*
* @param dialog The dialog component to spawn - the component must emit the 'close' event whenever it is closed
* @param props Properties to pass to the dialog
* @param props.container Optionally pass a query selector for the dialog container element
* @param props.appContext Optionally the app context to use (e.g. for registered components)
* @param onClose Callback when the dialog is closed (parameters of the 'close' event of the dialog)
*/
function spawnDialog(
dialog: Component | AsyncComponent,
props: Record<string, unknown>,
onClose: (...rest: unknown[]) => void = () => {},
): void;
```

## Usage

The main use case is to be able to spawn a dialog from code, without the need of including the dialog component in the template.
So a Vue dialog can be spawned in any context and not just from Vue components but also from callback functions or other API.

```vue
<template>
<div>
<NcButton @click="onSpawnDialog">🖱 click to spawn a dialog</NcButton>
</div>
</template>
<script>
// Example dialog component that would be normally imported
// Important: The dialog must emit a 'close' event whenever it is closed
const ExampleDialog = {
template: `
<NcDialog :buttons="buttons"
name="Spawned dialog"
message="This dialog was spawned using the 'spawnDialog' function."
@closing="saveResult"
@update:open="onClose" />`,
components: {
NcDialog,
},
props: {
timesClicked: {
type: Number,
required: true,
},
},
data() {
return {
result: null,
}
},
computed: {
buttons() {
return [
{
label: 'Cancel',
callback: () => 'cancelled',
},
{
label: 'Accept',
type: 'primary',
callback: () => 'accepted',
},
]
}
},
methods: {
saveResult(result) {
this.result = result
},
onClose() {
this.$emit('close', this.result)
},
}
}
export default {
data() {
return {
timesClicked: 0,
}
},
methods: {
onSpawnDialog() {
this.timesClicked += 1
spawnDialog(
ExampleDialog,
{
timesClicked: this.timesClicked,
},
(result) => window.alert(`Dialog was ${result}`)
)
},
},
}
</script>
```
19 changes: 16 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.5.1",
"babel-loader-exclude-node-modules-except": "^1.2.1",
"core-js": "^3.39.0",
"file-loader": "^6.2.0",
"gettext-extractor": "^3.8.0",
"gettext-parser": "^8.0.0",
Expand Down
51 changes: 51 additions & 0 deletions src/functions/dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { AppContext, Component } from 'vue'
import { createVNode, getCurrentInstance, render, toRaw } from 'vue'

interface DialogProps {
[index: string]: unknown
container?: string
appContext?: AppContext
}

/**
* Helper to spawn a Vue dialog without having to mount it from a component
*
* @param dialog The dialog component to spawn
* @param props Properties to pass to the dialog
* @param props.container Optionally pass a query selector for the dialog container element
* @param props.appContext Optionally the app context to use (e.g. for registered components)
* @param onClose Callback when the dialog is closed
*/
export function spawnDialog(
dialog: Component,
props?: DialogProps,
onClose: (...rest: unknown[]) => void = () => {},
): void {
const el = document.createElement('div')
const container: HTMLElement = typeof props?.container === 'string'
? (document.querySelector(props.container) || document.body)
: document.body
container.appendChild(el)

const vueComponent = createVNode(dialog, {
...props,
onclose: (...rest: unknown[]) => {
onClose(...rest.map(v => toRaw(v)))
// destroy the component
render(null, el)
el.remove()
},
})

// If there is an instance use it to get access to registered components
const appContext = props?.appContext ?? getCurrentInstance()?.appContext
if (appContext) {
vueComponent.appContext = { ...appContext }
}

render(vueComponent, el)
}
1 change: 1 addition & 0 deletions src/functions/index.js → src/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

export * from './a11y/index.ts'
export * from './dialog/index.ts'
export * from './emoji/index.ts'
export * from './reference/index.js'
export * from './isDarkTheme/index.ts'
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export * from './components/index'

export * from './composables/index.js'
export * from './functions/index.js'
export * from './functions/index.ts'
export * from './directives/index.js'
export * from './mixins/index.js'

Expand Down
9 changes: 9 additions & 0 deletions styleguide.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ module.exports = async () => {
},
},
},
resolve: {
alias: {
vue$: 'vue/dist/vue.esm-browser.js',
},
},
}),

exampleMode: 'collapse',
Expand Down Expand Up @@ -137,6 +142,10 @@ module.exports = async () => {
name: 'registerReference',
content: 'docs/functions/registerReference.md',
},
{
name: 'spawnDialog',
content: 'docs/functions/spawnDialog.md',
},
],
},
{
Expand Down
5 changes: 5 additions & 0 deletions styleguide/global.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import axios from '@nextcloud/axios'

import { isA11yActivation } from '../src/functions/a11y/index.ts'
import { EmojiSkinTone, emojiSearch, emojiAddRecent, getCurrentSkinTone, setCurrentSkinTone } from '../src/functions/emoji/index.ts'
import { spawnDialog } from '../src/functions/dialog/index.ts'
import usernameToColor from '../src/functions/usernameToColor/index.js'
import NcDialog from '../src/components/NcDialog/index.js'

const USER_GROUPS = [
{ id: 'admin', displayname: 'The administrators' },
Expand Down Expand Up @@ -157,6 +159,9 @@ window.emojiAddRecent = emojiAddRecent
window.getCurrentSkinTone = getCurrentSkinTone
window.setCurrentSkinTone = setCurrentSkinTone
window.usernameToColor = usernameToColor
window.spawnDialog = spawnDialog
// Exported components
window.NcDialog = NcDialog
// Exported composables
window.useIsDarkTheme = useIsDarkTheme

Expand Down

0 comments on commit 1cd8309

Please sign in to comment.