HM-134. Добавлен редактор кода Ace и внедрен для работы с хранилищами (#66)

This commit is contained in:
Nikolay
2020-09-06 20:07:01 +03:00
committed by GitHub
parent 5d19104445
commit 966febc7b5
13 changed files with 181 additions and 7 deletions

View File

@ -58,6 +58,7 @@
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.0"
}, },
"dependencies": { "dependencies": {
"ace-builds": "^1.4.12",
"axios": "^0.19.2", "axios": "^0.19.2",
"bootstrap": "^5.0.0-alpha1", "bootstrap": "^5.0.0-alpha1",
"moment": "^2.27.0", "moment": "^2.27.0",

View File

@ -32,6 +32,26 @@
</nav> </nav>
</template> </template>
<!-- Шаблон для CodeEditor -->
<template id="code-editor">
<div class="CodeEditor">
<div class="CodeEditor__editor form-control"></div>
</div>
</template>
<!-- Шаблон для FormCodeEditor -->
<template id="form-code-editor">
<div class="mb-3 FormCodeEditor">
<label class="form-label">
<span class="FormCodeEditor__label"></span>
<span class="FormCodeEditor__star text-danger"></span>
</label>
<input type="text" class="FormCodeEditor__input" />
<div class="FormCodeEditor__editor"></div>
<div class="FormCodeEditor__errorText form-text invalid-feedback"></div>
</div>
</template>
<!-- Шаблон карточек на главной--> <!-- Шаблон карточек на главной-->
<template id="main-statistic"> <template id="main-statistic">
<div class="h-100"> <div class="h-100">

View File

@ -15,6 +15,7 @@ import authServiceApi from './api/AuthServiceAPI';
import userInfoService from './services/UserInfoService'; import userInfoService from './services/UserInfoService';
import {EVENTS, ROUTES} from './core/consts'; import {EVENTS, ROUTES} from './core/consts';
import UsersPage from './pages/users/components/page/Page'; import UsersPage from './pages/users/components/page/Page';
import {DocumentationPage} from './pages/documentation/components/page/Page';
const initAppComponents = () => { const initAppComponents = () => {
const mainMenu = new MainMenu(); const mainMenu = new MainMenu();
@ -40,6 +41,7 @@ const initAppComponents = () => {
{url: ROUTES.USERS, pageComponent: UsersPage}, {url: ROUTES.USERS, pageComponent: UsersPage},
{url: ROUTES.LOGIN, pageComponent: LoginPage}, {url: ROUTES.LOGIN, pageComponent: LoginPage},
{url: ROUTES.PROFILE, pageComponent: ProfilePage}, {url: ROUTES.PROFILE, pageComponent: ProfilePage},
{url: ROUTES.DOCUMENTATION, pageComponent: DocumentationPage},
]); ]);
/** /**

View File

@ -0,0 +1,3 @@
.CodeEditor__editor {
width: 100%;
}

View File

@ -0,0 +1,43 @@
import ace from 'ace-builds/src-noconflict/ace';
import theme from 'ace-builds/src-noconflict/theme-github';
import {Mode as JSONMode} from 'ace-builds/src-noconflict/mode-json';
import Component from '../component/Component';
import {CODE_EDITOR_MODE} from '../../consts';
import './CodeEditor.css';
const Modes = {
[CODE_EDITOR_MODE.JSON]: JSONMode,
};
export class CodeEditor extends Component {
constructor ({
parentNode,
id,
mode,
rows = 5,
} = {}) {
super('#code-editor', parentNode);
this.editorContainer = this.mainNode.querySelector('.CodeEditor__editor');
this.editorContainer.setAttribute('id', id);
this.editorContainer.style.minHeight = `${Math.round(rows * 24.33)}px`;
this.editor = ace.edit(id);
this.editor.session.setMode(new Modes[mode]());
this.editor.setTheme(theme);
}
setValue = (value) => {
this.editor.setValue(value);
}
getValue = () => {
return this.editor.getValue();
}
disabled = (value) => {
this.editor.setReadOnly(value);
}
}

View File

@ -0,0 +1,13 @@
.FormCodeEditor {
position: relative;
}
.FormCodeEditor__input {
position: absolute;
z-index: -1000;
}
.FormCodeEditor__input:invalid + .FormCodeEditor__editor {
border: 1px solid var(--bs-red);
border-radius: .25rem;
}

View File

@ -0,0 +1,69 @@
import Component from '../component/Component';
import {CodeEditor} from '../code-editor/CodeEditor';
import './FormCodeEditor.css';
import {EVENTS} from '../../consts';
export class FormCodeEditor extends Component {
constructor (parentNode, {
id,
mode,
label,
required = false,
initValue = '',
className = '',
rows,
} = {}) {
super('#form-code-editor', parentNode);
this.label = this.mainNode.querySelector('.form-label');
this.labelText = this.mainNode.querySelector('.FormCodeEditor__label');
this.star = this.mainNode.querySelector('.FormCodeEditor__star');
this.editorContainer = this.mainNode.querySelector('.FormCodeEditor__editor');
this.errorText = this.mainNode.querySelector('.FormCodeEditor__errorText');
this.input = this.mainNode.querySelector('.FormCodeEditor__input');
this.addEventListener(this.editorContainer, EVENTS.CLICK, this.clearError);
this.addEventListener(this.editorContainer, EVENTS.KEYDOWN, this.clearError);
this.labelText.textContent = label;
this.editor = this.createComponent(CodeEditor, {
parentNode: this.editorContainer,
id,
mode,
rows,
});
if (className) {
this.editorContainer.classList.add(className);
}
if (required) {
this.star.textContent = ' *';
}
this.setValue(initValue);
}
disabled = (value) => {
this.editor.disabled(value);
}
getValue = () => {
return this.editor.getValue();
}
setValue = (value) => {
this.editor.setValue(value);
}
setError = (errorMessage) => {
this.errorText.textContent = errorMessage;
this.input.setCustomValidity(errorMessage);
}
clearError = () => {
this.setError('');
}
}

View File

@ -88,7 +88,7 @@ class FormControl extends Component {
} }
clearError = () => { clearError = () => {
this.errorText.textContent = ''; this.setError('');
} }
getInputTagName = (type) => { getInputTagName = (type) => {

View File

@ -22,6 +22,10 @@ const NAV_MENU = [
title: 'Пользователи', title: 'Пользователи',
url: ROUTES.USERS, url: ROUTES.USERS,
}, },
{
title: 'Документация',
url: ROUTES.DOCUMENTATION,
},
{ {
title: 'Личный кабинет', title: 'Личный кабинет',
url: ROUTES.PROFILE, url: ROUTES.PROFILE,

View File

@ -69,4 +69,9 @@ export const ROUTES = {
USERS: '/users', USERS: '/users',
LOGIN: '/login', LOGIN: '/login',
PROFILE: '/profile', PROFILE: '/profile',
DOCUMENTATION: '/documentation',
};
export const CODE_EDITOR_MODE = {
JSON: 'json',
}; };

View File

@ -0,0 +1,16 @@
import Component from '../../../../core/components/component/Component';
import {TAG_NAME} from '../../../../core/consts';
export class DocumentationPage extends Component {
constructor (mainNodeSelector, parentNode) {
super(mainNodeSelector, parentNode);
this.createElement({
tagName: TAG_NAME.DIV,
parentNode: this.mainNode,
options: {
textContent: 'Документация',
},
});
}
}

View File

@ -1,6 +1,3 @@
.Api__value-input {
height: 200px;
}
.Api__view-controls { .Api__view-controls {
position: sticky; position: sticky;
bottom: 0; bottom: 0;

View File

@ -1,11 +1,12 @@
import Component from '../../../../core/components/component/Component'; import Component from '../../../../core/components/component/Component';
import ModalSidebar from '../../../../core/components/modal-sidebar/ModalSibebar'; import ModalSidebar from '../../../../core/components/modal-sidebar/ModalSibebar';
import FormControl from '../../../../core/components/form-control/FormControl'; import FormControl from '../../../../core/components/form-control/FormControl';
import {FORM_TYPES, EVENTS, MODES} from '../../../../core/consts'; import {EVENTS, MODES, CODE_EDITOR_MODE} from '../../../../core/consts';
import './ApiTableViewForm.css'; import './ApiTableViewForm.css';
import storageApi from '../../api/StorageServiceAPI'; import storageApi from '../../api/StorageServiceAPI';
import routeService from '../../../../services/RouteService'; import routeService from '../../../../services/RouteService';
import {HooksComponent} from '../hooks-component/HooksComponent'; import {HooksComponent} from '../hooks-component/HooksComponent';
import {FormCodeEditor} from '../../../../core/components/form-code-editor/FormCodeEditor';
const TITLE_MODES = { const TITLE_MODES = {
[MODES.Create]: 'Создание нового хранилища', [MODES.Create]: 'Создание нового хранилища',
@ -52,11 +53,11 @@ class ApiTableViewForm extends Component {
label: 'Краткое описание', label: 'Краткое описание',
required: true, required: true,
}), }),
this.valueInput = this.createComponent(FormControl, this.form, { this.valueInput = this.createComponent(FormCodeEditor, this.form, {
id: 'api-value-input', id: 'api-value-input',
type: FORM_TYPES.TEXTAREA,
className: 'Api__value-input', className: 'Api__value-input',
label: 'Содержимое хранилища', label: 'Содержимое хранилища',
mode: CODE_EDITOR_MODE.JSON,
required: true, required: true,
}), }),
]; ];