diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index e9b8709..e14e157 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -5,6 +5,7 @@ on: - 'titlebar*' - 'contextmenu*' - 'notification*' + - 'toast*' name: Create Release diff --git a/README.md b/README.md index a5becba..a62b321 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ | [@electron-uikit/contextmenu](packages/contextmenu) | Context menu | [![contextmenu version](https://img.shields.io/npm/v/@electron-uikit/contextmenu.svg?label=%20)](packages/contextmenu/CHANGELOG.md) | | [@electron-uikit/notification](packages/notification) | Notification | [![notification version](https://img.shields.io/npm/v/@electron-uikit/notification.svg?label=%20)](packages/notification/CHANGELOG.md) | | [@electron-uikit/titlebar](packages/titlebar) | Title bar web component | [![titlebar version](https://img.shields.io/npm/v/@electron-uikit/titlebar.svg?label=%20)](packages/titlebar/CHANGELOG.md) | +| [@electron-uikit/toast](packages/toast) | Toast | [![toast version](https://img.shields.io/npm/v/@electron-uikit/toast.svg?label=%20)](packages/toast/CHANGELOG.md) | ## License diff --git a/package.json b/package.json index a65324d..4e6edb2 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "build:core": "pnpm run -C packages/core build", "build:contextmenu": "pnpm run -C packages/contextmenu build", "build:notification": "pnpm run -C packages/notification build", - "build:titlebar": "pnpm run -C packages/titlebar build" + "build:titlebar": "pnpm run -C packages/titlebar build", + "build:toast": "pnpm run -C packages/toast build" }, "devDependencies": { "@rollup/plugin-commonjs": "^26.0.1", diff --git a/packages/playground/package.json b/packages/playground/package.json index 062b8a2..4b56e7b 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -11,7 +11,8 @@ "@electron-uikit/contextmenu": "workspace:^", "@electron-uikit/core": "workspace:^", "@electron-uikit/notification": "workspace:^", - "@electron-uikit/titlebar": "workspace:^" + "@electron-uikit/titlebar": "workspace:^", + "@electron-uikit/toast": "workspace:^" }, "devDependencies": { "electron-vite": "^2.2.0", diff --git a/packages/playground/src/main/index.ts b/packages/playground/src/main/index.ts index 50cc291..c951e06 100644 --- a/packages/playground/src/main/index.ts +++ b/packages/playground/src/main/index.ts @@ -11,6 +11,7 @@ import { registerTitleBarListener, attachTitleBarToWindow } from '@electron-uikit/titlebar' +import { Toast } from '@electron-uikit/toast' function createWindow(): void { const mainWindow = new BrowserWindow({ @@ -85,6 +86,15 @@ app.whenReady().then(() => { return nativeTheme.shouldUseDarkColors }) + ipcMain.on('toast:loading', (e) => { + const win = BrowserWindow.fromWebContents(e.sender) + if (win) { + const toast = new Toast(win) + const reply = toast.loading('Downloading') + setTimeout(() => reply.dismiss(), 3000) + } + }) + app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) diff --git a/packages/playground/src/renderer/src/index.ts b/packages/playground/src/renderer/src/index.ts index e8f80b3..ffda3ea 100644 --- a/packages/playground/src/renderer/src/index.ts +++ b/packages/playground/src/renderer/src/index.ts @@ -1,7 +1,12 @@ import { Menu, MenuItem } from '@electron-uikit/contextmenu/renderer' import { notification } from '@electron-uikit/notification/renderer' +import { toast } from '@electron-uikit/toast/renderer' function init(): void { + toast.config({ + supportMain: true, + bottom: 80 + }) window.addEventListener('DOMContentLoaded', () => { const theme = document.getElementById('theme') const icon = document.getElementById('icon') @@ -33,11 +38,26 @@ function init(): void { menu.append(new MenuItem({ type: 'separator' })) menu.append( new MenuItem({ - type: 'checkbox', - label: 'Menu Item Two', - checked: true, + label: 'Show Text Toast', + click: (): void => { + toast.text('I am toast', 4000) + } + }) + ) + menu.append( + new MenuItem({ + label: 'Show Loading Toast', + click: (): void => { + const reply = toast.loading('Loading') + setTimeout(() => reply.success('Done'), 3000000) + } + }) + ) + menu.append( + new MenuItem({ + label: 'Show Loading Toast (Main Process)', click: (): void => { - console.log('menu item two') + window.uikit.ipcRenderer.send('toast:loading') } }) ) diff --git a/packages/toast/README.md b/packages/toast/README.md new file mode 100644 index 0000000..8275c28 --- /dev/null +++ b/packages/toast/README.md @@ -0,0 +1,150 @@ +# @electron-uikit/toast + +![toast version](https://img.shields.io/npm/v/@electron-uikit/toast.svg?color=orange&label=version) + +Toast for Electron app. + +Toast is a concise, non-modal notification method that is used to briefly display information on the user interface without interrupting the user's current operation. It is widely used in mobile operating systems such as Android and iOS to provide quick feedback and important prompt information. Through toast notifications, developers can improve user experience and effectively communicate application status changes. + +

+ +

+ +## Usage + +### Install + +```sh +npm i @electron-uikit/core @electron-uikit/toast +``` + +### Get Started + +#### Using the toast in the renderer process. + +```js +import { toast } from '@electron-uikit/toast/renderer' + +toast.text('foo') +toast.loading('loading') +``` + +#### Using the toast in the main process. + +1. Exposes the UI Kit APIs for components. See [@electron-uikit/core](https://github.com/alex8088/electron-uikit/tree/main/packages/core) guide for more details. + + You can expose it in the specified preload script: + + ```js + import { exposeUIKit } from '@electron-uikit/core/preload' + + exposeUIKit() + ``` + + Or, you can expose it globally in the main process for all renderer processes: + + ```js + import { useUIKit } from '@electron-uikit/core/main' + + useUIKit() + ``` + +> [!NOTE] +> If you are using [@electron-toolkit/preload](https://github.com/alex8088/electron-toolkit/tree/master/packages/preload) to expose Electron APIs, there is no need to use this module, because `core` is also an export of it. + +2. Register a listener in the renderer process, so that you can use it in the main process. + + ```js + import { toast } from '@electron-uikit/toast/renderer' + + toast.config({ + supportMain: true + }) + ``` + +3. Use the notification in the main process. + + ```js + import { BrowserWindow } from 'electron' + import { Toast } from '@electron-uikit/toast' + + const win = new BrowserWindow() + + const toast = new Toast(win) + toast.text('foo') + toast.loading('loading') + ``` + +## APIs + +> [!NOTE] +> To use Toast in the main process, you need to create a Toast instance of the specified window. + +### `.config(options)` + +Configure toast defaults or customize toast. Can only be used in the renderer process. + +- options: `object` + + - **container**: `HTMLElement` (optional) - Container element of Toast. Default to `document.body`. + - **duration**: `number` (optional) - Display duration in millisecond. If set to `0`, it will not turn off automatically. Default to `2000`. + - **customClass**: `string` (optional) - Custom CSS class name for toast. + - **bottom**: `number` (optional) - Toast position to the bottom. Default to `50`. + - **maxWidth**: `number` (optional) - The maximum width of toast. Default to `320`. + - **color**: `string` (optional) - Toast background color. + - **textColor**: `string` (optional) - Toast text color. + - **fontSize**: `number` (optional) - Toast font size. Default to `14`. + - **iconSize**: `number` (optional) - Toast icon size. Default to `20`. + - **supportMain**: `boolean` (optional) - Support Electron main process. Default to `false`. + +### `.text(text[, duration])` + +Show text. The default duration is `2000` ms. + +### `.loading(text[, duration])` + +Show loading. The default duration is 0, which means it is always displayed and can be turned off by calling its return value function. + +```js +import { toast } from '@electron-uikit/toast/renderer' + +const reply = toast.loading('Loading') + +setTimeout(() => { + reply.success('Successful') + // reply.fail('Failed') + // reply.dismiss() +}, 3000) +``` + +## Customization + +1. Customize using CSS classes + +```css +.toast { + --toast-bottom: 50px; + --toast-z-index: 5001; + --toast-color: #48484e; + --toast-text-color: #ffffffd1; + --toast-font-size: 14px; + --toast-font-family: -apple-system, BlinkMacSystemFont, Ubuntu, 'Segoe UI'; + --toast-icon-size: 20px; + --toast-max-width: 320px; +} +``` + +```js +toast.config({ + customClass: 'toast' +}) +``` + +2. Customize using `config` API + +```js +toast.config({ + bottom: 200, + maxWidth: 280 +}) +``` diff --git a/packages/toast/package.json b/packages/toast/package.json new file mode 100644 index 0000000..cb6df01 --- /dev/null +++ b/packages/toast/package.json @@ -0,0 +1,67 @@ +{ + "name": "@electron-uikit/toast", + "version": "0.0.0", + "description": "Toast for Electron app.", + "main": "dist/main.cjs", + "module": "dist/main.mjs", + "types": "dist/main.d.ts", + "exports": { + ".": { + "types": "./dist/main.d.ts", + "import": "./dist/main.mjs", + "require": "./dist/main.cjs" + }, + "./main": { + "types": "./dist/main.d.ts", + "import": "./dist/main.mjs", + "require": "./dist/main.cjs" + }, + "./renderer": { + "types": "./dist/renderer.d.ts", + "import": "./dist/renderer.mjs" + } + }, + "typesVersions": { + "*": { + "main": [ + "./dist/main.d.ts" + ], + "renderer": [ + "./dist/renderer.d.ts" + ] + } + }, + "files": [ + "dist" + ], + "author": "Alex Wei", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/alex8088/electron-uikit.git", + "directory": "packages/toast" + }, + "bugs": { + "url": "https://github.com/alex8088/electron-uikit/issues" + }, + "homepage": "https://github.com/alex8088/electron-uikit/tree/master/packages/toast#readme", + "keywords": [ + "electron", + "toast" + ], + "scripts": { + "build": "rollup -c rollup.config.ts --configPlugin typescript" + }, + "peerDependencies": { + "@electron-uikit/core": "*", + "electron": ">=15.0.0" + }, + "peerDependenciesMeta": { + "@electron-uikit/core": { + "optional": true + } + }, + "devDependencies": { + "@electron-uikit/core": "workspace:^" + } +} diff --git a/packages/toast/rollup.config.ts b/packages/toast/rollup.config.ts new file mode 100644 index 0000000..22b701b --- /dev/null +++ b/packages/toast/rollup.config.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { defineConfig } from 'rollup' +import resolve from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' +import ts from '@rollup/plugin-typescript' +import dts from 'rollup-plugin-dts' +import rm from 'rollup-plugin-rm' + +export default defineConfig([ + { + input: ['src/main.ts'], + output: [ + { + entryFileNames: '[name].cjs', + chunkFileNames: 'chunks/lib-[hash].cjs', + format: 'cjs', + dir: 'dist' + }, + { + entryFileNames: '[name].mjs', + chunkFileNames: 'chunks/lib-[hash].mjs', + format: 'es', + dir: 'dist' + } + ], + external: ['electron'], + plugins: [ + resolve(), + commonjs(), + ts({ + compilerOptions: { + rootDir: 'src', + declaration: true, + outDir: 'dist/types' + } + }), + rm('dist', 'buildStart') + ] + }, + { + input: ['src/renderer.ts'], + output: [ + { file: './dist/renderer.mjs', format: 'es' }, + { name: 'renderer', file: './dist/renderer.js', format: 'iife' } + ], + plugins: [ + ts({ + compilerOptions: { + rootDir: 'src', + declaration: true, + outDir: 'dist/types' + } + }) + ] + }, + { + input: ['dist/types/main.d.ts'], + output: [{ file: './dist/main.d.ts', format: 'es' }], + plugins: [dts()], + external: ['electron'] + }, + { + input: ['dist/types/renderer.d.ts'], + output: [{ file: './dist/renderer.d.ts', format: 'es' }], + plugins: [dts(), rm('dist/types', 'buildEnd')] + } +]) diff --git a/packages/toast/screenshots/toast.png b/packages/toast/screenshots/toast.png new file mode 100644 index 0000000..489192b Binary files /dev/null and b/packages/toast/screenshots/toast.png differ diff --git a/packages/toast/src/common.ts b/packages/toast/src/common.ts new file mode 100644 index 0000000..3e084fa --- /dev/null +++ b/packages/toast/src/common.ts @@ -0,0 +1,4 @@ +import type { UIKitAPI } from '@electron-uikit/core' + +export const core = ((globalThis || window).uikit || + (globalThis || window).electron) as UIKitAPI diff --git a/packages/toast/src/constants.ts b/packages/toast/src/constants.ts new file mode 100644 index 0000000..125c237 --- /dev/null +++ b/packages/toast/src/constants.ts @@ -0,0 +1 @@ +export const TOAST_CHANNEL = 'uikit:toast' diff --git a/packages/toast/src/main.ts b/packages/toast/src/main.ts new file mode 100644 index 0000000..7416e35 --- /dev/null +++ b/packages/toast/src/main.ts @@ -0,0 +1,68 @@ +import { BrowserWindow } from 'electron' +import { TOAST_CHANNEL } from './constants' + +import type { ToastType, ToastLoadingFn } from './types' + +export type { ToastLoadingFn } from './types' + +/** + * Toast for Electron main process. Before use, you must ensure that + * `toast.config({ supportMain: true }` is configured in the renderer. + * + * @example + * + * ``` + * import { BrowserWindow } from 'electron' + * import { Toast } from '@electron-uikit/toast' + * + * const win = new BrowserWindow() + * + * const toast = new Toast(win) + * toast.text('foo') + * toast.loading('loading') + * ``` + */ +export class Toast { + constructor(readonly win: BrowserWindow) {} + + private show(text: string, type: ToastType, duration?: number): void { + this.win.webContents.send(TOAST_CHANNEL, 1, text, type, duration) + } + + private close(): void { + this.win.webContents.send(TOAST_CHANNEL, 0) + } + + /** + * Show text. The default duration is `2000` ms. + */ + text(text: string, duration?: number): void { + this.show(text, 'text', duration) + } + + /** + * Show loading. The default duration is 0, which means it is always displayed + * and can be turned off by calling its return value function. + * + * @example + * + * ``` + * import { Toast } from '@electron-uikit/toast' + * + * const toast = new Toast(win) + * const reply = toast.loading('Loading') + * + * setTimeout(() => { + * reply.success('Successful') + * }, 3000) + * ``` + */ + loading(text: string, duration = 0): ToastLoadingFn { + this.show(text, 'loading', duration) + return { + success: (text, duration) => this.show(text, 'success', duration), + fail: (text, duration) => this.show(text, 'failed', duration), + dismiss: () => this.close() + } + } +} diff --git a/packages/toast/src/renderer.ts b/packages/toast/src/renderer.ts new file mode 100644 index 0000000..1475217 --- /dev/null +++ b/packages/toast/src/renderer.ts @@ -0,0 +1,353 @@ +import { core } from './common' +import { TOAST_CHANNEL } from './constants' + +import type { ToastType, ToastLoadingFn } from './types' + +export type { ToastLoadingFn } from './types' + +const SHADOW_ROOT_CSS = ` +:host { + position: fixed; + left: 0; + right: 0; + bottom: var(--toast-bottom, 50px); + z-index: var(--toast-z-index, 5001); + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + height: 0; + overflow: visible; + user-select: none; + --toast-color: #48484e; + --toast-text-color: #ffffffd1; + --toast-font-size: 14px; + --toast-font-family: -apple-system, BlinkMacSystemFont, Ubuntu, 'Segoe UI'; + --toast-icon-size: 20px; + --toast-icon-margin: 0 8px 0 0; + --toast-padding: 6px 14px; + --toast-border-radius: 4px; + --toast-max-width: 320px; + --toast-box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); +} +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +@keyframes loading { + 0% { + transform: rotate3d(0, 0, 1, 0deg); + } + + 100% { + transform: rotate3d(0, 0, 1, 360deg); + } +} +.fade-in { + animation: fadeIn ease 0.3s forwards; +} +.fade-out { + animation: fadeOut ease 0.3s forwards; +} +.icon-loading { + animation: loading 1s infinite cubic-bezier(0, 0, 1, 1); + mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 48 48' fill='none' xmlns='http://www.w3.org/2000/svg' stroke='currentColor' stroke-width='4'%3E%3Cpath d='M42 24c0 9.941-8.059 18-18 18S6 33.941 6 24 14.059 6 24 6'%3E%3C/path%3E%3C/svg%3E"); +} +.icon-success { + mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M116 569a28 28 0 0 1-3-37l21-26c9-11 25-14 36-6l180 120c9 6 26 6 35-2l476-386c10-8 27-8 37 2l11 11c11 11 10 27-1 37L397 793a41 41 0 0 1-58-2L116 569z'/%3E%3C/svg%3E"); +} +.icon-failed { + mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M223 854a55 55 0 0 1-39-94l572-572a55 55 0 1 1 78 78L263 838a55 55 0 0 1-40 16z'/%3E%3Cpath d='M795 854a55 55 0 0 1-39-16L184 266a55 55 0 1 1 79-78l571 572a55 55 0 0 1-39 94z'/%3E%3C/svg%3E"); +} +.toast { + box-sizing: border-box; + display: flex; + align-items: center; + padding: var(--toast-padding); + border-radius: var(--toast-border-radius); + flex-wrap: nowrap; + overflow: hidden; + max-width: var(--toast-max-width); + color: var(--toast-text-color); + background-color: var(--toast-color); + box-shadow: var(--toast-box-shadow); +} +.toast__icon { + margin: var(--toast-icon-margin); + height: var(--toast-icon-size); + width: var(--toast-icon-size); + font-size: var(--toast-icon-size); + mask-position: 50% 50%; + mask-repeat: no-repeat; + mask-size: 100%; + background-color: currentColor; +} +.toast__content { + display: inline-block; + line-height: 1.4; + font-size: var(--toast-font-size); + font-family: var(--toast-font-family); + word-break: break-all; + text-align: center; +} +` + +export type ToastOptions = { + /** + * Container element of Toast. Default to `document.body` + */ + container?: HTMLElement + /** + * Display duration in millisecond. If set to `0`, it will not turn off + * automatically. Default to `2000`. + */ + duration?: number + /** + * Custom CSS class name for toast. + */ + customClass?: string + /** + * Toast position to the bottom. Default to `50`. + */ + bottom?: number + /** + * The maximum width of toast. Default to `320`. + */ + maxWidth?: number + /** + * Toast background color. + */ + color?: string + /** + * Toast text color. + */ + textColor?: string + /** + * Toast font size. Default to `14`. + */ + fontSize?: number + /** + * Toast icon size. Default to `20`. + */ + iconSize?: number + /** + * Support Electron main process. Default to `false`. + */ + supportMain?: boolean +} + +class Toast { + private shadowHost: HTMLElement | null = null + private shadowRoot: ShadowRoot | null = null + private view: HTMLElement | null = null + private timeout: unknown | null = null + + private container?: HTMLElement + private duration = 2000 + private customClass?: string + private customStyle: Record = {} + + private h(text: string, type: ToastType = 'text'): HTMLElement { + const view = document.createElement('div') + + const toast = document.createElement('div') + toast.classList.add('toast') + + if (type !== 'text') { + const icon = document.createElement('i') + icon.classList.add('toast__icon') + icon.classList.add(`icon-${type}`) + toast.appendChild(icon) + } + + const content = document.createElement('div') + content.classList.add('toast__content') + content.innerText = text + + toast.appendChild(content) + + view.appendChild(toast) + + return view + } + + private show(text: string, type: ToastType, duration: number): void { + if (this.shadowHost) { + if (this.timeout) { + clearTimeout(this.timeout as number) + } + this.shadowRoot!.removeChild(this.view!) + + this.view = this.h(text, type) + + this.shadowRoot!.appendChild(this.view) + } else { + this.shadowHost = document.createElement('div') + if (this.customClass) { + this.shadowHost.classList.add(this.customClass) + } + + if (this.customStyle) { + for (const [key, value] of Object.entries(this.customStyle)) { + this.shadowHost.style.setProperty(key, value) + } + } + + this.shadowRoot = this.shadowHost.attachShadow({ mode: 'open' }) + + const style = document.createElement('style') + style.textContent = SHADOW_ROOT_CSS + this.shadowRoot.appendChild(style) + + this.view = this.h(text, type) + + this.shadowRoot.appendChild(this.view) + + this.view.classList.add('fade-in') + + if (this.container) { + this.container.appendChild(this.shadowHost) + } else { + document.body.appendChild(this.shadowHost) + } + } + + if (duration > 0) { + this.timeout = setTimeout(() => { + this.view!.classList.add('fade-out') + this.view!.addEventListener('animationend', () => { + this.shadowRoot!.removeChild(this.view!) + this.shadowRoot = null + this.shadowHost!.remove() + this.shadowHost = null + this.view = null + this.timeout = null + }) + }, duration) + } + } + + private close(): void { + if (this.shadowHost) { + if (this.timeout) { + clearTimeout(this.timeout as number) + } + this.view!.classList.add('fade-out') + this.view!.addEventListener('animationend', () => { + this.shadowRoot!.removeChild(this.view!) + this.shadowRoot = null + this.shadowHost!.remove() + this.shadowHost = null + this.view = null + this.timeout = null + }) + } + } + + /** + * Configure toast defaults or customize toast. + */ + config(options: ToastOptions): void { + const { + container, + customClass, + duration, + bottom, + maxWidth, + color, + textColor, + fontSize, + iconSize + } = options + + this.container = container + this.duration = duration || this.duration + this.customClass = customClass + + if (bottom) { + this.customStyle['--toast-bottom'] = `${bottom}px` + } + if (maxWidth) { + this.customStyle['--toast-max-width'] = `${maxWidth}px` + } + if (color) { + this.customStyle['--toast-color'] = color + } + if (textColor) { + this.customStyle['--toast-text-color'] = textColor + } + if (fontSize) { + this.customStyle['--toast-font-size'] = `${fontSize}px` + } + if (iconSize) { + this.customStyle['--toast-icon-size'] = `${iconSize}px` + } + + if (options.supportMain) { + core.ipcRenderer.on( + TOAST_CHANNEL, + ( + _, + action: 0 | 1, + text: string, + type: ToastType, + duration?: number + ) => { + if (action === 1) { + this.show(text, type, duration ?? this.duration) + } else { + this.close() + } + } + ) + } + } + + /** + * Show text. The default duration is `2000` ms. + */ + text(text: string, duration = this.duration): void { + this.show(text, 'text', duration) + } + + /** + * Show loading. The default duration is 0, which means it is always displayed + * and can be turned off by calling its return value function. + * + * @example + * + * ``` + * import { toast } from '@electron-uikit/toast/renderer' + * + * const reply = toast.loading('Loading') + * + * setTimeout(() => { + * reply.success('Successful') + * }, 3000) + * ``` + */ + loading(text: string, duration = 0): ToastLoadingFn { + this.show(text, 'loading', duration) + return { + success: (text, duration = this.duration) => + this.show(text, 'success', duration), + fail: (text, duration = this.duration) => + this.show(text, 'failed', duration), + dismiss: () => this.close() + } + } +} + +export const toast = new Toast() diff --git a/packages/toast/src/types.ts b/packages/toast/src/types.ts new file mode 100644 index 0000000..1d6178c --- /dev/null +++ b/packages/toast/src/types.ts @@ -0,0 +1,16 @@ +export type ToastType = 'text' | 'loading' | 'success' | 'failed' + +export type ToastLoadingFn = { + /** + * Toggle loading toast to success status. + */ + success: (text: string, duration?: number) => void + /** + * Toggle loading toast to failed state. + */ + fail: (text: string, duration?: number) => void + /** + * Dismiss loading toast. + */ + dismiss: () => void +} diff --git a/packages/toast/tsconfig.json b/packages/toast/tsconfig.json new file mode 100644 index 0000000..564a599 --- /dev/null +++ b/packages/toast/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb6d532..b4bf11b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,9 @@ importers: '@electron-uikit/titlebar': specifier: workspace:^ version: link:../titlebar + '@electron-uikit/toast': + specifier: workspace:^ + version: link:../toast devDependencies: electron-vite: specifier: ^2.2.0 @@ -104,6 +107,12 @@ importers: specifier: workspace:^ version: link:../core + packages/toast: + devDependencies: + '@electron-uikit/core': + specifier: workspace:^ + version: link:../core + packages: /@ampproject/remapping@2.3.0: