Fixed upload system
This commit is contained in:
parent
ba2b9dcde2
commit
d2236fe041
10 changed files with 233 additions and 147 deletions
|
|
@ -25,6 +25,7 @@ use Illuminate\Support\Str;
|
|||
use Illuminate\Validation\ValidationException;
|
||||
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
class UploadController extends Controller
|
||||
{
|
||||
|
|
@ -80,17 +81,22 @@ class UploadController extends Controller
|
|||
|
||||
$uplodedFile = $save->getFile();
|
||||
|
||||
/** @throws \Illuminate\Validation\ValidationException */
|
||||
$this->validateUploadedFile($uplodedFile);
|
||||
try {
|
||||
/** @throws \Illuminate\Validation\ValidationException */
|
||||
$this->validateUploadedFile($uplodedFile);
|
||||
|
||||
/** @var \App\Models\Media */
|
||||
$media = Media::whereId($request->media_id)
|
||||
->whereHas('dossier', function ($q) use ($dossier) {
|
||||
$q->whereId($dossier->id);
|
||||
})->firstOrNew();
|
||||
/** @var \App\Models\Media */
|
||||
$media = Media::whereId($request->media_id)
|
||||
->whereHas('dossier', function ($q) use ($dossier) {
|
||||
$q->whereId($dossier->id);
|
||||
})->firstOrNew();
|
||||
|
||||
// Save the file and return any response you need.
|
||||
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier);
|
||||
// Save the file and return any response you need.
|
||||
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier);
|
||||
} catch (ValidationException $e) {
|
||||
// 400 instead of 422 is for resumablejs to handle properly.
|
||||
return \response()->json($e->errors(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
// We are in chunk mode, lets send the current progress.
|
||||
|
|
@ -121,6 +127,7 @@ class UploadController extends Controller
|
|||
if ($receiver->isUploaded() === false) {
|
||||
throw new UploadMissingFileException();
|
||||
}
|
||||
|
||||
// Receive the file.
|
||||
$save = $receiver->receive();
|
||||
|
||||
|
|
@ -131,19 +138,24 @@ class UploadController extends Controller
|
|||
|
||||
$uplodedFile = $save->getFile();
|
||||
|
||||
/** @throws \Illuminate\Validation\ValidationException */
|
||||
$this->validateUploadedFile($uplodedFile);
|
||||
try {
|
||||
/** @throws \Illuminate\Validation\ValidationException */
|
||||
$this->validateUploadedFile($uplodedFile);
|
||||
|
||||
/** @var \App\Models\Media */
|
||||
$media = Media::whereId($request->media_id)
|
||||
->whereHas('sujet', function ($q) use ($sujet) {
|
||||
$q->whereId($sujet->id);
|
||||
})->whereHas('dossier', function ($q) use ($dossier) {
|
||||
$q->whereId($dossier->id);
|
||||
})->firstOrNew();
|
||||
/** @var \App\Models\Media */
|
||||
$media = Media::whereId($request->media_id)
|
||||
->whereHas('sujet', function ($q) use ($sujet) {
|
||||
$q->whereId($sujet->id);
|
||||
})->whereHas('dossier', function ($q) use ($dossier) {
|
||||
$q->whereId($dossier->id);
|
||||
})->firstOrNew();
|
||||
|
||||
// Save the file and return any response you need.
|
||||
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier, $sujet);
|
||||
// Save the file and return any response you need.
|
||||
return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier, $sujet);
|
||||
} catch (ValidationException $e) {
|
||||
// 400 instead of 422 is for resumablejs to handle properly.
|
||||
return \response()->json($e->errors(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
// We are in chunk mode, lets send the current progress.
|
||||
|
|
@ -170,22 +182,40 @@ class UploadController extends Controller
|
|||
], [
|
||||
'files.mimes' => trans('Ce type de fichier n\'est pas accepté.')
|
||||
])->validate();
|
||||
$this->validateExtension($file->getClientOriginalExtension());
|
||||
$this->validateExtension($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file extension matches allowed extensions enum
|
||||
*
|
||||
* @param string $extension
|
||||
* @param \Illuminate\Http\UploadedFile $file
|
||||
* @return void
|
||||
*/
|
||||
private function validateExtension(string $extension)
|
||||
private function validateExtension(UploadedFile $file)
|
||||
{
|
||||
if (!\is_null(MediaExtension::tryFrom($extension))) {
|
||||
ValidationException::withMessages([
|
||||
if (\is_null(MediaExtension::tryFrom($file->getClientOriginalExtension()))) {
|
||||
throw ValidationException::withMessages([
|
||||
'file' => trans('Ce type de fichier n\'est pas accepté.')
|
||||
]);
|
||||
}
|
||||
|
||||
/** @throws \ValueError If value is not defined in enum */
|
||||
$mediaExtension = MediaExtension::from($file->getClientOriginalExtension());
|
||||
|
||||
if ($file->guessExtension()) {
|
||||
$mt = new MimeTypes();
|
||||
$guessedExts = $mt->getExtensions($mt->guessMimeType($file->path()));
|
||||
|
||||
if (!\in_array($mediaExtension->value, $guessedExts)) {
|
||||
$guessedExts = \implode(',', $guessedExts);
|
||||
throw ValidationException::withMessages([
|
||||
'file' => trans('File guessed extensions :guessedExts does not match file name extension :extension', [
|
||||
'guessedExt' => $guessedExts,
|
||||
'extension' => $mediaExtension,
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -232,7 +262,6 @@ class UploadController extends Controller
|
|||
/** @var string */
|
||||
$sanitizedFileExtension = \pathinfo($sanitizedFileName, \PATHINFO_EXTENSION);
|
||||
|
||||
// * Validate Extension
|
||||
/** @throws \ValueError If value is not defined in enum */
|
||||
$mediaExtension = MediaExtension::from($sanitizedFileExtension);
|
||||
|
||||
|
|
@ -309,9 +338,7 @@ class UploadController extends Controller
|
|||
$media->saveOrFail();
|
||||
|
||||
// * Get props inserted by DB.
|
||||
$media->refresh();
|
||||
|
||||
return response()->json($media);
|
||||
return response()->json($media->refresh());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
config/chunk-upload.php
Normal file
41
config/chunk-upload.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* The storage config
|
||||
*/
|
||||
'storage' => [
|
||||
/*
|
||||
* Returns the folder name of the chunks. The location is in storage/app/{folder_name}
|
||||
*/
|
||||
'chunks' => 'chunks',
|
||||
'disk' => 'chunk_upload',
|
||||
],
|
||||
'clear' => [
|
||||
/*
|
||||
* How old chunks we should delete
|
||||
*/
|
||||
'timestamp' => '-3 HOURS',
|
||||
'schedule' => [
|
||||
'enabled' => true,
|
||||
'cron' => '25 * * * *', // run every hour on the 25th minute
|
||||
],
|
||||
],
|
||||
'chunk' => [
|
||||
// setup for the chunk naming setup to ensure same name upload at same time
|
||||
'name' => [
|
||||
'use' => [
|
||||
'session' => true, // should the chunk name use the session id? The uploader must send cookie!,
|
||||
'browser' => false, // instead of session we can use the ip and browser?
|
||||
],
|
||||
],
|
||||
],
|
||||
'handlers' => [
|
||||
// A list of handlers/providers that will be appended to existing list of handlers
|
||||
'custom' => [],
|
||||
// Overrides the list of handlers - use only what you really want
|
||||
'override' => [
|
||||
// \Pion\Laravel\ChunkUpload\Handler\DropZoneUploadHandler::class
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
@ -63,12 +63,19 @@ return [
|
|||
'root' => storage_path('arcadia_link/send'),
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
'arcadia_link_r' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('arcadia_link/receive'),
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
'chunk_upload' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('upload'),
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
|
|
|
|||
|
|
@ -316,10 +316,22 @@ export default {
|
|||
this.setupResumable();
|
||||
});
|
||||
},
|
||||
uploadFileError(file, message) {
|
||||
this.uploadErrors.push(
|
||||
`Une erreur durant le transfert du fichier ${file.fileName}`
|
||||
);
|
||||
uploadFileError(file, httpMessage) {
|
||||
let message = `Une erreur durant le transfert du fichier ${file.fileName}`;
|
||||
|
||||
const error = JSON.parse(httpMessage);
|
||||
if (error.file) {
|
||||
const errors = error.file;
|
||||
if (errors instanceof String) {
|
||||
message = errors;
|
||||
}
|
||||
if (errors instanceof Object) {
|
||||
message = Object.values(errors).reduce(function (previous, current) {
|
||||
return String(previous) + String(current);
|
||||
});
|
||||
}
|
||||
}
|
||||
this.uploadErrors.push(message);
|
||||
},
|
||||
removeFile(fileToRemove) {
|
||||
this.r.removeFile(fileToRemove);
|
||||
|
|
|
|||
|
|
@ -61,8 +61,12 @@
|
|||
class="media"
|
||||
:style="`background-image:url('${media.url}')`"
|
||||
></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video"></div>
|
||||
<div v-if="media.type_media_id === 4" class="media son"></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video">
|
||||
<font-awesome-icon icon="file-video" />
|
||||
</div>
|
||||
<div v-if="media.type_media_id === 4" class="media son">
|
||||
<font-awesome-icon icon="file-audio" />
|
||||
</div>
|
||||
|
||||
<p class="libelle">{{ media.libelle }}</p>
|
||||
<div class="info">
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</button>
|
||||
<button
|
||||
v-if="(storeRole === 1 || storeRole === 4) && !selectionContainsLockedMedias"
|
||||
v-on:click.prevent="deleteMedias"
|
||||
v-on:click.prevent="deleteMedia"
|
||||
class="action_delete_media"
|
||||
>
|
||||
<font-awesome-icon icon="trash-alt" />
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
class="btn btn_form_download ml-10"
|
||||
@click="performDownload"
|
||||
v-on:click.prevent="performDownload"
|
||||
>
|
||||
<font-awesome-icon icon="arrow-circle-down" />
|
||||
<span> Télécharger</span>
|
||||
|
|
@ -93,11 +93,11 @@
|
|||
</div>
|
||||
|
||||
<div class="upload_container high-level">
|
||||
<span class="fileinput-button" @click="addFiles">
|
||||
<a class="btn btn-primary basic-style">
|
||||
<span class="fileinput-button" v-on:click.prevent="addFiles">
|
||||
<button class="btn btn-primary basic-style">
|
||||
<font-awesome-icon icon="plus-circle" style="background-color: unset" />
|
||||
<span> Ajouter vos médias</span>
|
||||
</a>
|
||||
</button>
|
||||
</span>
|
||||
<div class="content_wrapper">
|
||||
<!-- The container for the uploaded errors -->
|
||||
|
|
@ -119,12 +119,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="upload_bar">
|
||||
<a id="submit" class="btn basic-style" href="#" @click="createMedias">
|
||||
Transférer
|
||||
</a>
|
||||
<a id="close_upload_form" class="basic-style" href="#" @click="closeUploadFrom">
|
||||
Fermer
|
||||
</a>
|
||||
<button id="submit" class="btn basic-style" v-on:click.prevent="createMedia">
|
||||
<span>Transférer</span>
|
||||
</button>
|
||||
<button
|
||||
id="close_upload_form"
|
||||
class="btn btn-red basic-style"
|
||||
v-on:click.prevent="closeUploadFrom"
|
||||
>
|
||||
<span>Fermer</span>
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
<input style="display: none" ref="fileupload" type="file" name="files[]" multiple />
|
||||
|
|
@ -136,14 +140,23 @@
|
|||
</h3>
|
||||
<hr />
|
||||
<div class="list_uploaded_files">
|
||||
<p class="list_uploaded_files_p" v-for="file in uploadedFilesErrors">
|
||||
<span> {{ file.libelle }}</span>
|
||||
<span> {{ file.extension }}</span>
|
||||
<span> {{ file.message }}</span>
|
||||
</p>
|
||||
<p class="list_uploaded_files_p" v-for="file in uploadedFiles">
|
||||
{{ file.libelle }}.{{ file.extension }}
|
||||
<button class="btn btn-sm btn-primary" @click="openMedia(file)">Editer</button>
|
||||
<button class="btn btn-sm btn-primary" v-on:click.prevent="openMedia(file)">
|
||||
<span>Editer</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="margin-top: 20px; text-align: center">
|
||||
<a @click="CloseListUploadedFiles" class="btn" href="#">Fermer</a>
|
||||
<button v-on:click.prevent="CloseListUploadedFiles" class="btn">
|
||||
<span>Fermer</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -187,9 +200,11 @@ export default {
|
|||
etiquettes: [],
|
||||
files: [],
|
||||
uploadedFiles: [],
|
||||
uploadedFilesErrors: [],
|
||||
selectionContainsLockedMedias: true,
|
||||
format_id: null,
|
||||
waitingRequest: false,
|
||||
resumable: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -245,7 +260,7 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
deleteMedias() {
|
||||
deleteMedia() {
|
||||
const self = this,
|
||||
mediaList = this.storeGrabbed;
|
||||
if (Object.keys(mediaList).length === 0) {
|
||||
|
|
@ -429,20 +444,21 @@ export default {
|
|||
type: "error",
|
||||
});
|
||||
} else {
|
||||
if (this.r === undefined) this.setupResumable();
|
||||
if (!this.resumable) this.setupResumable();
|
||||
$(".upload_container").show("fast");
|
||||
}
|
||||
},
|
||||
closeUploadFrom() {
|
||||
this.files = [];
|
||||
this.resumable.files = [];
|
||||
$(".upload_container").hide("fast");
|
||||
},
|
||||
addFiles() {
|
||||
this.$refs.fileupload.click();
|
||||
},
|
||||
removeFile(file) {
|
||||
this.r.removeFile(file);
|
||||
this.files = this.r.files;
|
||||
this.resumable.removeFile(file);
|
||||
this.files = this.resumable.files;
|
||||
},
|
||||
/**
|
||||
* @return {string}
|
||||
|
|
@ -462,8 +478,7 @@ export default {
|
|||
}
|
||||
},
|
||||
setupResumable() {
|
||||
this.r = null;
|
||||
this.r = new Resumable({
|
||||
this.resumable = new Resumable({
|
||||
target: `/ajax/upload-media/${this.storeSujet}/${this.storeFolder}`,
|
||||
simultaneousUploads: 3,
|
||||
maxFiles: 8,
|
||||
|
|
@ -476,11 +491,11 @@ export default {
|
|||
},
|
||||
});
|
||||
|
||||
this.r.on("fileAdded", this.uploadFileAdded);
|
||||
this.r.on("fileSuccess", this.uploadFileSuccess);
|
||||
this.r.on("fileError", this.uploadFileError);
|
||||
this.r.on("complete", this.uploadFileComplete);
|
||||
this.r.assignBrowse(this.$refs.fileupload);
|
||||
this.resumable.on("fileAdded", this.uploadFileAdded);
|
||||
this.resumable.on("fileSuccess", this.uploadFileSuccess);
|
||||
this.resumable.on("fileError", this.uploadFileError);
|
||||
this.resumable.on("complete", this.uploadFileComplete);
|
||||
this.resumable.assignBrowse(this.$refs.fileupload);
|
||||
},
|
||||
uploadFileAdded(file, event) {
|
||||
const acceptFileTypes = /^(gif|jpe?g|png|mp4|mp3|psd|ai|svg|pdf)$/i;
|
||||
|
|
@ -489,13 +504,23 @@ export default {
|
|||
file.fileName.split(".").pop().length &&
|
||||
!acceptFileTypes.test(file.fileName.split(".").pop())
|
||||
) {
|
||||
this.r.removeFile(file);
|
||||
this.resumable.removeFile(file);
|
||||
$.fancyMessage({
|
||||
title: "Attention",
|
||||
message: file.fileName + " ce type de fichier n'est pas pris en charge.",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
if (file.size.length && file.size > 5000000) {
|
||||
this.r.removeFile(file);
|
||||
this.resumable.removeFile(file);
|
||||
$.fancyMessage({
|
||||
title: "Attention",
|
||||
message: file.fileName + " ce type de fichier est trop grand.",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
|
||||
this.files = this.r.files;
|
||||
this.files = this.resumable.files;
|
||||
},
|
||||
uploadFileSuccess(file, message) {
|
||||
this.uploadedFiles.push(JSON.parse(message));
|
||||
|
|
@ -515,34 +540,51 @@ export default {
|
|||
uploadFileComplete() {
|
||||
this.closeUploadFrom();
|
||||
this.$loading.hide();
|
||||
//$.fancybox.close();
|
||||
// $.fancybox.open({
|
||||
// src : '#ListUploadedFiles',
|
||||
// type : 'inline',
|
||||
// opts : {
|
||||
// modal: true,
|
||||
// animationDuration : 350,
|
||||
// animationEffect : 'material',
|
||||
// }
|
||||
// });
|
||||
$.fancybox.close();
|
||||
$.fancybox.open({
|
||||
src: "#ListUploadedFiles",
|
||||
type: "inline",
|
||||
opts: {
|
||||
modal: true,
|
||||
animationDuration: 350,
|
||||
animationEffect: "material",
|
||||
},
|
||||
});
|
||||
console.log("complete");
|
||||
},
|
||||
uploadFileError(file, message) {
|
||||
uploadFileError(file, httpMessage) {
|
||||
let message = "Erreur lors de l'opération, merci d'actualiser la page.";
|
||||
this.closeUploadFrom();
|
||||
this.$loading.hide();
|
||||
$.fancybox.close();
|
||||
const error = JSON.parse(message);
|
||||
console.log(error);
|
||||
console.log(error.files);
|
||||
|
||||
const error = JSON.parse(httpMessage);
|
||||
if (error.file) {
|
||||
const errors = error.file;
|
||||
if (errors instanceof String) {
|
||||
message = errors;
|
||||
}
|
||||
if (errors instanceof Object) {
|
||||
message = Object.values(errors).reduce(function (previous, current) {
|
||||
return String(previous) + String(current);
|
||||
});
|
||||
}
|
||||
this.uploadedFilesErrors.push({
|
||||
libelle: file.fileName,
|
||||
extension: file.file.type,
|
||||
message: message,
|
||||
});
|
||||
console.warn(errors);
|
||||
}
|
||||
$.fancyMessage({
|
||||
title: "Erreur lors du téléchargement",
|
||||
message: error.files[0],
|
||||
title: "Erreur",
|
||||
message: message,
|
||||
type: "error",
|
||||
});
|
||||
},
|
||||
createMedias() {
|
||||
createMedia() {
|
||||
this.$loading.show({ delay: 0 });
|
||||
this.r.upload();
|
||||
this.resumable.upload();
|
||||
},
|
||||
openMedia(media) {
|
||||
$.fancybox.close();
|
||||
|
|
@ -553,12 +595,17 @@ export default {
|
|||
},
|
||||
CloseListUploadedFiles() {
|
||||
if (
|
||||
!this.uploadedFiles.length ||
|
||||
confirm(
|
||||
"En cliquant sur OK, vous confirmez que vous avez édité tous vos fichiers."
|
||||
)
|
||||
) {
|
||||
$.fancybox.close();
|
||||
this.uploadedFiles = [];
|
||||
this.uploadedFilesErrors = [];
|
||||
// * Reset input file.
|
||||
this.$refs.fileupload.files = new DataTransfer().files;
|
||||
this.$refs.fileupload.value = "";
|
||||
this.$store.commit({
|
||||
type: "Reload",
|
||||
reload: Math.random(),
|
||||
|
|
|
|||
|
|
@ -62,8 +62,12 @@
|
|||
class="media"
|
||||
:style="`background-image:url('${media.url}')`"
|
||||
></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video"></div>
|
||||
<div v-if="media.type_media_id === 4" class="media son"></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video">
|
||||
<font-awesome-icon icon="file-video" />
|
||||
</div>
|
||||
<div v-if="media.type_media_id === 4" class="media son">
|
||||
<font-awesome-icon icon="file-audio" />
|
||||
</div>
|
||||
<div class="bar">
|
||||
<a href="#" class="media_info" @click="openMedia(media)">
|
||||
<font-awesome-icon icon="info-circle" />
|
||||
|
|
|
|||
|
|
@ -77,8 +77,12 @@
|
|||
class="media"
|
||||
:style="`background-image:url('${media.url}')`"
|
||||
></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video"></div>
|
||||
<div v-if="media.type_media_id === 4" class="media son"></div>
|
||||
<div v-if="media.type_media_id === 3" class="media video">
|
||||
<font-awesome-icon icon="file-video" />
|
||||
</div>
|
||||
<div v-if="media.type_media_id === 4" class="media son">
|
||||
<font-awesome-icon icon="file-audio" />
|
||||
</div>
|
||||
|
||||
<p class="libelle">{{ media.libelle }}</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
|
||||
& > #actionsBar {
|
||||
float: left;
|
||||
button {
|
||||
& > div > button {
|
||||
@include resetButton;
|
||||
font-size: 13px;
|
||||
padding: 15px 13px;
|
||||
|
|
@ -365,7 +365,7 @@
|
|||
background-position: center;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
& svg {
|
||||
transform: translate(-50%, -50%);
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
|
@ -377,26 +377,6 @@
|
|||
font-size: 59px;
|
||||
z-index: 1;
|
||||
}
|
||||
&.video {
|
||||
&::before {
|
||||
content: "\f167";
|
||||
font-family: "Font Awesome 6 Brands";
|
||||
}
|
||||
}
|
||||
&.son {
|
||||
&::before {
|
||||
content: "\f58f";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
&.doc {
|
||||
&::before {
|
||||
content: "\f15c";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
}
|
||||
.libelle {
|
||||
padding: 4px;
|
||||
|
|
@ -758,7 +738,7 @@
|
|||
background-position: center;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
& svg {
|
||||
transform: translate(-50%, -50%);
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
|
@ -770,26 +750,6 @@
|
|||
font-size: 59px;
|
||||
z-index: 1;
|
||||
}
|
||||
&.video {
|
||||
&::before {
|
||||
content: "\f167";
|
||||
font-family: "Font Awesome 6 Brands";
|
||||
}
|
||||
}
|
||||
&.son {
|
||||
&::before {
|
||||
content: "\f58f";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
&.doc {
|
||||
&::before {
|
||||
content: "\f15c";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
|
|||
|
|
@ -185,26 +185,6 @@
|
|||
font-size: 59px;
|
||||
z-index: 1;
|
||||
}
|
||||
&.video {
|
||||
&::before {
|
||||
content: "\f167";
|
||||
font-family: "Font Awesome 6 Brands";
|
||||
}
|
||||
}
|
||||
&.son {
|
||||
&::before {
|
||||
content: "\f58f";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
&.doc {
|
||||
&::before {
|
||||
content: "\f15c";
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
}
|
||||
.libelle {
|
||||
padding: 4px;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue