parent
3685155a1d
commit
c33d62deaf
@ -0,0 +1,17 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
@ -1,157 +1,45 @@
|
||||
# ---> VisualStudioCode
|
||||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
Thumbs.db
|
||||
|
@ -1,9 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,27 +1,59 @@
|
||||
# Memory Map
|
||||
# Memory Map Front
|
||||
|
||||
Front du projet MemoryMap
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.0.6.
|
||||
|
||||
## Project setup
|
||||
## Development server
|
||||
|
||||
```
|
||||
npm install
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
```
|
||||
npm run serve
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
npm run build
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
npm run lint
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
|
@ -0,0 +1,90 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"frontv2": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/frontv2",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": [
|
||||
"node_modules/flowbite/dist/flowbite.min.js",
|
||||
"node_modules/leaflet/dist/leaflet.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "frontv2:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "frontv2:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +1,44 @@
|
||||
{
|
||||
"name": "memory_map",
|
||||
"name": "memory-map",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"core-js": "^3.8.3",
|
||||
"flowbite": "^2.5.1",
|
||||
"@angular/animations": "^19.0.0",
|
||||
"@angular/common": "^19.0.0",
|
||||
"@angular/compiler": "^19.0.0",
|
||||
"@angular/core": "^19.0.0",
|
||||
"@angular/forms": "^19.0.0",
|
||||
"@angular/platform-browser": "^19.0.0",
|
||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||
"@angular/router": "^19.0.0",
|
||||
"flowbite": "^2.5.2",
|
||||
"leaflet": "^1.9.4",
|
||||
"vue": "^3.2.13"
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@angular-devkit/build-angular": "^19.0.6",
|
||||
"@angular/cli": "^19.0.6",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/leaflet": "^1.9.15",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.12"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
"jasmine-core": "~5.4.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title>Memory Map</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 244 KiB |
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="container mx-auto h-screen">
|
||||
<NavBar />
|
||||
<LeafletMap />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LeafletMap from "./components/LeafletMap.vue";
|
||||
import NavBar from "./components/NavBar.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
NavBar,
|
||||
LeafletMap,
|
||||
},
|
||||
};
|
||||
</script>
|
@ -0,0 +1,4 @@
|
||||
<app-navbar></app-navbar>
|
||||
<app-leaflet-map></app-leaflet-map>
|
||||
|
||||
<router-outlet />
|
@ -0,0 +1,29 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AppComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have the 'frontv2' title`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('frontv2');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, frontv2');
|
||||
});
|
||||
});
|
@ -0,0 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { LeafletMapComponent } from './components/leaflet-map/leaflet-map.component';
|
||||
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, NavbarComponent, LeafletMapComponent],
|
||||
templateUrl: './app.component.html',
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'frontv2';
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [];
|
@ -0,0 +1,3 @@
|
||||
<div class="map-container h-[calc(100vh_-_72px)]">
|
||||
<div id="map" class="h-full w-full"></div>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LeafletMapComponent } from './leaflet-map.component';
|
||||
|
||||
describe('LeafletmapComponent', () => {
|
||||
let component: LeafletMapComponent;
|
||||
let fixture: ComponentFixture<LeafletMapComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LeafletMapComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LeafletMapComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,73 @@
|
||||
import { Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||
import * as L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { monuments } from '../../data/stub';
|
||||
import { MonumentMarkerComponent } from '../monument-marker/monument-marker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-leaflet-map',
|
||||
templateUrl: './leaflet-map.component.html',
|
||||
})
|
||||
export class LeafletMapComponent implements OnInit {
|
||||
private map!: L.Map;
|
||||
|
||||
constructor(private viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initializeMap();
|
||||
}
|
||||
|
||||
private initializeMap(): void {
|
||||
// Initialize the map
|
||||
this.map = L.map('map').setView([46.603354, 1.888334], 6);
|
||||
|
||||
// Add OpenStreetMap tiles
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '',
|
||||
}).addTo(this.map);
|
||||
this.map.attributionControl.setPrefix('');
|
||||
|
||||
// Define custom icons
|
||||
const visitedIcon = this.createDivIcon(`
|
||||
<svg class="w-6 h-6 text-gray-800" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path fill-rule="evenodd" d="M11.906 1.994a8.002 8.002 0 0 1 8.09 8.421 7.996 7.996 0 0 1-1.297 3.957.996.996 0 0 1-.133.204l-.108.129c-.178.243-.37.477-.573.699l-5.112 6.224a1 1 0 0 1-1.545 0L5.982 15.26l-.002-.002a18.146 18.146 0 0 1-.309-.38l-.133-.163a.999.999 0 0 1-.13-.202 7.995 7.995 0 0 1 6.498-12.518ZM15 9.997a3 3 0 1 1-5.999 0 3 3 0 0 1 5.999 0Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
`);
|
||||
|
||||
const notVisitedIcon = this.createDivIcon(`
|
||||
<svg class="w-6 h-6 text-gray-800" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" stroke-width="3" d="M11.083 5.104c.35-.8 1.485-.8 1.834 0l1.752 4.022a1 1 0 0 0 .84.597l4.463.342c.9.069 1.255 1.2.556 1.771l-3.33 2.723a1 1 0 0 0-.337 1.016l1.03 4.119c.214.858-.71 1.552-1.474 1.106l-3.913-2.281a1 1 0 0 0-1.008 0L7.583 20.8c-.764.446-1.688-.248-1.474-1.106l1.03-4.119A1 1 0 0 0 6.8 14.56l-3.33-2.723c-.698-.571-.342-1.702.557-1.771l4.462-.342a1 1 0 0 0 .84-.597l1.753-4.022Z"/>
|
||||
</svg>
|
||||
`);
|
||||
|
||||
// Add markers
|
||||
monuments.forEach((monument) => {
|
||||
const icon = monument.visited ? visitedIcon : notVisitedIcon;
|
||||
|
||||
const marker = L.marker(monument.coords as [number, number], {
|
||||
icon,
|
||||
}).addTo(this.map);
|
||||
|
||||
// Dynamically create Angular component and attach it to popup
|
||||
const popupDiv = document.createElement('div');
|
||||
const componentRef = this.viewContainerRef.createComponent(
|
||||
MonumentMarkerComponent
|
||||
);
|
||||
|
||||
componentRef.instance.monument = monument;
|
||||
popupDiv.appendChild(componentRef.location.nativeElement);
|
||||
|
||||
marker.bindPopup(popupDiv);
|
||||
});
|
||||
}
|
||||
|
||||
private createDivIcon(htmlContent: string): L.DivIcon {
|
||||
return L.divIcon({
|
||||
html: htmlContent,
|
||||
className: 'text-2xl text-blue-500',
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 24],
|
||||
popupAnchor: [0, -24],
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
<div class="text-center">
|
||||
<strong>{{ monument.name }}</strong>
|
||||
<div
|
||||
*ngIf="monument.images.length > 0"
|
||||
class="relative carousel overflow-hidden"
|
||||
>
|
||||
<!-- Carousel wrapper -->
|
||||
<div
|
||||
class="relative h-40 mt-2 overflow-hidden rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<div
|
||||
*ngFor="let image of monument.images; let index = index"
|
||||
[class]="
|
||||
'absolute inset-0 transition-opacity duration-700 ease-in-out' +
|
||||
(index === currentIndex ? ' opacity-100' : ' opacity-0')
|
||||
"
|
||||
>
|
||||
<img
|
||||
[src]="image"
|
||||
[alt]="monument.name"
|
||||
class="object-contain max-h-full max-w-full h-full w-auto mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slider controls -->
|
||||
<div *ngIf="monument.images.length > 1">
|
||||
<button
|
||||
type="button"
|
||||
class="absolute top-0 left-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
||||
(click)="prevSlide()"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-white rtl:rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 6 10"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 1 1 5l4 4"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Previous</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute top-0 right-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
||||
(click)="nextSlide()"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-white rtl:rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 6 10"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 9l4-4-4-4"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Next</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p [innerHTML]="formattedDescription"></p>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MonumentMarkerComponent } from './monument-marker.component';
|
||||
|
||||
describe('MonumentmarkerComponent', () => {
|
||||
let component: MonumentMarkerComponent;
|
||||
let fixture: ComponentFixture<MonumentMarkerComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MonumentMarkerComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MonumentMarkerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Monument } from '../../model/Monument';
|
||||
|
||||
@Component({
|
||||
selector: 'app-monument-marker',
|
||||
templateUrl: './monument-marker.component.html',
|
||||
imports: [CommonModule],
|
||||
})
|
||||
export class MonumentMarkerComponent {
|
||||
@Input() monument!: Monument;
|
||||
|
||||
currentIndex: number = 0;
|
||||
|
||||
get formattedDescription(): string {
|
||||
return this.formatDescription(this.monument.description);
|
||||
}
|
||||
|
||||
formatDescription(description: string): string {
|
||||
const regex = /@(\w+(-\w+)*(\.\w+(-\w+)*)*)/g;
|
||||
return description.replace(
|
||||
regex,
|
||||
`<a href="/profile/$1" class="text-blue-500 hover:underline">@$1</a>`
|
||||
);
|
||||
}
|
||||
|
||||
prevSlide(): void {
|
||||
this.currentIndex =
|
||||
(this.currentIndex - 1 + this.monument.images.length) %
|
||||
this.monument.images.length;
|
||||
}
|
||||
|
||||
nextSlide(): void {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.monument.images.length;
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
<nav class="bg-white border-gray-200 dark:bg-gray-900">
|
||||
<div
|
||||
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
|
||||
>
|
||||
<a class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<img src="./logo.png" class="h-10" alt="Memory Map Logo" />
|
||||
<span
|
||||
class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white"
|
||||
>Memory Map</span
|
||||
>
|
||||
</a>
|
||||
<div class="flex lg:order-2">
|
||||
<button
|
||||
type="button"
|
||||
data-collapse-toggle="navbar-search"
|
||||
aria-controls="navbar-search"
|
||||
aria-expanded="false"
|
||||
class="lg:hidden text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 me-1"
|
||||
>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Search</span>
|
||||
</button>
|
||||
<div class="relative hidden lg:block">
|
||||
<div
|
||||
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Search icon</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-navbar"
|
||||
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
data-collapse-toggle="navbar-search"
|
||||
type="button"
|
||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||
aria-controls="navbar-search"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 17 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="items-center justify-between hidden w-full lg:flex lg:w-auto lg:order-1"
|
||||
id="navbar-search"
|
||||
>
|
||||
<div class="relative mt-3 lg:hidden">
|
||||
<div
|
||||
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-navbar"
|
||||
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
class="flex flex-col p-4 lg:p-0 mt-4 font-medium border border-gray-100 rounded-lg bg-gray-50 lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 lg:bg-white dark:bg-gray-800 lg:dark:bg-gray-900 dark:border-gray-700"
|
||||
>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
|
||||
aria-current="page"
|
||||
>Home</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
|
||||
>Quests</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
|
||||
>Add a pin</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
|
||||
>Friends</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarComponent } from './navbar.component';
|
||||
|
||||
describe('NavbarComponent', () => {
|
||||
let component: NavbarComponent;
|
||||
let fixture: ComponentFixture<NavbarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NavbarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NavbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar',
|
||||
imports: [],
|
||||
templateUrl: './navbar.component.html',
|
||||
})
|
||||
export class NavbarComponent {}
|
@ -0,0 +1,32 @@
|
||||
import { Monument } from '../model/Monument';
|
||||
|
||||
export const monuments: Monument[] = [
|
||||
{
|
||||
coords: [48.85837, 2.294481],
|
||||
name: 'Tour Eiffel',
|
||||
images: [
|
||||
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQGGib245tSFiK1Qcx0cB0dZsoVyJElwsY3kA&s',
|
||||
'https://encrypted-tbn2.gstatic.com/licensed-image?q=tbn:ANd9GcTLB9B0j50rJbcSbdja9_hySHS6_KATbhTK_iCeWeNKtA92hTmTX5nTW3udjjovZrnU1JxqAjMS_VqHnMwHGhTs35-sU-7B29_X_T3uLV8',
|
||||
],
|
||||
description: 'Visité en 2020 avec la famille, un moment inoubliable.',
|
||||
visited: true,
|
||||
},
|
||||
{
|
||||
coords: [43.296482, 5.36978],
|
||||
name: 'Vieux Port de Marseille',
|
||||
images: [
|
||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Marseille_Old_Port.jpg/390px-Marseille_Old_Port.jpg',
|
||||
],
|
||||
description:
|
||||
"Découvert lors d'un week-end ensoleillé en 2019 avec @John-Doe.",
|
||||
visited: true,
|
||||
},
|
||||
{
|
||||
coords: [48.636063, -1.511457],
|
||||
name: 'Mont Saint-Michel',
|
||||
images: [],
|
||||
description: '',
|
||||
visited: false,
|
||||
},
|
||||
// ...
|
||||
];
|
@ -0,0 +1,7 @@
|
||||
export interface Monument {
|
||||
coords: number[];
|
||||
name: string;
|
||||
images: string[];
|
||||
description: string;
|
||||
visited: boolean;
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
<template>
|
||||
<div class="map-container h-[calc(100vh_-_72px)]">
|
||||
<div id="map" class="h-full w-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import L from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { createApp, h, onMounted } from "vue";
|
||||
import { monuments } from "../data/stub";
|
||||
import MonumentMarker from "./MonumentMarker.vue";
|
||||
|
||||
export default {
|
||||
name: "LeafletMap",
|
||||
setup() {
|
||||
onMounted(() => {
|
||||
// Initialiser la carte
|
||||
const map = L.map("map").setView([46.603354, 1.888334], 6); // TODO: A modifier pour centrer par rapport aux points de la carte
|
||||
// Supprimer les attributions de la carte Leaflet
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "",
|
||||
}).addTo(map);
|
||||
map.attributionControl.setPrefix("");
|
||||
|
||||
// Définir une fonction pour créer des icônes avec Heroicons
|
||||
const createDivIcon = (htmlContent) => {
|
||||
return L.divIcon({
|
||||
html: htmlContent,
|
||||
className: "text-2xl text-blue-500", // Utilise les classes CSS de Flowbite ou Tailwind pour styliser les icônes
|
||||
iconSize: [24, 24], // Taille approximative du conteneur de l'icône
|
||||
iconAnchor: [12, 24], // Point d'ancrage de l'icône (centre-bas)
|
||||
popupAnchor: [0, -24], // Point d'ancrage du popup (juste au-dessus du marqueur)
|
||||
});
|
||||
};
|
||||
|
||||
// Définir les icônes Heroicons
|
||||
const visitedIcon = createDivIcon(`
|
||||
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path fill-rule="evenodd" d="M11.906 1.994a8.002 8.002 0 0 1 8.09 8.421 7.996 7.996 0 0 1-1.297 3.957.996.996 0 0 1-.133.204l-.108.129c-.178.243-.37.477-.573.699l-5.112 6.224a1 1 0 0 1-1.545 0L5.982 15.26l-.002-.002a18.146 18.146 0 0 1-.309-.38l-.133-.163a.999.999 0 0 1-.13-.202 7.995 7.995 0 0 1 6.498-12.518ZM15 9.997a3 3 0 1 1-5.999 0 3 3 0 0 1 5.999 0Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
`);
|
||||
|
||||
const notVisitedIcon = createDivIcon(`
|
||||
<svg class="w-[24px] h-[24px] text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" stroke-width="3" d="M11.083 5.104c.35-.8 1.485-.8 1.834 0l1.752 4.022a1 1 0 0 0 .84.597l4.463.342c.9.069 1.255 1.2.556 1.771l-3.33 2.723a1 1 0 0 0-.337 1.016l1.03 4.119c.214.858-.71 1.552-1.474 1.106l-3.913-2.281a1 1 0 0 0-1.008 0L7.583 20.8c-.764.446-1.688-.248-1.474-1.106l1.03-4.119A1 1 0 0 0 6.8 14.56l-3.33-2.723c-.698-.571-.342-1.702.557-1.771l4.462-.342a1 1 0 0 0 .84-.597l1.753-4.022Z"/>
|
||||
</svg>
|
||||
`);
|
||||
|
||||
// Ajouter les marqueurs pour chaque monument
|
||||
monuments.forEach((monument) => {
|
||||
// Choisir l'icône en fonction de la propriété `visited`
|
||||
const icon = monument.visited ? visitedIcon : notVisitedIcon;
|
||||
|
||||
// Ajouter un marqueur avec l'icône appropriée
|
||||
const marker = L.marker(monument.coords, { icon }).addTo(map);
|
||||
|
||||
// Créer un conteneur DOM pour monter le composant Vue
|
||||
const popupContainer = document.createElement("div");
|
||||
|
||||
// Monter le composant Vue MonumentMarker sur le conteneur
|
||||
const app = createApp({
|
||||
render() {
|
||||
return h(MonumentMarker, { monument });
|
||||
},
|
||||
});
|
||||
app.mount(popupContainer);
|
||||
|
||||
// Attacher le popup au marqueur avec le contenu monté
|
||||
marker.bindPopup(popupContainer);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,130 +0,0 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<strong>{{ monument.name }}</strong>
|
||||
<div
|
||||
class="relative carousel overflow-hidden"
|
||||
v-if="monument.images.length > 0"
|
||||
>
|
||||
<!-- Carousel wrapper -->
|
||||
<div
|
||||
class="relative h-40 mt-2 overflow-hidden rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<div
|
||||
v-for="(image, index) in monument.images"
|
||||
:key="index"
|
||||
:class="[
|
||||
'absolute inset-0 transition-opacity duration-700 ease-in-out',
|
||||
{
|
||||
'opacity-100': index === currentIndex,
|
||||
'opacity-0': index !== currentIndex,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<img
|
||||
:src="image"
|
||||
:alt="monument.name"
|
||||
class="object-contain max-h-full max-w-full h-full w-auto mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slider controls -->
|
||||
<div v-if="monument.images.length > 1">
|
||||
<button
|
||||
type="button"
|
||||
class="absolute top-0 left-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
||||
@click="prevSlide"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 6 10"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 1 1 5l4 4"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Previous</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute top-0 right-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
|
||||
@click="nextSlide"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 6 10"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 9l4-4-4-4"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Next</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-html="formattedDescription"></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MonumentMarker",
|
||||
props: {
|
||||
monument: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedDescription() {
|
||||
return this.formatDescription(this.monument.description);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// Remplace les mentions d'utilisateurs par des liens cliquables
|
||||
formatDescription(description) {
|
||||
const regex = /@(\w+(-\w+)*(\.\w+(-\w+)*)*)/g;
|
||||
return description.replace(
|
||||
regex,
|
||||
'<a href="/profile/$1" class="text-blue-500 hover:underline">@$1</a>'
|
||||
);
|
||||
},
|
||||
prevSlide() {
|
||||
this.currentIndex =
|
||||
(this.currentIndex - 1 + this.monument.images.length) %
|
||||
this.monument.images.length;
|
||||
},
|
||||
nextSlide() {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.monument.images.length;
|
||||
},
|
||||
},
|
||||
// mounted() {
|
||||
// setInterval(this.nextSlide, 3000);
|
||||
// }
|
||||
};
|
||||
</script>
|
@ -1,152 +0,0 @@
|
||||
<template>
|
||||
<nav class="bg-white border-gray-200 dark:bg-gray-900">
|
||||
<div
|
||||
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
|
||||
>
|
||||
<a class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<img src="../assets/logo.png" class="h-10" alt="Memory Map Logo" />
|
||||
<span
|
||||
class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white"
|
||||
>Memory Map</span
|
||||
>
|
||||
</a>
|
||||
<div class="flex lg:order-2">
|
||||
<button
|
||||
type="button"
|
||||
data-collapse-toggle="navbar-search"
|
||||
aria-controls="navbar-search"
|
||||
aria-expanded="false"
|
||||
class="lg:hidden text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 me-1"
|
||||
>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Search</span>
|
||||
</button>
|
||||
<div class="relative hidden lg:block">
|
||||
<div
|
||||
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Search icon</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-navbar"
|
||||
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
data-collapse-toggle="navbar-search"
|
||||
type="button"
|
||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||
aria-controls="navbar-search"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 17 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="items-center justify-between hidden w-full lg:flex lg:w-auto lg:order-1"
|
||||
id="navbar-search"
|
||||
>
|
||||
<div class="relative mt-3 lg:hidden">
|
||||
<div
|
||||
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-navbar"
|
||||
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
class="flex flex-col p-4 lg:p-0 mt-4 font-medium border border-gray-100 rounded-lg bg-gray-50 lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 lg:bg-white dark:bg-gray-800 lg:dark:bg-gray-900 dark:border-gray-700"
|
||||
>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500"
|
||||
aria-current="page"
|
||||
>Home</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a href="#" class="block py-2 text-gray-900 dark:text-white"
|
||||
>Quests</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a href="#" class="block py-2 text-gray-900 dark:text-white"
|
||||
>Add a pin</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center space-x-2">
|
||||
<a href="#" class="block py-2 text-gray-900 dark:text-white"
|
||||
>Friends</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
@ -1,30 +0,0 @@
|
||||
export const monuments = [
|
||||
{
|
||||
coords: [48.85837, 2.294481],
|
||||
name: "Tour Eiffel",
|
||||
images: [
|
||||
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQGGib245tSFiK1Qcx0cB0dZsoVyJElwsY3kA&s",
|
||||
"https://encrypted-tbn2.gstatic.com/licensed-image?q=tbn:ANd9GcTLB9B0j50rJbcSbdja9_hySHS6_KATbhTK_iCeWeNKtA92hTmTX5nTW3udjjovZrnU1JxqAjMS_VqHnMwHGhTs35-sU-7B29_X_T3uLV8",
|
||||
],
|
||||
description: "Visité en 2020 avec la famille, un moment inoubliable.",
|
||||
visited: true,
|
||||
},
|
||||
{
|
||||
coords: [43.296482, 5.36978],
|
||||
name: "Vieux Port de Marseille",
|
||||
images: [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Marseille_Old_Port.jpg/390px-Marseille_Old_Port.jpg",
|
||||
],
|
||||
description:
|
||||
"Découvert lors d'un week-end ensoleillé en 2019 avec @John-Doe.",
|
||||
visited: true,
|
||||
},
|
||||
{
|
||||
coords: [48.636063, -1.511457],
|
||||
name: "Mont Saint-Michel",
|
||||
images: [],
|
||||
description: "",
|
||||
visited: false,
|
||||
},
|
||||
// ...
|
||||
];
|
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Memory Map</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
import "flowbite";
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./assets/main.css";
|
||||
|
||||
createApp(App).mount("#app");
|
@ -0,0 +1,6 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
@ -1,12 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
"./node_modules/flowbite/**/*.js",
|
||||
],
|
||||
content: ["./src/**/*.{html,ts}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require("flowbite/plugin")],
|
||||
plugins: [],
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
const { defineConfig } = require("@vue/cli-service");
|
||||
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
});
|
Loading…
Reference in new issue