From 592fed3eb2cdf3171c303e9aba5d2dc6f7d698a6 Mon Sep 17 00:00:00 2001 From: NovaFarma Dev Date: Thu, 11 Dec 2025 13:41:39 +0100 Subject: [PATCH] PHASE 17: UI Helpers - Button, panel, progress bar, tooltip, notification, checkbox creators --- index.html | 1 + src/ui/UIHelpers.js | 312 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 src/ui/UIHelpers.js diff --git a/index.html b/index.html index f834baa..d13cead 100644 --- a/index.html +++ b/index.html @@ -64,6 +64,7 @@ + diff --git a/src/ui/UIHelpers.js b/src/ui/UIHelpers.js new file mode 100644 index 0000000..69094d5 --- /dev/null +++ b/src/ui/UIHelpers.js @@ -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');