├── art └── banner.png ├── .gitignore ├── src ├── components │ ├── optional.vue │ ├── link.vue │ ├── progress.vue │ ├── attribute.vue │ ├── clear.vue │ ├── sticker.vue │ ├── empty.vue │ ├── tabs.vue │ ├── switch.vue │ ├── label.vue │ ├── tip.vue │ ├── badge.vue │ ├── cards.vue │ ├── banner.vue │ ├── error.vue │ ├── confirm.vue │ ├── notification.vue │ ├── remaining.vue │ ├── grid.vue │ ├── prompt.vue │ ├── modal.vue │ ├── metric.vue │ ├── dropdown.vue │ ├── table.vue │ ├── password.vue │ ├── textbox.vue │ ├── alert.vue │ ├── period.vue │ ├── menu.vue │ ├── tags.vue │ ├── search.vue │ ├── share.vue │ ├── button.vue │ ├── paginator.vue │ ├── upload.vue │ └── datetime.vue ├── mixins │ ├── Container.js │ ├── Utilities.js │ ├── Foundation.js │ └── Dialog.js ├── support │ └── vapor.js └── framework │ └── Dialog.js ├── README.md ├── LICENSE.md └── package.json /art/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caneara/varnish/HEAD/art/banner.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | docs/.vitepress/dist 4 | docs/public/ProximaVara.woff2 -------------------------------------------------------------------------------- /src/components/optional.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/link.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 |

7 | 8 | 9 |

10 | Latest Release 11 | License 12 |

