This commit is contained in:
Andrey Sharshov
2025-11-16 18:39:03 +01:00
commit f987e4dffe
15 changed files with 22849 additions and 0 deletions

6
.babelrc Normal file
View File

@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript"
]
}

6
.barrelsby.json Normal file
View File

@@ -0,0 +1,6 @@
{
"directory": "./src",
"exclude": ["index.ts","**/stories/**"],
"delete": true,
"exportDefault": false
}

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# This file is for unifying the coding style for different editors and IDEs.
# More information at http://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[{*.json,bower.json,.travis.yml}]

8
.eslintignore Normal file
View File

@@ -0,0 +1,8 @@
/docs/**
**/dist/**
**/lib/**
**/types/**
temp
docs
dist
node_modules

50
.eslintrc.json Normal file
View File

@@ -0,0 +1,50 @@
{
"extends": ["@pixi/eslint-config"],
"plugins": ["jsdoc", "no-mixed-operators"],
"settings": {
"jsdoc": {
"mode": "typescript",
"tagNamePreference": {
"method": "method",
"function": "function",
"extends": "extends",
"typeParam": "typeParam",
"api": "api"
}
}
},
"rules": {
"@typescript-eslint/no-unused-expressions": [1, {"allowShortCircuit": true, "allowTernary": true}],
"no-mixed-operators": "off",
"no-mixed-operators/no-mixed-operators": 1,
"@typescript-eslint/type-annotation-spacing": 1,
"jsdoc/multiline-blocks": [
1,
{ "noMultilineBlocks": true, "minimumLengthForMultiline": 115 }
],
"jsdoc/check-access": 1,
"jsdoc/check-alignment": 1,
"jsdoc/check-param-names": 1,
"jsdoc/check-property-names": 1,
"jsdoc/check-tag-names": 1,
"jsdoc/check-types": 1,
"jsdoc/check-values": 1,
"jsdoc/empty-tags": 1,
"jsdoc/implements-on-classes": 1,
"jsdoc/no-multi-asterisks": [1, { "allowWhitespace": true }],
"jsdoc/require-param": 1,
"jsdoc/require-param-description": 0,
"jsdoc/require-param-name": 1,
"jsdoc/require-param-type": [
"warn",
{ "contexts": ["TSMethodSignature"] }
],
"jsdoc/require-property": 1,
"jsdoc/require-property-description": 1,
"jsdoc/require-property-name": 1,
"jsdoc/require-property-type": 1,
"jsdoc/require-returns-description": 1,
"jsdoc/tag-lines": 1,
"jsdoc/valid-types": 1
}
}

7
.gitattributes vendored Normal file
View File

@@ -0,0 +1,7 @@
*.js text eol=lf
*.ts text eol=lf
*.json text eol=lf
*.yml text eol=lf
*.md text eol=lf
*.txt text eol=lf

44
.gitignore vendored Normal file
View File

@@ -0,0 +1,44 @@
# sublime text files
*.sublime*
*.vscode*
*.*~*.TMP
test/lib
# temp files
.DS_Store
Thumbs.db
Desktop.ini
npm-debug.log
# project files
.project
# vim swap files
*.sw*
# emacs temp files
*~
\#*#
# project ignores
!.gitkeep
*__temp
node_modules
bin/
lib/
dist/
coverage/
temp
types
yarn.lock
pnpm-lock.yaml
# jetBrains IDE ignores
.idea
.vs-code
.eslintcache
docs/
example.api.json*
.npmrc

48
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,48 @@
image: node:18.18.2
stages:
- install
- build
- deploy
variables:
NODE_ENV: 'production'
cache:
paths:
- node_modules/
before_script:
- echo "Setting up environment"
- echo "@popiplay:registry=https://gitlab.popiplay.dev/api/v4/projects/fe%2Fnpm%2Fstate-machine/packages/npm/" > .npmrc
- echo "//gitlab.popiplay.dev/api/v4/projects/fe%2Fnpm%2Fstate-machine/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
build:
stage: build
script:
- rm -rf node_modules package-lock.json
- npm install --include=dev
- npm run build
artifacts:
paths:
- lib/ # Сохраняем собранные файлы в артефакты, чтобы они были доступны на этапе deploy
publish master:
stage: deploy
script:
- git config --global user.email ${GITLAB_USER_EMAIL}
- git config --global user.name ${GITLAB_USER_LOGIN}
- npm version patch
- npm publish --access=public
only:
- master # Публиковать только при изменениях в main ветке
publish branch:
stage: deploy
script:
- git config --global user.email ${GITLAB_USER_EMAIL}
- git config --global user.name ${GITLAB_USER_LOGIN}
- npm version prerelease --preid=dev
- npm publish --access=public
except:
- master

1
README.md Normal file
View File

@@ -0,0 +1 @@
# SlotMachines Framework

22377
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

111
package.json Normal file
View File

@@ -0,0 +1,111 @@
{
"name": "@popiplay/state-machine",
"version": "1.0.5",
"description": "It is a library that contains slot machines and their components, that are extensible to allow them to be used in any project",
"homepage": "https://gitlab.popiplay.dev/fe/npm/state-machine#readme",
"bugs": "https://gitlab.popiplay.dev/fe/npm/state-machine/issues",
"repository": {
"type": "git",
"url": "git+https://gitlab.popiplay.dev/fe/npm/state-machine.git"
},
"license": "ISC",
"author": "Andrey Sharshov <andrey.sharshov@popiplay.com>",
"sideEffects": false,
"exports": {
".": {
"import": "./lib/index.mjs",
"require": "./lib/index.js"
},
"./*": "./lib/*"
},
"main": "./lib/index.js",
"module": "./lib/index.mjs",
"types": "./lib/index.d.ts",
"files": [
"lib",
"dist/"
],
"scripts": {
"start": "npm i && npm run storybook",
"build": "xs build",
"clean": "xs clean",
"deploy": "xs deploy",
"docs": "xs docs && npm run storybook:build",
"lint": "xs lint --max-warnings 0",
"lint:fix": "xs lint --fix",
"prepare": "husky install",
"release": "npm i && xs bump,build,docs,publish,git-push",
"serve": "xs serve",
"storybook": "storybook dev -p 6006",
"storybook:build": "storybook build --output-dir docs/storybook",
"types": "xs types",
"watch": "xs watch",
"generate-barrels": "barrelsby --directory ./src --exclude index.ts, stories --delete --exportDefault false --singleQuotes"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,js,mjs}": [
"npm run lint:fix --"
]
},
"extensionConfig": {
"lint": [
"src"
],
"docsName": "Popiplay Slot Machines",
"docsTitle": "Popiplay Slot Machines",
"docsDescription": "API Documentation for Slot Machines components made with PixiJS",
"docsKeyword": "PixiJS, SlotMachine, components"
},
"keywords": [
"state",
"machine"
],
"peerDependencies": {
},
"devDependencies": {
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.26.0",
"@chromatic-com/storybook": "^3.2.1",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@pixi/extension-scripts": "^2.4.1",
"@pixi/filter-motion-blur": "^5.1.1",
"@pixi/storybook-renderer": "^1.0.0",
"@pixi/storybook-webpack5": "^1.0.0",
"@popiplay/state-machine": "^1.0.0",
"@rollup/plugin-commonjs": "^28.0.1",
"@storybook/addon-docs": "^8.4.0",
"@storybook/addon-essentials": "^8.4.0",
"@storybook/addon-interactions": "^8.4.0",
"@storybook/addon-links": "^8.4.0",
"@storybook/addon-storysource": "^8.4.0",
"@storybook/addon-webpack5-compiler-babel": "^3.0.3",
"@storybook/test": "^8.4.0",
"@storybook/types": "^8.4.0",
"@types/babel__core": "^7.20.5",
"@types/jest": "^29.5.14",
"babel-loader": "^9.2.1",
"eslint": "^8.57.1",
"eslint-plugin-jsdoc": "^50.4.3",
"eslint-plugin-no-mixed-operators": "^1.1.1",
"husky": "^9.1.6",
"jest": "^29.7.0",
"jest-raw-loader": "^1.0.1",
"lint-staged": "^15.2.10",
"pixi-spine": "^4.0.4",
"pixi.js": "^7.4.2",
"storybook": "^8.4.0",
"tsc-alias": "^1.8.10",
"typescript": "^5.7.2"
},
"publishConfig": {
"@popiplay:registry": "https://gitlab.popiplay.dev/api/v4/projects/fe%2Fnpm%2Fstate-machine/packages/npm/"
}
}

60
src/core/State.ts Normal file
View File

@@ -0,0 +1,60 @@
/**
* @file State.ts
* @description A base class representing a state in a state machine architecture,
* with lifecycle hooks for entering, handling input, updating, and leaving the state.
*/
/**
* Represents a generic state in a state machine architecture.
* Provides lifecycle hooks that can be overridden by subclasses.
*/
export default class State {
/**
* The name that uniquely identifies the state.
*/
public name: string;
/**
* Creates a new instance of the State class.
* @param name - A string that uniquely identifies the state.
*/
constructor(name: string) {
this.name = name;
}
/**
* Method called when the state is entered. Intended to set up any state-specific requirements.
* To be overridden by subclasses.
*/
public enter(): void {
console.log(`Enter ${this.name}`);
}
/**
* A hook for handling input when the state is active.
* To be overridden by subclasses.
*
* @param _input - An optional object representing user input.
*/
public handleInput(_input?: Record<string, any>): void {
// Default implementation is a no-op.
}
/**
* A hook for updating the state in the game loop.
* To be overridden by subclasses.
*
* @param _dt - The time elapsed since the last update, in milliseconds.
*/
public update(_dt: number): void {
// Default implementation is a no-op.
}
/**
* Method called when the state is exited. Intended to perform cleanup or reset activities.
* To be overridden by subclasses.
*/
public leave(): void {
console.log(`Leave ${this.name}`);
}
}

