From b722fa361be91a3ae9a95030dda9826742d6dfff Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Fri, 29 Jul 2022 17:18:07 -0400 Subject: [PATCH] =?UTF-8?q?*=20feat:=20=F0=9F=8E=B8=20Add=20code-editor=20?= =?UTF-8?q?component=20(#1207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Add code-editor component Added code editor component which was taken from HCP, tweaked styles and imports to use boundary assets ✅ Closes: ICU-5167 --- .../rose/code-editor/field-editor/index.hbs | 6 + .../components/rose/code-editor/index.hbs | 8 ++ .../rose/code-editor/index.stories.mdx | 90 ++++++++++++ .../rose/code-editor/toolbar/index.hbs | 29 ++++ .../rose/code-editor/toolbar/index.js | 25 ++++ .../rose/nav/link/index.stories.mdx | 14 +- addons/rose/addon/modifiers/code-mirror.js | 88 ++++++++++++ .../rose/addon/modifiers/on-click-inside.js | 2 +- .../rose/addon/modifiers/on-click-outside.js | 2 +- .../addon/styles/rose/components/_index.scss | 1 + .../rose/components/code-editor/_index.scss | 80 +++++++++++ .../utilities/register-codemirror-hcl.js | 38 ++++++ .../rose/app/components/rose/code-editor.js | 1 + .../rose/code-editor/field-editor.js | 1 + .../components/rose/code-editor/toolbar.js | 1 + addons/rose/app/modifiers/code-mirror.js | 1 + addons/rose/ember-cli-build.js | 3 + addons/rose/package.json | 4 +- .../components/rose/code-editor-test.js | 27 ++++ .../rose/code-editor/field-editor-test.js | 69 ++++++++++ .../rose/code-editor/toolbar-test.js | 55 ++++++++ yarn.lock | 129 ++++++++++++++---- 22 files changed, 637 insertions(+), 37 deletions(-) create mode 100644 addons/rose/addon/components/rose/code-editor/field-editor/index.hbs create mode 100644 addons/rose/addon/components/rose/code-editor/index.hbs create mode 100644 addons/rose/addon/components/rose/code-editor/index.stories.mdx create mode 100644 addons/rose/addon/components/rose/code-editor/toolbar/index.hbs create mode 100644 addons/rose/addon/components/rose/code-editor/toolbar/index.js create mode 100644 addons/rose/addon/modifiers/code-mirror.js create mode 100644 addons/rose/addon/styles/rose/components/code-editor/_index.scss create mode 100644 addons/rose/addon/utilities/register-codemirror-hcl.js create mode 100644 addons/rose/app/components/rose/code-editor.js create mode 100644 addons/rose/app/components/rose/code-editor/field-editor.js create mode 100644 addons/rose/app/components/rose/code-editor/toolbar.js create mode 100644 addons/rose/app/modifiers/code-mirror.js create mode 100644 addons/rose/tests/integration/components/rose/code-editor-test.js create mode 100644 addons/rose/tests/integration/components/rose/code-editor/field-editor-test.js create mode 100644 addons/rose/tests/integration/components/rose/code-editor/toolbar-test.js diff --git a/addons/rose/addon/components/rose/code-editor/field-editor/index.hbs b/addons/rose/addon/components/rose/code-editor/field-editor/index.hbs new file mode 100644 index 000000000..82ed4cbae --- /dev/null +++ b/addons/rose/addon/components/rose/code-editor/field-editor/index.hbs @@ -0,0 +1,6 @@ +
\ No newline at end of file diff --git a/addons/rose/addon/components/rose/code-editor/index.hbs b/addons/rose/addon/components/rose/code-editor/index.hbs new file mode 100644 index 000000000..1357f73dc --- /dev/null +++ b/addons/rose/addon/components/rose/code-editor/index.hbs @@ -0,0 +1,8 @@ +
+ {{yield + (hash + fieldEditor=(component 'rose/code-editor/field-editor' value=@codeValue) + toolbar=(component 'rose/code-editor/toolbar' copyText=@codeValue) + ) + }} +
\ No newline at end of file diff --git a/addons/rose/addon/components/rose/code-editor/index.stories.mdx b/addons/rose/addon/components/rose/code-editor/index.stories.mdx new file mode 100644 index 000000000..05aacff99 --- /dev/null +++ b/addons/rose/addon/components/rose/code-editor/index.stories.mdx @@ -0,0 +1,90 @@ +import { Meta, Story, Preview } from '@storybook/addon-docs/blocks'; +import { hbs } from 'ember-cli-htmlbars'; + + + +# Code Editor + +`CodeEditor` is a component that yields two presentational components which together provide an IDE-like UX. +CodeEditor::fieldEditor is where the provided code is displayed with syntax highlighting. +CodeEditor::toolbar is intended to go on top of the fieldEditor and provides a 'Copy code' button as well as other optional user-provided actions. + +```html + + + + +``` + + + +export const codeValue = + 'hcp_boundary_cluster_id = ""\n\n' + + 'listener "tcp" {\n' + + ' address = "0.0.0.0:9202"\n' + + ' purpose = "proxy"\n' + + '}\n\n' + + 'worker {\n' + + ' public_addr = "107.22.128.152"\n' + + ' auth_storage_path = "/home/ubuntu/boundary/worker1"\n' + + ' tags {\n' + + ' type = ["dev-worker", "ubuntu"]\n' + + ' }\n' + + '}'; + + + + {{ + template: hbs` + + + + `, + context: { + codeValue: codeValue, + options: { + mode: 'hcl', + readOnly: true, + }, + }, + }} + + + + + + {{ + template: hbs` + + + + `, + context: { + codeValue: 'mkdir /home/ubuntu/boundary/', + options: { + mode: 'shell', + readOnly: true, + }, + }, + }} + + + + + + {{ + template: hbs` + + + + `, + context: { + codeValue: 'mkdir /home/ubuntu/boundary/', + options: { + mode: 'shell', + readOnly: false, + }, + }, + }} + + diff --git a/addons/rose/addon/components/rose/code-editor/toolbar/index.hbs b/addons/rose/addon/components/rose/code-editor/toolbar/index.hbs new file mode 100644 index 000000000..0d9b1928a --- /dev/null +++ b/addons/rose/addon/components/rose/code-editor/toolbar/index.hbs @@ -0,0 +1,29 @@ +
+
+ {{yield to='action'}} +
+ +
+ {{#if (has-block 'menu')}} +
+ {{yield to='menu'}} +
+
+ {{/if}} + + + Copy code + +
+
\ No newline at end of file diff --git a/addons/rose/addon/components/rose/code-editor/toolbar/index.js b/addons/rose/addon/components/rose/code-editor/toolbar/index.js new file mode 100644 index 000000000..9ec546b66 --- /dev/null +++ b/addons/rose/addon/components/rose/code-editor/toolbar/index.js @@ -0,0 +1,25 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { later } from '@ember/runloop'; + +export const COPY_ICON_TYPE = 'flight-icons/svg/clipboard-copy-16'; +export const COPIED_ICON_TYPE = 'flight-icons/svg/clipboard-checked-16'; + +export default class RoseCodeEditorToolbarComponent extends Component { + @tracked copyIconType = COPY_ICON_TYPE; + + @action + copied() { + let { onCopy } = this.args; + let originalIconType = this.copyIconType; + + this.copyIconType = COPIED_ICON_TYPE; + + if (onCopy) { + onCopy(); + } + + later(() => (this.copyIconType = originalIconType), 1000); + } +} diff --git a/addons/rose/addon/components/rose/nav/link/index.stories.mdx b/addons/rose/addon/components/rose/nav/link/index.stories.mdx index 198f434f4..655582cea 100644 --- a/addons/rose/addon/components/rose/nav/link/index.stories.mdx +++ b/addons/rose/addon/components/rose/nav/link/index.stories.mdx @@ -10,14 +10,16 @@ import { text, boolean, select } from '@storybook/addon-knobs'; Proxies the built-in `LinkTo` component, applying a class `rose-nav-link`. ```html -Text +Text ``` - {{ - template: hbs` - Text + + {{ + template: hbs` + Text `, - context: {}, - }} + context: {}, + }} + diff --git a/addons/rose/addon/modifiers/code-mirror.js b/addons/rose/addon/modifiers/code-mirror.js new file mode 100644 index 000000000..6efa1a419 --- /dev/null +++ b/addons/rose/addon/modifiers/code-mirror.js @@ -0,0 +1,88 @@ +import Modifier from 'ember-modifier'; +import codemirror from 'codemirror'; + +import '../utilities/register-codemirror-hcl'; +import 'codemirror/mode/go/go'; +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/mode/shell/shell'; +import 'codemirror/addon/edit/matchbrackets'; +import 'codemirror/addon/edit/closebrackets'; +import 'codemirror/addon/selection/active-line'; + +// Here we define default options for the editor. +// These should follow the codemirror configuration types +// +const _PRESET_DEFAULTS = { + theme: 'monokai', + lineNumbers: true, + cursorBlinkRate: 500, + matchBrackets: true, + autoCloseBrackets: true, + styleActiveLine: true, + mode: 'hcl', +}; + +/** + * + * `CodeMirror` implements a modifier that creates a CodeMirror instance on the provided element. + * + * The supported modes for the editor are currently: hcl, shell, go, javascript + * The supported themes are: monokai + * + * Sample usage: + * ``` + *
+ * ``` + * + * @class CodeMirror + * + */ +export default class CodeMirrorModifier extends Modifier { + _editor; + + didInstall() { + this._setup(); + } + + didUpdateArguments() { + if (this._editor?.getValue() !== this.args.named.value) { + this._editor?.setValue(this.args.named.value); + } + } + + /** + * Called when editor contents change. + * @method CodeMirror#_onChange + * See documentation for the CodeMirror editor events, specifically 'change' event here https://codemirror.net/doc/manual.html#events. + */ + _onChange(editor) { + const { onInput } = this.args.named; + const newVal = editor.getValue(); + onInput && onInput(newVal); + } + + /** + * Initializes the editor with default configuration merged with consumer-provided configuration. + * Also registers a 'change' event on the editor. + * @method CodeMirror#_setup + */ + _setup() { + if (!this.element) { + throw new Error('CodeMirror modifier has no element'); + } + + const editor = codemirror(this.element, { + ..._PRESET_DEFAULTS, + ...(this.args.named.options || {}), + value: this.args.named.value, + }); + + editor.on('change', (editor) => { + this._onChange(editor); + }); + + this._editor = editor; + } +} diff --git a/addons/rose/addon/modifiers/on-click-inside.js b/addons/rose/addon/modifiers/on-click-inside.js index ec64ff5f7..6364121cd 100644 --- a/addons/rose/addon/modifiers/on-click-inside.js +++ b/addons/rose/addon/modifiers/on-click-inside.js @@ -1,7 +1,7 @@ import { modifier } from 'ember-modifier'; /** - * Used to detect clicks occuring _inside_ the modified element. + * Used to detect clicks occurring _inside_ the modified element. * This is useful especially for dropdowns to toggle content visibility * when the user clicks an item. */ diff --git a/addons/rose/addon/modifiers/on-click-outside.js b/addons/rose/addon/modifiers/on-click-outside.js index be434a234..bab96ddaa 100644 --- a/addons/rose/addon/modifiers/on-click-outside.js +++ b/addons/rose/addon/modifiers/on-click-outside.js @@ -1,7 +1,7 @@ import { modifier } from 'ember-modifier'; /** - * Used to detect clicks occuring _outside_ of the modified element. + * Used to detect clicks occurring _outside_ of the modified element. * This is useful especially for dropdowns to toggle content visibility * when the user clicks away. */ diff --git a/addons/rose/addon/styles/rose/components/_index.scss b/addons/rose/addon/styles/rose/components/_index.scss index f8161a2c6..eaa43f53b 100644 --- a/addons/rose/addon/styles/rose/components/_index.scss +++ b/addons/rose/addon/styles/rose/components/_index.scss @@ -19,3 +19,4 @@ @import 'link-button'; @import 'badge'; @import 'toolbar'; +@import 'code-editor'; diff --git a/addons/rose/addon/styles/rose/components/code-editor/_index.scss b/addons/rose/addon/styles/rose/components/code-editor/_index.scss new file mode 100644 index 000000000..962da8eff --- /dev/null +++ b/addons/rose/addon/styles/rose/components/code-editor/_index.scss @@ -0,0 +1,80 @@ +@use '../../variables/sizing'; + +// Codemirror overrides +.rose-codemirror-container { + width: 100%; +} + +.CodeMirror { + max-width: 100%; + display: flex; + flex: 1; + font-family: ui-monospace, 'Menlo', monospace; +} + +.rose-autosize { + .CodeMirror { + height: auto; + } +} + +.CodeMirror-scroll { + width: 100%; +} + +.CodeMirror-sizer { + border-right-width: 0px !important; +} + +// Toolbar +.rose-code-editor-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + background-color: var(--ui-gray-subtler-6); + border-top: 1px solid var(--ui-gray-subtler-3); + border-bottom: 1px solid var(--ui-gray-subtler-3); + height: 2.75rem; + + &__action { + margin-left: sizing.rems(s); + } + + &__right-aligned { + display: flex; + height: 100%; + align-items: center; + } + + &__menu { + margin-right: sizing.rems(s); + + .pds-dropdownTrigger { + box-shadow: none; + + &__layoutWrapper { + padding-left: sizing.rems(s); + padding-right: sizing.rems(s); + } + } + + &-divider { + border-right: 1px solid var(--ui-gray-subtler-3); + height: sizing.rems(xl); + } + } + + &__copy-button { + border: none; + background-color: inherit; + cursor: pointer; + color: var(--ui-gray-starker-2); + margin-right: sizing.rems(xs); + margin-left: sizing.rems(s); + height: sizing.rems(xl); + + &-icon { + margin-right: sizing.rems(xs); + } + } +} diff --git a/addons/rose/addon/utilities/register-codemirror-hcl.js b/addons/rose/addon/utilities/register-codemirror-hcl.js new file mode 100644 index 000000000..f39180d72 --- /dev/null +++ b/addons/rose/addon/utilities/register-codemirror-hcl.js @@ -0,0 +1,38 @@ +import codemirror from 'codemirror'; +import 'codemirror/addon/mode/simple'; + +// This utility defines 'hcl' mode for a codemirror editor and enables correct syntax highlighting. +// Importing this file in the codemirror modifier will allow 'hcl' to be selected as a mode: +// import 'cloud-ui-core/utils/register-codemirror-hcl' +codemirror.defineSimpleMode('hcl', { + start: [ + { regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string' }, // strings + { regex: /(\w+)(\s+)(=)/, token: ['keyword', 'null', 'null'] }, // assignment of variables + { + regex: /(build|deploy|release|hook|registry|type|runner|url)( )({)/, + token: ['keyword', 'null', 'null'], + }, // top level keywords + { regex: /(variable)\b/, token: 'keyword' }, // input variable keyword + { regex: /true|false|null|undefined/, token: 'atom' }, // bool keywords + { + regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i, + token: 'number', + }, // numbers + { regex: /(#|\/\/)(\s*\S*)/, token: 'comment' }, // single line comments + { regex: /(path)(.)/, token: ['variable-2', 'null'] }, // path variables + { regex: /(workspace)(.)(\S*)/, token: ['string', 'null', 'string'] }, // workspace variables + { regex: /\/\*/, token: 'comment', next: 'comment' }, // multi-line comment + { regex: /[-+/*=<>!]+/, token: 'operator' }, // operators + { regex: /[{[(]/, indent: true }, // for auto indent + { regex: /[}\])]/, dedent: true }, + ], + // The multi-line comment state. + comment: [ + { regex: /.*?\*\//, token: 'comment', next: 'start' }, + { regex: /.*/, token: 'comment' }, + ], + meta: { + dontIndentStates: ['comment'], + lineComment: '#', + }, +}); diff --git a/addons/rose/app/components/rose/code-editor.js b/addons/rose/app/components/rose/code-editor.js new file mode 100644 index 000000000..385898fb5 --- /dev/null +++ b/addons/rose/app/components/rose/code-editor.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/code-editor'; diff --git a/addons/rose/app/components/rose/code-editor/field-editor.js b/addons/rose/app/components/rose/code-editor/field-editor.js new file mode 100644 index 000000000..2fac9081d --- /dev/null +++ b/addons/rose/app/components/rose/code-editor/field-editor.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/code-editor/field-editor'; diff --git a/addons/rose/app/components/rose/code-editor/toolbar.js b/addons/rose/app/components/rose/code-editor/toolbar.js new file mode 100644 index 000000000..b59e5c3c9 --- /dev/null +++ b/addons/rose/app/components/rose/code-editor/toolbar.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/code-editor/toolbar'; diff --git a/addons/rose/app/modifiers/code-mirror.js b/addons/rose/app/modifiers/code-mirror.js new file mode 100644 index 000000000..2251c72ee --- /dev/null +++ b/addons/rose/app/modifiers/code-mirror.js @@ -0,0 +1 @@ +export { default } from 'rose/modifiers/code-mirror'; diff --git a/addons/rose/ember-cli-build.js b/addons/rose/ember-cli-build.js index ba0649dc9..6d7b0d444 100644 --- a/addons/rose/ember-cli-build.js +++ b/addons/rose/ember-cli-build.js @@ -7,6 +7,9 @@ module.exports = function (defaults) { // Add options here }); + app.import('node_modules/codemirror/lib/codemirror.css'); + app.import('node_modules/codemirror/theme/monokai.css'); + /* This build file specifies the options for the dummy test app of this addon, located in `/tests/dummy` diff --git a/addons/rose/package.json b/addons/rose/package.json index 6cf3a5f59..5f0371cd2 100644 --- a/addons/rose/package.json +++ b/addons/rose/package.json @@ -31,8 +31,10 @@ "doc:toc": "doctoc README.md" }, "dependencies": { + "codemirror": "5.65.7", "ember-auto-import": "^2.4.2", "ember-cli-babel": "^7.26.8", + "ember-cli-clipboard": "^0.16.0", "ember-cli-htmlbars": "^5.7.1", "ember-composable-helpers": "^5.0.0", "ember-focus-trap": "^0.7.0", @@ -112,6 +114,6 @@ "lint-staged": { "*.js": "eslint --fix", "*.hbs": "ember-template-lint --fix", - "*.{js,scss}": "prettier --write" + "*.{js,scss,mdx}": "prettier --write" } } diff --git a/addons/rose/tests/integration/components/rose/code-editor-test.js b/addons/rose/tests/integration/components/rose/code-editor-test.js new file mode 100644 index 000000000..e14d2c85c --- /dev/null +++ b/addons/rose/tests/integration/components/rose/code-editor-test.js @@ -0,0 +1,27 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | rose/code-editor', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + const codeValue = 'export HCP_CLIENT_ID = {place your client_id here}'; + this.set('codeValue', codeValue); + + await render(hbs` + + + + + `); + assert.dom('[data-test-code-editor]').isVisible(); + assert.dom('[data-test-code-editor-toolbar]').isVisible(); + assert.dom('[data-test-code-editor-field-editor]').isVisible(); + assert.dom('[data-test-code-editor-field-editor]').includesText(codeValue); + }); +}); diff --git a/addons/rose/tests/integration/components/rose/code-editor/field-editor-test.js b/addons/rose/tests/integration/components/rose/code-editor/field-editor-test.js new file mode 100644 index 000000000..fb37c59b4 --- /dev/null +++ b/addons/rose/tests/integration/components/rose/code-editor/field-editor-test.js @@ -0,0 +1,69 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { typeIn, render, waitUntil } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module( + 'Integration | Component | rose/code-editor/field-editor', + function (hooks) { + setupRenderingTest(hooks); + const editorSelector = '[data-test-code-editor-field-editor]'; + + test('it renders', async function (assert) { + const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`; + this.set('codeValue', codeValue); + await render(hbs` + + `); + assert.dom(editorSelector).isVisible(); + assert.dom(editorSelector).includesText(codeValue); + + const myNewCode = 'NEW CODE'; + await typeIn(`${editorSelector} textarea`, myNewCode); + assert.dom(editorSelector).includesText(myNewCode); + }); + + test('it renders readonly', async function (assert) { + const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`; + this.set('codeValue', codeValue); + this.set('options', { + readOnly: true, + }); + await render(hbs` + + `); + assert.dom(editorSelector).isVisible(); + assert.dom(editorSelector).includesText(codeValue); + + const myNewCode = 'NEW CODE'; + waitUntil(async () => { + try { + // This should fail if the textarea is readonly + await typeIn(`${editorSelector} textarea`, myNewCode); + } catch { + return true; + } + }); + assert.dom(editorSelector).doesNotIncludeText(myNewCode); + }); + + test('it calls onInput on change', async function (assert) { + const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`; + const onInput = () => { + this.set('called', true); + }; + this.set('codeValue', codeValue); + this.set('onInput', onInput); + await render(hbs` + + `); + assert.dom(editorSelector).isVisible(); + assert.dom(editorSelector).includesText(codeValue); + + const myNewCode = 'NEW CODE'; + await typeIn(`${editorSelector} textarea`, myNewCode); + assert.dom(editorSelector).includesText(myNewCode); + assert.true(this.called); + }); + } +); diff --git a/addons/rose/tests/integration/components/rose/code-editor/toolbar-test.js b/addons/rose/tests/integration/components/rose/code-editor/toolbar-test.js new file mode 100644 index 000000000..78608e9f8 --- /dev/null +++ b/addons/rose/tests/integration/components/rose/code-editor/toolbar-test.js @@ -0,0 +1,55 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { click, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | rose/code-editor/toolbar', function (hooks) { + setupRenderingTest(hooks); + const toolbarSelector = '[data-test-code-editor-toolbar]'; + const copyButtonSelector = '[data-test-code-editor-toolbar-copy-button]'; + const menuDividerSelector = '[data-test-code-editor-toolbar-menu-divider]'; + + hooks.beforeEach(function () { + const copyText = 'copyText'; + this.set('copyText', copyText); + }); + + test('it renders', async function (assert) { + await render(hbs` + + `); + assert.dom(toolbarSelector).isVisible(); + assert.dom(copyButtonSelector).isVisible(); + assert.dom(menuDividerSelector).doesNotExist(); + }); + + test('it displays action', async function (assert) { + await render(hbs` + + <:action> +
Hello
+ +
+ `); + assert.dom(toolbarSelector).isVisible(); + assert.dom(copyButtonSelector).isVisible(); + assert.dom(menuDividerSelector).doesNotExist(); + assert.dom('[data-test-my-action]').isVisible(); + }); + + test('it calls onCopy callback', async function (assert) { + const onCopy = () => { + this.set('called', true); + }; + this.set('onCopy', onCopy); + await render(hbs` + + `); + assert.dom(toolbarSelector).isVisible(); + assert.dom(copyButtonSelector).isVisible(); + assert.dom(menuDividerSelector).doesNotExist(); + + await click(copyButtonSelector); + assert.true(this.called); + }); +}); diff --git a/yarn.lock b/yarn.lock index 3e68500ea..9c7c673bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3869,7 +3869,7 @@ ember-cli-babel "^7.22.1" ember-compatibility-helpers "^1.1.1" -"@ember/render-modifiers@^2.0.4": +"@ember/render-modifiers@^1.0.2 || ^2.0.0", "@ember/render-modifiers@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.0.4.tgz#0ac7af647cb736076dbfcd54ca71e090cd329d71" integrity sha512-Zh/fo5VUmVzYHkHVvzWVjJ1RjFUxA2jH0zCp2+DQa80Bf3DUXauiEByxU22UkN4LFT55DBFttC0xCQSJG3WTsg== @@ -9420,13 +9420,13 @@ browserify-zlib@^0.2.0: pako "~1.0.5" browserslist@4.14.2, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.16.7, browserslist@^4.17.3, browserslist@^4.17.5, browserslist@^4.19.1, browserslist@^4.20.2, browserslist@^4.21.0: - version "4.21.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" - integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== + version "4.21.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" + integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== dependencies: - caniuse-lite "^1.0.30001359" - electron-to-chromium "^1.4.172" - node-releases "^2.0.5" + caniuse-lite "^1.0.30001366" + electron-to-chromium "^1.4.188" + node-releases "^2.0.6" update-browserslist-db "^1.0.4" bser@2.1.1: @@ -9758,10 +9758,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001359: - version "1.0.30001364" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001364.tgz#1e118f0e933ed2b79f8d461796b8ce45398014a0" - integrity sha512-9O0xzV3wVyX0SlegIQ6knz+okhBB5pE0PC40MNdwcipjwpxoUEHL24uJ+gG42cgklPjfO5ZjZPme9FTSN3QT2Q== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001366: + version "1.0.30001367" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz#2b97fe472e8fa29c78c5970615d7cd2ee414108a" + integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw== capture-exit@^2.0.0: version "2.0.0" @@ -10155,6 +10155,15 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +clipboard@^2.0.6: + version "2.0.11" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5" + integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + clipboard@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba" @@ -10251,6 +10260,11 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codemirror@5.65.7: + version "5.65.7" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.7.tgz#29af41ce5f4c2b8f1c1e16f4e645ff392823716a" + integrity sha512-zb67cXzgugIQmb6tfD4G11ILjYoMfTjwcjn+cWsa4GewlI2adhR/h3kolkoCQTm1msD/1BuqVTKuO09ELsS++A== + collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -11757,10 +11771,10 @@ electron-rebuild@^3.2.6: tar "^6.0.5" yargs "^17.0.1" -electron-to-chromium@^1.4.172: - version "1.4.182" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.182.tgz#5d59214ebfe90b36f23e81cd226a42732cd8c677" - integrity sha512-OpEjTADzGoXABjqobGhpy0D2YsTncAax7IkER68ycc4adaq0dqEG9//9aenKPy7BGA90bqQdLac0dPp6uMkcSg== +electron-to-chromium@^1.4.188: + version "1.4.195" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz#139b2d95a42a3f17df217589723a1deac71d1473" + integrity sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg== element-resize-detector@^1.2.2: version "1.2.3" @@ -11836,6 +11850,41 @@ ember-auto-import@^1.10.1, ember-auto-import@^1.11.2, ember-auto-import@^1.2.15, walk-sync "^0.3.3" webpack "^4.43.0" +ember-auto-import@^1.11.3: + version "1.12.2" + resolved "https://registry.yarnpkg.com/ember-auto-import/-/ember-auto-import-1.12.2.tgz#cc7298ee5c0654b0249267de68fb27a2861c3579" + integrity sha512-gLqML2k77AuUiXxWNon1FSzuG1DV7PEPpCLCU5aJvf6fdL6rmFfElsZRh+8ELEB/qP9dT+LHjNEunVzd2dYc8A== + dependencies: + "@babel/core" "^7.1.6" + "@babel/preset-env" "^7.10.2" + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.1.6" + "@embroider/shared-internals" "^1.0.0" + babel-core "^6.26.3" + babel-loader "^8.0.6" + babel-plugin-syntax-dynamic-import "^6.18.0" + babylon "^6.18.0" + broccoli-debug "^0.6.4" + broccoli-node-api "^1.7.0" + broccoli-plugin "^4.0.0" + broccoli-source "^3.0.0" + debug "^3.1.0" + ember-cli-babel "^7.0.0" + enhanced-resolve "^4.0.0" + fs-extra "^6.0.1" + fs-tree-diff "^2.0.0" + handlebars "^4.3.1" + js-string-escape "^1.0.1" + lodash "^4.17.19" + mkdirp "^0.5.1" + resolve-package-path "^3.1.0" + rimraf "^2.6.2" + semver "^7.3.4" + symlink-or-copy "^1.2.0" + typescript-memoize "^1.0.0-alpha.3" + walk-sync "^0.3.3" + webpack "^4.43.0" + ember-auto-import@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/ember-auto-import/-/ember-auto-import-2.4.2.tgz#d4d3bc6885a11cf124f606f5c37169bdf76e37ae" @@ -11961,6 +12010,17 @@ ember-cli-babel@^6.16.0, ember-cli-babel@^6.6.0, ember-cli-babel@^6.8.1, ember-c rimraf "^3.0.1" semver "^5.5.0" +ember-cli-clipboard@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.16.0.tgz#f4878cbbfbc56e0bc6d2a65ccf5a334c47b0f614" + integrity sha512-l9iDVjcJLkbgpdbJe+bN29q2ibZmEpEV6bXstIG9q4HPvaqbXw0PbSFhaNeQWpJKNkd5dFKSNdgEfli6heJSFw== + dependencies: + "@ember/render-modifiers" "^1.0.2 || ^2.0.0" + clipboard "^2.0.6" + ember-auto-import "^1.11.3" + ember-cli-babel "^7.26.6" + ember-cli-htmlbars "^5.7.1" + ember-cli-code-coverage@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/ember-cli-code-coverage/-/ember-cli-code-coverage-1.0.3.tgz#9a6e5e6350d70761eba749d68ebe2e0d9aa3492f" @@ -16417,6 +16477,11 @@ ip@1.1.5, ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA== +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -20115,10 +20180,10 @@ node-notifier@^10.0.0, node-notifier@^8.0.1: uuid "^8.3.0" which "^2.0.2" -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== node-uuid@~1.4.7: version "1.4.8" @@ -21425,12 +21490,12 @@ playwright@^1.15.0: yazl "^2.5.1" plist@^3.0.0, plist@^3.0.1, plist@^3.0.4, plist@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" - integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" + integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== dependencies: base64-js "^1.5.1" - xmlbuilder "^9.0.7" + xmlbuilder "^15.1.1" pluralize@^1.2.1: version "1.2.1" @@ -23986,7 +24051,15 @@ socks-proxy-agent@^6.0.0: debug "^4.3.3" socks "^2.6.2" -socks@^2.3.3, socks@^2.6.2: +socks@^2.3.3: + version "2.7.0" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.0.tgz#f9225acdb841e874dca25f870e9130990f3913d0" + integrity sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + +socks@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== @@ -26711,10 +26784,10 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xmlbuilder@^9.0.7: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== xmlchars@^2.2.0: version "2.2.0"