149 lines
3.8 KiB
TypeScript
149 lines
3.8 KiB
TypeScript
import React from 'react';
|
|
import './APRSSymbol.scss';
|
|
|
|
export interface APRSSymbolProps {
|
|
/**
|
|
* APRS symbol as 2-character string: table + code
|
|
* Examples: "/!" (police station), "\!" (emergency), "/-" (house)
|
|
*
|
|
* First character is table identifier:
|
|
* - '/' for primary table
|
|
* - '\' for secondary table
|
|
* - alphanumeric (0-9, A-Z) for overlay
|
|
*
|
|
* Second character is the symbol code
|
|
*/
|
|
symbol: string;
|
|
|
|
/**
|
|
* Symbol size in pixels (default: 24)
|
|
* Available sizes: 24, 32, 48, 56, 64, 128, 256
|
|
*/
|
|
size?: 24 | 32 | 48 | 56 | 64 | 128 | 256;
|
|
|
|
/**
|
|
* Alternative text for accessibility
|
|
*/
|
|
alt?: string;
|
|
|
|
/**
|
|
* Additional CSS class name
|
|
*/
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* Get the table ID for the sprite filename
|
|
* - Primary table (/): 0
|
|
* - Secondary table (\): 1
|
|
* - Overlay characters: 2
|
|
*/
|
|
const getTableId = (table: string): number => {
|
|
if (table === '/') return 0;
|
|
if (table === '\\') return 1;
|
|
return 2; // Overlay
|
|
};
|
|
|
|
/**
|
|
* Calculate sprite position based on ASCII character code
|
|
* Symbols are arranged in a 16-column grid, ordered by ASCII value
|
|
*/
|
|
const getSymbolPosition = (table: string, code: string): { row: number; col: number } | null => {
|
|
if (!code || code.length !== 1) return null;
|
|
|
|
const charCode = code.charCodeAt(0);
|
|
|
|
// Primary and secondary tables use characters from ! (33) to } (125)
|
|
if (table === '/' || table === '\\') {
|
|
if (charCode < 33 || charCode > 125) return null;
|
|
const index = charCode - 33;
|
|
return {
|
|
row: Math.floor(index / 16),
|
|
col: index % 16
|
|
};
|
|
}
|
|
|
|
// Overlay characters: 0-9 (48-57), A-Z (65-90)
|
|
if (charCode >= 48 && charCode <= 57) {
|
|
// 0-9
|
|
const index = charCode - 48;
|
|
return {
|
|
row: Math.floor(index / 16),
|
|
col: index % 16
|
|
};
|
|
} else if (charCode >= 65 && charCode <= 90) {
|
|
// A-Z
|
|
const index = 10 + (charCode - 65);
|
|
return {
|
|
row: Math.floor(index / 16),
|
|
col: index % 16
|
|
};
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* React component for rendering APRS symbols from sprite sheets
|
|
*/
|
|
export const APRSSymbol: React.FC<APRSSymbolProps> = ({
|
|
symbol,
|
|
size = 24,
|
|
alt,
|
|
className = ''
|
|
}) => {
|
|
// Parse the symbol string (format: table + code)
|
|
if (!symbol || symbol.length < 2) {
|
|
// Return empty div if symbol is invalid
|
|
return <div className={`aprs-symbol aprs-symbol-${size} ${className}`} title={alt} />;
|
|
}
|
|
|
|
const table = symbol.charAt(0);
|
|
const code = symbol.charAt(1);
|
|
|
|
const tableId = getTableId(table);
|
|
const position = getSymbolPosition(table, code);
|
|
|
|
if (!position) {
|
|
// Return empty div if symbol is invalid
|
|
return <div className={`aprs-symbol aprs-symbol-${size} ${className}`} title={alt} />;
|
|
}
|
|
|
|
const { row, col } = position;
|
|
|
|
// Build image paths for different resolutions
|
|
const imagePath1x = `/image/protocol/aprs/aprs-symbols-${size}-${tableId}.png`;
|
|
const imagePath2x = `/image/protocol/aprs/aprs-symbols-${size}-${tableId}@2x.png`;
|
|
const imagePath3x = `/image/protocol/aprs/aprs-symbols-${size}-${tableId}@3x.png`;
|
|
|
|
// Calculate background position
|
|
const bgX = -(col * size);
|
|
const bgY = -(row * size);
|
|
|
|
// Use image-set for retina display support
|
|
const backgroundImage = [
|
|
`-webkit-image-set(url('${imagePath1x}') 1x, url('${imagePath2x}') 2x, url('${imagePath3x}') 3x)`,
|
|
`image-set(url('${imagePath1x}') 1x, url('${imagePath2x}') 2x, url('${imagePath3x}') 3x)`,
|
|
`url('${imagePath1x}')` // Fallback
|
|
].join(', ');
|
|
|
|
const style: React.CSSProperties = {
|
|
backgroundImage,
|
|
backgroundPosition: `${bgX}px ${bgY}px`,
|
|
width: `${size}px`,
|
|
height: `${size}px`
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`aprs-symbol aprs-symbol-${size} ${className}`}
|
|
style={style}
|
|
title={alt || `APRS symbol: ${symbol}`}
|
|
role="img"
|
|
aria-label={alt || `APRS symbol: ${symbol}`}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default APRSSymbol;
|