Radius/add network structure
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
# TT-Core Component Library (Vue 3)
|
||||
|
||||
Modern, reusable Vue 3 components and utilities for TheTool applications. Built with the Composition API and designed for maximum performance and developer experience.
|
||||
|
||||
**Version:** 2.0.0 (Vue 3)
|
||||
|
||||
## 📦 What's Included
|
||||
|
||||
### Components
|
||||
|
||||
#### Data Display
|
||||
- **`<tt-data-table>`** - Enhanced data table with loading states, skeletons, and placeholders
|
||||
- **`<tt-status-chip>`** - Smart online/offline status chip with lazy loading and IP copy
|
||||
|
||||
#### Feedback
|
||||
- **`<tt-loading-indicator>`** - Processing indicator with animated progress bar
|
||||
- **`<tt-skeleton>`** - Skeleton loader for loading states
|
||||
|
||||
#### Forms
|
||||
- **`<tt-smart-autocomplete>`** - Advanced autocomplete with mode switching (XINON/ESTMK)
|
||||
- **`<tt-file-dropzone>`** - Drag & drop file upload component
|
||||
|
||||
#### Overlays
|
||||
- **`<tt-dialog>`** - Modern modal dialog with portal rendering
|
||||
|
||||
#### Navigation
|
||||
- **`<tt-view-switcher>`** - Tab-based view switching with mobile support
|
||||
|
||||
### Utilities
|
||||
|
||||
Available globally via `window.TT_CORE`:
|
||||
|
||||
```javascript
|
||||
// Clipboard
|
||||
TT_CORE.copyToClipboard(text)
|
||||
|
||||
// Formatting
|
||||
TT_CORE.formatBytes(bytes, decimals)
|
||||
TT_CORE.formatDuration(seconds)
|
||||
TT_CORE.formatNumber(num, decimals, decimalSep, thousandsSep)
|
||||
TT_CORE.formatBits(bps)
|
||||
|
||||
// Validation
|
||||
TT_CORE.calculateSimilarity(str1, str2)
|
||||
TT_CORE.validateData(street, zip, city, info, threshold)
|
||||
TT_CORE.validateEmail(email)
|
||||
TT_CORE.generatePassword(length)
|
||||
|
||||
// Script Loading
|
||||
TT_CORE.loadScript(src)
|
||||
TT_CORE.loadScripts([src1, src2, ...])
|
||||
```
|
||||
|
||||
### Composables (Vue 3 Composition API)
|
||||
|
||||
```javascript
|
||||
// Use in setup() function with Composition API
|
||||
import { useIntersectionObserver, useInfiniteScroll, useAsyncData } from 'window.TT_CORE';
|
||||
|
||||
// Intersection Observer
|
||||
const { targetRef } = TT_CORE.useIntersectionObserver((entry) => {
|
||||
console.log('Element is visible!', entry);
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
// Infinite Scroll
|
||||
const items = ref([...]);
|
||||
const { sentinelRef, visibleItems, loadMore } = TT_CORE.useInfiniteScroll(items, {
|
||||
initialCount: 50,
|
||||
incrementBy: 50
|
||||
});
|
||||
|
||||
// Async Data Fetching
|
||||
const { data, isLoading, hasError, fetchData } = TT_CORE.useAsyncData();
|
||||
await fetchData('/api/users');
|
||||
```
|
||||
|
||||
### Mixins (Options API - Backward Compatibility)
|
||||
|
||||
```javascript
|
||||
// Use with Options API (if not using Composition API)
|
||||
export default {
|
||||
mixins: [
|
||||
TT_CORE.createIntersectionObserverMixin({ threshold: 0.1 }),
|
||||
TT_CORE.createInfiniteScrollMixin({ initialCount: 50 }),
|
||||
TT_CORE.createAsyncDataMixin()
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 Quick Start with Vue 3 CDN
|
||||
|
||||
### 1. Include Vue 3 and TT-Core
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- TT-Core CSS -->
|
||||
<link rel="stylesheet" href="/public/plugins/vue/tt-core/styles/tt-core.css">
|
||||
|
||||
<!-- Vue 3 CDN -->
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<tt-data-table :items="users" :is-loading="loading">
|
||||
<!-- ... -->
|
||||
</tt-data-table>
|
||||
</div>
|
||||
|
||||
<!-- TT-Core Library -->
|
||||
<script src="/public/plugins/vue/tt-core/index.js" type="module"></script>
|
||||
|
||||
<!-- TT-Core Components -->
|
||||
<script src="/public/plugins/vue/tt-core/components/data-display/TtDataTable.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/data-display/TtStatusChip.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/feedback/TtLoadingIndicator.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/feedback/TtSkeleton.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/forms/TtSmartAutocomplete.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/forms/TtFileDropzone.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/overlays/TtDialog.js"></script>
|
||||
<script src="/public/plugins/vue/tt-core/components/navigation/TtViewSwitcher.js"></script>
|
||||
|
||||
<!-- Your App -->
|
||||
<script>
|
||||
const { createApp, ref } = Vue;
|
||||
|
||||
const app = createApp({
|
||||
setup() {
|
||||
const users = ref([
|
||||
{ name: 'John', email: 'john@example.com' },
|
||||
{ name: 'Jane', email: 'jane@example.com' }
|
||||
]);
|
||||
const loading = ref(false);
|
||||
|
||||
return { users, loading };
|
||||
}
|
||||
});
|
||||
|
||||
// IMPORTANT: Register TT-Core components with your app
|
||||
TT_CORE.registerComponents(app);
|
||||
|
||||
app.mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## 📘 Component Usage Examples
|
||||
|
||||
### Data Table
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const users = ref([...]);
|
||||
const loading = ref(false);
|
||||
const hasSearched = ref(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tt-data-table
|
||||
:items="users"
|
||||
:is-loading="loading"
|
||||
:has-searched="hasSearched"
|
||||
density="compact"
|
||||
>
|
||||
<template #head>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</template>
|
||||
|
||||
<template #skeleton-row>
|
||||
<td><tt-skeleton /></td>
|
||||
<td><tt-skeleton /></td>
|
||||
<td><tt-skeleton width="80px" /></td>
|
||||
</template>
|
||||
|
||||
<template #row="{ item, index }">
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.email }}</td>
|
||||
<td>
|
||||
<tt-status-chip
|
||||
:username="item.username"
|
||||
@scan-ip="handleScan"
|
||||
/>
|
||||
</td>
|
||||
</template>
|
||||
</tt-data-table>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Smart Autocomplete (v-model support)
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const customerName = ref('');
|
||||
|
||||
const handleSelect = ({ custnum, display }) => {
|
||||
console.log('Selected:', custnum, display);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tt-smart-autocomplete
|
||||
v-model="customerName"
|
||||
placeholder="Suche Kunde..."
|
||||
@select="handleSelect"
|
||||
@enter="search"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### File Dropzone
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const handleFile = async (file) => {
|
||||
console.log('File selected:', file.name);
|
||||
// Process file...
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tt-file-dropzone
|
||||
accept=".xlsx,.xls"
|
||||
@file-selected="handleFile"
|
||||
buttonText="Datei auswählen"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Dialog/Modal
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const showModal = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tt-dialog
|
||||
:show="showModal"
|
||||
title="User Details"
|
||||
size="wide"
|
||||
@close="showModal = false"
|
||||
>
|
||||
<p>Modal content here...</p>
|
||||
|
||||
<template #footer>
|
||||
<button @click="save">Save</button>
|
||||
<button @click="showModal = false">Cancel</button>
|
||||
</template>
|
||||
</tt-dialog>
|
||||
</template>
|
||||
```
|
||||
|
||||
### View Switcher (v-model support)
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const currentView = ref('users');
|
||||
|
||||
const views = [
|
||||
{ id: 'users', name: 'Users', icon: 'fa fa-users' },
|
||||
{ id: 'settings', name: 'Settings', icon: 'fa fa-cog' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tt-view-switcher
|
||||
v-model="currentView"
|
||||
:options="views"
|
||||
/>
|
||||
|
||||
<div v-if="currentView === 'users'">Users View</div>
|
||||
<div v-else-if="currentView === 'settings'">Settings View</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 🎯 Using Composables in Your Components
|
||||
|
||||
### Intersection Observer
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const { targetRef } = window.TT_CORE.useIntersectionObserver((entry) => {
|
||||
console.log('Element visible!', entry);
|
||||
}, { threshold: 0.5, once: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="targetRef">
|
||||
I will trigger when 50% visible!
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Infinite Scroll
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const allItems = ref([/* 1000 items */]);
|
||||
|
||||
const { sentinelRef, visibleItems, hasMore } = window.TT_CORE.useInfiniteScroll(allItems, {
|
||||
initialCount: 50,
|
||||
incrementBy: 25
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="item in visibleItems" :key="item.id">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
|
||||
<!-- Sentinel element for infinite scroll -->
|
||||
<div ref="sentinelRef" v-if="hasMore">Loading more...</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Async Data Fetching
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const { data, isLoading, hasError, errorMessage, fetchData } = window.TT_CORE.useAsyncData();
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchData('/api/users');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="isLoading">Loading...</div>
|
||||
<div v-else-if="hasError">Error: {{ errorMessage }}</div>
|
||||
<div v-else>
|
||||
<div v-for="user in data" :key="user.id">
|
||||
{{ user.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 🔧 Options API (Traditional Vue Syntax)
|
||||
|
||||
If you prefer the Options API over Composition API:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<tt-data-table :items="users" :is-loading="loading">
|
||||
<!-- ... -->
|
||||
</tt-data-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mixins: [
|
||||
window.TT_CORE.createInfiniteScrollMixin({
|
||||
initialCount: 50,
|
||||
itemsKey: 'users'
|
||||
})
|
||||
],
|
||||
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadUsers();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadUsers() {
|
||||
this.loading = true;
|
||||
// ... fetch logic
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 📝 Migration from Vue 2
|
||||
|
||||
### Key Changes
|
||||
|
||||
1. **Component Registration:**
|
||||
- Vue 2: Components auto-register globally via `Vue.component()`
|
||||
- Vue 3: Must call `TT_CORE.registerComponents(app)` after creating your app
|
||||
|
||||
2. **v-model:**
|
||||
- Vue 2: `v-model` → `value` prop + `input` event
|
||||
- Vue 3: `v-model` → `modelValue` prop + `update:modelValue` event
|
||||
|
||||
3. **Lifecycle Hooks:**
|
||||
- `beforeDestroy` → `beforeUnmount`
|
||||
- `destroyed` → `unmounted`
|
||||
|
||||
4. **Composables:**
|
||||
- Vue 2: Use mixins with `createXxxMixin()`
|
||||
- Vue 3: Use composables with `useXxx()` in `setup()`
|
||||
|
||||
### Update Your Code:
|
||||
|
||||
```javascript
|
||||
// Vue 2
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
data: { ... }
|
||||
});
|
||||
|
||||
// Vue 3
|
||||
const { createApp } = Vue;
|
||||
const app = createApp({
|
||||
setup() {
|
||||
// Composition API
|
||||
}
|
||||
});
|
||||
TT_CORE.registerComponents(app); // ← REQUIRED!
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
## 🎨 Styling
|
||||
|
||||
All components use the `.tt-scope` class for scoping. Customize via CSS variables:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--tt-brand-blue: #005384;
|
||||
--tt-accent: #005384;
|
||||
--tt-accent-2: #1e88c9;
|
||||
--tt-ok: #0f9d58;
|
||||
--tt-bad: #e03131;
|
||||
--tt-border: #e6e9ef;
|
||||
--tt-radius: 10px;
|
||||
--tt-shadow: 0 8px 24px rgba(0, 83, 132, .08);
|
||||
}
|
||||
```
|
||||
|
||||
## 📁 Directory Structure
|
||||
|
||||
```
|
||||
tt-core/
|
||||
├── index.js # Main entry point (Vue 3)
|
||||
├── README.md # This file
|
||||
├── MIGRATION_GUIDE.md # Detailed migration guide
|
||||
├── SUMMARY.md # Project summary
|
||||
├── utils/ # Utility functions
|
||||
│ ├── clipboard.js
|
||||
│ ├── formatting.js
|
||||
│ ├── validation.js
|
||||
│ └── script-loader.js
|
||||
├── components/ # Vue 3 components
|
||||
│ ├── data-display/
|
||||
│ │ ├── TtDataTable.js
|
||||
│ │ └── TtStatusChip.js
|
||||
│ ├── feedback/
|
||||
│ │ ├── TtLoadingIndicator.js
|
||||
│ │ └── TtSkeleton.js
|
||||
│ ├── forms/
|
||||
│ │ ├── TtSmartAutocomplete.js
|
||||
│ │ └── TtFileDropzone.js
|
||||
│ ├── overlays/
|
||||
│ │ └── TtDialog.js
|
||||
│ └── navigation/
|
||||
│ └── TtViewSwitcher.js
|
||||
├── composables/ # Vue 3 composables + mixins
|
||||
│ ├── useIntersectionObserver.js
|
||||
│ ├── useInfiniteScroll.js
|
||||
│ └── useAsyncData.js
|
||||
└── styles/ # CSS styles
|
||||
└── tt-core.css
|
||||
```
|
||||
|
||||
## 🚀 Performance Tips
|
||||
|
||||
1. **Lazy Load Components:** Only load components you need
|
||||
2. **Use Composition API:** Better tree-shaking and performance
|
||||
3. **Leverage Composables:** Reuse logic across components
|
||||
4. **CSS Variables:** Fast theme changes without re-rendering
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Components not rendering?
|
||||
Make sure you called `TT_CORE.registerComponents(app)` after creating your Vue app!
|
||||
|
||||
```javascript
|
||||
const app = createApp({...});
|
||||
TT_CORE.registerComponents(app); // ← Don't forget!
|
||||
app.mount('#app');
|
||||
```
|
||||
|
||||
### v-model not working?
|
||||
Vue 3 uses `modelValue` instead of `value`. TT-Core components support both automatically.
|
||||
|
||||
### Composables not working?
|
||||
Make sure you're using them inside `setup()` or `<script setup>`:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
// ✅ Correct
|
||||
const { data } = TT_CORE.useAsyncData();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
// ❌ Wrong - can't use composables here
|
||||
data() {
|
||||
const { data } = TT_CORE.useAsyncData(); // Error!
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- [Vue 3 Documentation](https://vuejs.org/)
|
||||
- [Composition API Guide](https://vuejs.org/guide/extras/composition-api-faq.html)
|
||||
- [Migration from Vue 2](https://v3-migration.vuejs.org/)
|
||||
|
||||
## 📝 License
|
||||
|
||||
Internal use only - TheTool Development Team
|
||||
|
||||
---
|
||||
|
||||
**Version:** 2.0.0 (Vue 3)
|
||||
**Last Updated:** December 2024
|
||||
Reference in New Issue
Block a user