Browse Source

wip: code snippet population

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/1950/head
Pranav C 3 years ago
parent
commit
93c6d1dff1
  1. 125
      packages/nc-gui/components/monaco/CustomMonacoEditor.js
  2. 99
      packages/nc-gui/components/project/spreadsheet/components/codeSnippet.vue
  3. 323
      packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue
  4. 17562
      packages/nc-gui/package-lock.json
  5. 2
      packages/nc-gui/package.json

125
packages/nc-gui/components/monaco/CustomMonacoEditor.js

@ -0,0 +1,125 @@
/* eslint-disable */
// import assign from "nano-assign";
// import sqlAutoCompletions from "./sqlAutoCompletions";
// import {ext} from "vee-validate/dist/rules.esm";
export default {
name: "CustomMonacoEditor",
props: {
value: {
default: "",
type: String
},
theme: {
type: String,
default: "vs-dark"
},
lang: {type:String, default: 'typescript'},
readOnly:Boolean
},
model: {
event: "change"
},
watch: {
value(newVal) {
if (newVal !== this.editor.getValue()) {
if (typeof newVal === 'object') {
this.editor.setValue(JSON.stringify(newVal, 0, 2));
} else {
this.editor.setValue(newVal);
}
}
}
},
mounted() {
this.$nextTick(() => {
if (this.amdRequire) {
this.amdRequire(["vs/editor/editor.main"], () => {
this.initMonaco(window.monaco);
});
} else {
// ESM format so it can't be resolved by commonjs `require` in eslint
// eslint-disable import/no-unresolved
const monaco = require("monaco-editor");
// monaco.editor.defineTheme('monokai', require('./Cobalt.json'))
// monaco.editor.setTheme('monokai')
this.monaco = monaco;
this.initMonaco(monaco);
}
});
},
unmounted() {
},
beforeDestroy() {
this.editor && this.editor.dispose();
},
methods: {
resizeLayout() {
this.editor.layout();
},
initMonaco(monaco) {
const code = this.value;
const model = monaco.editor.createModel(code, this.lang ||"json");
this.editor = monaco.editor.create(this.$el, {
model: model,
theme: this.theme,
});
this.editor.onDidChangeModelContent(event => {
const value = this.editor.getValue();
if (this.value !== value) {
this.$emit("change", value, event);
}
});
this.editor.updateOptions({ readOnly: this.readOnly })
},
getMonaco() {
return this.editor;
},
getMonacoModule() {
return this.monaco;
},
},
render(h) {
return h("div");
},
created() {
},
destroyed() {
}
};
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

99
packages/nc-gui/components/project/spreadsheet/components/codeSnippet.vue

@ -0,0 +1,99 @@
<template>
<v-dialog :value="true">
<v-card style="overflow: hidden">
<v-tabs v-model="tab" height="30">
<v-tab
v-for="{lang} in langs"
:key="lang"
class="caption"
>
{{ lang }}
</v-tab>
</v-tabs>
<div class="nc-snippet-wrapper">
<div class="nc-snippet-actions">
<v-icon>mdi-clipboard</v-icon>sdds
<v-select
v-if="langs[tab].clients"
v-model="client"
class="nc-snippet-client"
outlined
dense
hide-details
style="max-width: 100px"
/>
</div>
<custom-monaco-editor style="min-height:500px;max-width: 100%" :value="code" read-only />
</div>
</v-card>
</v-dialog>
</template>
<script>
import HTTPSnippet from 'httpsnippet'
import CustomMonacoEditor from '~/components/monaco/CustomMonacoEditor'
export default {
name: 'CodeSnippet',
components: { CustomMonacoEditor },
props: {
meta: Object,
view: Object,
filters: [Object, Array],
sorts: [Object, Array],
fileds: [Object, Array]
},
data: () => ({
tab: 0,
client: null,
langs: [
{
lang: 'javascript',
clients: ['XMLHttpRequest', 'jQuery.ajax']
},
{
lang: 'node'
},
{
lang: 'shell'
},
{
lang: 'php'
},
{
lang: 'python'
},
{
lang: 'ruby'
},
{
lang: 'c'
}
]
}),
computed: {
snippet() {
return new HTTPSnippet({
method: 'GET',
headers: [
{ name: 'xc-auth', value: this.$store.state.users.token, comment: 'JWT Auth token' }
],
url: new URL(`/api/v1/db/data/noco/${this.projectId}/${this.meta.title}/views/${this.view.title}`, this.$store.state.project.projectInfo.ncSiteUrl).href
})
},
code() {
return this.snippet.convert(this.langs[this.tab].lang, this.client)
}
}
}
</script>
<style scoped>
.nc-snippet-wrapper{
position:relative;
}
.nc-snippet-actions{
position: absolute;
right: 10px;
top:40px;
}
</style>

