mirror of
https://github.com/beekeeper-studio/beekeeper-studio.git
synced 2026-03-13 10:12:54 +08:00
Merge pull request #2843 from beekeeper-studio/feat/changed-sign-json-viewer
Make changes in TableTable noticeable in JSON Viewer
This commit is contained in:
@@ -87,7 +87,6 @@ div.sidebar.detail-view-sidebar {
|
||||
|
||||
.CodeMirror.text-editor {
|
||||
width: 100%;
|
||||
padding-left: 0.65rem;
|
||||
|
||||
&, .CodeMirror-gutters {
|
||||
background-color: $theme-bg !important;
|
||||
|
||||
@@ -5,6 +5,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-foldgutter {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter-elt [class^=CodeMirror-foldgutter] {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter-background.changed {
|
||||
border-left: 2px solid $brand-warning;
|
||||
}
|
||||
|
||||
.CodeMirror-guttermaker-subtle {
|
||||
color: rgba($theme-base, 0.25);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
} from "@/lib/editor/vim";
|
||||
import { AppEvent } from "@/common/AppEvent";
|
||||
import { keymapTypes } from "@/lib/db/types"
|
||||
import { EditorMarker, LineGutter } from "@/lib/editor/utils";
|
||||
|
||||
interface InitializeOptions {
|
||||
userKeymap?: typeof keymapTypes[number]['value']
|
||||
@@ -75,6 +76,7 @@ export default {
|
||||
"bookmarks",
|
||||
"foldAll",
|
||||
"unfoldAll",
|
||||
"lineGutters",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@@ -82,6 +84,7 @@ export default {
|
||||
foundRootFold: false,
|
||||
bookmarkInstances: [],
|
||||
markInstances: [],
|
||||
activeLineGutters: [],
|
||||
wasEditorFocused: false,
|
||||
firstInitialization: true,
|
||||
};
|
||||
@@ -176,6 +179,9 @@ export default {
|
||||
bookmarks() {
|
||||
this.initializeBookmarks();
|
||||
},
|
||||
lineGutters() {
|
||||
this.initializeLineGutters();
|
||||
},
|
||||
foldAll() {
|
||||
CodeMirror.commands.foldAll(this.editor)
|
||||
},
|
||||
@@ -199,14 +205,27 @@ export default {
|
||||
async initialize(options: InitializeOptions = {}) {
|
||||
this.destroyEditor();
|
||||
|
||||
const gutters = []
|
||||
let foldGutter: { indicatorOpen: HTMLElement, indicatorFolded: HTMLElement };
|
||||
|
||||
const lineNumbers = this.lineNumbers ?? true
|
||||
if (lineNumbers) {
|
||||
gutters.push("CodeMirror-linenumbers")
|
||||
}
|
||||
if (this.foldGutter) {
|
||||
gutters.push("CodeMirror-foldgutter")
|
||||
}
|
||||
|
||||
const indicatorOpen = document.createElement("span");
|
||||
indicatorOpen.classList.add("foldgutter", "btn-fab", "open-close");
|
||||
indicatorOpen.classList.add("CodeMirror-foldgutter", "foldgutter", "btn-fab", "open-close");
|
||||
indicatorOpen.innerHTML = `<i class="dropdown-icon material-icons">keyboard_arrow_down</i>`;
|
||||
|
||||
const indicatorFolded = document.createElement("span");
|
||||
indicatorFolded.classList.add("foldgutter", "btn-fab", "open-close");
|
||||
indicatorFolded.classList.add("CodeMirror-foldgutter", "foldgutter", "btn-fab", "open-close");
|
||||
indicatorFolded.innerHTML = `<i class="dropdown-icon material-icons">keyboard_arrow_right</i>`;
|
||||
|
||||
foldGutter = { indicatorOpen, indicatorFolded };
|
||||
|
||||
const cm = CodeMirror.fromTextArea(this.$refs.editor, {
|
||||
autoRefresh: true,
|
||||
lineNumbers: this.lineNumbers ?? true,
|
||||
@@ -219,7 +238,6 @@ export default {
|
||||
[this.cmCtrlOrCmd("R")]: "replace",
|
||||
[this.cmCtrlOrCmd("Shift-R")]: "replaceAll",
|
||||
},
|
||||
// @ts-expect-error not fully typed
|
||||
options: {
|
||||
closeOnBlur: false,
|
||||
},
|
||||
@@ -228,10 +246,10 @@ export default {
|
||||
hintOptions: this.hintOptions,
|
||||
keyMap: options.userKeymap,
|
||||
getColumns: this.columnsGetter,
|
||||
...(this.foldGutter && {
|
||||
gutters: ["CodeMirror-linenumbers", { className: "CodeMirror-foldgutter", style: "width: 18px" }],
|
||||
foldGutter: { indicatorOpen, indicatorFolded },
|
||||
}),
|
||||
gutters,
|
||||
// @ts-expect-error not fully typed
|
||||
foldGutter,
|
||||
autoRefresh: true,
|
||||
// Remove JSON root key from folding
|
||||
...(this.removeJsonRootBrackets && {
|
||||
foldGutter: {
|
||||
@@ -361,11 +379,12 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
this.initializeMarkers();
|
||||
this.initializeBookmarks();
|
||||
this.initializeLineGutters();
|
||||
this.$emit("update:initialized", true);
|
||||
})
|
||||
},
|
||||
initializeMarkers() {
|
||||
const markers = this.markers || [];
|
||||
const markers: EditorMarker[] = this.markers || [];
|
||||
if (!this.editor) return;
|
||||
|
||||
// Cleanup existing bookmarks
|
||||
@@ -423,6 +442,21 @@ export default {
|
||||
this.bookmarkInstances.push(mark);
|
||||
}
|
||||
},
|
||||
initializeLineGutters() {
|
||||
const lineGutters = this.lineGutters || [];
|
||||
if (!this.editor) return;
|
||||
|
||||
// Cleanup existing line gutters
|
||||
this.activeLineGutters.forEach((lineGutter: LineGutter) => {
|
||||
this.editor.removeLineClass(lineGutter.line, "gutter", "changed");
|
||||
})
|
||||
this.activeLineGutters = []
|
||||
|
||||
lineGutters.forEach((lineGutter: LineGutter) => {
|
||||
this.editor.addLineClass(lineGutter.line, "gutter", "changed");
|
||||
this.activeLineGutters.push(lineGutter)
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
if (this.editor) {
|
||||
this.editor
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
:force-initizalize="reinitializeTextEditor + (reinitialize ?? 0)"
|
||||
:markers="markers"
|
||||
:plugins="textEditorPlugins"
|
||||
:line-gutters="lineGutters"
|
||||
:line-numbers="false"
|
||||
/>
|
||||
<div class="empty-state" v-show="empty">
|
||||
@@ -91,7 +92,7 @@ import {
|
||||
eachPaths,
|
||||
} from "@/lib/data/detail_view";
|
||||
import { mapGetters } from "vuex";
|
||||
import { EditorMarker } from "@/lib/editor/utils";
|
||||
import { EditorMarker, LineGutter } from "@/lib/editor/utils";
|
||||
import { persistJsonFold } from "@/lib/editor/plugins/persistJsonFold";
|
||||
import DetailViewSidebarUpsell from '@/components/upsell/DetailViewSidebarUpsell.vue'
|
||||
import rawLog from "@bksLogger";
|
||||
@@ -102,7 +103,7 @@ const log = rawLog.scope("detail-view-sidebar");
|
||||
|
||||
export default Vue.extend({
|
||||
components: { TextEditor, DetailViewSidebarUpsell },
|
||||
props: ["value", "hidden", "expandablePaths", "dataId", "title", "reinitialize"],
|
||||
props: ["value", "hidden", "expandablePaths", "dataId", "title", "reinitialize", "signs"],
|
||||
data() {
|
||||
return {
|
||||
reinitializeTextEditor: 0,
|
||||
@@ -235,6 +236,19 @@ export default Vue.extend({
|
||||
lines() {
|
||||
return this.text?.split("\n") || [];
|
||||
},
|
||||
lineGutters() {
|
||||
const lineGutters: LineGutter[] = []
|
||||
_.forEach(this.signs, (_i, key) => {
|
||||
const type = this.signs[key]
|
||||
const line = findKeyPosition(this.text, [key]);
|
||||
if (line === -1) {
|
||||
log.warn(`Failed to sign key \`${key}\`. \`${key}\` is not found.`)
|
||||
return
|
||||
}
|
||||
lineGutters.push({ line, type });
|
||||
})
|
||||
return lineGutters;
|
||||
},
|
||||
menuOptions() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
:hidden="!openDetailView"
|
||||
:expandable-paths="expandablePaths"
|
||||
:reinitialize="reinitializeDetailView"
|
||||
:signs="selectedRowDataSigns"
|
||||
@expandPath="expandForeignKey"
|
||||
@close="toggleOpenDetailView(false)"
|
||||
/>
|
||||
@@ -741,7 +742,17 @@ export default Vue.extend({
|
||||
label: createMenuItem("Open Column Filter"),
|
||||
action: this.showColumnFilterModal,
|
||||
}
|
||||
}
|
||||
},
|
||||
/** This tells which fields have been modified */
|
||||
selectedRowDataSigns() {
|
||||
const signs = {}
|
||||
for (const pendingUpdate of this.pendingChanges.updates) {
|
||||
if (pendingUpdate.rowIndex === this.selectedRowIndex) {
|
||||
signs[pendingUpdate.column] = "changed"
|
||||
}
|
||||
}
|
||||
return signs
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -1113,7 +1124,7 @@ export default Vue.extend({
|
||||
},
|
||||
buildPendingUpdates() {
|
||||
return this.pendingChanges.updates.map((update) => {
|
||||
return _.omit(update, ['key', 'oldValue', 'cell'])
|
||||
return _.omit(update, ['key', 'oldValue', 'cell', 'rowIndex'])
|
||||
});
|
||||
},
|
||||
buildPendingInserts() {
|
||||
@@ -1270,7 +1281,7 @@ export default Vue.extend({
|
||||
if (currentEdit) {
|
||||
currentEdit.value = cell.getValue()
|
||||
} else {
|
||||
const payload: TableUpdate & { key: string, oldValue: any, cell: any } = {
|
||||
const payload: TableUpdate & { key: string, oldValue: any, cell: any, rowIndex: number } = {
|
||||
key: key,
|
||||
table: this.table.name,
|
||||
schema: this.table.schema,
|
||||
@@ -1281,7 +1292,8 @@ export default Vue.extend({
|
||||
primaryKeys,
|
||||
oldValue: cell.getOldValue(),
|
||||
cell: cell,
|
||||
value: cell.getValue(0)
|
||||
value: cell.getValue(0),
|
||||
rowIndex: this.indexRowOf(cell.getRow())
|
||||
}
|
||||
// remove existing pending updates with identical pKey-column combo
|
||||
let pendingUpdates = _.reject(this.pendingChanges.updates, { 'key': payload.key })
|
||||
|
||||
@@ -10,6 +10,11 @@ export interface EditorMarker {
|
||||
type: "error" | "highlight" | "custom"; // | "warning"
|
||||
}
|
||||
|
||||
export interface LineGutter {
|
||||
line: number;
|
||||
type: "changed";
|
||||
}
|
||||
|
||||
export const plugins = {
|
||||
autoquote: CodeMirrorPlugins.registerAutoquote,
|
||||
autoComplete: CodeMirrorPlugins.registerAutoComplete,
|
||||
|
||||
Reference in New Issue
Block a user