// MODIBU shared components
const { useState, useEffect, useRef, useMemo } = React;
// ============ Logo ============
function ModibuLogo({ size = 48, withTagline = false }) {
return (
Monopoli
CyberCare
{withTagline && (
Bermain, belajar, dan peduli
cegah cyberbullying!
)}
);
}
// ============ Decorative background ============
function CloudBG({ density = 'normal' }) {
return (
{density === 'normal' && (
<>
>
)}
);
}
function Cloud({ style }) {
// simple cloud: a pill + two circles via pseudos (CSS .c)
return ;
}
// ============ Skyline (silhouette of buildings at bottom) ============
function Skyline() {
return (
);
}
// ============ Avatar ============
function Avatar({ color, label, size = 56, active = false }) {
const cls = `avatar ${color}${active ? ' active' : ''}`;
const sizeStr = typeof size === 'number' ? `${size}px` : size;
const fontSize = typeof size === 'number' ? `${size * 0.4}px` : `calc(${size} * 0.4)`;
const isImage = label && (label.startsWith('/') || label.includes('.png'));
return (
{isImage ? (

) : label}
);
}
// ============ Pawn ============
function Pawn({ color, icon, size = 1 }) {
const isImage = icon && (icon.startsWith('/') || icon.includes('.png'));
if (isImage) {
return (
);
}
return (
);
}
// ============ Dice ============
const DICE_FACES = {
1: [4],
2: [0, 8],
3: [0, 4, 8],
4: [0, 2, 6, 8],
5: [0, 2, 4, 6, 8],
6: [0, 2, 3, 5, 6, 8],
};
function Dice({ value, rolling, size = 72 }) {
const dots = DICE_FACES[value] || [4];
const sizeStr = typeof size === 'number' ? `${size}px` : size;
return (
{Array.from({ length: 9 }).map((_, i) => (
))}
);
}
// ============ Score Pill ============
function ScorePill({ player, active }) {
return (
{player.name}
{player.score} poin
);
}
// ============ Tile icon (category) ============
function TileIcon({ type, size = 22 }) {
const s = typeof size === 'string' ? size : `${size}px`;
// Mapping to new public sticker images
const StickerMap = {
edukasi: '/book.png',
fakta: '/fun-fact.png',
tantangan: '/target.png',
refleksi: '/thinking.png',
kesempatan: '/dice-game.png',
hukuman: '/shock.png',
start: '/lets-go.png',
finish: '/trophy.png',
bonus: '/celebrating.png',
penjara: '/not-accept.png'
};
const src = StickerMap[type];
if (src) {
return (
);
}
// Fallback to SVGs if image missing
const Icons = {
edukasi: (
),
fakta: (
),
tantangan: (
),
refleksi: (
),
kesempatan: (
),
hukuman: (
),
start: (
),
finish: (
),
penjara: (
),
bonus: (
),
};
return Icons[type] || null;
}
// ============ Chip ============
function Chip({ color, label, icon }) {
return (
{color && }
{icon}
{label}
);
}
// ============ Section header ============
function SectionHeader({ title, sub, accent }) {
return (
{accent && ●}
{title}
{sub &&
{sub}
}
);
}
// ============ Decorative monochrome backdrop pattern for tile ============
function TilePattern() {
return (
);
}
// expose
Object.assign(window, {
ModibuLogo, CloudBG, Cloud, Skyline, Avatar, Pawn, Dice, ScorePill,
TileIcon, Chip, SectionHeader, TilePattern,
});