|
| 1 | +/** |
| 2 | + * Copyright (c) HashiCorp, Inc. |
| 3 | + * SPDX-License-Identifier: MPL-2.0 |
| 4 | + */ |
| 5 | + |
| 6 | +import Component from '@glimmer/component'; |
| 7 | +import { tracked } from '@glimmer/tracking'; |
| 8 | +import { action } from '@ember/object'; |
| 9 | +import { modifier } from 'ember-modifier'; |
| 10 | + |
| 11 | +import type { ComponentLike } from '@glint/template'; |
| 12 | +import type { HdsCodeEditorSignature as HdsCodeEditorModifierSignature } from 'src/modifiers/hds-code-editor'; |
| 13 | +import type { HdsCodeEditorDescriptionSignature } from './description'; |
| 14 | +import type { HdsCodeEditorTitleSignature } from './title'; |
| 15 | +import type { HdsCodeEditorGenericSignature } from './generic'; |
| 16 | +import type { EditorView } from '@codemirror/view'; |
| 17 | +import { guidFor } from '@ember/object/internals'; |
| 18 | + |
| 19 | +export interface HdsCodeEditorSignature { |
| 20 | + Args: { |
| 21 | + ariaLabel?: string; |
| 22 | + ariaLabelledBy?: string; |
| 23 | + hasCopyButton?: boolean; |
| 24 | + hasFullScreenButton?: boolean; |
| 25 | + isStandalone?: boolean; |
| 26 | + language?: HdsCodeEditorModifierSignature['Args']['Named']['language']; |
| 27 | + value?: HdsCodeEditorModifierSignature['Args']['Named']['value']; |
| 28 | + onBlur?: HdsCodeEditorModifierSignature['Args']['Named']['onBlur']; |
| 29 | + onInput?: HdsCodeEditorModifierSignature['Args']['Named']['onInput']; |
| 30 | + onSetup?: HdsCodeEditorModifierSignature['Args']['Named']['onSetup']; |
| 31 | + }; |
| 32 | + Blocks: { |
| 33 | + default: [ |
| 34 | + { |
| 35 | + Title?: ComponentLike<HdsCodeEditorTitleSignature>; |
| 36 | + Description?: ComponentLike<HdsCodeEditorDescriptionSignature>; |
| 37 | + Generic?: ComponentLike<HdsCodeEditorGenericSignature>; |
| 38 | + }, |
| 39 | + ]; |
| 40 | + }; |
| 41 | + Element: HTMLDivElement; |
| 42 | +} |
| 43 | + |
| 44 | +export default class HdsCodeEditor extends Component<HdsCodeEditorSignature> { |
| 45 | + @tracked private _isFullScreen = false; |
| 46 | + @tracked private _isSetupComplete = false; |
| 47 | + @tracked private _value; |
| 48 | + @tracked private _titleId: string | undefined; |
| 49 | + |
| 50 | + private _id = guidFor(this); |
| 51 | + |
| 52 | + private _handleEscape = modifier(() => { |
| 53 | + const handleKeyDown = (event: KeyboardEvent) => { |
| 54 | + if (event.key !== 'Escape' || !this._isFullScreen) { |
| 55 | + return; |
| 56 | + } |
| 57 | + |
| 58 | + this.toggleFullScreen(); |
| 59 | + }; |
| 60 | + |
| 61 | + document.addEventListener('keydown', handleKeyDown); |
| 62 | + |
| 63 | + return () => { |
| 64 | + document.removeEventListener('keydown', handleKeyDown); |
| 65 | + }; |
| 66 | + }); |
| 67 | + |
| 68 | + constructor(owner: unknown, args: HdsCodeEditorSignature['Args']) { |
| 69 | + super(owner, args); |
| 70 | + |
| 71 | + if (args.value) { |
| 72 | + this._value = args.value; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + get ariaLabelledBy(): string | undefined { |
| 77 | + if (this.args.ariaLabel !== undefined) { |
| 78 | + return; |
| 79 | + } |
| 80 | + |
| 81 | + return this.args.ariaLabelledBy ?? this._titleId; |
| 82 | + } |
| 83 | + |
| 84 | + get hasActions(): boolean { |
| 85 | + return (this.args.hasCopyButton || this.args.hasFullScreenButton) ?? false; |
| 86 | + } |
| 87 | + |
| 88 | + get isStandalone(): boolean { |
| 89 | + return this.args.isStandalone ?? true; |
| 90 | + } |
| 91 | + |
| 92 | + get classNames(): string { |
| 93 | + // Currently there is only one theme so the class name is hard-coded. |
| 94 | + // In the future, additional themes such as a "light" theme could be added. |
| 95 | + const classes = ['hds-code-editor', 'hds-code-editor--theme-dark']; |
| 96 | + |
| 97 | + if (this._isFullScreen) { |
| 98 | + classes.push('hds-code-editor--is-full-screen'); |
| 99 | + } |
| 100 | + |
| 101 | + if (this.isStandalone) { |
| 102 | + classes.push('hds-code-editor--is-standalone'); |
| 103 | + } |
| 104 | + |
| 105 | + return classes.join(' '); |
| 106 | + } |
| 107 | + |
| 108 | + @action |
| 109 | + registerTitleElement(element: HdsCodeEditorTitleSignature['Element']): void { |
| 110 | + this._titleId = element.id; |
| 111 | + } |
| 112 | + |
| 113 | + @action |
| 114 | + toggleFullScreen(): void { |
| 115 | + this._isFullScreen = !this._isFullScreen; |
| 116 | + } |
| 117 | + |
| 118 | + @action |
| 119 | + onInput(newValue: string): void { |
| 120 | + this._value = newValue; |
| 121 | + this.args.onInput?.(newValue); |
| 122 | + } |
| 123 | + |
| 124 | + @action |
| 125 | + onKeyDown(event: KeyboardEvent): void { |
| 126 | + if (event.key === 'Escape' && this._isFullScreen) { |
| 127 | + this.toggleFullScreen(); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + @action |
| 132 | + onSetup(editorView: EditorView): void { |
| 133 | + this._isSetupComplete = true; |
| 134 | + this.args.onSetup?.(editorView); |
| 135 | + } |
| 136 | +} |
0 commit comments