323
packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue

@ -61,7 +61,9 @@
>
{{ viewIcons[view.type].icon }}
</v-icon>
<v-icon v-else color="primary" small> mdi-table </v-icon>
<v-icon v-else color="primary" small>
mdi-table
</v-icon>
</v-list-item-icon>
<v-list-item-title>
<v-tooltip bottom>
@ -77,7 +79,7 @@
@click.stop
@keydown.enter.stop="updateViewName(view, i)"
@blur="updateViewName(view, i)"
/>
>
<template v-else>
<span v-on="on">{{
view.alias || view.title
@ -183,7 +185,9 @@
@click="openCreateViewDlg(viewTypes.GRID)"
>
<v-list-item-icon class="mr-n1">
<v-icon color="blue" x-small> mdi-grid-large </v-icon>
<v-icon color="blue" x-small>
mdi-grid-large
</v-icon>
</v-list-item-icon>
<v-list-item-title>
<span class="font-weight-regular">
@ -192,7 +196,9 @@
</span>
</v-list-item-title>
<v-spacer />
<v-icon class="mr-1" small> mdi-plus </v-icon>
<v-icon class="mr-1" small>
mdi-plus
</v-icon>
</v-list-item>
</template>
<!-- Add Grid View -->
@ -207,7 +213,9 @@
@click="openCreateViewDlg(viewTypes.GALLERY)"
>
<v-list-item-icon class="mr-n1">
<v-icon color="orange" x-small> mdi-camera-image </v-icon>
<v-icon color="orange" x-small>
mdi-camera-image
</v-icon>
</v-list-item-icon>
<v-list-item-title>
<span class="font-weight-regular">
@ -217,7 +225,9 @@
</v-list-item-title>
<v-spacer />
<v-icon class="mr-1" small> mdi-plus </v-icon>
<v-icon class="mr-1" small>
mdi-plus
</v-icon>
</v-list-item>
</template>
<!-- Add Gallery View -->
@ -251,7 +261,9 @@
</v-list-item-title>
<v-spacer />
<v-icon class="mr-1" small> mdi-plus </v-icon>
<v-icon class="mr-1" small>
mdi-plus
</v-icon>
</v-list-item>
</template>
<!-- Add Form View -->
@ -261,6 +273,10 @@
</template>
</div>
<div>
<code-snippet :meta="meta" :view="selectedView" />
</div>
<div
v-if="time - $store.state.windows.miniSponsorCard > 15 * 60 * 1000"
class="pa-2 sponsor-wrapper"
@ -270,10 +286,10 @@
</v-icon>
<!-- <extras />-->
<v-divider/>
<extras class="pl-1"/>
<v-divider />
<extras class="pl-1" />
<!-- <sponsor-mini nav />-->
<!-- <sponsor-mini nav />-->
</div>
<!--<div class="text-center">
<v-hover >
@ -490,17 +506,18 @@
</template>
<script>
import draggable from "vuedraggable";
import { ViewTypes } from "nocodb-sdk";
import CreateViewDialog from "@/components/project/spreadsheet/dialog/createViewDialog";
import Extras from "~/components/project/spreadsheet/components/extras";
import viewIcons from "~/helpers/viewIcons";
import { copyTextToClipboard } from "~/helpers/xutils";
import SponsorMini from "~/components/sponsorMini";
import draggable from 'vuedraggable'
import { ViewTypes } from 'nocodb-sdk'
import CreateViewDialog from '@/components/project/spreadsheet/dialog/createViewDialog'
import Extras from '~/components/project/spreadsheet/components/extras'
import viewIcons from '~/helpers/viewIcons'
import { copyTextToClipboard } from '~/helpers/xutils'
import SponsorMini from '~/components/sponsorMini'
import CodeSnippet from '~/components/project/spreadsheet/components/codeSnippet'
export default {
name: "SpreadsheetNavDrawer",
components: { SponsorMini, Extras, CreateViewDialog, draggable },
name: 'SpreadsheetNavDrawer',
components: { CodeSnippet, SponsorMini, Extras, CreateViewDialog, draggable },
props: {
extraViewParams: Object,
showAdvanceOptions: Boolean,
@ -509,7 +526,7 @@ export default {
primaryValueColumn: [Number, String],
toggleDrawer: {
type: Boolean,
default: false,
default: false
},
nodes: Object,
table: String,
@ -523,7 +540,7 @@ export default {
sortList: [Object, Array],
load: {
default: true,
type: Boolean,
type: Boolean
},
currentApiUrl: String,
fieldsOrder: Array,
@ -532,23 +549,23 @@ export default {
// coverImageField: String,
groupingField: String,
// showSystemFields: Boolean,
views: Array,
views: Array
},
data: () => ({
drag: false,
dragOptions: {
animation: 200,
group: "description",
group: 'description',
disabled: false,
ghostClass: "ghost",
ghostClass: 'ghost'
},
time: Date.now(),
sponsorMiniVisible: true,
enableDummyFeat: false,
searchQueryVal: "",
searchQueryVal: '',
showShareLinkPassword: false,
passwordProtect: false,
sharedViewPassword: "",
sharedViewPassword: '',
overAdvShieldIcon: false,
overShieldIcon: false,
viewIcons,
@ -558,120 +575,120 @@ export default {
showCreateView: false,
loading: false,
viewTypeAlias: {
[ViewTypes.GRID]: "grid",
[ViewTypes.FORM]: "form",
[ViewTypes.GALLERY]: "gallery",
},
[ViewTypes.GRID]: 'grid',
[ViewTypes.FORM]: 'form',
[ViewTypes.GALLERY]: 'gallery'
}
}),
computed: {
viewsList: {
set(v) {
this.$emit("update:views", v);
this.$emit('update:views', v)
},
get() {
return this.views;
},
return this.views
}
},
viewTypes() {
return ViewTypes;
return ViewTypes
},
newViewParams() {
if (!this.showFields) {
return {};
return {}
}
const showFields = { ...this.showFields };
const showFields = { ...this.showFields }
Object.keys(showFields).forEach((k) => {
showFields[k] = true;
});
return { showFields };
showFields[k] = true
})
return { showFields }
},
selectedViewIdLocal: {
set(val) {
const view = (this.views || []).find((v) => v.id === val);
const view = (this.views || []).find(v => v.id === val)
this.$router.push({
query: {
...this.$route.query,
view: view && view.id,
},
});
view: view && view.id
}
})
},
get() {
let id;
let id
if (this.views) {
const view = this.views.find((v) => v.id === this.$route.query.view);
id = (view && view.id) || ((this.views && this.views[0]) || {}).id;
const view = this.views.find(v => v.id === this.$route.query.view)
id = (view && view.id) || ((this.views && this.views[0]) || {}).id
}
return id;
},
return id
}
},
sharedViewUrl() {
let viewType;
let viewType
switch (this.shareLink.type) {
case this.viewTypes.FORM:
viewType = "form";
break;
viewType = 'form'
break
case this.viewTypes.KANBAN:
viewType = "kanban";
break;
viewType = 'kanban'
break
default:
viewType = "view";
viewType = 'view'
}
return `${this.dashboardUrl}#/nc/${viewType}/${this.shareLink.uuid}`;
},
return `${this.dashboardUrl}#/nc/${viewType}/${this.shareLink.uuid}`
}
},
watch: {
async load(v) {
if (v) {
await this.loadViews();
this.onViewIdChange(this.selectedViewIdLocal);
await this.loadViews()
this.onViewIdChange(this.selectedViewIdLocal)
}
},
selectedViewIdLocal(id) {
this.onViewIdChange(id);
},
this.onViewIdChange(id)
}
},
async created() {
if (this.load) {
await this.loadViews();
await this.loadViews()
}
this.onViewIdChange(this.selectedViewIdLocal);
this.onViewIdChange(this.selectedViewIdLocal)
},
methods: {
async onMove(event) {
if (this.viewsList.length - 1 === event.moved.newIndex) {
this.$set(
this.viewsList[event.moved.newIndex],
"order",
'order',
this.viewsList[event.moved.newIndex - 1].order + 1
);
)
} else if (event.moved.newIndex === 0) {
this.$set(
this.viewsList[event.moved.newIndex],
"order",
'order',
this.viewsList[1].order / 2
);
)
} else {
this.$set(
this.viewsList[event.moved.newIndex],
"order",
'order',
(this.viewsList[event.moved.newIndex - 1].order +
this.viewsList[event.moved.newIndex + 1].order) /
2
);
)
}
await this.$api.dbView.update(this.viewsList[event.moved.newIndex].id, {
title: this.viewsList[event.moved.newIndex].title,
order: this.viewsList[event.moved.newIndex].order,
});
order: this.viewsList[event.moved.newIndex].order
})
this.$e("a:view:reorder");
this.$e('a:view:reorder')
},
onViewIdChange(id) {
const selectedView = this.views && this.views.find((v) => v.id === id);
const selectedView = this.views && this.views.find(v => v.id === id)
// const queryParams = {}
this.$emit("update:selectedViewId", id);
this.$emit("update:selectedView", selectedView);
this.$emit('update:selectedViewId', id)
this.$emit('update:selectedView', selectedView)
// if (selectedView.type === 'table') {
// return;
// }
@ -694,52 +711,52 @@ export default {
// } else {
// this.$emit('mapFieldsAndShowFields')
// }
this.$emit("loadTableData");
this.$emit('loadTableData')
},
hideMiniSponsorCard() {
this.$store.commit("windows/MutMiniSponsorCard", Date.now());
this.$store.commit('windows/MutMiniSponsorCard', Date.now())
},
openCreateViewDlg(type) {
const mainView = this.viewsList.find(
(v) => v.type === "table" || v.type === "view"
);
v => v.type === 'table' || v.type === 'view'
)
try {
this.copyViewRef = this.copyViewRef || {
query_params: JSON.stringify({
...this.newViewParams,
fieldsOrder: JSON.parse(mainView.query_params).fieldsOrder,
}),
};
fieldsOrder: JSON.parse(mainView.query_params).fieldsOrder
})
}
} catch {}
this.createViewType = type;
this.showCreateView = true;
this.$e("c:view:create", { view: type });
this.createViewType = type
this.showCreateView = true
this.$e('c:view:create', { view: type })
},
isCentrallyAligned(col) {
return ![
"SingleLineText",
"LongText",
"Attachment",
"Date",
"Time",
"Email",
"URL",
"DateTime",
"CreateTime",
"LastModifiedTime",
].includes(col.uidt);
'SingleLineText',
'LongText',
'Attachment',
'Date',
'Time',
'Email',
'URL',
'DateTime',
'CreateTime',
'LastModifiedTime'
].includes(col.uidt)
},
onPasswordProtectChange() {
if (!this.passwordProtect) {
this.shareLink.password = null;
this.saveShareLinkPassword();
this.shareLink.password = null
this.saveShareLinkPassword()
}
},
async saveShareLinkPassword() {
try {
await this.$api.dbViewShare.update(this.shareLink.id, {
password: this.shareLink.password,
});
password: this.shareLink.password
})
// await this.$store.dispatch('sqlMgr/ActSqlOp', [
// { dbAlias: this.nodes.dbAlias },
@ -749,14 +766,14 @@ export default {
// password: this.shareLink.password
// }
// ])
this.$toast.success("Successfully updated").goAway(3000);
this.$toast.success('Successfully updated').goAway(3000)
} catch (e) {
this.$toast
.error(await this._extractSdkResponseErrorMsg(e))
.goAway(3000);
.goAway(3000)
}
this.$e("a:view:share:enable-pwd");
this.$e('a:view:share:enable-pwd')
},
async loadViews() {
// this.viewsList = await this.sqlOp(
@ -772,8 +789,8 @@ export default {
// this.viewsList = []
const views = (await this.$api.dbView.list(this.meta.id)).list;
this.$emit("update:views", views);
const views = (await this.$api.dbView.list(this.meta.id)).list
this.$emit('update:views', views)
},
// async onViewChange() {
// let query_params = {}
@ -793,27 +810,27 @@ export default {
// this.$emit('loadTableData');
// },
copyapiUrlToClipboard() {
copyTextToClipboard(this.currentApiUrl);
this.clipboardSuccessHandler();
copyTextToClipboard(this.currentApiUrl)
this.clipboardSuccessHandler()
},
async updateViewName(view, index) {
if (!view.edit) {
return;
return
}
// const oldTitle = view.title
this.$set(view, "edit", false);
this.$set(view, 'edit', false)
if (view.title_temp === view.title) {
return;
return
}
if (
this.viewsList.some(
(v, i) => i !== index && (v.alias || v.title) === view.title_temp
)
) {
this.$toast.info("View name should be unique").goAway(3000);
return;
this.$toast.info('View name should be unique').goAway(3000)
return
}
try {
// if (this.selectedViewIdLocal === view.id) {
@ -824,39 +841,39 @@ export default {
// }
// })
// }
this.$set(view, "title", view.title_temp);
this.$set(view, 'title', view.title_temp)
await this.$api.dbView.update(view.id, {
title: view.title,
order: view.order,
});
this.$toast.success("View renamed successfully").goAway(3000);
order: view.order
})
this.$toast.success('View renamed successfully').goAway(3000)
} catch (e) {
this.$toast
.error(await this._extractSdkResponseErrorMsg(e))
.goAway(3000);
.goAway(3000)
}
},
showRenameTextBox(view, i) {
this.$set(view, "edit", true);
this.$set(view, "title_temp", view.title);
this.$set(view, 'edit', true)
this.$set(view, 'title_temp', view.title)
this.$nextTick(() => {
const input = this.$refs[`input${i}`][0];
input.focus();
input.setSelectionRange(0, input.value.length);
});
this.$e("c:view:rename", { view: view.type });
const input = this.$refs[`input${i}`][0]
input.focus()
input.setSelectionRange(0, input.value.length)
})
this.$e('c:view:rename', { view: view.type })
},
async deleteView(view) {
try {
await this.$api.dbView.delete(view.id);
this.$toast.success("View deleted successfully").goAway(3000);
await this.loadViews();
await this.$api.dbView.delete(view.id)
this.$toast.success('View deleted successfully').goAway(3000)
await this.loadViews()
} catch (e) {
this.$toast
.error(await this._extractSdkResponseErrorMsg(e))
.goAway(3000);
.goAway(3000)
}
this.$e("a:view:delete", { view: view.type });
this.$e('a:view:delete', { view: view.type })
},
async genShareLink() {
// const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [
@ -883,44 +900,44 @@ export default {
// password: this.sharedViewPassword
// }
// ])
const shared = await this.$api.dbViewShare.create(this.selectedViewId);
const shared = await this.$api.dbViewShare.create(this.selectedViewId)
// todo: url
this.shareLink = shared;
this.showShareModel = true;
this.shareLink = shared
this.showShareModel = true
},
copyView(view, i) {
this.createViewType = view.type;
this.showCreateView = true;
this.copyViewRef = view;
this.$e("c:view:copy", { view: view.type });
this.createViewType = view.type
this.showCreateView = true
this.copyViewRef = view
this.$e('c:view:copy', { view: view.type })
},
async onViewCreate(viewMeta) {
this.copyViewRef = null;
await this.loadViews();
this.selectedViewIdLocal = viewMeta.id;
this.copyViewRef = null
await this.loadViews()
this.selectedViewIdLocal = viewMeta.id
// await this.onViewChange();
this.$e("a:view:create", { view: viewMeta.type });
this.$e('a:view:create', { view: viewMeta.type })
},
clipboard(str) {
const el = document.createElement("textarea");
el.addEventListener("focusin", (e) => e.stopPropagation());
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
const el = document.createElement('textarea')
el.addEventListener('focusin', e => e.stopPropagation())
el.value = str
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
},
clipboardSuccessHandler() {
this.$toast.info("Copied to clipboard").goAway(1000);
this.$toast.info('Copied to clipboard').goAway(1000)
},
copyShareUrlToClipboard() {
this.clipboard(this.sharedViewUrl);
this.clipboardSuccessHandler();
this.$e("c:view:share:copy-url");
},
},
};
this.clipboard(this.sharedViewUrl)
this.clipboardSuccessHandler()
this.$e('c:view:share:copy-url')
}
}
}
</script>
<style scoped lang="scss">

17562
packages/nc-gui/package-lock.json generated

File diff suppressed because it is too large Load Diff

2
packages/nc-gui/package.json

@ -23,6 +23,7 @@
"debounce": "^1.2.0",
"file-saver": "^2.0.5",
"fix-path": "^3.0.0",
"httpsnippet": "^2.0.0",
"inflection": "^1.12.0",
"jsep": "^0.4.0",
"material-design-icons-iconfont": "^5.0.1",
@ -65,6 +66,7 @@
"@intlify/eslint-plugin-vue-i18n": "^0.11.1",
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/vuetify": "^1.11.2",
"@types/httpsnippet": "^1.23.1",
"babel-eslint": "^10.1.0",
"eslint": "^7.31.0",
"monaco-editor-webpack-plugin": "^1.9.1",

Loading…
Cancel
Save