mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-19 13:19:42 +02:00
added new block
This commit is contained in:
parent
28580e300b
commit
680bcde28c
12 changed files with 949 additions and 395 deletions
60
package-lock.json
generated
60
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/abort-controller": "^3.374.0",
|
||||||
"@aws-sdk/client-s3": "^3.181.0",
|
"@aws-sdk/client-s3": "^3.181.0",
|
||||||
"@codex-team/config-loader": "0.1.0-rc1",
|
"@codex-team/config-loader": "0.1.0-rc1",
|
||||||
"@codexteam/shortcuts": "^1.2.0",
|
"@codexteam/shortcuts": "^1.2.0",
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.5",
|
||||||
"csurf": "^1.2.2",
|
"csurf": "^1.2.2",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
|
"editorjs-header-with-alignment": "^1.0.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"file-type": "^16.5.4",
|
"file-type": "^16.5.4",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
@ -50,9 +52,9 @@
|
||||||
"@babel/polyfill": "^7.12.1",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@codexteam/misprints": "^1.0.0",
|
"@codexteam/misprints": "^1.0.0",
|
||||||
|
"@coolbytes/editorjs-delimiter": "^1.0.3",
|
||||||
"@editorjs/checklist": "^1.3.0",
|
"@editorjs/checklist": "^1.3.0",
|
||||||
"@editorjs/code": "^2.7.0",
|
"@editorjs/code": "^2.7.0",
|
||||||
"@editorjs/delimiter": "^1.2.0",
|
|
||||||
"@editorjs/editorjs": "^2.25.0",
|
"@editorjs/editorjs": "^2.25.0",
|
||||||
"@editorjs/embed": "^2.5.1",
|
"@editorjs/embed": "^2.5.1",
|
||||||
"@editorjs/header": "^2.6.2",
|
"@editorjs/header": "^2.6.2",
|
||||||
|
@ -427,17 +429,39 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/abort-controller": {
|
"node_modules/@aws-sdk/abort-controller": {
|
||||||
"version": "3.178.0",
|
"version": "3.374.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.178.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.374.0.tgz",
|
||||||
"integrity": "sha512-ptDkCB06BJrYdhKzamM9yI15LxcGkPczY80hzKAY/aecm09alnW27uCt5HJJx2nCd18IUH28ZO1sc7DTLOWb3A==",
|
"integrity": "sha512-pO1pqFBdIF28ZvnJmg58Erj35RLzXsTrjvHghdc/xgtSvodFFCNrUsPg6AP3On8eiw9elpHoS4P8jMx1pHDXEw==",
|
||||||
"license": "Apache-2.0",
|
"deprecated": "This package has moved to @smithy/abort-controller",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/types": "3.178.0",
|
"@smithy/abort-controller": "^1.0.1",
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.5.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@aws-sdk/abort-controller/node_modules/@smithy/abort-controller": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@smithy/types": "^1.2.0",
|
||||||
|
"tslib": "^2.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@aws-sdk/abort-controller/node_modules/@smithy/types": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-cognito-identity": {
|
"node_modules/@aws-sdk/client-cognito-identity": {
|
||||||
|
@ -3524,6 +3548,12 @@
|
||||||
"integrity": "sha512-Udb8lkwhXEiPoLm7krtUv2f8jYQTutHxsLecmsMvMbOxMJ49LA/EUUzn8Fo32mxOFrI7qozOovspLhHb+y60nQ==",
|
"integrity": "sha512-Udb8lkwhXEiPoLm7krtUv2f8jYQTutHxsLecmsMvMbOxMJ49LA/EUUzn8Fo32mxOFrI7qozOovspLhHb+y60nQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@coolbytes/editorjs-delimiter": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@coolbytes/editorjs-delimiter/-/editorjs-delimiter-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-Ix0z5ujv12Y+JXkUypxtdX36OsDBNyjLo/k8oPxBcq6UxCS1F96dXvE9XRb/tpN6qW8BbWUfViyBbeOLAbGkLw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
|
@ -3600,13 +3630,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@editorjs/delimiter": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/delimiter/-/delimiter-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-GKsCFPk85vH5FuCuVQ48NTLc9hk0T3DsBH9zABaicTYIJayFcUa8N4/Y+L3i4tduzDqqyvoxkv+5n43GmC5gEA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/editorjs": {
|
"node_modules/@editorjs/editorjs": {
|
||||||
"version": "2.30.8",
|
"version": "2.30.8",
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.8.tgz",
|
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.8.tgz",
|
||||||
|
@ -7964,6 +7987,11 @@
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/editorjs-header-with-alignment": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/editorjs-header-with-alignment/-/editorjs-header-with-alignment-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-L7cDSx/H3wgb9dg05HH9xPMotj3tkoElTd0TeY81pxvO0+tPV+yRZ9hV7fUyZolYUObKfYL9QS+yeTY/YCht5g=="
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"test:js": "cross-env NODE_ENV=testing mocha --recursive ./dist/test --exit",
|
"test:js": "cross-env NODE_ENV=testing mocha --recursive ./dist/test --exit",
|
||||||
"test": "cross-env NODE_ENV=testing ts-mocha -n loader=ts-node/esm ./src/test/*.ts ./src/test/**/*.ts --exit ",
|
"test": "cross-env NODE_ENV=testing ts-mocha -n loader=ts-node/esm ./src/test/*.ts ./src/test/**/*.ts --exit ",
|
||||||
"lint": "eslint --fix --ext .ts ./src/backend",
|
"lint": "eslint --fix --ext .ts ./src/backend",
|
||||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest"
|
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/abort-controller": "^3.374.0",
|
"@aws-sdk/abort-controller": "^3.374.0",
|
||||||
|
@ -37,6 +37,8 @@
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.5",
|
||||||
"csurf": "^1.2.2",
|
"csurf": "^1.2.2",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
|
"editorjs-header-with-alignment": "^1.0.1",
|
||||||
|
"editorjs-alert": "^1.1.4",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"file-type": "^16.5.4",
|
"file-type": "^16.5.4",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
@ -66,10 +68,11 @@
|
||||||
"@codexteam/misprints": "^1.0.0",
|
"@codexteam/misprints": "^1.0.0",
|
||||||
"@editorjs/checklist": "^1.3.0",
|
"@editorjs/checklist": "^1.3.0",
|
||||||
"@editorjs/code": "^2.7.0",
|
"@editorjs/code": "^2.7.0",
|
||||||
"@editorjs/delimiter": "^1.2.0",
|
"@coolbytes/editorjs-delimiter": "^1.0.3",
|
||||||
"@editorjs/editorjs": "^2.25.0",
|
"@editorjs/editorjs": "^2.25.0",
|
||||||
"@editorjs/embed": "^2.5.1",
|
"@editorjs/embed": "^2.5.1",
|
||||||
"@editorjs/header": "^2.6.2",
|
"@editorjs/header": "^2.6.2",
|
||||||
|
"@editorjs/paragraph": "^2.11.7",
|
||||||
"@editorjs/image": "^2.6.2",
|
"@editorjs/image": "^2.6.2",
|
||||||
"@editorjs/inline-code": "^1.3.1",
|
"@editorjs/inline-code": "^1.3.1",
|
||||||
"@editorjs/link": "^2.4.0",
|
"@editorjs/link": "^2.4.0",
|
||||||
|
|
|
@ -102,16 +102,23 @@ function createApp(): express.Express {
|
||||||
if (appConfig.hawk?.backendToken && err instanceof Error) {
|
if (appConfig.hawk?.backendToken && err instanceof Error) {
|
||||||
HawkCatcher.send(err);
|
HawkCatcher.send(err);
|
||||||
}
|
}
|
||||||
|
// CSRF error (from csurf)
|
||||||
|
if (err && typeof err === 'object' && 'code' in err && (err as any).code === 'EBADCSRFTOKEN') {
|
||||||
|
res.status(403);
|
||||||
|
return res.render('error', { status: 403, message: 'Invalid CSRF token' });
|
||||||
|
}
|
||||||
// only send Http based exception to client.
|
// only send Http based exception to client.
|
||||||
if (err instanceof HttpException) {
|
if (err instanceof HttpException) {
|
||||||
// set locals, only providing error in development
|
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||||
// render the error page
|
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500);
|
||||||
res.render('error');
|
if (err.status === 403) {
|
||||||
|
return res.render('error', { status: 403, message: 'Доступ запрещён' });
|
||||||
}
|
}
|
||||||
next(err);
|
return res.render('error');
|
||||||
|
}
|
||||||
|
// fallback for other errors
|
||||||
|
res.status(500).render('error', { status: 500, message: 'Internal Server Error' });
|
||||||
});
|
});
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
|
|
10
src/backend/views/pages/blocks/alert.twig
Normal file
10
src/backend/views/pages/blocks/alert.twig
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="cdx-alert cdx-alert-{{ type }}{% if align %} cdx-alert-align-{{ align }}{% endif %}">
|
||||||
|
{% if title is not empty %}
|
||||||
|
<div class="cdx-alert__title">
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="cdx-alert__message">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,22 +1,21 @@
|
||||||
{% set width = data.width|default('100%') %}
|
{% set classes = ['block-image__content'] %}
|
||||||
{% set alignment = data.alignment|default('center') %}
|
|
||||||
|
|
||||||
<figure class="block-image" style="width: {{ width }}; max-width: 100%; margin:
|
{% if withBorder %}
|
||||||
{% if alignment == 'left' %}0 auto 0 0
|
{% set classes = classes|merge(['block-image__content--bordered']) %}
|
||||||
{% elseif alignment == 'right' %}0 0 0 auto
|
{% endif %}
|
||||||
{% else %}0 auto
|
|
||||||
{% endif %};">
|
{% if withBackground %}
|
||||||
|
{% set classes = classes|merge(['block-image__content--with-background']) %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<figure class="block-image">
|
||||||
<div class="{{ classes.join(' ') }}">
|
<div class="{{ classes.join(' ') }}">
|
||||||
{% if file.mime and file.mime == 'video/mp4' %}
|
{% if file.mime and file.mime == 'video/mp4' %}
|
||||||
<video controls style="width: 100%;">
|
<video autoplay loop muted playsinline>
|
||||||
<source src="{{ file.url }}" type="video/mp4">
|
<source src="{{ file.url }}" type="video/mp4">
|
||||||
</video>
|
</video>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img
|
<img src="{{ file.url }}" alt="{{ caption ? caption | striptags : '' }}">
|
||||||
src="{{ file.url }}"
|
|
||||||
alt="{{ caption ? caption | striptags : '' }}"
|
|
||||||
style="width: {{ width }};"
|
|
||||||
>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
{% set width = width|default('100%') %}
|
|
||||||
{% set height = height|default('auto') %}
|
|
||||||
{% set alignment = alignment|default('center') %}
|
{% set alignment = alignment|default('center') %}
|
||||||
{% set filetype = filetype|default('file') %}
|
{% set filetype = filetype|default('file') %}
|
||||||
|
|
||||||
<figure class="block-video" style="width: {{ width }}; max-width: 100%; margin:
|
<figure class="block-video" style="margin:
|
||||||
{% if alignment == 'left' %}0 auto 0 0
|
{% if alignment == 'left' %}0 auto 0 0
|
||||||
{% elseif alignment == 'right' %}0 0 0 auto
|
{% elseif alignment == 'right' %}0 0 0 auto
|
||||||
{% else %}0 auto
|
{% else %}0 auto
|
||||||
|
@ -15,20 +13,14 @@
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
|
|
||||||
></iframe>
|
></iframe>
|
||||||
{% else %}
|
{% else %}
|
||||||
<video
|
<video controls>
|
||||||
controls
|
|
||||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
|
|
||||||
{% if width %}width="{{ width }}"{% endif %}
|
|
||||||
{% if height %}height="{{ height }}"{% endif %}
|
|
||||||
>
|
|
||||||
<source src="{{ url }}" type="{{ mime|default('video/mp4') }}">
|
<source src="{{ url }}" type="{{ mime|default('video/mp4') }}">
|
||||||
Âàø áðàóçåð íå ïîääåðæèâàåò âèäåî òåã.
|
Ваш браузер не поддерживает видео тег.
|
||||||
</video>
|
</video>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if caption %}
|
{% if caption %}
|
||||||
<footer class="block-video__caption">
|
<footer class="block-video__caption">
|
||||||
{{ caption|raw }}
|
{{ caption|raw }}
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
{% for block in page.body.blocks %}
|
{% for block in page.body.blocks %}
|
||||||
{# Skip first header, because it is already showed as a Title #}
|
{# Skip first header, because it is already showed as a Title #}
|
||||||
{% if not (loop.first and block.type == 'header') %}
|
{% if not (loop.first and block.type == 'header') %}
|
||||||
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist', 'linkTool', 'raw', 'embed','video'] %}
|
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist', 'linkTool', 'raw', 'embed', 'video', 'alert'] %}
|
||||||
<div class="page__content-block">
|
<div class="page__content-block">
|
||||||
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
import ImageTool from '@editorjs/image';
|
|
||||||
|
|
||||||
export default class CustomImageTool extends ImageTool {
|
|
||||||
static get toolbox() {
|
|
||||||
return ImageTool.toolbox; // Возвращаем стандартную иконку
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor({ data, api, config }) {
|
|
||||||
super({ data, api, config });
|
|
||||||
this.alignment = data.alignment || 'center';
|
|
||||||
this.width = data.width || '100%';
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const container = super.render();
|
|
||||||
this._container = container;
|
|
||||||
|
|
||||||
// Добавляем обработчики после полного рендера
|
|
||||||
setTimeout(() => {
|
|
||||||
const wrapper = container.querySelector('.cdx-image');
|
|
||||||
if (wrapper) {
|
|
||||||
this._applyStyles(wrapper);
|
|
||||||
this._addControls(wrapper);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
_applyStyles(wrapper) {
|
|
||||||
wrapper.style.display = 'block';
|
|
||||||
wrapper.style.width = 'fit-content';
|
|
||||||
wrapper.style.margin = this._getAlignmentMargin();
|
|
||||||
|
|
||||||
const img = wrapper.querySelector('img');
|
|
||||||
if (img) {
|
|
||||||
img.style.width = this.width;
|
|
||||||
img.style.height = 'auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getAlignmentMargin() {
|
|
||||||
switch(this.alignment) {
|
|
||||||
case 'left': return '0 auto 0 0';
|
|
||||||
case 'right': return '0 0 0 auto';
|
|
||||||
default: return '0 auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_addControls(wrapper) {
|
|
||||||
// Добавляем кнопки выравнивания
|
|
||||||
const alignControls = document.createElement('div');
|
|
||||||
alignControls.className = 'image-align-controls';
|
|
||||||
|
|
||||||
['left', 'center', 'right'].forEach(align => {
|
|
||||||
const btn = document.createElement('button');
|
|
||||||
btn.innerHTML = this._getAlignmentIcon(align);
|
|
||||||
btn.dataset.align = align;
|
|
||||||
btn.classList.toggle('active', this.alignment === align);
|
|
||||||
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
this.alignment = align;
|
|
||||||
wrapper.style.margin = this._getAlignmentMargin();
|
|
||||||
alignControls.querySelectorAll('button').forEach(b => {
|
|
||||||
b.classList.toggle('active', b.dataset.align === align);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
alignControls.appendChild(btn);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Добавляем ручку ресайза
|
|
||||||
const resizeHandle = document.createElement('div');
|
|
||||||
resizeHandle.className = 'image-resize-handle';
|
|
||||||
resizeHandle.innerHTML = '↔';
|
|
||||||
|
|
||||||
resizeHandle.addEventListener('mousedown', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const img = wrapper.querySelector('img');
|
|
||||||
if (img) this._initResize(img, e);
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapper.appendChild(alignControls);
|
|
||||||
wrapper.appendChild(resizeHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initResize(element, startEvent) {
|
|
||||||
const startWidth = parseInt(element.style.width) || element.offsetWidth;
|
|
||||||
const startX = startEvent.clientX;
|
|
||||||
const maxWidth = this._container.offsetWidth;
|
|
||||||
|
|
||||||
const doResize = (e) => {
|
|
||||||
const newWidth = Math.max(200, Math.min(startWidth + (e.clientX - startX), maxWidth));
|
|
||||||
element.style.width = `${newWidth}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopResize = () => {
|
|
||||||
document.removeEventListener('mousemove', doResize);
|
|
||||||
document.removeEventListener('mouseup', stopResize);
|
|
||||||
this.width = element.style.width;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', doResize);
|
|
||||||
document.addEventListener('mouseup', stopResize, { once: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
save(blockContent) {
|
|
||||||
const savedData = super.save(blockContent);
|
|
||||||
return {
|
|
||||||
...savedData,
|
|
||||||
width: this.width,
|
|
||||||
alignment: this.alignment
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_getAlignmentIcon(align) {
|
|
||||||
const icons = {
|
|
||||||
left: '⎸',
|
|
||||||
center: '⎸⎹',
|
|
||||||
right: ' |'
|
|
||||||
};
|
|
||||||
return icons[align] || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,12 +4,15 @@ import EditorJS from '@editorjs/editorjs';
|
||||||
* Block Tools for the Editor
|
* Block Tools for the Editor
|
||||||
*/
|
*/
|
||||||
import Header from '@editorjs/header';
|
import Header from '@editorjs/header';
|
||||||
|
import Paragraph from '@editorjs/paragraph';
|
||||||
import Image from '@editorjs/image';
|
import Image from '@editorjs/image';
|
||||||
import CodeTool from '@editorjs/code';
|
import CodeTool from '@editorjs/code';
|
||||||
import List from '@editorjs/list';
|
import List from '@editorjs/list';
|
||||||
import Delimiter from '@editorjs/delimiter';
|
//import Delimiter from '@editorjs/delimiter';
|
||||||
|
import Delimiter from '@coolbytes/editorjs-delimiter';
|
||||||
import Table from '@editorjs/table';
|
import Table from '@editorjs/table';
|
||||||
import Warning from '@editorjs/warning';
|
import Warning from '@editorjs/warning';
|
||||||
|
import Alert from 'editorjs-alert';
|
||||||
import Checklist from '@editorjs/checklist';
|
import Checklist from '@editorjs/checklist';
|
||||||
import LinkTool from '@editorjs/link';
|
import LinkTool from '@editorjs/link';
|
||||||
import RawTool from '@editorjs/raw';
|
import RawTool from '@editorjs/raw';
|
||||||
|
@ -23,7 +26,6 @@ import Marker from '@editorjs/marker';
|
||||||
import { TextColorTool } from './text-color-tool.js';
|
import { TextColorTool } from './text-color-tool.js';
|
||||||
import { TextSizeTool } from './text-size-tool.js';
|
import { TextSizeTool } from './text-size-tool.js';
|
||||||
import VideoTool from './video-tool.js';
|
import VideoTool from './video-tool.js';
|
||||||
import CustomImageTool from './custom-image-tool.js';
|
|
||||||
/*
|
/*
|
||||||
* Class for working with Editor.js
|
* Class for working with Editor.js
|
||||||
*/
|
*/
|
||||||
|
@ -50,7 +52,11 @@ export default class Editor {
|
||||||
placeholder: options.headerPlaceholder || '',
|
placeholder: options.headerPlaceholder || '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
paragraph: {
|
||||||
|
class: Paragraph,
|
||||||
|
inlineToolbar: true,
|
||||||
|
},
|
||||||
|
//header: Header,
|
||||||
textColor: {
|
textColor: {
|
||||||
class: TextColorTool,
|
class: TextColorTool,
|
||||||
config: {
|
config: {
|
||||||
|
@ -124,6 +130,8 @@ export default class Editor {
|
||||||
inlineToolbar: true,
|
inlineToolbar: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
alert: Alert,
|
||||||
|
|
||||||
checklist: {
|
checklist: {
|
||||||
class: Checklist,
|
class: Checklist,
|
||||||
inlineToolbar: true,
|
inlineToolbar: true,
|
||||||
|
@ -146,13 +154,14 @@ export default class Editor {
|
||||||
|
|
||||||
embed: Embed,
|
embed: Embed,
|
||||||
},
|
},
|
||||||
|
defaultBlock: 'Paragraph',
|
||||||
data: {
|
data: {
|
||||||
blocks: [
|
blocks: [
|
||||||
{
|
{
|
||||||
type: 'header',
|
type: 'header',
|
||||||
data: {
|
data: {
|
||||||
text: '',
|
text: '',
|
||||||
level: 2,
|
level: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -170,4 +179,6 @@ export default class Editor {
|
||||||
save() {
|
save() {
|
||||||
return this.editor.saver.save();
|
return this.editor.saver.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,28 +6,24 @@ export default class VideoTool {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor({ data, api, config, block }) { // Äîáàâëÿåì block â ïàðàìåòðû
|
constructor({ data, api, config, block }) {
|
||||||
this.data = data || {};
|
this.data = data || {};
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.config = config || {};
|
this.config = config || {};
|
||||||
this.block = block; // Ñîõðàíÿåì ññûëêó íà òåêóùèé áëîê
|
this.block = block;
|
||||||
this.wrapper = null;
|
this.wrapper = null;
|
||||||
this.alignment = data.alignment || 'center';
|
this.alignment = data.alignment || 'center';
|
||||||
this.width = data.width || '100%';
|
|
||||||
this.filetype = data.filetype || 'file';
|
this.filetype = data.filetype || 'file';
|
||||||
|
|
||||||
// Ïðèâÿçûâàåì êîíòåêñò äëÿ îáðàáîò÷èêîâ
|
|
||||||
this._handleFileUpload = this._handleFileUpload.bind(this);
|
this._handleFileUpload = this._handleFileUpload.bind(this);
|
||||||
this._initResize = this._initResize.bind(this);
|
|
||||||
this._handleUrlSubmit = this._handleUrlSubmit.bind(this);
|
this._handleUrlSubmit = this._handleUrlSubmit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.wrapper = document.createElement('div');
|
this.wrapper = document.createElement('div');
|
||||||
this.wrapper.classList.add('video-tool');
|
this.wrapper.classList.add('video-tool');
|
||||||
|
|
||||||
if (this.data.url) {
|
if (this.data.url) {
|
||||||
// Äîáàâëÿåì ïðîâåðêó äëÿ ñîâìåñòèìîñòè ñî ñòàðûìè äàííûìè
|
|
||||||
const filetype = this.data.filetype ||
|
const filetype = this.data.filetype ||
|
||||||
(this.data.url.includes('youtube') ? 'youtube' :
|
(this.data.url.includes('youtube') ? 'youtube' :
|
||||||
this.data.url.includes('rutube') ? 'rutube' : 'file');
|
this.data.url.includes('rutube') ? 'rutube' : 'file');
|
||||||
|
@ -61,7 +57,7 @@ export default class VideoTool {
|
||||||
<option value="rutube">Rutube</option>
|
<option value="rutube">Rutube</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" class="embed-url" placeholder="Paste video URL...">
|
<input type="text" class="embed-url" placeholder="Paste video URL...">
|
||||||
<button class="embed-submit">Âñòàâèòü âèäåî</button>
|
<button class="embed-submit">Добавить</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,30 +71,26 @@ export default class VideoTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
_bindFormHandlers() {
|
_bindFormHandlers() {
|
||||||
// Tab switching
|
|
||||||
this.wrapper.querySelectorAll('.tab-button').forEach(btn => {
|
this.wrapper.querySelectorAll('.tab-button').forEach(btn => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
this.wrapper.querySelectorAll('.tab-button, .tab-content').forEach(el => {
|
this.wrapper.querySelectorAll('.tab-button, .tab-content').forEach(el => {
|
||||||
el.classList.toggle('active', el.dataset.tab === btn.dataset.tab);
|
el.classList.toggle('active', el.dataset.tab === btn.dataset.tab);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ïîñëå ïåðåêëþ÷åíèÿ âêëàäêè ïåðåïðèâÿçûâàåì îáðàáîò÷èêè
|
|
||||||
this._bindFormHandlers();
|
this._bindFormHandlers();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// File upload handler
|
|
||||||
const fileInput = this.wrapper.querySelector('.video-file-input');
|
const fileInput = this.wrapper.querySelector('.video-file-input');
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.addEventListener('change', this._handleFileUpload);
|
fileInput.addEventListener('change', this._handleFileUpload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL embed handler
|
|
||||||
const embedSubmit = this.wrapper.querySelector('.embed-submit');
|
const embedSubmit = this.wrapper.querySelector('.embed-submit');
|
||||||
if (embedSubmit) {
|
if (embedSubmit) {
|
||||||
embedSubmit.addEventListener('click', this._handleUrlSubmit);
|
embedSubmit.addEventListener('click', this._handleUrlSubmit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleFileUpload(event) {
|
async _handleFileUpload(event) {
|
||||||
if (!event.target.files || event.target.files.length === 0) {
|
if (!event.target.files || event.target.files.length === 0) {
|
||||||
|
@ -129,51 +121,44 @@ export default class VideoTool {
|
||||||
const url = urlInput.value.trim();
|
const url = urlInput.value.trim();
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
this._showError('Ïîæàëóéñòà, ââåäèòå URL âèäåî');
|
this._showError(', URL ');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const submitBtn = wrapper.querySelector('.embed-submit');
|
const submitBtn = wrapper.querySelector('.embed-submit');
|
||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
submitBtn.textContent = 'Îáðàáîòêà...';
|
submitBtn.textContent = '...';
|
||||||
|
|
||||||
const embedUrl = this._parseEmbedUrl(service, url);
|
const embedUrl = this._parseEmbedUrl(service, url);
|
||||||
|
|
||||||
// Îáíîâëÿåì äàííûå òåêóùåãî áëîêà
|
|
||||||
this.data = {
|
this.data = {
|
||||||
url: embedUrl,
|
url: embedUrl,
|
||||||
filetype: service,
|
filetype: service,
|
||||||
width: this.width,
|
|
||||||
alignment: this.alignment,
|
alignment: this.alignment,
|
||||||
caption: ''
|
caption: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ñîõðàíÿåì èçìåíåíèÿ
|
|
||||||
this.api.blocks.update(this.block.id, this.data);
|
this.api.blocks.update(this.block.id, this.data);
|
||||||
|
|
||||||
// Ïåðåñîçäàåì ýëåìåíò
|
|
||||||
this.wrapper.innerHTML = '';
|
this.wrapper.innerHTML = '';
|
||||||
this._createVideoElement(embedUrl, '');
|
this._createVideoElement(embedUrl, '');
|
||||||
|
|
||||||
// Âîññòàíàâëèâàåì êíîïêó
|
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtn.textContent = 'Âñòàâèòü âèäåî';
|
submitBtn.textContent = ' ';
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._showError(error.message);
|
this._showError(error.message);
|
||||||
const submitBtn = wrapper.querySelector('.embed-submit');
|
const submitBtn = wrapper.querySelector('.embed-submit');
|
||||||
if (submitBtn) {
|
if (submitBtn) {
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtn.textContent = 'Âñòàâèòü âèäåî';
|
submitBtn.textContent = ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_parseEmbedUrl(service, url) {
|
_parseEmbedUrl(service, url) {
|
||||||
// YouTube
|
|
||||||
if (service === 'youtube') {
|
if (service === 'youtube') {
|
||||||
// Ïîääåðæêà âñåõ ôîðìàòîâ YouTube ññûëîê
|
|
||||||
const regExp = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i;
|
const regExp = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i;
|
||||||
const match = url.match(regExp);
|
const match = url.match(regExp);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
|
@ -181,30 +166,26 @@ export default class VideoTool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rutube
|
|
||||||
if (service === 'rutube') {
|
if (service === 'rutube') {
|
||||||
// Ïîääåðæêà âñåõ ôîðìàòîâ Rutube ññûëîê
|
|
||||||
const regExp = /rutube\.ru\/(?:video\/|play\/embed\/|video\/embed\/)?([a-zA-Z0-9]+)/i;
|
const regExp = /rutube\.ru\/(?:video\/|play\/embed\/|video\/embed\/)?([a-zA-Z0-9]+)/i;
|
||||||
const match = url.match(regExp);
|
const match = url.match(regExp);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
return `https://rutube.ru/play/embed/${match[1]}`;
|
return `https://rutube.ru/play/embed/${match[1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Íåâåðíûé URL Rutube. Ïðèìåð ïðàâèëüíîãî ôîðìàòà: https://rutube.ru/video/CODE/');
|
throw new Error(' URL Rutube. : https://rutube.ru/video/CODE/');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Invalid URL.');
|
throw new Error('Invalid URL.');
|
||||||
}
|
}
|
||||||
|
|
||||||
_createVideoElement(url, caption, filetype = null) {
|
_createVideoElement(url, caption, filetype = null) {
|
||||||
this.wrapper.innerHTML = '';
|
this.wrapper.innerHTML = '';
|
||||||
|
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.className = 'video-container';
|
container.className = 'video-container';
|
||||||
container.style.margin = this._getAlignmentMargin();
|
container.style.margin = this._getAlignmentMargin();
|
||||||
container.style.width = this.width;
|
|
||||||
|
|
||||||
// Îïðåäåëÿåì òèï êîíòåíòà
|
|
||||||
const type = filetype || this.filetype ||
|
const type = filetype || this.filetype ||
|
||||||
(url.match(/\.(mp4|webm|ogg)$/i) ? 'file' :
|
(url.match(/\.(mp4|webm|ogg)$/i) ? 'file' :
|
||||||
(url.includes('youtube.com/embed') || url.includes('youtu.be') ? 'youtube' :
|
(url.includes('youtube.com/embed') || url.includes('youtu.be') ? 'youtube' :
|
||||||
|
@ -243,9 +224,8 @@ _createVideoElement(url, caption, filetype = null) {
|
||||||
|
|
||||||
container.appendChild(mediaContainer);
|
container.appendChild(mediaContainer);
|
||||||
this._addCaption(container, caption);
|
this._addCaption(container, caption);
|
||||||
this._addResizeHandle(container);
|
|
||||||
this.wrapper.appendChild(container);
|
this.wrapper.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
_addCaption(container, captionText) {
|
_addCaption(container, captionText) {
|
||||||
if (!captionText) return;
|
if (!captionText) return;
|
||||||
|
@ -284,59 +264,6 @@ _createVideoElement(url, caption, filetype = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addResizeHandle(container) {
|
|
||||||
const video = container.querySelector('video, iframe');
|
|
||||||
|
|
||||||
if (!video) return; // Åñëè âèäåî íå íàéäåíî, âûõîäèì
|
|
||||||
|
|
||||||
const handle = document.createElement('div');
|
|
||||||
handle.className = 'resize-handle';
|
|
||||||
handle.innerHTML = '-';
|
|
||||||
|
|
||||||
handle.addEventListener('mousedown', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const startWidth = parseInt(video.style.width) || video.offsetWidth;
|
|
||||||
const startX = e.clientX;
|
|
||||||
|
|
||||||
const doResize = (e) => {
|
|
||||||
const newWidth = Math.max(200, startWidth + (e.clientX - startX));
|
|
||||||
video.style.width = `${newWidth}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopResize = () => {
|
|
||||||
document.removeEventListener('mousemove', doResize);
|
|
||||||
document.removeEventListener('mouseup', stopResize);
|
|
||||||
this.width = video.style.width;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', doResize);
|
|
||||||
document.addEventListener('mouseup', stopResize, { once: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
container.appendChild(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initResize(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const video = this.wrapper.querySelector('video');
|
|
||||||
const startWidth = parseInt(video.style.width) || video.offsetWidth;
|
|
||||||
const startX = e.clientX;
|
|
||||||
|
|
||||||
const doResize = (e) => {
|
|
||||||
const newWidth = Math.max(200, startWidth + (e.clientX - startX));
|
|
||||||
video.style.width = `${newWidth}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopResize = () => {
|
|
||||||
document.removeEventListener('mousemove', doResize);
|
|
||||||
document.removeEventListener('mouseup', stopResize);
|
|
||||||
this.width = video.style.width;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', doResize);
|
|
||||||
document.addEventListener('mouseup', stopResize, { once: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
save(blockContent) {
|
save(blockContent) {
|
||||||
const video = blockContent.querySelector('video, iframe');
|
const video = blockContent.querySelector('video, iframe');
|
||||||
const caption = blockContent.querySelector('.video-caption');
|
const caption = blockContent.querySelector('.video-caption');
|
||||||
|
@ -344,7 +271,6 @@ _createVideoElement(url, caption, filetype = null) {
|
||||||
return {
|
return {
|
||||||
url: video?.src || this.data.url || '',
|
url: video?.src || this.data.url || '',
|
||||||
caption: caption?.innerHTML || this.data.caption || '',
|
caption: caption?.innerHTML || this.data.caption || '',
|
||||||
width: this.width || '100%',
|
|
||||||
alignment: this.alignment,
|
alignment: this.alignment,
|
||||||
filetype: this.filetype || (video?.tagName === 'IFRAME' ?
|
filetype: this.filetype || (video?.tagName === 'IFRAME' ?
|
||||||
(video.src.includes('youtube') ? 'youtube' : 'rutube')
|
(video.src.includes('youtube') ? 'youtube' : 'rutube')
|
||||||
|
@ -363,25 +289,22 @@ _createVideoElement(url, caption, filetype = null) {
|
||||||
_showError(message) {
|
_showError(message) {
|
||||||
if (!this.wrapper) return;
|
if (!this.wrapper) return;
|
||||||
|
|
||||||
// Ñîõðàíÿåì òåêóùåå ñîñòîÿíèå ôîðìû
|
|
||||||
const currentTab = this.wrapper.querySelector('.tab-button.active').dataset.tab;
|
const currentTab = this.wrapper.querySelector('.tab-button.active').dataset.tab;
|
||||||
const currentUrl = this.wrapper.querySelector('.embed-url')?.value || '';
|
const currentUrl = this.wrapper.querySelector('.embed-url')?.value || '';
|
||||||
|
|
||||||
this.wrapper.innerHTML = `
|
this.wrapper.innerHTML = `
|
||||||
<div class="video-error">
|
<div class="video-error">
|
||||||
<p>${message}</p>
|
<p>${message}</p>
|
||||||
<button class="retry-button">Ïîïðîáîâàòü ñíîâà</button>
|
<button class="retry-button"> </button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Îáðàáîò÷èê äëÿ êíîïêè ïîâòîðà
|
|
||||||
this.wrapper.querySelector('.retry-button').addEventListener('click', () => {
|
this.wrapper.querySelector('.retry-button').addEventListener('click', () => {
|
||||||
this._createUploadForm();
|
this._createUploadForm();
|
||||||
// Âîññòàíàâëèâàåì ñîñòîÿíèå
|
|
||||||
if (currentTab === 'embed') {
|
if (currentTab === 'embed') {
|
||||||
const embedUrl = this.wrapper.querySelector('.embed-url');
|
const embedUrl = this.wrapper.querySelector('.embed-url');
|
||||||
if (embedUrl) embedUrl.value = currentUrl;
|
if (embedUrl) embedUrl.value = currentUrl;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -374,6 +374,73 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert (Editor.js style)
|
||||||
|
* ==================
|
||||||
|
*/
|
||||||
|
.cdx-alert {
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.cdx-alert__title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.cdx-alert__message {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.cdx-alert-align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.cdx-alert-align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.cdx-alert-align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.cdx-alert-primary {
|
||||||
|
background-color: #ebf8ff;
|
||||||
|
border: 1px solid #4299e1;
|
||||||
|
color: #2b6cb0;
|
||||||
|
}
|
||||||
|
.cdx-alert-secondary {
|
||||||
|
background-color: #f7fafc;
|
||||||
|
border: 1px solid #cbd5e0;
|
||||||
|
color: #222731;
|
||||||
|
}
|
||||||
|
.cdx-alert-info {
|
||||||
|
background-color: #e6fdff;
|
||||||
|
border: 1px solid #4cd4ce;
|
||||||
|
color: #00727c;
|
||||||
|
}
|
||||||
|
.cdx-alert-success {
|
||||||
|
background-color: #f0fff4;
|
||||||
|
border: 1px solid #68d391;
|
||||||
|
color: #2f855a;
|
||||||
|
}
|
||||||
|
.cdx-alert-warning {
|
||||||
|
background-color: #fffaf0;
|
||||||
|
border: 1px solid #ed8936;
|
||||||
|
color: #c05621;
|
||||||
|
}
|
||||||
|
.cdx-alert-danger {
|
||||||
|
background-color: #fff5f5;
|
||||||
|
border: 1px solid #fc8181;
|
||||||
|
color: #c53030;
|
||||||
|
}
|
||||||
|
.cdx-alert-light {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #edf2f7;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
.cdx-alert-dark {
|
||||||
|
background-color: #2d3748;
|
||||||
|
border: 1px solid #1a202c;
|
||||||
|
color: #d3d3d3;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checklist
|
* Checklist
|
||||||
* ==================
|
* ==================
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue