Skip to content

Commit b6c2c17

Browse files
authored
Merge pull request #1297 from nextcloud-libraries/feat/provide-generic-dialogs
feat: Provide generic dialogs previously provided by `OC.dialogs`
2 parents d3131f1 + 27994f5 commit b6c2c17

File tree

5 files changed

+264
-1
lines changed

5 files changed

+264
-1
lines changed

lib/components/GenericDialog.vue

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<template>
2+
<NcDialog dialog-classes="nc-generic-dialog"
3+
:buttons="buttons"
4+
:name="name"
5+
:message="text"
6+
@update:open="$emit('close')">
7+
<NcNoteCard v-if="severity" :type="severity">
8+
<p v-text="text" />
9+
</NcNoteCard>
10+
<!-- eslint-disable-next-line vue/no-v-html -->
11+
<div v-if="html" v-html="html" />
12+
</NcDialog>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import type { ISeverity } from './types.d.ts'
17+
18+
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
19+
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
20+
import type { IDialogButton } from './types'
21+
import { onMounted, onUnmounted } from 'vue'
22+
23+
const props = defineProps<{
24+
/**
25+
* Headline of the dialog
26+
*/
27+
name: string
28+
29+
/**
30+
* Main text of the dialog
31+
*/
32+
text: string
33+
34+
/**
35+
* HTML content
36+
* @deprecated DO NOT USE! This is just for backwards compatibility and will be removed in the near future!
37+
*/
38+
html?: string
39+
40+
/**
41+
* Buttons on the dialog
42+
*/
43+
buttons?: IDialogButton[]
44+
45+
/**
46+
* Severity of the dialog - if a notecard is used
47+
*/
48+
severity?: ISeverity
49+
}>()
50+
51+
/**
52+
* Handler used to ensure the message is also shown when the page is unloaded
53+
* This is for backwards compatibility with OC.dialogs
54+
*/
55+
const handleUnload = () => `${props.name}: ${props.text}`
56+
57+
onMounted(() => window.addEventListener('unload', handleUnload))
58+
onUnmounted(() => window.removeEventListener('unload', handleUnload))
59+
</script>
60+
61+
<style>
62+
.nc-generic-dialog .dialog__actions {
63+
justify-content: space-between;
64+
min-width: calc(100% - 12px);
65+
}
66+
</style>

lib/components/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import type { Node } from '@nextcloud/files'
2424

