Skip to content

Commit

Permalink
moved code for selecting/executing the action as far as possible to A…
Browse files Browse the repository at this point in the history
…ctionBox.ts
  • Loading branch information
annekekleppe committed Oct 1, 2024
1 parent dbe0da5 commit 9798e83
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 135 deletions.
15 changes: 7 additions & 8 deletions packages/core-svelte/src/lib/components/TextComponent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
inputElement.selectionStart = myHelper.from >= 0 ? myHelper.from : 0;
inputElement.selectionEnd = myHelper.to >= 0 ? myHelper.to : 0;
inputElement.focus();
if (partOfDropdown) dispatcher('showDropdown');
}
};
Expand Down Expand Up @@ -132,12 +133,13 @@
* @param event
*/
function onClick(event: MouseEvent) {
LOGGER.log('onClick: ')
if (!!inputElement) {
LOGGER.log('onClick: ' + id + ', ' + inputElement?.selectionStart + ", " + inputElement?.selectionEnd);
myHelper.setFromAndTo(inputElement.selectionStart, inputElement.selectionEnd);
}
if (partOfDropdown) { // let TextDropdownComponent know, dropdown menu needs to be altered
console.log(`textUpdate from onClick`)
LOGGER.log(`textUpdate from onClick`)
dispatcher('showDropdown')
dispatcher('textUpdate', {content: text, caret: myHelper.from});
}
Expand Down Expand Up @@ -265,10 +267,7 @@
LOGGER.log(`${id}: onFocusOut `+ " partof:" + partOfDropdown + " isEditing:" + isEditing)
if (!partOfDropdown && isEditing) {
endEditing();
} else {
// let TextDropdownComponent handle it
dispatcher("onFocusOutText")
}
} // else Let TextDropdownComponent handle it. The event will bubble up.
}
const refresh = () => {
Expand Down Expand Up @@ -308,14 +307,14 @@
inputElement.focus();
editStart = false;
} else if (isEditing) {
// console.log(`check deleteWhenEmpty, caret: ${myHelper.from}-${myHelper.to}, text: "${text}", empty:${myHelper.isTextEmpty()}, deleteWhenEmpty: ${box.deleteWhenEmpty}`)
// LOGGER.log(`check deleteWhenEmpty, caret: ${myHelper.from}-${myHelper.to}, text: "${text}", empty:${myHelper.isTextEmpty()}, deleteWhenEmpty: ${box.deleteWhenEmpty}`)
if (myHelper.isTextEmpty() && box.deleteWhenEmpty) { // the text is completely empty, and we may delete the node
console.log("Deleting box")
LOGGER.log("Deleting box")
dispatcher('hideDropdown');
editor.deleteBox(box);
} else if (partOfDropdown) {
// send event to parent TextDropdownComponent
console.log(`${id}: dispatching textUpdateFunction with text ` + text + ' from afterUpdate');
LOGGER.log(`${id}: dispatching textUpdateFunction with text ` + text + ' from afterUpdate');
dispatcher('textUpdate', {content: text, caret: myHelper.from});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,16 @@
ARROW_DOWN,
ARROW_UP,
ENTER,
ESCAPE,
FreEditor,
FreLogger,
type FrePostAction,
isActionBox,
isRegExp,
isSelectBox,
isReferenceBox,
type SelectOption,
TextBox,
triggerTypeToString
BehaviorExecutionResult
} from "@freon4dsl/core"
import { runInAction } from "mobx"
import { afterUpdate, onMount } from "svelte";
const LOGGER = new FreLogger("TextDropdownComponent"); // .mute(); muting done through webapp/logging/LoggerSettings
Expand Down Expand Up @@ -71,7 +67,7 @@
if (!!textComponent) {
textComponent.setFocus();
} else {
console.error('TextDropdownComponent ' + id + ' has no textComponent' )
LOGGER.error('TextDropdownComponent ' + id + ' has no textComponent' )
}
}
Expand Down Expand Up @@ -110,49 +106,10 @@
});
const triggerKeyPressEvent = (key: string) => {
box.textHelper.setText(key)
allOptions = getOptions();
filteredOptions = allOptions.filter(o => o.label.startsWith(key))
makeFilteredOptionsUnique();
// Only one option and has been fully typed in, use this option without waiting for the ENTER key
// console.log(`textUpdate: (${filteredOptions.length}, ${filteredOptions[0]?.label}, ${filteredOptions[0]?.label?.length}`)
if (filteredOptions.length === 1 && filteredOptions[0].label === key && filteredOptions[0].label.length === 1 ) {
storeOrExecute(filteredOptions[0])
return
}
if (isActionBox(box)) {
// Try to match a regular expression, and execute the action that is associated with it
matchRegExpAndExecuteAction();
}
}
function matchRegExpAndExecuteAction() {
// Try to match a regular expression
const matchingOption = box.getOptions(editor).find(option => {
if (isRegExp(option.action.trigger)) {
if (option.action.trigger.test(text)) {
LOGGER.log("Matched regexp" + triggerTypeToString(option.action.trigger) + " for '" + text + "'")
return true
}
return false
}
})
// If there is a match, execute it and stop editing.
if (!!matchingOption) {
LOGGER.log(`Found match to regexp: ${matchingOption.label}`)
// isn't this equal to BehaviourUtil.executeBehavior
let execResult: FrePostAction = null;
runInAction(() => {
runInAction(() => {
const command = matchingOption.action.command();
execResult = command.execute(box,text, editor, 0);
});
if (!!execResult) {
execResult();
}
})
endEditing();
}
// allOptions = getOptions();
// filteredOptions = allOptions.filter(o => o.label.startsWith(key))
// makeFilteredOptionsUnique();
// dropdownShown = true;
}
/**
Expand All @@ -163,12 +120,12 @@
* @param event
*/
const textUpdate = (event: CustomEvent) => {
console.log(`textUpdate for ${box.kind}: ` + JSON.stringify(event.detail) + ", start: "+ text.substring(0, event.detail.caret));
LOGGER.log(`textUpdate for ${box.kind}: ` + JSON.stringify(event.detail) + ", start: "+ text.substring(0, event.detail.caret));
allOptions = getOptions();
filteredOptions = allOptions.filter(o => o.label.startsWith(text.substring(0, event.detail.caret)));
makeFilteredOptionsUnique();
// Only one option and has been fully typed in, use this option without waiting for the ENTER key
// console.log(`textUpdate: (${filteredOptions.length}, ${filteredOptions[0]?.label}, ${filteredOptions[0]?.label?.length}`)
LOGGER.log(`textUpdate: (${filteredOptions.length}, ${filteredOptions[0]?.label}, ${filteredOptions[0]?.label?.length}`)
if (filteredOptions.length === 1 && filteredOptions[0].label === text && filteredOptions[0].label.length === event.detail.caret ) {
event.preventDefault()
event.stopPropagation()
Expand All @@ -177,7 +134,8 @@
}
if (isActionBox(box)) {
// Try to match a regular expression, and execute the action that is associated with it
matchRegExpAndExecuteAction();
const result = box.tryToMatchRegExpAndExecuteAction(text, editor);
if (result === BehaviorExecutionResult.EXECUTED) endEditing();
}
};
Expand Down Expand Up @@ -232,7 +190,7 @@
* @param event
*/
const onKeyDown = (event: KeyboardEvent) => {
console.log("XX onKeyDown: " + id + " [" + event.key + "] alt [" + event.altKey + "] shift [" + event.shiftKey + "] ctrl [" + event.ctrlKey + "] meta [" + event.metaKey + "]" + ", selectedId: " + selectedId + " dropdown:" + dropdownShown + " editing:" + isEditing);
LOGGER.log("XX onKeyDown: " + id + " [" + event.key + "] alt [" + event.altKey + "] shift [" + event.shiftKey + "] ctrl [" + event.ctrlKey + "] meta [" + event.metaKey + "]" + ", selectedId: " + selectedId + " dropdown:" + dropdownShown + " editing:" + isEditing);
if (dropdownShown) {
if (!event.ctrlKey && !event.altKey) {
switch (event.key) {
Expand Down Expand Up @@ -375,7 +333,7 @@
isEditing = false;
dropdownShown = false;
box.selectOption(editor, selected); // TODO the result of the execution is ignored
box.executeOption(editor, selected); // TODO the result of the execution is ignored
if (isActionBox(box)) { // ActionBox, action done, clear input text
setTextLocalAndInBox('');
}
Expand Down Expand Up @@ -409,19 +367,16 @@
};
const onBlur = () => {
// We use on:blur instead of on:focusout, because when the user selects an item in the dropdown list,
// the text component will trigger a focus out event. The focus out event from the text component
// always comes before the click in the dropdown. If we react to focus out by endEditing(), any click
// on the dropdown list will have no effect.
LOGGER.log("onBlur " + id);
if (!document.hasFocus() || !$selectedBoxes.includes(box)) {
endEditing();
}
};
const onFocusOutText = () => {
LOGGER.log(`onFocusOutText ${id} text '${text}'`);
if (isEditing) {
endEditing();
}
};
/**
* The "click_outside" event was triggered because of `use:clickOutsideConditional`.
*/
Expand Down Expand Up @@ -468,7 +423,6 @@
on:showDropdown={showDropdown}
on:startEditing={startEditing}
on:endEditing={endEditing}
on:onFocusOutText={onFocusOutText}
/>
{#if isReferenceBox(box) && box.isSelectAble()}
<button class="reference-button" id="{id}" on:click={(event) => selectReferred(event)}>
Expand All @@ -477,9 +431,9 @@
{/if}
{#if dropdownShown}
<DropdownComponent
bind:selectedId={selectedId}
bind:options={filteredOptions}
on:freItemSelected={itemSelected}/>
bind:selectedId={selectedId}
bind:options={filteredOptions}
on:freItemSelected={itemSelected}/>
{/if}
</span>

Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {
BACKSPACE,
BehaviorExecutionResult,
FreCaret,
FreEditor,
FreErrorSeverity,
FreLogger,
isActionBox,
TextBox,
} from "@freon4dsl/core";
import { EventDispatcher } from "svelte";
import { executeCustomKeyboardShortCut } from "./CommonFunctions.js";
import {EventDispatcher} from "svelte";
import {executeCustomKeyboardShortCut} from "./CommonFunctions.js";

const LOGGER = new FreLogger("TextComponentHelper").mute();

Expand Down Expand Up @@ -106,27 +107,28 @@ export class TextComponentHelper {
}

handleGoToPrevious(event: KeyboardEvent, editor: FreEditor, htmlId: string) {
editor.selectPreviousLeaf();
this._endEditing();
editor.selectPreviousLeafIncludingExpressionPreOrPost();
LOGGER.log(htmlId + " PREVIOUS LEAF IS " + editor.selectedBox.role);
if (isActionBox(editor.selectedBox)) {
LOGGER.log(" is an action box");
editor.selectedBox.triggerKeyPressEvent(event.key);
editor.selectedBox.setCaret(FreCaret.RIGHT_MOST)
const executionResult: BehaviorExecutionResult = editor.selectedBox.tryToExecute(event.key, editor)
if (executionResult !== BehaviorExecutionResult.EXECUTED) {
editor.selectedBox.setCaret(FreCaret.LEFT_MOST, editor)
}
}
event.preventDefault();
event.stopPropagation();
}

handleGoToNext(event: KeyboardEvent, editor: FreEditor, htmlId: string) {
editor.selectNextLeaf();
LOGGER.log(htmlId + " NEXT LEAF IS " + editor.selectedBox.role);
this._endEditing();
editor.selectNextLeafIncludingExpressionPreOrPost();
console.log(htmlId + " NEXT LEAF IS " + editor.selectedBox.role);
if (isActionBox(editor.selectedBox)) {
// editor.selectedBox.execute(event.key)
// editor.selectedBox.setText(key)
LOGGER.log(" is an action box");
editor.selectedBox.triggerKeyPressEvent(event.key);
editor.selectedBox.setCaret(FreCaret.RIGHT_MOST)
editor.selectedCaretPosition = FreCaret.RIGHT_MOST
const executionResult: BehaviorExecutionResult = editor.selectedBox.tryToExecute(event.key, editor)
if (executionResult !== BehaviorExecutionResult.EXECUTED) {
editor.selectedBox.setCaret(FreCaret.RIGHT_MOST, editor)
}
}
event.preventDefault();
event.stopPropagation();
Expand Down Expand Up @@ -216,8 +218,8 @@ export class TextComponentHelper {
if (this._from !== this._getText().length) { // when the arrow key can stay within the text, do not let the parent handle it
event.stopPropagation();
// note: caret is set to one more because getCaretPosition is calculated before the event is executed
this._from =+ 1;
this._to =+ 1;
this._from += 1;
this._to += 1;
console.log(`textUpdate from handleArrowLeft`)
this._dispatcher('textUpdate', {content: this._getText(), caret: this._from});
} else { // the key will cause this element to lose focus, its content should be saved
Expand Down
52 changes: 41 additions & 11 deletions packages/core/src/editor/FreEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FreLogger } from "../logging";
import { FreAction } from "./actions";
import { Box, FreCombinedActions, FreCaret, FreProjectionHandler, wait, isTextBox, ElementBox } from "./index";
import { FreErrorSeverity } from "../validator";
import { isNullOrUndefined } from "../util";
import { isNullOrUndefined, isExpressionPreOrPost } from "../util";

const LOGGER = new FreLogger("FreEditor");

Expand Down Expand Up @@ -298,7 +298,7 @@ export class FreEditor {
if (!box.selectable) {
// get the ElementBox for the selected element
this._selectedBox = this.projection.getBox(box.node);
// console.log('box not selectable: ' + box.kind)
console.log('box not selectable: ' + box.kind)
} else {
this._selectedBox = box;
}
Expand All @@ -308,10 +308,6 @@ export class FreEditor {
// TODO Only needed when something actually changed
this.selectionChanged();
}
// console.log(`==> this._selectedElement = ${this._selectedElement.freId()}=${this._selectedElement.freLanguageConcept()};
// this._selectedBox = ${this._selectedBox.role} of kind ${this._selectedBox.kind};
// this._selectedIndex = ${this._selectedIndex};
// this._selectedProperty = ${this._selectedProperty};`);
}

selectParent() {
Expand Down Expand Up @@ -428,10 +424,16 @@ export class FreEditor {
* Sets the previous sibling of the currently selected box to be the selected box.
* TODO what if there is no previous sibling?
*/
selectPreviousLeaf() {
const previous: Box = this.selectedBox?.nextLeafLeft;
selectPreviousLeaf(box?: Box) {
if (isNullOrUndefined(box)) {
box = this._selectedBox
}
const previous: Box = box?.nextLeafLeft;
LOGGER.log("Select previous leaf is box " + previous?.role);
if (!!previous) {
if (isExpressionPreOrPost(previous)){
// Special expression prefix or postfix box, don't select it
this.selectPreviousLeaf(previous);
} else {
this.selectElementForBox(previous, FreCaret.RIGHT_MOST);
}
}
Expand All @@ -446,8 +448,36 @@ export class FreEditor {
if (isNullOrUndefined(box)) {
box = this._selectedBox
}
const next = box?.nextLeafRight;
LOGGER.log("Select next leaf is box " + next?.role);
const next: Box = box?.nextLeafRight;
console.log("Select next leaf is box " + next?.role);
if (!!next) {
if (isExpressionPreOrPost(next)){
// Special expression prefix or postfix box, don't select it
console.log(`selectNextleaf: skipping ${next.id} ${next.kind}`)
this.selectNextLeaf(next);
} else {
this.selectElementForBox(next, FreCaret.LEFT_MOST);
}
}
}

selectPreviousLeafIncludingExpressionPreOrPost(box?: Box) {
if (isNullOrUndefined(box)) {
box = this._selectedBox
}
const previous: Box = box?.nextLeafLeft;
LOGGER.log("Select previous leaf is box " + previous?.role);
if (!!previous) {
this.selectElementForBox(previous, FreCaret.RIGHT_MOST);
}
}

selectNextLeafIncludingExpressionPreOrPost(box?: Box) {
if (isNullOrUndefined(box)) {
box = this._selectedBox
}
const next: Box = box?.nextLeafRight;
console.log("Select next leaf is box " + next?.role);
if (!!next) {
this.selectElementForBox(next, FreCaret.LEFT_MOST);
}
Expand Down
Loading

0 comments on commit 9798e83

Please sign in to comment.