├── .github └── FUNDING.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── app-extension ├── .npmignore ├── README.md ├── package.json └── src │ ├── boot │ └── register.js │ └── index.js └── ui ├── .npmignore ├── README.md ├── build ├── config.js ├── entry │ ├── index.common.js │ ├── index.esm.js │ └── index.umd.js ├── index.js ├── script.app-ext.js ├── script.clean.js ├── script.css.js ├── script.javascript.js ├── script.open-umd.js └── utils.js ├── dev ├── .editorconfig ├── .firebase │ └── hosting.ZGlzdC9zcGE.cache ├── .firebaserc ├── .gitignore ├── .postcssrc.js ├── README.md ├── babel.config.js ├── firebase.json ├── package-lock.json ├── package.json ├── quasar.conf.js ├── quasar.extensions.json └── src │ ├── App.vue │ ├── assets │ └── .gitkeep │ ├── boot │ ├── register.js │ └── vueComponents.js │ ├── components │ ├── .gitkeep │ ├── DialogWrapper.vue │ ├── InfoBoxWrapper.vue │ ├── MarkdownCode.vue │ ├── QDialogMethods.js │ ├── Template.vue │ └── showMoreWrapper.vue │ ├── css │ └── app.sass │ ├── helpers │ ├── conversion.js │ ├── parseCodeForPrint.js │ └── schemaBuilders.js │ ├── index.template.html │ ├── layouts │ └── MyLayout.vue │ ├── pages │ ├── EasyTableDemo.vue │ ├── Index.vue │ └── SlotsDemo.vue │ ├── router │ ├── index.js │ └── routes.js │ ├── schemas │ ├── examples │ │ ├── basics.js │ │ ├── computedFields.js │ │ ├── editing.js │ │ ├── editing2.js │ │ ├── index.js │ │ ├── selection.js │ │ └── slots.js │ └── pages │ │ ├── basics.js │ │ ├── computedFields.js │ │ ├── editing.js │ │ ├── index.js │ │ ├── selection.js │ │ └── slots.js │ └── statics │ ├── app-logo-128x128.png │ └── icons │ ├── apple-icon-120x120.png │ ├── apple-icon-152x152.png │ ├── apple-icon-167x167.png │ ├── apple-icon-180x180.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── icon-128x128.png │ ├── icon-192x192.png │ ├── icon-256x256.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── ms-icon-144x144.png │ └── safari-pinned-tab.svg ├── package-lock.json ├── package.json ├── src ├── components │ ├── EasyCell.vue │ ├── EasyRow.vue │ └── EasyTable.vue ├── helpers │ ├── dateHelpers.js │ ├── flattenPerSchema.js │ └── schemaToQTableColumns.js ├── index.js ├── index.sass ├── margin-padding.sass ├── meta │ ├── dependencyMap.js │ ├── lang.js │ └── quasarPropsJson.js └── mixins │ └── .gitkeep └── umd-test.html /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mesqueeb 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | node_modules 4 | dist 5 | yarn.lock 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "semi": false, 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Luca Ban - Mesqueeb 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Quasar Easy Tables 🪑 (ノಠ益ಠ)ノ彡┻━┻ 2 | 3 | New version: 4 | 5 | # Blitzar released! 🎉 6 | 7 | Quasar Easy Forms & Tables was upgraded to become much more powerful! 8 | 9 | The new version is called **Blitzar** 10 | 11 | - see blog post here: https://lucaban.medium.com/better-faster-vue-forms-with-blitzar-a0d71258a3bb 12 | - see documentation here: https://blitzar.cycraft.co 13 | - see the upgrade guide here: https://github.com/CyCraft/blitzar/releases/tag/v0.0.14 14 | 15 | # Support 16 | 17 | If this helped you in any way, you can contribute to the package's long term survival by supporting me: 18 | 19 | ### [💜 Support my open-source work on GitHub](https://github.com/sponsors/mesqueeb) 20 | 21 | Be sure to check out my sponsor page, I have a lot of open-source packages that might help you! 22 | 23 | (GitHub currently **doubles your support**! So if you support me with $10/mo, I will $20 instead! They're alchemists 🦾😉) 24 | 25 | Thank you so much!!! 26 | 27 | # License 28 | MIT (c) Luca Ban - Mesqueeb 29 | -------------------------------------------------------------------------------- /app-extension/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | yarn.lock 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | .editorconfig 16 | .eslintignore 17 | .eslintrc.js 18 | -------------------------------------------------------------------------------- /app-extension/README.md: -------------------------------------------------------------------------------- 1 | Quasar Easy Tables 🪑 (ノಠ益ಠ)ノ彡┻━┻ 2 | 3 | New version: 4 | 5 | # Blitzar released! 🎉 6 | 7 | Quasar Easy Forms & Tables was upgraded to become much more powerful! 8 | 9 | The new version is called **Blitzar** 10 | 11 | - see blog post here: https://lucaban.medium.com/better-faster-vue-forms-with-blitzar-a0d71258a3bb 12 | - see documentation here: https://blitzar.cycraft.co 13 | - see the upgrade guide here: https://github.com/CyCraft/blitzar/releases/tag/v0.0.14 14 | 15 | # Support 16 | 17 | If this helped you in any way, you can contribute to the package's long term survival by supporting me: 18 | 19 | ### [💜 Support my open-source work on GitHub](https://github.com/sponsors/mesqueeb) 20 | 21 | Be sure to check out my sponsor page, I have a lot of open-source packages that might help you! 22 | 23 | (GitHub currently **doubles your support**! So if you support me with $10/mo, I will $20 instead! They're alchemists 🦾😉) 24 | 25 | Thank you so much!!! 26 | 27 | # License 28 | MIT (c) Luca Ban - Mesqueeb 29 | -------------------------------------------------------------------------------- /app-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quasar-app-extension-easy-tables", 3 | "version": "0.8.6", 4 | "description": "Easily generate tables by only defining a \"schema\" object.", 5 | "author": "Luca Ban - Mesqueeb", 6 | "license": "MIT", 7 | "main": "src/index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/mesqueeb/quasar-ui-easy-tables" 11 | }, 12 | "bugs": "https://github.com/mesqueeb/quasar-ui-easy-tables/issues", 13 | "homepage": "https://quasar-easy-tables.web.app", 14 | "dependencies": { 15 | "quasar-ui-easy-tables": "^0.8.5" 16 | }, 17 | "engines": { 18 | "node": ">= 8.9.0", 19 | "npm": ">= 5.6.0", 20 | "yarn": ">= 1.6.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app-extension/src/boot/register.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VuePlugin from 'quasar-ui-easy-tables' 3 | 4 | Vue.use(VuePlugin) 5 | -------------------------------------------------------------------------------- /app-extension/src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Quasar App Extension index/runner script 3 | * (runs on each dev/build) 4 | * 5 | * Docs: https://quasar.dev/app-extensions/development-guide/index-api 6 | * API: https://github.com/quasarframework/quasar/blob/master/app/lib/app-extension/IndexAPI.js 7 | */ 8 | 9 | function extendConf (conf) { 10 | // register our boot file 11 | conf.boot.push('~quasar-app-extension-easy-tables/src/boot/register.js') 12 | 13 | // make sure app extension files & ui package gets transpiled 14 | conf.build.transpileDependencies.push(/quasar-app-extension-easy-tables[\\/]src/) 15 | } 16 | 17 | module.exports = function (api) { 18 | // Quasar compatibility check; you may need 19 | // hard dependencies, as in a minimum version of the "quasar" 20 | // package or a minimum version of "@quasar/app" CLI 21 | api.compatibleWith('quasar', '^1.1.1') 22 | api.compatibleWith('@quasar/app', '^1.1.0') 23 | 24 | // Uncomment the line below if you provide a JSON API for your component 25 | // api.registerDescribeApi('EasyTable', '~quasar-ui-easy-tables/src/components/EasyTable.json') 26 | 27 | // We extend /quasar.conf.js 28 | api.extendQuasarConf(extendConf) 29 | } 30 | -------------------------------------------------------------------------------- /ui/.npmignore: -------------------------------------------------------------------------------- 1 | /build 2 | /dev 3 | umd-test.html 4 | 5 | .DS_Store 6 | .thumbs.db 7 | yarn.lock 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Editor directories and files 13 | .idea 14 | .vscode 15 | *.suo 16 | *.ntvs* 17 | *.njsproj 18 | *.sln 19 | .editorconfig 20 | .eslintignore 21 | .eslintrc.js 22 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | Quasar Easy Tables 🪑 (ノಠ益ಠ)ノ彡┻━┻ 2 | 3 | New version: 4 | 5 | # Blitzar released! 🎉 6 | 7 | Quasar Easy Forms & Tables was upgraded to become much more powerful! 8 | 9 | The new version is called **Blitzar** 10 | 11 | - see blog post here: https://lucaban.medium.com/better-faster-vue-forms-with-blitzar-a0d71258a3bb 12 | - see documentation here: https://blitzar.cycraft.co 13 | - see the upgrade guide here: https://github.com/CyCraft/blitzar/releases/tag/v0.0.14 14 | 15 | # Support 16 | 17 | If this helped you in any way, you can contribute to the package's long term survival by supporting me: 18 | 19 | ### [💜 Support my open-source work on GitHub](https://github.com/sponsors/mesqueeb) 20 | 21 | Be sure to check out my sponsor page, I have a lot of open-source packages that might help you! 22 | 23 | (GitHub currently **doubles your support**! So if you support me with $10/mo, I will $20 instead! They're alchemists 🦾😉) 24 | 25 | Thank you so much!!! 26 | 27 | # License 28 | MIT (c) Luca Ban - Mesqueeb 29 | -------------------------------------------------------------------------------- /ui/build/config.js: -------------------------------------------------------------------------------- 1 | const { name, author, version } = require('../package.json') 2 | const year = (new Date()).getFullYear() 3 | 4 | module.exports = { 5 | name, 6 | version, 7 | banner: 8 | '/*!\n' + 9 | ' * ' + name + ' v' + version + '\n' + 10 | ' * (c) ' + year + ' ' + author + '\n' + 11 | ' * Released under the MIT License.\n' + 12 | ' */\n' 13 | } 14 | -------------------------------------------------------------------------------- /ui/build/entry/index.common.js: -------------------------------------------------------------------------------- 1 | import Plugin from '../../src/index' 2 | 3 | export default Plugin 4 | -------------------------------------------------------------------------------- /ui/build/entry/index.esm.js: -------------------------------------------------------------------------------- 1 | import Plugin from '../../src/index' 2 | 3 | export default Plugin 4 | export * from '../../src/index' 5 | -------------------------------------------------------------------------------- /ui/build/entry/index.umd.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Plugin from '../../src/index' 3 | 4 | Vue.use(Plugin) 5 | 6 | export * from '../../src/index' 7 | -------------------------------------------------------------------------------- /ui/build/index.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'production' 2 | 3 | const { join } = require('path') 4 | const { createFolder } = require('./utils') 5 | const { green, blue } = require('chalk') 6 | 7 | console.log() 8 | 9 | require('./script.app-ext.js').syncAppExt() 10 | require('./script.clean.js') 11 | 12 | console.log(` 📦 Building ${green('v' + require('../package.json').version)}...\n`) 13 | 14 | createFolder('dist') 15 | 16 | require(join(__dirname, './script.javascript.js')) 17 | -------------------------------------------------------------------------------- /ui/build/script.app-ext.js: -------------------------------------------------------------------------------- 1 | const 2 | fs = require('fs'), 3 | path = require('path'), 4 | root = path.resolve(__dirname, '../..'), 5 | resolvePath = file => path.resolve(root, file), 6 | { blue } = require('chalk') 7 | 8 | const writeJson = function (file, json) { 9 | return fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n', 'utf-8') 10 | } 11 | 12 | module.exports.syncAppExt = function (both = true) { 13 | // make sure this project has an app-extension project 14 | const appExtDir = resolvePath('app-extension') 15 | if (!fs.existsSync(appExtDir)) { 16 | return 17 | } 18 | 19 | // make sure this project has an ui project 20 | const uiDir = resolvePath('ui') 21 | if (!fs.existsSync(uiDir)) { 22 | return 23 | } 24 | 25 | // get version and name from ui package.json 26 | const { name, version } = require(resolvePath(resolvePath('ui/package.json'))) 27 | 28 | // read app-ext package.json 29 | const appExtFile = resolvePath('app-extension/package.json') 30 | let appExtJson = require(appExtFile), 31 | finished = false 32 | 33 | // sync version numbers 34 | if (both === true) { 35 | appExtJson.version = version 36 | } 37 | 38 | // check dependencies 39 | if (appExtJson.dependencies !== void 0) { 40 | if (appExtJson.dependencies[name] !== void 0) { 41 | appExtJson.dependencies[name] = '^' + version 42 | finished = true 43 | } 44 | } 45 | // check devDependencies, if not finished 46 | if (finished === false && appExtJson.devDependencies !== void 0) { 47 | if (appExtJson.devDependencies[name] !== void 0) { 48 | appExtJson.devDependencies[name] = '^' + version 49 | finished = true 50 | } 51 | } 52 | 53 | if (finished === true) { 54 | writeJson(appExtFile, appExtJson) 55 | console.log(` ⭐️ App Extension version ${blue(appExtJson.name)} synced.\n`) 56 | return 57 | } 58 | 59 | console.error(` App Extension version and dependency NOT synced.\n`) 60 | } -------------------------------------------------------------------------------- /ui/build/script.clean.js: -------------------------------------------------------------------------------- 1 | var 2 | rimraf = require('rimraf'), 3 | path = require('path') 4 | 5 | rimraf.sync(path.resolve(__dirname, '../dist/*')) 6 | console.log(` 💥 Cleaned build artifacts.\n`) 7 | -------------------------------------------------------------------------------- /ui/build/script.css.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const sass = require('node-sass') 3 | const postcss = require('postcss') 4 | const cssnano = require('cssnano') 5 | const rtl = require('postcss-rtl') 6 | const autoprefixer = require('autoprefixer') 7 | 8 | const buildConf = require('./config') 9 | const buildUtils = require('./utils') 10 | 11 | const postCssCompiler = postcss([ autoprefixer ]) 12 | const postCssRtlCompiler = postcss([ rtl({}) ]) 13 | 14 | const nano = postcss([ 15 | cssnano({ 16 | preset: ['default', { 17 | mergeLonghand: false, 18 | convertValues: false, 19 | cssDeclarationSorter: false, 20 | reduceTransforms: false 21 | }] 22 | }) 23 | ]) 24 | 25 | Promise 26 | .all([ 27 | generate('src/index.sass', `dist/index`) 28 | ]) 29 | .catch(e => { 30 | console.error(e) 31 | process.exit(1) 32 | }) 33 | 34 | /** 35 | * Helpers 36 | */ 37 | 38 | function resolve (_path) { 39 | return path.resolve(__dirname, '..', _path) 40 | } 41 | 42 | function generate (src, dest) { 43 | src = resolve(src) 44 | dest = resolve(dest) 45 | 46 | return new Promise((resolve, reject) => { 47 | sass.render({ file: src, includePaths: ['node_modules'] }, (err, result) => { 48 | if (err) { 49 | reject(err) 50 | return 51 | } 52 | 53 | resolve(result.css) 54 | }) 55 | }) 56 | .then(code => buildConf.banner + code) 57 | .then(code => postCssCompiler.process(code, { from: void 0 })) 58 | .then(code => { 59 | code.warnings().forEach(warn => { 60 | console.warn(warn.toString()) 61 | }) 62 | return code.css 63 | }) 64 | .then(code => Promise.all([ 65 | generateUMD(dest, code), 66 | postCssRtlCompiler.process(code, { from: void 0 }) 67 | .then(code => generateUMD(dest, code.css, '.rtl')) 68 | ])) 69 | } 70 | 71 | function generateUMD (dest, code, ext = '') { 72 | return buildUtils.writeFile(`${dest}${ext}.css`, code, true) 73 | .then(code => nano.process(code, { from: void 0 })) 74 | .then(code => buildUtils.writeFile(`${dest}${ext}.min.css`, code.css, true)) 75 | } 76 | -------------------------------------------------------------------------------- /ui/build/script.javascript.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const fse = require('fs-extra') 4 | const rollup = require('rollup') 5 | const uglify = require('uglify-es') 6 | const buble = require('@rollup/plugin-buble') 7 | const json = require('@rollup/plugin-json') 8 | const nodeResolve = require('@rollup/plugin-node-resolve') 9 | const VuePlugin = require('rollup-plugin-vue') 10 | const commonjs = require('@rollup/plugin-commonjs') 11 | 12 | const buildConf = require('./config') 13 | const buildUtils = require('./utils') 14 | 15 | const rollupPlugins = [ 16 | commonjs(), 17 | nodeResolve({ 18 | extensions: ['.js'], 19 | preferBuiltins: false, 20 | }), 21 | VuePlugin(), 22 | json(), 23 | buble({ 24 | objectAssign: 'Object.assign', 25 | }), 26 | ] 27 | 28 | const builds = [ 29 | { 30 | rollup: { 31 | input: { 32 | input: resolve(`entry/index.esm.js`), 33 | }, 34 | output: { 35 | file: resolve(`../dist/index.esm.js`), 36 | format: 'es', 37 | }, 38 | }, 39 | build: { 40 | unminified: true, 41 | // minified: true, 42 | }, 43 | }, 44 | { 45 | rollup: { 46 | input: { 47 | input: resolve(`entry/index.common.js`), 48 | }, 49 | output: { 50 | file: resolve(`../dist/index.common.js`), 51 | format: 'cjs', 52 | }, 53 | }, 54 | build: { 55 | unminified: true, 56 | // minified: true, 57 | }, 58 | }, 59 | { 60 | rollup: { 61 | input: { 62 | input: resolve(`entry/index.umd.js`), 63 | }, 64 | output: { 65 | name: 'easyTables', 66 | file: resolve(`../dist/index.umd.js`), 67 | format: 'umd', 68 | }, 69 | }, 70 | build: { 71 | unminified: true, 72 | minified: true, 73 | minExt: true, 74 | }, 75 | }, 76 | ] 77 | 78 | // Add your asset folders here 79 | // addAssets(builds, 'icon-set', 'iconSet') 80 | // addAssets(builds, 'lang', 'lang') 81 | 82 | build(builds) 83 | 84 | /** 85 | * Helpers 86 | */ 87 | 88 | function resolve (_path) { 89 | return path.resolve(__dirname, _path) 90 | } 91 | 92 | function addAssets (builds, type, injectName) { 93 | const files = fs.readdirSync(resolve('../../ui/src/components/' + type)), 94 | plugins = [buble(bubleConfig)], 95 | outputDir = resolve(`../dist/${type}`) 96 | 97 | fse.mkdirp(outputDir) 98 | 99 | files 100 | .filter(file => file.endsWith('.js')) 101 | .forEach(file => { 102 | const name = file.substr(0, file.length - 3).replace(/-([a-z])/g, g => g[1].toUpperCase()) 103 | builds.push({ 104 | rollup: { 105 | input: { 106 | input: resolve(`../src/components/${type}/${file}`), 107 | plugins, 108 | }, 109 | output: { 110 | file: addExtension(resolve(`../dist/${type}/${file}`), 'umd'), 111 | format: 'umd', 112 | name: `easyTables.${injectName}.${name}`, 113 | }, 114 | }, 115 | build: { 116 | minified: true, 117 | }, 118 | }) 119 | }) 120 | } 121 | 122 | function build (builds) { 123 | return Promise.all(builds.map(genConfig).map(buildEntry)).catch(buildUtils.logError) 124 | } 125 | 126 | function genConfig (opts) { 127 | Object.assign(opts.rollup.input, { 128 | plugins: rollupPlugins, 129 | external: ['vue', 'quasar', 'quasar-ui-easy-forms'], 130 | }) 131 | 132 | Object.assign(opts.rollup.output, { 133 | banner: buildConf.banner, 134 | globals: { vue: 'Vue', quasar: 'Quasar', 'quasar-ui-easy-forms': 'EasyForms' }, 135 | }) 136 | 137 | return opts 138 | } 139 | 140 | function addExtension (filename, ext = 'min') { 141 | const insertionPoint = filename.lastIndexOf('.') 142 | return `${filename.slice(0, insertionPoint)}.${ext}${filename.slice(insertionPoint)}` 143 | } 144 | 145 | function buildEntry (config) { 146 | return rollup 147 | .rollup(config.rollup.input) 148 | .then(bundle => bundle.generate(config.rollup.output)) 149 | .then(({ output }) => { 150 | const code = 151 | config.rollup.output.format === 'umd' 152 | ? injectVueRequirement(output[0].code) 153 | : output[0].code 154 | 155 | return config.build.unminified ? buildUtils.writeFile(config.rollup.output.file, code) : code 156 | }) 157 | .then(code => { 158 | if (!config.build.minified) { 159 | return code 160 | } 161 | 162 | const minified = uglify.minify(code, { 163 | compress: { 164 | pure_funcs: ['makeMap'], 165 | }, 166 | }) 167 | 168 | if (minified.error) { 169 | return Promise.reject(minified.error) 170 | } 171 | 172 | return buildUtils.writeFile( 173 | config.build.minExt === true 174 | ? addExtension(config.rollup.output.file) 175 | : config.rollup.output.file, 176 | buildConf.banner + minified.code, 177 | true 178 | ) 179 | }) 180 | .catch(err => { 181 | console.error(err) 182 | process.exit(1) 183 | }) 184 | } 185 | 186 | function injectVueRequirement (code) { 187 | const index = code.indexOf(`Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue`) 188 | 189 | if (index === -1) { 190 | return code 191 | } 192 | 193 | const checkMe = ` if (Vue === void 0) { 194 | console.error('[ Quasar ] Vue is required to run. Please add a script tag for it before loading Quasar.') 195 | return 196 | } 197 | ` 198 | 199 | return code.substring(0, index - 1) + checkMe + code.substring(index) 200 | } 201 | -------------------------------------------------------------------------------- /ui/build/script.open-umd.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | const open = require('open') 3 | 4 | open( 5 | resolve(__dirname, '../umd-test.html') 6 | ) 7 | -------------------------------------------------------------------------------- /ui/build/utils.js: -------------------------------------------------------------------------------- 1 | const 2 | fs = require('fs'), 3 | path = require('path'), 4 | zlib = require('zlib'), 5 | { green, blue, red, cyan } = require('chalk'), 6 | kebabRegex = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g 7 | 8 | function getSize (code) { 9 | return (code.length / 1024).toFixed(2) + 'kb' 10 | } 11 | 12 | module.exports.createFolder = function (folder) { 13 | const dir = path.join(__dirname, '..', folder) 14 | if (!fs.existsSync(dir)) { 15 | fs.mkdirSync(dir) 16 | } 17 | } 18 | 19 | module.exports.writeFile = function (dest, code, zip) { 20 | const banner = dest.indexOf('.json') > -1 21 | ? red('[json]') 22 | : dest.indexOf('.js') > -1 23 | ? green('[js] ') 24 | : dest.indexOf('.ts') > -1 25 | ? cyan('[ts] ') 26 | : blue('[css] ') 27 | 28 | return new Promise((resolve, reject) => { 29 | function report (extra) { 30 | console.log(`${banner} ${path.relative(process.cwd(), dest).padEnd(41)} ${getSize(code).padStart(8)}${extra || ''}`) 31 | resolve(code) 32 | } 33 | 34 | fs.writeFile(dest, code, err => { 35 | if (err) return reject(err) 36 | if (zip) { 37 | zlib.gzip(code, (err, zipped) => { 38 | if (err) return reject(err) 39 | report(` (gzipped: ${getSize(zipped).padStart(8)})`) 40 | }) 41 | } 42 | else { 43 | report() 44 | } 45 | }) 46 | }) 47 | } 48 | 49 | module.exports.readFile = function (file) { 50 | return fs.readFileSync(file, 'utf-8') 51 | } 52 | 53 | module.exports.logError = function (err) { 54 | console.error('\n' + red('[Error]'), err) 55 | console.log() 56 | } 57 | -------------------------------------------------------------------------------- /ui/dev/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /ui/dev/.firebase/hosting.ZGlzdC9zcGE.cache: -------------------------------------------------------------------------------- 1 | index.html,1590727263243,311618c8b3a9586de6bc09d414e68cc4b310f4ff4c9cfe12fb2039601ca0d53b 2 | css/4.e7b6832b.css,1590727263239,200b654d00881441b5795196795d761120a37c31fe9de48938931e47d2d295a8 3 | css/5.6f0819b1.css,1590727263241,998dd72ad2d6276a0f2721ab55d6b7ae532339a237406682dde88663de09a1ee 4 | css/6.0eb74e2f.css,1590727263242,9d4d8891f30b5b77abc0a4a65dd3757c0b984c879414ec6bda869999d23544b6 5 | css/chunk-common.e5ca51ca.css,1590727263237,c59e728bddee043a5585323eefcb0148533e24e0e2ad33638255ab25f536ee7d 6 | js/4.be7f69f1.js,1590727263241,e6dab5ad27d27c847d4c53e31830ecd1ad9fe4529e75ae38f5b9f2c147fa948c 7 | js/5.6a16ff69.js,1590727263241,672acc4040d243d9f8ee9de386dee531cc9621c00de3a660f7102925e40917f4 8 | js/6.8caa064d.js,1590727263242,ecfb1a90a76c4cd599bf46bd67e9e27bae43bbdfa18f0c591d28be75831c4d5b 9 | js/7.b4b11c5e.js,1590727263242,a4f0d9595a8189eb2b39521908e57e476687a8d887b154de9f1419962194d79a 10 | js/runtime.13d7c7fb.js,1590727263238,24a35c01489a3217478694f7d743186cb3235197d9c802758bfaa27a8a456d42 11 | statics/app-logo-128x128.png,1590727263241,3ba36b6fe48820fa565c588c7057d5b20de3777c1ac01299e7a9f090a558f175 12 | statics/icons/apple-icon-120x120.png,1590727263241,9e147671e0051a9fe6042d4a0797037f077100fcb121d4206f4543838ddf87eb 13 | js/app.fd3dd48d.js,1590727263238,5a6292207bf94643b33e74fedaee2cfa2804be26aa40cd7d14216506bbdd4ae1 14 | statics/icons/apple-icon-152x152.png,1590727263242,6dc20f0fb7da357654cb1a9312a74702e1e97ceb3056d67b5ad0a066e96c0ed8 15 | statics/icons/favicon-16x16.png,1590727263242,9a9519211345ba591ae9ecd63082d20cdec901bd22da4ff5fa1c0c0aa45fdbee 16 | statics/icons/favicon-32x32.png,1590727263242,6b423caf88b215fc7f52e76642141dd78522862cc989114233eb50c77deb16d8 17 | statics/icons/apple-icon-180x180.png,1590727263242,43f646dadecb2dbabbe53a3e888aa3c377594f843167dd54001fa3a34a4cabaa 18 | js/chunk-common.bfe2648e.js,1590727263239,a3b9bfdfb78d1d026f38fbd342a2369fc3ce1ff2862d442229f3b7f842a8d8a5 19 | statics/icons/favicon-96x96.png,1590727263242,aea2f51632b5e5a4d9bbb3fd010b438940bb568838f6cb186a4b9e33eb85e562 20 | statics/icons/icon-128x128.png,1590727263244,3ba36b6fe48820fa565c588c7057d5b20de3777c1ac01299e7a9f090a558f175 21 | statics/icons/icon-192x192.png,1590727263244,efe6efc75397aab84684918e7c45e24aadc2f8612789d6c91a6e7432d47724c2 22 | statics/icons/ms-icon-144x144.png,1590727263244,a1bee2aec520eea50583e3ace8461eb229b8bd5679c573cabd6c54a37b2286da 23 | statics/icons/safari-pinned-tab.svg,1590727263244,3356180d03fd5493efc6f27816d1d4e5f908ac300f5efb9f8044d9ef8823c4d1 24 | fonts/KFOkCnqEu92Fr1MmgVxIIzQ.a45108d3.woff,1590727263238,0cc82ea4e2383c3b0d0f46b32471cee5e920f87d2a151d302635cb2ca8cb6e43 25 | fonts/KFOlCnqEu92Fr1MmEU9fBBc-.cea99d3e.woff,1590727263238,42c7afdcff7aea098db26a417a9bb7929eed6df8823461b05cea5e6185216248 26 | fonts/KFOlCnqEu92Fr1MmSU5fBBc-.865f928c.woff,1590727263236,92689fd58bd8ba23da284d871afeb0e05fce981a9a28464b0acbad2051f6545a 27 | fonts/KFOlCnqEu92Fr1MmYUtfBBc-.bac8362e.woff,1590727263238,f225f153b48f4208f17a7fcc4595d2832b5b29916c357c8f7979ba64831b9ad0 28 | fonts/KFOlCnqEu92Fr1MmWUlfBBc-.2267169e.woff,1590727263238,de91d90d88d21edee1be62c920126d606e0beac61e6ee2243c417490b107279e 29 | statics/icons/favicon.ico,1590727263242,f4066f8f9d3212c5156547c3e2e2f607d1b17f1887f96d3a0bf59221e6eb519e 30 | fonts/KFOmCnqEu92Fr1Mu4mxM.49ae34d4.woff,1590727263238,68fb4aa339f56ddb03b15f12cae4f82cd06640dca3dcfe230b802593f36312e4 31 | statics/icons/apple-icon-167x167.png,1590727263243,89d93abc376213db7d1663adc8bd595d76daccd4e2890e29f19152e33d2ee4d8 32 | statics/icons/icon-256x256.png,1590727263244,fef8007201e545ef561f5fa50a12fa654c0fa9f03e9d998b6199727de8238c25 33 | statics/icons/icon-384x384.png,1590727263244,4e90fdacc7e781857ab93ece4b8e816b65ed69319f3abbe18367df2f47111049 34 | statics/icons/icon-512x512.png,1590727263244,83cb2337e81081fd5a948805b4fb550c68daa81346add2e8ea86f43d13f6ce9d 35 | fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.12a47ed5.woff2,1590727263238,a99652277b6479b04b3231319123ca269fe04f85cddac90b8dcbc142d4625d4d 36 | fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.f2a09334.woff,1590727263238,64fc5686c655301234b304dfb176d6ca1d6fd56f9434abf380b3549ce0058b69 37 | css/app.22b18900.css,1590727263238,5d890761d95f5c280ca49b8ed79577ec691de04777cb78373278aacff5fdb4e3 38 | js/vendor.61eed14f.js,1590727263237,490a6535de6f513e5bfa20b7794efdc9c7920b95fec0d3710eddc19ac031a7f0 39 | -------------------------------------------------------------------------------- /ui/dev/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "quasar-easy-tables" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /ui/dev/.gitignore: -------------------------------------------------------------------------------- 1 | .quasar 2 | .DS_Store 3 | .thumbs.db 4 | node_modules 5 | /dist 6 | /src-cordova/node_modules 7 | /src-cordova/platforms 8 | /src-cordova/plugins 9 | /src-cordova/www 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | -------------------------------------------------------------------------------- /ui/dev/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /ui/dev/README.md: -------------------------------------------------------------------------------- 1 | # Dev app (playground) 2 | 3 | Adding .vue files to src/pages/ will auto-add them to the Index page list. 4 | -------------------------------------------------------------------------------- /ui/dev/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@quasar/babel-preset-app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /ui/dev/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist/spa", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ui/dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev", 3 | "version": "1.0.0", 4 | "description": "A Quasar Framework app", 5 | "productName": "Quasar App", 6 | "cordovaId": "org.cordova.quasar.app", 7 | "private": true, 8 | "scripts": { 9 | "build": "quasar build", 10 | "deploy": "quasar build && firebase deploy", 11 | "dev": "quasar dev", 12 | "dev:ssr": "quasar dev -m ssr", 13 | "dev:ios": "quasar dev -m ios", 14 | "dev:android": "quasar dev -m android", 15 | "dev:electron": "quasar dev -m electron" 16 | }, 17 | "dependencies": { 18 | "@quasar/extras": "^1.8.1", 19 | "case-anything": "^0.3.1", 20 | "copy-anything": "^1.6.0", 21 | "filter-anything": "^1.1.5", 22 | "is-what": "^3.11.3", 23 | "merge-anything": "^2.4.4", 24 | "quasar-ui-component-info-card": "0.0.7", 25 | "quasar-ui-easy-forms": "^1.2.7", 26 | "raw-loader": "^4.0.1", 27 | "sort-anything": "^0.1.0" 28 | }, 29 | "devDependencies": { 30 | "@quasar/app": "^1.9.1", 31 | "@quasar/quasar-app-extension-qmarkdown": "^1.0.27" 32 | }, 33 | "engines": { 34 | "node": ">= 8.9.0", 35 | "npm": ">= 5.6.0", 36 | "yarn": ">= 1.6.0" 37 | }, 38 | "browserslist": [ 39 | "last 1 version, not dead, ie >= 11" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /ui/dev/quasar.conf.js: -------------------------------------------------------------------------------- 1 | // Configuration for your app 2 | // https://quasar.dev/quasar-cli/quasar-conf-js 3 | 4 | const path = require('path') 5 | 6 | module.exports = function (ctx) { 7 | return { 8 | // app boot file (/src/boot) 9 | // --> boot files are part of "main.js" 10 | boot: ['vueComponents.js', 'register.js'], 11 | 12 | css: [], 13 | 14 | extras: [ 15 | // 'ionicons-v4', 16 | // 'mdi-v4', 17 | // 'fontawesome-v5', 18 | // 'eva-icons', 19 | // 'themify', 20 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 21 | 22 | 'roboto-font', // optional, you are not bound to it 23 | 'material-icons', // optional, you are not bound to it 24 | ], 25 | 26 | framework: { 27 | // iconSet: 'ionicons-v4', // Quasar icon set 28 | // lang: 'de', // Quasar language pack 29 | 30 | // Possible values for "all": 31 | // * 'auto' - Auto-import needed Quasar components & directives 32 | // (slightly higher compile time; next to minimum bundle size; most convenient) 33 | // * false - Manually specify what to import 34 | // (fastest compile time; minimum bundle size; most tedious) 35 | // * true - Import everything from Quasar 36 | // (not treeshaking Quasar; biggest bundle size; convenient) 37 | all: 'auto', 38 | 39 | components: ['QInput', 'QImg', 'QToggle', 'QSelect', 'QBtnToggle', 'QIcon'], 40 | directives: [], 41 | 42 | // Quasar plugins 43 | plugins: ['Dialog'], 44 | }, 45 | 46 | supportIE: false, 47 | 48 | // animations: 'all', // --- includes all animations 49 | animations: [], 50 | 51 | build: { 52 | vueRouterMode: 'history', 53 | 54 | chainWebpack (chain) { 55 | chain.resolve.alias.merge({ 56 | ui: path.resolve(__dirname, '../src/index.js'), 57 | }) 58 | }, 59 | }, 60 | 61 | devServer: { 62 | // port: 8080, 63 | open: true, // opens browser window automatically 64 | }, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ui/dev/quasar.extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "@quasar/qmarkdown": { 3 | "import_md": false, 4 | "import_vmd": false 5 | } 6 | } -------------------------------------------------------------------------------- /ui/dev/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /ui/dev/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/assets/.gitkeep -------------------------------------------------------------------------------- /ui/dev/src/boot/register.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import EasyForms from 'quasar-ui-easy-forms' 3 | import VuePlugin from 'ui' // "ui" is aliased in quasar.conf.js 4 | 5 | Vue.use(EasyForms) 6 | Vue.use(VuePlugin) 7 | -------------------------------------------------------------------------------- /ui/dev/src/boot/vueComponents.js: -------------------------------------------------------------------------------- 1 | import { InfoCard } from 'quasar-ui-component-info-card' 2 | import InfoBoxWrapper from '../components/InfoBoxWrapper' 3 | 4 | export default ({ Vue }) => { 5 | Vue.component(InfoCard.name, InfoCard) 6 | Vue.component(InfoBoxWrapper.name, InfoBoxWrapper) 7 | } 8 | -------------------------------------------------------------------------------- /ui/dev/src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/components/.gitkeep -------------------------------------------------------------------------------- /ui/dev/src/components/DialogWrapper.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 30 | 31 | 77 | -------------------------------------------------------------------------------- /ui/dev/src/components/InfoBoxWrapper.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | 31 | 52 | -------------------------------------------------------------------------------- /ui/dev/src/components/MarkdownCode.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 50 | -------------------------------------------------------------------------------- /ui/dev/src/components/QDialogMethods.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | methods: { 4 | // following method is REQUIRED 5 | // (don't change its name --> "show") 6 | show () { 7 | if (!this.$refs.dialog) return 8 | this.$refs.dialog.show() 9 | }, 10 | 11 | // following method is REQUIRED 12 | // (don't change its name --> "hide") 13 | hide () { 14 | if (!this.$refs.dialog) return 15 | this.$refs.dialog.hide() 16 | }, 17 | 18 | onDialogHide (val) { 19 | if (val !== undefined) { 20 | return this.Ok(val) 21 | } 22 | // required to be emitted 23 | // when QDialog emits "hide" event 24 | this.$emit('hide') 25 | }, 26 | 27 | Ok (val) { 28 | // on Ok, it is REQUIRED to 29 | // emit "ok" event (with optional payload) 30 | // before hiding the QDialog 31 | this.$emit('ok', val) 32 | // or with payload: this.$emit('ok', { ... }) 33 | 34 | // then hiding dialog 35 | this.hide() 36 | }, 37 | 38 | onCancelClick () { 39 | // we just need to hide dialog 40 | this.hide() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ui/dev/src/components/Template.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /ui/dev/src/components/showMoreWrapper.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 63 | 64 | 94 | -------------------------------------------------------------------------------- /ui/dev/src/css/app.sass: -------------------------------------------------------------------------------- 1 | // app global css in Sass form 2 | .text-wrap-all 3 | white-space: pre-line 4 | word-break: break-word 5 | -------------------------------------------------------------------------------- /ui/dev/src/helpers/conversion.js: -------------------------------------------------------------------------------- 1 | import { isFullString } from 'is-what' 2 | 3 | export function stringToJs (string) { 4 | if (!isFullString(string)) return undefined 5 | const jsonString = string.replace(/([a-zA-Z0-9]+?):/g, '"$1":').replace(/'/g, '"') 6 | let parsed 7 | try { 8 | parsed = JSON.parse(jsonString) 9 | } catch (e) { 10 | return string 11 | } 12 | return parsed 13 | } 14 | -------------------------------------------------------------------------------- /ui/dev/src/helpers/parseCodeForPrint.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'is-what' 2 | 3 | export default function parseCodeForPrint (code) { 4 | // return early on 0, undefined, null, etc. 5 | if (!code) return code 6 | const stringifiedFns = [] 7 | function replacer (key, value) { 8 | if (isFunction(value) && value.prototype.stringifiedFn) { 9 | const fnString = value.prototype.stringifiedFn 10 | stringifiedFns.push(fnString) 11 | return fnString 12 | } 13 | return value 14 | } 15 | const string = JSON.stringify(code, replacer, 2) 16 | const cleanedString = string.replace(/'/g, `\\'`).replace(/"/g, `'`) 17 | const parsedString = stringifiedFns.reduce((str, fnString) => { 18 | const fnStringRegex = `'${fnString.replace(/'/g, `\\'`).replace(/"/g, `'`)}'` 19 | return str.replace(fnStringRegex, fnString) 20 | }, cleanedString) 21 | return parsedString 22 | } 23 | -------------------------------------------------------------------------------- /ui/dev/src/helpers/schemaBuilders.js: -------------------------------------------------------------------------------- 1 | import { isArray, isUndefined, isFunction } from 'is-what' 2 | import copy from 'copy-anything' 3 | import merge from 'merge-anything' 4 | import { stringToJs } from './conversion' 5 | 6 | export function propToPropSchema (propKey, propInfo) { 7 | const { desc, type, inheritedProp, examples, default: df, values, category } = propInfo 8 | // make the raw prop info from the components into an EasyForm: 9 | // whatever the prop is, default to an 'input' EasyField 10 | const events = {} 11 | let component = 'QInput' 12 | let subLabel = desc 13 | let options, 14 | outlined, 15 | standout, 16 | disable, 17 | parseInput, 18 | parseValue, 19 | autogrow, 20 | debounce, 21 | span, 22 | emitValue 23 | let fieldClasses = [] 24 | let _default = df === true || undefined 25 | // If it has a default, write it in the description 26 | if (!isUndefined(df)) subLabel += `\n\nDefault: \`${isFunction(df) ? JSON.stringify(df()) : df}\`` 27 | // if the prop is a Boolean, show this as a 'toggle' EasyField 28 | if (type === Boolean || (isArray(type) && type.includes(Boolean))) { 29 | component = 'QToggle' 30 | _default = df === true 31 | } 32 | // if the prop has a fixed set of possible values, show this as an 'option' EasyField 33 | const propHasValues = isArray(values) && values.length 34 | if (propHasValues) { 35 | component = 'QSelect' 36 | emitValue = true 37 | options = values.map(v => ({ label: v, value: v })) 38 | } 39 | // Create a special input for defining arrays and/or objects 40 | if ( 41 | type === Array || 42 | type === Object || 43 | type === Function || 44 | (isArray(type) && [Array, Object].some(t => type.includes(t)) && type.length === 2) 45 | ) { 46 | // events.blur = (e, val) => console.log(stringToJs(val)) 47 | outlined = false 48 | standout = true 49 | debounce = 500 50 | parseInput = stringToJs 51 | parseValue = JSON.stringify 52 | autogrow = true 53 | if (isArray(examples)) subLabel += `\nExamples: \`${examples.join('` | `')}\`` 54 | } 55 | // Don't allow editing props that accept functions. 56 | if (type === Function) disable = true 57 | // If it's the prop called 'schema', span the entire form, add extra info and don't return any input field 58 | if (propKey === 'schema') { 59 | component = '' 60 | span = true 61 | subLabel += 62 | '\n\n> 👀 Check「Source tab」→「Schema」to see the following code in color and with indentation.' 63 | } 64 | // Create the EasyField schema for the prop 65 | return { 66 | id: propKey, 67 | component, 68 | type: type === Number ? 'number' : undefined, 69 | // schema, 70 | label: propKey, 71 | subLabel, 72 | placeholder: !isArray(examples) ? '' : examples.join(', '), 73 | inheritedProp, 74 | options, 75 | outlined, 76 | standout, 77 | disable, 78 | parseInput, 79 | parseValue, 80 | autogrow, 81 | category, 82 | fieldClasses, 83 | debounce, 84 | events, 85 | span, 86 | emitValue, 87 | // if the prop is `true` by default, set to true 88 | default: _default, 89 | // defaults 90 | hasMarkdown: true, 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ui/dev/src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= htmlWebpackPlugin.options.productName %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /ui/dev/src/layouts/MyLayout.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /ui/dev/src/pages/EasyTableDemo.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 37 | 38 | 102 | -------------------------------------------------------------------------------- /ui/dev/src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 95 | 96 | 102 | -------------------------------------------------------------------------------- /ui/dev/src/pages/SlotsDemo.vue: -------------------------------------------------------------------------------- 1 | 100 | 101 | 117 | 118 | 184 | -------------------------------------------------------------------------------- /ui/dev/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import routes from './routes' 5 | 6 | Vue.use(VueRouter) 7 | 8 | /* 9 | * If not building with SSR mode, you can 10 | * directly export the Router instantiation 11 | */ 12 | 13 | export default function (/* { store, ssrContext } */) { 14 | const Router = new VueRouter({ 15 | scrollBehavior: () => ({ x: 0, y: 0 }), 16 | routes, 17 | 18 | // Leave these as is and change from quasar.conf.js instead! 19 | // quasar.conf.js -> build -> vueRouterMode 20 | // quasar.conf.js -> build -> publicPath 21 | mode: process.env.VUE_ROUTER_MODE, 22 | base: process.env.VUE_ROUTER_BASE 23 | }) 24 | 25 | // we get each page from server first! 26 | if (process.env.MODE === 'ssr' && process.env.CLIENT) { 27 | console.log('!!!!') 28 | console.log('On route change we deliberately load page from server -- in order to test hydration errors') 29 | console.log('!!!!') 30 | 31 | let reload = false 32 | Router.beforeEach((to, _, next) => { 33 | if (reload) { 34 | window.location.href = to.fullPath 35 | return 36 | } 37 | reload = true 38 | next() 39 | }) 40 | } 41 | 42 | return Router 43 | } 44 | -------------------------------------------------------------------------------- /ui/dev/src/router/routes.js: -------------------------------------------------------------------------------- 1 | const routes = [ 2 | { 3 | path: '/', 4 | component: () => import('layouts/MyLayout.vue'), 5 | children: [ 6 | { path: '', component: () => import('pages/Index.vue') }, 7 | { path: '/slots', component: () => import('pages/SlotsDemo.vue') }, 8 | { path: '/:schemaId', component: () => import('pages/EasyTableDemo.vue'), props: true }, 9 | ], 10 | }, 11 | ] 12 | 13 | export default routes 14 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/basics.js: -------------------------------------------------------------------------------- 1 | const rows = [ 2 | { 3 | title: 'Mathematica', 4 | topic: 'curriculum', 5 | subject: 'We will look at the curriculum of the Mathematica √/%^×-+÷', 6 | img: 7 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 8 | grade: 90000, 9 | passing: true, 10 | created: new Date(), 11 | classes: [{ label: 'One', value: '1' }], 12 | }, 13 | { 14 | title: 'Films', 15 | topic: 'split', 16 | subject: 'We will look at the split of the Films √/%^×-+÷', 17 | img: 18 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 19 | grade: 80000, 20 | passing: false, 21 | created: new Date('2020/01/01'), 22 | classes: [{ label: 'One', value: '1' }], 23 | }, 24 | { 25 | title: 'Winds', 26 | topic: 'north', 27 | subject: 'We will look at the north of the Winds √/%^×-+÷', 28 | img: 29 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 30 | grade: 120000, 31 | passing: false, 32 | created: new Date(), 33 | classes: [{ label: 'Two', value: '2' }], 34 | }, 35 | { 36 | title: 'Apps', 37 | topic: 'hotdog', 38 | subject: 'We will look at the hotdog of the Apps √/%^×-+÷', 39 | img: 40 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 41 | grade: 25, 42 | passing: true, 43 | created: new Date('1990-01-01'), 44 | classes: [ 45 | { label: 'One', value: '1' }, 46 | { label: 'Two', value: '2' }, 47 | ], 48 | }, 49 | { 50 | title: 'Computers', 51 | topic: 'hardware', 52 | subject: 'We will look at the hardware of the Computers √/%^×-+÷', 53 | img: 54 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 55 | grade: 0, 56 | passing: false, 57 | created: new Date(), 58 | classes: [{ label: 'One', value: '1' }], 59 | }, 60 | ] 61 | 62 | export default { 63 | title: 'My Lessons', 64 | rows, 65 | schemaColumns: [ 66 | { 67 | id: 'title', 68 | label: 'Lesson Title', 69 | component: 'QInput', 70 | }, 71 | { 72 | id: 'topic', 73 | label: 'Topic', 74 | component: 'QInput', 75 | }, 76 | { 77 | id: 'subject', 78 | label: 'Subject', 79 | component: 'QInput', 80 | }, 81 | { 82 | id: 'img', 83 | label: 'Image', 84 | component: 'QImg', 85 | mode: 'view', 86 | evaluatedProps: ['src'], 87 | internalErrors: true, 88 | // component props: 89 | src: val => val, 90 | }, 91 | { 92 | id: 'grade', 93 | label: 'Grade', 94 | component: 'QInput', 95 | // component props: 96 | type: 'number', 97 | }, 98 | { 99 | id: 'passing', 100 | label: 'Passing', 101 | component: 'QToggle', 102 | default: false, 103 | }, 104 | { 105 | id: 'created', 106 | label: 'Created at', 107 | component: 'QInput', 108 | parseInput: val => new Date(val), 109 | dateFormat: 'YYYY/MM/DD', 110 | valueType: 'date', 111 | // component props: 112 | mask: '####/##/##', 113 | placeholder: 'YYYY/MM/DD', 114 | }, 115 | { 116 | id: 'classes', 117 | label: 'Classes', 118 | component: 'QSelect', 119 | // component props: 120 | multiple: true, 121 | options: [ 122 | { label: 'One', value: '1' }, 123 | { label: 'Two', value: '2' }, 124 | { label: 'Three', value: '3' }, 125 | ], 126 | }, 127 | ], 128 | get schemaGrid () { 129 | return this.schemaColumns 130 | }, 131 | } 132 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/computedFields.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'is-what' 2 | 3 | const rows = [ 4 | { firstName: 'Taylor', lastName: 'Roach' }, 5 | { firstName: 'Corey', lastName: 'Carson' }, 6 | { firstName: 'Alden', lastName: 'Cantrell' }, 7 | { firstName: 'Ezequiel', lastName: 'Davenport' }, 8 | { firstName: 'Kendrick', lastName: 'Schultz' }, 9 | { firstName: 'Bradyn', lastName: 'Arroyo' }, 10 | { firstName: 'Kade', lastName: 'Craig' }, 11 | { firstName: 'Carissa', lastName: 'Atkinson' }, 12 | { firstName: 'Zaire', lastName: 'Lambert' }, 13 | { firstName: 'Alec', lastName: 'Villanueva' }, 14 | { firstName: 'Sheldon', lastName: 'Rogers' }, 15 | { firstName: 'Celeste', lastName: 'Trujillo' }, 16 | ] 17 | 18 | export default { 19 | title: 'My Lessons', 20 | rows, 21 | schemaColumns: [ 22 | { 23 | id: 'fullName', 24 | label: 'Full name', 25 | component: 'QInput', 26 | parseValue: (val, { formData, fieldInput }) => { 27 | const value = `${formData.firstName || ''} ${formData.lastName || ''}`.trim() 28 | fieldInput({ id: 'fullName', value }) 29 | return value 30 | }, 31 | }, 32 | ], 33 | get schemaGrid () { 34 | return this.schemaColumns 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/editing.js: -------------------------------------------------------------------------------- 1 | import { Dialog } from 'quasar' 2 | import DialogWrapper from '../../components/DialogWrapper.vue' 3 | // For more info on this DialogWrapper see: 4 | // https://github.com/mesqueeb/quasar-ui-easy-tables/blob/master/ui/dev/src/components/DialogWrapper.vue 5 | 6 | const schemaColumns = [ 7 | { 8 | id: 'name', 9 | label: 'Name', 10 | component: 'QInput', 11 | }, 12 | { 13 | id: 'img', 14 | label: 'Profile picture', 15 | component: 'QImg', 16 | mode: 'view', 17 | evaluatedProps: ['src'], 18 | internalErrors: true, 19 | // component props: 20 | src: val => val, 21 | }, 22 | { 23 | id: 'level', 24 | label: 'Level', 25 | component: 'QInput', 26 | // component props: 27 | type: 'number', 28 | }, 29 | { 30 | id: 'darkSide', 31 | label: 'Dark side', 32 | component: 'QToggle', 33 | default: false, 34 | }, 35 | { 36 | id: 'birthdate', 37 | label: 'Created at', 38 | component: 'QInput', 39 | parseInput: val => new Date(val), 40 | valueType: 'date', 41 | // component props: 42 | mask: '####/##/##', 43 | placeholder: 'YYYY/MM/DD', 44 | dateFormat: 'YYYY/MM/DD', 45 | }, 46 | { 47 | id: 'trialsCompleted', 48 | label: 'Trials completed', 49 | component: 'QSelect', 50 | // component props: 51 | multiple: true, 52 | options: [ 53 | { label: 'One', value: '1' }, 54 | { label: 'Two', value: '2' }, 55 | { label: 'Three', value: '3' }, 56 | ], 57 | }, 58 | ] 59 | const rows = [ 60 | { 61 | id: 'abc123', 62 | name: 'Luke', 63 | img: 64 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 65 | level: 90000, 66 | darkSide: true, 67 | birthdate: new Date(), 68 | trialsCompleted: [{ label: 'One', value: '1' }], 69 | }, 70 | { 71 | id: 'def456', 72 | name: 'Leia', 73 | img: 74 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 75 | level: 80000, 76 | darkSide: false, 77 | birthdate: new Date('1990-01-01'), 78 | trialsCompleted: [{ label: 'Two', value: '2' }], 79 | }, 80 | ] 81 | 82 | export default { 83 | rows, 84 | schemaColumns, 85 | schemaGrid: schemaColumns, 86 | events: { 87 | 'row-click': (event, rowData) => { 88 | Dialog.create({ 89 | // tell Quasar's Dialog plugin to use DialogWrapper.vue 90 | component: DialogWrapper, 91 | parent: this, 92 | // tell DialogWrapper.vue to use an EasyForm 93 | slotComponent: 'EasyForm', 94 | // props bound to EasyForm via v-bind="slotProps" 95 | slotProps: { 96 | actionButtons: ['edit', 'cancel', 'save'], 97 | value: rowData, 98 | schema: schemaColumns, 99 | class: 'q-pa-lg', 100 | mode: 'edit', 101 | }, 102 | // events bound to EasyForm via v-on="slotEvents" 103 | slotEvents: ({ hide }) => ({ 104 | cancel: hide, 105 | save: ({ newData }) => { 106 | const { id: rowId } = rowData 107 | const rowToUpdate = rows.find(r => r.id === rowId) 108 | Object.entries(newData).forEach(([fieldId, value]) => { 109 | rowToUpdate[fieldId] = value 110 | }) 111 | hide() 112 | }, 113 | }), 114 | }) 115 | }, 116 | }, 117 | } 118 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/editing2.js: -------------------------------------------------------------------------------- 1 | import { Dialog } from 'quasar' 2 | import DialogWrapper from '../../components/DialogWrapper.vue' 3 | // For more info on this DialogWrapper see: 4 | // https://github.com/mesqueeb/quasar-ui-easy-tables/blob/master/ui/dev/src/components/DialogWrapper.vue 5 | 6 | const schemaColumns = [ 7 | { 8 | id: 'name', 9 | label: 'Name', 10 | component: 'QInput', 11 | }, 12 | { 13 | id: 'img', 14 | label: 'Profile picture', 15 | component: 'QImg', 16 | mode: 'view', 17 | evaluatedProps: ['src'], 18 | internalErrors: true, 19 | // component props: 20 | src: val => val, 21 | }, 22 | { 23 | id: 'level', 24 | label: 'Level', 25 | component: 'QInput', 26 | // component props: 27 | type: 'number', 28 | }, 29 | { 30 | id: 'darkSide', 31 | label: 'Dark side', 32 | component: 'QToggle', 33 | default: false, 34 | }, 35 | { 36 | id: 'birthdate', 37 | label: 'Created at', 38 | component: 'QInput', 39 | parseInput: val => new Date(val), 40 | valueType: 'date', 41 | // component props: 42 | mask: '####/##/##', 43 | placeholder: 'YYYY/MM/DD', 44 | dateFormat: 'YYYY/MM/DD', 45 | }, 46 | { 47 | id: 'trialsCompleted', 48 | label: 'Trials completed', 49 | component: 'QSelect', 50 | // component props: 51 | multiple: true, 52 | options: [ 53 | { label: 'One', value: '1' }, 54 | { label: 'Two', value: '2' }, 55 | { label: 'Three', value: '3' }, 56 | ], 57 | }, 58 | ] 59 | const rows = [ 60 | { 61 | id: 'abc123', 62 | name: 'Luke', 63 | img: 64 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 65 | level: 90000, 66 | darkSide: true, 67 | birthdate: new Date(), 68 | trialsCompleted: [{ label: 'One', value: '1' }], 69 | }, 70 | { 71 | id: 'def456', 72 | name: 'Leia', 73 | img: 74 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 75 | level: 80000, 76 | darkSide: false, 77 | birthdate: new Date('1990-01-01'), 78 | trialsCompleted: [{ label: 'Two', value: '2' }], 79 | }, 80 | ] 81 | 82 | export default { 83 | rows, 84 | schemaColumns: [ 85 | { 86 | id: 'edit-btn', 87 | component: 'EfBtn', 88 | btnLabel: 'Edit', 89 | mode: 'edit', 90 | unelevated: true, 91 | size: 'sm', 92 | dense: true, 93 | hideBottomSpace: true, 94 | events: { 95 | click: (event, { formData }) => { 96 | Dialog.create({ 97 | // tell Quasar's Dialog plugin to use DialogWrapper.vue 98 | component: DialogWrapper, 99 | parent: this, 100 | // tell DialogWrapper.vue to use an EasyForm 101 | slotComponent: 'EasyForm', 102 | // props bound to EasyForm via v-bind="slotProps" 103 | slotProps: { 104 | actionButtons: ['edit', 'cancel', 'save'], 105 | value: formData, 106 | schema: schemaColumns, 107 | class: 'q-pa-lg', 108 | mode: 'edit', 109 | }, 110 | // events bound to EasyForm via v-on="slotEvents" 111 | slotEvents: ({ hide }) => ({ 112 | cancel: hide, 113 | save: ({ newData }) => { 114 | const { id: rowId } = formData 115 | const rowToUpdate = rows.find(r => r.id === rowId) 116 | Object.entries(newData).forEach(([fieldId, value]) => { 117 | rowToUpdate[fieldId] = value 118 | }) 119 | hide() 120 | }, 121 | }), 122 | }) 123 | }, 124 | }, 125 | }, 126 | ].concat(schemaColumns), 127 | } 128 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/index.js: -------------------------------------------------------------------------------- 1 | export const basics = { 2 | code: [require('./basics').default], 3 | string: [require('!!raw-loader!./basics').default], 4 | } 5 | export const editing = { 6 | code: [require('./editing').default, require('./editing2').default], 7 | string: [require('!!raw-loader!./editing').default, require('!!raw-loader!./editing2').default], 8 | } 9 | export const slots = { 10 | code: [require('./slots').default], 11 | string: [require('!!raw-loader!./slots').default], 12 | } 13 | export const computedFields = { 14 | code: [require('./computedFields').default], 15 | string: [require('!!raw-loader!./computedFields').default], 16 | } 17 | export const selection = { 18 | code: [require('./selection').default], 19 | string: [require('!!raw-loader!./selection').default], 20 | } 21 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/selection.js: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'is-what' 2 | 3 | const rows = [ 4 | { fullName: 'Taylor Roach', id: 'taylor-roach' }, 5 | { fullName: 'Corey Carson', id: 'corey-carson' }, 6 | { fullName: 'Alden Cantrell', id: 'alden-cantrell' }, 7 | { fullName: 'Ezequiel Davenport', id: 'ezequiel-davenport' }, 8 | { fullName: 'Kendrick Schultz', id: 'kendrick-schultz' }, 9 | { fullName: 'Bradyn Arroyo', id: 'bradyn-arroyo' }, 10 | { fullName: 'Kade Craig', id: 'kade-craig' }, 11 | { fullName: 'Carissa Atkinson', id: 'carissa-atkinson' }, 12 | { fullName: 'Zaire Lambert', id: 'zaire-lambert' }, 13 | { fullName: 'Alec Villanueva', id: 'alec-villanueva' }, 14 | { fullName: 'Sheldon Rogers', id: 'sheldon-rogers' }, 15 | { fullName: 'Celeste Trujillo', id: 'celeste-trujillo' }, 16 | ] 17 | 18 | const table = { 19 | selection: 'multiple', 20 | title: 'My Lessons', 21 | rows, 22 | schemaColumns: [ 23 | { 24 | id: 'fullName', 25 | label: 'Full name', 26 | component: 'QInput', 27 | }, 28 | ], 29 | get schemaGrid () { 30 | return this.schemaColumns 31 | }, 32 | selected: [], 33 | events: { 34 | 'update:selected': selection => { 35 | table.selected = selection 36 | }, 37 | }, 38 | } 39 | export default table 40 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/examples/slots.js: -------------------------------------------------------------------------------- 1 | const rows = [ 2 | { 3 | title: 'Mathematica', 4 | topic: 'curriculum', 5 | subject: 'We will look at the Mathematica of the Maths √/%^×-+÷', 6 | img: 7 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 8 | grade: 90000, 9 | passing: true, 10 | created: new Date(), 11 | classes: [{ label: 'One', value: '1' }], 12 | }, 13 | { 14 | title: 'Mathematica', 15 | topic: 'split', 16 | subject: 'We will look at the Mathematica of the Maths √/%^×-+÷', 17 | img: 18 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 19 | grade: 90000, 20 | passing: false, 21 | created: new Date(), 22 | classes: [{ label: 'One', value: '1' }], 23 | }, 24 | { 25 | title: 'Mathematica', 26 | topic: 'north', 27 | subject: 'We will look at the Mathematica of the Maths √/%^×-+÷', 28 | img: 29 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 30 | grade: 90000, 31 | passing: true, 32 | created: new Date(), 33 | classes: [{ label: 'One', value: '1' }], 34 | }, 35 | { 36 | title: 'Mathematica', 37 | topic: 'hotdog', 38 | subject: 'We will look at the Mathematica of the Maths √/%^×-+÷', 39 | img: 40 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 41 | grade: 90000, 42 | passing: true, 43 | created: new Date(), 44 | classes: [{ label: 'One', value: '1' }], 45 | }, 46 | { 47 | title: 'Mathematica', 48 | topic: 'hardware', 49 | subject: 'We will look at the Mathematica of the Maths √/%^×-+÷', 50 | img: 51 | 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=160', 52 | grade: 90000, 53 | passing: true, 54 | created: new Date(), 55 | classes: [{ label: 'One', value: '1' }], 56 | }, 57 | ] 58 | 59 | export default { 60 | title: 'My Lessons', 61 | rows, 62 | schemaColumns: [ 63 | { 64 | id: 'title', 65 | label: 'Lesson Title', 66 | component: 'QInput', 67 | }, 68 | { 69 | id: 'topic', 70 | label: 'Topic', 71 | component: 'QInput', 72 | }, 73 | { 74 | id: 'subject', 75 | label: 'Subject', 76 | component: 'QInput', 77 | }, 78 | { 79 | id: 'img', 80 | label: 'Image', 81 | component: 'QImg', 82 | mode: 'view', 83 | evaluatedProps: ['src'], 84 | internalErrors: true, 85 | // component props: 86 | src: val => val, 87 | }, 88 | { 89 | id: 'grade', 90 | label: 'Grade', 91 | component: 'QInput', 92 | // component props: 93 | type: 'number', 94 | }, 95 | { 96 | id: 'passing', 97 | label: 'Passing', 98 | component: 'QToggle', 99 | default: false, 100 | }, 101 | { 102 | id: 'created', 103 | label: 'Created at', 104 | component: 'QInput', 105 | parseInput: val => new Date(val), 106 | dateFormat: 'YYYY/MM/DD', 107 | valueType: 'date', 108 | // component props: 109 | mask: '####/##/##', 110 | placeholder: 'YYYY/MM/DD', 111 | }, 112 | { 113 | id: 'classes', 114 | label: 'Classes', 115 | component: 'QSelect', 116 | // component props: 117 | multiple: true, 118 | options: [ 119 | { label: 'One', value: '1' }, 120 | { label: 'Two', value: '2' }, 121 | { label: 'Three', value: '3' }, 122 | ], 123 | }, 124 | ], 125 | get schemaGrid () { 126 | return this.schemaColumns 127 | }, 128 | } 129 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/basics.js: -------------------------------------------------------------------------------- 1 | const description = `\`\` is Quasar's QTable but with the allows you to define columns & grid-cards via the schema syntax of [EasyForms](https://quasar-easy-forms.web.app). 2 | 3 | A single schema object describing the fields in your data can be used for: 4 | - defining the columns of an EasyTable 5 | - defining the fields to show on each card for the grid-view of the EasyTable 6 | - an EasyForm to be show in a pop-up on eg. clicking a row 7 | 8 | Of course you can also use all of Quasar's QTable props on and they will be passed to . 9 | 10 | See below the interactive demo. You can check the source code, as well as _edit_ any prop and see its effect immidiately!` 11 | 12 | export default { 13 | mode: 'edit', 14 | actionButtons: [], 15 | schema: [ 16 | { 17 | component: 'QMarkdown', 18 | src: description, 19 | noContainer: true, 20 | noLineNumbers: true, 21 | }, 22 | ], 23 | } 24 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/computedFields.js: -------------------------------------------------------------------------------- 1 | const description = `This is an example of a table with [Computed Fields](https://quasar-easy-forms.web.app/computedFields). 2 | 3 | For this example we used a Computed Field that's not yet saved to the database and is computed on the fly when showing the table. 4 | 5 | ` 6 | 7 | export default { 8 | mode: 'edit', 9 | actionButtons: [], 10 | schema: [ 11 | { 12 | component: 'QMarkdown', 13 | src: description, 14 | noContainer: true, 15 | noLineNumbers: true, 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/editing.js: -------------------------------------------------------------------------------- 1 | const description = `Since an \`\` is based on the [EasyForms](https://quasar-easy-forms.web.app) schema system, it's possible to easily implement stuff like inline editing; popup editing; or show an editable form on a row click.` 2 | 3 | export default { 4 | mode: 'edit', 5 | actionButtons: [], 6 | schema: Object.values({ 7 | _1: { 8 | component: 'QMarkdown', 9 | src: description, 10 | noContainer: true, 11 | noLineNumbers: true, 12 | }, 13 | get _2 () { 14 | return { 15 | id: 'chosenExample', 16 | component: 'QBtnToggle', 17 | spread: true, 18 | noCaps: true, 19 | options: [this._3, this._4].map((field, index) => { 20 | return { label: field.label, value: index } 21 | }), 22 | } 23 | }, 24 | _3: { 25 | showCondition: (value, { formData }) => formData.chosenExample === 0, 26 | label: 'Editable form on row click', 27 | span: true, 28 | evaluatedProps: ['showCondition'], 29 | }, 30 | _4: { 31 | showCondition: (value, { formData }) => formData.chosenExample === 1, 32 | label: 'Editable form on button click', 33 | span: true, 34 | evaluatedProps: ['showCondition'], 35 | }, 36 | }), 37 | } 38 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/index.js: -------------------------------------------------------------------------------- 1 | export { default as basics } from './basics' 2 | export { default as editing } from './editing' 3 | export { default as slots } from './slots' 4 | export { default as computedFields } from './computedFields' 5 | export { default as selection } from './selection' 6 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/selection.js: -------------------------------------------------------------------------------- 1 | const description = `By default selection is implemented for both table and grid view, however, every row NEEDS an 'id' prop in order for it to work. 2 | 3 | CSS can be overwritten with these classes: 4 | - \`.easy-table__grid-item.selected\` 5 | 6 | - \`.easy-table__row.selected\` 7 | ` 8 | 9 | export default { 10 | mode: 'edit', 11 | actionButtons: [], 12 | schema: [ 13 | { 14 | component: 'QMarkdown', 15 | src: description, 16 | noContainer: true, 17 | noLineNumbers: true, 18 | }, 19 | ], 20 | } 21 | -------------------------------------------------------------------------------- /ui/dev/src/schemas/pages/slots.js: -------------------------------------------------------------------------------- 1 | const description = `You can use slots with EasyTable. 2 | 3 | ### EasyTable specific slots: 4 | - above-nav-row 5 | - above-table 6 | 7 | ### Quasar's QTable slots: 8 | You can use all Quasar slots "around" the table. However, EasyTable uses these slots to generate its content: \`body\` for the rows, \`item\` for the cards in grid mode. 9 | 10 | If you find yourself in a spot where you also want to use slots for the rows or items, you are probably better off using a regular QTable, perhaps in combination with quasar-easy-forms. Feel free to look at my source code for how I built the EasyTable component. 😉 11 | 12 | > Please note that while the examples below show slot usage in the preview, they don't correctly show the use of the slots in the source code > template yet. I still have to improve this example. 😂 13 | ` 14 | 15 | export default { 16 | mode: 'edit', 17 | actionButtons: [], 18 | schema: [ 19 | { 20 | component: 'QMarkdown', 21 | src: description, 22 | noContainer: true, 23 | noLineNumbers: true, 24 | }, 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /ui/dev/src/statics/app-logo-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/app-logo-128x128.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/apple-icon-167x167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/apple-icon-167x167.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/favicon-16x16.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/favicon-32x32.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/favicon-96x96.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/favicon.ico -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/icon-128x128.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/icon-192x192.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/icon-256x256.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/icon-384x384.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/icon-512x512.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/dev/src/statics/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /ui/dev/src/statics/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quasar-ui-easy-tables", 3 | "version": "0.8.6", 4 | "author": "Luca Ban - Mesqueeb", 5 | "description": "A Vue plugin (that uses Quasar components) to easily generate tables by only defining a \"schema\" object.", 6 | "license": "MIT", 7 | "module": "dist/index.esm.js", 8 | "main": "dist/index.common.js", 9 | "scripts": { 10 | "deploy": "npm run build && cd dev && npm run deploy && cd .. && npm publish && cd ../app-extension && npm publish", 11 | "dev": "cd dev && yarn dev && cd ..", 12 | "dev:umd": "yarn build && node build/script.open-umd.js", 13 | "dev:ssr": "cd dev && yarn 'dev:ssr' && cd ..", 14 | "dev:ios": "cd dev && yarn 'dev:ios' && cd ..", 15 | "dev:android": "cd dev && yarn 'dev:android' && cd ..", 16 | "dev:electron": "cd dev && yarn 'dev:electron' && cd ..", 17 | "build": "node build/index.js", 18 | "build:js": "node build/script.javascript.js" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/mesqueeb/quasar-ui-easy-tables" 23 | }, 24 | "bugs": "https://github.com/mesqueeb/quasar-ui-easy-tables/issues", 25 | "homepage": "https://quasar-easy-tables.web.app", 26 | "peerDependencies": { 27 | "quasar-ui-easy-forms": "^2.3.4" 28 | }, 29 | "devDependencies": { 30 | "@rollup/plugin-buble": "^0.21.3", 31 | "@rollup/plugin-json": "^4.0.3", 32 | "chalk": "^2.4.2", 33 | "fs-extra": "^8.1.0", 34 | "open": "^6.4.0", 35 | "quasar": "^1.12.0", 36 | "quasar-ui-easy-forms": "^2.3.4", 37 | "rimraf": "^3.0.2", 38 | "rollup": "^1.32.1", 39 | "rollup-plugin-vue": "^5.1.9", 40 | "ts-toolbelt": "^6.9.4", 41 | "uglify-es": "^3.3.10", 42 | "vue-template-compiler": "^2.6.11", 43 | "zlib": "^1.0.5" 44 | }, 45 | "browserslist": ["last 1 version, not dead, ie >= 11"], 46 | "dependencies": { 47 | "@rollup/plugin-commonjs": "^11.1.0", 48 | "@rollup/plugin-node-resolve": "^6.1.0", 49 | "commafy-anything": "^1.1.3", 50 | "copy-anything": "^1.6.0", 51 | "flatten-anything": "^1.4.1", 52 | "is-what": "^3.11.3", 53 | "merge-anything": "^2.4.4", 54 | "nestify-anything": "0.0.1", 55 | "path-to-prop": "0.0.3" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ui/src/components/EasyCell.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 101 | -------------------------------------------------------------------------------- /ui/src/components/EasyRow.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 105 | -------------------------------------------------------------------------------- /ui/src/components/EasyTable.vue: -------------------------------------------------------------------------------- 1 | 103 | 104 | 136 | 137 | 420 | -------------------------------------------------------------------------------- /ui/src/helpers/dateHelpers.js: -------------------------------------------------------------------------------- 1 | import { isString, isDate } from 'is-what' 2 | import { date } from 'quasar' 3 | const { formatDate, adjustDate } = date 4 | 5 | export function dateStamp (date, format) { 6 | if (isString(date)) date = new Date(date) 7 | if (!isDate(date)) return 'null' 8 | if (format === 'short') return formatDate(date, 'YYYY/MM/DD') 9 | if (isString(format)) return formatDate(date, format) 10 | return formatDate(date, 'YYYY年MM月DD日') 11 | } 12 | 13 | export function timeStamp (date) { 14 | if (isString(date)) date = new Date(date) 15 | if (!isDate(date)) return '00:00' 16 | return formatDate(date, 'HH:mm') 17 | } 18 | 19 | export function dateTimeStamp (date) { 20 | return dateStamp(date) + ' ' + timeStamp(date) 21 | } 22 | 23 | /** 24 | * Create a new date object from a dateStamp and timeStamp 25 | * 26 | * @export 27 | * @param {(string|date)} dateStamp eg. 2019-12-31 - a date string or date object 28 | * @param {string} timeStamp eg. 23:59 time in the 24h format 29 | * @returns {date} 30 | */ 31 | export function makeDateFromStamps (dateStamp, timeStamp) { 32 | let date = dateStamp 33 | if (isString(date)) date = new Date(date) 34 | if (!isDate(date)) throw new Error('invalid date parameter') 35 | if (!isString(timeStamp)) return date 36 | const hours = Number(timeStamp.split(':')[0]) 37 | const minutes = Number(timeStamp.split(':')[1]) 38 | return adjustDate(date, { hours, minutes }) 39 | } 40 | 41 | export function numberToHourTimestamp (number) { 42 | if (number < 0) { 43 | number = 24 + number 44 | } 45 | const numberAsString = number < 10 ? `0${String(number)}` : String(number) 46 | return `${numberAsString}:00` 47 | } 48 | -------------------------------------------------------------------------------- /ui/src/helpers/flattenPerSchema.js: -------------------------------------------------------------------------------- 1 | import { flattenObjectProps } from 'flatten-anything' 2 | import { isArray } from 'is-what' 3 | 4 | /** 5 | * Flattens an object to be in line with a schema. 6 | * 7 | * @export 8 | * @param {Object} target the target object 9 | * @param {(Object|Object[])} schema 10 | * @returns {Object} 11 | */ 12 | export default function flattenPerSchema (target, schema) { 13 | const schemaArray = isArray(schema) ? schema : Object.values(schema) 14 | const schemaNestedIds = schemaArray 15 | .map(blueprint => blueprint.id) 16 | .filter(id => id && id.includes('.')) 17 | return flattenObjectProps(target, schemaNestedIds) 18 | } 19 | -------------------------------------------------------------------------------- /ui/src/helpers/schemaToQTableColumns.js: -------------------------------------------------------------------------------- 1 | import { isArray, isUndefined, isFunction, isFullString, isBoolean, isPlainObject } from 'is-what' 2 | import pathToProp from 'path-to-prop' 3 | 4 | export default function schemaToQTableColumns (schema) { 5 | const schemaArray = !isArray(schema) ? [schema] : schema 6 | return schemaArray.map(blueprint => { 7 | const { 8 | id, 9 | valueType, 10 | type, 11 | label, 12 | align, 13 | default: df, 14 | sortable, 15 | parseValue, 16 | sort, 17 | format, 18 | headerClasses, 19 | headerStyle, 20 | } = blueprint 21 | const field = row => { 22 | const value = pathToProp(row, id) 23 | // todo: can I retrieve the EasyField context? should I? 24 | const easyFieldContext = { formData: row, fieldInput: () => {} } 25 | // fieldInput has a chance to be triggered on `parseValue`, and thus needs to be added as fn to make sure it exists on the context but does nothing. We don't want to trigger fieldInput on sorting, therefore it must be an empty fn. 26 | const valueOrDefaultValue = !isUndefined(value) 27 | ? value 28 | : isFunction(df) 29 | ? df(row, easyFieldContext) 30 | : df 31 | if (isFunction(parseValue)) return parseValue(valueOrDefaultValue, easyFieldContext) 32 | return valueOrDefaultValue 33 | } 34 | const quasarColumnConfig = { 35 | name: id, 36 | field, 37 | label, 38 | align: ['left', 'right', 'center'].includes(align) 39 | ? align 40 | : [valueType, type].includes('number') 41 | ? 'right' 42 | : 'left', 43 | sortable: isBoolean(sortable) ? sortable : id !== 'id', 44 | sort: isFunction(sort) ? sort : undefined, 45 | format: isFunction(format) ? format : undefined, 46 | headerClasses: [isArray, isPlainObject, isFullString].some(fn => fn(headerClasses)) 47 | ? headerClasses 48 | : undefined, 49 | headerStyle: [isArray, isPlainObject, isFullString].some(fn => fn(headerStyle)) 50 | ? headerStyle 51 | : undefined, 52 | // not sure why i'd want to set these: 53 | // required, 54 | // style, 55 | // classes, 56 | } 57 | return quasarColumnConfig 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /ui/src/index.js: -------------------------------------------------------------------------------- 1 | import { version } from '../package.json' 2 | 3 | import EasyTable from './components/EasyTable.vue' 4 | 5 | import schemaToQTableColumns from './helpers/schemaToQTableColumns.js' 6 | 7 | import dependencyMap from './meta/dependencyMap' 8 | 9 | export { version, EasyTable, schemaToQTableColumns, dependencyMap } 10 | 11 | export default { 12 | version, 13 | 14 | EasyTable, 15 | 16 | schemaToQTableColumns, 17 | 18 | dependencyMap, 19 | 20 | install (Vue) { 21 | Vue.component(EasyTable.name, EasyTable) 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /ui/src/index.sass: -------------------------------------------------------------------------------- 1 | // when installed as package needs to be: 2 | // @ import 'quasar/src/css/variables.sass' 3 | // when using npm run dev in `dev` folder it needs to be: 4 | // @ import '../node_modules/quasar/src/css/variables.sass' 5 | 6 | @import 'margin-padding.sass' 7 | 8 | .text-wrap-all 9 | white-space: pre-line 10 | word-break: break-word 11 | -------------------------------------------------------------------------------- /ui/src/margin-padding.sass: -------------------------------------------------------------------------------- 1 | 2 | $_space-base: 16px 3 | 4 | $xxs: ($_space-base * .1) 5 | $xs: ($_space-base * .25) 6 | $sm: ($_space-base * .5) 7 | $md: $_space-base 8 | $lg: ($_space-base * 1.5) 9 | $xl: ($_space-base * 2.3) 10 | $xxl: ($_space-base * 3) 11 | $xxxl: ($_space-base * 5) 12 | -------------------------------------------------------------------------------- /ui/src/meta/dependencyMap.js: -------------------------------------------------------------------------------- 1 | import merge from 'merge-anything' 2 | import { QTable } from './quasarPropsJson' 3 | 4 | export const dependencyMap = { 5 | EasyTable: { 6 | QTable, 7 | }, 8 | } 9 | 10 | export function getPassedProps (tagName) { 11 | const inheritedComponents = dependencyMap[tagName] || {} 12 | return merge(...Object.values(inheritedComponents)) 13 | } 14 | 15 | export default { ...dependencyMap, getPassedProps } 16 | -------------------------------------------------------------------------------- /ui/src/meta/lang.js: -------------------------------------------------------------------------------- 1 | const defaultLang = { 2 | add: 'Add new', 3 | duplicate: 'Duplicate', 4 | } 5 | 6 | export default defaultLang 7 | -------------------------------------------------------------------------------- /ui/src/meta/quasarPropsJson.js: -------------------------------------------------------------------------------- 1 | import copy from 'copy-anything' 2 | import { camelCase } from 'case-anything' 3 | import { isArray } from 'is-what' 4 | import QTableJson from 'quasar/dist/api/QTable.json' 5 | 6 | const stringToTypeFnDictionary = { 7 | Object: Object, 8 | Function: Function, 9 | Array: Array, 10 | String: String, 11 | Number: Number, 12 | Boolean: Boolean, 13 | RegExp: RegExp, 14 | Date: Date, 15 | Symbol: Symbol, 16 | } 17 | const stringToTypeFn = string => stringToTypeFnDictionary[string] 18 | 19 | function jsonToPropFormat ({ props }) { 20 | if (!props) return {} 21 | return Object.entries(props).reduce((carry, [key, value]) => { 22 | value = copy(value) 23 | value.inheritedProp = true 24 | value.type = isArray(value.type) ? value.type.map(stringToTypeFn) : stringToTypeFn(value.type) 25 | carry[camelCase(key)] = value 26 | return carry 27 | }, {}) 28 | } 29 | 30 | export const QTable = jsonToPropFormat(QTableJson) 31 | 32 | export default { 33 | QTable, 34 | } 35 | -------------------------------------------------------------------------------- /ui/src/mixins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesqueeb/quasar-ui-easy-tables/c8611d97513dc3bdab7ed1d0108a8444239e1270/ui/src/mixins/.gitkeep -------------------------------------------------------------------------------- /ui/umd-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | UMD test 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | quasar-ui-easy-tables v{{ version }} 21 | 22 | 23 |
Quasar v{{ $q.version }}
24 |
25 |
26 | 27 | 28 | 29 |
    30 |
  • In /ui, run: "yarn build"
  • 31 |
  • You need to build & refresh page on each change manually.
  • 32 |
  • Use self-closing tags only!
  • 33 |
  • Example: <my-component></my-component>
  • 34 |
35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 57 | 58 | --------------------------------------------------------------------------------