📦 NEW: frontend: Modifier info et Article
This commit is contained in:
parent
5eb64d200b
commit
9c5d1fc06b
129
website/components/AddEditFunction.js
Normal file
129
website/components/AddEditFunction.js
Normal file
@ -0,0 +1,129 @@
|
||||
import { Fragment, useState, useEffect } from 'react';
|
||||
import htmlParser from 'html-react-parser';
|
||||
import Loader from '../components/Loader';
|
||||
import useAPI from '../hooks/useAPI';
|
||||
import api from '../utils/api';
|
||||
import '../public/css/pages/admin.css';
|
||||
|
||||
const AddEditFunction = (props) => {
|
||||
|
||||
const [, categories] = useAPI('/categories');
|
||||
const [inputState, setInputState] = useState(props.defaultInputState);
|
||||
const [message, setMessage] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (categories.length > 0 && !props.isEditing) {
|
||||
handleChange({
|
||||
target: {
|
||||
name: "categorieId",
|
||||
value: categories[0].id
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [categories]);
|
||||
|
||||
const apiCallFunction = (formData) => {
|
||||
if (props.isEditing) return api.put(`/admin/functions/${inputState.id}`, formData, { headers: { 'Authorization': props.user.token } });
|
||||
return api.post('/admin/functions', formData, { headers: { 'Authorization': props.user.token } });
|
||||
}
|
||||
|
||||
const handleChange = (event, isTypeCheck = false) => {
|
||||
const inputStateNew = { ...inputState };
|
||||
inputStateNew[event.target.name] = (event.target.files != undefined) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value;
|
||||
setInputState(inputStateNew);
|
||||
}
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
setIsLoading(true);
|
||||
const formData = new FormData();
|
||||
formData.append('type', inputState.type);
|
||||
formData.append('categorieId', inputState.categorieId);
|
||||
formData.append('title', inputState.title);
|
||||
formData.append('slug', inputState.slug);
|
||||
formData.append('description', inputState.description);
|
||||
formData.append('image', inputState.image);
|
||||
|
||||
if (props.isEditing) {
|
||||
formData.append('isOnline', inputState.isOnline);
|
||||
}
|
||||
|
||||
apiCallFunction(formData)
|
||||
.then(() => {
|
||||
setIsLoading(false);
|
||||
window.location.reload(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="title">Titre :</label>
|
||||
<input value={inputState.title} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="slug">Slug :</label>
|
||||
<input value={inputState.slug} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="description">Description :</label>
|
||||
<textarea style={{ height: 'auto' }} value={inputState.description} onChange={handleChange} name="description" id="description" className="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="type">Type :</label>
|
||||
<select onChange={handleChange} name="type" id="type" className="form-control" { ...(props.isEditing) && { value: inputState.type } }>
|
||||
<option value="form">Formulaire</option>
|
||||
<option value="article">Article</option>
|
||||
<option value="page">Page</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="categorieId">Catégorie :</label>
|
||||
<select onChange={handleChange} name="categorieId" id="categorieId" className="form-control" { ...(props.isEditing) && { value: inputState.categorieId } }>
|
||||
{categories.map((category) => (
|
||||
<option key={category.id} value={category.id} className="Admin__Modal-select-option" style={{ backgroundColor: category.color }}>{category.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="image">Image <em>(150x150 recommandé)</em> :</label>
|
||||
<br/>
|
||||
<input onChange={handleChange} accept="image/jpeg,image/jpg,image/png" type="file" name="image" id="image" />
|
||||
</div>
|
||||
|
||||
{(props.isEditing) &&
|
||||
<div className="form-group custom-control custom-switch">
|
||||
<input onChange={(event) => handleChange(event, true)} type="checkbox" name="isOnline" checked={inputState.isOnline} className="custom-control-input" id="isOnline" />
|
||||
<label className="custom-control-label" htmlFor="isOnline">isOnline</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="form-group text-center">
|
||||
<button type="submit" className="btn btn-dark">Envoyer</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="form-result text-center">
|
||||
{
|
||||
(isLoading) ?
|
||||
<Loader />
|
||||
:
|
||||
htmlParser(message)
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddEditFunction;
|
46
website/components/EditArticleFunction.js
Normal file
46
website/components/EditArticleFunction.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { useState } from 'react';
|
||||
import 'suneditor/dist/css/suneditor.min.css';
|
||||
import dynamic from 'next/dynamic';
|
||||
import htmlParser from 'html-react-parser';
|
||||
import { complex } from '../utils/sunEditorConfig';
|
||||
import api from '../utils/api';
|
||||
import 'notyf/notyf.min.css'; // for React and Vue
|
||||
|
||||
const SunEditor = dynamic(
|
||||
() => import('suneditor-react'),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
const EditArticleFunction = (props) => {
|
||||
|
||||
const [htmlContent, setHtmlContent] = useState("");
|
||||
|
||||
const handleEditorChange = (content) => {
|
||||
setHtmlContent(content);
|
||||
}
|
||||
|
||||
const handleSave = async (content) => {
|
||||
let Notyf;
|
||||
if (typeof window != 'undefined') {
|
||||
Notyf = require('notyf');
|
||||
}
|
||||
const notyf = new Notyf.Notyf({
|
||||
duration: 5000
|
||||
});
|
||||
try {
|
||||
await api.put(`/admin/functions/article/${props.functionInfo.id}`, { article: content }, { headers: { 'Authorization': props.user.token } });
|
||||
notyf.success('Sauvegardé!');
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<SunEditor setContents={props.functionInfo.article} lang="fr" onChange={handleEditorChange} setOptions={{ buttonList: complex, callBackSave: handleSave }} />
|
||||
<div className="container-fluid">
|
||||
{htmlParser(htmlContent)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditArticleFunction;
|
@ -1,7 +1,16 @@
|
||||
import htmlParser from 'html-react-parser';
|
||||
import FunctionTabs from './FunctionTabs/FunctionTabs';
|
||||
import FunctionForm from './FunctionForm';
|
||||
import FunctionComments from './FunctionComments/FunctionComments';
|
||||
|
||||
const Article = ({ article }) => {
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
{(article != undefined) && htmlParser(article)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const FunctionTabManager = (props) => {
|
||||
if (props.type === "form") {
|
||||
return (
|
||||
@ -9,7 +18,9 @@ const FunctionTabManager = (props) => {
|
||||
<div className="FunctionComponent__slide">
|
||||
<FunctionForm inputArray={ [...props.utilizationForm || []] } slug={props.slug} />
|
||||
</div>
|
||||
<div className="FunctionComponent__slide text-center">Article</div>
|
||||
<div className="FunctionComponent__slide">
|
||||
<Article article={props.article} />
|
||||
</div>
|
||||
<div className="FunctionComponent__slide">
|
||||
<FunctionComments functionId={props.id} />
|
||||
</div>
|
||||
@ -19,7 +30,9 @@ const FunctionTabManager = (props) => {
|
||||
|
||||
return (
|
||||
<FunctionTabs type={props.type}>
|
||||
<div className="FunctionComponent__slide text-center">Article</div>
|
||||
<div className="FunctionComponent__slide">
|
||||
<Article article={props.article} />
|
||||
</div>
|
||||
<div className="FunctionComponent__slide">
|
||||
<FunctionComments functionId={props.id} />
|
||||
</div>
|
||||
|
19
website/package-lock.json
generated
19
website/package-lock.json
generated
@ -5862,6 +5862,11 @@
|
||||
"sort-keys": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"notyf": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/notyf/-/notyf-3.6.0.tgz",
|
||||
"integrity": "sha512-TqirJJOj5xTrkUGbat94aeFdBCMuJqlxvFAeySV08WRhcZDVzegIsQrYgek1ZN/Vhc+Ux3BgUmndS4ii1W2TJg=="
|
||||
},
|
||||
"nprogress": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||
@ -9537,6 +9542,20 @@
|
||||
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
||||
},
|
||||
"suneditor": {
|
||||
"version": "2.28.4",
|
||||
"resolved": "https://registry.npmjs.org/suneditor/-/suneditor-2.28.4.tgz",
|
||||
"integrity": "sha512-KLmKMq1QrBT9GT+/yv0d/a6YGEQ5+QUSmzZYJaIXGJ8QNNRV0BqfQP0gBnWY8s35HFKiU5meXWuzrI03AmubRg=="
|
||||
},
|
||||
"suneditor-react": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/suneditor-react/-/suneditor-react-2.9.1.tgz",
|
||||
"integrity": "sha512-O26aLQ4dRWbdJv+d78pPaL/mLwWgVMiz64uo531+kHjBlNlREpV2dvvIYVH7TNxKi6LboeDZybJ5rm8kPMchAQ==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2",
|
||||
"suneditor": "^2.28.4"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
|
@ -18,12 +18,14 @@
|
||||
"html-react-parser": "^0.10.2",
|
||||
"next": "9.3.2",
|
||||
"next-fonts": "^1.0.3",
|
||||
"notyf": "^3.6.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"react": "16.13.0",
|
||||
"react-color": "^2.18.0",
|
||||
"react-dom": "16.13.0",
|
||||
"react-swipeable-views": "^0.13.9",
|
||||
"react-swipeable-views-utils": "^0.13.9",
|
||||
"suneditor-react": "^2.9.1",
|
||||
"universal-cookie": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,31 +1,76 @@
|
||||
import { Fragment } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import Cookies from "universal-cookie";
|
||||
import SwipeableViews from 'react-swipeable-views';
|
||||
import HeadTag from '../../components/HeadTag';
|
||||
import AddEditFunction from '../../components/AddEditFunction';
|
||||
import EditArticleFunction from '../../components/EditArticleFunction';
|
||||
import redirect from '../../utils/redirect';
|
||||
import api from '../../utils/api';
|
||||
import { API_URL } from '../../utils/config';
|
||||
import '../../components/FunctionTabs/FunctionTabs.css';
|
||||
import '../../public/css/pages/admin.css';
|
||||
|
||||
const AdminFunctionComponent = (props) => {
|
||||
|
||||
if (!props.user.isAdmin && typeof window != 'undefined') {
|
||||
return redirect({}, '/404');
|
||||
}
|
||||
const [slideIndex, setSlideIndex] = useState(0);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<HeadTag />
|
||||
<p>{props.slug}</p>
|
||||
<HeadTag title={props.functionInfo.title} description={props.functionInfo.description} image={API_URL + props.functionInfo.image} />
|
||||
|
||||
<div className="container-fluid">
|
||||
<div className="container">
|
||||
<div className="row justify-content-center">
|
||||
<ul className="FunctionTabs__nav">
|
||||
<li className="FunctionTabs__nav-item">
|
||||
<a onClick={() => setSlideIndex(0)} className={`FunctionTabs__nav-link ${(slideIndex === 0) && "FunctionTabs__nav-link-active"}`}>✒️ Modifier</a>
|
||||
</li>
|
||||
<li className="FunctionTabs__nav-item">
|
||||
<a onClick={() => setSlideIndex(1)} className={`FunctionTabs__nav-link ${(slideIndex === 1) && "FunctionTabs__nav-link-active"}`}>📝 Article</a>
|
||||
</li>
|
||||
<li className="FunctionTabs__nav-item">
|
||||
<a onClick={() => setSlideIndex(2)} className={`FunctionTabs__nav-link ${(slideIndex === 2) && "FunctionTabs__nav-link-active"}`}>⚙️ Utilisation</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-fluid">
|
||||
<SwipeableViews onChangeIndex={(index) => setSlideIndex(index)} index={slideIndex}>
|
||||
<div className="Admin__Function-slide">
|
||||
<AddEditFunction
|
||||
defaultInputState={{ ...props.functionInfo }}
|
||||
user={props.user}
|
||||
isEditing
|
||||
/>
|
||||
</div>
|
||||
<div className="Admin__Function-slide">
|
||||
<EditArticleFunction { ...props } />
|
||||
</div>
|
||||
</SwipeableViews>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps({ req, params }) {
|
||||
const cookies = new Cookies(req.headers.cookie);
|
||||
const { slug } = params;
|
||||
return {
|
||||
props: {
|
||||
user: { ...cookies.get('user') },
|
||||
slug
|
||||
}
|
||||
};
|
||||
export async function getServerSideProps(context) {
|
||||
const cookies = new Cookies(context.req.headers.cookie);
|
||||
const user = { ...cookies.get('user') };
|
||||
const { slug } = context.params;
|
||||
if (!user.isAdmin) {
|
||||
return redirect(context, '/404');
|
||||
}
|
||||
return api.get(`/admin/functions/${slug}`, { headers: { 'Authorization': user.token } })
|
||||
.then((response) => {
|
||||
return {
|
||||
props: {
|
||||
user,
|
||||
functionInfo: response.data
|
||||
}
|
||||
};
|
||||
})
|
||||
.catch(() => redirect(context, '/404'));
|
||||
}
|
||||
|
||||
export default AdminFunctionComponent;
|
@ -1,67 +1,21 @@
|
||||
import Link from 'next/link';
|
||||
import { Fragment, useState, useEffect } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import Cookies from "universal-cookie";
|
||||
import HeadTag from '../../components/HeadTag';
|
||||
import FunctionsList from '../../components/FunctionsList/FunctionsList';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||
import Modal from '../../components/Modal';
|
||||
import FunctionsList from '../../components/FunctionsList/FunctionsList';
|
||||
import AddEditFunction from '../../components/AddEditFunction';
|
||||
import redirect from '../../utils/redirect';
|
||||
import htmlParser from 'html-react-parser';
|
||||
import Loader from '../../components/Loader';
|
||||
import useAPI from '../../hooks/useAPI';
|
||||
import api from '../../utils/api';
|
||||
import '../../public/css/pages/admin.css';
|
||||
|
||||
const Admin = (props) => {
|
||||
|
||||
const [, categories] = useAPI('/categories');
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [inputState, setInputState] = useState({ type: 'form' });
|
||||
const [message, setMessage] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (categories.length > 0) {
|
||||
handleChange({
|
||||
target: {
|
||||
name: "categorieId",
|
||||
value: categories[0].id
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [categories]);
|
||||
|
||||
const toggleModal = () => setIsOpen(!isOpen);
|
||||
|
||||
const handleChange = (event, isTypeCheck = false) => {
|
||||
const inputStateNew = { ...inputState };
|
||||
inputStateNew[event.target.name] = (event.target.files != undefined) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value;
|
||||
setInputState(inputStateNew);
|
||||
}
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
setIsLoading(true);
|
||||
const formData = new FormData();
|
||||
formData.append('type', inputState.type);
|
||||
formData.append('categorieId', inputState.categorieId);
|
||||
formData.append('title', inputState.title);
|
||||
formData.append('slug', inputState.slug);
|
||||
formData.append('description', inputState.description);
|
||||
formData.append('image', inputState.image);
|
||||
|
||||
api.post('/admin/functions', formData, { headers: { 'Authorization': props.user.token } })
|
||||
.then((response) => {
|
||||
setMessage(`<p class="form-success"><b>Succès:</b> ${response.data.message}</p>`);
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (!props.user.isAdmin && typeof window != 'undefined') {
|
||||
return redirect({}, '/404');
|
||||
}
|
||||
@ -87,58 +41,7 @@ const Admin = (props) => {
|
||||
</div>
|
||||
|
||||
<div className="col-24">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="title">Titre :</label>
|
||||
<input value={inputState.title} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="slug">Slug :</label>
|
||||
<input value={inputState.slug} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="description">Description :</label>
|
||||
<textarea style={{ height: 'auto' }} value={inputState.biography} onChange={handleChange} name="description" id="description" className="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="type">Type :</label>
|
||||
<select onChange={handleChange} name="type" id="type" className="form-control">
|
||||
<option value="form">Formulaire</option>
|
||||
<option value="article">Article</option>
|
||||
<option value="page">Page</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="categorieId">Catégorie :</label>
|
||||
<select onChange={handleChange} name="categorieId" id="categorieId" className="form-control">
|
||||
{categories.map((category) => (
|
||||
<option key={category.id} value={category.id} className="Admin__Modal-select-option" style={{ backgroundColor: category.color }}>{category.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="image">Image <em>(150x150 recommandé)</em> :</label>
|
||||
<br/>
|
||||
<input onChange={handleChange} accept="image/jpeg,image/jpg,image/png" type="file" name="image" id="image" />
|
||||
</div>
|
||||
|
||||
<div className="form-group text-center">
|
||||
<button type="submit" className="btn btn-dark">Envoyer</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="form-result text-center">
|
||||
{
|
||||
(isLoading) ?
|
||||
<Loader />
|
||||
:
|
||||
htmlParser(message)
|
||||
}
|
||||
</div>
|
||||
<AddEditFunction defaultInputState={{ type: 'form' }} { ...props } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,6 +44,9 @@ a:hover {
|
||||
div[aria-hidden="true"] > * {
|
||||
display: none;
|
||||
}
|
||||
.notyf__message {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* LOADING */
|
||||
.isLoading {
|
||||
@ -135,4 +138,67 @@ a, .important {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40;
|
||||
}
|
||||
.custom-control {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.custom-control-input {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
}
|
||||
.custom-control-label {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.custom-control-input:checked~.custom-control-label::before {
|
||||
color: #fff;
|
||||
border-color: #007bff;
|
||||
background-color: #007bff;
|
||||
}
|
||||
.custom-switch .custom-control-label::before {
|
||||
left: -2.25rem;
|
||||
width: 1.75rem;
|
||||
pointer-events: all;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
.custom-control-label::before {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: #fff;
|
||||
border: #adb5bd solid 1px;
|
||||
}
|
||||
.custom-switch .custom-control-input:checked~.custom-control-label::after {
|
||||
background-color: #fff;
|
||||
-webkit-transform: translateX(.75rem);
|
||||
transform: translateX(.75rem);
|
||||
}
|
||||
.custom-switch .custom-control-label::after {
|
||||
top: calc(.25rem + 2px);
|
||||
left: calc(-2.25rem + 2px);
|
||||
width: calc(1rem - 4px);
|
||||
height: calc(1rem - 4px);
|
||||
background-color: #adb5bd;
|
||||
border-radius: .5rem;
|
||||
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||
}
|
||||
.custom-control-label::after {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
content: "";
|
||||
background: no-repeat 50%/50% 50%;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-top: 30px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.FunctionComponent__title {
|
||||
margin: 0;
|
||||
|
@ -29,4 +29,7 @@
|
||||
}
|
||||
.Admin__table-row {
|
||||
padding: 15px;
|
||||
}
|
||||
.Admin__Function-slide {
|
||||
margin-top: 40px;
|
||||
}
|
@ -25,67 +25,4 @@
|
||||
}
|
||||
.Profile__Modal-top-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.custom-control {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.custom-control-input {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
}
|
||||
.custom-control-label {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.custom-control-input:checked~.custom-control-label::before {
|
||||
color: #fff;
|
||||
border-color: #007bff;
|
||||
background-color: #007bff;
|
||||
}
|
||||
.custom-switch .custom-control-label::before {
|
||||
left: -2.25rem;
|
||||
width: 1.75rem;
|
||||
pointer-events: all;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
.custom-control-label::before {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: #fff;
|
||||
border: #adb5bd solid 1px;
|
||||
}
|
||||
.custom-switch .custom-control-input:checked~.custom-control-label::after {
|
||||
background-color: #fff;
|
||||
-webkit-transform: translateX(.75rem);
|
||||
transform: translateX(.75rem);
|
||||
}
|
||||
.custom-switch .custom-control-label::after {
|
||||
top: calc(.25rem + 2px);
|
||||
left: calc(-2.25rem + 2px);
|
||||
width: calc(1rem - 4px);
|
||||
height: calc(1rem - 4px);
|
||||
background-color: #adb5bd;
|
||||
border-radius: .5rem;
|
||||
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||
}
|
||||
.custom-control-label::after {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
content: "";
|
||||
background: no-repeat 50%/50% 50%;
|
||||
}
|
14
website/utils/sunEditorConfig.js
Normal file
14
website/utils/sunEditorConfig.js
Normal file
@ -0,0 +1,14 @@
|
||||
export const complex = [
|
||||
["undo", "redo"],
|
||||
["font", "fontSize", "formatBlock"],
|
||||
["bold", "underline", "italic", "strike", "subscript", "superscript"],
|
||||
["removeFormat"],
|
||||
"/",
|
||||
["fontColor", "hiliteColor"],
|
||||
["outdent", "indent"],
|
||||
["align", "horizontalRule", "list", "table"],
|
||||
["link", "image", "video"],
|
||||
["fullScreen", "showBlocks", "codeView"],
|
||||
["preview", "print"],
|
||||
["save", "template"]
|
||||
];
|
Reference in New Issue
Block a user