Skip to content

Commit 6e84e4c

Browse files
authored
Merge pull request #258 from harunurhan/highlight-field
highlights field instead of flashing
2 parents 70c8ba4 + 3f1ac0e commit 6e84e4c

9 files changed

+56
-43
lines changed

src/json-editor.component.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ label {
163163
color: $label-color;
164164
}
165165

166-
.flash {
167-
border: 2px solid yellow;
166+
.highlight {
167+
border: 2px solid yellow !important;
168168
}
169169

170170
// FIXME: only used in table-list and table-list it's own version?

src/primitive-field/primitive-field.component.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
<tr [ngClass]="errorNgClass">
44
<ng-template #errorsTooltipTemplate>
55
<ul class="tooltip-left-align">
6-
<li *ngFor="let error of errors" >
6+
<li *ngFor="let error of errors">
77
{{error.message}}
88
</li>
99
</ul>
1010
</ng-template>
11-
<td class="value-container" [ngClass]="disabledClass" [tooltip]="errorsTooltipTemplate" [isDisabled]="!isErrorTooltipEnabled" placement="{{tooltipPosition}}" container="body">
11+
<td class="value-container" [ngClass]="disabledClass" [tooltip]="errorsTooltipTemplate" [isDisabled]="!isErrorTooltipEnabled"
12+
placement="{{tooltipPosition}}" container="body">
1213
<div *ngSwitchCase="'string'">
1314
<div [attr.contenteditable]="!disabled" attr.data-path="{{pathString}}" [tabindex]="tabIndex" [(contentModel)]="value" (blur)="commitValueChange()"
1415
(keypress)="onKeypress($event)" attr.placeholder="{{schema.title}}"></div>
1516
</div>
1617
<div *ngSwitchCase="'enum'">
1718
<searchable-dropdown [pathString]="pathString" [value]="value" [placeholder]="schema.title" [items]="schema.enum" [shortcutMap]="schema.enumShorcutMap"
18-
(onSelect)="onSearchableDropdownSelect($event)" [tabIndex]="tabIndex"></searchable-dropdown>
19+
(onSelect)="onSearchableDropdownSelect($event)" [tabIndex]="tabIndex" (onBlur)="domUtilService.clearHighlight()"></searchable-dropdown>
1920
</div>
2021
<div *ngSwitchCase="'autocomplete'">
2122
<autocomplete-input [pathString]="pathString" [value]="value" [path]="path" [autocompletionConfig]="schema.autocompletionConfig"

src/primitive-field/primitive-field.component.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ import {
3939
ComponentTypeService,
4040
SchemaValidationService,
4141
JsonStoreService,
42-
PathUtilService
42+
PathUtilService,
43+
DomUtilService,
44+
TabsUtilService
4345
} from '../shared/services';
4446
import { ContentModelDirective } from '../shared/directives';
4547

@@ -91,6 +93,8 @@ describe('PrimitiveFieldComponent', () => {
9193
ComponentTypeService,
9294
SchemaValidationService,
9395
PathUtilService,
96+
DomUtilService,
97+
TabsUtilService,
9498
{ provide: JsonStoreService, useClass: MockJsonStoreService }
9599
]
96100
}).compileComponents();

src/primitive-field/primitive-field.component.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import {
3434
ComponentTypeService,
3535
JsonStoreService,
3636
SchemaValidationService,
37-
PathUtilService
37+
PathUtilService,
38+
DomUtilService
3839
} from '../shared/services';
3940
import { JSONSchema } from '../shared/interfaces';
4041

@@ -58,7 +59,9 @@ export class PrimitiveFieldComponent extends AbstractFieldComponent implements O
5859
public componentTypeService: ComponentTypeService,
5960
public appGlobalsService: AppGlobalsService,
6061
public jsonStoreService: JsonStoreService,
61-
public pathUtilService: PathUtilService) {
62+
public pathUtilService: PathUtilService,
63+
public domUtilService: DomUtilService
64+
) {
6265
super(appGlobalsService, pathUtilService);
6366
}
6467

@@ -71,7 +74,8 @@ export class PrimitiveFieldComponent extends AbstractFieldComponent implements O
7174
}
7275

