├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src ├── assets │ └── style.css ├── c.ts └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist/ 4 | build/ 5 | .nvmrc 6 | 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | 18 | # Editor directories and files 19 | *.tgz 20 | .idea 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | 27 | # lock files 28 | package-lock.json 29 | *.lock 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Balthazar DOSSOU 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - [TinyDialog](#tinydialog) 2 | - [Description](#description) 3 | - [Using on Node Js](#using-on-node-js) 4 | - [Example with HTML](#example-with-html) 5 | - [Docs](#docs) 6 | - [Types \& Interfaces](#types--interfaces) 7 | - [Options](#options) 8 | - [Prototype public functions](#prototype-public-functions) 9 | - [ClassName](#classname) 10 | 11 | 12 | # TinyDialog 13 | Tiny Javascript dialog box made with TypeScript 14 | 15 | image 16 | 17 | 18 | 19 | 20 | 21 | ## Description 22 | Tiny, flexible and featured javascript dialog box wrote in TypeScript. 23 | You can add it to any web project with Angular, Vue, React, and others Front-End Framework. 24 | Fully compatible with FontAwesome Icons. 25 | 26 | ## Using on Node Js 27 | ```sh 28 | npm install @lbgm/tiny-dialog 29 | ``` 30 | 31 | ```ts 32 | // import style only once 33 | import "@lbgm/tiny-dialog/style" 34 | 35 | import { TinyDialog } from "@lbgm/tiny-dialog"; 36 | 37 | const mydialog = new TinyDialog({ 38 | closeIcon: true, 39 | ctrlOpen: false, 40 | timer: "ok|36000", 41 | icon: "far fa-grin-beam", 42 | title: "", 43 | content: "Are you confirming your participation for the Friday event?", 44 | buttons: { 45 | ok: { 46 | className: "as-button btn-accept", 47 | text: ' Confirm', 48 | action: function () { 49 | // do something 50 | } 51 | }, 52 | cancel: { 53 | hide: false, 54 | className: "as-button btn-cancel", 55 | text: ' Refuse', 56 | action: function () { 57 | // do something 58 | } 59 | } 60 | }, 61 | onContentReady: function () { 62 | // do something 63 | }, 64 | onOpenBefore: function () { 65 | // do something 66 | }, 67 | onOpen: function () { 68 | // do something 69 | }, 70 | onAction: function () { 71 | // do something 72 | }, 73 | onClose: function () { 74 | // do something 75 | }, 76 | onDestroy: function () { 77 | // do something 78 | } 79 | }); 80 | ``` 81 | 82 | 83 | ## Example with HTML 84 | 85 | - clone and build by running `npm run compile`; 86 | - Copy `build/src`(`c.js|js.map`, `index.js|js.map`) into your destination. 87 | 88 | ```html 89 | 90 | 91 | 119 | ``` 120 | 121 | # Docs 122 | 123 | ## Types & Interfaces 124 | 125 | ```ts 126 | type TinyDialogContent = string | HTMLElement | Node | Node[]; 127 | export type TinyDialogButton = { 128 | hide?: boolean; 129 | className?: string; 130 | text?: TinyDialogContent; 131 | action: (...args: any[]) => any; 132 | }; 133 | export type TinyDialogButtons = { 134 | [id_button: string]: TinyDialogButton; 135 | }; 136 | export interface TinyDialogArg { 137 | closeIcon?: boolean; 138 | ctrlOpen?: boolean; 139 | timer?: string; 140 | title?: string; 141 | content?: TinyDialogContent; 142 | icon?: string; 143 | buttons?: TinyDialogButtons; 144 | onContentReady?: (...args: any[]) => any; 145 | onOpenBefore?: (...args: any[]) => any; 146 | onOpen?: (...args: any[]) => any; 147 | onAction?: (...args: any[]) => any; 148 | onClose?: (...args: any[]) => any; 149 | onDestroy?: (...args: any[]) => any; 150 | } 151 | export declare class TinyDialog { 152 | #private; 153 | arg: TinyDialogArg; 154 | $$content: HTMLDivElement; 155 | $$title: HTMLDivElement; 156 | $$buttons: HTMLDivElement; 157 | $$icon: HTMLDivElement; 158 | constructor(arg: TinyDialogArg); 159 | destroy(): boolean; 160 | close(): boolean | void; 161 | open(): boolean | void; 162 | isOpen(): boolean; 163 | domResized(): void; 164 | } 165 | ``` 166 | 167 | ## Options 168 | 169 | | Option | Type | Description | 170 | | :--- | :--- | :--- | 171 | | closeIcon | `boolean` | This option displays an icon to close the box. the widget is directly linked to the close and action functions `onClose`, `onAction`. Code inside these functions will be executed. | 172 | | ctrlOpen | `boolean` | This option controls the automatic opening of the box. it is linked to the pre-opening and opening functions `onOpenBefore`, `onOpen`. Code inside these functions will be executed. When the option is `true` you must call `.open()` to open the box. If it is `false`, the box will open automatically. | 173 | | timer | `string` | `id_button\|delay_time`
This option automatically executes a `click` on a button `id_button` located on the box after a given time in milliseconds `delay_time`. This function is linked to the `onAction` function, and the action function linked to the targeted button. Code found inside these functions will be executed. | 174 | | icon | `string` | This option displays a main icon. use a [fontAwesome](https://fontawesome.com/) className or a className displaying a personal icon. The displayed content is accessible via `this.$$icon` | 175 | | title | `string` | `text or html`
This option displays a main title. The displayed content is accessible via `this.$$title` . | 176 | | content | `string` | `text or html`
This option displays html or text content in the main container. The displayed content is accessible via `this.$$content` . | 177 | | buttons | `TinyDialogButtons` | This option creates buttons.
The buttons are accessible via their parent `this.$$buttons`. A button is accessible via `this.$$id_button`. | 178 | | onContentReady | `Function` | This function runs when the box instance is created and ready for use. Code inside this function will be executed. within the function, `this` refers to the box instance. Thus its elements like `this.$$title`, `this.$$icon`, `this.$$buttons`, `this.$$content` are accessible (the same for the functions below). | 179 | | onOpenBefore | `Function` | This function runs before opening the box. Code inside this function will be executed. | 180 | | onOpen | `Function` | This function runs when the box is opened. Code inside this function will be executed. | 181 | | onAction | `Function` | This function is executed when an action is triggered on the box. Code inside this function will be executed. | 182 | | onClose | `Function` | This function runs when the box is closed. Code inside this function will be executed. | 183 | | onDestroy | `Function` | This function is executed when the box is destroyed. Code inside this function will be executed. | 184 | 185 | ## Prototype public functions 186 | 187 | | Function | Description | 188 | | :--- | :--- | 189 | | .open() | This function opens the box. It returns true on success or void on failure. | 190 | | .close() | This function closes the box. It returns true on success or void on failure. Note that closing does not destroy the box on DOM. We can reopen the box by calling .open() | 191 | | .destroy() | This function closes and destroys the box on DOM. It returns true on success or false on failure. | 192 | | .isOpen() | This function checks if the box is open or closed. It returns true or false. | 193 | 194 | ## ClassName 195 | 196 | | Name | Description | 197 | | :--- | :--- | 198 | | tiny-dialog-cross | svg icon className of closeIcon option. If you need to change the color of the icon, use the `tiny-dialog-cross-path` className then define some css.
ex: `fill: #fff;`. | 199 | | tiny-dialog-closer | container className of the button to close the box. | 200 | | tiny-dialog-icon | container className of box icon. | 201 | | tiny-dialog-wrp | box overlay className. | 202 | | tiny-dialog-bw | className of the box container. Flexible, centered and no selection of content possible. | 203 | | tiny-dialog-head | header container className. It extends over the available width. | 204 | | tiny-dialog-title | box title container className. It extends over the available width. | 205 | | tiny-dialog-content | className of the container of the content you are displaying. It extends over the available width. | 206 | | tiny-dialog-btns | Dialog button container className. Flexible, it extends over the available width. | 207 | 208 | > All containers are `HTMLDivElement` 209 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lbgm/tiny-dialog", 3 | "version": "1.0.3", 4 | "author": "@lbgm", 5 | "description": "JS dialog box", 6 | "license": "MIT", 7 | "main": "build/src/index.js", 8 | "exports": { 9 | ".": { 10 | "import": "./build/src/index.js", 11 | "require": "./build/src/index.js" 12 | }, 13 | "./style": { 14 | "import": "./build/src/assets/style.css", 15 | "require": "./build/src/assets/style.css" 16 | }, 17 | "./cdn": { 18 | "import": "./build/src/c.js", 19 | "require": "./build/src/index.js" 20 | } 21 | }, 22 | "types": "build/src/index.d.ts", 23 | "files": [ 24 | "build/src" 25 | ], 26 | "keywords": [ 27 | "vuejs", 28 | "angular", 29 | "dialog box", 30 | "typescript", 31 | "javascript", 32 | "typescript dialog box" 33 | ], 34 | "engines": { 35 | "node": ">=14.18.3", 36 | "npm": ">=6.14.15" 37 | }, 38 | "scripts": { 39 | "test": "echo \"Error: no test specified\" && exit 1", 40 | "lint": "gts lint", 41 | "clean": "gts clean", 42 | "compile": "tsc && cp -R ./src/assets build/src/.", 43 | "fix": "gts fix", 44 | "prepare": "npm run compile", 45 | "pretest": "npm run compile", 46 | "posttest": "npm run lint" 47 | }, 48 | "devDependencies": { 49 | "@types/node": "^14.18.34", 50 | "eslint": "^7.32.0", 51 | "gts": "^3.1.0", 52 | "typescript": "^4.0.3" 53 | }, 54 | "dependencies": {}, 55 | "repository": { 56 | "type": "git", 57 | "url": "git+https://github.com/lbgm/tiny-dialog.git" 58 | }, 59 | "bugs": { 60 | "url": "https://github.com/lbgm/tiny-dialog/issues" 61 | }, 62 | "homepage": "https://github.com/lbgm/tiny-dialog#readme" 63 | } 64 | -------------------------------------------------------------------------------- /src/assets/style.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * TinyDialog Box Style 3 | * Author: Balthazar DOSSOU {https://github.com/lbgm} 4 | * Rodolphe SOUNLIN {https://github.com/BigD95} 5 | * Licensed under MIT 6 | */ 7 | body[data-tinydialog-is-under] { 8 | overflow: hidden 9 | } 10 | 11 | .tiny-dialog-wrp { 12 | width: 100vw; 13 | height: 100vh; 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | display: flex; 18 | flex-direction: column; 19 | justify-content: center; 20 | align-items: center; 21 | background: rgba(0, 0, 0, .7) 22 | } 23 | 24 | .tiny-dialog-wrp *, 25 | .tiny-dialog-wrp { 26 | -user-select: none; 27 | -moz-user-select: none; 28 | -khtml-user-select: none; 29 | -ms-user-select: none; 30 | -webkit-user-select: none; 31 | -webkit-touch-callout: none; 32 | box-sizing: border-box; 33 | -webkit-tap-highlight-color: transparent; 34 | outline: none 35 | } 36 | 37 | .tiny-dialog-bw { 38 | width: 300px; 39 | max-width: 90%; 40 | max-height: 80%; 41 | position: relative; 42 | background: #dedede; 43 | padding: 15px; 44 | color: #444; 45 | border-radius: 6px; 46 | overflow-x: visible; 47 | overflow-y: auto; 48 | box-shadow: 0px 2px 4px 0px rgba(68, 68, 68, .3) 49 | } 50 | 51 | .tiny-dialog-bw>* { 52 | margin: 0; 53 | margin-bottom: 16px 54 | } 55 | 56 | .tiny-dialog-head { 57 | display: flex; 58 | justify-content: space-between; 59 | align-items: center; 60 | position: relative; 61 | width: 100% 62 | } 63 | 64 | .tiny-dialog-head-flend { 65 | justify-content: flex-end 66 | } 67 | 68 | .tiny-dialog-title { 69 | width: 100%; 70 | font-size: 18px; 71 | font-weight: bold; 72 | white-space: pre-line; 73 | word-break: break-word 74 | } 75 | 76 | .tiny-dialog-content { 77 | width: 100%; 78 | white-space: pre-line; 79 | word-break: break-word 80 | } 81 | 82 | .tiny-dialog-btns { 83 | width: 100%; 84 | display: flex; 85 | justify-content: space-around; 86 | flex-direction: row; 87 | flex-wrap: wrap; 88 | margin: 0 89 | } 90 | 91 | .tiny-dialog-btns button { 92 | margin: 5px 93 | } 94 | 95 | .tiny-dialog-icon { 96 | display: inline-flex; 97 | align-items: center; 98 | width: 16px; 99 | height: 16px; 100 | pointer-events: none 101 | } 102 | 103 | .tiny-dialog-closer { 104 | width: 16px; 105 | height: 16px; 106 | cursor: pointer; 107 | display: inline-flex; 108 | align-items: center 109 | } 110 | 111 | .tiny-dialog-cross { 112 | fill: rgba(68, 68, 68, .5) 113 | } -------------------------------------------------------------------------------- /src/c.ts: -------------------------------------------------------------------------------- 1 | import { TinyDialog } from './index.js'; 2 | 3 | type WWindow = typeof window & { 4 | TinyDialog: typeof TinyDialog; 5 | }; 6 | 7 | (window as WWindow).TinyDialog = TinyDialog; 8 | 9 | const tinydialogStyle = ` 10 | /**! 11 | * TinyDialog CSS 12 | * Author: Balthazar DOSSOU {dosanel@outlook.fr} 13 | * Rodolphe SOUNLIN {rodolphe.sounlin@yahoo.fr} 14 | * Licensed under MIT 15 | */ 16 | body[data-tinydialog-is-under] { 17 | overflow: hidden 18 | } 19 | 20 | .tiny-dialog-wrp { 21 | width: 100vw; 22 | height: 100vh; 23 | position: fixed; 24 | top: 0; 25 | left: 0; 26 | display: flex; 27 | flex-direction: column; 28 | justify-content: center; 29 | align-items: center; 30 | background: rgba(0, 0, 0, .7) 31 | } 32 | 33 | .tiny-dialog-wrp *, 34 | .tiny-dialog-wrp { 35 | -user-select: none; 36 | -moz-user-select: none; 37 | -khtml-user-select: none; 38 | -ms-user-select: none; 39 | -webkit-user-select: none; 40 | -webkit-touch-callout: none; 41 | box-sizing: border-box; 42 | -webkit-tap-highlight-color: transparent; 43 | outline: none 44 | } 45 | 46 | .tiny-dialog-bw { 47 | width: 300px; 48 | max-width: 90%; 49 | max-height: 80%; 50 | position: relative; 51 | background: #dedede; 52 | padding: 15px; 53 | color: #444; 54 | border-radius: 6px; 55 | overflow-x: visible; 56 | overflow-y: auto; 57 | box-shadow: 0px 2px 4px 0px rgba(68, 68, 68, .3) 58 | } 59 | 60 | .tiny-dialog-bw>* { 61 | margin: 0; 62 | margin-bottom: 16px 63 | } 64 | 65 | .tiny-dialog-head { 66 | display: flex; 67 | justify-content: space-between; 68 | align-items: center; 69 | position: relative; 70 | width: 100% 71 | } 72 | 73 | .tiny-dialog-head-flend { 74 | justify-content: flex-end 75 | } 76 | 77 | .tiny-dialog-title { 78 | width: 100%; 79 | font-size: 18px; 80 | font-weight: bold; 81 | white-space: pre-line; 82 | word-break: break-word 83 | } 84 | 85 | .tiny-dialog-content { 86 | width: 100%; 87 | white-space: pre-line; 88 | word-break: break-word 89 | } 90 | 91 | .tiny-dialog-btns { 92 | width: 100%; 93 | display: flex; 94 | justify-content: space-around; 95 | flex-direction: row; 96 | flex-wrap: wrap; 97 | margin: 0 98 | } 99 | 100 | .tiny-dialog-btns button { 101 | margin: 5px 102 | } 103 | 104 | .tiny-dialog-icon { 105 | display: inline-flex; 106 | align-items: center; 107 | width: 16px; 108 | height: 16px; 109 | pointer-events: none 110 | } 111 | 112 | .tiny-dialog-closer { 113 | width: 16px; 114 | height: 16px; 115 | cursor: pointer; 116 | display: inline-flex; 117 | align-items: center 118 | } 119 | 120 | .tiny-dialog-cross { 121 | fill: rgba(68, 68, 68, .5) 122 | } 123 | `; 124 | 125 | 126 | (() => { 127 | const styleElement: HTMLStyleElement = document.createElement('style'); 128 | styleElement.setAttribute('type', 'text/css'); 129 | styleElement.setAttribute('data-tiny-dialog', 'style'); 130 | 131 | styleElement.innerHTML = tinydialogStyle; 132 | 133 | const head = document.head || document.getElementsByTagName('head')[0]; 134 | 135 | if (head) head.appendChild(styleElement); 136 | 137 | })(); -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * TinyDialog Box 3 | * Author: Balthazar DOSSOU {https://github.com/lbgm} 4 | * Rodolphe SOUNLIN {https://github.com/BigD95} 5 | * Licensed under MIT 6 | */ 7 | 8 | const resizeDecorator = (target?: Object, propertyKey?: string, descriptor?: PropertyDescriptor): PropertyDescriptor | undefined => { 9 | const original = (descriptor as PropertyDescriptor).value as () => void; 10 | (descriptor as PropertyDescriptor).value = function (...args: any) { 11 | let result = original.apply(this, args); 12 | window.addEventListener('resize', (e) => { 13 | result = original.apply(this, args); 14 | }); 15 | return result; 16 | } 17 | 18 | return descriptor; 19 | } 20 | 21 | type TinyDialogContent = string | HTMLElement | Node | Node[]; 22 | 23 | export type TinyDialogButton = { 24 | hide?: boolean; 25 | className?: string; 26 | text?: TinyDialogContent; 27 | action: (...args: any[]) => any; 28 | }; 29 | 30 | export type TinyDialogButtons = { 31 | [id_button: string]: TinyDialogButton 32 | }; 33 | 34 | export interface TinyDialogArg { 35 | closeIcon?: boolean; 36 | ctrlOpen?: boolean; 37 | timer?: string; 38 | title?: string; 39 | content?: TinyDialogContent; 40 | icon?: string; 41 | buttons?: TinyDialogButtons; 42 | onContentReady?: (...args: any[]) => any; 43 | onOpenBefore?: (...args: any[]) => any; 44 | onOpen?: (...args: any[]) => any; 45 | onAction?: (...args: any[]) => any; 46 | onClose?: (...args: any[]) => any; 47 | onDestroy?: (...args: any[]) => any; 48 | } 49 | 50 | export class TinyDialog { 51 | #cross?: SVGSVGElement; 52 | #closer?: HTMLDivElement; 53 | #icon?: HTMLDivElement; 54 | #wrp?: HTMLDivElement; 55 | #bw?: HTMLDivElement; 56 | #head?: HTMLDivElement; 57 | #title?: HTMLDivElement; 58 | #content?: HTMLDivElement; 59 | #buttons?: HTMLDivElement; 60 | #state?: number; 61 | #$cd?: HTMLSpanElement; 62 | #id?: number; 63 | #detached?: HTMLDivElement; 64 | #timerInterval?: NodeJS.Timeout; 65 | 66 | $$content!: HTMLDivElement; 67 | $$title!: HTMLDivElement; 68 | $$buttons!: HTMLDivElement; 69 | $$icon!: HTMLDivElement; 70 | 71 | constructor(public arg: TinyDialogArg) { 72 | 73 | if (typeof arg !== 'object') return this; 74 | arg.title = arg.title || ''; 75 | arg.content = arg.content || ''; 76 | 77 | this.arg = arg; 78 | 79 | this.#cross = this.#createSVG( 80 | ['16', '16', '0 0 16 16', 'button', 'tiny-dialog-cross'], 81 | [ 82 | 'M21.181,19.289,26.9,13.573A1.339,1.339,0,1,0,25,11.678l-5.715,5.716-5.715-5.716a1.339,1.339,0,1,0-1.894,1.894l5.715,5.716L11.679,25A1.339,1.339,0,0,0,13.573,26.9l5.715-5.716L25,26.9A1.339,1.339,0,0,0,26.9,25Z', 83 | 'translate(-11.285 -11.289)', 84 | 'tiny-dialog-cross-path', 85 | ] 86 | ); 87 | this.#closer = this.#query('div', { className: 'tiny-dialog-closer', html: this.#cross }) as HTMLDivElement; 88 | this.#icon = this.#query('div', { 89 | className: 'tiny-dialog-icon', 90 | html: this.#query('i', { className: this.arg.icon || '' }), 91 | }) as HTMLDivElement; 92 | 93 | this.#wrp = this.#query('div', { className: 'tiny-dialog-wrp' }) as HTMLDivElement; 94 | this.#bw = this.#query('div', { className: 'tiny-dialog-bw' }) as HTMLDivElement; 95 | 96 | this.#head = this.#query('div', { className: 'tiny-dialog-head' }) as HTMLDivElement; 97 | this.#title = this.#query('div', { className: 'tiny-dialog-title', html: this.arg.title }) as HTMLDivElement; 98 | this.#content = this.#query('div', { 99 | className: 'tiny-dialog-content', 100 | html: this.arg.content, 101 | }) as HTMLDivElement; 102 | this.#buttons = this.#query('div', { className: 'tiny-dialog-btns' }) as HTMLDivElement; 103 | 104 | this.#putContent(); 105 | 106 | if (typeof this.arg.onOpenBefore === 'function') this.arg.onOpenBefore.apply(this); 107 | 108 | if (this.#lazyOpen() === 1) void 0; 109 | else this.open(); 110 | } 111 | 112 | #query(element: string, params: { className?: string; id?: string; html?: TinyDialogContent; }): HTMLElement { 113 | const el = document.createElement(element); 114 | if (params.id) el.id = params.id; 115 | if (params.className) el.className = params.className; 116 | if (typeof params.html === 'string') 117 | el.innerHTML = params.html; 118 | else if (params.html) { 119 | // readme: (params.html as Node[])[0] if Jquery $(...) is passed 120 | el.append((params.html as Node[])[0] || params.html); 121 | } 122 | 123 | return el; 124 | } 125 | 126 | #el(element: string): HTMLElement | null { 127 | return document.querySelector(element); 128 | } 129 | 130 | #createButtons(): number | void { 131 | let res: any; 132 | if (typeof this.arg.buttons !== 'object') return 0; 133 | this.#buttons!.innerHTML = ''; 134 | Object.entries(this.arg.buttons).forEach((value: [string, TinyDialogButton]) => { 135 | const a = value[0]; 136 | const b = value[1]; 137 | Object(this)[`$$${a}`] = this.#query('button', { 138 | id: a, 139 | className: b.className || '', 140 | html: b.text || a, 141 | }) as HTMLButtonElement; 142 | this.#buttons!.append(Object(this)[`$$${a}`]); 143 | Object(this)[`$$${a}`].addEventListener('click', (e: Event) => { 144 | e.preventDefault(); 145 | if (typeof this.arg.onAction === 'function') 146 | this.arg.onAction.apply(this, [Object(this)[`$$${a}`]]); 147 | if (typeof b.action !== 'undefined') 148 | res = b.action.apply(this, [e, Object(this)[`$$${a}`]]); 149 | this.#stopTimer(); 150 | if (typeof res === 'undefined' || res) this.close(); 151 | }); 152 | if (typeof b.hide === 'boolean' && b.hide) (Object(this)[`$$${a}`] as HTMLButtonElement).style.display = 'none'; 153 | }); 154 | } 155 | 156 | #closerIcon(): boolean { 157 | if (typeof this.arg.closeIcon === 'boolean' && this.arg.closeIcon) 158 | return !0; 159 | else return !1; 160 | } 161 | 162 | #lazyOpen(): number { 163 | if (typeof this.arg.ctrlOpen === 'boolean' && this.arg.ctrlOpen) return 1; 164 | else if (typeof this.arg.ctrlOpen === 'boolean' && !this.arg.ctrlOpen) 165 | return 0; 166 | else return 0; 167 | } 168 | 169 | /** 170 | * start timer 171 | * @returns {boolean | void} 172 | */ 173 | #startTimer(): boolean | void { 174 | const opt = String(this.arg.timer).split('|'); 175 | if (opt.length !== 2) { 176 | console.error("Invalid. example 'close|10000'"); 177 | return !1; 178 | } 179 | const button_key = opt[0]; 180 | const time = parseInt(opt[1]); 181 | if (typeof Object(this)[`$$${button_key}`] === 'undefined') { 182 | console.error(`button key ${button_key} not found`); 183 | return !1; 184 | } 185 | let seconds = Math.ceil(time / 1e3); 186 | this.#$cd = this.#query('span', { html: ` (${seconds})` }); 187 | Object(this)[`$$${button_key}`].append(this.#$cd); 188 | 189 | this.#timerInterval = setInterval(() => { 190 | this.#$cd!.innerHTML = ` (${(seconds -= 1)})`; 191 | if (seconds <= 0) { 192 | Object(this)[`$$${button_key}`].dispatchEvent(new Event('click')); 193 | this.#stopTimer(); 194 | } 195 | }, 1e3); 196 | } 197 | 198 | #rgnrt(): void { 199 | this.#id = new Date().getTime(); 200 | this.#wrp!.setAttribute('id', `tiny-dialog-wrp-${this.#id}`); 201 | this.#bw!.setAttribute('id', `tiny-dialog-bw-${this.#id}`); 202 | } 203 | 204 | /** 205 | * Build dialog content 206 | */ 207 | #putContent(): void { 208 | this.#bw!.innerHTML = ''; 209 | //head 210 | this.#bw!.prepend(this.#head as HTMLDivElement); 211 | //icon 212 | if (typeof this.arg.icon !== 'undefined') this.#head!.prepend(this.#icon as HTMLDivElement); 213 | else this.#head!.classList.add('tiny-dialog-head-flend'); 214 | //closer icon 215 | if (this.#closerIcon()) this.#head!.append(this.#closer as HTMLDivElement); 216 | this.#closer!.addEventListener('click', (e: Event) => { 217 | e.preventDefault(); 218 | if (typeof this.arg.onAction === 'function') 219 | this.arg.onAction.apply(this, [this.#closer]); 220 | this.close(); 221 | }); 222 | //box title 223 | this.#bw!.append(this.#title as HTMLDivElement); 224 | //box content 225 | this.#bw!.append(this.#content as HTMLDivElement); 226 | //box buttons 227 | this.#bw!.append(this.#buttons as HTMLDivElement); 228 | this.#createButtons(); 229 | 230 | this.$$content = this.#content as HTMLDivElement; 231 | this.$$title = this.#title as HTMLDivElement; 232 | this.$$buttons = this.#buttons as HTMLDivElement; 233 | this.$$icon = this.#icon as HTMLDivElement; 234 | 235 | this.#rgnrt(); 236 | 237 | this.#wrp!.appendChild(this.#bw as HTMLDivElement); 238 | 239 | this.#wrp!.style.zIndex = `${new Date().getTime()}`; 240 | 241 | //check window innerSize 242 | this.domResized(); 243 | 244 | if (typeof this.arg.onContentReady === 'function') 245 | this.arg.onContentReady.apply(this); 246 | } 247 | 248 | #stopTimer(): void { 249 | clearInterval(this.#timerInterval); 250 | if (this.#$cd) this.#$cd.remove(); 251 | } 252 | 253 | #createSVG(...r: any): SVGSVGElement { 254 | const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 255 | svg.setAttribute('width', r[0][0]); 256 | svg.setAttribute('height', r[0][1]); 257 | svg.setAttribute('viewBox', r[0][2]); 258 | svg.setAttribute('role', r[0][3] || ''); 259 | svg.setAttribute('class', r[0][4] || ''); 260 | const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 261 | path.setAttribute('d', r[1][0]); 262 | path.setAttribute('transform', r[1][1]); 263 | path.setAttribute('class', r[1][2]); 264 | svg.appendChild(path); 265 | return svg; 266 | } 267 | 268 | destroy(): boolean { 269 | try { 270 | this.#stopTimer(); 271 | this.#wrp!.remove(); 272 | if (typeof this.arg.onDestroy === 'function') 273 | this.arg.onDestroy.apply(this); 274 | return !0; 275 | } catch (e) { 276 | void e; 277 | return !1; 278 | } 279 | } 280 | 281 | close(): boolean | void { 282 | let res: any; 283 | if (this.#state === 300) { 284 | console.warn('tinydialog was closed'); 285 | return void 0; 286 | } 287 | if (typeof this.arg.onClose === 'function') 288 | res = this.arg.onClose.apply(this); 289 | if (typeof res !== 'undefined' && !res) return !0; 290 | this.#stopTimer(); 291 | this.#detached = this.#wrp; 292 | this.#wrp!.remove(); 293 | this.#state = 300; 294 | document.body.removeAttribute('data-tinydialog-is-under'); 295 | return !0; 296 | } 297 | 298 | open(): boolean | void { 299 | if (this.#state === 200) { 300 | console.warn('tinydialog was opened'); 301 | return void 0; 302 | } else if (this.#state === 300) document.body.appendChild(this.#detached as HTMLDivElement); 303 | else document.body.appendChild(this.#wrp as HTMLDivElement); 304 | 305 | this.#state = 200; 306 | 307 | //trigger onOpen argument and #startTimer if is defined 308 | if (typeof this.arg.onOpen === 'function') this.arg.onOpen.apply(this); 309 | if (typeof this.arg.timer !== 'undefined') this.#startTimer.apply(this); 310 | document.body.setAttribute('data-tinydialog-is-under', 'RDSTATE'); 311 | 312 | return !0; 313 | } 314 | 315 | isOpen(): boolean { 316 | try { 317 | if (this.#state === 200) return !0; 318 | else return !1; 319 | } catch (e) { 320 | void e; 321 | return !1; 322 | } 323 | } 324 | 325 | @resizeDecorator 326 | domResized(): void { 327 | this.#wrp!.style.height = `${window.innerHeight}px`; 328 | } 329 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts/tsconfig-google.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "lib": [ 7 | "es6", 8 | "dom" 9 | ], 10 | "target": "es6", 11 | "module": "es6", 12 | "experimentalDecorators": true, 13 | }, 14 | "include": [ 15 | "src/**/*.ts", 16 | "src/**/*", 17 | "test/**/*.ts" 18 | ], 19 | "moduleResolution": "node", 20 | "experimentalDecorators": true, 21 | "emitDecoratorMetadata": true 22 | } 23 | --------------------------------------------------------------------------------