feat: Initialize Svelte application with logo gallery functionality
- Add Rollup configuration for building and serving the application. - Implement logo scanning script to generate logos.json from logo files. - Create main App component to manage logo display and search functionality. - Develop LogoGrid and LogoList components for different viewing modes. - Add LogoModal component for logo preview with details. - Implement URL copying and logo downloading features. - Style components for improved user experience and responsiveness.
53
.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Node.js dependencies
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Build output
|
||||
public/build/
|
||||
|
||||
# macOS specific files
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Docker related
|
||||
.docker/
|
||||
*.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Svelte related
|
||||
.svelte-kit/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development
|
||||
.env.test
|
||||
.env.production
|
||||
131
DEVELOPMENT.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Logo Gallery - Developer Documentation
|
||||
|
||||
This document provides instructions for developers working on the Logo Gallery project.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Logo Gallery is a web application that displays a collection of company and brand logos with the following features:
|
||||
- Grid and list views of logos
|
||||
- Search functionality
|
||||
- Direct URL copying for each logo
|
||||
- Download capability
|
||||
- Responsive design
|
||||
|
||||
The project is built with:
|
||||
- Svelte for the front-end UI
|
||||
- Node.js for script automation
|
||||
- Docker for containerization
|
||||
- GitHub Pages for deployment
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker and Docker Compose
|
||||
- Git
|
||||
|
||||
No local Node.js installation is required as all operations run inside Docker containers.
|
||||
|
||||
### Getting Started
|
||||
|
||||
1. Clone the repository:
|
||||
```
|
||||
git clone https://github.com/yourusername/logos.git
|
||||
cd logos
|
||||
```
|
||||
|
||||
2. Build and start the application:
|
||||
```
|
||||
make build
|
||||
make start
|
||||
```
|
||||
|
||||
3. Access the application at http://localhost:5005
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding New Logos
|
||||
|
||||
1. Add logo files (SVG or PNG preferred) to the `public/logos/` directory
|
||||
2. Scan the logos directory and update the data file:
|
||||
```
|
||||
make scan-logos-dev
|
||||
```
|
||||
(This runs `npm run scan-logos` inside the dev container)
|
||||
3. The application will automatically rebuild with the new logos
|
||||
|
||||
### Modifying the UI
|
||||
|
||||
1. Edit files in the `src/` directory
|
||||
2. The changes will require a rebuild:
|
||||
```
|
||||
make rebuild
|
||||
```
|
||||
|
||||
### Running Custom Commands
|
||||
|
||||
To run any npm or shell command inside the Docker container:
|
||||
```
|
||||
make run CMD="your-command-here"
|
||||
```
|
||||
|
||||
Examples:
|
||||
- List logo files: `make run CMD="ls -la public/assets/logos"`
|
||||
- Run a specific npm script: `make run CMD="npm run some-script"`
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
logos/
|
||||
├── public/ # Static assets
|
||||
│ ├── assets/
|
||||
│ │ └── logos/ # Logo files (SVG, PNG)
|
||||
│ ├── data/ # JSON data files
|
||||
│ ├── build/ # Compiled JS/CSS (generated)
|
||||
│ └── global.css # Global styles
|
||||
├── src/ # Application source code
|
||||
│ ├── components/ # Svelte components
|
||||
│ ├── App.svelte # Main app component
|
||||
│ └── main.js # App entry point
|
||||
├── scripts/ # Utility scripts
|
||||
├── Dockerfile # Docker configuration
|
||||
├── compose.yml # Docker Compose configuration
|
||||
├── Makefile # Development commands
|
||||
└── README.md # Project overview
|
||||
```
|
||||
|
||||
## Deployment to GitHub Pages
|
||||
|
||||
To deploy the application to GitHub Pages:
|
||||
|
||||
1. Build the application:
|
||||
```
|
||||
make build
|
||||
```
|
||||
|
||||
2. The `public/` directory contains all files needed for deployment
|
||||
|
||||
3. Push the contents of the `public/` directory to the `gh-pages` branch of your repository
|
||||
|
||||
## Available Make Commands
|
||||
|
||||
Run `make help` to see all available commands:
|
||||
|
||||
- `make build` - Build the Docker container
|
||||
- `make start` - Start the application
|
||||
- `make stop` - Stop the application
|
||||
- `make restart` - Restart the application
|
||||
- `make logs` - View the application logs
|
||||
- `make run CMD=<cmd>` - Run a command in the container
|
||||
- `make generate-logos` - Generate logos.json from assets directory
|
||||
- `make clean` - Clean up build artifacts
|
||||
- `make rebuild` - Completely rebuild from scratch
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. Check the logs: `make logs`
|
||||
2. Try a complete rebuild: `make rebuild`
|
||||
3. Ensure the Docker service is running
|
||||
4. Verify your logo files are in the correct format and location
|
||||
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM node:slim
|
||||
|
||||
# Update package index and upgrade packages to reduce vulnerabilities
|
||||
RUN apt-get update && apt-get upgrade -y
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
# Copy all source files except those in .dockerignore
|
||||
COPY . .
|
||||
|
||||
# Create necessary directories for the build
|
||||
RUN mkdir -p public/build
|
||||
RUN mkdir -p public/data
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 5000
|
||||
EXPOSE 35729
|
||||
|
||||
# Start the server
|
||||
CMD ["npm", "run", "start"]
|
||||
15
Dockerfile.dev
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN [ -f package-lock.json ] || touch package-lock.json
|
||||
RUN npm install
|
||||
|
||||
# Only copy minimal files for initial build, source will be mounted
|
||||
COPY public/index.html public/global.css ./public/
|
||||
|
||||
EXPOSE 5000
|
||||
EXPOSE 35729
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
||||
81
Makefile
Normal file
@@ -0,0 +1,81 @@
|
||||
# Logo Gallery Project Makefile
|
||||
|
||||
# Configuration
|
||||
DOCKER_COMPOSE = docker compose
|
||||
CONTAINER_NAME = logo-gallery
|
||||
PORT = 5005
|
||||
DEV_PORT = 5006
|
||||
|
||||
# Main targets
|
||||
.PHONY: all build start stop restart logs clean scan-logos-dev help dev
|
||||
|
||||
all: build start
|
||||
|
||||
# Development mode with hot reloading
|
||||
dev:
|
||||
docker compose -f compose.dev.yml up --build
|
||||
|
||||
# Build the Docker container
|
||||
build:
|
||||
@echo "Building the Logo Gallery container..."
|
||||
$(DOCKER_COMPOSE) build
|
||||
|
||||
# Start the application in the background
|
||||
start:
|
||||
@echo "Starting Logo Gallery application on port $(PORT)..."
|
||||
$(DOCKER_COMPOSE) up -d
|
||||
@echo "Application is running at http://localhost:$(PORT)"
|
||||
|
||||
# Stop the application
|
||||
stop:
|
||||
@echo "Stopping Logo Gallery application..."
|
||||
$(DOCKER_COMPOSE) down
|
||||
|
||||
# Restart the application
|
||||
restart: stop start
|
||||
|
||||
# View the application logs
|
||||
logs:
|
||||
@echo "Showing application logs (press Ctrl+C to exit)..."
|
||||
$(DOCKER_COMPOSE) logs -f
|
||||
|
||||
# Run a command inside the container
|
||||
# Usage: make run CMD="npm run build"
|
||||
run:
|
||||
@echo "Running command in container: $(CMD)"
|
||||
$(DOCKER_COMPOSE) run --rm $(CONTAINER_NAME) $(CMD)
|
||||
|
||||
# Scan logos.json from files in the logos directory (for dev mode)
|
||||
scan-logos-dev:
|
||||
@echo "Scanning logos directory and updating logos.json for development..."
|
||||
$(DOCKER_COMPOSE) -f compose.dev.yml run --rm logo-gallery-dev npm run scan-logos
|
||||
@echo "Logos have been updated - refresh the browser to see changes"
|
||||
|
||||
# Clean up build artifacts and temporary files
|
||||
clean:
|
||||
@echo "Cleaning up build artifacts and temporary files..."
|
||||
$(DOCKER_COMPOSE) down
|
||||
docker builder prune -f
|
||||
|
||||
# Complete rebuild from scratch
|
||||
rebuild:
|
||||
@echo "Performing complete rebuild..."
|
||||
$(DOCKER_COMPOSE) down
|
||||
docker builder prune -f
|
||||
$(DOCKER_COMPOSE) build --no-cache
|
||||
$(DOCKER_COMPOSE) up -d
|
||||
@echo "Rebuild complete. Application is running at http://localhost:$(PORT)"
|
||||
|
||||
# Display help information
|
||||
help:
|
||||
@echo "Logo Gallery Makefile commands:"
|
||||
@echo " make build - Build the Docker container"
|
||||
@echo " make start - Start the application (http://localhost:$(PORT))"
|
||||
@echo " make stop - Stop the application"
|
||||
@echo " make restart - Restart the application"
|
||||
@echo " make logs - View the application logs"
|
||||
@echo " make run CMD=<cmd> - Run a command in the container"
|
||||
@echo " make scan-logos-dev - Scan logos.json from assets directory"
|
||||
@echo " make clean - Clean up build artifacts"
|
||||
@echo " make rebuild - Completely rebuild from scratch"
|
||||
@echo " make help - Display this help information"
|
||||
96
README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Logo Gallery
|
||||
|
||||
A collection of company and brand logos hosted on GitHub Pages. This project provides an easy way to access and use various brand logos in SVG and PNG formats with a user-friendly interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Browse logos in grid or list view
|
||||
- Search functionality to find specific logos
|
||||
- Copy direct URL to any logo with one click
|
||||
- Download logos directly
|
||||
- Responsive design that works on mobile and desktop
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Running with Docker (Recommended)
|
||||
|
||||
The easiest way to run the project locally is using Docker, which doesn't require installing Node.js or npm packages directly on your system:
|
||||
|
||||
1. Clone this repository:
|
||||
```
|
||||
git clone https://github.com/yourusername/logos.git
|
||||
cd logos
|
||||
```
|
||||
|
||||
2. Start the Docker container:
|
||||
```
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
The application will be available at http://localhost:5000 with live reloading enabled.
|
||||
|
||||
### Running Manually (Alternative)
|
||||
|
||||
If you prefer to run the project without Docker:
|
||||
|
||||
1. Clone this repository:
|
||||
```
|
||||
git clone https://github.com/yourusername/logos.git
|
||||
cd logos
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
3. Start the development server:
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. Build for production deployment:
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Deploying to GitHub Pages
|
||||
|
||||
1. Build the project:
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. Push the contents to your GitHub repository's `gh-pages` branch.
|
||||
|
||||
## Adding New Logos
|
||||
|
||||
To add new logos to the collection:
|
||||
|
||||
1. Add the logo file (SVG or PNG) to the `public/logos/` directory
|
||||
2. Run the logo scan script:
|
||||
```
|
||||
make scan-logos-dev
|
||||
```
|
||||
(This runs `npm run scan-logos` inside the dev container)
|
||||
3. The application will automatically rebuild with the new logos
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
logos/
|
||||
├── public/ # Static assets
|
||||
│ ├── assets/
|
||||
│ │ └── logos/ # Logo files
|
||||
│ └── global.css # Global styles
|
||||
├── src/ # Application source code
|
||||
│ ├── components/ # Svelte components
|
||||
│ ├── data/ # Data files
|
||||
│ ├── App.svelte # Main app component
|
||||
│ └── main.js # App entry point
|
||||
└── index.html # HTML entry point
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is MIT licensed. Please note that the logos themselves are property of their respective owners and should be used according to their brand guidelines.
|
||||
20
compose.dev.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
services:
|
||||
logo-gallery-dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: logo-gallery-dev
|
||||
ports:
|
||||
- "5006:5000"
|
||||
- "35729:35729"
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- ./public:/app/public
|
||||
- ./rollup.config.js:/app/rollup.config.js
|
||||
- ./package.json:/app/package.json
|
||||
- ./scripts:/app/scripts
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- CHOKIDAR_USEPOLLING=true
|
||||
- HOST=0.0.0.0
|
||||
command: npm run dev
|
||||
10
compose.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
services:
|
||||
logo-gallery:
|
||||
build: .
|
||||
container_name: logo-gallery
|
||||
ports:
|
||||
- "5005:5000" # App port
|
||||
- "35729:35729" # LiveReload port
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- HOST=0.0.0.0
|
||||
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "logo-gallery",
|
||||
"version": "1.0.0",
|
||||
"description": "A collection of company and brand logos accessible via GitHub Pages",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public --host 0.0.0.0 --dev --single",
|
||||
"scan-logos": "node scripts/scanLogos.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^1.0.0"
|
||||
}
|
||||
}
|
||||
56
public/data/logos.json
Normal file
@@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"name": "Apple (black)",
|
||||
"path": "logos/apple_black.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "ATB",
|
||||
"path": "logos/atb.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Binance",
|
||||
"path": "logos/binance.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Dalnoboy Service",
|
||||
"path": "logos/dalnoboy-service.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Google",
|
||||
"path": "logos/google.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Privatbank",
|
||||
"path": "logos/privatbank.png",
|
||||
"format": "PNG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Pumb",
|
||||
"path": "logos/pumb.png",
|
||||
"format": "PNG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Roomerin",
|
||||
"path": "logos/roomerin.svg",
|
||||
"format": "SVG",
|
||||
"disable": false
|
||||
},
|
||||
{
|
||||
"name": "Shkafnik",
|
||||
"path": "logos/shkafnik.png",
|
||||
"format": "PNG",
|
||||
"disable": false
|
||||
}
|
||||
]
|
||||
102
public/global.css
Normal file
@@ -0,0 +1,102 @@
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--secondary-color: #2c3e50;
|
||||
--background-color: #f8f9fa;
|
||||
--card-background: #ffffff;
|
||||
--text-color: #333333;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: var(--secondary-color);
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
|
||||
/* Direct logo image size constraints that will work with any component structure */
|
||||
div.logo-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.logo-image img {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Grid specific */
|
||||
.logo-grid .logo-item .logo-image {
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* List specific */
|
||||
.logo-list .logo-item .logo-image {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
height: 100px;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
14
public/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Logo Gallery</title>
|
||||
<link rel="stylesheet" href="global.css">
|
||||
<link rel="stylesheet" href="build/bundle.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="build/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
public/logos/apple_black.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1100 1100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g><path d="M695.579,184.597c18.506,-22.393 33.973,-49.186 43.976,-78.123c10.015,-28.955 14.535,-60.049 11.135,-91.073c-26.66,1.075 -56.128,9.969 -83.708,24.178c-27.568,14.218 -53.264,33.758 -72.385,56.136c-17.138,19.821 -33.225,45.599 -44.007,74.022c-10.781,28.414 -16.259,59.488 -12.21,89.89c29.729,2.308 59.775,-5.251 87.016,-18.978c27.243,-13.733 51.673,-33.644 70.183,-56.052Z"/><path d="M900.01,745.451c35.18,33.47 70.36,46.979 71.14,47.319c-0.59,1.891 -6.15,20.9 -17.96,48.77c-11.8,27.88 -29.83,64.62 -55.38,101.95c-22.08,32.27 -44.58,64.5 -70.58,88.8c-26.02,24.32 -55.52,40.73 -91.634,41.4c-35.476,0.65 -58.913,-9.86 -83.622,-20.55c-24.716,-10.68 -50.703,-21.52 -91.26,-21.52c-40.548,-0 -67.156,10.18 -92.028,20.7c-24.874,10.51 -47.998,21.35 -81.589,22.68c-34.841,1.32 -65.535,-16.14 -93.082,-41.96c-27.559,-25.82 -51.961,-60.01 -74.218,-92.16c-45.502,-65.79 -85.641,-158.719 -102.729,-255.487c-17.086,-96.758 -11.131,-197.342 35.566,-278.41c23.192,-40.264 55.508,-73.131 93.276,-96.107c37.77,-22.975 80.994,-36.059 125.981,-36.71c34.228,-0.649 67.49,10.865 97.422,22.537c29.938,11.677 56.546,23.519 77.467,23.519c20.906,-0 50.986,-14.242 86.163,-27.436c35.167,-13.188 75.419,-25.336 116.687,-21.15c17.28,0.716 50.16,4.211 86.51,18.909c36.34,14.706 76.16,40.624 107.3,86.2c-2.51,1.555 -31.44,18.448 -59.59,51.704c-78.5,92.761 -72.3,232.832 16.16,317.002Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
31
public/logos/atb.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Creator: CorelDRAW X7 -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="150mm" height="150mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
|
||||
viewBox="0 0 15000 15000"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.fil1 {fill:none}
|
||||
.fil2 {fill:#FEFEFE}
|
||||
.fil0 {fill:#256BB1}
|
||||
.fil3 {fill:#C32328;fill-rule:nonzero}
|
||||
]]>
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Layer_x0020_1">
|
||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
||||
<rect class="fil0" x="1290" y="1290" width="12421" height="12421" rx="1402" ry="1402"/>
|
||||
<polygon class="fil1" points="0,0 15000,0 15000,15000 0,15000 "/>
|
||||
<path class="fil2" d="M7500 1942c3070,0 5559,2488 5559,5558 0,3070 -2489,5559 -5559,5559 -3070,0 -5558,-2489 -5558,-5559 0,-3070 2488,-5558 5558,-5558z"/>
|
||||
<polygon class="fil3" points="5306,6127 6005,6127 6005,8866 5306,8866 5306,8496 4055,8496 4757,7826 5306,7826 5306,7100 3364,8929 3364,7963 "/>
|
||||
<polygon class="fil3" points="6770,6848 6196,6848 6196,6131 8703,6131 7949,6848 7523,6848 7523,8869 6770,8869 "/>
|
||||
<path class="fil3" d="M8878 6790l1400 0 694 -659 -2094 0 -695 659 0 2079 1594 0c100,-1 194,-25 281,-71 88,-47 165,-110 231,-191 67,-80 119,-171 157,-273 37,-103 57,-211 57,-323 0,-127 -19,-243 -57,-346 -37,-103 -89,-191 -155,-264 -67,-74 -144,-131 -233,-170 -88,-40 -182,-60 -285,-60l-744 0 0 658 607 0c55,1 97,19 127,53 30,34 46,79 46,136 0,37 -7,70 -21,99 -13,29 -33,51 -59,68 -26,17 -57,25 -93,25l-758 0 0 -1420z"/>
|
||||
<path class="fil0" d="M11102 4153l-504 1419c-1008,-1431 -2885,-1024 -5214,344 1612,-1637 3479,-2365 5718,-1763z"/>
|
||||
<path class="fil0" d="M3898 10847l504 -1419c1008,1431 2885,1024 5214,-344 -1612,1637 -3479,2365 -5718,1763z"/>
|
||||
<path class="fil0" d="M10696 3734c-3574,-3372 -9861,617 -7759,5609 132,-3725 4861,-6340 7759,-5609z"/>
|
||||
<path class="fil0" d="M4304 11266c3574,3372 9861,-617 7759,-5609 -132,3725 -4861,6340 -7759,5609z"/>
|
||||
<path class="fil0" d="M2575 905l9850 0c919,0 1670,751 1670,1670l0 9850c0,919 -751,1670 -1670,1670l-9850 0c-919,0 -1670,-751 -1670,-1670l0 -9850c0,-919 751,-1670 1670,-1670zm-46 -153l9942 0c977,0 1777,800 1777,1777l0 9942c0,977 -800,1777 -1777,1777l-9942 0c-977,0 -1777,-800 -1777,-1777l0 -9942c0,-977 800,-1777 1777,-1777z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
1
public/logos/binance.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 126.61 126.61" xmlns="http://www.w3.org/2000/svg"><g fill="#f3ba2f"><path d="m38.73 53.2 24.59-24.58 24.6 24.6 14.3-14.31-38.9-38.91-38.9 38.9z"/><path d="m0 63.31 14.3-14.31 14.31 14.31-14.31 14.3z"/><path d="m38.73 73.41 24.59 24.59 24.6-24.6 14.31 14.29-38.9 38.91-38.91-38.88z"/><path d="m98 63.31 14.3-14.31 14.31 14.3-14.31 14.32z"/><path d="m77.83 63.3-14.51-14.52-10.73 10.73-1.24 1.23-2.54 2.54 14.51 14.5 14.51-14.47z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 458 B |
1
public/logos/dalnoboy-service.svg
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
8
public/logos/google.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="272" height="92" viewBox="0 0 272 92">
|
||||
<path fill="#EA4335" d="M115.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18C71.25 34.32 81.24 25 93.5 25s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44S80.99 39.2 80.99 47.18c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/>
|
||||
<path fill="#FBBC05" d="M163.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18c0-12.85 9.99-22.18 22.25-22.18s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44s-12.51 5.46-12.51 13.44c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/>
|
||||
<path fill="#4285F4" d="M209.75 26.34v39.82c0 16.38-9.66 23.07-21.08 23.07-10.75 0-17.22-7.19-19.66-13.07l8.48-3.53c1.51 3.61 5.21 7.87 11.17 7.87 7.31 0 11.84-4.51 11.84-13v-3.19h-.34c-2.18 2.69-6.38 5.04-11.68 5.04-11.09 0-21.25-9.66-21.25-22.09 0-12.52 10.16-22.26 21.25-22.26 5.29 0 9.49 2.35 11.68 4.96h.34v-3.61h9.25zm-8.56 20.92c0-7.81-5.21-13.52-11.84-13.52-6.72 0-12.35 5.71-12.35 13.52 0 7.73 5.63 13.36 12.35 13.36 6.63 0 11.84-5.63 11.84-13.36z"/>
|
||||
<path fill="#34A853" d="M225 3v65h-9.5V3h9.5z"/>
|
||||
<path fill="#EA4335" d="M262.02 54.48l7.56 5.04c-2.44 3.61-8.32 9.83-18.48 9.83-12.6 0-22.01-9.74-22.01-22.18 0-13.19 9.49-22.18 20.92-22.18 11.51 0 17.14 9.16 18.98 14.11l1.01 2.52-29.65 12.28c2.27 4.45 5.8 6.72 10.75 6.72 4.96 0 8.4-2.44 10.92-6.14zm-23.27-7.98l19.82-8.23c-1.09-2.77-4.37-4.7-8.23-4.7-4.95 0-11.84 4.37-11.59 12.93z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logos/privatbank.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/logos/pumb.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
1
public/logos/roomerin.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/logos/shkafnik.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
78
rollup.config.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import svelte from 'rollup-plugin-svelte';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import css from 'rollup-plugin-css-only';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/build/bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
compilerOptions: {
|
||||
// enable run-time checks when not in production
|
||||
dev: !production
|
||||
},
|
||||
// Force Svelte to emit CSS for components
|
||||
emitCss: true
|
||||
}),
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css({ output: 'bundle.css' }),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte']
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
107
scripts/scanLogos.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// This file has been renamed and updated. See scanLogos.js in the same directory for the new script.
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Configuration
|
||||
const logosDir = path.join(__dirname, '../public/logos');
|
||||
const outputFile = path.join(__dirname, '../public/data/logos.json');
|
||||
|
||||
// Get file extension without the dot
|
||||
function getFileExtension(filename) {
|
||||
return path.extname(filename).slice(1).toUpperCase();
|
||||
}
|
||||
|
||||
// Get file name without extension
|
||||
function getBaseName(filename) {
|
||||
return path.basename(filename, path.extname(filename));
|
||||
}
|
||||
|
||||
// Convert filename to readable name (replace hyphens with spaces, capitalize words)
|
||||
function formatName(filename) {
|
||||
return getBaseName(filename)
|
||||
.split(/[-_]/)
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// Scan directory and update logo objects
|
||||
function scanLogos() {
|
||||
console.log(`Scanning logos directory: ${logosDir}`);
|
||||
|
||||
let existing = [];
|
||||
if (fs.existsSync(outputFile)) {
|
||||
try {
|
||||
existing = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
|
||||
} catch (e) {
|
||||
console.error('Could not parse existing logos.json:', e);
|
||||
}
|
||||
}
|
||||
const existingMap = new Map();
|
||||
for (const item of existing) {
|
||||
existingMap.set(item.path, item);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(logosDir)) {
|
||||
console.error(`Directory does not exist: ${logosDir}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(logosDir);
|
||||
// Filter for image files (svg, png, jpg, jpeg)
|
||||
const logoFiles = files.filter(file =>
|
||||
/\.(svg|png|jpg|jpeg)$/i.test(file)
|
||||
);
|
||||
|
||||
console.log(`Found ${logoFiles.length} logo files`);
|
||||
|
||||
// Create logo objects
|
||||
const logos = logoFiles.map(file => {
|
||||
const format = getFileExtension(file);
|
||||
const logoPath = `logos/${file}`;
|
||||
const existingItem = existingMap.get(logoPath);
|
||||
if (existingItem) {
|
||||
// Preserve name and disable, update format/path
|
||||
return {
|
||||
...existingItem,
|
||||
path: logoPath,
|
||||
format: format,
|
||||
disable: typeof existingItem.disable === 'boolean' ? existingItem.disable : false
|
||||
};
|
||||
} else {
|
||||
// New logo
|
||||
return {
|
||||
name: formatName(file),
|
||||
path: logoPath,
|
||||
format: format,
|
||||
disable: false
|
||||
};
|
||||
}
|
||||
});
|
||||
return logos;
|
||||
} catch (error) {
|
||||
console.error('Error scanning logos directory:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Save logos data to JSON file
|
||||
function saveLogosToJson(logos) {
|
||||
try {
|
||||
const data = JSON.stringify(logos, null, 2);
|
||||
fs.writeFileSync(outputFile, data);
|
||||
console.log(`Successfully wrote ${logos.length} logos to ${outputFile}`);
|
||||
} catch (error) {
|
||||
console.error('Error writing logos data to file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
function main() {
|
||||
const logos = scanLogos();
|
||||
saveLogosToJson(logos);
|
||||
}
|
||||
|
||||
// Run the script
|
||||
main();
|
||||
191
src/App.svelte
Normal file
@@ -0,0 +1,191 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import LogoGrid from './components/LogoGrid.svelte';
|
||||
import LogoList from './components/LogoList.svelte';
|
||||
|
||||
let viewMode = 'grid'; // 'grid' or 'list'
|
||||
let searchQuery = '';
|
||||
let logos = [];
|
||||
let filteredLogos = [];
|
||||
|
||||
// Load logos from JSON file with cache busting
|
||||
onMount(async () => {
|
||||
try {
|
||||
// Add timestamp as cache-busting query parameter
|
||||
const timestamp = new Date().getTime();
|
||||
const response = await fetch(`data/logos.json?t=${timestamp}`, {
|
||||
// Force reload from server, don't use cache
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
logos = await response.json();
|
||||
filteredLogos = logos;
|
||||
console.log('Loaded logos:', logos.length, 'at', new Date().toLocaleTimeString());
|
||||
} else {
|
||||
console.error('Failed to load logos data', response.status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading logos:', error);
|
||||
}
|
||||
});
|
||||
|
||||
$: {
|
||||
filteredLogos = logos.filter(logo =>
|
||||
logo.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
function setGridView() {
|
||||
console.log('Setting view mode to: grid');
|
||||
viewMode = 'grid';
|
||||
}
|
||||
|
||||
function setListView() {
|
||||
console.log('Setting view mode to: list');
|
||||
viewMode = 'list';
|
||||
}
|
||||
|
||||
function copyUrl(logoPath) {
|
||||
const url = `${window.location.origin}/${logoPath}`;
|
||||
navigator.clipboard.writeText(url)
|
||||
.then(() => {
|
||||
alert('URL copied to clipboard!');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy URL: ', err);
|
||||
});
|
||||
}
|
||||
|
||||
function downloadLogo(logoPath, logoName) {
|
||||
const link = document.createElement('a');
|
||||
link.href = logoPath;
|
||||
link.download = logoName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="container">
|
||||
<header>
|
||||
<h1>Logo Gallery</h1>
|
||||
<p>Collection of company and brand logos for your projects</p>
|
||||
|
||||
<div class="search-bar">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search logos..."
|
||||
bind:value={searchQuery}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="view-toggle">
|
||||
<button
|
||||
class:active={viewMode === 'grid'}
|
||||
on:click={setGridView}
|
||||
>
|
||||
Grid View
|
||||
</button>
|
||||
<button
|
||||
class:active={viewMode === 'list'}
|
||||
on:click={setListView}
|
||||
>
|
||||
List View
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Debug info to show current view mode -->
|
||||
<div style="margin-bottom: 10px; font-size: 0.8rem; color: #666;">
|
||||
Current view: {viewMode}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="logos-container">
|
||||
{#if viewMode === 'grid'}
|
||||
<LogoGrid
|
||||
logos={filteredLogos}
|
||||
onCopy={copyUrl}
|
||||
onDownload={downloadLogo}
|
||||
/>
|
||||
{:else}
|
||||
<LogoList
|
||||
logos={filteredLogos}
|
||||
onCopy={copyUrl}
|
||||
onDownload={downloadLogo}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© {new Date().getFullYear()} Logo Gallery. All logos are property of their respective owners.</p>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--secondary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.view-toggle button {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #e0e0e0;
|
||||
color: var(--text-color);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: var(--secondary-color) !important;
|
||||
color: white !important;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.logos-container {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
121
src/components/LogoGrid.svelte
Normal file
@@ -0,0 +1,121 @@
|
||||
<script>
|
||||
import LogoModal from './LogoModal.svelte';
|
||||
|
||||
export let logos = [];
|
||||
export let onCopy;
|
||||
export let onDownload;
|
||||
|
||||
let showModal = false;
|
||||
let selectedLogo = null;
|
||||
|
||||
function openPreview(logo) {
|
||||
selectedLogo = logo;
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<LogoModal show={showModal} logo={selectedLogo} on:close={closeModal} />
|
||||
|
||||
<div class="logo-grid">
|
||||
{#each logos as logo}
|
||||
<div class="logo-card">
|
||||
<div class="logo-image"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Preview {logo.name}"
|
||||
on:click={() => openPreview(logo)}
|
||||
on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && openPreview(logo)}
|
||||
style="cursor:pointer;"
|
||||
>
|
||||
<img src={logo.path} alt={logo.name} />
|
||||
</div>
|
||||
<div class="logo-info">
|
||||
<h3>{logo.name}</h3>
|
||||
<p>Format: {logo.format}</p>
|
||||
<div class="logo-actions">
|
||||
<button class="copy-btn" on:click={() => onCopy(logo.path)}>
|
||||
Copy URL
|
||||
</button>
|
||||
<button class="download-btn" on:click={() => onDownload(logo.path, logo.name)}>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="no-results">No logos found matching your search criteria.</p>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.logo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.logo-card {
|
||||
background: var(--card-background);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.logo-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo-image img {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.logo-info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.logo-info h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.logo-info p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
grid-column: 1 / -1;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
150
src/components/LogoList.svelte
Normal file
@@ -0,0 +1,150 @@
|
||||
<script>
|
||||
import LogoModal from './LogoModal.svelte';
|
||||
|
||||
export let logos = [];
|
||||
export let onCopy;
|
||||
export let onDownload;
|
||||
|
||||
let showModal = false;
|
||||
let selectedLogo = null;
|
||||
|
||||
function openPreview(logo) {
|
||||
selectedLogo = logo;
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<LogoModal show={showModal} logo={selectedLogo} on:close={closeModal} />
|
||||
|
||||
<div class="logo-list">
|
||||
{#each logos as logo}
|
||||
<div class="logo-item">
|
||||
<div class="logo-image"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Preview {logo.name}"
|
||||
on:click={() => openPreview(logo)}
|
||||
on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && openPreview(logo)}
|
||||
style="cursor:pointer;"
|
||||
>
|
||||
<img src={logo.path} alt={logo.name} />
|
||||
</div>
|
||||
<div class="logo-info">
|
||||
<h3>{logo.name}</h3>
|
||||
<p>Format: {logo.format}</p>
|
||||
</div>
|
||||
<div class="logo-actions">
|
||||
<button class="copy-btn" on:click={() => onCopy(logo.path)}>
|
||||
Copy URL
|
||||
</button>
|
||||
<button class="download-btn" on:click={() => onDownload(logo.path, logo.name)}>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="no-results">No logos found matching your search criteria.</p>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.logo-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.logo-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--card-background);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
height: 100px;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo-image img {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.logo-info {
|
||||
flex-grow: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.logo-info h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--secondary-color);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.logo-info p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.logo-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
min-width: 120px;
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
.logo-actions button {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
text-align: center;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
143
src/components/LogoModal.svelte
Normal file
@@ -0,0 +1,143 @@
|
||||
<script>
|
||||
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
export let logo = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function close() {
|
||||
dispatch('close');
|
||||
}
|
||||
|
||||
function handleKeydown(event) {
|
||||
if (event.key === 'Escape') {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
document.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if show && logo}
|
||||
<div class="modal-backdrop"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>{logo.name}</h2>
|
||||
<button class="close-btn" on:click={close} aria-label="Close preview">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="preview-container"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Close preview"
|
||||
on:click={close}
|
||||
on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && close()}
|
||||
style="cursor:pointer;"
|
||||
>
|
||||
<img src={logo.path} alt={logo.name} />
|
||||
</div>
|
||||
<div class="logo-details">
|
||||
<p><strong>Format:</strong> {logo.format}</p>
|
||||
<p><strong>Path:</strong> {logo.path}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 900px;
|
||||
max-height: 90vh;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
padding: 2rem;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.preview-container img {
|
||||
max-width: 100%;
|
||||
max-height: 60vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.logo-details {
|
||||
padding: 1rem;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.logo-details p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
7
src/main.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app') || document.body
|
||||
});
|
||||
|
||||
export default app;
|
||||