feat: ✨ brought ui back to daisyui standard implemented v1 pointbloud (powered by chatgpt) and build functionless navbar
This commit is contained in:
parent
6864b89b34
commit
ab030af44b
28
web/package-lock.json
generated
28
web/package-lock.json
generated
@ -11,8 +11,10 @@
|
|||||||
"maplibre-gl": "^4.7.1",
|
"maplibre-gl": "^4.7.1",
|
||||||
"my-vue-app": "file:",
|
"my-vue-app": "file:",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
|
"three": "^0.172.0",
|
||||||
"vue": "^3.4.37",
|
"vue": "^3.4.37",
|
||||||
"vue-daisyui-theme-manager": "^0.0.29"
|
"vue-daisyui-theme-manager": "^0.0.29",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.2",
|
||||||
@ -3371,6 +3373,11 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
|
||||||
|
},
|
||||||
"node_modules/@vue/language-core": {
|
"node_modules/@vue/language-core": {
|
||||||
"version": "2.1.6",
|
"version": "2.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz",
|
||||||
@ -12121,6 +12128,11 @@
|
|||||||
"node": ">=8.9.0"
|
"node": ">=8.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/three": {
|
||||||
|
"version": "0.172.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/three/-/three-0.172.0.tgz",
|
||||||
|
"integrity": "sha512-6HMgMlzU97MsV7D/tY8Va38b83kz8YJX+BefKjspMNAv0Vx6dxMogHOrnRl/sbMIs3BPUKijPqDqJ/+UwJbIow=="
|
||||||
|
},
|
||||||
"node_modules/thunky": {
|
"node_modules/thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
@ -12697,6 +12709,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.6.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-style-loader": {
|
"node_modules/vue-style-loader": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||||
|
@ -11,8 +11,10 @@
|
|||||||
"maplibre-gl": "^4.7.1",
|
"maplibre-gl": "^4.7.1",
|
||||||
"my-vue-app": "file:",
|
"my-vue-app": "file:",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
|
"three": "^0.172.0",
|
||||||
"vue": "^3.4.37",
|
"vue": "^3.4.37",
|
||||||
"vue-daisyui-theme-manager": "^0.0.29"
|
"vue-daisyui-theme-manager": "^0.0.29",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.2",
|
||||||
|
@ -3,11 +3,11 @@ import {defineComponent, Ref, ref, SetupContext} from 'vue';
|
|||||||
import DebugLog from "./classes/debugger";
|
import DebugLog from "./classes/debugger";
|
||||||
import Settings from "./components/Settings.vue";
|
import Settings from "./components/Settings.vue";
|
||||||
import cookiePrompt from "./components/cookiePrompt.vue";
|
import cookiePrompt from "./components/cookiePrompt.vue";
|
||||||
import Map from "./components/map.vue";
|
|
||||||
import FileUpload from './components/fileUpload.vue';
|
import FileUpload from './components/fileUpload.vue';
|
||||||
|
import PointCloud from './components/cloud.vue';
|
||||||
|
import Navbar from './components/navbar.vue';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {Settings, cookiePrompt, Map},
|
components: {Settings, cookiePrompt, PointCloud, Navbar},
|
||||||
setup() {
|
setup() {
|
||||||
var showSettings:Ref<boolean> = ref(false);
|
var showSettings:Ref<boolean> = ref(false);
|
||||||
var showCookiePrompt:Ref<boolean> = ref(localStorage.getItem('allow-data-storage') == undefined);
|
var showCookiePrompt:Ref<boolean> = ref(localStorage.getItem('allow-data-storage') == undefined);
|
||||||
@ -42,41 +42,13 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="appContainer">
|
||||||
|
<Navbar></Navbar>
|
||||||
<Settings v-if="showSettings" @close="toggleSettings"/>
|
<Settings v-if="showSettings" @close="toggleSettings"/>
|
||||||
<cookiePrompt v-if="showCookiePrompt"></cookiePrompt>
|
<cookiePrompt v-if="showCookiePrompt"></cookiePrompt>
|
||||||
<FileUpload></FileUpload>
|
<FileUpload></FileUpload>
|
||||||
<div class="grid-cols-2">
|
<PointCloud></PointCloud>
|
||||||
<div>
|
</div>
|
||||||
<ul class="menu bg-base-200 w-56 p-0 [&_li>*]:rounded-none" style="height: calc(100vh - 77px);">
|
|
||||||
<li><a>Item 1</a></li>
|
|
||||||
<li><a>Item 2</a></li>
|
|
||||||
<li><a>Item 3</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="float: right; width: 100%;">
|
|
||||||
<ul class="menu bg-base-200 lg:menu-horizontal" style="width: 100%;">
|
|
||||||
<li>
|
|
||||||
<a>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cloud-upload-fill" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"/>
|
|
||||||
</svg>
|
|
||||||
Upload
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a @click="toggleSettings">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-sliders" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M11.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M9.05 3a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0V3zM4.5 7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M2.05 8a2.5 2.5 0 0 1 4.9 0H16v1H6.95a2.5 2.5 0 0 1-4.9 0H0V8zm9.45 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m-2.45 1a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0v-1z"/>
|
|
||||||
</svg>
|
|
||||||
Settings
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<Map></Map>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -84,7 +56,7 @@ export default defineComponent({
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.container {
|
.appContainer {
|
||||||
margin: 0;
|
margin: 0 10% 0 10%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
177
web/src/components/cloud.vue
Normal file
177
web/src/components/cloud.vue
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, SetupContext, ref, onMounted, Ref } from "vue";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
type Route = {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
time: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "ClickablePointCloud",
|
||||||
|
emits: ["close"],
|
||||||
|
setup(_, { emit }: SetupContext) {
|
||||||
|
const canvasRef: Ref<HTMLCanvasElement | null> = ref(null);
|
||||||
|
const tooltipRef: Ref<HTMLDivElement | null> = ref(null);
|
||||||
|
const tooltipText = ref("");
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
emit("close");
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!canvasRef.value) return;
|
||||||
|
|
||||||
|
// Szene, Kamera und Renderer einrichten
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(
|
||||||
|
75,
|
||||||
|
window.innerWidth / window.innerHeight,
|
||||||
|
0.1,
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
const renderer = new THREE.WebGLRenderer({
|
||||||
|
canvas: canvasRef.value,
|
||||||
|
antialias: true,
|
||||||
|
});
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
|
// OrbitControls aktivieren
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement);
|
||||||
|
|
||||||
|
// Raycaster und Maus
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const mouse = new THREE.Vector2();
|
||||||
|
|
||||||
|
// Beispiel-Daten (Punkte)
|
||||||
|
const points: Route[] = Array.from({ length: 100 }, (_, i) => ({
|
||||||
|
name: `Route ${i + 1}`,
|
||||||
|
id: i + 1,
|
||||||
|
time: new Date(Date.now() - i * 1000 * 60),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Punktwolke erstellen
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
const vertices = new Float32Array(
|
||||||
|
points.flatMap((_, i) => [
|
||||||
|
(Math.random() - 0.5) * 20,
|
||||||
|
(Math.random() - 0.5) * 20,
|
||||||
|
(Math.random() - 0.5) * 20,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
|
||||||
|
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
color: 0x0077ff,
|
||||||
|
size: 0.3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pointCloud = new THREE.Points(geometry, material);
|
||||||
|
scene.add(pointCloud);
|
||||||
|
|
||||||
|
// Kamera und Animation
|
||||||
|
camera.position.set(10, 10, 25);
|
||||||
|
controls.update();
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
controls.update();
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
animate();
|
||||||
|
|
||||||
|
// Mausbewegung (für Tooltip)
|
||||||
|
const onMouseMove = (event: MouseEvent) => {
|
||||||
|
if (!canvasRef.value || !tooltipRef.value) return;
|
||||||
|
|
||||||
|
// Mausposition berechnen (normalisiert)
|
||||||
|
const rect = canvasRef.value.getBoundingClientRect();
|
||||||
|
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
|
// Raycasting
|
||||||
|
raycaster.setFromCamera(mouse, camera);
|
||||||
|
const intersects = raycaster.intersectObject(pointCloud);
|
||||||
|
|
||||||
|
if (intersects.length > 0) {
|
||||||
|
const intersectIndex = Math.floor(intersects[0].index || 0);
|
||||||
|
const point = points[intersectIndex];
|
||||||
|
|
||||||
|
tooltipRef.value.style.display = "block";
|
||||||
|
tooltipRef.value.style.left = `${event.clientX}px`;
|
||||||
|
tooltipRef.value.style.top = `${event.clientY - 20}px`;
|
||||||
|
tooltipText.value = `${point.name} (${point.time.toLocaleString()})`;
|
||||||
|
} else {
|
||||||
|
tooltipRef.value.style.display = "none";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Punkt klicken
|
||||||
|
const onMouseClick = (event: MouseEvent) => {
|
||||||
|
if (!canvasRef.value) return;
|
||||||
|
|
||||||
|
// Raycasting
|
||||||
|
raycaster.setFromCamera(mouse, camera);
|
||||||
|
const intersects = raycaster.intersectObject(pointCloud);
|
||||||
|
|
||||||
|
if (intersects.length > 0) {
|
||||||
|
const intersectIndex = Math.floor(intersects[0].index || 0);
|
||||||
|
const point = points[intersectIndex];
|
||||||
|
router.push(`/routeViewer/${point.id}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
canvasRef.value.addEventListener("mousemove", onMouseMove);
|
||||||
|
canvasRef.value.addEventListener("click", onMouseClick);
|
||||||
|
|
||||||
|
// Fenstergröße anpassen
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
close,
|
||||||
|
canvasRef,
|
||||||
|
tooltipRef,
|
||||||
|
tooltipText,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="pointcloud-container relative">
|
||||||
|
<canvas ref="canvasRef"></canvas>
|
||||||
|
<div ref="tooltipRef" class="tooltip hidden fixed z-50 px-2 py-1 bg-base-200 text-base-content rounded">
|
||||||
|
{{ tooltipText }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pointcloud-container {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(90vh);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,62 +1,37 @@
|
|||||||
<template>
|
<script lang="ts">
|
||||||
<div class="map-wrap">
|
import {defineComponent, SetupContext} from 'vue';
|
||||||
<div class="map" ref="mapContainer"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
export default defineComponent({
|
||||||
import { Map, NavigationControl, Marker } from 'maplibre-gl';
|
name: 'map',
|
||||||
import { shallowRef, onMounted, onUnmounted, markRaw } from 'vue';
|
setup(_, { emit }: SetupContext) {
|
||||||
|
return {
|
||||||
export default {
|
};
|
||||||
name: "Map",
|
},
|
||||||
setup () {
|
});
|
||||||
const mapContainer = shallowRef(null);
|
</script>
|
||||||
const map = shallowRef(null);
|
|
||||||
|
|
||||||
onMounted(() => {
|
<template>
|
||||||
const apiKey = "irEA6TiXQV7M8II74un7";
|
the map has not yet been implemented
|
||||||
if (apiKey == null) {
|
<ul class="menu bg-base-200 lg:menu-horizontal" style="width: 100%;">
|
||||||
throw new Error("You need to configure env VUE_APP_API_KEY first, see README");
|
<li>
|
||||||
}
|
<a>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cloud-upload-fill" viewBox="0 0 16 16">
|
||||||
const initialState = { lng: 139.753, lat: 35.6844, zoom: 14 };
|
<path fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"/>
|
||||||
|
</svg>
|
||||||
map.value = markRaw(new Map({
|
Upload
|
||||||
container: mapContainer.value,
|
</a>
|
||||||
style: `https://api.maptiler.com/maps/streets/style.json?key=${apiKey}`,
|
</li>
|
||||||
center: [initialState.lng, initialState.lat],
|
<li>
|
||||||
zoom: initialState.zoom
|
<a @click="toggleSettings">
|
||||||
}));
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-sliders" viewBox="0 0 16 16">
|
||||||
map.value.addControl(new NavigationControl(), 'top-right');
|
<path fill-rule="evenodd" d="M11.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M9.05 3a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0V3zM4.5 7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M2.05 8a2.5 2.5 0 0 1 4.9 0H16v1H6.95a2.5 2.5 0 0 1-4.9 0H0V8zm9.45 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m-2.45 1a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0v-1z"/>
|
||||||
new Marker({color: "#FF0000"})
|
</svg>
|
||||||
.setLngLat([139.7525,35.6841])
|
Settings
|
||||||
.addTo(map.value);
|
</a>
|
||||||
});
|
</li>
|
||||||
|
</ul>
|
||||||
onUnmounted(() => {
|
</template>
|
||||||
map.value?.remove();
|
|
||||||
});
|
<style scoped>
|
||||||
|
</style>
|
||||||
return {
|
|
||||||
map, mapContainer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@import 'maplibre-gl/dist/maplibre-gl.css';
|
|
||||||
|
|
||||||
.map-wrap {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - 77px); /* calculate height of the screen minus the heading */
|
|
||||||
}
|
|
||||||
|
|
||||||
.map {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
70
web/src/components/navbar.vue
Normal file
70
web/src/components/navbar.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent, SetupContext} from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Navbar',
|
||||||
|
setup(_, { emit }: SetupContext) {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="navbar bg-base-100">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<div class="dropdown">
|
||||||
|
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M4 6h16M4 12h8m-8 6h16" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabindex="0"
|
||||||
|
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
|
||||||
|
<li><a>Item 1</a></li>
|
||||||
|
<li>
|
||||||
|
<a>Parent</a>
|
||||||
|
<ul class="p-4">
|
||||||
|
<li><a>Submenu 1</a></li>
|
||||||
|
<li><a>Submenu 2</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a>Item 3</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-ghost text-xl">GeoTrack</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-center hidden lg:flex">
|
||||||
|
<ul class="menu menu-horizontal px-1">
|
||||||
|
<li><a>Home</a></li>
|
||||||
|
<li><a>Driver</a></li>
|
||||||
|
<li><a>Vehicle</a></li>
|
||||||
|
<li><a>Routes</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<details class="dropdown">
|
||||||
|
<summary class="btn m-1">menu</summary>
|
||||||
|
<ul class="menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
|
||||||
|
<li><a>login</a></li>
|
||||||
|
<li><a>register</a></li>
|
||||||
|
<li><a>Settings</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user