7376
commitValueChange() {
74-
// Validation
77+
this.domUtilService.clearHighlight();
78+
7579
let errors = this.schemaValidationService.validateValue(this.value, this.schema);
7680
if (!errors.length) {
7781
this.jsonStoreService.setIn(this.path, this.value);

src/searchable-dropdown/searchable-dropdown.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="btn-group" dropdown keyboardNav="true" [isOpen]="status.isOpen" (onShown)="status.isOpen = true" (onHidden)="status.isOpen = false">
22
<div class="toggle-container" dropdownToggle>
33
<input #inputToggle attr.data-path="{{pathString}}" [placeholder]="placeholder" [(ngModel)]="expressionOrValue" (keypress)="onKeypress($event.key)"
4-
[tabindex]="tabIndex" (focus)="onFocus($event)">
4+
[tabindex]="tabIndex" (focus)="onFocus($event)" (blur)="onBlur.emit()">
55
<i class="fa fa-caret-down" (click)="inputToggle.focus()"></i>
66
</div>
77
<ul class="dropdown-menu" *dropdownMenu role="menu">

src/searchable-dropdown/searchable-dropdown.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export class SearchableDropdownComponent implements OnInit {
4242
status: { isOpen: boolean } = { isOpen: false };
4343

4444
@Output() onSelect = new EventEmitter<string>();
45+
@Output() onBlur = new EventEmitter<void>();
46+
4547

4648
ngOnInit() {
4749
this.placeholder = this.value || this.placeholder || '';

src/shared/services/dom-util.service.ts

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,28 @@ import { TabsUtilService } from './tabs-util.service';
2727
export class DomUtilService {
2828

2929
private editableSelector = '.value-container input, div[contenteditable=true]';
30+
// highlight class is defined in json-editor.component.scss
31+
private highlightClass = 'highlight';
32+
private highlightedElement: HTMLElement;
3033

3134
constructor(public tabsUtilService: TabsUtilService) { }
3235

33-
focusAndSelectFirstEditableChildById(id: string) {
36+
focusAndSelectFirstEditableChildById(id: string, highlight = false) {
3437
this.tabsUtilService.selectTabIfNeeded(id);
3538
setTimeout(() => {
3639
let el = document.getElementById(id);
3740
if (el) {
38-
let firstInput = el.querySelector(this.editableSelector) as HTMLElement;
39-
if (firstInput) {
40-
firstInput.focus();
41-
this.selectAllContent(firstInput);
41+
let firstEditable = el.querySelector(this.editableSelector) as HTMLElement;
42+
if (firstEditable) {
43+
firstEditable.focus();
44+
this.selectAllContent(firstEditable);
45+
if (highlight) {
46+
firstEditable.classList.add(this.highlightClass);
47+
this.highlightedElement = firstEditable;
48+
}
4249
} else {
4350
// if element doesn't have any input, open add-field-dropdown if it exists.
44-
firstInput = el.querySelector('div.btn-group') as HTMLInputElement;
45-
if (firstInput) {
46-
let dropDownButton = el.querySelector('div.btn-group button') as HTMLButtonElement;
47-
if (dropDownButton) {
48-
dropDownButton.click();
49-
}
50-
}
51+
this.openDropdown(el);
5152
}
5253
}
5354
});
@@ -80,25 +81,18 @@ export class DomUtilService {
8081
}
8182
}
8283

83-
flashElementById(id: string) {
84-
this.tabsUtilService.selectTabIfNeeded(id);
85-
setTimeout(() => {
86-
let el = document.getElementById(id);
87-
if (el) {
88-
// .flash is defined json-editor.component.scss, {border: 2px solid yellow;}
89-
el.classList.add('flash');
90-
setTimeout(() => {
91-
el.classList.remove('flash');
92-
}, 500);
93-
}
94-
});
95-
}
96-
9784
blurFirstEditableChildById(id: string) {
9885
let el = document.getElementById(id);
99-
let firstInput = el.querySelector(this.editableSelector) as HTMLElement;
100-
if (firstInput) {
101-
firstInput.blur();
86+
let firstEditable = el.querySelector(this.editableSelector) as HTMLElement;
87+
if (firstEditable) {
88+
firstEditable.blur();
89+
}
90+
}
91+
92+
clearHighlight() {
93+
if (this.highlightedElement) {
94+
this.highlightedElement.classList.remove(this.highlightClass);
95+
this.highlightedElement = undefined;
10296
}
10397
}
10498

@@ -115,4 +109,14 @@ export class DomUtilService {
115109
}
116110
}
117111

112+
private openDropdown(el: HTMLElement) {
113+
let dropdown = el.querySelector('div.btn-group');
114+
if (dropdown) {
115+
let dropDownButton = dropdown.querySelector('button') as HTMLButtonElement;
116+
if (dropDownButton) {
117+
dropDownButton.click();
118+
}
119+
}
120+
}
121+
118122
}

src/shared/services/shortcut-action.service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,7 @@ export class ShortcutActionService {
206206
}
207207

208208
private focusElementInPath(path: string) {
209-
this.domUtilService.flashElementById(path);
210-
this.domUtilService.focusAndSelectFirstEditableChildById(path);
209+
this.domUtilService.focusAndSelectFirstEditableChildById(path, true);
211210
}
212211

213212
generateShortcutAction(action: string) {

src/tree-menu/tree-menu-item.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ export class TreeMenuItemComponent extends AbstractTrackerComponent implements O
7575
event.preventDefault();
7676

7777
this.isCollapsed = !this.isCollapsed;
78-
this.domUtilService.focusAndSelectFirstEditableChildById(this.path);
79-
this.domUtilService.flashElementById(this.path);
78+
this.domUtilService.focusAndSelectFirstEditableChildById(this.path, true);
8079
}
8180

8281
collapse() {

0 commit comments

Comments
 (0)