front y back minimos
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*.db
|
||||
back/data/
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
|
13
back/.config/dotnet-tools.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "9.0.8",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
4
back/Constants.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
public static class Constants
|
||||
{
|
||||
public const string Data = "data";
|
||||
}
|
12
back/DTO/PhotoFormModel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace back.DTO;
|
||||
|
||||
public class PhotoFormModel
|
||||
{
|
||||
public required string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
public string? People { get; set; }
|
||||
public IFormFile? Image { get; set; }
|
||||
public string? Ubicacion { get; set; }
|
||||
public string? Evento { get; set; }
|
||||
}
|
@@ -9,13 +9,22 @@ public class Program
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
Directory.CreateDirectory(Constants.Data);
|
||||
// Add services to the container.
|
||||
builder.Services.AddDbContext<PhotoContext>(options =>options.UseSqlite("Data Source=photos.db"));
|
||||
builder.Services.AddDbContext<PhotoContext>(options => options.UseSqlite($"Data Source={Constants.Data}/photos.db"));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll",
|
||||
builder => builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
@@ -29,6 +38,7 @@ public class Program
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCors("AllowAll");
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
|
@@ -1,24 +1,14 @@
|
||||
using back.ApiService.context;
|
||||
using back.ApiService.models;
|
||||
using back.DTO;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace back.controllers;
|
||||
|
||||
public class PhotoFormModel
|
||||
{
|
||||
public required string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
public string? People { get; set; }
|
||||
public IFormFile? Image { get; set; }
|
||||
public string? Ubicacion { get; set; }
|
||||
public string? Evento { get; set; }
|
||||
}
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class PhotoController(PhotoContext photoContext) : ControllerBase
|
||||
public class PhotosController(PhotoContext photoContext) : ControllerBase
|
||||
{
|
||||
private readonly PhotoContext _photoContext = photoContext;
|
||||
|
||||
@@ -42,13 +32,28 @@ public class PhotoController(PhotoContext photoContext) : ControllerBase
|
||||
}
|
||||
|
||||
// GET api/<PhotoController>/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<Photo>> Get(Guid id)
|
||||
[HttpGet("{id}/{res}")]
|
||||
public async Task<IActionResult> Get(Guid id, string res = "low")
|
||||
{
|
||||
var photo = await _photoContext.Photos.FindAsync(id);
|
||||
if (photo == null)
|
||||
return NotFound();
|
||||
return photo;
|
||||
|
||||
string? filePath = res.ToLower() switch
|
||||
{
|
||||
"low" => photo.LowResUrl,
|
||||
"mid" => photo.MidResUrl,
|
||||
"high" => photo.HighResUrl,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (filePath == null || !System.IO.File.Exists(Path.Combine(Constants.Data, filePath)))
|
||||
return NotFound();
|
||||
|
||||
var fileBytes = await System.IO.File.ReadAllBytesAsync(Path.Combine(Constants.Data, filePath));
|
||||
var contentType = "image/jpeg"; // Cambia si usas otro formato
|
||||
|
||||
return File(fileBytes, contentType);
|
||||
}
|
||||
|
||||
// POST api/<PhotoController>
|
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
@@ -10,29 +11,33 @@ public class PhotoBuilder
|
||||
public static Photo Build(string? title, string? description, List<string>? tags, List<string>? personsIn, IFormFile image)
|
||||
{
|
||||
// Genera un nombre de archivo único
|
||||
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(image.FileName)}";
|
||||
var photo = new Photo(title, description, tags, personsIn, fileName);
|
||||
var id = Guid.NewGuid();
|
||||
var fileName = $"{id}{Path.GetExtension(image.FileName)}";
|
||||
var photo = new Photo(title, description, tags, personsIn, fileName)
|
||||
{
|
||||
Id = id
|
||||
};
|
||||
|
||||
// Asegura que los directorios existen
|
||||
Directory.CreateDirectory(Photo.LowResFolder);
|
||||
Directory.CreateDirectory(Photo.MidResFolder);
|
||||
Directory.CreateDirectory(Photo.HighResFolder);
|
||||
Directory.CreateDirectory(Path.Join(Constants.Data, Photo.LowResFolder));
|
||||
Directory.CreateDirectory(Path.Join(Constants.Data, Photo.MidResFolder));
|
||||
Directory.CreateDirectory(Path.Join(Constants.Data, Photo.HighResFolder));
|
||||
|
||||
// Procesa y guarda las imágenes
|
||||
using var stream = image.OpenReadStream();
|
||||
using var img = Image.Load(stream);
|
||||
// Baja resolución (480px)
|
||||
img.Mutate(x => x.Resize(new ResizeOptions { Size = new Size(480, 0), Mode = ResizeMode.Max }));
|
||||
img.Save(photo.LowResUrl);
|
||||
img.Save(Path.Join(Constants.Data, photo.LowResUrl));
|
||||
|
||||
// Media resolución (720px)
|
||||
img.Mutate(x => x.Resize(new ResizeOptions { Size = new Size(720, 0), Mode = ResizeMode.Max }));
|
||||
img.Save(photo.MidResUrl);
|
||||
img.Save(Path.Join(Constants.Data, photo.MidResUrl));
|
||||
|
||||
// Original
|
||||
stream.Position = 0;
|
||||
using var original = Image.Load(stream);
|
||||
original.Save(photo.HighResUrl);
|
||||
original.Save(Path.Join(Constants.Data, photo.HighResUrl));
|
||||
|
||||
return photo;
|
||||
}
|
||||
@@ -45,7 +50,7 @@ public class Photo
|
||||
public const string MidResFolder = "imgs/mid";
|
||||
public const string HighResFolder = "imgs/high";
|
||||
|
||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
|
BIN
back/photos.db-shm
Normal file
0
back/photos.db-wal
Normal file
3
front/public/assets/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"bff": "https://localhost:7273"
|
||||
}
|
Before Width: | Height: | Size: 6.7 MiB |
Before Width: | Height: | Size: 3.6 MiB |
Before Width: | Height: | Size: 10 MiB |
Before Width: | Height: | Size: 16 MiB |
Before Width: | Height: | Size: 12 MiB |
Before Width: | Height: | Size: 8.7 MiB |
Before Width: | Height: | Size: 7.9 MiB |
Before Width: | Height: | Size: 6.6 MiB |
Before Width: | Height: | Size: 5.3 MiB |
Before Width: | Height: | Size: 5.5 MiB |
Before Width: | Height: | Size: 18 MiB |
Before Width: | Height: | Size: 7.1 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 16 MiB |
Before Width: | Height: | Size: 16 MiB |
Before Width: | Height: | Size: 19 MiB |
Before Width: | Height: | Size: 9.4 MiB |
Before Width: | Height: | Size: 5.8 MiB |
Before Width: | Height: | Size: 21 MiB |
Before Width: | Height: | Size: 8.6 MiB |
Before Width: | Height: | Size: 16 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 11 MiB |
Before Width: | Height: | Size: 13 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 6.1 MiB |
Before Width: | Height: | Size: 17 MiB |
Before Width: | Height: | Size: 16 MiB |
Before Width: | Height: | Size: 3.5 MiB |
Before Width: | Height: | Size: 18 MiB |
Before Width: | Height: | Size: 6.7 MiB |
@@ -1,7 +1,7 @@
|
||||
<header class="main-header">
|
||||
<div class="header-container">
|
||||
<!-- Logo/Marca -->
|
||||
<div class="logo">
|
||||
<div class="logo" (click)="navigateTo('home')" style="cursor:pointer;">
|
||||
<h1>{{ brandName }}</h1>
|
||||
</div>
|
||||
|
||||
|
@@ -8,11 +8,11 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(variables.$primary-white, 0.95);
|
||||
background: variables.$primary-white;
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid variables.$border-grey;
|
||||
box-shadow: 0 4px 20px rgba(42, 41, 38, 0.1);
|
||||
border-bottom: 2px solid variables.$border-grey;
|
||||
box-shadow: 0 6px 10px rgba(42, 41, 38, 0.15);
|
||||
width: 100%;
|
||||
|
||||
.header-container {
|
||||
@@ -133,11 +133,12 @@
|
||||
}
|
||||
|
||||
.main-content {
|
||||
// margin-top: 0;
|
||||
// margin-top: calc(
|
||||
// 80px + (gallery.$gap-size * 2)
|
||||
// ); // Space for fixed header + 2rem
|
||||
min-height: calc(100vh - 80px);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
overflow-x: hidden; /* Sin scroll horizontal */
|
||||
overflow-y: scroll; /* Permite scroll vertical */
|
||||
}
|
||||
|
||||
// Responsive design
|
||||
@@ -175,8 +176,11 @@
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: calc(70px + 2rem);
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
min-height: calc(100vh - 70px);
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,5 +209,7 @@
|
||||
.main-content {
|
||||
margin-top: calc(120px + 2rem);
|
||||
min-height: calc(100vh - 120px);
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { Gallery } from './gallery/gallery';
|
||||
import { Button } from './button/button';
|
||||
import { SvgLoader } from './svg/svg';
|
||||
import { UploadImageForm } from './upload-image-form/upload-image-form';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -29,7 +30,11 @@ export class App implements OnInit {
|
||||
protected searchQuery = '';
|
||||
protected isMenuOpen = false;
|
||||
|
||||
constructor(private titleService: Title, private metaService: Meta) {}
|
||||
constructor(
|
||||
private titleService: Title,
|
||||
private metaService: Meta,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Set page meta information
|
||||
@@ -85,8 +90,10 @@ export class App implements OnInit {
|
||||
|
||||
navigateTo(section: string): void {
|
||||
this.isMenuOpen = false; // Close menu after navigation
|
||||
// TODO: Implement navigation logic
|
||||
console.log('Navigating to:', section);
|
||||
this.showUploadForm = false;
|
||||
if (section === 'home') {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
||||
// Close menu when clicking outside
|
||||
|
@@ -1,45 +1,49 @@
|
||||
$gap-size: 20px;
|
||||
|
||||
.low-res-image-list {
|
||||
columns: 4;
|
||||
column-gap: $gap-size;
|
||||
list-style: none;
|
||||
margin-top: 3%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
columns: 4;
|
||||
column-gap: $gap-size;
|
||||
list-style: none;
|
||||
margin-top: 3%;
|
||||
min-height: 100lvh;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Mobile - 1 column */
|
||||
@media (max-width: 639px) {
|
||||
.low-res-image-list {
|
||||
columns: 1;
|
||||
}
|
||||
.low-res-image-list {
|
||||
columns: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablets in portrait - 3 columns for better balance */
|
||||
@media (min-width: 640px) and (max-width: 1023px) and (orientation: portrait) {
|
||||
.low-res-image-list {
|
||||
columns: 3;
|
||||
}
|
||||
.low-res-image-list {
|
||||
columns: 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablets in landscape - 1 column */
|
||||
@media (min-width: 640px) and (max-width: 1023px) and (orientation: landscape) {
|
||||
.low-res-image-list {
|
||||
columns: 1;
|
||||
max-width: 90%;
|
||||
min-height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.low-res-image-list {
|
||||
columns: 1;
|
||||
max-width: 90%;
|
||||
min-height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop - mosaic with margins */
|
||||
@media (min-width: 1024px) {
|
||||
.low-res-image-list {
|
||||
columns: 4;
|
||||
margin-left: 17lvw;
|
||||
margin-right: 17lvw;
|
||||
}
|
||||
.low-res-image-list {
|
||||
columns: 4;
|
||||
margin-left: 17svw;
|
||||
margin-right: 17svw;
|
||||
max-width: calc(100vw - 34svw);
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
@@ -2,15 +2,16 @@ import { Component, computed } from '@angular/core';
|
||||
import { LowResImage } from '../low-res-image/low-res-image';
|
||||
import { Observable } from 'rxjs';
|
||||
import { MidResImage } from '../mid-res-image/mid-res-image';
|
||||
import axios from 'axios';
|
||||
|
||||
@Component({
|
||||
selector: 'gallery',
|
||||
imports: [LowResImage, MidResImage],
|
||||
templateUrl: './gallery.html',
|
||||
styleUrl: './gallery.scss'
|
||||
selector: 'gallery',
|
||||
imports: [LowResImage, MidResImage],
|
||||
templateUrl: './gallery.html',
|
||||
styleUrl: './gallery.scss',
|
||||
})
|
||||
export class Gallery {
|
||||
protected images : LowResImage[] = [];
|
||||
protected images: LowResImage[] = [];
|
||||
thereIsFullscreenImage: boolean = false;
|
||||
midResImage: MidResImage | null = null;
|
||||
isAnimatingToFullscreen: boolean = false;
|
||||
@@ -35,7 +36,10 @@ export class Gallery {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
onClickedImageWithEvent(data: {midResImage: MidResImage, event: MouseEvent}): void {
|
||||
onClickedImageWithEvent(data: {
|
||||
midResImage: MidResImage;
|
||||
event: MouseEvent;
|
||||
}): void {
|
||||
this.onClickedImage(data.midResImage, data.event);
|
||||
}
|
||||
|
||||
@@ -46,13 +50,13 @@ export class Gallery {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2,
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
height: rect.height,
|
||||
};
|
||||
|
||||
// Guardar información de transición en el document para uso del mid-res-image
|
||||
(document as any).__imageTransition = {
|
||||
...initialPosition,
|
||||
timestamp: Date.now()
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,17 +68,37 @@ export class Gallery {
|
||||
public loadImages(): void {
|
||||
// when backend is online ask for photos // return this.http.get<string[]>('/api/images');
|
||||
// meanwhile, load examples from assets/fotosPrueba
|
||||
this.images = this.getImages();
|
||||
this.getImages();
|
||||
}
|
||||
|
||||
private getImages(): LowResImage[] {
|
||||
var exampleImages : LowResImage[] = []
|
||||
for (let i = 1; i <= 33; i++) {
|
||||
const img = new LowResImage();
|
||||
img.src = `assets/fotosPrueba/${i}.jpg`;
|
||||
img.alt = `Image ${i}`;
|
||||
exampleImages.push(img);
|
||||
}
|
||||
return exampleImages;
|
||||
private getImages(): void {
|
||||
// hacer un get /api/photos para coger la primera página de fotos
|
||||
axios.get('/photos').then((response) => {
|
||||
const photos = response.data;
|
||||
this.images = photos.map((photo: PhotoDTO) => {
|
||||
let p = new LowResImage();
|
||||
p.id = photo.id;
|
||||
p.src = axios.defaults.baseURL + '/photos/' + photo.id + '/low';
|
||||
p.alt = photo.description;
|
||||
return p;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PhotoDTO {
|
||||
public id: string = '';
|
||||
public src: string = '';
|
||||
public alt: string = '';
|
||||
public description: string = '';
|
||||
public title: string = '';
|
||||
public tags: string = '';
|
||||
public people: string = '';
|
||||
public createdAt: Date | null = null;
|
||||
public updatedAt: Date | null = null;
|
||||
public createdBy: Date | null = null;
|
||||
public updatedBy: Date | null = null;
|
||||
public evento: string | null = null;
|
||||
public ubicacion: string | null = null;
|
||||
public ranking: number | null = null;
|
||||
}
|
||||
|
@@ -2,23 +2,26 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { MidResImage } from '../mid-res-image/mid-res-image';
|
||||
|
||||
@Component({
|
||||
selector: 'low-res-image',
|
||||
imports: [],
|
||||
templateUrl: './low-res-image.html',
|
||||
styleUrl: './low-res-image.scss'
|
||||
selector: 'low-res-image',
|
||||
imports: [],
|
||||
templateUrl: './low-res-image.html',
|
||||
styleUrl: './low-res-image.scss',
|
||||
})
|
||||
export class LowResImage {
|
||||
@Input({ required: true }) src: string = '';
|
||||
@Input({ required: true }) alt: string = '';
|
||||
public id : string = 'low-res-image-' + Math.random().toString(36).substring(2, 15);
|
||||
public id: string | null = null;
|
||||
|
||||
@Output() clicked = new EventEmitter<{midResImage: MidResImage, event: MouseEvent}>();
|
||||
@Output() clicked = new EventEmitter<{
|
||||
midResImage: MidResImage;
|
||||
event: MouseEvent;
|
||||
}>();
|
||||
|
||||
openFullscreen(event: MouseEvent): void {
|
||||
event.stopPropagation();
|
||||
const midResImage = new MidResImage();
|
||||
midResImage.src = this.src.replace('low', 'mid');
|
||||
midResImage.alt = this.alt;
|
||||
this.clicked.emit({midResImage, event});
|
||||
this.clicked.emit({ midResImage, event });
|
||||
}
|
||||
}
|
||||
|
@@ -31,18 +31,12 @@ export class UploadImageForm {
|
||||
// Here you would typically send the formData to your backend API
|
||||
|
||||
axios
|
||||
.post('https://localhost:7273/api/photo', formData, {
|
||||
.post('/photos', formData, {
|
||||
headers: {},
|
||||
validateStatus: (status) => status == 201,
|
||||
})
|
||||
.then((response) => {
|
||||
console.log('Upload successful:', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Upload failed:', error);
|
||||
.then(() => {
|
||||
this.close.emit();
|
||||
});
|
||||
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { App } from './app/app';
|
||||
import axios from 'axios';
|
||||
|
||||
bootstrapApplication(App, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
fetch('/assets/config.json')
|
||||
.then((response) => response.json())
|
||||
.then((config) => {
|
||||
axios.defaults.headers.post['Access-Control-Allow-Origin'] = '*';
|
||||
axios.defaults.baseURL = config.bff + '/api'; // Configurar base URL del BFF desde el archivo de configuración
|
||||
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
|
||||
});
|
||||
|
@@ -1,4 +1,9 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
@@ -6,5 +11,29 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden; /* Elimina scroll horizontal */
|
||||
overflow-y: scroll; /* Permite scroll vertical */
|
||||
scroll-behavior: smooth;
|
||||
box-sizing: border-box;
|
||||
|
||||
// /* Estilos personalizados para la barra de scroll */
|
||||
// ::-webkit-scrollbar-track {
|
||||
// background: #f1f1f1;
|
||||
// border-radius: 4px;
|
||||
// }
|
||||
|
||||
// ::-webkit-scrollbar-thumb {
|
||||
// background: #c1c1c1;
|
||||
// border-radius: 4px;
|
||||
// transition: background 0.3s ease;
|
||||
// }
|
||||
|
||||
// ::-webkit-scrollbar-thumb:hover {
|
||||
// background: #a8a8a8;
|
||||
// }
|
||||
|
||||
// /* Para Firefox */
|
||||
// body {
|
||||
// scrollbar-width: thin;
|
||||
// scrollbar-color: #c1c1c1 #f1f1f1;
|
||||
// }
|
||||
}
|
||||
|