ui: Fix code editor resizing and restyle (#11474)

Fixes an issue where the code editor would not resizing to the full extent of the browser window plus CodeEditor restyling/refactoring

- :label named block
- :tools named block
- :content named block
- code and CSS cleanup
- CodeEditor.mdx

Signed-off-by: Alessandro De Blasis <alex@deblasis.net>

Co-authored-by: John Cowen <johncowen@users.noreply.github.com>
This commit is contained in:
Alessandro De Blasis 2021-11-12 15:28:06 +00:00 committed by GitHub
parent dadb7a7c33
commit e0b46721c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 225 additions and 52 deletions

6
.changelog/11474.txt Normal file
View File

@ -0,0 +1,6 @@
```release-note:bug
ui: code editor styling (layout consistency + wide screen support)
```
```release-note:improvement
ui: added copy to clipboard button in code editor toolbars
```

View File

@ -6,6 +6,49 @@ state: needs-love
# CodeEditor # CodeEditor
```hbs preview-template ```hbs preview-template
<CodeEditor /> <CodeEditor
@readonly={{true}}
@name={{concat name "[Rules]"}}
@syntax="hcl"
@oninput={{noop}}
>
<:label>
Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
<:content>
<!-- Plain text or something that renders data like the following -->
<Consul::ServiceIdentity::Template
@nspace={{nspace}}
@name={{item.Name}}
/>
</:content>
</CodeEditor>
``` ```
A code-editor with syntax highlighting supporting multiple languages (JSON, HCL, YAML), validation and simple tools such as "Copy to clipboard"
### Arguments
| Argument | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly` | `Boolean` | false | If true, the content (value) of the CodeEditor cannot be changed by the user |
| `name` | `String` | | The name attribute of the form element |
| `syntax` | `String` | | Identifies the language used to validate/syntax highlight the code (possible values: hcl, json, yaml) |
| `oninput` | `Action` | noop | Action/callback that is triggered when the user inputs data |
### Named Blocks
| Name | Description | Behaviour if empty |
| --- | --- | --- |
| `:label` | Used to define the title that's displayed on the left inside the toolbar above the CodeEditor | Nothing is displayed |
| `:tools` | Used to define tools, buttons, widgets that will be displayed on the right inside the toolbar above the CodeEditor | By default it renders a `language selector` dropdown (if `readonly`== false and `syntax` is falsy) and a `CopyButton`
| `:content` | Used to display specific content such as code templates inside the CodeEditor | if the block is defined, @value will be displayed instead |
### See
- [Component Source Code](./index.js)
- [Template Source Code](./index.hbs)
---

View File

@ -1,11 +1,28 @@
<div class="toolbar-container">
<div class="toolbar">
<label class="title">
{{#if (has-block "label")}}
{{yield to="label"}}
{{/if}}
</label>
<div class="tools">
{{#if (has-block "tools")}}
{{yield to="tools"}}
{{else}}
{{#if (and (not readonly) (not syntax))}}
<PowerSelect
@onChange={{action "change"}}
@selected={{mode}}
@searchEnabled={{false}}
@options={{modes}} as |mode|>
{{mode.name}}
</PowerSelect>
<div class="toolbar-separator"></div>
<CopyButton @value={{value}} @name="value" />
{{/if}}
{{/if}}
</div>
</div>
</div>
<IvyCodemirror @value={{value}} @name={{name}} @class={{class}} @options={{options}} @valueUpdated={{action onkeyup}} /> <IvyCodemirror @value={{value}} @name={{name}} @class={{class}} @options={{options}} @valueUpdated={{action onkeyup}} />
<pre><code>{{yield}}</code></pre> <pre><code>{{#if (has-block "content")}}{{yield to="content"}}{{else}}{{value}}{{/if}}</code></pre>
{{#if (and (not readonly) (not syntax))}}
<PowerSelect
@onChange={{action "change"}}
@selected={{mode}}
@searchEnabled={{false}}
@options={{modes}} as |mode|>
{{mode.name}}
</PowerSelect>
{{/if}}

View File

@ -3,23 +3,9 @@
border: 10px; border: 10px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
clear: both;
} }
%code-editor .ember-power-select-trigger {
@extend %code-editor-syntax-select;
}
%code-editor-syntax-select {
width: 200px;
float: right;
z-index: 1;
}
%code-editor-syntax-select {
margin-top: 1px;
border: 0;
background-color: rgb(var(--black));
color: rgb(var(--white));
border-left: 1px solid;
border-radius: 0;
}
%code-editor::after { %code-editor::after {
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
@ -32,3 +18,51 @@
%code-editor > pre { %code-editor > pre {
display: none; display: none;
} }
%code-editor {
.toolbar-container,
.toolbar-container .toolbar {
align-items: center;
justify-content: space-between;
display: flex;
}
.toolbar-container {
position: relative;
margin-top: 4px;
height: 44px;
.toolbar {
flex: 1;
white-space: nowrap;
.title {
padding: 0 8px;
}
.toolbar-separator {
height: 32px;
margin: 0 4px;
width: 0;
}
.tools {
display: flex;
flex-direction: row;
margin: 0 10px;
align-items: center;
.copy-button {
margin-left: 10px;
}
}
}
.ember-basic-dropdown-trigger {
margin: 0 8px;
width: 120px;
height: 32px;
display: flex;
align-items: center;
flex-direction: row;
}
}
}

View File

@ -24,7 +24,7 @@ $syntax-dark-gray: #535f73;
--syntax-yellow: rgb(var(--tone-yellow-500)); --syntax-yellow: rgb(var(--tone-yellow-500));
} }
.CodeMirror { .CodeMirror {
max-width: 1150px; max-width: 1260px;
min-height: 300px; min-height: 300px;
height: auto; height: auto;
/* adds some space at the bottom of the editor for when a horizonal-scroll has appeared */ /* adds some space at the bottom of the editor for when a horizonal-scroll has appeared */
@ -186,3 +186,35 @@ $syntax-dark-gray: #535f73;
} }
} }
} }
%code-editor {
.toolbar-container {
background: rgb(var(--tone-gray-050));
background: linear-gradient(
180deg,
rgb(var(--tone-gray-050)) 50%,
rgb(var(--tone-gray-150)) 100%
);
border: 1px solid rgb(var(--tone-gray-150));
border-bottom-color: rgb(var(--tone-gray-600));
border-top-color: rgb(var(--tone-gray-400));
.toolbar {
.title {
color: rgb(var(--tone-gray-900));
font-size: 14px;
font-weight: 700;
}
.toolbar-separator {
border-right: 1px solid rgb(var(--tone-gray-300));
}
}
.ember-power-select-trigger {
background-color: rgb(var(--tone-gray-000));
color: rgb(var(--tone-gray-999));
border-radius: var(--decor-radius-100);
border: var(--decor-border-100);
border-color: rgb(var(--tone-gray-700));
}
}
}

View File

@ -38,16 +38,19 @@
<span>Code</span> <span>Code</span>
</label> </label>
</div> </div>
<label for="" class="type-text{{if api.data.error.Value ' has-error'}}"> <label for="" class="type-text{{if api.data.error.Value ' has-error'}}">
<span>Value</span>
{{#if json}} {{#if json}}
<CodeEditor <CodeEditor
@name="value" @name="value"
@readonly={{or disabld api.disabled}} @readonly={{or disabld api.disabled}}
@value={{atob api.data.Value}} @value={{atob api.data.Value}}
@onkeyup={{action api.change "value"}} @onkeyup={{action api.change "value"}}
/> >
<:label>Value</:label>
</CodeEditor>
{{else}} {{else}}
<span>Value</span>
<textarea <textarea
{{disabled (or disabld api.disabled)}} {{disabled (or disabld api.disabled)}}
autofocus={{not api.isCreate}} autofocus={{not api.isCreate}}

View File

@ -36,27 +36,40 @@
<strong>{{item.error.Name.validation}}</strong> <strong>{{item.error.Name.validation}}</strong>
{{/if}} {{/if}}
</label> </label>
<label class="type-text" data-test-rules> <label for="" class="type-text" data-test-rules>
<span>Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
{{#if (eq item.template 'service-identity')}} {{#if (eq item.template 'service-identity')}}
<CodeEditor <CodeEditor
@readonly={{true}} @readonly={{true}}
@name={{concat name "[Rules]"}} @name={{concat name "[Rules]"}}
@syntax="hcl" @syntax="hcl"
@oninput={{action "change" (concat name "[Rules]")}} @oninput={{action "change" (concat name "[Rules]")}}
><Consul::ServiceIdentity::Template >
@nspace={{nspace}} <:label>
@name={{item.Name}} Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
/></CodeEditor> </:label>
<:content>
<Consul::ServiceIdentity::Template
@nspace={{nspace}}
@name={{item.Name}}
/>
</:content>
</CodeEditor>
{{else if (eq item.template 'node-identity')}} {{else if (eq item.template 'node-identity')}}
<CodeEditor <CodeEditor
@readonly={{true}} @readonly={{true}}
@name={{concat name "[Rules]"}} @name={{concat name "[Rules]"}}
@syntax="hcl" @syntax="hcl"
@oninput={{action "change" (concat name "[Rules]")}} @oninput={{action "change" (concat name "[Rules]")}}
><Consul::NodeIdentity::Template >
@name={{item.Name}} <:label>
/></CodeEditor> Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
<:content>
<Consul::NodeIdentity::Template
@name={{item.Name}}
/>
</:content>
</CodeEditor>
{{else}} {{else}}
<CodeEditor <CodeEditor
@syntax="hcl" @syntax="hcl"
@ -64,7 +77,11 @@
@name={{concat name "[Rules]"}} @name={{concat name "[Rules]"}}
@value={{item.Rules}} @value={{item.Rules}}
@onkeyup={{action "change" (concat name "[Rules]")}} @onkeyup={{action "change" (concat name "[Rules]")}}
/> >
<:label>
Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
</CodeEditor>
{{#if item.error.Rules}} {{#if item.error.Rules}}
<strong>{{item.error.Rules.validation}}</strong> <strong>{{item.error.Rules.validation}}</strong>
{{/if}} {{/if}}

View File

@ -114,28 +114,45 @@
</dl> </dl>
{{/if}} {{/if}}
<label class="type-text"> <label class="type-text">
<span>Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
{{#if (eq item.template 'service-identity')}} {{#if (eq item.template 'service-identity')}}
<CodeEditor <CodeEditor
@syntax="hcl" @syntax="hcl"
@readonly={{true}} @readonly={{true}}
><Consul::ServiceIdentity::Template >
@nspace={{nspace}} <:label>
@name={{item.Name}} Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
/></CodeEditor> </:label>
<:content>
<Consul::ServiceIdentity::Template
@nspace={{nspace}}
@name={{item.Name}}
/>
</:content>
</CodeEditor>
{{else if (eq item.template 'node-identity')}} {{else if (eq item.template 'node-identity')}}
<CodeEditor <CodeEditor
@syntax="hcl" @syntax="hcl"
@readonly={{true}} @readonly={{true}}
><Consul::NodeIdentity::Template >
@name={{item.Name}} <:label>
/></CodeEditor> Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
<:content>
<Consul::NodeIdentity::Template
@name={{item.Name}}
/>
</:content>
</CodeEditor>
{{else}} {{else}}
<CodeEditor <CodeEditor
@syntax="hcl" @syntax="hcl"
@readonly={{true}} @readonly={{true}}
@value={{or loadedItem.Rules item.Rules}} @value={{or loadedItem.Rules item.Rules}}
/> >
<:label>
Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
</CodeEditor>
{{/if}} {{/if}}
</label> </label>
{{#if (not disabled)}} {{#if (not disabled)}}

View File

@ -16,8 +16,12 @@
</div> </div>
{{/if}} {{/if}}
<label class="type-text"> <label class="type-text">
<span>Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span> <CodeEditor
<CodeEditor @class={{if item.error.Rules "error"}} @name="Rules" @syntax="hcl" @value={{item.Rules}} @onkeyup={{action "change" "Rules"}} /> @class={{if item.error.Rules "error"}} @name="Rules" @syntax="hcl" @value={{item.Rules}} @onkeyup={{action "change" "Rules"}}>
<:label>
Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a>
</:label>
</CodeEditor>
</label> </label>
{{#if create }} {{#if create }}
<label class="type-text"> <label class="type-text">