25+
export type ISeverity = 'info' | 'warning' | 'error'
26+
2527
/**
2628
* Interface for defining buttons passed to the Dialog component
2729
* See NcDialogButton

lib/dialogs.ts

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* @copyright Copyright (c) 2024 Ferdinand Thiessen <[email protected]>
3+
*
4+
* @author Ferdinand Thiessen <[email protected]>
5+
*
6+
* @license AGPL-3.0-or-later
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
import type { IDialogButton, ISeverity } from './components/types'
24+
import type Vue from 'vue'
25+
26+
import GenericDialog from './components/GenericDialog.vue'
27+
import { spawnDialog } from './utils/dialogs'
28+
29+
/**
30+
* This class provides generic Nextcloud themed dialogs
31+
*/
32+
export class Dialog {
33+
34+
#name: string
35+
#text: string
36+
#buttons: IDialogButton[]
37+
#severity?: ISeverity
38+
#dialog?: Vue
39+
40+
/** @deprecated */
41+
#html?: string
42+
43+
constructor(
44+
name: string,
45+
text: string,
46+
buttons: IDialogButton[] = [],
47+
severity?: ISeverity,
48+
) {
49+
this.#name = name
50+
this.#text = text
51+
this.#buttons = buttons
52+
this.#severity = severity
53+
this.#dialog = undefined
54+
this.#html = undefined
55+
}
56+
57+
/**
58+
* @deprecated DO NOT USE! It will be removed in the near future!
59+
* @param html HTML content
60+
*/
61+
setHTML(html: string) {
62+
this.#html = html
63+
return this
64+
}
65+
66+
/**
67+
* Spawn and show the dialog - if already open the previous instance will be destroyed
68+
* @return Promise that resolves when the dialog is answered successfully and rejects on close
69+
*/
70+
show() {
71+
if (this.#dialog) {
72+
this.#dialog.$destroy()
73+
}
74+
75+
return new Promise((resolve) => {
76+
this.#dialog = spawnDialog(GenericDialog,
77+
{
78+
buttons: this.#buttons,
79+
name: this.#name,
80+
text: this.#text,
81+
severity: this.#severity,
82+
html: this.#html,
83+
},
84+
resolve,
85+
)
86+
})
87+
}
88+
89+
/**
90+
* Hide and destroy the current dialog instance
91+
*/
92+
hide() {
93+
this.#dialog?.$destroy()
94+
}
95+
96+
}
97+
98+
/**
99+
* The DialogBuilder provides an easy to use interface for creating simple dialogs in consistent Nextcloud design
100+
*/
101+
export class DialogBuilder {
102+
103+
#severity?: ISeverity
104+
#text: string
105+
#name: string
106+
#buttons: IDialogButton[]
107+
108+
constructor() {
109+
this.#severity = undefined
110+
this.#text = ''
111+
this.#name = ''
112+
this.#buttons = []
113+
}
114+
115+
/**
116+
* Set dialog name
117+
* @param name The name or headline of the dialog
118+
*/
119+
setName(name: string) {
120+
this.#name = name
121+
return this
122+
}
123+
124+
/**
125+
* Set the dialog text
126+
* @param text Main text of the dialog
127+
*/
128+
setText(text: string) {
129+
this.#text = text
130+
return this
131+
}
132+
133+
/**
134+
* Set the severity of the dialog
135+
* @param severity Severity of the dialog
136+
*/
137+
setSeverity(severity: ISeverity) {
138+
this.#severity = severity
139+
return this
140+
}
141+
142+
/**
143+
* Set buttons from array
144+
* @param buttons Either an array of dialog buttons
145+
*/
146+
setButtons(buttons: IDialogButton[]) {
147+
if (this.#buttons.length > 0) {
148+
console.warn('[@nextcloud/dialogs] Dialog buttons are already set - this overrides previous buttons.')
149+
}
150+
this.#buttons = buttons
151+
return this
152+
}
153+
154+
/**
155+
* Add a single button
156+
* @param button Button to add
157+
*/
158+
addButton(button: IDialogButton) {
159+
this.#buttons.push(button)
160+
return this
161+
}
162+
163+
build() {
164+
return new Dialog(this.#name, this.#text, this.#buttons, this.#severity)
165+
}
166+
167+
}

lib/index.ts

+27
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* @copyright Copyright (c) 2023 Ferdinand Thiessen <[email protected]>
3+
*
4+
* @author Ferdinand Thiessen <[email protected]>
5+
*
6+
* @license AGPL-3.0-or-later
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
123
export {
224
FilePicker,
325
FilePickerClosed,
@@ -29,4 +51,9 @@ export type {
2951

3052
export { spawnDialog } from './utils/dialogs.js'
3153

54+
export {
55+
Dialog,
56+
DialogBuilder,
57+
} from './dialogs'
58+
3259
export type { IFilePickerButton, IFilePickerFilter } from './components/types.js'

lib/utils/dialogs.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import Vue, { toRaw } from 'vue'
3030
* @param props Properties to pass to the dialog
3131
* @param onClose Callback when the dialog is closed
3232
*/
33-
export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onClose: (...rest: unknown[]) => void = () => {}) => {
33+
export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onClose: (...rest: unknown[]) => void = () => {}): Vue => {
3434
const el = document.createElement('div')
3535

3636
const container: HTMLElement = document.querySelector(props?.container) || document.body
@@ -50,4 +50,5 @@ export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onCl
5050
},
5151
}),
5252
})
53+
return vue
5354
}

0 commit comments

Comments
 (0)