-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EditableField types and tests (#240)
* Fix CustomEditableField bug * Update field.d.ts * Update editable-field.hbs
- Loading branch information
Showing
10 changed files
with
242 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import Component from "@glimmer/component"; | ||
import { tracked } from "@glimmer/tracking"; | ||
import { action } from "@ember/object"; | ||
import { | ||
CustomEditableField, | ||
HermesDocument, | ||
HermesUser, | ||
} from "hermes/types/document"; | ||
|
||
interface CustomEditableFieldComponentSignature { | ||
Args: { | ||
document: HermesDocument; | ||
field: string; | ||
attributes: CustomEditableField; | ||
onChange: (value: any) => void; | ||
loading?: boolean; | ||
disabled?: boolean; | ||
}; | ||
} | ||
|
||
export default class CustomEditableFieldComponent extends Component<CustomEditableFieldComponentSignature> { | ||
@tracked protected emails: string | string[] = | ||
this.args.attributes.value || []; | ||
|
||
protected get typeIsString(): boolean { | ||
return this.args.attributes.type === "STRING"; | ||
} | ||
|
||
protected get typeIsPeople(): boolean { | ||
return this.args.attributes.type === "PEOPLE"; | ||
} | ||
|
||
protected get people(): HermesUser[] { | ||
let emails = this.emails instanceof Array ? this.emails : [this.emails]; | ||
return emails.map((email: string) => { | ||
return { email, imgURL: null }; | ||
}); | ||
} | ||
|
||
@action protected updateEmails(people: HermesUser[]) { | ||
this.emails = people.map((person: HermesUser) => { | ||
return person.email; | ||
}); | ||
} | ||
} | ||
|
||
declare module "@glint/environment-ember-loose/registry" { | ||
export default interface Registry { | ||
CustomEditableField: typeof CustomEditableFieldComponent; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,92 @@ | ||
// @ts-nocheck | ||
// TODO: Type this file | ||
import Component from "@glimmer/component"; | ||
import { tracked } from "@glimmer/tracking"; | ||
import { action } from "@ember/object"; | ||
import { scheduleOnce } from "@ember/runloop"; | ||
import { assert } from "@ember/debug"; | ||
|
||
export const FOCUSABLE = | ||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; | ||
|
||
export default class EditableField extends Component { | ||
@tracked editing = false; | ||
@tracked element = null; | ||
@tracked cachedValue = null; | ||
interface EditableFieldComponentSignature { | ||
Element: HTMLDivElement; | ||
Args: { | ||
value: any; | ||
onChange: (value: any) => void; | ||
loading?: boolean; | ||
disabled?: boolean; | ||
}; | ||
Blocks: { | ||
default: []; | ||
editing: [ | ||
F: { | ||
value: any; | ||
update: (value: any) => void; | ||
} | ||
]; | ||
}; | ||
} | ||
|
||
export default class EditableFieldComponent extends Component<EditableFieldComponentSignature> { | ||
@tracked protected editing = false; | ||
@tracked protected el: HTMLElement | null = null; | ||
@tracked protected cachedValue = null; | ||
|
||
@action | ||
captureElement(el) { | ||
this.element = el; | ||
@action protected captureElement(el: HTMLElement) { | ||
this.el = el; | ||
} | ||
|
||
@action | ||
edit() { | ||
@action protected edit() { | ||
this.cachedValue = this.args.value; | ||
this.editing = true; | ||
|
||
// Kinda gross, but this gives focus to the first focusable element in the | ||
// :editing block, which will typically be an input. | ||
scheduleOnce("afterRender", this, () => { | ||
if (this.element && !this.element.contains(document.activeElement)) { | ||
const firstInput = this.element.querySelector(FOCUSABLE); | ||
if (firstInput) firstInput.focus(); | ||
if (this.el && !this.el.contains(document.activeElement)) { | ||
const firstInput = this.el.querySelector(FOCUSABLE); | ||
if (firstInput) (firstInput as HTMLElement).focus(); | ||
} | ||
}); | ||
} | ||
|
||
@action | ||
cancel(ev) { | ||
@action protected cancel(ev: KeyboardEvent) { | ||
if (ev.key === "Escape") { | ||
ev.preventDefault(); | ||
scheduleOnce("actions", this, () => { | ||
this.editing = false; | ||
}); | ||
ev.preventDefault(); | ||
} | ||
} | ||
|
||
@action | ||
preventNewlines(ev) { | ||
@action protected preventNewlines(ev: KeyboardEvent) { | ||
if (ev.key === "Enter") { | ||
ev.preventDefault(); | ||
} | ||
} | ||
|
||
@action | ||
update(ev) { | ||
@action protected update(eventOrValue: Event | any) { | ||
scheduleOnce("actions", this, () => { | ||
this.editing = false; | ||
}); | ||
|
||
const newValue = ev instanceof Event ? ev.target.value : ev; | ||
let newValue = eventOrValue; | ||
|
||
if (eventOrValue instanceof Event) { | ||
const target = eventOrValue.target; | ||
assert("target must exist", target); | ||
assert("value must exist in the target", "value" in target); | ||
const value = target.value; | ||
newValue = value; | ||
} | ||
|
||
if (newValue !== this.cachedValue) { | ||
this.args.onChange?.(newValue); | ||
} | ||
} | ||
} | ||
|
||
declare module "@glint/environment-ember-loose/registry" { | ||
export default interface Registry { | ||
EditableField: typeof EditableFieldComponent; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.