1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

...

33 Commits

Author SHA1 Message Date
949f7add8f chore(release): 0.0.4 2020-08-06 10:22:06 +02:00
968ec1edf7 ci: improvements on travis release config 2020-08-06 10:20:36 +02:00
a9d3a57281 Merge pull request #26 from EStarium/dependabot/npm_and_yarn/sass-loader-9.0.3
Bump sass-loader from 9.0.2 to 9.0.3
2020-08-06 08:54:45 +02:00
dependabot[bot]
f787439009 Bump sass-loader from 9.0.2 to 9.0.3
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v9.0.2...v9.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-06 06:11:22 +00:00
b03d461e21 build: fix to build configuration on Travis 2020-08-05 22:09:23 +02:00
5c05e3e9e9 refactor: moved queri fields mapping to main process 2020-08-05 22:08:20 +02:00
0089c0cbac feat: window title in app title bar 2020-08-05 13:53:30 +02:00
4fd72ec9e7 refactor: improvements to blob editor and code cleanup 2020-08-04 17:54:19 +02:00
712fe9f00d feat: blob fields edit/view/download 2020-08-03 18:07:08 +02:00
092e8a0732 style: 🎨 stylelint implementation 2020-07-31 18:16:28 +02:00
70908eb076 Minor Improvements 2020-07-31 15:45:32 +02:00
f8a0783769 Merge pull request #25 from EStarium/dependabot/npm_and_yarn/webpack-4.44.1
Bump webpack from 4.44.0 to 4.44.1
2020-07-31 08:10:30 +02:00
dependabot[bot]
cdf964ef07 Bump webpack from 4.44.0 to 4.44.1
Bumps [webpack](https://github.com/webpack/webpack) from 4.44.0 to 4.44.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.44.0...v4.44.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-31 05:22:32 +00:00
413b56916c Notifications timeout, large text editor 2020-07-30 19:12:29 +02:00
acd3310228 Merge pull request #24 from EStarium/dependabot/npm_and_yarn/vue-i18n-8.20.0
Bump vue-i18n from 8.19.0 to 8.20.0
2020-07-30 08:16:32 +02:00
dependabot[bot]
e014f81a9c Bump vue-i18n from 8.19.0 to 8.20.0
Bumps [vue-i18n](https://github.com/kazupon/vue-i18n) from 8.19.0 to 8.20.0.
- [Release notes](https://github.com/kazupon/vue-i18n/releases)
- [Changelog](https://github.com/kazupon/vue-i18n/blob/v8.x/CHANGELOG.md)
- [Commits](https://github.com/kazupon/vue-i18n/compare/v8.19.0...v8.20.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 05:32:52 +00:00
530361144e Merge branch 'master' of https://github.com/EStarium/antares 2020-07-29 15:56:32 +02:00
d69e411581 Moved to electron 9 2020-07-29 15:56:29 +02:00
9a3a0513e2 Merge pull request #23 from EStarium/dependabot/npm_and_yarn/electron-9.1.2
Bump electron from 8.4.0 to 9.1.2
2020-07-29 15:04:58 +02:00
dependabot[bot]
be8fa96c93 Bump electron from 8.4.0 to 9.1.2
Bumps [electron](https://github.com/electron/electron) from 8.4.0 to 9.1.2.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v8.4.0...v9.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-29 05:58:58 +00:00
587116bd20 Merge pull request #22 from EStarium/dependabot/npm_and_yarn/electron-updater-4.3.4
Bump electron-updater from 4.3.1 to 4.3.4
2020-07-28 09:53:45 +02:00
dependabot[bot]
145d1dd1ad Bump electron-updater from 4.3.1 to 4.3.4
Bumps [electron-updater](https://github.com/electron-userland/electron-builder) from 4.3.1 to 4.3.4.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-28 07:50:07 +00:00
457410b65a Merge pull request #21 from EStarium/dependabot/npm_and_yarn/electron-builder-22.8.0
Bump electron-builder from 22.7.0 to 22.8.0
2020-07-28 08:09:09 +02:00
dependabot[bot]
b183eacae1 Bump electron-builder from 22.7.0 to 22.8.0
Bumps [electron-builder](https://github.com/electron-userland/electron-builder) from 22.7.0 to 22.8.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/compare/v22.7.0...v22.8.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-28 05:20:18 +00:00
76cafdb69a Merge pull request #20 from ReverbOD/master
Update it-IT translation
2020-07-27 20:55:10 +02:00
Giuseppe Gigliotti
2d63ddb9c7 Update it-IT translation
fixed & updated italian translation
2020-07-27 20:12:48 +02:00
f12f00dbb7 Added .travis.yml 2020-07-27 16:27:33 +02:00
1ecb6d892c Merge pull request #19 from EStarium/dependabot/npm_and_yarn/webpack-4.44.0
Bump webpack from 4.43.0 to 4.44.0
2020-07-27 07:59:02 +02:00
fcd83f35d8 Merge pull request #18 from EStarium/dependabot/npm_and_yarn/vue-i18n-8.19.0
Bump vue-i18n from 8.18.2 to 8.19.0
2020-07-27 07:58:47 +02:00
dependabot[bot]
7a4d8286a6 Bump webpack from 4.43.0 to 4.44.0
Bumps [webpack](https://github.com/webpack/webpack) from 4.43.0 to 4.44.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.43.0...v4.44.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-27 05:41:17 +00:00
dependabot[bot]
dd5ec2c661 Bump vue-i18n from 8.18.2 to 8.19.0
Bumps [vue-i18n](https://github.com/kazupon/vue-i18n) from 8.18.2 to 8.19.0.
- [Release notes](https://github.com/kazupon/vue-i18n/releases)
- [Changelog](https://github.com/kazupon/vue-i18n/blob/v8.x/CHANGELOG.md)
- [Commits](https://github.com/kazupon/vue-i18n/compare/v8.18.2...v8.19.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-27 05:39:54 +00:00
3f0e5d3512 Fix typos 2020-07-26 15:10:01 +02:00
ac01511c10 Update version 2020-07-24 17:34:39 +02:00
60 changed files with 1512 additions and 14757 deletions

2
.gitignore vendored
View File

@@ -6,4 +6,4 @@ thumbs.db
.vscode
TODO.md
*.txt
dev-app-update.yml
package-lock.json

14
.stylelintrc Normal file
View File

@@ -0,0 +1,14 @@
{
"extends": [
"stylelint-config-standard"
],
"fix": true,
"formatter": "verbose",
"plugins": [
"stylelint-scss"
],
"rules": {
"at-rule-no-unknown": null
},
"syntax": "scss"
}

38
.travis.yml Normal file
View File

@@ -0,0 +1,38 @@
language: node_js
node_js: 12
before_install:
- npm install
cache:
directories:
- node_modules
- app/node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
- $HOME/.npm/_prebuilds
env:
global:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
jobs:
include:
- stage: Test
script:
- npm test
- stage: Deploy Linux & Windows
if: tag IS present
os: linux
services: docker
script:
- docker run --rm --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') -v ${PWD}:/project -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine /bin/bash -c "npm run build -- --linux --win -p always"
before_cache:
- rm -rf $HOME/.cache/electron-builder/wine
- stage: Deploy Mac
if: tag IS present
os: osx
osx_image: xcode10.2
script:
- npm run build -- -p always

View File

@@ -1,18 +1,11 @@
# Changelog
## [0.0.2-alpha](https://github.com/Fabio286/antares/releases/tag/v0.0.2-alpha) - 2020-06-26
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### Added
### [0.0.4](https://github.com/EStarium/antares/compare/v0.0.3-alpha...v0.0.4) (2020-08-06)
- **Edit table fields:** You can edit fields from tables by double click. For now is available only on numeric and textual values.
- Various improvements under the hood.
### Fixed
- Connection test.
## [0.0.1-alpha](https://github.com/Fabio286/antares/releases/tag/v0.0.1-alpha) - 2020-06-19
### Features
- **Initial release:**
* blob fields edit/view/download ([712fe9f](https://github.com/EStarium/antares/commit/712fe9f00d210db0f2317eca61e7fb648383e3fe))
* window title in app title bar ([0089c0c](https://github.com/EStarium/antares/commit/0089c0cbac6caf0a6fd195849099f18713580228))

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2017 Jakub Szwacz
Copyright (c) 2020 Fabio Di Stasio
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -4,12 +4,14 @@
# Antares SQL Client
![GitHub package.json version](https://img.shields.io/github/package-json/v/estarium/antares) [![Build Status](https://travis-ci.com/EStarium/antares.svg?branch=master)](https://travis-ci.com/EStarium/antares) ![GitHub All Releases](https://img.shields.io/github/downloads/estarium/antares/total) ![GitHub](https://img.shields.io/github/license/estarium/antares)
Antares is an SQL client based on Electron.js and Vue.js that aims to become a useful tool, especially for developers.
My target is to support as many databases as possible, and all major operating systems, including the ARM versions.
At the moment this application is in a development state, it lacks many features, and is'nt ready as a main SQL client. However i'm actively working on it, hoping to provide all all minimum features as soon as possible.
At the moment this application is in a development state, it lacks many features, and is'nt ready as a main SQL client. However i'm actively working on it, hoping to provide all essential features as soon as possible.
If you are curious to try this early state of Antares you can download and install the [latest release](https://github.com/EStarium/antares/releases), and stay tuned for updates. At moment is only available for Windows.
If you are curious to try this early state of Antares you can download and install the [latest release](https://github.com/EStarium/antares/releases), and stay tuned for updates. At moment i'm testing only on Windows.
## Currently supported
@@ -27,3 +29,7 @@ If you are curious to try this early state of Antares you can download and insta
- [x] Windows
- [ ] Linux
- [ ] MacOS
## Translations
[Giuseppe Gigliotti](https://github.com/ReverbOD) / [Italian Translation](https://github.com/EStarium/antares/pull/20)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 25 KiB

13771
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,56 @@
{
"name": "antares",
"productName": "Antares",
"version": "0.0.2-alpha",
"version": "0.0.4",
"description": "A cross-platform easy to use SQL client.",
"main": "src/main/index.js",
"license": "MIT",
"repository": "https://github.com/EStarium/antares.git",
"scripts": {
"dev": "cross-env NODE_ENV=development electron-webpack dev",
"compile": "electron-webpack",
"dist": "cross-env NODE_ENV=production npm run compile && electron-builder",
"dist:dir": "cross-env NODE_ENV=production npm run dist --dir -c.compression=store -c.mac.identity=null",
"publish": "electron-builder -p always"
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
"release": "standard-version",
"release:pre": "npm run release -- --prerelease alpha"
},
"author": "Fabio Di Stasio <fabio286@gmail.com>",
"build": {
"npmRebuild": false,
"asar": true,
"appId": "com.estarium.antares",
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
"files": [
"static/*"
]
"dist/**/*",
"src/**/*",
"node_modules/**/*",
"package.json"
],
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis"
]
},
"linux": {
"target": [
"deb",
"AppImage"
],
"category": "Development"
}
},
"electronWebpack": {
"whiteListedModules": [
@@ -31,7 +63,7 @@
"dependencies": {
"codemirror": "^5.56.0",
"electron-log": "^4.2.2",
"electron-updater": "^4.3.1",
"electron-updater": "^4.3.4",
"lodash": "^4.17.19",
"material-design-icons": "^3.0.1",
"moment": "^2.27.0",
@@ -41,7 +73,7 @@
"source-map-support": "^0.5.16",
"spectre.css": "^0.5.9",
"vue-click-outside": "^1.1.0",
"vue-i18n": "^8.18.2",
"vue-i18n": "^8.20.0",
"vue-the-mask": "^0.11.1",
"vuedraggable": "^2.24.0",
"vuex": "^3.5.1",
@@ -50,8 +82,8 @@
"devDependencies": {
"babel-eslint": "^10.1.0",
"cross-env": "^7.0.2",
"electron": "^8.4.0",
"electron-builder": "^22.7.0",
"electron": "^9.1.2",
"electron-builder": "^22.8.0",
"electron-devtools-installer": "^3.1.1",
"electron-webpack": "^2.8.2",
"electron-webpack-vue": "^2.4.0",
@@ -63,8 +95,12 @@
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"sass-loader": "^9.0.2",
"sass-loader": "^9.0.3",
"standard-version": "^8.0.2",
"stylelint": "^13.6.1",
"stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0",
"vue": "^2.6.11",
"webpack": "^4.43.0"
"webpack": "^4.44.1"
}
}

12
src/common/fieldTypes.js Normal file
View File

@@ -0,0 +1,12 @@
export const TEXT = ['char', 'varchar'];
export const LONG_TEXT = ['text', 'mediumtext', 'longtext'];
export const NUMBER = ['int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float', 'double', 'decimal'];
export const DATE = ['date'];
export const TIME = ['time'];
export const DATETIME = ['datetime', 'timestamp'];
export const BLOB = ['blob', 'mediumblob', 'longblob'];
export const BIT = ['bit'];

View File

@@ -0,0 +1,7 @@
'use strict';
export function bufferToBase64 (buf) {
const binstr = Array.prototype.map.call(buf, ch => {
return String.fromCharCode(ch);
}).join('');
return btoa(binstr);
}

View File

@@ -0,0 +1,12 @@
'use strict';
export function formatBytes (bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

View File

@@ -1,13 +1,10 @@
export function uidGen () {
return Math.random().toString(36).substr(2, 9).toUpperCase();
};
'use strict';
export function mimeFromHex (hex) {
switch (hex.substring(0, 4)) { // 2 bytes
case '424D':
return { ext: 'bmp', mime: 'image/bmp' };
case '1F8B':
return { ext: 'gz', mime: 'application/gzip' };
return { ext: 'tar.gz', mime: 'application/gzip' };
case '0B77':
return { ext: 'ac3', mime: 'audio/vnd.dolby.dd-raw' };
case '7801':
@@ -20,7 +17,7 @@ export function mimeFromHex (hex) {
default:
switch (hex.substring(0, 6)) { // 3 bytes
case 'FFD8FF':
return { ext: 'jpj', mime: 'image/jpeg' };
return { ext: 'jpg', mime: 'image/jpeg' };
case '4949BC':
return { ext: 'jxr', mime: 'image/vnd.ms-photo' };
case '425A68':
@@ -39,21 +36,11 @@ export function mimeFromHex (hex) {
return { ext: 'bpg', mime: 'image/bpg' };
case '4D4D002A':
return { ext: 'tif', mime: 'image/tiff' };
case '00000100':
return { ext: 'ico', mime: 'image/x-icon' };
default:
return { ext: '???', mime: 'unknown ' + hex };
return { ext: '', mime: 'unknown ' + hex };
}
}
}
};
export function formatBytes (bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

View File

@@ -0,0 +1,19 @@
/* eslint-disable no-useless-escape */
// eslint-disable-next-line no-control-regex
const regex = new RegExp(/[\0\x08\x09\x1a\n\r"'\\\%]/g);
/**
* Escapes a string
*
* @param {String} string
* @returns {String}
*/
function sqlEscaper (string) {
return string.replace(regex, (char) => {
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '"', '\\', '\\\\', '%'];
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\'\'', '""', '\\\\', '\\\\\\\\', '\\%'];
return r[m.indexOf(char)];
});
}
export { sqlEscaper };

View File

@@ -0,0 +1,4 @@
'use strict';
export function uidGen () {
return Math.random().toString(36).substr(2, 9).toUpperCase();
};

View File

@@ -12,35 +12,29 @@ process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
// global reference to mainWindow (necessary to prevent window from being garbage collected)
let mainWindow;
function createMainWindow () {
async function createMainWindow () {
const icon = require('../renderer/images/logo-32.png');
const window = new BrowserWindow({
width: 1600,
height: 1000,
minHeight: 550,
width: 1024,
height: 800,
minWidth: 900,
minHeight: 550,
title: 'Antares',
autoHideMenuBar: true,
icon: nativeImage.createFromDataURL(icon.default),
webPreferences: {
nodeIntegration: true,
'web-security': false
'web-security': false,
enableRemoteModule: true,
spellcheck: false
},
frame: false,
backgroundColor: '#1d1d1d'
});
if (isDevelopment)
window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
else {
window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
}
if (isDevelopment) {
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
window.webContents.openDevTools();
@@ -52,6 +46,13 @@ function createMainWindow () {
console.log(err);
});
}
else {
await window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
}
window.on('closed', () => {
mainWindow = null;
@@ -64,12 +65,12 @@ function createMainWindow () {
});
});
// Initialize ipcHandlers
ipcHandlers();
return window;
};
// Initialize ipcHandlers
ipcHandlers();
// quit application when all windows are closed
app.on('window-all-closed', () => {
// on macOS it is common for applications to stay open until the user explicitly quits

View File

@@ -0,0 +1,7 @@
import { app, ipcMain } from 'electron';
export default () => {
ipcMain.on('closeApp', () => {
app.exit();
});
};

View File

@@ -1,6 +1,7 @@
import connection from './connection';
import tables from './tables';
import updates from './updates';
import application from './application';
const connections = {};
@@ -8,4 +9,5 @@ export default () => {
connection(connections);
tables(connections);
updates();
application();
};

View File

@@ -20,6 +20,7 @@ export class AntaresConnector {
this._params = args.params;
this._poolSize = args.poolSize || false;
this._connection = null;
this._logger = args.logger || console.log;
this._queryDefaults = {
schema: '',
@@ -247,7 +248,7 @@ export class AntaresConnector {
* @memberof AntaresConnector
*/
async raw (sql) {
if (process.env.NODE_ENV === 'development') console.log(sql);
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
switch (this._client) { // TODO: uniform fields with every client type, needed table name and fields array
case 'maria':

View File

@@ -13,13 +13,22 @@ export default class {
.run();
}
static getTableColumns (connection, schema, table) {
return connection
static async getTableColumns (connection, schema, table) {
const { rows } = await connection
.select('*')
.schema('information_schema')
.from('COLUMNS')
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
.orderBy({ ORDINAL_POSITION: 'ASC' })
.run();
return rows.map(field => {
return {
name: field.COLUMN_NAME,
key: field.COLUMN_KEY.toLowerCase(),
type: field.DATA_TYPE,
precision: field.DATETIME_PRECISION
};
});
}
}

View File

@@ -1,4 +1,8 @@
'use strict';
import { sqlEscaper } from 'common/libs/sqlEscaper';
import { TEXT, LONG_TEXT, NUMBER, BLOB } from 'common/fieldTypes';
import fs from 'fs';
export default class {
static async getTableData (connection, schema, table) {
return connection
@@ -9,13 +13,34 @@ export default class {
.run();
}
static async updateTableCell (connection, params) { // TODO: Handle different field types
return connection
.update({ [params.field]: `= "${params.content}"` })
static async updateTableCell (connection, params) {
let escapedParam;
let reload = false;
if (NUMBER.includes(params.type))
escapedParam = params.content;
else if ([...TEXT, ...LONG_TEXT].includes(params.type))
escapedParam = `"${sqlEscaper(params.content)}"`;
else if (BLOB.includes(params.type)) {
if (params.content) {
const fileBlob = fs.readFileSync(params.content);
escapedParam = `0x${fileBlob.toString('hex')}`;
reload = true;
}
else
escapedParam = '""';
}
else
escapedParam = `"${sqlEscaper(params.content)}"`;
await connection
.update({ [params.field]: `= ${escapedParam}` })
.schema(params.schema)
.from(params.table)
.where({ [params.primary]: `= ${params.id}` })
.run();
return { reload };
}
static async deleteTableRows (connection, params) {

View File

@@ -40,8 +40,7 @@ export default {
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings')
},
data () {
return {
};
return {};
},
computed: {
...mapGetters({
@@ -64,30 +63,30 @@ export default {
</script>
<style lang="scss">
html,
body{
height: 100%;
}
html,
body {
height: 100%;
}
#wrapper{
height: 100vh;
position: relative;
}
#wrapper {
height: 100vh;
position: relative;
}
#window-content{
display: flex;
position: relative;
overflow: hidden;
}
#window-content {
display: flex;
position: relative;
overflow: hidden;
}
#main-content {
padding: 0;
justify-content: flex-start;
height: calc(100vh - #{$excluding-size});
width: calc(100% - #{$settingbar-width});
#main-content {
padding: 0;
justify-content: flex-start;
height: calc(100vh - #{$excluding-size});
width: calc(100% - #{$settingbar-width});
> .columns{
height: calc(100vh - #{$footer-height});
}
}
> .columns {
height: calc(100vh - #{$footer-height});
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="modal modal-sm active">
<div class="modal active" :class="modalSizeClass">
<a class="modal-overlay" @click="hideModal" />
<div class="modal-container">
<div v-if="hasHeader" class="modal-header">
@@ -29,13 +29,13 @@
class="btn btn-primary mr-2"
@click="confirmModal"
>
{{ $t('word.confirm') }}
{{ confirmText || $t('word.confirm') }}
</button>
<button
class="btn btn-link"
@click="hideModal"
>
{{ $t('word.cancel') }}
{{ cancelText || $t('word.cancel') }}
</button>
</div>
</div>
@@ -45,6 +45,15 @@
<script>
export default {
name: 'BaseConfirmModal',
props: {
size: {
type: String,
validator: prop => ['small', 'medium', 'large'].includes(prop),
default: 'small'
},
confirmText: String,
cancelText: String
},
computed: {
hasHeader () {
return !!this.$slots.header;
@@ -54,6 +63,13 @@ export default {
},
hasDefault () {
return !!this.$slots.default;
},
modalSizeClass () {
if (this.size === 'small')
return 'modal-sm';
else if (this.size === 'large')
return 'modal-lg';
else return '';
}
},
methods: {
@@ -70,7 +86,7 @@ export default {
</script>
<style scoped>
.modal.modal-sm .modal-container{
padding: 0;
}
.modal.modal-sm .modal-container {
padding: 0;
}
</style>

View File

@@ -38,58 +38,57 @@ export default {
</script>
<style lang="scss">
.context{
.context {
display: flex;
color: $body-font-color;
font-size: 16px;
z-index: 400;
justify-content: center;
align-items: center;
overflow: hidden;
padding: 0.4rem;
position: fixed;
right: 0;
top: 0;
left: 0;
bottom: 0;
pointer-events: none;
.context-container {
min-width: 100px;
max-width: 150px;
z-index: 1;
box-shadow: 0 0 1px 0 #000;
padding: 0;
background: #1d1d1d;
border-radius: 0.1rem;
display: flex;
flex-direction: column;
position: absolute;
pointer-events: initial;
.context-element {
display: flex;
align-items: center;
padding: 0.1rem 0.3rem;
cursor: pointer;
&:hover {
background: $primary-color;
}
}
}
.context-overlay {
background: transparent;
bottom: 0;
cursor: default;
display: block;
left: 0;
position: absolute;
color: $body-font-color;
font-size: 16px;
z-index: 400;
justify-content: center;
align-items: center;
overflow: hidden;
padding: 0.4rem;
position: fixed;
right: 0;
top: 0;
left: 0;
bottom: 0;
pointer-events: none;
.context-container{
min-width: 100px;
max-width: 150px;
z-index: 1;
box-shadow: 0px 0px 1px 0px #000;
padding: 0;
background: #1d1d1d;
border-radius: 0.1rem;
display: flex;
flex-direction: column;
position: absolute;
pointer-events: initial;
.context-element{
display: flex;
align-items: center;
padding: .1rem .3rem;
cursor: pointer;
&:hover{
background: $primary-color;
}
}
}
.context-overlay{
background: transparent;
bottom: 0;
cursor: default;
display: block;
left: 0;
position: absolute;
right: 0;
top: 0;
}
}
}
}
</style>

View File

@@ -71,25 +71,24 @@ export default {
};
</script>
<style scoped>
.toast{
display: flex;
justify-content: space-between;
user-select: text;
word-break: break-all;
width: fit-content;
margin-left: auto;
}
.toast {
display: flex;
justify-content: space-between;
user-select: text;
word-break: break-all;
width: fit-content;
margin-left: auto;
}
.notification-message{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
max-width: 30rem;
user-select: none;
}
.notification-message {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
max-width: 30rem;
}
.expanded .notification-message{
white-space: initial;
}
.expanded .notification-message {
white-space: initial;
}
</style>

View File

@@ -70,10 +70,10 @@ export default {
};
</script>
<style scoped>
.toast{
display: flex;
justify-content: space-between;
user-select: text;
word-break: break-all;
}
.toast {
display: flex;
justify-content: space-between;
user-select: text;
word-break: break-all;
}
</style>

View File

@@ -71,7 +71,3 @@ export default {
}
};
</script>
<style>
</style>

View File

@@ -36,7 +36,7 @@
<option value="maria">
MariaDB
</option>
<option value="mssql">
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="pg">
@@ -44,7 +44,7 @@
</option>
<option value="oracledb">
Oracle DB
</option>
</option> -->
</select>
</div>
</div>
@@ -230,7 +230,7 @@ export default {
</script>
<style scoped>
.modal-container{
max-width: 450px;
}
.modal-container {
max-width: 450px;
}
</style>

View File

@@ -40,7 +40,7 @@
<option value="maria">
MariaDB
</option>
<option value="mssql">
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="pg">
@@ -48,7 +48,7 @@
</option>
<option value="oracledb">
Oracle DB
</option>
</option> -->
</select>
</div>
</div>
@@ -148,7 +148,7 @@
<script>
import { mapActions } from 'vuex';
import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/utilities';
import { uidGen } from 'common/libs/uidGen';
import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseToast from '@/components/BaseToast';
@@ -253,7 +253,7 @@ export default {
</script>
<style scoped>
.modal-container{
max-width: 450px;
}
.modal-container {
max-width: 450px;
}
</style>

View File

@@ -45,8 +45,8 @@
<div v-if="selectedTab === 'general'" class="panel-body py-4">
<form class="form-horizontal">
<div class="col-6 col-sm-12">
<div class="form-group">
<div class="col-8 col-sm-12">
<div class="form-group mb-4">
<div class="col-6 col-sm-12">
<label class="form-label">
<i class="material-icons md-18 mr-1">translate</i>
@@ -69,6 +69,25 @@
</select>
</div>
</div>
<div class="form-group">
<div class="col-6 col-sm-12">
<label class="form-label">
{{ $t('message.notificationsTimeout') }}:
</label>
</div>
<div class="col-6 col-sm-12">
<div class="input-group">
<input
v-model="localTimeout"
class="form-input"
type="number"
min="1"
@focusout="checkNotificationsTimeout"
>
<span class="input-group-addon">{{ $t('word.seconds') }}</span>
</div>
</div>
</div>
</div>
</form>
</div>
@@ -115,6 +134,7 @@ export default {
return {
isUpdate: false,
localLocale: null,
localTimeout: null,
selectedTab: 'general'
};
},
@@ -123,7 +143,8 @@ export default {
appName: 'application/appName',
appVersion: 'application/appVersion',
selectedSettingTab: 'application/selectedSettingTab',
selectedLocale: 'settings/getLocale'
selectedLocale: 'settings/getLocale',
notificationsTimeout: 'settings/getNotificationsTimeout'
}),
locales () {
const locales = [];
@@ -135,42 +156,49 @@ export default {
},
created () {
this.localLocale = this.selectedLocale;
this.localTimeout = this.notificationsTimeout;
this.selectedTab = this.selectedSettingTab;
},
methods: {
...mapActions({
closeModal: 'application/hideSettingModal',
changeLocale: 'settings/changeLocale'
changeLocale: 'settings/changeLocale',
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
}),
selectTab (tab) {
this.selectedTab = tab;
},
openOutside (link) {
shell.openExternal(link);
},
checkNotificationsTimeout () {
if (!this.localTimeout)
this.localTimeout = 10;
this.updateNotificationsTimeout(+this.localTimeout);
}
}
};
</script>
<style lang="scss">
#settings{
.modal-body{
overflow: hidden;
#settings {
.modal-body {
overflow: hidden;
.panel-body{
height: calc(70vh - 70px);
overflow: auto;
}
.panel-body {
height: calc(70vh - 70px);
overflow: auto;
}
.badge::after{
background: #32b643;
}
.form-label{
display: flex;
align-items: center;
}
}
.badge::after {
background: #32b643;
}
.form-label {
display: flex;
align-items: center;
}
}
}
</style>

View File

@@ -78,7 +78,7 @@ export default {
</script>
<style lang="scss">
.empty{
color: $body-font-color;
.empty {
color: $body-font-color;
}
</style>

View File

@@ -69,21 +69,20 @@ export default {
</script>
<style lang="scss">
.editor-wrapper{
border-bottom: 1px solid #444444;
}
.editor-wrapper {
border-bottom: 1px solid #444;
}
.CodeMirror{
height: 200px;
.CodeMirror {
height: 200px;
.CodeMirror-scroll{
max-width: 100%;
}
.CodeMirror-scroll {
max-width: 100%;
}
.CodeMirror-line {
word-break: break-word!important;
white-space: pre-wrap!important;
word-break: normal;
}
}
.CodeMirror-line {
word-break: break-word !important;
white-space: pre-wrap !important;
}
}
</style>

View File

@@ -69,7 +69,3 @@ export default {
}
};
</script>
<style>
</style>

View File

@@ -26,12 +26,12 @@ export default {
</script>
<style scoped>
.empty{
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
}
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

View File

@@ -50,41 +50,41 @@ export default {
</script>
<style lang="scss">
#footer{
height: $footer-height;
#footer {
height: $footer-height;
display: flex;
justify-content: space-between;
align-items: center;
background: $primary-color;
padding: 0 0.2rem;
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 1px 0 #000;
.footer-elements {
list-style: none;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
background: $primary-color;
padding: 0 .2rem;
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 1px 0px #000;
.footer-elements{
list-style: none;
margin: 0;
display: flex;
align-items: center;
.footer-element {
height: $footer-height;
display: flex;
align-items: center;
padding: 0 0.4rem;
margin: 0;
.footer-element{
height: $footer-height;
display: flex;
align-items: center;
padding: 0 .4rem;
margin: 0;
&.footer-link {
cursor: pointer;
transition: background 0.2s;
&.footer-link{
cursor: pointer;
transition: background .2s;
&:hover{
background: rgba($color: #fff, $alpha: .1);
}
}
}
&:hover {
background: rgba($color: #fff, $alpha: 0.1);
}
}
}
}
}
}
</style>

View File

@@ -1,5 +1,9 @@
<template>
<div id="notifications-board">
<div
id="notifications-board"
@mouseenter="clearTimeouts"
@mouseleave="rearmTimeouts"
>
<transition-group name="slide-fade">
<BaseNotification
v-for="notification in latestNotifications"
@@ -21,27 +25,60 @@ export default {
components: {
BaseNotification
},
data () {
return {
timeouts: {}
};
},
computed: {
...mapGetters({
notifications: 'notifications/getNotifications'
notifications: 'notifications/getNotifications',
notificationsTimeout: 'settings/getNotificationsTimeout'
}),
latestNotifications () {
return this.notifications.slice(0, 10);
}
},
watch: {
notifications: {
deep: true,
handler: function (notification) {
if (notification.length) {
this.timeouts[notification[0].uid] = setTimeout(() => {
this.removeNotification(notification[0].uid);
delete this.timeouts[notification.uid];
}, this.notificationsTimeout * 1000);
}
}
}
},
methods: {
...mapActions({
removeNotification: 'notifications/removeNotification'
})
}),
clearTimeouts () {
for (const uid in this.timeouts) {
clearTimeout(this.timeouts[uid]);
delete this.timeouts[uid];
}
},
rearmTimeouts () {
for (const notification of this.notifications) {
this.timeouts[notification.uid] = setTimeout(() => {
this.removeNotification(notification.uid);
delete this.timeouts[notification.uid];
}, this.notificationsTimeout * 1000);
}
}
}
};
</script>
<style lang="scss">
#notifications-board{
position: absolute;
z-index: 9;
right: 1rem;
bottom: 1rem;
}
#notifications-board {
position: absolute;
z-index: 9;
right: 1rem;
bottom: 1rem;
}
</style>

View File

@@ -106,101 +106,98 @@ export default {
</script>
<style lang="scss">
#settingbar{
width: $settingbar-width;
height: calc(100vh - #{$excluding-size});
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
#settingbar {
width: $settingbar-width;
height: calc(100vh - #{$excluding-size});
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
background: $bg-color-light;
padding: 0;
box-shadow: 0 0 1px 0 #000;
z-index: 9;
.settingbar-top-elements {
overflow-x: hidden;
overflow-y: overlay;
max-height: calc((100vh - 3.5rem) - #{$excluding-size});
&::-webkit-scrollbar {
width: 3px;
}
}
.settingbar-bottom-elements {
padding-top: 0.5rem;
background: $bg-color-light;
z-index: 1;
}
.settingbar-elements {
list-style: none;
text-align: center;
width: $settingbar-width;
padding: 0;
box-shadow: 0 0 1px 0px #000;
z-index: 9;
margin: 0;
.settingbar-top-elements{
overflow-x: hidden;
overflow-y: overlay;
max-height: calc((100vh - 3.5rem) - #{$excluding-size});
.settingbar-element {
height: $settingbar-width;
width: 100%;
margin: 0;
border-left: 3px solid transparent;
opacity: 0.5;
transition: opacity 0.2s;
display: flex;
align-content: center;
justify-content: center;
flex-direction: column;
&::-webkit-scrollbar {
width: 3px;
}
&:hover {
opacity: 1;
}
&.selected {
border-left-color: $body-font-color;
opacity: 1;
}
.settingbar-element-icon {
&.badge::after {
bottom: -10px;
right: 0;
position: absolute;
background: $success-color;
}
}
}
}
}
.settingbar-bottom-elements{
padding-top: .5rem;
background: $bg-color-light;
z-index: 1;
}
.ex-tooltip {// Because both overflow-x: visible and overflow-y:auto are evil!!!
.ex-tooltip-content {
z-index: 999;
visibility: hidden;
opacity: 0;
display: block;
position: absolute;
text-align: center;
margin: 0 0 0 calc(#{$settingbar-width} - 5px);
left: 0;
padding: 0.2rem 0.4rem;
font-size: 0.7rem;
background: rgba(48, 55, 66, 0.95);
border-radius: 0.1rem;
color: #fff;
max-width: 320px;
pointer-events: none;
text-overflow: ellipsis;
transition: opacity 0.2s;
}
.settingbar-elements{
list-style: none;
text-align: center;
width: $settingbar-width;
padding: 0;
margin: 0;
.settingbar-element{
height: $settingbar-width;
width: 100%;
margin: 0;
border-left: 3px solid transparent;
opacity: .5;
transition: opacity .2s;
display: flex;
align-content: center;
justify-content: center;
flex-direction: column;
&:hover{
opacity: 1;
}
&.selected{
border-left-color: $body-font-color;
opacity: 1;
}
.settingbar-element-icon{
&.badge::after{
bottom: -10px;
right: 0;
position: absolute;
background: $success-color;
}
}
}
}
}
.ex-tooltip{// Because both overflow-x: visible and overflow-y:auto are evil!!!
.ex-tooltip-content{
z-index: 999;
visibility: hidden;
opacity: 0;
display:block;
position:absolute;
background-color:#feffe1;
text-align: center;
margin:.0 0 0 calc(#{$settingbar-width} - 5px);
left: 0;
padding: .2rem .4rem;
font-size: .7rem;
background: rgba(48,55,66,.95);
border-radius: .1rem;
color: #fff;
max-width: 320px;
pointer-events: none;
text-overflow: ellipsis;
transition: opacity .2s;
}
&:hover .ex-tooltip-content{
visibility: visible;
opacity: 1;
}
}
&:hover .ex-tooltip-content {
visibility: visible;
opacity: 1;
}
}
</style>

View File

@@ -5,7 +5,7 @@
<img class="titlebar-logo" :src="require('@/images/logo.svg').default">
</div>
<div class="titlebar-elements">
<!-- -->
{{ windowTitle }}
</div>
<div class="titlebar-elements">
<div
@@ -37,7 +37,8 @@
</template>
<script>
import { remote } from 'electron';
import { remote, ipcRenderer } from 'electron';
import { mapGetters } from 'vuex';
export default {
name: 'TheTitleBar',
@@ -48,6 +49,22 @@ export default {
isDevelopment: process.env.NODE_ENV === 'development'
};
},
computed: {
...mapGetters({
getConnectionName: 'connections/getConnectionName',
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
windowTitle () {
if (!this.selectedWorkspace) return '';
const connectionName = this.getConnectionName(this.selectedWorkspace);
const workspace = this.getWorkspace(this.selectedWorkspace);
const breadcrumbs = Object.values(workspace.breadcrumbs).filter(breadcrumb => breadcrumb);
return [connectionName, ...breadcrumbs].join(' • ');
}
},
created () {
window.addEventListener('resize', this.onResize);
},
@@ -56,7 +73,7 @@ export default {
},
methods: {
closeApp () {
this.w.close();
ipcRenderer.send('closeApp');
},
minimizeApp () {
this.w.minimize();
@@ -81,55 +98,55 @@ export default {
</script>
<style lang="scss">
#titlebar{
#titlebar {
display: flex;
position: relative;
justify-content: space-between;
background: $bg-color-light;
align-items: center;
height: $titlebar-height;
-webkit-app-region: drag;
user-select: none;
box-shadow: 0 0 1px 0 #000;
z-index: 9999;
.titlebar-resizer {
position: absolute;
top: 0;
width: 100%;
height: 4px;
z-index: 999;
-webkit-app-region: no-drag;
}
.titlebar-elements {
display: flex;
position: relative;
justify-content: space-between;
background: $bg-color-light;
align-items: center;
height: $titlebar-height;
-webkit-app-region: drag;
user-select: none;
box-shadow: 0 0 1px 0px #000;
z-index: 9999;
.titlebar-resizer{
position: absolute;
top: 0;
width: 100%;
height: 4px;
z-index: 999;
-webkit-app-region: no-drag;
.titlebar-logo {
height: $titlebar-height;
padding: 0 0.4rem;
}
.titlebar-elements{
display: flex;
align-items: center;
.titlebar-element {
display: flex;
align-items: center;
height: $titlebar-height;
line-height: 0;
padding: 0 0.7rem;
opacity: 0.7;
transition: opacity 0.2s;
-webkit-app-region: no-drag;
.titlebar-logo{
height: $titlebar-height;
padding: 0 .4rem;
}
&:hover {
opacity: 1;
background: rgba($color: #fff, $alpha: 0.2);
}
.titlebar-element{
display: flex;
align-items: center;
height: $titlebar-height;
line-height: 0;
padding: 0 .7rem;
opacity: .7;
transition: opacity .2s;
-webkit-app-region: no-drag;
&:hover{
opacity: 1;
background: rgba($color: #fff, $alpha: .2);
}
&.close-button:hover{
background: red;
}
}
&.close-button:hover {
background: red;
}
}
}
}
}
</style>

View File

@@ -92,94 +92,94 @@ export default {
</script>
<style lang="scss">
.workspace{
padding: 0;
margin: 0;
.workspace {
padding: 0;
margin: 0;
.workspace-tabs{
overflow: auto;
height: calc(100vh - #{$excluding-size});
.workspace-tabs {
overflow: auto;
height: calc(100vh - #{$excluding-size});
.tab-block{
background: $bg-color-light;
margin-top: 0;
.tab-block {
background: $bg-color-light;
margin-top: 0;
.tab-item{
max-width: 12rem;
width: fit-content;
flex: initial;
.tab-item {
max-width: 12rem;
width: fit-content;
flex: initial;
&.active a{
opacity: 1;
}
&.active a {
opacity: 1;
}
> a{
padding: .2rem .8rem;
color: $body-font-color;
cursor: pointer;
display: flex;
align-items: center;
opacity: .7;
transition: opacity .2s;
> a {
padding: 0.2rem 0.8rem;
color: $body-font-color;
cursor: pointer;
display: flex;
align-items: center;
opacity: 0.7;
transition: opacity 0.2s;
&:hover{
opacity: 1;
}
&:hover {
opacity: 1;
}
> span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
.workspace-query-results{
overflow: auto;
white-space: nowrap;
.table{
width: auto;
border-collapse: separate;
.th{
position: sticky;
top: 0;
background: $bg-color;
border: 1px solid;
border-left: none;
border-bottom-width: 2px;
border-color: $bg-color-light;
padding: 0;
font-weight: 700;
font-size: .7rem;
> div {
padding: .1rem .4rem;
min-width: -webkit-fill-available;
}
}
.td{
border-right: 1px solid;
border-bottom: 1px solid;
border-color: $bg-color-light;
padding: 0 .4rem;
text-overflow: ellipsis;
max-width: 200px;
white-space: nowrap;
> span {
overflow: hidden;
font-size: .7rem;
&:focus{
box-shadow:inset 0px 0px 0px 1px $body-font-color;
background: rgba($color: #000000, $alpha: .3);
outline: none;
}
}
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
}
.workspace-query-results {
overflow: auto;
white-space: nowrap;
.table {
width: auto;
border-collapse: separate;
.th {
position: sticky;
top: 0;
background: $bg-color;
border: 1px solid;
border-left: none;
border-bottom-width: 2px;
border-color: $bg-color-light;
padding: 0;
font-weight: 700;
font-size: 0.7rem;
> div {
padding: 0.1rem 0.4rem;
min-width: -webkit-fill-available;
}
}
.td {
border-right: 1px solid;
border-bottom: 1px solid;
border-color: $bg-color-light;
padding: 0 0.4rem;
text-overflow: ellipsis;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
font-size: 0.7rem;
&:focus {
box-shadow: inset 0 0 0 1px $body-font-color;
background: rgba($color: #000, $alpha: 0.3);
outline: none;
}
}
}
}
}
</style>

View File

@@ -72,11 +72,11 @@ export default {
</script>
<style scoped>
.empty{
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
}
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -123,70 +123,70 @@ export default {
</script>
<style lang="scss">
.workspace-explorebar-resizer{
position: absolute;
width: 4px;
right: -2px;
top: 0;
height: calc(100vh - #{$excluding-size});
cursor: ew-resize;
z-index: 99;
}
.workspace-explorebar-resizer {
position: absolute;
width: 4px;
right: -2px;
top: 0;
height: calc(100vh - #{$excluding-size});
cursor: ew-resize;
z-index: 99;
}
.workspace-explorebar{
width: $explorebar-width;
.workspace-explorebar {
width: $explorebar-width;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
text-align: left;
background: $bg-color-gray;
box-shadow: 0 0 1px 0 #000;
z-index: 8;
flex: initial;
position: relative;
padding: 0;
.workspace-explorebar-header {
width: 100%;
padding: 0.3rem;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
text-align: left;
background: $bg-color-gray;
box-shadow: 0 0 1px 0px #000;
z-index: 8;
flex: initial;
position: relative;
padding: 0;
justify-content: space-between;
font-size: 0.6rem;
font-weight: 700;
text-transform: uppercase;
.workspace-explorebar-header{
width: 100%;
padding: .3rem;
display: flex;
justify-content: space-between;
font-size: .6rem;
font-weight: 700;
text-transform: uppercase;
.workspace-explorebar-title{
width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
align-items: center;
}
.workspace-explorebar-tools {
display: flex;
align-items: center;
> i{
opacity: .6;
transition: opacity .2s;
display: flex;
align-items: center;
&:hover{
opacity: 1;
}
}
}
.workspace-explorebar-title {
width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
align-items: center;
}
.workspace-explorebar-body{
width: 100%;
height: calc((100vh - 30px) - #{$excluding-size});
overflow: overlay;
padding: 0 .1rem;
.workspace-explorebar-tools {
display: flex;
align-items: center;
> i {
opacity: 0.6;
transition: opacity 0.2s;
display: flex;
align-items: center;
&:hover {
opacity: 1;
}
}
}
}
}
.workspace-explorebar-body {
width: 100%;
height: calc((100vh - 30px) - #{$excluding-size});
overflow: overlay;
padding: 0 0.1rem;
}
}
</style>

View File

@@ -56,35 +56,35 @@ export default {
</script>
<style lang="scss">
.workspace-explorebar-database{
.database-name,
a.table-name{
display: flex;
align-items: center;
padding: .1rem;
cursor: pointer;
font-size: .7rem;
.workspace-explorebar-database {
.database-name,
a.table-name {
display: flex;
align-items: center;
padding: 0.1rem;
cursor: pointer;
font-size: 0.7rem;
> span{
overflow: hidden;
white-space: nowrap;
display: block;
text-overflow: ellipsis;
}
&:hover{
color: $body-font-color;
background: rgba($color: #FFF, $alpha: .05);
border-radius: 2px;
}
> span {
overflow: hidden;
white-space: nowrap;
display: block;
text-overflow: ellipsis;
}
.menu-item{
line-height: 1.2;
&:hover {
color: $body-font-color;
background: rgba($color: #fff, $alpha: 0.05);
border-radius: 2px;
}
}
.database-tables{
margin-left: 1.2rem;
}
}
.menu-item {
line-height: 1.2;
}
.database-tables {
margin-left: 1.2rem;
}
}
</style>

View File

@@ -8,15 +8,11 @@
class="btn btn-link btn-sm"
:class="{'loading':isQuering}"
:disabled="!query"
@click="runQuery"
@click="runQuery(query)"
>
<span>{{ $t('word.run') }}</span>
<i class="material-icons text-success">play_arrow</i>
</button>
<!-- <button class="btn btn-link btn-sm">
<span>{{ $t('word.save') }}</span>
<i class="material-icons ml-1">save</i>
</button> -->
</div>
<div class="workspace-query-info">
<div v-if="results.rows">
@@ -33,7 +29,7 @@
v-if="results"
ref="queryTable"
:results="results"
:fields="resultsFields"
:fields="fields"
@updateField="updateField"
@deleteSelected="deleteSelected"
/>
@@ -62,6 +58,7 @@ export default {
data () {
return {
query: '',
lastQuery: '',
isQuering: false,
results: {},
fields: []
@@ -74,25 +71,6 @@ export default {
workspace () {
return this.getWorkspace(this.connection.uid);
},
resultsFields () {
if (this.results) {
return this.fields.map(field => { // TODO: move to main process
return {
name: field.COLUMN_NAME,
key: field.COLUMN_KEY.toLowerCase(),
type: field.DATA_TYPE,
precision: field.DATETIME_PRECISION
};
}).filter(field => {
if (this.results.fields) {
const queryFields = this.results.fields.map(field => field.name);
if (queryFields.includes(field.name)) return field;
}
});
}
else
return [];
},
table () {
if (this.results.fields.length)
return this.results.fields[0].orgTable;
@@ -103,15 +81,15 @@ export default {
...mapActions({
addNotification: 'notifications/addNotification'
}),
async runQuery () {
if (!this.query) return;
async runQuery (query) {
if (!query) return;
this.isQuering = true;
this.results = {};
try {
const params = {
uid: this.connection.uid,
query: this.query,
query,
schema: this.workspace.breadcrumbs.schema
};
@@ -134,7 +112,7 @@ export default {
const { status, response } = await Tables.getTableColumns(params);
if (status === 'success')
this.fields = response.rows;
this.fields = response;
else
this.addNotification({ status: 'error', message: response });
}
@@ -143,43 +121,46 @@ export default {
}
this.isQuering = false;
this.lastQuery = query;
},
reloadTable () {
this.runQuery(this.lastQuery);
}
}
};
</script>
<style lang="scss">
.workspace-tabs{
align-content: baseline;
.workspace-tabs {
align-content: baseline;
.workspace-query-runner{
.workspace-query-runner {
.workspace-query-runner-footer {
display: flex;
justify-content: space-between;
padding: 0.3rem 0.6rem 0.4rem;
align-items: center;
.workspace-query-runner-footer{
display: flex;
justify-content: space-between;
padding: .3rem .6rem .4rem;
align-items: center;
.workspace-query-buttons {
display: flex;
.workspace-query-buttons{
display: flex;
.btn{
display: flex;
align-self: center;
color: $body-font-color;
margin-right: .4rem;
}
}
.workspace-query-info{
display: flex;
> div + div{
padding-left: .6rem;
}
}
.btn {
display: flex;
align-self: center;
color: $body-font-color;
margin-right: 0.4rem;
}
}
}
.workspace-query-info {
display: flex;
> div + div {
padding-left: 0.6rem;
}
}
}
}
}
</style>

View File

@@ -66,7 +66,7 @@
</template>
<script>
import { uidGen } from 'common/libs/utilities';
import { uidGen } from 'common/libs/uidGen';
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
import WorkspaceQueryTableCell from '@/components/WorkspaceQueryTableCell';
import TableContext from '@/components/WorkspaceQueryTableContext';
@@ -269,46 +269,46 @@ export default {
<style lang="scss">
.vscroll {
height: 1000px;
overflow: auto;
overflow-anchor: none;
height: 1000px;
overflow: auto;
overflow-anchor: none;
}
.column-resizable{
&:hover,
&:active{
resize: horizontal;
overflow: hidden;
}
.column-resizable {
&:hover,
&:active {
resize: horizontal;
overflow: hidden;
}
}
.table-column-title{
display: flex;
align-items: center;
.table-column-title {
display: flex;
align-items: center;
}
.sort-icon{
font-size: .7rem;
line-height: 1;
margin-left: .2rem;
.sort-icon {
font-size: 0.7rem;
line-height: 1;
margin-left: 0.2rem;
}
.column-key{
transform: rotate(90deg);
font-size: .7rem;
line-height: 1.5;
margin-right: .2rem;
.column-key {
transform: rotate(90deg);
font-size: 0.7rem;
line-height: 1.5;
margin-right: 0.2rem;
&.key-pri{
color: goldenrod;
}
&.key-pri {
color: goldenrod;
}
&.key-uni{
color: deepskyblue;
}
&.key-uni {
color: deepskyblue;
}
&.key-mul{
color: palegreen;
}
&.key-mul {
color: palegreen;
}
}
</style>

View File

@@ -2,15 +2,14 @@
<div
v-if="field !== '_id'"
ref="cell"
class="td"
:class="`type-${type} p-0`"
class="td p-0"
tabindex="0"
@contextmenu.prevent="$emit('contextmenu', $event)"
>
<span
v-if="!isEditing"
v-if="!isInlineEditor"
class="cell-content px-2"
:class="isNull(content)"
:class="`${isNull(content)} type-${type}`"
@dblclick="editON"
>{{ content | typeFormat(type, precision) | cutText }}</span>
<template v-else>
@@ -34,18 +33,102 @@
@blur="editOFF"
>
</template>
<ConfirmModal
v-if="isTextareaEditor"
:confirm-text="$t('word.update')"
size="medium"
@confirm="editOFF"
@hide="hideEditorModal"
>
<template :slot="'header'">
{{ $t('word.edit') }} "{{ field }}"
</template>
<div :slot="'body'">
<div class="mb-2">
<div>
<textarea
v-model="localContent"
class="form-input textarea-editor"
/>
</div>
<div class="editor-field-info">
<div><b>{{ $t('word.size') }}</b>: {{ localContent.length }}</div>
<div><b>{{ $t('word.type') }}</b>: {{ type.toUpperCase() }}</div>
</div>
</div>
</div>
</ConfirmModal>
<ConfirmModal
v-if="isBlobEditor"
:confirm-text="$t('word.update')"
@confirm="editOFF"
@hide="hideEditorModal"
>
<template :slot="'header'">
{{ $t('word.edit') }} "{{ field }}"
</template>
<div :slot="'body'">
<div class="mb-2">
<transition name="jump-down">
<div v-if="contentInfo.size">
<img
v-if="isImage"
:src="`data:${contentInfo.mime};base64, ${bufferToBase64(localContent)}`"
class="img-responsive p-centered bg-checkered"
>
<div v-else class="text-center">
<i class="material-icons md-36">insert_drive_file</i>
</div>
<div class="editor-buttons mt-2">
<button class="btn btn-link btn-sm" @click="downloadFile">
<span>{{ $t('word.download') }}</span>
<i class="material-icons ml-1">file_download</i>
</button>
<button class="btn btn-link btn-sm" @click="prepareToDelete">
<span>{{ $t('word.delete') }}</span>
<i class="material-icons ml-1">delete_forever</i>
</button>
</div>
</div>
</transition>
<div class="editor-field-info">
<div>
<b>{{ $t('word.size') }}</b>: {{ localContent.length | formatBytes }}<br>
<b>{{ $t('word.mimeType') }}</b>: {{ contentInfo.mime }}
</div>
<div><b>{{ $t('word.type') }}</b>: {{ type.toUpperCase() }}</div>
</div>
<div class="mt-3">
<label>{{ $t('message.uploadFile') }}</label>
<input
class="form-input"
type="file"
@change="filesChange($event)"
>
</div>
</div>
</div>
</ConfirmModal>
</div>
</template>
<script>
import moment from 'moment';
import { mimeFromHex, formatBytes } from 'common/libs/utilities';
import { mimeFromHex } from 'common/libs/mimeFromHex';
import { formatBytes } from 'common/libs/formatBytes';
import { bufferToBase64 } from 'common/libs/bufferToBase64';
import hexToBinary from 'common/libs/hexToBinary';
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
import { mask } from 'vue-the-mask';
import ConfirmModal from '@/components/BaseConfirmModal';
export default {
name: 'WorkspaceQueryTableCell',
components: {
ConfirmModal
},
filters: {
formatBytes,
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
@@ -53,39 +136,31 @@ export default {
typeFormat (val, type, precision) {
if (!val) return val;
switch (type) {
case 'char':
case 'varchar':
case 'text':
case 'mediumtext':
return val;
case 'date': {
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
}
case 'datetime':
case 'timestamp': {
let datePrecision = '';
for (let i = 0; i < precision; i++)
datePrecision += i === 0 ? '.S' : 'S';
if (DATE.includes(type))
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
}
case 'blob':
case 'mediumblob':
case 'longblob': {
const buff = Buffer.from(val);
if (!buff.length) return '';
if (DATETIME.includes(type)) {
let datePrecision = '';
for (let i = 0; i < precision; i++)
datePrecision += i === 0 ? '.S' : 'S';
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
}
case 'bit': {
const hex = Buffer.from(val).toString('hex');
return hexToBinary(hex);
}
default:
return val;
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
}
if (BLOB.includes(type)) {
const buff = Buffer.from(val);
if (!buff.length) return '';
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
}
if (BIT.includes(type)) {
const hex = Buffer.from(val).toString('hex');
return hexToBinary(hex);
}
return val;
}
},
directives: {
@@ -99,81 +174,188 @@ export default {
},
data () {
return {
isEditing: false,
localContent: ''
isInlineEditor: false,
isTextareaEditor: false,
isBlobEditor: false,
willBeDeleted: false,
localContent: null,
contentInfo: {
ext: '',
mime: '',
size: null
},
fileToUpload: null
};
},
computed: {
inputProps () {
switch (this.type) {
case 'char':
case 'varchar':
case 'text':
case 'mediumtext':
return { type: 'text', mask: false };
case 'int':
case 'tinyint':
case 'smallint':
case 'mediumint':
return { type: 'number', mask: false };
case 'date':
return { type: 'text', mask: '####-##-##' };
case 'datetime':
case 'timestamp': {
let datetimeMask = '####-##-## ##:##:##';
for (let i = 0; i < this.precision; i++)
datetimeMask += i === 0 ? '.#' : '#';
return { type: 'text', mask: datetimeMask };
}
case 'blob':
case 'mediumblob':
case 'longblob':
case 'bit':
return { type: 'file', mask: false };
default:
return 'hidden';
if ([...TEXT, ...LONG_TEXT].includes(this.type))
return { type: 'text', mask: false };
if (NUMBER.includes(this.type))
return { type: 'number', mask: false };
if (TIME.includes(this.type))
return { type: 'number', mask: false };
if (DATE.includes(this.type))
return { type: 'text', mask: '####-##-##' };
if (DATETIME.includes(this.type)) {
let datetimeMask = '####-##-## ##:##:##';
for (let i = 0; i < this.precision; i++)
datetimeMask += i === 0 ? '.#' : '#';
return { type: 'text', mask: datetimeMask };
}
if (BLOB.includes(this.type))
return { type: 'file', mask: false };
if (BIT.includes(this.type))
return { type: 'text', mask: false };
return { type: 'text', mask: false };
},
isImage () {
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
}
},
methods: {
isNull (value) {
return value === null ? ' is-null' : '';
},
bufferToBase64 (val) {
return bufferToBase64(val);
},
editON () {
if (['file'].includes(this.inputProps.type)) return;// TODO: remove temporary file block
if (LONG_TEXT.includes(this.type)) {
this.isTextareaEditor = true;
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
return;
}
this.$nextTick(() => {
if (BLOB.includes(this.type)) {
this.isBlobEditor = true;
this.localContent = this.content ? this.content : '';
this.fileToUpload = null;
this.willBeDeleted = false;
if (this.content !== null) {
const buff = Buffer.from(this.localContent);
if (buff.length) {
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
const { ext, mime } = mimeFromHex(hex);
this.contentInfo = {
ext,
mime,
size: this.localContent.length
};
}
}
return;
}
// Inline editable fields
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
this.$nextTick(() => { // Focus on input
this.$refs.cell.blur();
this.$nextTick(() => this.$refs.editField.focus());
});
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
this.isEditing = true;
this.isInlineEditor = true;
},
editOFF () {
this.isEditing = false;
if (this.localContent === this.$options.filters.typeFormat(this.content, this.type)) return;
this.isInlineEditor = false;
let content;
if (!['blob', 'mediumblob', 'longblob'].includes(this.type)) {
if (this.localContent === this.$options.filters.typeFormat(this.content, this.type)) return;// If not changed
content = this.localContent;
}
else { // Handle file upload
if (this.willBeDeleted) {
content = '';
this.willBeDeleted = false;
}
else {
if (!this.fileToUpload) return;
content = this.fileToUpload.file.path;
}
}
const { field, type, localContent: content } = this;
this.$emit('updateField', { field, type, content });
this.$emit('updateField', {
field: this.field,
type: this.type,
content
});
},
hideEditorModal () {
this.isTextareaEditor = false;
this.isBlobEditor = false;
},
downloadFile () {
const downloadLink = document.createElement('a');
downloadLink.href = `data:${this.contentInfo.mime};base64, ${bufferToBase64(this.localContent)}`;
downloadLink.setAttribute('download', `${this.field}.${this.contentInfo.ext}`);
document.body.appendChild(downloadLink);
downloadLink.click();
downloadLink.remove();
},
filesChange (event) {
const { files } = event.target;
if (!files.length) return;
this.fileToUpload = { name: files[0].name, file: files[0] };
this.willBeDeleted = false;
},
prepareToDelete () {
this.localContent = '';
this.contentInfo = {
ext: '',
mime: '',
size: null
};
this.willBeDeleted = true;
}
}
};
</script>
<style lang="scss">
.editable-field{
margin: 0;
border: none;
line-height: 1;
width: 100%;
.editable-field {
margin: 0;
border: none;
line-height: 1;
width: 100%;
}
.cell-content{
display: block;
min-height: .8rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.cell-content {
display: block;
min-height: 0.8rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.textarea-editor {
height: 50vh !important;
}
.editor-field-info {
margin-top: 0.6rem;
display: flex;
justify-content: space-between;
white-space: normal;
}
.editor-buttons {
display: flex;
justify-content: space-evenly;
.btn {
display: flex;
align-items: center;
}
}
</style>

View File

@@ -9,7 +9,7 @@
<ConfirmModal
v-if="isConfirmModal"
@confirm="deleteRows()"
@confirm="deleteRows"
@hide="hideConfirmModal"
>
<template :slot="'header'">
@@ -67,7 +67,3 @@ export default {
}
};
</script>
<style>
</style>

View File

@@ -6,15 +6,19 @@
<button
class="btn btn-link btn-sm"
:class="{'loading':isQuering}"
@click="getTableData"
@click="reloadTable"
>
<span>{{ $t('word.refresh') }}</span>
<i class="material-icons ml-1">refresh</i>
</button>
<!-- <button class="btn btn-link btn-sm">
<span>{{ $t('word.save') }}</span>
<i class="material-icons ml-1">save</i>
</button> -->
<button
class="btn btn-link btn-sm"
:class="{'disabled':isQuering}"
@click="showAddModal"
>
<span>{{ $t('word.add') }}</span>
<i class="material-icons ml-1">playlist_add</i>
</button>
</div>
<div class="workspace-query-info">
<div v-if="results.rows">
@@ -31,7 +35,7 @@
v-if="results"
ref="queryTable"
:results="results"
:fields="resultsFields"
:fields="fields"
@updateField="updateField"
@deleteSelected="deleteSelected"
/>
@@ -72,16 +76,6 @@ export default {
},
isSelected () {
return this.workspace.selected_tab === 1;
},
resultsFields () {
return this.fields.map(field => { // TODO: move to main process
return {
name: field.COLUMN_NAME,
key: field.COLUMN_KEY.toLowerCase(),
type: field.DATA_TYPE,
precision: field.DATETIME_PRECISION
};
});
}
},
watch: {
@@ -119,7 +113,7 @@ export default {
try {
const { status, response } = await Tables.getTableColumns(params);
if (status === 'success')
this.fields = response.rows;
this.fields = response;
else
this.addNotification({ status: 'error', message: response });
}
@@ -140,43 +134,46 @@ export default {
}
this.isQuering = false;
}
},
reloadTable () {
this.getTableData();
},
showAddModal () {}
}
};
</script>
<style lang="scss">
.workspace-tabs{
align-content: baseline;
.workspace-tabs {
align-content: baseline;
.workspace-query-runner{
.workspace-query-runner {
.workspace-query-runner-footer {
display: flex;
justify-content: space-between;
padding: 0.3rem 0.6rem 0.4rem;
align-items: center;
.workspace-query-runner-footer{
display: flex;
justify-content: space-between;
padding: .3rem .6rem .4rem;
align-items: center;
.workspace-query-buttons {
display: flex;
.workspace-query-buttons{
display: flex;
.btn{
display: flex;
align-self: center;
color: $body-font-color;
margin-right: .4rem;
}
}
.workspace-query-info{
display: flex;
> div + div{
padding-left: .6rem;
}
}
.btn {
display: flex;
align-self: center;
color: $body-font-color;
margin-right: 0.4rem;
}
}
}
.workspace-query-info {
display: flex;
> div + div {
padding-left: 0.6rem;
}
}
}
}
}
</style>

View File

@@ -29,7 +29,13 @@ module.exports = {
donate: 'Donate',
run: 'Run',
schema: 'Schema',
results: 'Results'
results: 'Results',
size: 'Size',
seconds: 'Seconds',
type: 'Type',
mimeType: 'Mime-Type',
download: 'Download',
add: 'Add'
},
message: {
appWelcome: 'Welcome to Antares SQL Client!',
@@ -55,7 +61,9 @@ module.exports = {
unableEditFieldWithoutPrimary: 'Unable to edit a field without a primary key in resultset',
editCell: 'Edit cell',
deleteRows: 'Delete row | Delete {count} rows',
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?'
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?',
notificationsTimeout: 'Notifications timeout',
uploadFile: 'Upload file'
},
// Date and Time
short: {

View File

@@ -27,7 +27,9 @@ module.exports = {
language: 'Lingua',
version: 'Versione',
donate: 'Dona',
run: 'Esegui'
run: 'Esegui',
schema: 'Schema',
results: 'Results'
},
message: {
appWelcome: 'Benvenuto in Antares SQL Client!',
@@ -41,7 +43,19 @@ module.exports = {
deleteConnection: 'Elimina connessione',
deleteConnectionCorfirm: 'Confermi l\'eliminazione di',
connectionSuccessfullyMade: 'Connessione avvenuta con successo!',
madeWithJS: 'Fatto con 💛 e JavaScript!'
madeWithJS: 'Fatto con 💛 e JavaScript!',
checkForUpdates: 'Cerca aggiornamenti',
noUpdatesAvailable: 'Nessun aggiornamento disponibile',
checkingForUpdate: 'Controllo aggiornamenti in corso',
checkFailure: 'Controllo fallito, riprova più tardi',
updateAvailable: 'Aggiornamento disponibile',
downloadingUpdate: 'Download dell\'aggiornamento',
updateDownloaded: 'Aggiornamento scaricato',
restartToInstall: 'Riavvia Antares per installare l\'aggiornamento',
unableEditFieldWithoutPrimary: 'Impossibile modificare il campo senza una primary key nel resultset',
editCell: 'Modifica cella',
deleteRows: 'Elimina riga | Elimina {count} righe',
confirmToDeleteRows: 'Confermi di voler cancellare una riga? | Confermi di voler cancellare {count} righe?'
},
// Date and Time
short: {

View File

@@ -3,6 +3,8 @@ import Tables from '@/ipc-api/Tables';
export default {
methods: {
async updateField (payload) {
this.isQuering = true;
const params = {
uid: this.connection.uid,
schema: this.workspace.breadcrumbs.schema,
@@ -12,16 +14,24 @@ export default {
try {
const { status, response } = await Tables.updateTableCell(params);
if (status === 'success')
this.$refs.queryTable.applyUpdate(payload);
if (status === 'success') {
if (response.reload)// Needed for blob fields
this.reloadTable();
else
this.$refs.queryTable.applyUpdate(payload);
}
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.isQuering = false;
},
async deleteSelected (payload) {
this.isQuering = true;
const params = {
uid: this.connection.uid,
schema: this.workspace.breadcrumbs.schema,
@@ -42,6 +52,8 @@ export default {
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.isQuering = false;
}
}
};

View File

@@ -1,43 +1,46 @@
@mixin type-colors($types) {
@each $type, $color in $types {
.type-#{$type} {
color: $color;
@each $type, $color in $types {
.type-#{$type} {
color: $color;
@if $type == "number" {
text-align: right;
}
@if $type == "number" {
text-align: right;
}
}
}
}
}
@include type-colors(
(
"char": seagreen,
"varchar": seagreen,
"text": seagreen,
"mediumtext": seagreen,
"longtext": seagreen,
"int": cornflowerblue,
"tinyint": cornflowerblue,
"smallint": cornflowerblue,
"mediumint": cornflowerblue,
"bigint": cornflowerblue,
"datetime": coral,
"date": coral,
"time": coral,
"timestamp": coral,
"bit": lightskyblue,
"blob": darkorchid,
"mediumblob": darkorchid,
"longblob": darkorchid,
"unknown": gray,
)
(
"char": seagreen,
"varchar": seagreen,
"text": seagreen,
"mediumtext": seagreen,
"longtext": seagreen,
"int": cornflowerblue,
"tinyint": cornflowerblue,
"smallint": cornflowerblue,
"mediumint": cornflowerblue,
"float": cornflowerblue,
"double": cornflowerblue,
"decimal": cornflowerblue,
"bigint": cornflowerblue,
"datetime": coral,
"date": coral,
"time": coral,
"timestamp": coral,
"bit": lightskyblue,
"blob": darkorchid,
"mediumblob": darkorchid,
"longblob": darkorchid,
"unknown": gray,
)
);
.is-null {
color: gray;
color: gray;
&::after {
content: "NULL";
}
&::after {
content: "NULL";
}
}

View File

@@ -1,26 +1,26 @@
.dbi {
display: inline-block;
width: 42px;
height: 42px;
background-size: cover;
display: inline-block;
width: 42px;
height: 42px;
background-size: cover;
&.dbi-mysql {
background-image: url("../images/svg/mysql.svg");
}
&.dbi-mysql {
background-image: url("../images/svg/mysql.svg");
}
&.dbi-maria {
background-image: url("../images/svg/mariadb.svg");
}
&.dbi-maria {
background-image: url("../images/svg/mariadb.svg");
}
&.dbi-mssql {
background-image: url("../images/svg/mssql.svg");
}
&.dbi-mssql {
background-image: url("../images/svg/mssql.svg");
}
&.dbi-pg {
background-image: url("../images/svg/pg.svg");
}
&.dbi-pg {
background-image: url("../images/svg/pg.svg");
}
&.dbi-oracledb {
background-image: url("../images/svg/oracledb.svg");
}
&.dbi-oracledb {
background-image: url("../images/svg/oracledb.svg");
}
}

View File

@@ -5,12 +5,35 @@
display: table;
table-layout: fixed;
&.table-striped {
.tbody {
.tr:nth-of-type(odd) {
background: $bg-color;
}
}
.tbody {
display: table-row-group;
}
.tr {
display: table-row;
}
// Scollable tables
&.table-scroll {
display: block;
overflow-x: auto;
padding-bottom: 0.75rem;
white-space: nowrap;
}
.thead {
display: table-header-group;
}
.td,
.th {
border-bottom: $border-width solid $border-color;
padding: $unit-3 $unit-2;
display: table-cell;
}
.th {
border-bottom-width: $border-width-lg;
}
&,
@@ -20,6 +43,7 @@
&.selected {
background: #333 !important;
}
&.active {
background: $bg-color-dark;
}
@@ -37,33 +61,11 @@
}
}
// Scollable tables
&.table-scroll {
display: block;
overflow-x: auto;
padding-bottom: 0.75rem;
white-space: nowrap;
}
.thead {
display: table-header-group;
}
.tbody {
display: table-row-group;
}
.tr {
display: table-row;
}
.td,
.th {
border-bottom: $border-width solid $border-color;
padding: $unit-3 $unit-2;
display: table-cell;
}
.th {
border-bottom-width: $border-width-lg;
&.table-striped {
.tbody {
.tr:nth-of-type(odd) {
background: $bg-color;
}
}
}
}

View File

@@ -1,16 +1,38 @@
.material-icons {
// TODO: rewrite with rem
.material-icons{// TODO: rewrite with rem
/* Rules for sizing the icon. */
&.md-18 { font-size: 18px; }
&.md-24 { font-size: 24px; }
&.md-36 { font-size: 36px; }
&.md-48 { font-size: 48px; }
/* Rules for using icons as black on a light background. */
&.md-dark { color: rgba(0, 0, 0, 0.54); }
&.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
/* Rules for using icons as white on a dark background. */
&.md-light { color: rgba(255, 255, 255, 1); }
&.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
}
/* Rules for sizing the icon. */
&.md-18 {
font-size: 18px;
}
&.md-24 {
font-size: 24px;
}
&.md-36 {
font-size: 36px;
}
&.md-48 {
font-size: 48px;
}
/* Rules for using icons as black on a light background. */
&.md-dark {
color: rgba(0, 0, 0, 0.54);
}
&.md-dark.md-inactive {
color: rgba(0, 0, 0, 0.26);
}
/* Rules for using icons as white on a dark background. */
&.md-light {
color: rgba(255, 255, 255, 1);
}
&.md-light.md-inactive {
color: rgba(255, 255, 255, 0.3);
}
}

View File

@@ -1,11 +1,41 @@
.slide-fade-enter-active {
transition: all 0.3s ease;
}
.slide-fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.jump-down-enter-active {
animation: jump-down-in 0.2s;
}
.jump-down-leave-active {
animation: jump-down-in 0.2s reverse;
}
@keyframes jump-down-in {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}

View File

@@ -1,4 +1,4 @@
/*Colors*/
/* Colors */
$body-bg: #1d1d1d;
$body-font-color: #fff;
$bg-color: #1d1d1d;
@@ -9,9 +9,9 @@ $success-color: #32b643;
$error-color: #de3b28;
$warning-color: #e0a40c;
/*Sizes*/
/* Sizes */
$titlebar-height: 1.5rem;
$settingbar-width: 3rem;
$explorebar-width: 14rem;
$footer-height: 1.5rem;
$excluding-size: $footer-height + $titlebar-height;
$excluding-size: $footer-height + $titlebar-height;

View File

@@ -9,109 +9,119 @@
@import "~spectre.css/src/spectre-exp";
body {
user-select: none;
user-select: none;
}
/*Additions*/
/* Additions */
@include margin-variant(3, $unit-3);
@include margin-variant(4, $unit-4);
@include padding-variant(3, $unit-3);
@include padding-variant(4, $unit-4);
.btn.btn-gray {
color: #fff;
background: $bg-color-gray;
color: #fff;
background: $bg-color-gray;
&:hover {
background: $bg-color;
}
&:hover {
background: $bg-color;
}
}
.p-vcentered {
display: flex !important;
align-items: center;
display: flex !important;
align-items: center;
}
.c-help {
cursor: help;
cursor: help;
}
.bg-checkered {
background-image:
linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
linear-gradient(to right, black 50%, white 50%),
linear-gradient(to bottom, black 50%, white 50%);
background-blend-mode: normal, difference, normal;
background-size: 2em 2em;
}
// Scrollbars
::-webkit-scrollbar {
width: 10px;
height: 10px;
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: $bg-color-light;
background: $bg-color-light;
}
::-webkit-scrollbar-thumb {
background: rgba($color: #fff, $alpha: 0.5);
background: rgba($color: #fff, $alpha: 0.5);
&:hover {
background: rgba($color: #fff, $alpha: 1);
}
&:hover {
background: rgba($color: #fff, $alpha: 1);
}
}
// Animations
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
.rotate {
animation: rotation 0.8s infinite linear;
animation: rotation 0.8s infinite linear;
}
/*Override*/
/* Override */
.modal {
.modal-overlay,
&.active .modal-overlay {
background: rgba(255, 255, 255, 0.15);
}
.modal-overlay,
&.active .modal-overlay {
background: rgba(255, 255, 255, 0.15);
}
.modal-sm .modal-container,
.modal-container {
box-shadow: 0 0 1px 0px #000;
padding: 0;
background: $bg-color;
.modal-container,
.modal-sm .modal-container {
box-shadow: 0 0 1px 0 #000;
padding: 0;
background: $bg-color;
.modal-header {
padding: 0.4rem 0.8rem;
text-transform: uppercase;
background: $bg-color-gray;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
}
}
.modal-header {
padding: 0.4rem 0.8rem;
text-transform: uppercase;
background: $bg-color-gray;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
}
}
}
.tab {
border-color: #272727;
border-color: #272727;
}
.panel {
border: none;
border: none;
}
.badge {
&[data-badge],
&:not([data-badge]) {
&::after {
box-shadow: none;
}
}
&[data-badge],
&:not([data-badge]) {
&::after {
box-shadow: none;
}
}
}
.form-select {
cursor: pointer;
cursor: pointer;
}
.form-select,
@@ -119,30 +129,35 @@ body {
.form-input,
.form-checkbox .form-icon,
.form-radio .form-icon {
border-color: $bg-color-light;
background: $bg-color-gray;
border-color: $bg-color-light;
background: $bg-color-gray;
}
.form-select:not([multiple]):not([size]):focus {
border-color: $primary-color;
border-color: $primary-color;
}
.input-group .input-group-addon {
border-color: #3f3f3f;
}
.menu {
font-size: 0.7rem;
.menu-item {
+ .menu-item {
margin-top: 0;
}
}
font-size: 0.7rem;
.menu-item {
+ .menu-item {
margin-top: 0;
}
}
}
.accordion-body {
max-height: 500rem !important;
max-height: 500rem !important;
}
.btn.loading {
> .material-icons,
> span {
visibility: hidden;
}
> .material-icons,
> span {
visibility: hidden;
}
}

View File

@@ -1,5 +1,5 @@
'use strict';
import { uidGen } from 'common/libs/utilities';
import { uidGen } from 'common/libs/uidGen';
export default {
namespaced: true,

View File

@@ -6,17 +6,22 @@ export default {
strict: true,
state: {
locale: 'en-US',
explorebar_size: null
explorebar_size: null,
notifications_timeout: 10
},
getters: {
getLocale: state => state.locale,
getExplorebarSize: state => state.explorebar_size
getExplorebarSize: state => state.explorebar_size,
getNotificationsTimeout: state => state.notifications_timeout
},
mutations: {
SET_LOCALE (state, locale) {
state.locale = locale;
i18n.locale = locale;
},
SET_NOTIFICATIONS_TIMEOUT (state, timeout) {
state.notifications_timeout = timeout;
},
SET_EXPLOREBAR_SIZE (state, size) {
state.explorebar_size = size;
}
@@ -25,6 +30,9 @@ export default {
changeLocale ({ commit }, locale) {
commit('SET_LOCALE', locale);
},
updateNotificationsTimeout ({ commit }, timeout) {
commit('SET_NOTIFICATIONS_TIMEOUT', timeout);
},
changeExplorebarSize ({ commit }, size) {
commit('SET_EXPLOREBAR_SIZE', size);
}

View File

@@ -1,6 +1,6 @@
'use strict';
import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/utilities';
import { uidGen } from 'common/libs/uidGen';
function remapStructure (structure) {
const databases = structure.map(table => table.TABLE_SCHEMA)