import { LoaderType } from '../types/model-viewer';
/**
* Automatically detects model loader type based on URL, MIME type, or File/Blob.
*/
export function detectLoaderType(
source: string | File | Blob,
mimeType?: string
): LoaderType {
// 1️⃣ Detekce podle přípony (nejčastější případ)
const getExt = (s: string) => s.toLowerCase().split('.').pop() || '';
if (typeof source === 'string') {
const ext = getExt(source);
if (ext === 'stl') return 'stl';
if (ext === 'obj') return 'obj';
if (ext === 'gltf') return 'gltf';
if (ext === 'glb') return 'glb';
}
// 2️⃣ Pokud je to File nebo Blob, zkusíme jméno souboru
if (source instanceof File || source instanceof Blob) {
const name = (source as File).name?.toLowerCase?.() || '';
const ext = getExt(name);
if (ext === 'stl') return 'stl';
if (ext === 'obj') return 'obj';
if (ext === 'gltf') return 'gltf';
if (ext === 'glb') return 'glb';
}
// 3️⃣ Fallback podle MIME typu
const type = (mimeType || '').toLowerCase();
if (type.includes('stl')) return 'stl';
if (type.includes('obj')) return 'obj';
if (type.includes('gltf')) return 'gltf';
if (type.includes('glb')) return 'glb';
throw new Error('Cannot detect loader type from input');
}
/**
* Adds the CSS styles for a spinner overlay to the page.
*
* @returns {void}
*/
function addSpinnerStyle() {
const style = document.createElement('style');
style.textContent = `
.press3d-spinner {
box-sizing: border-box;
border: 2px solid #f3f3f3;
border-top: 2px solid #333;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
box-shadow: 0 0 0 0.3px rgba(204,204,204,0.7);
}
.press3d-spinner-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
color: #333;
font-family: sans-serif;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
}
/**
* Creates a spinner overlay inside the given element.
*
* @param {HTMLElement} elem The element that should contain the spinner.
*/
export function createSpinnerInElement(elem: HTMLElement) {
// --- Overlay spinner ---
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.background = 'transparent';
overlay.innerHTML = `
`;
elem.style.position = 'relative';
elem.appendChild(overlay);
const textElem = overlay.querySelector('.press3d-spinner-text');
addSpinnerStyle();
return {
show: () => {
overlay.style.display = 'flex';
},
hide: () => {
overlay.style.display = 'none';
},
setProgress: (progress: number) => {
if (textElem) {
textElem.textContent = `${Math.round(progress * 100)}%`;
}
},
destroy: () => {
if (overlay.parentNode) {
overlay.parentNode.removeChild(overlay);
}
}
};
}
/**
* Adds the CSS styles for a progress bar overlay to the page.
*
* @returns {void}
*/
export function addProgressBarStyle() {
const style = document.createElement('style');
style.textContent = `
.press3d-progress-container {
width: 60%;
max-width: 200px;
height: 2px;
background: rgba(0,0,0,0.05);
border-radius: 2px;
overflow: hidden;
box-shadow: 0 0 0 0.3px rgba(204,204,204,0.7);
}
.press3d-progress-bar {
width: 0%;
height: 100%;
background: var(--wp--preset--color--primary, #aaa);
transition: width 0.2s ease;
}
.press3d-progress-text {
margin-bottom: 8px;
font-size: 12px;
color: #333;
font-family: sans-serif;
}
`;
document.head.appendChild(style);
}
/**
* Creates a progress bar overlay inside the given element.
*
* @param {HTMLElement} elem The element that should contain the progress bar.
* @returns {Object} An object with a show and hide method.
*/
export function createProgressBarInElement(elem: HTMLElement) {
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.display = 'flex';
overlay.style.flexDirection = 'column';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.background = 'transparent';
overlay.innerHTML = `
0%
`;
elem.style.position = 'relative';
elem.appendChild(overlay);
const progressBar = overlay.querySelector('.press3d-progress-bar');
const textElem = overlay.querySelector('.press3d-progress-text');
if (!progressBar) {
throw new Error('Could not find progress bar element');
}
addProgressBarStyle();
return {
show: () => {
progressBar.style.width = '0%';
if (textElem) textElem.textContent = '0%';
overlay.style.display = 'flex';
},
hide: () => {
overlay.style.display = 'none';
},
setProgress: (progress: number) => {
const percent = Math.round(progress * 100);
progressBar.style.width = `${percent}%`;
if (textElem) {
textElem.textContent = `${percent}%`;
}
},
destroy: () => {
if (overlay.parentNode) {
overlay.parentNode.removeChild(overlay);
}
}
};
}
/**
* Adds the CSS styles for a 3D cube loader overlay to the page.
*
* @returns {void}
*/
export function addCubeStyle() {
const style = document.createElement('style');
style.textContent = `
.press3d-cube-loader {
position: relative;
width: 40px;
height: 40px;
transform-style: preserve-3d;
animation: press3d-cube-spin 2s infinite linear;
}
.press3d-cube-face {
position: absolute;
width: 40px;
height: 40px;
background: rgba(0, 0, 0, 0.02);
border: 1px solid #888;
box-sizing: border-box;
box-shadow: 0 0 0 0.3px rgba(255,255,255,0.7);
}
.press3d-cube-face:nth-child(1) { transform: rotateY(0deg) translateZ(20px); }
.press3d-cube-face:nth-child(2) { transform: rotateY(90deg) translateZ(20px); }
.press3d-cube-face:nth-child(3) { transform: rotateY(180deg) translateZ(20px); }
.press3d-cube-face:nth-child(4) { transform: rotateY(-90deg) translateZ(20px); }
.press3d-cube-face:nth-child(5) { transform: rotateX(90deg) translateZ(20px); }
.press3d-cube-face:nth-child(6) { transform: rotateX(-90deg) translateZ(20px); }
@keyframes press3d-cube-spin {
0% { transform: rotateX(0deg) rotateY(0deg); }
100% { transform: rotateX(360deg) rotateY(360deg); }
}
`;
document.head.appendChild(style);
}
/**
* Creates a 3D cube loader overlay inside the given element.
*
* @param {HTMLElement} elem The element that should contain the cube loader.
* @returns {Object} An object with a show and hide method.
*/
export function createCubeInElement(elem: HTMLElement) {
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.background = 'transparent';
overlay.style.perspective = '1000px'; // Necessary for 3D effect
overlay.innerHTML = `
`;
elem.style.position = 'relative';
elem.appendChild(overlay);
addCubeStyle();
return {
show: () => {
overlay.style.display = 'flex';
},
hide: () => {
overlay.style.display = 'none';
},
destroy: () => {
if (overlay.parentNode) {
overlay.parentNode.removeChild(overlay);
}
}
};
}