dialog.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. /**
  5. * Native system dialogs for opening and saving files.
  6. *
  7. * This package is also accessible with `window.__TAURI__.dialog` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
  8. *
  9. * The APIs must be added to [`tauri.allowlist.dialog`](https://tauri.app/v1/api/config/#allowlistconfig.dialog) in `tauri.conf.json`:
  10. * ```json
  11. * {
  12. * "tauri": {
  13. * "allowlist": {
  14. * "dialog": {
  15. * "all": true, // enable all dialog APIs
  16. * "open": true, // enable file open API
  17. * "save": true // enable file save API
  18. * }
  19. * }
  20. * }
  21. * }
  22. * ```
  23. * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
  24. * @module
  25. */
  26. import { invokeTauriCommand } from './helpers/tauri'
  27. /** Extension filters for the file dialog. */
  28. interface DialogFilter {
  29. /** Filter name. */
  30. name: string
  31. /**
  32. * Extensions to filter, without a `.` prefix.
  33. * @example
  34. * ```typescript
  35. * extensions: ['svg', 'png']
  36. * ```
  37. */
  38. extensions: string[]
  39. }
  40. /** Options for the open dialog. */
  41. interface OpenDialogOptions {
  42. /** The title of the dialog window. */
  43. title?: string
  44. /** The filters of the dialog. */
  45. filters?: DialogFilter[]
  46. /** Initial directory or file path. */
  47. defaultPath?: string
  48. /** Whether the dialog allows multiple selection or not. */
  49. multiple?: boolean
  50. /** Whether the dialog is a directory selection or not. */
  51. directory?: boolean
  52. /**
  53. * If `directory` is true, indicates that it will be read recursively later.
  54. * Defines whether subdirectories will be allowed on the scope or not.
  55. */
  56. recursive?: boolean
  57. }
  58. /** Options for the save dialog. */
  59. interface SaveDialogOptions {
  60. /** The title of the dialog window. */
  61. title?: string
  62. /** The filters of the dialog. */
  63. filters?: DialogFilter[]
  64. /**
  65. * Initial directory or file path.
  66. * If it's a directory path, the dialog interface will change to that folder.
  67. * If it's not an existing directory, the file name will be set to the dialog's file name input and the dialog will be set to the parent folder.
  68. */
  69. defaultPath?: string
  70. }
  71. interface MessageDialogOptions {
  72. /** The title of the dialog. Defaults to the app name. */
  73. title?: string
  74. /** The type of the dialog. Defaults to `info`. */
  75. type?: 'info' | 'warning' | 'error'
  76. }
  77. /**
  78. * Open a file/directory selection dialog.
  79. *
  80. * The selected paths are added to the filesystem and asset protocol allowlist scopes.
  81. * When security is more important than the easy of use of this API,
  82. * prefer writing a dedicated command instead.
  83. *
  84. * Note that the allowlist scope change is not persisted, so the values are cleared when the application is restarted.
  85. * You can save it to the filesystem using [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
  86. * @example
  87. * ```typescript
  88. * import { open } from '@tauri-apps/api/dialog';
  89. * // Open a selection dialog for image files
  90. * const selected = await open({
  91. * multiple: true,
  92. * filters: [{
  93. * name: 'Image',
  94. * extensions: ['png', 'jpeg']
  95. * }]
  96. * });
  97. * if (Array.isArray(selected)) {
  98. * // user selected multiple files
  99. * } else if (selected === null) {
  100. * // user cancelled the selection
  101. * } else {
  102. * // user selected a single file
  103. * }
  104. * ```
  105. *
  106. * @example
  107. * ```typescript
  108. * import { open } from '@tauri-apps/api/dialog';
  109. * import { appDir } from '@tauri-apps/api/path';
  110. * // Open a selection dialog for directories
  111. * const selected = await open({
  112. * directory: true,
  113. * multiple: true,
  114. * defaultPath: await appDir(),
  115. * });
  116. * if (Array.isArray(selected)) {
  117. * // user selected multiple directories
  118. * } else if (selected === null) {
  119. * // user cancelled the selection
  120. * } else {
  121. * // user selected a single directory
  122. * }
  123. * ```
  124. *
  125. * @returns A promise resolving to the selected path(s)
  126. */
  127. async function open(
  128. options: OpenDialogOptions = {}
  129. ): Promise<null | string | string[]> {
  130. if (typeof options === 'object') {
  131. Object.freeze(options)
  132. }
  133. return invokeTauriCommand({
  134. __tauriModule: 'Dialog',
  135. message: {
  136. cmd: 'openDialog',
  137. options
  138. }
  139. })
  140. }
  141. /**
  142. * Open a file/directory save dialog.
  143. *
  144. * The selected path is added to the filesystem and asset protocol allowlist scopes.
  145. * When security is more important than the easy of use of this API,
  146. * prefer writing a dedicated command instead.
  147. *
  148. * Note that the allowlist scope change is not persisted, so the values are cleared when the application is restarted.
  149. * You can save it to the filesystem using [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
  150. * @example
  151. * ```typescript
  152. * import { save } from '@tauri-apps/api/dialog';
  153. * const filePath = await save({
  154. * multiple: true,
  155. * filters: [{
  156. * name: 'Image',
  157. * extensions: ['stronghold']
  158. * }]
  159. * });
  160. * ```
  161. *
  162. * @returns A promise resolving to the selected path.
  163. */
  164. async function save(options: SaveDialogOptions = {}): Promise<string> {
  165. if (typeof options === 'object') {
  166. Object.freeze(options)
  167. }
  168. return invokeTauriCommand({
  169. __tauriModule: 'Dialog',
  170. message: {
  171. cmd: 'saveDialog',
  172. options
  173. }
  174. })
  175. }
  176. /**
  177. * Shows a message dialog with an `Ok` button.
  178. * @example
  179. * ```typescript
  180. * import { message } from '@tauri-apps/api/dialog';
  181. * await message('Tauri is awesome', 'Tauri');
  182. * await message('File not found', { title: 'Tauri', type: 'error' });
  183. * ```
  184. *
  185. * @param {string} message The message to show.
  186. * @param {string|MessageDialogOptions|undefined} options The dialog's options. If a string, it represents the dialog title.
  187. *
  188. * @return {Promise<void>} A promise indicating the success or failure of the operation.
  189. */
  190. async function message(
  191. message: string,
  192. options?: string | MessageDialogOptions
  193. ): Promise<void> {
  194. const opts = typeof options === 'string' ? { title: options } : options
  195. return invokeTauriCommand({
  196. __tauriModule: 'Dialog',
  197. message: {
  198. cmd: 'messageDialog',
  199. message: message.toString(),
  200. title: opts?.title?.toString(),
  201. type: opts?.type
  202. }
  203. })
  204. }
  205. /**
  206. * Shows a question dialog with `Yes` and `No` buttons.
  207. * @example
  208. * ```typescript
  209. * import { ask } from '@tauri-apps/api/dialog';
  210. * const yes = await ask('Are you sure?', 'Tauri');
  211. * const yes2 = await ask('This action cannot be reverted. Are you sure?', { title: 'Tauri', type: 'warning' });
  212. * ```
  213. *
  214. * @param {string} message The message to show.
  215. * @param {string|MessageDialogOptions|undefined} options The dialog's options. If a string, it represents the dialog title.
  216. *
  217. * @return {Promise<void>} A promise resolving to a boolean indicating whether `Yes` was clicked or not.
  218. */
  219. async function ask(
  220. message: string,
  221. options?: string | MessageDialogOptions
  222. ): Promise<boolean> {
  223. const opts = typeof options === 'string' ? { title: options } : options
  224. return invokeTauriCommand({
  225. __tauriModule: 'Dialog',
  226. message: {
  227. cmd: 'askDialog',
  228. message: message.toString(),
  229. title: opts?.title?.toString(),
  230. type: opts?.type
  231. }
  232. })
  233. }
  234. /**
  235. * Shows a question dialog with `Ok` and `Cancel` buttons.
  236. * @example
  237. * ```typescript
  238. * import { confirm } from '@tauri-apps/api/dialog';
  239. * const confirmed = await confirm('Are you sure?', 'Tauri');
  240. * const confirmed2 = await confirm('This action cannot be reverted. Are you sure?', { title: 'Tauri', type: 'warning' });
  241. * ```
  242. *
  243. * @param {string} message The message to show.
  244. * @param {string|MessageDialogOptions|undefined} options The dialog's options. If a string, it represents the dialog title.
  245. *
  246. * @return {Promise<void>} A promise resolving to a boolean indicating whether `Ok` was clicked or not.
  247. */
  248. async function confirm(
  249. message: string,
  250. options?: string | MessageDialogOptions
  251. ): Promise<boolean> {
  252. const opts = typeof options === 'string' ? { title: options } : options
  253. return invokeTauriCommand({
  254. __tauriModule: 'Dialog',
  255. message: {
  256. cmd: 'confirmDialog',
  257. message: message.toString(),
  258. title: opts?.title?.toString(),
  259. type: opts?.type
  260. }
  261. })
  262. }
  263. export type {
  264. DialogFilter,
  265. OpenDialogOptions,
  266. SaveDialogOptions,
  267. MessageDialogOptions
  268. }
  269. export { open, save, message, ask, confirm }