13 | 14 | # Varnish UI 15 | 16 | This library contains a set of UI components. Originally, these components were part of [Lumeno's](https://github.com/caneara/lumeno) codebase, however they have since been extracted, extended and made available for open-source use. 17 | 18 | ## Abandoned 19 | 20 | This package is now abandoned and will not receive further updates. That said, feel free to explore it for educational value. 21 | 22 | ## License 23 | 24 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 25 | -------------------------------------------------------------------------------- /src/components/progress.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /src/mixins/Container.js: -------------------------------------------------------------------------------- 1 | export default 2 | { 3 | /** 4 | * Execute actions when the component is instantiated. 5 | * 6 | */ 7 | created() 8 | { 9 | document.addEventListener('click', this.pageClicked); 10 | }, 11 | 12 | /** 13 | * Execute actions when the component is unmounted. 14 | * 15 | */ 16 | unmounted() 17 | { 18 | document.removeEventListener('click', this.pageClicked); 19 | }, 20 | 21 | /** 22 | * Define the supporting methods. 23 | * 24 | */ 25 | methods : 26 | { 27 | /** 28 | * Handle a click or tap on the page. 29 | * 30 | */ 31 | pageClicked(event) 32 | { 33 | let trigger = this.$refs.trigger ? this.$refs.trigger : this.$el; 34 | 35 | if (! this.hasUserAttention() || trigger === event.target) return; 36 | 37 | trigger.contains(event.target) ? null : this.lostUserAttention(); 38 | }, 39 | } 40 | } -------------------------------------------------------------------------------- /src/components/attribute.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © Caneara and contributors 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@caneara/varnish", 3 | "author": "Caneara", 4 | "version": "5.0.4", 5 | "license": "MIT", 6 | "description": "A library of UI components built using Vue.js and TailwindCSS.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/caneara/varnish.git" 10 | }, 11 | "keywords": [ 12 | "ui", 13 | "vue", 14 | "library", 15 | "tailwind", 16 | "component" 17 | ], 18 | "files": [ 19 | "src" 20 | ], 21 | "scripts": { 22 | "watch": "vitepress dev docs", 23 | "build": "vitepress build docs", 24 | "serve": "vitepress serve docs" 25 | }, 26 | "dependencies": { 27 | "@yaireo/tagify": "^4.16.4", 28 | "luxon": "^3.0.3" 29 | }, 30 | "devDependencies": { 31 | "autoprefixer": "^10.4.1", 32 | "tailwindcss": "^3.1.8", 33 | "vitepress": "^1.0.0-alpha.15" 34 | }, 35 | "postcss": { 36 | "plugins": { 37 | "autoprefixer": {}, 38 | "tailwindcss": { 39 | "config": "docs/.vitepress/tailwind.js" 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/components/clear.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /src/mixins/Utilities.js: -------------------------------------------------------------------------------- 1 | export default 2 | { 3 | /** 4 | * Define the supporting methods. 5 | * 6 | */ 7 | methods : 8 | { 9 | /** 10 | * Determine if an automated testing framework is running. 11 | * 12 | */ 13 | automated() 14 | { 15 | let platforms = ['cypress', 'dusk']; 16 | 17 | for (const platform of platforms) { 18 | if (document.body.classList.contains(platform)) { 19 | return true; 20 | } 21 | } 22 | 23 | return false; 24 | }, 25 | 26 | /** 27 | * Determine if the given value is empty. 28 | * 29 | */ 30 | blank(value) 31 | { 32 | if (Array.isArray(value)) { 33 | return value.length === 0; 34 | } 35 | 36 | if (value instanceof Date) { 37 | return false; 38 | } 39 | 40 | if (typeof value === 'object' && value !== null) { 41 | return Object.keys(value).length === 0 && 42 | Object.getOwnPropertyNames(value).length === 0; 43 | } 44 | 45 | return ['', null, undefined].includes(value); 46 | }, 47 | 48 | /** 49 | * Assign the given value to the clipboard. 50 | * 51 | */ 52 | copy(value, closure = null) 53 | { 54 | let promise = window.navigator.clipboard.writeText(value); 55 | 56 | return closure ? promise.then(() => closure()) : null; 57 | }, 58 | } 59 | } -------------------------------------------------------------------------------- /src/components/sticker.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /src/components/empty.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | -------------------------------------------------------------------------------- /src/components/tabs.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/switch.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/label.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | -------------------------------------------------------------------------------- /src/components/tip.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | -------------------------------------------------------------------------------- /src/mixins/Foundation.js: -------------------------------------------------------------------------------- 1 | export default 2 | { 3 | /** 4 | * Define the data model. 5 | * 6 | */ 7 | data() { return { 8 | fault : this.error, 9 | focus : false, 10 | hover : false, 11 | }}, 12 | 13 | /** 14 | * Define the events. 15 | * 16 | */ 17 | emits : ['change', 'update:modelValue'], 18 | 19 | /** 20 | * Define the public properties. 21 | * 22 | */ 23 | props : { 24 | 'autocomplete' : { type : String, default : 'off' }, 25 | 'error' : { type : String, default : '' }, 26 | 'icon' : { type : String, default : '' }, 27 | 'id' : { type : String, default : '' }, 28 | 'label' : { type : String, default : 'Label' }, 29 | 'modelValue' : { type : [String, Number, Boolean, Array], default : '' }, 30 | 'optional' : { type : Boolean, default : false }, 31 | 'optionalText' : { type : String, default : 'Optional' }, 32 | 'placeholder' : { type : String, default : '' }, 33 | }, 34 | 35 | /** 36 | * Define the computed properties. 37 | * 38 | */ 39 | computed : 40 | { 41 | /** 42 | * Create a unique identifier for the component. 43 | * 44 | */ 45 | name() 46 | { 47 | if (this.id) return this.id; 48 | 49 | if (this.label) return this.label.toLowerCase().replaceAll(' ', '_'); 50 | 51 | return `id-${parseInt(performance.now())}`; 52 | }, 53 | }, 54 | 55 | /** 56 | * Define the watch methods. 57 | * 58 | */ 59 | watch : 60 | { 61 | /** 62 | * Watch the 'error' property. 63 | * 64 | */ 65 | error : function(current, previous) 66 | { 67 | return this.fault = current; 68 | } 69 | }, 70 | 71 | /** 72 | * Define the supporting methods. 73 | * 74 | */ 75 | methods : 76 | { 77 | /** 78 | * Set the component's value. 79 | * 80 | */ 81 | change(payload = undefined) 82 | { 83 | this.fault = ''; 84 | 85 | if (payload === undefined) return; 86 | 87 | this.$emit('change', payload); 88 | this.$emit('update:modelValue', payload); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /src/components/badge.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /src/support/vapor.js: -------------------------------------------------------------------------------- 1 | export default class Vapor 2 | { 3 | /** 4 | * Retrieve a signed storage link to upload with. 5 | * 6 | */ 7 | static async requestSignedUrl(file, options = {}) 8 | { 9 | return JSON.parse(await function() { 10 | return new Promise(function(resolve) { 11 | let request = new XMLHttpRequest(); 12 | 13 | request.open('POST', '/vapor/signed-storage-url'); 14 | request.onload = () => resolve(request.response); 15 | 16 | request.setRequestHeader('Content-Type', 'application/json'); 17 | request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 18 | request.setRequestHeader('X-CSRF-TOKEN', window.app.config.globalProperties.$page.props.csrf); 19 | 20 | request.send(JSON.stringify({ 21 | bucket : options.bucket || '', 22 | content_type : options.contentType || file.type, 23 | expires : options.expires || '', 24 | visibility : options.visibility || 'public-read', 25 | })); 26 | }); 27 | }()); 28 | } 29 | 30 | /** 31 | * Send the given file using the given signed storage url. 32 | * 33 | */ 34 | static async sendFile(url, file, options = {}, headers = {}) 35 | { 36 | return await function() { 37 | return new Promise(function(resolve) { 38 | let request = new XMLHttpRequest(); 39 | 40 | request.open('PUT', url); 41 | request.onload = () => resolve(request.response); 42 | 43 | if (options.progress) { 44 | request.upload.onprogress = (e) => options.progress( 45 | Math.ceil((e.loaded / e.total) * 100) 46 | ); 47 | } 48 | 49 | for (const header in headers) { 50 | request.setRequestHeader(header, headers[header]); 51 | } 52 | 53 | request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 54 | request.setRequestHeader('Content-Type', options.contentType || file.type); 55 | request.setRequestHeader('X-CSRF-TOKEN', window.app.config.globalProperties.$page.props.csrf); 56 | 57 | request.send(file); 58 | }); 59 | }(); 60 | } 61 | 62 | /** 63 | * Store a file in S3 and return its UUID, key, and other information. 64 | * 65 | */ 66 | static async store(file, options = {}) 67 | { 68 | let response = await this.requestSignedUrl(file, options); 69 | 70 | if ('Host' in response.headers) delete response.headers['Host']; 71 | if ('Content-Type' in response.headers) delete response.headers['Content-Type']; 72 | 73 | await this.sendFile(response.url, file, options, response.headers); 74 | 75 | if (file.name) response.extension = file.name.split('.').pop(); 76 | 77 | return response; 78 | } 79 | } -------------------------------------------------------------------------------- /src/components/cards.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | -------------------------------------------------------------------------------- /src/components/banner.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | -------------------------------------------------------------------------------- /src/components/error.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/confirm.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | -------------------------------------------------------------------------------- /src/components/notification.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | -------------------------------------------------------------------------------- /src/components/remaining.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /src/components/grid.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | -------------------------------------------------------------------------------- /src/components/prompt.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | -------------------------------------------------------------------------------- /src/components/modal.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/metric.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | -------------------------------------------------------------------------------- /src/framework/Dialog.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import TipComponent from '../components/tip.vue'; 3 | import ShareComponent from '../components/share.vue'; 4 | import PromptComponent from '../components/prompt.vue'; 5 | import ConfirmComponent from '../components/confirm.vue'; 6 | import NotificationComponent from '../components/notification.vue'; 7 | 8 | export default class Dialog 9 | { 10 | /** 11 | * Insert a blank container into the DOM. 12 | * 13 | */ 14 | static #createDialogElement() 15 | { 16 | let div = document.createElement('div'); 17 | 18 | div.id = `dialog-${parseInt(window.performance.now())}`; 19 | 20 | document.body.appendChild(div); 21 | 22 | return { container : null, id : div.id }; 23 | } 24 | 25 | /** 26 | * Remove the open dialog from the viewport. 27 | * 28 | */ 29 | static #closeDialog(element) 30 | { 31 | element.container._container._vnode.component.props.visible = false; 32 | 33 | setTimeout(() => { 34 | element.container.unmount(); 35 | 36 | element.container = undefined; 37 | 38 | document.body.removeChild(document.getElementById(element.id)); 39 | }, 300); 40 | } 41 | 42 | /** 43 | * Request that the user confirm a particular action. 44 | * 45 | */ 46 | static confirm(title = null, summary = null) 47 | { 48 | let element = Dialog.#createDialogElement(); 49 | 50 | return new Promise((resolve, reject) => 51 | { 52 | element.container = createApp(ConfirmComponent, { 53 | summary : summary ?? 'Note that in most cases, this action is not reversible. If you need some help, then please contact support.', 54 | title : title ?? 'Are you sure you wish to proceed?', 55 | visible : true, 56 | onCancel : () => { 57 | resolve(false); 58 | 59 | Dialog.#closeDialog(element); 60 | }, 61 | onContinue : () => { 62 | resolve(true); 63 | 64 | Dialog.#closeDialog(element); 65 | }, 66 | }); 67 | 68 | element.container.mount(`#${element.id}`); 69 | }); 70 | } 71 | 72 | /** 73 | * Advise the user that something has happened. 74 | * 75 | */ 76 | static notification(type, message) 77 | { 78 | let element = Dialog.#createDialogElement(); 79 | 80 | element.container = createApp(NotificationComponent, { 81 | message : message, 82 | type : type, 83 | }); 84 | 85 | element.container.mount(`#${element.id}`); 86 | 87 | setTimeout(() => Dialog.#closeDialog(element), 3500); 88 | } 89 | 90 | /** 91 | * Request that the user provide some feedback. 92 | * 93 | */ 94 | static prompt(title = null, summary = null, label = null, fallback = '', lines = 1, maxLength = null) 95 | { 96 | let element = Dialog.#createDialogElement(); 97 | 98 | return new Promise((resolve, reject) => 99 | { 100 | element.container = createApp(PromptComponent, { 101 | fallback : fallback, 102 | label : label ?? 'Your response', 103 | lines : lines, 104 | maxLength : maxLength, 105 | summary : summary ?? 'In order to proceed, some input is required. Please enter it below, then press continue, or press cancel.', 106 | title : title ?? 'Awaiting your response...', 107 | visible : true, 108 | onCancel : () => { 109 | resolve(fallback); 110 | 111 | Dialog.#closeDialog(element); 112 | }, 113 | onContinue : (event) => { 114 | resolve(['', null, undefined].includes(event) ? fallback : event); 115 | 116 | Dialog.#closeDialog(element); 117 | }, 118 | }); 119 | 120 | element.container.mount(`#${element.id}`); 121 | }); 122 | } 123 | 124 | /** 125 | * Allow the user to share the given link. 126 | * 127 | */ 128 | static share(url) 129 | { 130 | let element = Dialog.#createDialogElement(); 131 | 132 | element.container = createApp(ShareComponent, { 133 | url : url, 134 | visible : true, 135 | }); 136 | 137 | element.container.mount(`#${element.id}`); 138 | } 139 | 140 | /** 141 | * Display some information to the user. 142 | * 143 | */ 144 | static tip(message = '') 145 | { 146 | let element = Dialog.#createDialogElement(); 147 | 148 | element.container = createApp(TipComponent, { 149 | message : message, 150 | visible : true, 151 | onClose : () => Dialog.#closeDialog(element), 152 | }); 153 | 154 | element.container.mount(`#${element.id}`); 155 | } 156 | } -------------------------------------------------------------------------------- /src/components/dropdown.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | -------------------------------------------------------------------------------- /src/components/table.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | -------------------------------------------------------------------------------- /src/components/password.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | -------------------------------------------------------------------------------- /src/components/textbox.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | -------------------------------------------------------------------------------- /src/components/alert.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | -------------------------------------------------------------------------------- /src/mixins/Dialog.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import TipComponent from '../components/tip.vue'; 3 | import ShareComponent from '../components/share.vue'; 4 | import PromptComponent from '../components/prompt.vue'; 5 | import ConfirmComponent from '../components/confirm.vue'; 6 | import NotificationComponent from '../components/notification.vue'; 7 | 8 | export default 9 | { 10 | /** 11 | * Define the components. 12 | * 13 | */ 14 | components : { 15 | 'v-confirm' : ConfirmComponent, 16 | 'v-notification' : NotificationComponent, 17 | 'v-prompt' : PromptComponent, 18 | 'v-share' : ShareComponent, 19 | 'v-tip' : TipComponent, 20 | }, 21 | 22 | /** 23 | * Define the data model. 24 | * 25 | */ 26 | data() { 27 | return { 28 | dialogs : [], 29 | } 30 | }, 31 | 32 | /** 33 | * Define the supporting methods. 34 | * 35 | */ 36 | methods : 37 | { 38 | /** 39 | * Insert a blank container into the DOM. 40 | * 41 | */ 42 | createDialogElement() 43 | { 44 | let div = document.createElement('div'); 45 | 46 | div.id = `dialog-${parseInt(performance.now())}`; 47 | 48 | document.body.appendChild(div); 49 | 50 | this.dialogs.push({ container : null, id : div.id }) 51 | 52 | return this.dialogs.slice(-1)[0]; 53 | }, 54 | 55 | /** 56 | * Remove the open dialog from the viewport. 57 | * 58 | */ 59 | closeDialog(dialog) 60 | { 61 | dialog.container._container._vnode.component.props.visible = false; 62 | 63 | setTimeout(() => { 64 | dialog.container.unmount(); 65 | 66 | dialog.container = undefined; 67 | 68 | document.body.removeChild(document.getElementById(dialog.id)); 69 | }, 300); 70 | }, 71 | 72 | /** 73 | * Request that the user confirm a particular action. 74 | * 75 | */ 76 | confirm(title = null, summary = null) 77 | { 78 | let dialog = this.createDialogElement(); 79 | 80 | return new Promise((resolve, reject) => 81 | { 82 | dialog.container = createApp(ConfirmComponent, { 83 | summary : summary ?? 'Note that in most cases, this action is not reversible. If you need some help, then please contact support.', 84 | title : title ?? 'Are you sure you wish to proceed?', 85 | visible : true, 86 | onCancel : () => { 87 | resolve(false); 88 | 89 | this.closeDialog(dialog); 90 | }, 91 | onContinue : () => { 92 | resolve(true); 93 | 94 | this.closeDialog(dialog); 95 | }, 96 | }); 97 | 98 | dialog.container.mount(`#${dialog.id}`); 99 | }); 100 | }, 101 | 102 | /** 103 | * Advise the user that something has happened. 104 | * 105 | */ 106 | notify(type, message) 107 | { 108 | let dialog = this.createDialogElement(); 109 | 110 | dialog.container = createApp(NotificationComponent, { 111 | message : message, 112 | type : type, 113 | }); 114 | 115 | dialog.container.mount(`#${dialog.id}`); 116 | 117 | setTimeout(() => this.closeDialog(dialog), 3500); 118 | }, 119 | 120 | /** 121 | * Request that the user provide some feedback. 122 | * 123 | */ 124 | prompt(title = null, summary = null, label = null, fallback = '', lines = 1, maxLength = null) 125 | { 126 | let dialog = this.createDialogElement(); 127 | 128 | return new Promise((resolve, reject) => 129 | { 130 | dialog.container = createApp(PromptComponent, { 131 | fallback : fallback, 132 | label : label ?? 'Your response', 133 | lines : lines, 134 | maxLength : maxLength, 135 | summary : summary ?? 'In order to proceed, some input is required. Please enter it below, then press continue, or press cancel.', 136 | title : title ?? 'Awaiting your response...', 137 | visible : true, 138 | onCancel : () => { 139 | resolve(fallback); 140 | 141 | this.closeDialog(dialog); 142 | }, 143 | onContinue : (event) => { 144 | resolve(['', null, undefined].includes(event) ? fallback : event); 145 | 146 | this.closeDialog(dialog); 147 | }, 148 | }); 149 | 150 | dialog.container.mount(`#${dialog.id}`); 151 | }); 152 | }, 153 | 154 | /** 155 | * Allow the user to share the given link. 156 | * 157 | */ 158 | share(url) 159 | { 160 | let dialog = this.createDialogElement(); 161 | 162 | dialog.container = createApp(ShareComponent, { 163 | url : url, 164 | visible : true, 165 | }); 166 | 167 | dialog.container.mount(`#${dialog.id}`); 168 | }, 169 | 170 | /** 171 | * Display some information to the user. 172 | * 173 | */ 174 | tip(message = '') 175 | { 176 | let dialog = this.createDialogElement(); 177 | 178 | dialog.container = createApp(TipComponent, { 179 | message : message, 180 | visible : true, 181 | onClose : () => this.closeDialog(dialog), 182 | }); 183 | 184 | dialog.container.mount(`#${dialog.id}`); 185 | }, 186 | } 187 | } -------------------------------------------------------------------------------- /src/components/period.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | -------------------------------------------------------------------------------- /src/components/menu.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | -------------------------------------------------------------------------------- /src/components/tags.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 132 | 133 | -------------------------------------------------------------------------------- /src/components/search.vue: -------------------------------------------------------------------------------- 1 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/share.vue: -------------------------------------------------------------------------------- 1 | 118 | 119 | -------------------------------------------------------------------------------- /src/components/button.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 162 | 163 | -------------------------------------------------------------------------------- /src/components/paginator.vue: -------------------------------------------------------------------------------- 1 | 143 | 144 | -------------------------------------------------------------------------------- /src/components/upload.vue: -------------------------------------------------------------------------------- 1 | 104 | 105 | -------------------------------------------------------------------------------- /src/components/datetime.vue: -------------------------------------------------------------------------------- 1 | 258 | 259 | 605 | --------------------------------------------------------------------------------