PHASE 17: UI Helpers - Button, panel, progress bar, tooltip, notification, checkbox creators
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
|
||||
<!-- UI Theme -->
|
||||
<script src="src/ui/UITheme.js"></script>
|
||||
<script src="src/ui/UIHelpers.js"></script>
|
||||
|
||||
<!-- Utilities -->
|
||||
<script src="src/utils/PerlinNoise.js"></script>
|
||||
|
||||
312
src/ui/UIHelpers.js
Normal file
312
src/ui/UIHelpers.js
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* UI HELPERS
|
||||
* Quick utility functions for creating themed UI elements
|
||||
* Uses UITheme for consistent styling
|
||||
*/
|
||||
|
||||
class UIHelpers {
|
||||
/**
|
||||
* Create themed button
|
||||
*/
|
||||
static createButton(scene, x, y, text, callback, buttonType = 'primary') {
|
||||
const theme = window.UITheme;
|
||||
const btnStyle = theme.buttons[buttonType] || theme.buttons.primary;
|
||||
|
||||
// Button background
|
||||
const width = Math.max(120, text.length * 10 + 40);
|
||||
const height = 40;
|
||||
|
||||
const button = scene.add.container(x, y);
|
||||
|
||||
// Background rectangle
|
||||
const bg = scene.add.rectangle(0, 0, width, height,
|
||||
parseInt(btnStyle.background.replace('#', '0x')), 1);
|
||||
bg.setStrokeStyle(2, parseInt(btnStyle.border.replace('#', '0x')));
|
||||
|
||||
// Text
|
||||
const btnText = scene.add.text(0, 0, text, {
|
||||
fontFamily: theme.fonts.primary,
|
||||
fontSize: theme.fontSizes.medium,
|
||||
color: btnStyle.text
|
||||
});
|
||||
btnText.setOrigin(0.5);
|
||||
|
||||
button.add([bg, btnText]);
|
||||
|
||||
// Make interactive
|
||||
bg.setInteractive({ useHandCursor: true });
|
||||
|
||||
// Hover effect
|
||||
bg.on('pointerover', () => {
|
||||
bg.setFillStyle(parseInt(btnStyle.backgroundHover.replace('#', '0x')));
|
||||
});
|
||||
|
||||
bg.on('pointerout', () => {
|
||||
bg.setFillStyle(parseInt(btnStyle.background.replace('#', '0x')));
|
||||
});
|
||||
|
||||
// Click
|
||||
if (callback) {
|
||||
bg.on('pointerdown', callback);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create themed panel
|
||||
*/
|
||||
static createPanel(scene, x, y, width, height, panelType = 'wooden') {
|
||||
const theme = window.UITheme;
|
||||
return theme.createPanel(scene, x, y, width, height, panelType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create themed text
|
||||
*/
|
||||
static createText(scene, x, y, text, style = 'normal') {
|
||||
const theme = window.UITheme;
|
||||
const textStyle = theme.applyTextStyle(null, style);
|
||||
|
||||
const textObj = scene.add.text(x, y, text, textStyle);
|
||||
return textObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create header text
|
||||
*/
|
||||
static createHeader(scene, x, y, text) {
|
||||
return this.createText(scene, x, y, text, 'header');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create progress bar
|
||||
*/
|
||||
static createProgressBar(scene, x, y, width, height, progress = 0.5) {
|
||||
const theme = window.UITheme;
|
||||
|
||||
const container = scene.add.container(x, y);
|
||||
|
||||
// Background
|
||||
const bg = scene.add.rectangle(0, 0, width, height,
|
||||
theme.getColor('backgrounds.dark'), 1);
|
||||
bg.setStrokeStyle(2, theme.getColor('secondary.steel'));
|
||||
bg.setOrigin(0, 0.5);
|
||||
|
||||
// Fill
|
||||
const fill = scene.add.rectangle(2, 0, (width - 4) * progress, height - 4,
|
||||
theme.getColor('states.success'), 1);
|
||||
fill.setOrigin(0, 0.5);
|
||||
|
||||
container.add([bg, fill]);
|
||||
|
||||
// Update method
|
||||
container.setProgress = function (value) {
|
||||
fill.width = Math.max(0, Math.min(width - 4, (width - 4) * value));
|
||||
};
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create tooltip
|
||||
*/
|
||||
static createTooltip(scene, x, y, text) {
|
||||
const theme = window.UITheme;
|
||||
|
||||
const tooltip = scene.add.container(x, y);
|
||||
tooltip.setDepth(100000);
|
||||
|
||||
// Measure text
|
||||
const tempText = scene.add.text(0, 0, text, {
|
||||
fontFamily: theme.fonts.primary,
|
||||
fontSize: theme.fontSizes.small
|
||||
});
|
||||
const textWidth = tempText.width;
|
||||
const textHeight = tempText.height;
|
||||
tempText.destroy();
|
||||
|
||||
// Background
|
||||
const padding = 8;
|
||||
const bg = scene.add.rectangle(0, 0, textWidth + padding * 2, textHeight + padding * 2,
|
||||
theme.getColor('backgrounds.overlay'));
|
||||
bg.setStrokeStyle(1, theme.getColor('primary.lightBrown'));
|
||||
|
||||
// Text
|
||||
const tooltipText = scene.add.text(0, 0, text, {
|
||||
fontFamily: theme.fonts.primary,
|
||||
fontSize: theme.fontSizes.small,
|
||||
color: theme.colors.text.primary
|
||||
});
|
||||
tooltipText.setOrigin(0.5);
|
||||
|
||||
tooltip.add([bg, tooltipText]);
|
||||
tooltip.setAlpha(0);
|
||||
|
||||
// Show/hide methods
|
||||
tooltip.show = function () {
|
||||
this.setAlpha(1);
|
||||
};
|
||||
|
||||
tooltip.hide = function () {
|
||||
this.setAlpha(0);
|
||||
};
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create notification popup
|
||||
*/
|
||||
static createNotification(scene, text, duration = 2000, type = 'info') {
|
||||
const theme = window.UITheme;
|
||||
|
||||
const width = scene.cameras.main.width;
|
||||
const height = scene.cameras.main.height;
|
||||
|
||||
const notification = scene.add.container(width / 2, height - 100);
|
||||
notification.setScrollFactor(0);
|
||||
notification.setDepth(100000);
|
||||
|
||||
// Background color based on type
|
||||
let bgColor = theme.getColor('states.' + type);
|
||||
if (!bgColor) bgColor = theme.getColor('backgrounds.medium');
|
||||
|
||||
// Background
|
||||
const bg = scene.add.rectangle(0, 0, 300, 60, bgColor, 0.95);
|
||||
bg.setStrokeStyle(2, theme.getColor('primary.darkBrown'));
|
||||
|
||||
// Text
|
||||
const notifText = scene.add.text(0, 0, text, {
|
||||
fontFamily: theme.fonts.primary,
|
||||
fontSize: theme.fontSizes.medium,
|
||||
color: theme.colors.text.primary,
|
||||
align: 'center',
|
||||
wordWrap: { width: 280 }
|
||||
});
|
||||
notifText.setOrigin(0.5);
|
||||
|
||||
notification.add([bg, notifText]);
|
||||
|
||||
// Animate in
|
||||
notification.setAlpha(0);
|
||||
notification.y = height - 50;
|
||||
|
||||
scene.tweens.add({
|
||||
targets: notification,
|
||||
alpha: 1,
|
||||
y: height - 100,
|
||||
duration: 300,
|
||||
ease: 'Back.easeOut'
|
||||
});
|
||||
|
||||
// Animate out
|
||||
scene.tweens.add({
|
||||
targets: notification,
|
||||
alpha: 0,
|
||||
y: height - 50,
|
||||
duration: 300,
|
||||
delay: duration,
|
||||
onComplete: () => {
|
||||
notification.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create icon button (emoji/icon only)
|
||||
*/
|
||||
static createIconButton(scene, x, y, icon, callback, size = 40) {
|
||||
const theme = window.UITheme;
|
||||
|
||||
const button = scene.add.container(x, y);
|
||||
|
||||
// Background
|
||||
const bg = scene.add.circle(0, 0, size / 2,
|
||||
theme.getColor('primary.mediumBrown'), 1);
|
||||
bg.setStrokeStyle(2, theme.getColor('primary.darkBrown'));
|
||||
|
||||
// Icon
|
||||
const iconText = scene.add.text(0, 0, icon, {
|
||||
fontSize: (size * 0.6) + 'px'
|
||||
});
|
||||
iconText.setOrigin(0.5);
|
||||
|
||||
button.add([bg, iconText]);
|
||||
|
||||
// Interactive
|
||||
bg.setInteractive({ useHandCursor: true });
|
||||
|
||||
bg.on('pointerover', () => {
|
||||
bg.setFillStyle(theme.getColor('primary.lightBrown'));
|
||||
});
|
||||
|
||||
bg.on('pointerout', () => {
|
||||
bg.setFillStyle(theme.getColor('primary.mediumBrown'));
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
bg.on('pointerdown', callback);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create checkbox
|
||||
*/
|
||||
static createCheckbox(scene, x, y, label, isChecked = false, callback) {
|
||||
const theme = window.UITheme;
|
||||
|
||||
const checkbox = scene.add.container(x, y);
|
||||
|
||||
// Box
|
||||
const size = 20;
|
||||
const box = scene.add.rectangle(0, 0, size, size,
|
||||
theme.getColor('backgrounds.light'), 1);
|
||||
box.setStrokeStyle(2, theme.getColor('primary.darkBrown'));
|
||||
box.setOrigin(0, 0.5);
|
||||
|
||||
// Checkmark
|
||||
const check = scene.add.text(size / 2, 0, '✓', {
|
||||
fontSize: '18px',
|
||||
color: theme.colors.accent.forestGreen
|
||||
});
|
||||
check.setOrigin(0.5);
|
||||
check.setVisible(isChecked);
|
||||
|
||||
// Label
|
||||
const labelText = scene.add.text(size + 10, 0, label, {
|
||||
fontFamily: theme.fonts.primary,
|
||||
fontSize: theme.fontSizes.normal,
|
||||
color: theme.colors.text.primary
|
||||
});
|
||||
labelText.setOrigin(0, 0.5);
|
||||
|
||||
checkbox.add([box, check, labelText]);
|
||||
|
||||
// State
|
||||
checkbox.checked = isChecked;
|
||||
|
||||
// Interactive
|
||||
box.setInteractive({ useHandCursor: true });
|
||||
|
||||
box.on('pointerdown', () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
check.setVisible(checkbox.checked);
|
||||
|
||||
if (callback) {
|
||||
callback(checkbox.checked);
|
||||
}
|
||||
});
|
||||
|
||||
return checkbox;
|
||||
}
|
||||
}
|
||||
|
||||
// Make globally available
|
||||
window.UIHelpers = UIHelpers;
|
||||
|
||||
console.log('🎨 UI Helpers loaded');
|
||||
Reference in New Issue
Block a user