From 0ef50b3f8fc436296433e7962fd6966a1adc9b6c Mon Sep 17 00:00:00 2001 From: Zyie <24736175+Zyie@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:36:08 +0000 Subject: [PATCH 1/2] Feat: allow TextClass to be passed into components --- src/CheckBox.ts | 28 ++-- src/Input.ts | 35 +++-- src/Select.ts | 27 ++-- src/SliderBase.ts | 22 +-- src/stories/checkbox/CheckBoxHTML.stories.ts | 95 ++++++++++++ src/stories/select/SelectHTMLText.stories.ts | 143 +++++++++++++++++++ src/utils/helpers/text.ts | 8 +- 7 files changed, 317 insertions(+), 41 deletions(-) create mode 100644 src/stories/checkbox/CheckBoxHTML.stories.ts create mode 100644 src/stories/select/SelectHTMLText.stories.ts diff --git a/src/CheckBox.ts b/src/CheckBox.ts index 00e4b0c8..e272ff11 100644 --- a/src/CheckBox.ts +++ b/src/CheckBox.ts @@ -1,16 +1,15 @@ import { Container } from '@pixi/display'; -import { TextStyle, ITextStyle, Text } from '@pixi/text'; +import { Text } from '@pixi/text'; import { Signal } from 'typed-signals'; import { Switcher } from './Switcher'; import { cleanup } from './utils/helpers/cleanup'; +import { PixiText, PixiTextClass, PixiTextStyle } from './utils/helpers/text'; import { getView } from './utils/helpers/view'; -type LabelStyle = TextStyle | Partial; - type CheckBoxStyle = { checked: Container | string; unchecked: Container | string; - text?: LabelStyle; + text?: PixiTextStyle; textOffset?: { x?: number; y?: number; @@ -20,6 +19,7 @@ type CheckBoxStyle = { export type CheckBoxOptions = { style: CheckBoxStyle; text?: string; + TextClass?: PixiTextClass; checked?: boolean; }; @@ -36,18 +36,20 @@ export type CheckBoxOptions = { export class CheckBox extends Switcher { //* Text label */ - label!: Text; + label!: PixiText; /** Signal emitted when checkbox state changes. */ onCheck: Signal<(state: boolean) => void>; protected _style: CheckBoxStyle; + protected _textClass: PixiTextClass; constructor(options: CheckBoxOptions) { super(); this.text = options.text; + this._textClass = options.TextClass ?? Text; this.style = options.style; @@ -62,11 +64,11 @@ export class CheckBox extends Switcher this.onChange.connect(() => this.onCheck.emit(this.checked)); } - protected addLabel(text?: string, style?: LabelStyle) + protected addLabel(text?: string, style?: PixiTextStyle) { if (!text) return; - this.label = new Text(text ?? '', style ?? this._style?.text); + this.label = new this._textClass(text ?? '', style ?? this._style?.text); this.addChild(this.label); this.label.cursor = 'pointer'; @@ -120,7 +122,17 @@ export class CheckBox extends Switcher if (this.label) { - if (style.text) this.label.style = style.text; + if (style.text) + { + if ('style' in this.label) + { + this.label.style = style.text; + } + else + { + Object.assign(this.label, style.text); + } + } this.label.x = uncheckedView.width + 10 + (style.textOffset?.x ?? 0); this.label.y = ((uncheckedView.height - this.label.height) / 2) + (style.textOffset?.y ?? 0); diff --git a/src/Input.ts b/src/Input.ts index 6a5329ba..a119934d 100644 --- a/src/Input.ts +++ b/src/Input.ts @@ -1,18 +1,20 @@ -import { Texture, utils, Ticker } from '@pixi/core'; +import { Texture, Ticker, utils } from '@pixi/core'; import { Container, IDestroyOptions } from '@pixi/display'; +import { Graphics } from '@pixi/graphics'; +import { NineSlicePlane } from '@pixi/mesh-extras'; import { Sprite } from '@pixi/sprite'; -import { TextStyle, Text } from '@pixi/text'; +import { Text, TextStyle } from '@pixi/text'; import { Signal } from 'typed-signals'; -import { getView } from './utils/helpers/view'; import { Padding } from './utils/HelpTypes'; -import { NineSlicePlane } from '@pixi/mesh-extras'; -import { Graphics } from '@pixi/graphics'; +import { PixiText, PixiTextClass, PixiTextStyle } from './utils/helpers/text'; +import { getView } from './utils/helpers/view'; type ViewType = Sprite | Graphics | string; export type InputOptions = { bg: ViewType; - textStyle?: Partial; + textStyle?: PixiTextStyle; + TextClass?: PixiTextClass; placeholder?: string; value?: string; maxLength?: number; @@ -41,8 +43,8 @@ export class Input extends Container protected _bg?: Container | NineSlicePlane | Graphics; protected inputMask: Container | NineSlicePlane | Graphics; protected _cursor: Sprite; - protected inputField: Text; - protected placeholder: Text; + protected inputField: PixiText; + protected placeholder: PixiText; protected editing = false; protected tick = 0; @@ -158,19 +160,26 @@ export class Input extends Container } as TextStyle; this.options.textStyle = options.textStyle ?? defaultTextStyle; + this.options.TextClass = options.TextClass ?? Text; + const textStyle = { ...defaultTextStyle, ...options.textStyle }; - const textStyle = new TextStyle(options.textStyle ?? defaultTextStyle); - - this.inputField = new Text('', textStyle); + this.inputField = new this.options.TextClass('', textStyle); this._cursor = new Sprite(Texture.WHITE); - this._cursor.tint = Number(options.textStyle.fill) || 0x000000; + if ('tint' in options.textStyle) + { + this._cursor.tint = options.textStyle.tint; + } + else + { + this._cursor.tint = Number((options.textStyle as TextStyle).fill) || 0x000000; + } this._cursor.anchor.set(0.5); this._cursor.width = 2; this._cursor.height = this.inputField.height * 0.8; this._cursor.alpha = 0; - this.placeholder = new Text(options.placeholder, textStyle ?? defaultTextStyle); + this.placeholder = new this.options.TextClass(options.placeholder, textStyle ?? defaultTextStyle); this.placeholder.visible = !!options.placeholder; this.addChild(this.inputField, this.placeholder, this._cursor); diff --git a/src/Select.ts b/src/Select.ts index ff2c62a3..3d3b73ef 100644 --- a/src/Select.ts +++ b/src/Select.ts @@ -1,9 +1,10 @@ import { Container } from '@pixi/display'; import { Graphics } from '@pixi/graphics'; -import { Text, TextStyle } from '@pixi/text'; +import { Text } from '@pixi/text'; import { Signal } from 'typed-signals'; import { FancyButton } from './FancyButton'; import { ScrollBox, ScrollBoxOptions } from './ScrollBox'; +import { PixiText, PixiTextStyle } from './utils/helpers/text'; import { getView } from './utils/helpers/view'; const defaultVisibleItems = 5; @@ -19,14 +20,16 @@ export type SelectItemsOptions = { width: number; height: number; hoverColor?: number; - textStyle?: Partial; + textStyle?: PixiTextStyle; + TextClass?: new (...args: any[]) => PixiText; radius?: number; }; export type SelectOptions = { closedBG: string | Container; openBG: string | Container; - textStyle?: Partial; + textStyle?: PixiTextStyle; + TextClass?: new (...args: any[]) => PixiText; selected?: number; selectedTextOffset?: { x?: number; y?: number }; @@ -104,9 +107,13 @@ export class Select extends Container * @param root0.selectedTextOffset * @param root0.scrollBox * @param root0.visibleItems + * @param root0.TextClass */ - init({ closedBG, textStyle, items, openBG, selected, selectedTextOffset, scrollBox, visibleItems }: SelectOptions) + init({ + closedBG, textStyle, TextClass, items, openBG, selected, selectedTextOffset, scrollBox, visibleItems + }: SelectOptions) { + TextClass = TextClass ?? Text; if (this.openView && this.openView !== openBG) { this.removeChild(this.openView); @@ -117,7 +124,7 @@ export class Select extends Container { this.openButton = new FancyButton({ defaultView: getView(closedBG), - text: new Text(items?.items ? items.items[0] : '', textStyle), + text: new TextClass(items?.items ? items.items[0] : '', textStyle), textOffset: selectedTextOffset }); this.openButton.onPress.connect(() => this.toggle()); @@ -126,7 +133,7 @@ export class Select extends Container else { this.openButton.defaultView = getView(closedBG); - this.openButton.textView = new Text(items?.items ? items.items[0] : '', textStyle); + this.openButton.textView = new TextClass(items?.items ? items.items[0] : '', textStyle); this.openButton.textOffset = selectedTextOffset; } @@ -146,7 +153,7 @@ export class Select extends Container defaultView: new Graphics() .beginFill(0x000000, 0.00001) .drawRect(0, 0, this.openButton.width, this.openButton.height), - text: new Text(items?.items ? items.items[0] : '', textStyle), + text: new TextClass(items?.items ? items.items[0] : '', textStyle), textOffset: selectedTextOffset }); this.closeButton.onPress.connect(() => this.toggle()); @@ -158,7 +165,7 @@ export class Select extends Container .beginFill(0x000000, 0.00001) .drawRect(0, 0, this.openButton.width, this.openButton.height); - this.closeButton.textView = new Text(items?.items ? items.items[0] : '', textStyle); + this.closeButton.textView = new TextClass(items?.items ? items.items[0] : '', textStyle); this.openButton.textOffset = selectedTextOffset; } @@ -263,9 +270,11 @@ export class Select extends Container width, height, textStyle, + TextClass, radius }: SelectItemsOptions): FancyButton[] { + TextClass = TextClass ?? Text; const buttons: FancyButton[] = []; items.forEach((item) => @@ -275,7 +284,7 @@ export class Select extends Container const color = hoverColor ?? backgroundColor; const hoverView = new Graphics().beginFill(color).drawRoundedRect(0, 0, width, height, radius); - const text = new Text(item, textStyle); + const text = new TextClass(item, textStyle); const button = new FancyButton({ defaultView, hoverView, text }); diff --git a/src/SliderBase.ts b/src/SliderBase.ts index 6d305d48..7f1cf6b9 100644 --- a/src/SliderBase.ts +++ b/src/SliderBase.ts @@ -1,15 +1,17 @@ import { Container } from '@pixi/display'; -import { Sprite } from '@pixi/sprite'; -import { ITextStyle, Text, TextStyle } from '@pixi/text'; -import type { DragObject } from './utils/HelpTypes'; import { FederatedPointerEvent } from '@pixi/events'; +import { Sprite } from '@pixi/sprite'; +import { Text } from '@pixi/text'; import { ProgressBar, ProgressBarOptions, ProgressBarViewType } from './ProgressBar'; +import type { DragObject } from './utils/HelpTypes'; +import { PixiText, PixiTextClass, PixiTextStyle } from './utils/helpers/text'; import { getView } from './utils/helpers/view'; export type BaseSliderOptions = ProgressBarOptions & { min?: number; max?: number; - valueTextStyle?: TextStyle | Partial; + valueTextStyle?: PixiTextStyle; + valueTextClass?: PixiTextClass; showValue?: boolean; valueTextOffset?: { x?: number; @@ -31,8 +33,8 @@ export class SliderBase extends ProgressBar protected _slider1: Container; protected _slider2: Container; - protected value1Text?: Text; - protected value2Text?: Text; + protected value1Text?: PixiText; + protected value2Text?: PixiText; protected _value1: number; protected _value2: number; @@ -82,7 +84,9 @@ export class SliderBase extends ProgressBar if (this.settings.showValue && !this.value1Text) { - this.value1Text = new Text('', this.settings.valueTextStyle || { fill: 0xffffff }); + const TextClass = this.settings.valueTextClass ?? Text; + + this.value1Text = new TextClass('', this.settings.valueTextStyle || { fill: 0xffffff }); this.value1Text.anchor.set(0.5); this.addChild(this.value1Text); } @@ -112,7 +116,9 @@ export class SliderBase extends ProgressBar if (this.settings.showValue && !this.value2Text) { - this.value2Text = new Text('', this.settings.valueTextStyle || { fill: 0xffffff }); + const TextClass = this.settings.valueTextClass ?? Text; + + this.value2Text = new TextClass('', this.settings.valueTextStyle || { fill: 0xffffff }); this.value2Text.anchor.set(0.5); this.addChild(this.value2Text); } diff --git a/src/stories/checkbox/CheckBoxHTML.stories.ts b/src/stories/checkbox/CheckBoxHTML.stories.ts new file mode 100644 index 00000000..9b36cbd3 --- /dev/null +++ b/src/stories/checkbox/CheckBoxHTML.stories.ts @@ -0,0 +1,95 @@ +import { Graphics } from '@pixi/graphics'; +import { CheckBox } from '../../CheckBox'; +import { action } from '@storybook/addon-actions'; +import { List } from '../../List'; +import { argTypes, getDefaultArgs } from '../utils/argTypes'; +import { defaultTextStyle } from '../../utils/helpers/styles'; +import { centerElement } from '../../utils/helpers/resize'; +import { getColor } from '../utils/color'; +import { HTMLText } from '@pixi/text-html'; + +const args = { + text: 'Checkbox', + textColor: '#FFFFFF', + color: '#F1D583', + borderColor: '#DCB000', + fillBorderColor: '#FFFFFF', + fillColor: '#A5E24D', + width: 50, + height: 50, + radius: 11, + amount: 3, + checked: false, + onPress: action('Checkbox') +}; + +export const UseHtml = ({ + text, + amount, + checked, + + textColor, + borderColor, + fillBorderColor, + fillColor, + color, + width, + height, + radius, + + onPress +}: any) => +{ + const view = new List({ type: 'vertical', elementsMargin: 10 }); + + color = getColor(color); + borderColor = getColor(borderColor); + fillColor = getColor(fillColor); + fillBorderColor = getColor(fillBorderColor); + + for (let i = 0; i < amount; i++) + { + // Component usage !!! + const checkBox = new CheckBox({ + text: `${text} ${i + 1}`, + TextClass: HTMLText, + checked, + style: { + unchecked: new Graphics() + .beginFill(borderColor) + .drawRoundedRect(-2, -2, width + 4, height + 4, radius) + .beginFill(color) + .drawRoundedRect(0, 0, width, height, radius), + checked: new Graphics() + .beginFill(borderColor) + .drawRoundedRect(-2, -2, width + 4, height + 4, radius) + .beginFill(color) + .drawRoundedRect(0, 0, width, height, radius) + .beginFill(fillBorderColor) + .drawRoundedRect(3, 3, width - 6, height - 6, radius) + .beginFill(fillColor) + .drawRoundedRect(5, 5, width - 10, height - 10, radius), + text: { + ...defaultTextStyle, + fontSize: 22, + fill: textColor + }, + } + }); + + checkBox.onCheck.connect((checked) => + { + onPress(`checkBox ${i + 1} ${checked}`); + }); + + view.addChild(checkBox); + } + + return { view, resize: () => centerElement(view) }; +}; + +export default { + title: 'Components/Checkbox/Use Html', + argTypes: argTypes(args), + args: getDefaultArgs(args) +}; diff --git a/src/stories/select/SelectHTMLText.stories.ts b/src/stories/select/SelectHTMLText.stories.ts new file mode 100644 index 00000000..049b6c23 --- /dev/null +++ b/src/stories/select/SelectHTMLText.stories.ts @@ -0,0 +1,143 @@ +import { Graphics } from '@pixi/graphics'; +import { Container } from '@pixi/display'; +import { Sprite } from '@pixi/sprite'; +import { HTMLText } from '@pixi/text-html'; +import { argTypes, getDefaultArgs } from '../utils/argTypes'; +import { Select } from '../../Select'; +import { action } from '@storybook/addon-actions'; +import { preload } from '../utils/loader'; +import { defaultTextStyle } from '../../utils/helpers/styles'; +import { centerElement } from '../../utils/helpers/resize'; +import type { StoryFn } from '@storybook/types'; +import { getColor } from '../utils/color'; + +const args = { + backgroundColor: '#F5E3A9', + dropDownBackgroundColor: '#F5E3A9', + dropDownHoverColor: '#A5E24D', + fontColor: '#000000', + fontSize: 28, + width: 250, + height: 50, + radius: 15, + itemsAmount: 5, + onSelect: action('Item selected') +}; + +export const UseHTMLText: StoryFn = ({ + fontColor, + fontSize, + width, + height, + radius, + itemsAmount, + backgroundColor, + dropDownBackgroundColor, + dropDownHoverColor, + onSelect +}: any) => +{ + const view = new Container(); + + backgroundColor = getColor(backgroundColor); + fontColor = getColor(fontColor); + dropDownBackgroundColor = getColor(dropDownBackgroundColor); + const hoverColor = getColor(dropDownHoverColor); + const textStyle = { ...defaultTextStyle, fill: fontColor, fontSize }; + + const items = getItems(itemsAmount, 'Item'); + + // Component usage !!! + // Important: in order scroll to work, you have to call update() method in your game loop. + const select = new Select({ + closedBG: getClosedBG(backgroundColor, width, height, radius), + openBG: getOpenBG(dropDownBackgroundColor, width, height, radius), + textStyle, + TextClass: HTMLText, + items: { + items, + backgroundColor, + hoverColor, + width, + height, + textStyle, + TextClass: HTMLText, + radius + }, + scrollBox: { + width, + height: height * 5, + radius + } + }); + + select.y = 10; + + select.onSelect.connect((_, text) => + { + onSelect({ + id: select.value, + text + }); + }); + + view.addChild(select); + + return { + view, + resize: () => centerElement(view, 0.5, 0) + }; +}; + +function getClosedBG(backgroundColor: number, width: number, height: number, radius: number) +{ + const closedBG = new Graphics().beginFill(backgroundColor).drawRoundedRect(0, 0, width, height, radius); + + preload(['arrow_down.png']).then(() => + { + const arrowDown = Sprite.from('arrow_down.png'); + + arrowDown.anchor.set(0.5); + arrowDown.x = width * 0.9; + arrowDown.y = height / 2; + closedBG.addChild(arrowDown); + }); + + return closedBG; +} + +function getOpenBG(backgroundColor: number, width: number, height: number, radius: number) +{ + const openBG = new Graphics().beginFill(backgroundColor).drawRoundedRect(0, 0, width, height * 6, radius); + + preload(['arrow_down.png']).then(() => + { + const arrowUp = Sprite.from('arrow_down.png'); + + arrowUp.angle = 180; + arrowUp.anchor.set(0.5); + arrowUp.x = width * 0.9; + arrowUp.y = height / 2; + openBG.addChild(arrowUp); + }); + + return openBG; +} + +function getItems(itemsAmount: number, text: string): string[] +{ + const items: string[] = []; + + for (let i = 0; i < itemsAmount; i++) + { + items.push(`${text} ${i + 1}`); + } + + return items; +} + +export default { + title: 'Components/Select/Use HTML Text', + argTypes: argTypes(args), + args: getDefaultArgs(args) +}; diff --git a/src/utils/helpers/text.ts b/src/utils/helpers/text.ts index 3d24030b..896eb53b 100644 --- a/src/utils/helpers/text.ts +++ b/src/utils/helpers/text.ts @@ -1,9 +1,11 @@ -import { Text } from '@pixi/text'; -import { BitmapText } from '@pixi/text-bitmap'; -import { HTMLText } from '@pixi/text-html'; +import { Text, ITextStyle, TextStyle } from '@pixi/text'; +import { BitmapText, IBitmapTextStyle } from '@pixi/text-bitmap'; +import { HTMLText, HTMLTextStyle } from '@pixi/text-html'; export type PixiText = Text | BitmapText | HTMLText; export type AnyText = string | number | PixiText; +export type PixiTextClass = new (...args: any[]) => PixiText; +export type PixiTextStyle = Partial | TextStyle | HTMLTextStyle | Partial; export function getTextView(text: AnyText): Text | BitmapText | HTMLText { From e71293fe4c66c1910fade1b112dd73ddd3ef4046 Mon Sep 17 00:00:00 2001 From: Zyie <24736175+Zyie@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:16:09 +0000 Subject: [PATCH 2/2] fix text class undefined --- src/CheckBox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CheckBox.ts b/src/CheckBox.ts index e272ff11..13d033f1 100644 --- a/src/CheckBox.ts +++ b/src/CheckBox.ts @@ -48,8 +48,8 @@ export class CheckBox extends Switcher { super(); - this.text = options.text; this._textClass = options.TextClass ?? Text; + this.text = options.text; this.style = options.style;