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:
// 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)
// 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)
// 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
<!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
<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)
<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
<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
<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)
<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
<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
<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
<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:
<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
-
Component Registration:
- Vue 2: Components auto-register globally via
Vue.component() - Vue 3: Must call
TT_CORE.registerComponents(app)after creating your app
- Vue 2: Components auto-register globally via
-
v-model:
- Vue 2:
v-model→valueprop +inputevent - Vue 3:
v-model→modelValueprop +update:modelValueevent
- Vue 2:
-
Lifecycle Hooks:
beforeDestroy→beforeUnmountdestroyed→unmounted
-
Composables:
- Vue 2: Use mixins with
createXxxMixin() - Vue 3: Use composables with
useXxx()insetup()
- Vue 2: Use mixins with
Update Your Code:
// 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:
: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
- Lazy Load Components: Only load components you need
- Use Composition API: Better tree-shaking and performance
- Leverage Composables: Reuse logic across components
- 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!
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>:
<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
📝 License
Internal use only - TheTool Development Team
Version: 2.0.0 (Vue 3) Last Updated: December 2024