83
src/core/StateMachine.ts Normal file
View File

@@ -0,0 +1,83 @@
/**
* @file StateMachine.ts
* @description A simple state machine implementation for managing state transitions.
*/
import State from './State';
/**
* Represents a state machine for managing state transitions.
* @template T The type of the controlled object or entity.
*/
export default class StateMachine<T, S extends State = State> {
/**
* The controlled object or entity.
*/
public target: T;
/**
* A list of states managed by the state machine.
*/
public states: S[];
/**
* The current active state.
*/
public currentState: S | null;
/**
* Constructs a new StateMachine instance.
* @param target - The controlled object or entity.
*/
constructor(target: T) {
this.target = target;
this.states = [new State("Entry")] as S[];
this.currentState = null;
}
/**
* Provides a list of states to the state machine.
* @param states - An array of states to manage.
*/
public provideStates(states: S[]): void {
this.states = states;
}
/**
* Transitions the state machine to a specific state by its index in the states array.
* @param stateIndex - The index of the state to transition to.
* @throws If the index is out of bounds.
*/
public setState(stateIndex: number): void {
if (stateIndex < 0 || stateIndex >= this.states.length) {
throw new Error(`State index ${stateIndex} is out of bounds.`);
}
if (this.currentState) {
this.currentState.leave();
}
this.currentState = this.states[stateIndex];
this.currentState.enter();
}
/**
* Updates the current state of the state machine.
* @param dt - The time elapsed since the last update in milliseconds.
*/
public update(dt: number): void {
if (this.currentState) {
this.currentState.update(dt);
}
}
/**
* Handles user input and delegates it to the current state.
* @param input - An object representing user input.
*/
public handleInput(input: Record<string, any>): void {
if (this.currentState) {
this.currentState.handleInput(input);
}
}
}

4
src/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export { default as StateMachine } from "./core/StateMachine";
export * from "./core/StateMachine";
export { default as State } from "./core/State";
export * from "./core/State";

32
tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext", "DOM", "ScriptHost"],
"strict": true,
"esModuleInterop": true,
"allowJs": true,
"moduleResolution": "node",
"sourceMap": false,
"declaration": true,
"declarationMap": true,
"declarationDir": "./lib",
"outDir": "./lib",
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"strictNullChecks": false,
"resolveJsonModule": true,
"skipLibCheck": true,
"noImplicitOverride": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": "./",
"paths": {
"src/*": ["src/*"]
}
},
"include": ["./src", "./typings", "./test", "./docs-source"]
}