add auto-highlighting of selected/updated code
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { IApplication } from './../../../domain/IApplication';
|
||||
import { ICategory, IScript } from '@/domain/ICategory';
|
||||
import { INode } from './SelectableTree/INode';
|
||||
import { INode, NodeType } from './SelectableTree/Node/INode';
|
||||
|
||||
export function parseAllCategories(app: IApplication): INode[] | undefined {
|
||||
const nodes = new Array<INode>();
|
||||
@@ -23,9 +23,15 @@ export function parseSingleCategory(categoryId: number, app: IApplication): INod
|
||||
export function getScriptNodeId(script: IScript): string {
|
||||
return script.id;
|
||||
}
|
||||
export function getScriptId(nodeId: string): string {
|
||||
return nodeId;
|
||||
}
|
||||
export function getCategoryId(nodeId: string): number {
|
||||
return +nodeId;
|
||||
}
|
||||
|
||||
export function getCategoryNodeId(category: ICategory): string {
|
||||
return `Category${category.id}`;
|
||||
return `${category.id}`;
|
||||
}
|
||||
|
||||
function parseCategoryRecursively(
|
||||
@@ -64,6 +70,7 @@ function convertCategoryToNode(
|
||||
category: ICategory, children: readonly INode[]): INode {
|
||||
return {
|
||||
id: getCategoryNodeId(category),
|
||||
type: NodeType.Category,
|
||||
text: category.name,
|
||||
children,
|
||||
documentationUrls: category.documentationUrls,
|
||||
@@ -74,6 +81,7 @@ function convertCategoryToNode(
|
||||
function convertScriptToNode(script: IScript): INode {
|
||||
return {
|
||||
id: getScriptNodeId(script),
|
||||
type: NodeType.Script,
|
||||
text: script.name,
|
||||
children: undefined,
|
||||
documentationUrls: script.documentationUrls,
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { IApplicationState, IUserSelection } from '@/application/State/IApplicationState';
|
||||
import { IFilterResult } from '@/application/State/Filter/IFilterResult';
|
||||
import { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId } from './ScriptNodeParser';
|
||||
import SelectableTree, { FilterPredicate } from './SelectableTree/SelectableTree.vue';
|
||||
import { INode } from './SelectableTree/INode';
|
||||
import { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId, getCategoryId, getScriptId } from './ScriptNodeParser';
|
||||
import SelectableTree from './SelectableTree/SelectableTree.vue';
|
||||
import { INode, NodeType } from './SelectableTree/Node/INode';
|
||||
import { SelectedScript } from '@/application/State/Selection/SelectedScript';
|
||||
import { INodeSelectedEvent } from './SelectableTree/INodeSelectedEvent';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -53,15 +54,17 @@
|
||||
await this.initializeNodesAsync(this.categoryId);
|
||||
}
|
||||
|
||||
public async toggleNodeSelectionAsync(node: INode) {
|
||||
if (node.children != null && node.children.length > 0) {
|
||||
return; // only interested in script nodes
|
||||
}
|
||||
public async toggleNodeSelectionAsync(event: INodeSelectedEvent) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
if (!this.selectedNodeIds.some((id) => id === node.id)) {
|
||||
state.selection.addSelectedScript(node.id, false);
|
||||
} else {
|
||||
state.selection.removeSelectedScript(node.id);
|
||||
switch (event.node.type) {
|
||||
case NodeType.Category:
|
||||
this.toggleCategoryNodeSelection(event, state);
|
||||
break;
|
||||
case NodeType.Script:
|
||||
this.toggleScriptNodeSelection(event, state);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown node type: ${event.node.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +100,24 @@
|
||||
this.filterText = result.query;
|
||||
this.filtered = result;
|
||||
}
|
||||
private toggleCategoryNodeSelection(event: INodeSelectedEvent, state: IApplicationState): void {
|
||||
const categoryId = getCategoryId(event.node.id);
|
||||
if (event.isSelected) {
|
||||
state.selection.addAllInCategory(categoryId);
|
||||
} else {
|
||||
state.selection.removeAllInCategory(categoryId);
|
||||
}
|
||||
}
|
||||
private toggleScriptNodeSelection(event: INodeSelectedEvent, state: IApplicationState): void {
|
||||
const scriptId = getScriptId(event.node.id);
|
||||
const actualToggleState = state.selection.isSelected(scriptId);
|
||||
const targetToggleState = event.isSelected;
|
||||
if (targetToggleState && !actualToggleState) {
|
||||
state.selection.addSelectedScript(scriptId, false);
|
||||
} else if (!targetToggleState && actualToggleState) {
|
||||
state.selection.removeSelectedScript(scriptId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { INode } from './Node/INode';
|
||||
|
||||
export interface INodeSelectedEvent {
|
||||
isSelected: boolean;
|
||||
node: INode;
|
||||
}
|
||||
73
src/presentation/Scripts/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts
vendored
Normal file
73
src/presentation/Scripts/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Two ways of typing other libraries: https://stackoverflow.com/a/53070501
|
||||
|
||||
declare module 'liquor-tree' {
|
||||
import { PluginObject } from 'vue';
|
||||
import { VueClass } from 'vue-class-component/lib/declarations';
|
||||
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/lib/Tree.js
|
||||
export interface ILiquorTree {
|
||||
readonly model: ReadonlyArray<ILiquorTreeExistingNode>;
|
||||
filter(query: string): void;
|
||||
clearFilter(): void;
|
||||
setModel(nodes: ReadonlyArray<ILiquorTreeNewNode>): void;
|
||||
}
|
||||
interface ICustomLiquorTreeData {
|
||||
type: number;
|
||||
documentationUrls: ReadonlyArray<string>;
|
||||
isReversible: boolean;
|
||||
}
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
export interface ILiquorTreeNodeState {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export interface ILiquorTreeNode {
|
||||
id: string;
|
||||
data: ICustomLiquorTreeData;
|
||||
children: ReadonlyArray<ILiquorTreeNode> | undefined;
|
||||
}
|
||||
/**
|
||||
* Returned from Node tree view events.
|
||||
* See constructor in https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
*/
|
||||
export interface ILiquorTreeExistingNode extends ILiquorTreeNode {
|
||||
data: ILiquorTreeNodeData;
|
||||
states: ILiquorTreeNodeState | undefined;
|
||||
children: ReadonlyArray<ILiquorTreeExistingNode> | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent to liquor tree to define of new nodes.
|
||||
* https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
*/
|
||||
export interface ILiquorTreeNewNode extends ILiquorTreeNode {
|
||||
text: string;
|
||||
state: ILiquorTreeNodeState | undefined;
|
||||
children: ReadonlyArray<ILiquorTreeNewNode> | undefined;
|
||||
}
|
||||
|
||||
// https://amsik.github.io/liquor-tree/#Component-Options
|
||||
export interface ILiquorTreeOptions {
|
||||
multiple: boolean;
|
||||
checkbox: boolean;
|
||||
checkOnSelect: boolean;
|
||||
autoCheckChildren: boolean;
|
||||
parentSelect: boolean;
|
||||
keyboardNavigation: boolean;
|
||||
filter: ILiquorTreeFilter;
|
||||
deletion(node: ILiquorTreeNode): boolean;
|
||||
}
|
||||
|
||||
interface ILiquorTreeNodeData extends ICustomLiquorTreeData {
|
||||
text: string;
|
||||
}
|
||||
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/components/TreeRoot.vue
|
||||
export interface ILiquorTreeFilter {
|
||||
emptyText: string;
|
||||
matcher(query: string, node: ILiquorTreeExistingNode): boolean;
|
||||
}
|
||||
|
||||
const LiquorTree: PluginObject<any> & VueClass<any>;
|
||||
export default LiquorTree;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ILiquorTreeOptions, ILiquorTreeFilter, ILiquorTreeNode } from 'liquor-tree';
|
||||
|
||||
export class LiquorTreeOptions implements ILiquorTreeOptions {
|
||||
public multiple = true;
|
||||
public checkbox = true;
|
||||
public checkOnSelect = true;
|
||||
/* For checkbox mode only. Children will have the same checked state as their parent.
|
||||
This is false as it's handled manually to be able to batch select for performance + highlighting */
|
||||
public autoCheckChildren = false;
|
||||
public parentSelect = false;
|
||||
public keyboardNavigation = true;
|
||||
constructor(public filter: ILiquorTreeFilter) { }
|
||||
public deletion(node: ILiquorTreeNode): boolean {
|
||||
return false; // no op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ILiquorTreeFilter, ILiquorTreeExistingNode } from 'liquor-tree';
|
||||
import { convertExistingToNode } from './NodeTranslator';
|
||||
import { INode } from '../Node/INode';
|
||||
|
||||
export type FilterPredicate = (node: INode) => boolean;
|
||||
|
||||
export class NodePredicateFilter implements ILiquorTreeFilter {
|
||||
public emptyText: string = '🕵️Hmm.. Can not see one 🧐';
|
||||
constructor(private readonly filterPredicate: FilterPredicate) {
|
||||
if (!filterPredicate) {
|
||||
throw new Error('filterPredicate is undefined');
|
||||
}
|
||||
}
|
||||
public matcher(query: string, node: ILiquorTreeExistingNode): boolean {
|
||||
return this.filterPredicate(convertExistingToNode(node));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { ILiquorTreeExistingNode, ILiquorTreeNewNode, ILiquorTreeNodeState, ILiquorTreeNode } from 'liquor-tree';
|
||||
import { NodeType } from './../Node/INode';
|
||||
|
||||
export function updateNodesCheckedState(
|
||||
oldNodes: ReadonlyArray<ILiquorTreeExistingNode>,
|
||||
selectedNodeIds: ReadonlyArray<string>): ReadonlyArray<ILiquorTreeNewNode> {
|
||||
const result = new Array<ILiquorTreeNewNode>();
|
||||
for (const oldNode of oldNodes) {
|
||||
const newState = oldNode.states;
|
||||
newState.checked = getNewCheckedState(oldNode, selectedNodeIds);
|
||||
const newNode: ILiquorTreeNewNode = {
|
||||
id: oldNode.id,
|
||||
text: oldNode.data.text,
|
||||
data: {
|
||||
type: oldNode.data.type,
|
||||
documentationUrls: oldNode.data.documentationUrls,
|
||||
isReversible: oldNode.data.isReversible,
|
||||
},
|
||||
children: !oldNode.children ? [] : updateNodesCheckedState(oldNode.children, selectedNodeIds),
|
||||
state: newState,
|
||||
};
|
||||
result.push(newNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getNewCheckedState(
|
||||
oldNode: ILiquorTreeNode,
|
||||
selectedNodeIds: ReadonlyArray<string>): boolean {
|
||||
switch (oldNode.data.type) {
|
||||
case NodeType.Script:
|
||||
return selectedNodeIds.some((id) => id === oldNode.id);
|
||||
case NodeType.Category:
|
||||
return parseAllSubScriptIds(oldNode).every((id) => selectedNodeIds.includes(id));
|
||||
default:
|
||||
throw new Error('Unknown node type');
|
||||
}
|
||||
}
|
||||
|
||||
function parseAllSubScriptIds(categoryNode: ILiquorTreeNode): ReadonlyArray<string> {
|
||||
if (categoryNode.data.type !== NodeType.Category) {
|
||||
throw new Error('Not a category node');
|
||||
}
|
||||
if (!categoryNode.children) {
|
||||
return [];
|
||||
}
|
||||
const ids = new Array<string>();
|
||||
for (const child of categoryNode.children) {
|
||||
addNodeIds(child, ids);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
function addNodeIds(node: ILiquorTreeNode, ids: string[]) {
|
||||
switch (node.data.type) {
|
||||
case NodeType.Script:
|
||||
ids.push(node.id);
|
||||
break;
|
||||
case NodeType.Category:
|
||||
const subCategoryIds = parseAllSubScriptIds(node);
|
||||
ids.push(...subCategoryIds);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown node type');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ILiquorTreeNewNode, ILiquorTreeExistingNode } from 'liquor-tree';
|
||||
import { INode } from './INode';
|
||||
import { INode } from './../Node/INode';
|
||||
|
||||
// Functions to translate INode to LiqourTree models and vice versa for anti-corruption
|
||||
|
||||
@@ -7,6 +7,7 @@ export function convertExistingToNode(liquorTreeNode: ILiquorTreeExistingNode):
|
||||
if (!liquorTreeNode) { throw new Error('liquorTreeNode is undefined'); }
|
||||
return {
|
||||
id: liquorTreeNode.id,
|
||||
type: liquorTreeNode.data.type,
|
||||
text: liquorTreeNode.data.text,
|
||||
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
||||
children: convertChildren(liquorTreeNode.children, convertExistingToNode),
|
||||
@@ -27,6 +28,7 @@ export function toNewLiquorTreeNode(node: INode): ILiquorTreeNewNode {
|
||||
data: {
|
||||
documentationUrls: node.documentationUrls,
|
||||
isReversible: node.isReversible,
|
||||
type: node.type,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
export enum NodeType {
|
||||
Script,
|
||||
Category,
|
||||
}
|
||||
|
||||
export interface INode {
|
||||
readonly id: string;
|
||||
readonly text: string;
|
||||
readonly isReversible: boolean;
|
||||
readonly documentationUrls: ReadonlyArray<string>;
|
||||
readonly children?: ReadonlyArray<INode>;
|
||||
readonly type: NodeType;
|
||||
}
|
||||
@@ -19,16 +19,19 @@
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Emit, Watch } from 'vue-property-decorator';
|
||||
import LiquorTree, { ILiquorTreeNewNode, ILiquorTreeExistingNode, ILiquorTree, ILiquorTreeOptions } from 'liquor-tree';
|
||||
import Node from './Node.vue';
|
||||
import { INode } from './INode';
|
||||
import { convertExistingToNode, toNewLiquorTreeNode } from './NodeTranslator';
|
||||
export type FilterPredicate = (node: INode) => boolean;
|
||||
import Node from './Node/Node.vue';
|
||||
import { INode, NodeType } from './Node/INode';
|
||||
import { convertExistingToNode, toNewLiquorTreeNode } from './LiquorTree/NodeTranslator';
|
||||
import { INodeSelectedEvent } from './/INodeSelectedEvent';
|
||||
import { updateNodesCheckedState, getNewCheckedState } from './LiquorTree/NodeStateUpdater';
|
||||
import { LiquorTreeOptions } from './LiquorTree/LiquorTreeOptions';
|
||||
import { FilterPredicate, NodePredicateFilter } from './LiquorTree/NodePredicateFilter';
|
||||
|
||||
/** Wrapper for Liquor Tree, reveals only abstracted INode for communication */
|
||||
@Component({
|
||||
components: {
|
||||
LiquorTree,
|
||||
Node,
|
||||
LiquorTree,
|
||||
Node,
|
||||
},
|
||||
})
|
||||
export default class SelectableTree extends Vue {
|
||||
@@ -38,7 +41,7 @@
|
||||
@Prop() public initialNodes?: ReadonlyArray<INode>;
|
||||
|
||||
public initialLiquourTreeNodes?: ILiquorTreeNewNode[] = null;
|
||||
public liquorTreeOptions = this.getDefaults();
|
||||
public liquorTreeOptions = new LiquorTreeOptions(new NodePredicateFilter((node) => this.filterPredicate(node)));
|
||||
public convertExistingToNode = convertExistingToNode;
|
||||
|
||||
public mounted() {
|
||||
@@ -46,7 +49,7 @@
|
||||
const initialNodes = this.initialNodes.map((node) => toNewLiquorTreeNode(node));
|
||||
if (this.selectedNodeIds) {
|
||||
recurseDown(initialNodes,
|
||||
(node) => node.state.checked = this.selectedNodeIds.includes(node.id));
|
||||
(node) => node.state.checked = getNewCheckedState(node, this.selectedNodeIds));
|
||||
}
|
||||
this.initialLiquourTreeNodes = initialNodes;
|
||||
} else {
|
||||
@@ -58,7 +61,11 @@
|
||||
}
|
||||
|
||||
public nodeSelected(node: ILiquorTreeExistingNode) {
|
||||
this.$emit('nodeSelected', convertExistingToNode(node));
|
||||
const event: INodeSelectedEvent = {
|
||||
node: convertExistingToNode(node),
|
||||
isSelected: node.states.checked,
|
||||
};
|
||||
this.$emit('nodeSelected', event);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,11 +80,11 @@
|
||||
}
|
||||
|
||||
@Watch('selectedNodeIds')
|
||||
public setSelectedStatus(selectedNodeIds: ReadonlyArray<string>) {
|
||||
public setSelectedStatusAsync(selectedNodeIds: ReadonlyArray<string>) {
|
||||
if (!selectedNodeIds) {
|
||||
throw new Error('Selected nodes are undefined');
|
||||
}
|
||||
const newNodes = updateCheckedState(this.getLiquorTreeApi().model, selectedNodeIds);
|
||||
const newNodes = updateNodesCheckedState(this.getLiquorTreeApi().model, selectedNodeIds);
|
||||
this.getLiquorTreeApi().setModel(newNodes);
|
||||
/* Alternative:
|
||||
this.getLiquorTreeApi().recurseDown((node) => {
|
||||
@@ -98,27 +105,6 @@
|
||||
}
|
||||
return (this.$refs.treeElement as any).tree;
|
||||
}
|
||||
|
||||
private getDefaults(): ILiquorTreeOptions {
|
||||
return {
|
||||
multiple: true,
|
||||
checkbox: true,
|
||||
checkOnSelect: true,
|
||||
autoCheckChildren: true,
|
||||
parentSelect: false,
|
||||
keyboardNavigation: true,
|
||||
deletion: (node) => !node.children || node.children.length === 0,
|
||||
filter: {
|
||||
matcher: (query: string, node: ILiquorTreeExistingNode) => {
|
||||
if (!this.filterPredicate) {
|
||||
throw new Error('Cannot filter as predicate is null');
|
||||
}
|
||||
return this.filterPredicate(convertExistingToNode(node));
|
||||
},
|
||||
emptyText: '🕵️Hmm.. Can not see one 🧐',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function recurseDown(
|
||||
@@ -131,27 +117,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateCheckedState(
|
||||
oldNodes: ReadonlyArray<ILiquorTreeExistingNode>,
|
||||
selectedNodeIds: ReadonlyArray<string>): ReadonlyArray<ILiquorTreeNewNode> {
|
||||
const result = new Array<ILiquorTreeNewNode>();
|
||||
for (const oldNode of oldNodes) {
|
||||
const newState = oldNode.states;
|
||||
newState.checked = selectedNodeIds.some((id) => id === oldNode.id);
|
||||
const newNode: ILiquorTreeNewNode = {
|
||||
id: oldNode.id,
|
||||
text: oldNode.data.text,
|
||||
data: {
|
||||
documentationUrls: oldNode.data.documentationUrls,
|
||||
isReversible: oldNode.data.isReversible,
|
||||
},
|
||||
children: oldNode.children == null ? [] :
|
||||
updateCheckedState(oldNode.children, selectedNodeIds),
|
||||
state: newState,
|
||||
};
|
||||
result.push(newNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,9 @@ import { Component, Prop, Watch, Vue } from 'vue-property-decorator';
|
||||
import { StatefulVue } from './StatefulVue';
|
||||
import ace from 'ace-builds';
|
||||
import 'ace-builds/webpack-resolver';
|
||||
import { CodeBuilder } from '../application/State/Code/CodeBuilder';
|
||||
import { CodeBuilder } from '@/application/State/Code/Generation/CodeBuilder';
|
||||
import { ICodeChangedEvent } from '@/application/State/Code/Event/ICodeChangedEvent';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
|
||||
const NothingChosenCode =
|
||||
new CodeBuilder()
|
||||
@@ -28,19 +30,65 @@ const NothingChosenCode =
|
||||
@Component
|
||||
export default class TheCodeArea extends StatefulVue {
|
||||
public readonly editorId = 'codeEditor';
|
||||
|
||||
private editor!: ace.Ace.Editor;
|
||||
private currentMarkerId?: number;
|
||||
|
||||
@Prop() private theme!: string;
|
||||
|
||||
public async mounted() {
|
||||
this.editor = initializeEditor(this.theme, this.editorId);
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.updateCode(state.code.current);
|
||||
this.editor.setValue(state.code.current || NothingChosenCode, 1);
|
||||
state.code.changed.on((code) => this.updateCode(code));
|
||||
}
|
||||
|
||||
private updateCode(code: string) {
|
||||
this.editor.setValue(code || NothingChosenCode, 1);
|
||||
private updateCode(event: ICodeChangedEvent) {
|
||||
this.removeCurrentHighlighting();
|
||||
if (event.isEmpty()) {
|
||||
this.editor.setValue(NothingChosenCode, 1);
|
||||
return;
|
||||
}
|
||||
this.editor.setValue(event.code, 1);
|
||||
|
||||
if (event.addedScripts && event.addedScripts.length) {
|
||||
this.reactToChanges(event, event.addedScripts);
|
||||
} else if (event.changedScripts && event.changedScripts.length) {
|
||||
this.reactToChanges(event, event.changedScripts);
|
||||
}
|
||||
}
|
||||
|
||||
private reactToChanges(event: ICodeChangedEvent, scripts: ReadonlyArray<IScript>) {
|
||||
const positions = scripts
|
||||
.map((script) => event.getScriptPositionInCode(script));
|
||||
const start = Math.min(
|
||||
...positions.map((position) => position.startLine),
|
||||
);
|
||||
const end = Math.max(
|
||||
...positions.map((position) => position.endLine),
|
||||
);
|
||||
this.scrollToLine(end + 2);
|
||||
this.highlight(start, end);
|
||||
}
|
||||
|
||||
private highlight(startRow: number, endRow: number) {
|
||||
const AceRange = ace.require('ace/range').Range;
|
||||
this.currentMarkerId = this.editor.session.addMarker(
|
||||
new AceRange(startRow, 0, endRow, 0), 'code-area__highlight', 'fullLine',
|
||||
);
|
||||
}
|
||||
|
||||
private scrollToLine(row: number) {
|
||||
const column = this.editor.session.getLine(row).length;
|
||||
this.editor.gotoLine(row, column, true);
|
||||
}
|
||||
|
||||
private removeCurrentHighlighting() {
|
||||
if (!this.currentMarkerId) {
|
||||
return;
|
||||
}
|
||||
this.editor.session.removeMarker(this.currentMarkerId);
|
||||
this.currentMarkerId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +106,16 @@ function initializeEditor(theme: string, editorId: string): ace.Ace.Editor {
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss">
|
||||
@import "@/presentation/styles/colors.scss";
|
||||
.code-area {
|
||||
/* ----- Fill its parent div ------ */
|
||||
width: 100%;
|
||||
/* height */
|
||||
max-height: 1000px;
|
||||
min-height: 200px;
|
||||
&__highlight {
|
||||
background-color:$accent;
|
||||
opacity: 20%;
|
||||
position:absolute;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.hasCode = state.code.current && state.code.current.length > 0;
|
||||
state.code.changed.on((code) => {
|
||||
this.hasCode = code && code.length > 0;
|
||||
this.hasCode = code && code.code.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user