diff --git a/README.md b/README.md index 4414983..24e22d7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## typescript-package/state -Simple state management for TypeScript. +Simple state management for different types in TypeScript. [![npm version][typescript-package-npm-badge-svg]][typescript-package-npm-badge] @@ -22,10 +22,23 @@ Simple state management for TypeScript. * [Installation](#installation) * [Api](#api) + * [`Ability`](#ability) + * [`ArrayState`](#arraystate) + * [`BooleanArrayState`](#booleanarraystate) + * [`NamedArrayState`](#namedarraystate) + * `NamedBooleanArrayState` + * `Boolean` * [`BooleanState`](#booleanstate) + * `Enum` * [`EnumState`](#enumstate) + * [`BooleanActionObjectState`](#booleanactionobjectstate) + * `BooleanObjectState` + * `NamedObjectState` + * `ObjectState` + * `ImmutableState` * [`NullState`](#nullstate) * [`NumberState`](#numberstate) + * `State` * [Immutability](#immutability) * [Sealed](#sealed) * [Frozen](#frozen) @@ -45,8 +58,28 @@ npm install @typescript-package/state ```typescript import { + Ability, + + // Array. + ArrayState, + BooleanArrayState, + NamedArrayState, + NamedBooleanArrayState, + + // Boolean. + Boolean, BooleanState, + + // Enum. + Enum, EnumState, + + // Object. + BooleanActionObjectState, + BooleanObjectState, + NamedObjectState, + ObjectState, + NullState, NumberState, ImmutableState, @@ -54,6 +87,211 @@ import { } from '@typescript-package/state'; ``` +### `Ability` + +```typescript +import { Ability } from '@typescript-package/state'; + +// Extend the Ability class for a specific type +export class Connection extends Ability {} + +// Initialize the connection. +const connection = new Connection(); + +// Disables the connection. +connection.disable(); +connection.isDisabled(); + +// Enables the connection. +connection.enable(); +connection.isEnabled(); +``` + +### `ArrayState` + +```typescript +import { ArrayState } from '@typescript-package/state'; + +// Extend the ArrayState class for a specific type +export class Numbers extends ArrayState { + // Additional custom methods specific to Numbers can be added if needed +} + +// Initialize `Numbers`. +const numbers = new Numbers([27, 28, 29]); + +// Append a number to the array state +numbers.append(30); +console.log(numbers.state); // Output: [27, 28, 29, 30] + +// Insert a number at a specific index +numbers.insert(2, 99); +console.log(numbers.state); // Output: [27, 28, 99, 29, 30] + +// Remove a number by index +numbers.remove(1); +console.log(numbers.state); // Output: [27, 99, 29, 30] + +// Pick specific indexes +const picked = numbers.pick(0, 2); +console.log(picked); // Output: [27, 29] + +// Swap two elements +numbers.swap(1, 3); +console.log(numbers.state); // Output: [27, 30, 29, 99] + +// Reset the state to its initial state +numbers.reset(); +console.log(numbers.state); // Output: [27, 28, 29] +``` + +A simple queue implementation. + +```typescript +import { ArrayState } from '@typescript-package/state'; + +// Extend the ArrayState class for a specific type +export class Queue extends ArrayState { + /** + * Adds an item to the end of the queue (enqueue operation). + * @param {number} value - The number to add to the queue. + * @returns {this} + */ + public enqueue(value: number): this { + return this.append(value); + } + + /** + * Removes and returns the item at the front of the queue (dequeue operation). + * @returns {number | undefined} - The dequeued number or undefined if the queue is empty. + */ + public dequeue(): number | undefined { + const front = this.first(); + this.remove(0); // Remove the first element + return front; + } + + /** + * Returns the item at the front of the queue without removing it. + * @returns {number | undefined} - The number at the front of the queue. + */ + public peek(): number | undefined { + return this.first(); + } + + /** + * Checks if the queue is empty. + * @returns {boolean} - True if the queue is empty, false otherwise. + */ + public isEmpty(): boolean { + return this.length === 0; + } +} + +// Initialize `Queue`. +const queue = new Queue([27, 28, 29]); + +// Enqueue a number +queue.enqueue(30); +console.log(queue.state); // Output: [27, 28, 29, 30] + +// Dequeue a number +const dequeued = queue.dequeue(); +console.log(dequeued); // Output: 27 +console.log(queue.state); // Output: [28, 29, 30] + +// Peek at the front of the queue +const front = queue.peek(); +console.log(front); // Output: 28 + +// Check if the queue is empty +console.log(queue.isEmpty()); // Output: false + +// Dequeue all items +queue.dequeue(); +queue.dequeue(); +queue.dequeue(); +console.log(queue.isEmpty()); // Output: true +``` + +### `BooleanArrayState` + +```typescript +import { BooleanArrayState as AbstractBooleanArrayState } from '@typescript-package/state'; + +// Extend the AbstractBooleanArrayState class for a specific type +export class BooleanArrayState extends AbstractBooleanArrayState { + // Additional custom methods specific to BooleanArrayState can be added if needed +} + +let booleanArrayState = new BooleanArrayState(false, false, true, true); + +// Sets all values to `false`. +booleanArrayState.false(); +console.log(booleanArrayState.state); // Output: [false, false, false, false] + +// Toggles all values to `true`. +booleanArrayState.toggle(); +console.log(booleanArrayState.state); // Output: [true, true, true, true] + +// Toggles all values to `false`. +booleanArrayState.toggle(); +console.log(booleanArrayState.state); // Output: [false, false, false, false] + +// Sets all values to `true`. +booleanArrayState.true(); +console.log(booleanArrayState.state); // Output: [true, true, true, true] +``` + +### `NamedArrayState` + +```typescript +import { NamedArrayState } from '@typescript-package/state'; + +// Extend the NamedArrayState class for a specific type +export class AppConfiguration extends NamedArrayState<'theme' | 'language' | 'notifications', string | boolean> { + constructor() { + super( + ['theme', 'language', 'notifications'], // Names of the configuration settings + ['dark', 'en', true] // Default values + ); + } + + /** + * Updates the value of a specific configuration by name. + * @param {string} name - The name of the configuration to update. + * @param {string | boolean} value - The new value to set. + */ + public updateConfiguration(name: 'theme' | 'language' | 'notifications', value: string | boolean) { + this.update(this.indexOf(name), value); + } +} + +// Initialize. +const config = new AppConfiguration(); + +// View the current state as an object +console.log(config.toObject()); // Output: { theme: 'dark', language: 'en', notifications: true } + +// Get the value of a specific setting +console.log(config.get('theme')); // Output: 'dark' + +// Update a specific configuration setting +config.updateConfiguration('theme', 'light'); +console.log(config.get('theme')); // Output: 'light' + +// Selecting multiple configuration options +const selectedValues = config.select('theme', 'language'); +console.log(selectedValues); // Output: ['light', 'en'] + +// Retrieve state with names as tuples +console.log(config.stateWithNames); // Output: [['theme', 'light'], ['language', 'en'], ['notifications', true]] + +// Reset the configuration state to its initial value +config.reset(); +console.log(config.toObject()); // Output: { theme: 'dark', language: 'en', notifications: true } +``` + ### `BooleanState` ```typescript @@ -101,7 +339,6 @@ export class ActiveEnum extends EnumState { constructor(state: Active) { super(state, Active); } - } // Initialize. @@ -111,6 +348,37 @@ const activeEnum = new ActiveEnum(Active.No); activeEnum.is(Active.No) ``` +### `BooleanActionObjectState` + +```typescript +import { BooleanActionObjectState } from '@typescript-package/state'; + +// Extend the BooleanActionObjectState class for a specific type +export class Connection extends BooleanActionObjectState { + /** + * Checks whether connection is connected. + * @returns {boolean} + */ + public isConnected() { + return this.is("connected" as any, true); + } +} +// Initialize the `Connection` with state `connected` and actions `connect` and `disconnect`. +const connection = new Connection(['connected'], ['connect', 'disconnect']); + +// Dispatches the `connect` action. +connection.dispatch('connect'); +console.log(connection.isConnected()); // Output: true + +// Dispatches the `disconnect` action. +connection.dispatch('disconnect'); +console.log(connection.isConnected()); // Output: false + +// Dispatches the `connect` action. +connection.dispatch('connect'); +console.log(connection.isConnected()); // Output: true +``` + ### `NullState` ```typescript diff --git a/package.json b/package.json index b3e452a..efea00e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-package/state", - "version": "1.0.0", + "version": "2.0.0", "author": "wwwdev.io ", "description": "State management for TypeScript.", "license": "MIT", diff --git a/src/lib/ability/ability.abstract.ts b/src/lib/ability/ability.abstract.ts new file mode 100644 index 0000000..1f365b9 --- /dev/null +++ b/src/lib/ability/ability.abstract.ts @@ -0,0 +1,84 @@ +// Class. +import { Boolean as Disabled, Boolean as Enabled } from "../boolean"; +/** + * @description Ability as a `boolean` state (enabled/disabled) + * @export + * @abstract + * @class Ability + */ +export abstract class Ability { + /** + * @description Default state for the `Disabled` instance. + * @public + * @static + * @type {boolean} + */ + public static disabled = false; + + /** + * @description Default state for the `Enabled` instance. + * @public + * @static + * @type {boolean} + */ + public static enabled = true; + + /** + * @description Privately stored disabled state. + * @type {Disabled} + */ + #disabled = new Disabled(Ability.disabled); + + /** + * @description Privately stored enabled state. + * @type {Enabled} + */ + #enabled = new Enabled(Ability.enabled); + + /** + * Creates an instance of `Ability`. + * @constructor + * @param {?boolean} [enabled] + */ + constructor(enabled?: boolean) { + if (typeof enabled == 'boolean') { + enabled === true ? this.enable() : this.disable(); + } + } + + /** + * @description Disables the ability. + * @returns {this} + */ + public disable(): this { + this.#enabled.false(); + this.#disabled.true(); + return this; + } + + /** + * @description Enables the ability. + * @returns {this} + */ + public enable(): this { + this.#enabled.true(); + this.#disabled.false(); + return this; + } + + /** + * @description Gets the current disabled status of the ability. + * @returns {boolean} + */ + public isDisabled(): boolean { + return this.#disabled.is(); + } + + /** + * @description Gets the current status of the ability. + * @returns {boolean} + */ + public isEnabled(): boolean { + return this.#enabled.is(); + } +} diff --git a/src/lib/ability/index.ts b/src/lib/ability/index.ts new file mode 100644 index 0000000..30f514d --- /dev/null +++ b/src/lib/ability/index.ts @@ -0,0 +1 @@ +export { Ability } from './ability.abstract'; diff --git a/src/lib/array/array-state.abstract.ts b/src/lib/array/array-state.abstract.ts new file mode 100644 index 0000000..273e7d4 --- /dev/null +++ b/src/lib/array/array-state.abstract.ts @@ -0,0 +1,249 @@ +// Abstract. +import { State } from "../state.abstract"; +/** + * @description The `abstract class` handles the state of `Array`. + * @export + * @abstract + * @class ArrayState + * @template Type + * @extends {State>} + */ +export abstract class ArrayState extends State> { + /** + * @description Returns the `array` state length. + * @public + * @returns {number} + */ + public get length() { + return super.state.length; + } + + /** + * @inheritdoc Return the frozen readonly state. + * @public + * @readonly + * @type {Readonly} + */ + public override get state(): ReadonlyArray { + return Object.freeze(super.state); + } + + /** + * @description The initial state that used in resetting the state. + * @type {Type[]} + */ + #initialState: Type[]; + + /** + * Creates an instance of child class. + * @constructor + * @param {Type} state The initial state of `Type`. + */ + constructor(state: Type[]) { + super(state); + this.#initialState = state; + } + + /** + * @description Appends the `value` at the end of an `array` state. + * @public + * @param {Type} value The `value` of `Type` to append. + * @returns {this} + */ + public append(value: Type): this { + super.set([...super.state, value]); + return this; + } + + /** + * @description Returns the value of `Type` from the given `index`. + * @public + * @param {number} index + * @returns {Type} + */ + public at(index: number): Type | undefined { + return this.state.at(index); + } + + /** + * @description Clears the `array` state to empty. + * @public + * @returns {this} + */ + public clear(): this { + super.set([]); + return this; + } + + /** + * @description Finds the values in `array` state. + * @public + * @param {(value: Type) => boolean} predicate + * @returns {(Type | undefined)} + */ + public find(predicate: (value: Type) => boolean): Type | undefined { + return this.state.find(predicate); + } + + /** + * @description Filters the `array` state. + * @public + * @param {(value: Type) => boolean} predicate + * @returns {Type[]} + */ + public filter(predicate: (value: Type) => boolean): Type[] { + return this.state.filter(predicate); + } + + /** + * @description Returns the first element of `array` state. + * @public + * @returns {Type} + */ + public first(): Type { + return this.state[0]; + } + + /** + * @description Inserts the `value` at the specified `index` into the `array` state. + * @public + * @param {number} index The index of `number` type to insert the `value`. + * @param {Type} value The `value` to insert at the specified `index`. + * @returns {this} + */ + public insert(index: number, value: Type): this { + super.set([...[...super.state].slice(0, index), value, ...[...super.state].slice(index)]); + return this; + } + + /** + * @description Returns the last element of `array` state. + * @public + * @returns {Type} + */ + public last(): Type { + return this.state[this.length - 1]; + } + + /** + * @description Merges `values` into the `array` state starting at position. + * @public + * @param {Type[]} values Array of `Type` to merge with `array` state. + * @param {number} [startAt=this.length] The position to start merging `values` with an `array` state. + * @returns {this} + */ + public merge(values: Type[], startAt: number = this.length): this { + if (startAt < 0 || startAt > this.length) { + throw new Error("startAt index is out of bounds"); + } + super.set([ + ...super.state.slice(0, startAt), + ...values, + ...super.state.slice(startAt), + ]); + return this; + } + + /** + * @description Adds the `values` at the beginning of `array` state. + * @public + * @param {...Type[]} values The `values` to add at the beginning. + * @returns {this} + */ + public prepend(...values: Type[]): this { + super.set([...values, ...this.state]); + return this; + } + + /** + * @description Picks the specified indexes. + * @public + * @param {...number[]} indexes Indexes to pick from the `array` state. + * @returns {Type[]} + */ + public pick(...indexes: number[]): Type[] { + return [...super.state].filter((element, index) => indexes.includes(index)); + } + + /** + * @description Removes the values from the `array` state of the specified `indexes`. + * @public + * @param {...number[]} indexes Indexes to remove from the `array` state. + * @returns {this} + */ + public remove(...indexes: number[]): this { + super.set([...super.state].filter((value, index) => !indexes.includes(index))); + return this; + } + + /** + * @description Removes the values of specified `start` and `end` indexes from `array` state. + * @public + * @param {number} startAt The start `index` to begin removing. + * @param {number} endAt The end `index` to end removing. + * @returns {this} + */ + public removeRange(startAt: number, endAt: number): this { + super.set([...super.state].filter((value, index) => !(Array.from({ length: endAt + 1 - startAt }, (value, i) => startAt + i)).includes(index))); + return this; + } + + /** + * @description Resets the state to the initial set in the `constructor`. + * @public + * @returns {this} + */ + public reset(): this { + super.set(this.#initialState); + return this; + } + + /** + * @inheritdoc + * @public + * @param {ReadonlyArray} state + * @returns {this} + */ + public override set(state: ReadonlyArray): this { + super.set(state); + return this; + } + + /** + * @description + * @public + * @param {number} index + * @param {number} withIndex + * @returns {this} + */ + public swap(index: number, withIndex: number): this { + const state = [...this.state]; + [state[index], state[withIndex]] = [state[withIndex], state[index]]; + super.set(state); + return this; + } + + /** + * @description Updates the `value` at the `index` in the `array` state. + * @public + * @param {number} index The index of `array` state to be updated with the `value`. + * @param {Type} value The `value` of `Type` to update at the specified `index`. + * @returns {this} + */ + public update(index: number, value: Type): this { + super.set(this.state.map((v, i) => (i === index ? value : v))); + return this; + } + + /** + * @description Protected method to update the state of the specified `indexes` by using callback function. + * @protected + * @param {number[]} indexes Indexes to update the state. + * @param {(value: Type, index: number) => Type} callbackFn The callback function to update the state at index. + * @returns {Type[]} + */ + protected updateIndexes(indexes: number[], callbackFn: (value: Type, index: number) => Type): Type[] { + indexes = (indexes.length > 0 ? indexes : Array.from({ length: super.state.length }, (_, i) => 0 + i)); + return super.state.map((value, index) => indexes.includes(index) ? callbackFn(super.state[index], index) : value); + } +} diff --git a/src/lib/array/boolean-array-state.abstract.ts b/src/lib/array/boolean-array-state.abstract.ts new file mode 100644 index 0000000..c57470d --- /dev/null +++ b/src/lib/array/boolean-array-state.abstract.ts @@ -0,0 +1,52 @@ +// Abstract. +import { ArrayState } from "./array-state.abstract"; +/** + * @description Manages the `array` of `boolean` type state. + * @export + * @abstract + * @class BooleanArrayState + * @extends {ArrayState} + */ +export abstract class BooleanArrayState extends ArrayState { + /** + * Creates an instance of child class. + * @constructor + * @param {...boolean[]} states + */ + constructor(...states: boolean[]) { + super(states); + } + + /** + * @description Sets the specified `indexes` to `false` in the `array` state of `boolean` type. + * @public + * @param {...number[]} indexes Indexes to set to `false`. + * @returns {this} + */ + public false(...indexes: number[]): this { + super.set(this.updateIndexes(indexes, () => false)); + return this; + } + + /** + * @description Toggles the specified `indexes` between `true` and `false` in the `array` state of `boolean` type. + * @public + * @param {...number[]} indexes Indexes to toggle. + * @returns {this} + */ + public toggle(...indexes: number[]): this { + super.set(this.updateIndexes(indexes, value => !value)); + return this; + } + + /** + * @description Sets the specified `indexes` to `true` in the `array` state of `boolean` type. + * @public + * @param {...number[]} indexes Indexes to set to `true`. + * @returns {this} + */ + public true(...indexes: number[]): this { + super.set(this.updateIndexes(indexes, () => true)); + return this; + } +} diff --git a/src/lib/array/index.ts b/src/lib/array/index.ts new file mode 100644 index 0000000..2f70f10 --- /dev/null +++ b/src/lib/array/index.ts @@ -0,0 +1,4 @@ +export { ArrayState } from './array-state.abstract'; +export { BooleanArrayState } from './boolean-array-state.abstract'; +export { NamedArrayState } from './named-array-state.abstract'; +export { NamedBooleanArrayState } from './named-boolean-array-state.abstract'; \ No newline at end of file diff --git a/src/lib/array/named-array-state.abstract.ts b/src/lib/array/named-array-state.abstract.ts new file mode 100644 index 0000000..67946ff --- /dev/null +++ b/src/lib/array/named-array-state.abstract.ts @@ -0,0 +1,103 @@ +// Abstract. +import { ArrayState } from "./array-state.abstract"; +/** + * @description Manages the `array` state of `Type` type with the specified names of generic type variable `Names`. + * @export + * @abstract + * @class NamedArrayState + * @template {string} Names + * @template Type + * @extends {ArrayState} + */ +export abstract class NamedArrayState< + Names extends string, + Type +> extends ArrayState { + /** + * @description Returns tuple an `array` where the first element contains an `array` of names and the second `array` contains their corresponding values. + * @public + * @returns {[Names[], Type[]]} + */ + public get stateAsTuple(): [Names[], Type[]] { + return [this.#names, [...super.state]]; + } + + /** + * @description Returns an `array` state of tuple pairs name-type. + * @public + * @readonly + * @type {[Names, Type][]} + */ + public get stateWithNames(): [Names, Type][] { + return this.#names.map((name, index) => [name, super.state[index]]); + } + + /** + * @description A private property, an `array` of generic type variable `Names`, used to associate `Type` values. + * @type {Names[]} + */ + #names: Names[]; + + /** + * Creates an instance of child `class`. + * @constructor + * @param {Names[]} names Arbitrary parameter `names` of generic type variable `Names` to associate `Type` values. + * @param {Type[]} [values=[]] The `values` to set to the respective `names`. + */ + constructor(names: Names[], values: Type[] = []) { + super(values); + this.#names = names; + } + + /** + * @description Returns the value from element of the specified `name`. + * @public + * @param {Names} name The name of generic type variable `Names` to get the value. + * @returns {Type} + */ + public get(name: Names): Type { + return this.state[this.#names.indexOf(name)]; + } + + /** + * @description Returns index of the specified `name`. + * @public + * @param {Names} name The name of generic type variable `Names` to get its index. + * @returns {number} + */ + public indexOf(name: Names): number { + return this.#names.indexOf(name); + } + + /** + * @description Selects values from the specified `names`. + * @public + * @param {...Names[]} names Arbitrary parameter of names to select the values from `array` state. + * @returns {Type[]} + */ + public select(...names: Names[]): Type[] { + return super.pick(...this.indexesOf(...names)); + } + + /** + * @description Returns the `object` of names with their `Type` values. + * @public + * @returns {{ [Name in Names]: Type }} + */ + public toObject() { + return this.#names.reduce( + (acc, name) => (acc[name] = super.state[this.indexOf(name)], acc), + {} as { [Name in Names]: Type } + ); + } + + /** + * @description Returns the indexes of the specified `names`. + * @protected + * @param {...Names[]} names Arbitrary parameter of generic type variable `Names` to get their indexes. + * @returns {number[]} + */ + protected indexesOf(...names: Names[]): number[] { + return names.map(name => this.#names.indexOf(name)); + } +} diff --git a/src/lib/array/named-boolean-array-state.abstract.ts b/src/lib/array/named-boolean-array-state.abstract.ts new file mode 100644 index 0000000..2b32054 --- /dev/null +++ b/src/lib/array/named-boolean-array-state.abstract.ts @@ -0,0 +1,57 @@ +// Abstract. +import { NamedArrayState } from "./named-array-state.abstract"; +/** + * @description Manages the `array` state of `boolean` type with the specified names of generic type variable `Names`. + * @export + * @abstract + * @class NamedBooleanArrayState + * @typedef {NamedBooleanArrayState} + * @template {string} Names + * @extends {NamedArrayState} + */ +export abstract class NamedBooleanArrayState< + Names extends string +> extends NamedArrayState { + /** + * Creates an instance of child `class`. + * @constructor + * @param {...Names[]} names Arbitrary parameter `names` of generic type variable `Names` to associate `boolean` values. + * @param {boolean[]} [values=[]] The `values` of `boolean` type to set to the respective `names`. + */ + constructor(names: Names[], values: boolean[] = []) { + super(names, names.map((name, index) => values.length > -1 && index <= values.length - 1 ? values[index] : !!name)); + } + + /** + * @description Sets to `false` elements of the specified `names`. + * @public + * @param {...Names[]} names Arbitrary parameter of generic type variable `Names` to set the elements to `false`. + * @returns {this} + */ + public false(...names: Names[]): this { + super.set(this.updateIndexes(this.indexesOf(...names), () => false)); + return this; + } + + /** + * @description Toggles between `false` and `true` specified `names`. + * @public + * @param {...Names[]} names Arbitrary parameter of generic type variable `Names` to toggle their values. + * @returns {this} + */ + public toggle(...names: Names[]): this { + super.set(super.updateIndexes(this.indexesOf(...names), value => !value)); + return this; + } + + /** + * @description Sets the `true` under the specified `names` in the `array` state. + * @public + * @param {...Names[]} names Arbitrary parameter of generic type variable `Names` to set the elements to `true`. + * @returns {this} + */ + public true(...names: Names[]): this { + super.set(super.updateIndexes(this.indexesOf(...names), () => true)); + return this; + } +} diff --git a/src/lib/boolean-state.abstract.ts b/src/lib/boolean-state.abstract.ts deleted file mode 100644 index c729f6f..0000000 --- a/src/lib/boolean-state.abstract.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Abstract. -import { State } from "./state.abstract"; -/** - * @description Handles the `boolean` type state. - * @export - * @abstract - * @class BooleanState - * @typedef {BooleanState} - * @extends {State} - */ -export abstract class BooleanState extends State { - /** - * Creates an instance of parent class. - * @constructor - * @param {?boolean} [state] Sets initially boolean state. - */ - constructor(state: boolean = true) { - super(state); - } - - /** - * @description Sets boolean state to `false`. - * @protected - * @returns {this} - */ - protected false(): this { - super.set(false); - return this; - } - - /** - * @description Checks whether state is `false`. - * @protected - * @returns {boolean} - */ - protected isFalse() { - return super.state === false; - } - - /** - * @description Checks whether state is `true`. - * @protected - * @returns {boolean} - */ - protected isTrue() { - return super.state === true; - } - - /** - * @description Toggle boolean state. - * @protected - * @returns {this} - */ - protected toggle(): this { - super.set(!super.state) - return this; - } - - /** - * @description Sets boolean state to `true`. - * @protected - * @returns {this} - */ - protected true(): this { - super.set(true); - return this; - } -} \ No newline at end of file diff --git a/src/lib/boolean/boolean-state.abstract.ts b/src/lib/boolean/boolean-state.abstract.ts new file mode 100644 index 0000000..20b2e24 --- /dev/null +++ b/src/lib/boolean/boolean-state.abstract.ts @@ -0,0 +1,159 @@ +// Abstract. +import { State } from "../state.abstract"; +/** + * @description Handles the `boolean` type state. + * @export + * @abstract + * @class BooleanState + * @extends {State} + */ +export abstract class BooleanState extends State { + /** + * @description Default state for the instance. + * @public + * @static + * @type {boolean} + */ + public static state = true; + + /** + * @description Privately stored callback function performed on state set to `false`. + * @type {*} + */ + #onFalseCallback; + + /** + * @description Privately stored callback function performed on state set to `true`. + * @type {*} + */ + #onTrueCallback; + + /** + * Creates an instance of child class. + * @constructor + * @param {boolean} [state=BooleanState.state] Sets initially `boolean` state. + * @param {?() => any} [onTrueCallback] Optional callback function performed on each state change to `true`. + * @param {?() => any} [onFalseCallback] Optional callback function performed on each state change to `false`. + */ + constructor( + state: boolean = BooleanState.state, + onTrueCallback?: () => any, + onFalseCallback?: () => any, + ) { + super(state); + this.#onTrueCallback = onTrueCallback; + this.#onFalseCallback = onFalseCallback; + state ? onTrueCallback?.() : onFalseCallback?.(); + } + + /** + * @description Sets boolean state to `false`. + * @public + * @param {*} [callback=this.#onFalseCallback] Performs the callback function if provided. + * @returns {this} + */ + public false(callback = this.#onFalseCallback): this { + super.set(false); + typeof callback === 'function' && callback(); + return this; + } + + /** + * @description Checks the active state and perform callback. + * @public + * @param {?boolean} [expected] Expected state of `boolean` type. + * @param {?(result: boolean) => void} [callback] The callback function with a expected `result`. + * @returns {boolean} + */ + public is(expected?: boolean, callback?: (result: boolean) => void): boolean { + const result = typeof expected === 'boolean' ? this.state === expected : this.state; + callback?.(result); + return result; + } + + /** + * @description Checks whether the state is `false`. + * @public + * @returns {boolean} + */ + public isFalse() { + return super.state === false; + } + + /** + * @description Checks whether the state is `true`. + * @public + * @returns {boolean} + */ + public isTrue() { + return super.state === true + } + + /** + * @description Performs the `callback` function on state `false`. + * @public + * @param {() => void} callback The callback function to perform on state `false`. + * @returns {this} + */ + public onFalse(callback: () => void): this { + this.isFalse() && callback(); + return this; + } + + /** + * @description Performs the `callback` function on state `true`. + * @public + * @param {() => void} callback The callback function to perform on state `true`. + * @returns {this} + */ + public onTrue(callback: () => void): this { + this.isTrue() && callback(); + return this; + } + + /** + * @description Sets the callback function performed on state set to `false`. + * @public + * @param {() => void} onFalseCallback The callback function performed on state `false`. + * @returns {this} + */ + public setOnFalse(onFalseCallback: () => void): this { + typeof onFalseCallback === 'function' && (this.#onFalseCallback = onFalseCallback); + return this; + } + + /** + * @description Sets the callback function performed on state set to `true`. + * @public + * @param {() => void} onTrueCallback The callback function performed on state `true`. + * @returns {this} + */ + public setOnTrue(onTrueCallback: () => void): this { + typeof onTrueCallback === 'function' && (this.#onTrueCallback = onTrueCallback); + return this; + } + + /** + * @description Toggle boolean state. + * @public + * @returns {this} + */ + public toggle(): this { + super.set(!super.state); + this.#onFalseCallback && this.onFalse(this.#onFalseCallback); + this.#onTrueCallback && this.onTrue(this.#onTrueCallback); + return this; + } + + /** + * @description Sets boolean state to `true`. + * @public + * @param {?() => void} [callback] Performs the callback function if provided. + * @returns {this} + */ + public true(callback = this.#onTrueCallback): this { + super.set(true); + typeof callback === 'function' && callback(); + return this; + } +} \ No newline at end of file diff --git a/src/lib/boolean/boolean.class.ts b/src/lib/boolean/boolean.class.ts new file mode 100644 index 0000000..7be37ff --- /dev/null +++ b/src/lib/boolean/boolean.class.ts @@ -0,0 +1,9 @@ +// Abstract. +import { BooleanState } from "./boolean-state.abstract"; +/** + * @description Handles the `boolean` type state. + * @export + * @class Boolean + * @extends {BooleanState} + */ +export class Boolean extends BooleanState {} diff --git a/src/lib/boolean/index.ts b/src/lib/boolean/index.ts new file mode 100644 index 0000000..c56e339 --- /dev/null +++ b/src/lib/boolean/index.ts @@ -0,0 +1,2 @@ +export { BooleanState } from './boolean-state.abstract'; +export { Boolean } from './boolean.class'; \ No newline at end of file diff --git a/src/lib/enum-state.abstract.ts b/src/lib/enum/enum-state.abstract.ts similarity index 52% rename from src/lib/enum-state.abstract.ts rename to src/lib/enum/enum-state.abstract.ts index 7dda06f..a3cdf60 100644 --- a/src/lib/enum-state.abstract.ts +++ b/src/lib/enum/enum-state.abstract.ts @@ -1,36 +1,35 @@ // Abstract. -import { State } from "./state.abstract"; +import { State } from "../state.abstract"; /** * @description Handles the `enum` type state. * @export * @abstract * @class EnumState - * @typedef {EnumState} * @template {object} EnumObject - * @template {EnumObject[keyof EnumObject]} T - * @extends {State} + * @template {EnumObject[keyof EnumObject]} Type + * @extends {State} */ export abstract class EnumState< EnumObject extends object, - T extends EnumObject[keyof EnumObject] -> extends State { + Type extends EnumObject[keyof EnumObject] +> extends State { /** - * Creates an instance of parent class. + * Creates an instance of child class. * @constructor - * @param {T} state + * @param {Type} state * @param {EnumObject} enumObject */ - constructor(state: T, enumObject: EnumObject) { + constructor(state: Type, enumObject: EnumObject) { super(state); } - + /** * @description Checks whether the state is of the specific enum. * @public - * @param {T} state The specific enum to check whether state is. + * @param {Type} state The specific enum to check whether state is. * @returns {boolean} */ - public is(state: T) { + public is(state: Type) { return super.state === state; } } diff --git a/src/lib/enum/enum.class.ts b/src/lib/enum/enum.class.ts new file mode 100644 index 0000000..05e11ea --- /dev/null +++ b/src/lib/enum/enum.class.ts @@ -0,0 +1,15 @@ +// Abstract. +import { EnumState } from "./enum-state.abstract"; +/** + * @description + * @export + * @class Enum + * @typedef {Enum} + * @template {object} EnumObject + * @template {EnumObject[keyof EnumObject]} Type + * @extends {EnumState} + */ +export class Enum< + EnumObject extends object, + Type extends EnumObject[keyof EnumObject] +> extends EnumState {} diff --git a/src/lib/enum/index.ts b/src/lib/enum/index.ts new file mode 100644 index 0000000..f143a8d --- /dev/null +++ b/src/lib/enum/index.ts @@ -0,0 +1,2 @@ +export { EnumState } from './enum-state.abstract'; +export { Enum } from './enum.class'; \ No newline at end of file diff --git a/src/lib/immutable-state.abstract.ts b/src/lib/immutable-state.abstract.ts index 6a3f63c..2adfb4e 100644 --- a/src/lib/immutable-state.abstract.ts +++ b/src/lib/immutable-state.abstract.ts @@ -10,7 +10,7 @@ export abstract class ImmutableState { * @description Privately stored locked state as 'locked' if locked, otherwise `undefined`. * @type {?'locked'} */ - #state?: 'locked'; + #locked?: 'locked'; /** * @description "Prevents the modification of existing property attributes and values, and prevents the addition of new properties." @@ -40,7 +40,7 @@ export abstract class ImmutableState { * @returns {"locked"} */ public isLocked() { - return this.#state === 'locked'; + return this.#locked === 'locked'; } /** @@ -71,7 +71,7 @@ export abstract class ImmutableState { if (!this.isFrozen()) { this.freeze(); } - this.#state = 'locked'; + this.#locked = 'locked'; return this; } diff --git a/src/lib/index.ts b/src/lib/index.ts index 1048a39..f0a8ce5 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,5 +1,34 @@ -export { BooleanState } from './boolean-state.abstract'; -export { EnumState } from './enum-state.abstract'; +// Ability. +export { Ability } from './ability'; + +// Array state. +export { + ArrayState, + BooleanArrayState, + NamedArrayState, + NamedBooleanArrayState +} from './array'; + +// Boolean state. +export { + Boolean, + BooleanState +} from './boolean'; + +// Enum state. +export { + Enum, + EnumState +} from './enum'; + +// Object state. +export { + BooleanActionObjectState, + BooleanObjectState, + NamedObjectState, + ObjectState +} from './object'; + export { ImmutableState } from './immutable-state.abstract'; export { NullState } from './null-state.abstract'; export { NumberState } from './number-state.abstract'; diff --git a/src/lib/null-state.abstract.ts b/src/lib/null-state.abstract.ts index 87277d3..1969444 100644 --- a/src/lib/null-state.abstract.ts +++ b/src/lib/null-state.abstract.ts @@ -10,7 +10,7 @@ import { State } from "./state.abstract"; */ export abstract class NullState extends State { /** - * Creates an instance of parent class. + * Creates an instance of child class. * @constructor * @param {?null} [state] */ diff --git a/src/lib/number-state.abstract.ts b/src/lib/number-state.abstract.ts index c313474..aab4613 100644 --- a/src/lib/number-state.abstract.ts +++ b/src/lib/number-state.abstract.ts @@ -5,7 +5,6 @@ import { State } from "./state.abstract"; * @export * @abstract * @class NumberState - * @typedef {NumberState} * @extends {State} */ export abstract class NumberState extends State { @@ -52,7 +51,7 @@ export abstract class NumberState extends State { #resetValue; /** - * Creates an instance of `NumberState`. + * Creates an instance of child class. * @constructor * @param {?number} [state] Sets the initial, and reset state value(if not set). * @param {number} [increment=this.#incrementValue] Sets incremental size. diff --git a/src/lib/object/boolean-action-object-state.abstract.ts b/src/lib/object/boolean-action-object-state.abstract.ts new file mode 100644 index 0000000..7cea8a6 --- /dev/null +++ b/src/lib/object/boolean-action-object-state.abstract.ts @@ -0,0 +1,56 @@ +// Class. +import { BooleanObjectState } from "./boolean-object-state.abstract"; +/** + * @description + * @export + * @abstract + * @class BooleanActionObjectState + * @template {string} [Names=string] + * @template {string} [Actions=string] + * @extends {BooleanObjectState} + */ +export abstract class BooleanActionObjectState< + Names extends PropertyKey = PropertyKey, + Actions extends string = string, +> extends BooleanObjectState { + /** + * @description + * @type {*} + */ + #actions = new Set(); + + /** + * Creates an instance of child `class`. + * @constructor + * @param {Names[]} names + * @param {?Actions[]} [actions] + */ + constructor( + names: Names[], + actions?: Actions[], + ) { + super(names); + actions?.forEach(action => this.#actions.add(action)); + this.#actions.size > 0 && this.true(super.names[0]).false(...super.names.slice(1)); + } + + /** + * @description Dispatches the action where first action sets the first state to `true`. + * @public + * @template {Actions} Action + * @param {Action} action The action name to dispatch. + * @returns {this} + */ + public dispatch(action: Action): this { + const [firstValue] = [...this.#actions]; + const restNames = super.names.slice(1); + if (firstValue === action) { + this.true(super.names[0]); + restNames.length > 0 && this.false(...restNames); + } else { + this.false(super.names[0]); + restNames.length > 0 && this.true(...restNames); + } + return this; + } +} diff --git a/src/lib/object/boolean-object-state.abstract.ts b/src/lib/object/boolean-object-state.abstract.ts new file mode 100644 index 0000000..23e9a78 --- /dev/null +++ b/src/lib/object/boolean-object-state.abstract.ts @@ -0,0 +1,89 @@ +// Abstract. +import { NamedObjectState } from "./named-object-state.abstract"; +/** + * @description Manages the `object` state of pairs name-boolean. + * @export + * @abstract + * @class BooleanObjectState + * @template {PropertyKey} Names + * @extends {NamedObjectState} + */ +export abstract class BooleanObjectState< + Names extends PropertyKey, +> extends NamedObjectState { + /** + * Creates an instance of child class. + * @constructor + * @param {Names[]} names + */ + constructor(names: Names[]) { + super(names); + const partial: Partial<{[Name in Names]: boolean}> = {}; + names.forEach(name => Object.assign(partial, {[name]: true})); + this.update(partial); + } + + /** + * @description Sets the state to `false in `Object` states of the specified `names`. + * @public + * @template {Names} Name + * @param {...Name[]} names The names to set the state to `false`. + * @returns {this} + */ + public false(...names: Name[]): this { + this.updateNames(names, () => false); + return this; + } + + /** + * @description + * @public + * @template {Names} Name + * @param {Name} name + * @param {?boolean} [expected] + * @returns {Object} + */ + public is(name: Name, expected?: boolean) { + return expected ? super.state[name] === expected : super.state[name]; + } + + /** + * @description Toggles the state of the specified `names`. + * @public + * @template {Names} Name + * @param {...Name[]} names The `names` to toggle the state. + * @returns {this} + */ + public toggle(...names: Name[]): this { + this.updateNames(names, name => !this.state[name]); + return this; + } + + /** + * @description Sets the `boolean` value to `true in `object` state in the specified `names`. + * @public + * @template {Names} Name + * @param {...Name[]} names Arbitrary parameter of state `names` to sets to `true`. + * @returns {this} + */ + public true(...names: Name[]): this { + this.updateNames(names, () => true); + return this; + } + + /** + * @description Updates the state of the specified `names` with the `valueFn`. + * @private + * @param {Names[]} [names=[]] + * @param {(name: Names) => boolean} callbackFn The function to update the value under the specified `name`. + * @returns {this} + */ + private updateNames(names: Names[] = [], callbackFn: (name: Names) => boolean): this { + names.length > 0 && this.update( + (names.length > 0 ? names : Object.keys(this.state) as Names[]).reduce>( + (partial, name) => (Object.assign(partial, { [name]: callbackFn(name) }), partial), {} + ) + ); + return this; + } +} diff --git a/src/lib/object/index.ts b/src/lib/object/index.ts new file mode 100644 index 0000000..4e1737b --- /dev/null +++ b/src/lib/object/index.ts @@ -0,0 +1,4 @@ +export { BooleanActionObjectState } from './boolean-action-object-state.abstract'; +export { BooleanObjectState } from './boolean-object-state.abstract'; +export { NamedObjectState } from './named-object-state.abstract'; +export { ObjectState } from './object-state.abstract'; \ No newline at end of file diff --git a/src/lib/object/named-object-state.abstract.ts b/src/lib/object/named-object-state.abstract.ts new file mode 100644 index 0000000..3a1518a --- /dev/null +++ b/src/lib/object/named-object-state.abstract.ts @@ -0,0 +1,63 @@ +// Abstract. +import { ObjectState } from "./object-state.abstract"; +/** + * @description The `abstract class` handles the `object` state with the specified `names`. + * @export + * @abstract + * @class NamedObjectState + * @template [Type=any] + * @template {PropertyKey} [Names=PropertyKey] + * @extends {ObjectState<{[Name in Names]: Type}>} + */ +export abstract class NamedObjectState< + Type = any, + Names extends PropertyKey = PropertyKey, +> extends ObjectState<{[Name in Names]: Type}> { + /** + * @description + * @public + * @readonly + * @type {Names[]} + */ + public get names(): Names[] { + return this.#names; + } + + /** + * @description + * @type {Names[]} + */ + #names: Names[]; + + /** + * Creates an instance of child class. + * @constructor + * @param {Names[]} names The names to + */ + constructor(names: Names[]) { + super({} as {[Name in Names]: Type}); + this.#names = names; + } + + /** + * @inheritdoc + * @public + * @template {Names} Name + * @param {Name} name + * @returns {Type} + */ + public override get(name: Name): Type { + return super.get(name); + } + + /** + * @inheritdoc + * @public + * @template {Names} Name + * @param {...Name[]} names + * @returns {Pick<{[Name in Names]: Type}, Name>} + */ + public override pick(...names: Name[]): Pick<{[Name in Names]: Type}, Name> { + return super.pick(...names); + } +} diff --git a/src/lib/object/object-state.abstract.ts b/src/lib/object/object-state.abstract.ts new file mode 100644 index 0000000..fa701d3 --- /dev/null +++ b/src/lib/object/object-state.abstract.ts @@ -0,0 +1,106 @@ +// Abstract. +import { State } from "../state.abstract"; +/** + * @description The `abstract class` handles the state of Javascript Object. + * @export + * @abstract + * @class ObjectState + * @template {object} Type + * @extends {State} + */ +export abstract class ObjectState extends State { + /** + * @description Returns the frozen object of the current state. + * @public + * @readonly + * @type {Type} + */ + public override get state(): Readonly { + return Object.freeze(super.state); + } + + /** + * @description The initial state that used in resetting the state. + * @type {Type} + */ + #initialState; + + /** + * Creates an instance of child class. + * @constructor + * @param {Type} state The initial state of `Type`. + */ + constructor(state: Type) { + super(state); + this.#initialState = state; + } + + /** + * @description Returns the value from the specified `key`. + * @public + * @template {keyof Type} Key + * @param {Key} key The key of generic type variable `Key` to get from the `object` state. + * @returns {Type[Key]} The return value is the value from the specified `key` of the `object` state. + */ + public get(key: Key): Type[Key] { + return this.state[key]; + } + + /** + * @description Picks the specified `keys` to the returned `object`. + * @public + * @template {keyof Type} Keys + * @param {...Keys[]} keys Arbitrary parameter `keys` of generic type variable `Keys` to pick from `object` state. + * @returns {Pick} The returned value is an `object` of `Type` with the specified `keys`. + */ + public pick(...keys: Keys[]): Pick { + return keys.reduce( + (object, key) => ( + Object.hasOwn(this.state, key) && Object.assign(object, {[key]: this.state[key]}), + object + ), + {} as Type + ); + } + + /** + * @description Resets the state to the initial state set in the `constructor`. + * @public + * @returns {this} + */ + public reset(): this { + super.set(this.#initialState); + return this; + } + + /** + * @inheritdoc + * @public + * @param {Type} state + * @returns {this} + */ + public override set(state: Type): this { + super.set(state); + return this; + } + + /** + * @description Converts the state to JSON string. + * @public + * @returns {string} + */ + public toJSON(): string { + return JSON.stringify(super.state); + } + + /** + * @description Updates the state with `partial` value. + * @public + * @param {Partial} partial The partial `object` to merge into the state. + * @returns {this} + */ + public update(partial: Partial): this { + this.set({ ...super.state, ...partial }); + return this; + } +} diff --git a/src/lib/selectable/index.ts b/src/lib/selectable/index.ts new file mode 100644 index 0000000..5acfead --- /dev/null +++ b/src/lib/selectable/index.ts @@ -0,0 +1,2 @@ +export { Selectable } from './selectable.abstract'; +export { SelectableGroup } from './selectable-group.abstract'; \ No newline at end of file diff --git a/src/lib/selectable/selectable-group.abstract.ts b/src/lib/selectable/selectable-group.abstract.ts new file mode 100644 index 0000000..8229933 --- /dev/null +++ b/src/lib/selectable/selectable-group.abstract.ts @@ -0,0 +1,62 @@ +// Abstract. +import { Ability } from "../ability/ability.abstract"; +// Class. +import { Selectable } from "./selectable.abstract"; +/** + * @description + * @export + * @abstract + * @class SelectableGroup + * @template {Selectable} T + * @extends {Ability} + */ +export abstract class SelectableGroup extends Ability { + + /** + * @description + * @protected + * @type {T[]} + */ + protected selectable: T[]; + + /** + * Creates an instance of `SelectableGroup`. + * @constructor + * @param {T[]} selectable + * @param {boolean} [enabled=true] + */ + constructor(selectable: T[], enabled = true) { + super(enabled); + this.selectable = selectable; + } + + /** + * @description + * @public + * @returns {this} + */ + public selectAll(): this { + this.selectable.forEach(selectable => selectable.select()); + return this; + } + + /** + * @description + * @public + * @returns {this} + */ + public deselectAll(): this { + this.selectable.forEach(selectable => selectable.deselect()); + return this; + } + + /** + * @description + * @public + * @returns {this} + */ + public toggleSelection(): this { + this.selectable.forEach(selectable => selectable.toggle()); + return this; + } +} diff --git a/src/lib/selectable/selectable.abstract.ts b/src/lib/selectable/selectable.abstract.ts new file mode 100644 index 0000000..ef50bf4 --- /dev/null +++ b/src/lib/selectable/selectable.abstract.ts @@ -0,0 +1,55 @@ +// Class. +import { Ability } from '../ability/ability.abstract'; +import { Boolean as Selected } from '../boolean'; +/** + * @description Manages the selected state. + * @export + * @class Selectable + * @extends {Ability} + */ +export abstract class Selectable extends Ability { + /** + * @description Privately stored selected state. + * @type {Selected} + */ + #selected = new Selected(); + + /** + * @description Sets the selectable state to unselected if the ability is enabled. + * @public + * @returns {this} + */ + public deselect(): this { + super.isEnabled() && this.#selected.false(); + return this; + } + + /** + * @description Checks whether the selectable state is selected. + * @public + * @returns {boolean} + */ + public isSelected(): boolean { + return this.#selected.is(); + } + + /** + * @description Sets the selectable state to selected if the ability is enabled. + * @public + * @returns {this} + */ + public select(): this { + super.isEnabled() && this.#selected.true(); + return this; + } + + /** + * @description Toggles the selectable state between selected and unselected if the ability is enabled. + * @public + * @returns {this} + */ + public toggle(): this { + super.isEnabled() && this.#selected.toggle(); + return this; + } +} diff --git a/src/lib/state.abstract.ts b/src/lib/state.abstract.ts index a20bd18..acdc91e 100644 --- a/src/lib/state.abstract.ts +++ b/src/lib/state.abstract.ts @@ -1,11 +1,10 @@ // Abstract. import { ImmutableState } from "./immutable-state.abstract"; /** - * @description Common class for setting the state of `Type`. + * @description Common `abstract class` for setting the state of the generic type variable `Type`. * @export * @abstract * @class State - * @typedef {State} * @template Type * @extends {ImmutableState} */ @@ -16,7 +15,7 @@ export abstract class State extends ImmutableState { * @readonly * @type {Type} */ - public get state() { + public get state(): Readonly { return this.#state; } @@ -28,22 +27,33 @@ export abstract class State extends ImmutableState { #state!: Type; /** - * Creates an instance of parent class. + * Creates an instance of child class. * @constructor - * @param {Type} [initialState] Initial state of `Type`. + * @param {Type} initialState Initial state of `Type`. */ constructor(initialState: Type) { super(); - this.set(initialState); + this.#state = initialState; + } + + /** + * @description Performs the `callback` function on `state`. + * @public + * @param {(state: Type) => void} stateCallback The callback function with a `state` to perform. + * @returns {this} + */ + public on(stateCallback: (state: Type) => void): this { + stateCallback(this.#state); + return this; } /** - * @description Sets the state if the object is not locked. + * @description Sets the state if the object is not locked and is allowed. * @public - * @param {Type} state + * @param {Type} state The state of `Type` to set. * @returns {this} */ - protected set(state: Type) { + protected set(state: Type): this { if (super.isLocked()) { throw new Error('Cannot set when object is locked.'); } diff --git a/src/public-api.ts b/src/public-api.ts index 2eba120..ec1af55 100644 --- a/src/public-api.ts +++ b/src/public-api.ts @@ -2,11 +2,26 @@ * Public API Surface of state */ export { + Ability, + + ArrayState, + BooleanArrayState, + NamedArrayState, + NamedBooleanArrayState, + + Boolean, BooleanState, + + Enum, EnumState, + + BooleanActionObjectState, + BooleanObjectState, + NamedObjectState, + ObjectState, + + ImmutableState, NullState, NumberState, - ImmutableState, State } from './lib'; - \ No newline at end of file diff --git a/src/test/ability.spec.ts b/src/test/ability.spec.ts new file mode 100644 index 0000000..2e1ed5f --- /dev/null +++ b/src/test/ability.spec.ts @@ -0,0 +1,27 @@ +import { Ability } from "../lib"; + + +export class Connection extends Ability {} + +let connection = new Connection(); + +describe(`Ability`, () => { + beforeEach(() => { + connection = new Connection(true); + }); + + + it(`enable()`, () => { + expect(connection.isDisabled()).toBeFalse(); + connection.enable(); + expect(connection.isDisabled()).toBeFalse(); + expect(connection.isEnabled()).toBeTrue(); + }); + + it(`disable()`, () => { + connection.disable() + expect(connection.isDisabled()).toBeTrue(); + expect(connection.isEnabled()).toBeFalse(); + }); + +}); diff --git a/src/test/array/array-state.spec.ts b/src/test/array/array-state.spec.ts new file mode 100644 index 0000000..25401fd --- /dev/null +++ b/src/test/array/array-state.spec.ts @@ -0,0 +1,176 @@ +import { ArrayState } from '../../lib/array'; + +export class NumberArrayState extends ArrayState {} + +let numberArrayState = new NumberArrayState([27, 37, 47]); + +describe(`ArrayState`, () => { + beforeEach(() => { + numberArrayState = new NumberArrayState([27, 37, 47]); + }); + it(`append()`, () => { + numberArrayState.append(34); + expect(numberArrayState.state[3]).toEqual(34); + }); + + it(`at()`, () => { + expect(numberArrayState.at(2)).toEqual(47); + }); + + it(`clear()`, () => { + expect(numberArrayState.clear().state).toEqual([]); + }); + + it(`find()`, () => { + expect(numberArrayState.find(value => value === 37)).toEqual(37); + }); + + it(`filter()`, () => { + expect(numberArrayState.filter(value => value === 37)).toEqual([37]); + }); + + it(`first()`, () => { + expect(numberArrayState.first()).toEqual(27); + }); + + it(`insert()`, () => { + expect(numberArrayState.insert(1, 34).state[1]).toEqual(34); + }); + + it(`last()`, () => { + expect(numberArrayState.last()).toEqual(47); + }); + + it(`merge()`, () => { + expect(numberArrayState.merge([34, 0], 1).state).toEqual([27, 34, 0, 37, 47]); + }); + + it(`prepend()`, () => { + expect(numberArrayState.prepend(34, 0).state).toEqual([34, 0, 27, 37, 47]); + }); + + it(`pick()`, () => { + expect(numberArrayState.pick(1, 2)).toEqual([37, 47]); + }); + + it(`remove()`, () => { + expect(numberArrayState.remove(1, 2).at(0)).toEqual(27); + }); + + it(`removeRange()`, () => { + numberArrayState.merge([34, 35, 36, 37, 38, 39, 40, 41, 42]); + expect(numberArrayState.removeRange(4, 7).state).toEqual([27, 37, 47, 34, 39, 40, 41, 42]); + }); + + it(`reset()`, () => { + expect(numberArrayState.reset().state).toEqual([27, 37, 47]); + }); + + it(`swap()`, () => { + expect(numberArrayState.swap(1, 2).state).toEqual([27, 47, 37]); + console.log(numberArrayState.state); + }); + + it(`update()`, () => { + expect(numberArrayState.update(1, 344).state[1]).toEqual(344); + }); +}); +console.group(`ArrayState`); +console.info(`numberArrayState.state`, numberArrayState.state); + + +// Extend the ArrayState class for a specific type +export class Numbers extends ArrayState { + // Additional custom methods specific to Numbers can be added if needed +} + +// Initialize `Numbers`. +const numbers = new Numbers([27, 28, 29]); + +// Append a number to the array state +numbers.append(30); +console.log(numbers.state); // Output: [27, 28, 29, 30] + +// Insert a number at a specific index +numbers.insert(2, 99); +console.log(numbers.state); // Output: [27, 28, 99, 29, 30] + +// Remove a number by index +numbers.remove(1); +console.log(numbers.state); // Output: [27, 99, 29, 30] + +// Pick specific indexes +const picked = numbers.pick(0, 2); +console.log(picked); // Output: [27, 29] + +// Swap two elements +numbers.swap(1, 3); +console.log(numbers.state); // Output: [27, 30, 29, 99] + +// Reset the state to its initial state +numbers.reset(); +console.log(numbers.state); // Output: [27, 28, 29] + +export class Queue extends ArrayState { + /** + * Adds an item to the end of the queue (enqueue operation). + * @param {number} value - The number to add to the queue. + * @returns {this} + */ + public enqueue(value: number): this { + return this.append(value); + } + + /** + * Removes and returns the item at the front of the queue (dequeue operation). + * @returns {number | undefined} - The dequeued number or undefined if the queue is empty. + */ + public dequeue(): number | undefined { + const front = this.first(); + this.remove(0); // Remove the first element + return front; + } + + /** + * Returns the item at the front of the queue without removing it. + * @returns {number | undefined} - The number at the front of the queue. + */ + public peek(): number | undefined { + return this.first(); + } + + /** + * Checks if the queue is empty. + * @returns {boolean} - True if the queue is empty, false otherwise. + */ + public isEmpty(): boolean { + return this.length === 0; + } +} + +// Initialize `Queue`. +const queue = new Queue([27, 28, 29]); + +// Enqueue a number +queue.enqueue(30); +console.log(`queue.state`, queue.state); // Output: [27, 28, 29, 30] + +// Dequeue a number +const dequeued = queue.dequeue(); +console.log(dequeued); // Output: 27 +console.log(queue.state); // Output: [28, 29, 30] + +// Peek at the front of the queue +const front = queue.peek(); +console.log(front); // Output: 28 + +// Check if the queue is empty +console.log(queue.isEmpty()); // Output: false + +// Dequeue all items +queue.dequeue(); +queue.dequeue(); +queue.dequeue(); +console.log(queue.isEmpty()); // Output: true + +console.groupEnd(); diff --git a/src/test/array/boolean-array-state.spec.ts b/src/test/array/boolean-array-state.spec.ts new file mode 100644 index 0000000..1acb190 --- /dev/null +++ b/src/test/array/boolean-array-state.spec.ts @@ -0,0 +1,125 @@ +import { BooleanArrayState as AbstractBooleanArrayState } from '../../lib/array'; + +export class BooleanArrayState extends AbstractBooleanArrayState {} + +let booleanArrayState = new BooleanArrayState(false, false, true, true); + +describe(`BooleanArrayState`, () => { + beforeEach(() => { + booleanArrayState = new BooleanArrayState(false, false, true, true); + }); + + it(`false()`, () => { + booleanArrayState.false(3); + expect(booleanArrayState.state[3]).toBeFalse(); + expect(booleanArrayState.state[2]).toBeTrue(); + booleanArrayState.false(); + expect(booleanArrayState.state).toEqual([false, false, false, false]); + }); + + it(`true()`, () => { + booleanArrayState.true(0); + expect(booleanArrayState.state[0]).toBeTrue(); + expect(booleanArrayState.state[1]).toBeFalse(); + booleanArrayState.true(); + expect(booleanArrayState.state).toEqual([true, true, true, true]); + }); + + it(`toggle()`, () => { + booleanArrayState.toggle(); + console.log(booleanArrayState.state); + expect(booleanArrayState.state).toEqual([true, true, false, false]); + }); + + // Defaults. + it(`append()`, () => { + booleanArrayState.append(true); + expect(booleanArrayState.state[4]).toBeTrue(); + + booleanArrayState.append(false); + expect(booleanArrayState.state[5]).toBeFalse(); + }); + + it(`at()`, () => { + expect(booleanArrayState.at(0)).toBeFalse(); + }); + + it(`clear()`, () => { + expect(booleanArrayState.clear().state).toEqual([]); + }); + + it(`find()`, () => { + expect(booleanArrayState.find(value => value === false)).toEqual(false); + }); + + it(`filter()`, () => { + expect(booleanArrayState.filter(value => value === true)).toEqual([true, true]); + }); + + it(`first()`, () => { + expect(booleanArrayState.first()).toEqual(false); + }); + + it(`insert()`, () => { + expect(booleanArrayState.insert(1, true).state[1]).toEqual(true); + expect(booleanArrayState.insert(0, false).state[0]).toEqual(false); + }); + + it(`last()`, () => { + expect(booleanArrayState.last()).toEqual(true); + }); + + it(`merge()`, () => { + expect(booleanArrayState.merge([true, false], 1).state).toEqual([false, true, false, false, true, true]); + }); + + it(`prepend()`, () => { + expect(booleanArrayState.prepend(false, true).state).toEqual([false, true, false, false, true, true]); + }); + + it(`pick()`, () => { + expect(booleanArrayState.pick(1, 2)).toEqual([false, true]); + }); + + it(`remove()`, () => { + expect(booleanArrayState.remove(1, 2).state).toEqual([false, true]); + }); + + it(`removeRange()`, () => { + expect(booleanArrayState.merge([false, true, true, true, false, false, false, false]).removeRange(3, 10).state).toEqual([false, false, true, false]); + }); + + it(`reset()`, () => { + expect(booleanArrayState.merge([false, true, true, true, false, false, false, false]).reset().state).toEqual([false, false, true, true]); + }); + + it(`swap()`, () => { + expect(booleanArrayState.swap(0, 3).state).toEqual([true, false, true, false]); + }); + + it(`update()`, () => { + expect(booleanArrayState.update(1, true).state[1]).toEqual(true); + }); +}); +console.group(`BooleanArrayState`); +console.info(`booleanArrayState.state: `, booleanArrayState.state); + +booleanArrayState = new BooleanArrayState(false, false, true, true); + +// Sets all values to `false`. +booleanArrayState.false(); +console.log(booleanArrayState.state); // Output: [false, false, false, false] + +// Toggles all values to `true`. +booleanArrayState.toggle(); +console.log(booleanArrayState.state); // Output: [true, true, true, true] + +// Toggles all values to `false`. +booleanArrayState.toggle(); +console.log(booleanArrayState.state); // Output: [false, false, false, false] + +// Sets all values to `true`. +booleanArrayState.true(); +console.log(booleanArrayState.state); // Output: [true, true, true, true] + +console.groupEnd(); diff --git a/src/test/array/named-array-state.spec.ts b/src/test/array/named-array-state.spec.ts new file mode 100644 index 0000000..d19f425 --- /dev/null +++ b/src/test/array/named-array-state.spec.ts @@ -0,0 +1,13 @@ +import { NamedArrayState as AbstractNamedArrayState } from "../../lib"; + + +export class Profile< + Names extends string, + Type +> extends AbstractNamedArrayState {} + + +let namedArrayState = new Profile(['firstname', 'surname', 'age'], ['Name', 'Valkovitz', 39]); + + +// namedArrayState. \ No newline at end of file diff --git a/src/test/boolean-action-object-state.spec.ts b/src/test/boolean-action-object-state.spec.ts new file mode 100644 index 0000000..67417c9 --- /dev/null +++ b/src/test/boolean-action-object-state.spec.ts @@ -0,0 +1,52 @@ +import { BooleanActionObjectState } from "../lib/object"; + +export class Connection extends BooleanActionObjectState { + /** + * Checks whether connection is connected. + * @returns {boolean} + */ + public isConnected() { + return this.is("connected" as any, true); + } +} + +let connection1 = new Connection(['connected'], ['connect', 'disconnect']); + +describe(`BooleanActionObjectState`, () => { + beforeEach(() => { + connection1 = new Connection(['connected'], ['connect', 'disconnect']); + }); + + it(`dispatch('connect')`, () => { + connection1.dispatch('connect'); + expect(connection1.state.connected).toBeTrue(); + expect(connection1.isConnected()).toBeTrue(); + }); + + it(`dispatch('disconnect')`, () => { + connection1.dispatch('connect').dispatch('disconnect'); + expect(connection1.state.connected).toBeFalse(); + expect(connection1.isConnected()).toBeFalse(); + }); +}); + +console.group(`BooleanActionObjectState`); + +connection1.dispatch('connect'); + +console.log(`connection1`, connection1); + + +const connection2 = new Connection( + ['connected', 'disconnected'], ['connect', 'disconnect']); + +connection2.dispatch('connect'); + +console.log(`connection2 connected`, connection2.get('connected')); + +connection2.dispatch('disconnect'); + +console.log(`connection disconnected`, connection2.get('connected')); + + +console.groupEnd(); diff --git a/src/test/boolean-array-state.spec.ts b/src/test/boolean-array-state.spec.ts new file mode 100644 index 0000000..4ab8e58 --- /dev/null +++ b/src/test/boolean-array-state.spec.ts @@ -0,0 +1,23 @@ +import { BooleanArrayState } from "../lib/array"; + +export class Options extends BooleanArrayState {} + +let options = new Options(true, true, true, false); + +describe(`BooleanArrayState`, () => { + beforeEach(() => { + options = new Options(true, true, true, false); + }); + it(`false()`, () => { + expect(options.false(0, 1).state).toEqual([false, false, true, false]); + }); + it(`true()`, () => { + expect(options.true(options.length - 1).state).toEqual([true, true, true, true]); + }); + it(`true()`, () => { + expect(options.true(0, 1)); + }); +}); +console.group(`BooleanArrayState`); +console.log(`options: `, options); +console.groupEnd(); diff --git a/src/test/boolean-object-state.spec.ts b/src/test/boolean-object-state.spec.ts new file mode 100644 index 0000000..7d50c81 --- /dev/null +++ b/src/test/boolean-object-state.spec.ts @@ -0,0 +1,20 @@ +import { BooleanObjectState } from "../lib/object/boolean-object-state.abstract"; + +export class Options extends BooleanObjectState { + constructor(names: Names[]) { + super(names); + } +} + +const options = new Options(['option1', 'option2', 'option3', 'option4']); + +options.update({ 'option1': false }); +console.log(`options.state.option1: `, options.state.option1); + +console.log(`options.state: `, options.state); +options.toggle(); +console.log(`options.state: `, options.state); + +// const booleanStates = new Options(['option1', 'option2', 'option3', 'option4']); + + diff --git a/src/test/enum-state.spec.ts b/src/test/enum-state.spec.ts index a62d8af..7c2ef36 100644 --- a/src/test/enum-state.spec.ts +++ b/src/test/enum-state.spec.ts @@ -1,4 +1,4 @@ -import { EnumState } from "../lib/enum-state.abstract"; +import { EnumState } from "../lib/enum"; enum Active { Yes, @@ -14,4 +14,6 @@ export class ActiveEnum extends EnumState { const activeEnum = new ActiveEnum(Active.No); +console.group('EnumState'); console.log(activeEnum.is(Active.No)); +console.groupEnd(); diff --git a/src/test/experimentals.spec.ts b/src/test/experimentals.spec.ts new file mode 100644 index 0000000..6814cff --- /dev/null +++ b/src/test/experimentals.spec.ts @@ -0,0 +1,64 @@ +import { Ability, Boolean as Checked, Boolean as Disabled, NamedArrayState } from "../lib"; + +export class Checkbox extends Ability { + private checked = new Checked(); + + constructor(checked: boolean, enabled: boolean = true) { + super(enabled); + + checked && this.checked.true(); + } +} + + +const checkbox = new Checkbox(false); + +console.log(`Checkbox`, checkbox.disable()); + + +// NamedArrayState + +class AppConfiguration extends NamedArrayState<'theme' | 'language' | 'notifications', string | boolean> { + constructor() { + super( + ['theme', 'language', 'notifications'], // Names of the configuration settings + ['dark', 'en', true] // Default values + ); + } + + /** + * Updates the value of a specific configuration by name. + * @param {string} name - The name of the configuration to update. + * @param {string | boolean} value - The new value to set. + */ + public updateConfiguration(name: 'theme' | 'language' | 'notifications', value: string | boolean) { + this.update(this.indexOf(name), value); + } +} + +const config = new AppConfiguration(); + +console.group(`NamedArrayState`); + +// View the current state as an object +console.log(config.toObject()); // Output: { theme: 'dark', language: 'en', notifications: true } + +// Get the value of a specific setting +console.log(config.get('theme')); // Output: 'dark' + +// Update a specific configuration setting +config.updateConfiguration('theme', 'light'); +console.log(config.get('theme')); // Output: 'light' + +// Selecting multiple configuration options +const selectedValues = config.select('theme', 'language'); +console.log(selectedValues); // Output: ['light', 'en'] + +// Retrieve state with names as tuples +console.log(config.stateWithNames); // Output: [['theme', 'light'], ['language', 'en'], ['notifications', true]] + +// Reset the configuration state to its initial values +config.reset(); +console.log(config.toObject()); // Output: { theme: 'dark', language: 'en', notifications: true } + +console.groupEnd(); diff --git a/src/test/immutability.spec.ts b/src/test/immutability.spec.ts deleted file mode 100644 index 0045411..0000000 --- a/src/test/immutability.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { BooleanState } from "../lib"; - -export class ActiveState extends BooleanState { - public override get state() { - return super.state; - } - - public override false() { - super.false(); - return this; - } - - - public activate() { - super.true(); - return this; - } - - public deactivate() { - super.false(); - return this; - } -} - -const activeState = new ActiveState(); - -activeState.seal(); - -console.log(`--Sealed`); -console.log(activeState); -console.log(activeState.false()); -console.log(activeState.activate()); - -activeState.freeze(); - -console.log(`--Frozen`); -console.log(activeState.activate()); -console.log(activeState.false()); - -activeState.lock(); - -console.log(`--Locked`); -// console.log(activeState.activate()); -// console.log(activeState.false()); - diff --git a/src/test/named-array-state.spec.ts b/src/test/named-array-state.spec.ts new file mode 100644 index 0000000..667e758 --- /dev/null +++ b/src/test/named-array-state.spec.ts @@ -0,0 +1,51 @@ +import { NamedArrayState } from '../lib/array'; + +export class SomeoneState extends NamedArrayState {} + +let someoneState = new SomeoneState(['Someone1', 'Someone2', 'Someone3'], [27, 34, 37, 47]); + +describe(`NamedArrayState`, () => { + beforeEach(() => { + someoneState = new SomeoneState(['Someone1', 'Someone2', 'Someone3'], [27, 34, 37, 47]); + }); + + it(`someoneState.state`, () => { + expect(someoneState.state).toEqual([27, 34, 37, 47]); + }); + + it(`someoneState.stateAsTuple`, () => { + expect(someoneState.stateAsTuple).toEqual([['Someone1', 'Someone2', 'Someone3'], [27, 34, 37, 47]]); + }); + + it(`someoneState.stateWithNames`, () => { + expect(someoneState.stateWithNames).toEqual([['Someone1', 27], ['Someone2', 34], ['Someone3', 37]]); + }); + + it(`someoneState.state`, () => { + expect(someoneState.state).toEqual([27, 34, 37, 47]); + }); + + it(`someoneState.get()`, () => { + expect(someoneState.get('Someone2')).toEqual(34); + }); + + it(`someoneState.indexOf()`, () => { + expect(someoneState.indexOf('Someone2')).toEqual(1); + }); + + it(`someoneState.select()`, () => { + expect(someoneState.select('Someone2', 'Someone3')).toEqual([34, 37]); + }); + + it(`someoneState.toObject()`, () => { + expect(someoneState.toObject()).toEqual({ + 'Someone1': 27, + 'Someone2': 34, + 'Someone3': 37 + }); + }); + +}); +console.group(`NamedArrayState`); +console.log(`someoneState`, someoneState); +console.groupEnd(); diff --git a/src/test/named-boolean-array-state.spec.ts b/src/test/named-boolean-array-state.spec.ts new file mode 100644 index 0000000..188c8f4 --- /dev/null +++ b/src/test/named-boolean-array-state.spec.ts @@ -0,0 +1,34 @@ +import { NamedBooleanArrayState } from '../lib/array'; + + +export class Options extends NamedBooleanArrayState { + public get name() { + return this.toObject(); + } +} + +let options = new Options(['option1', 'option2', 'option3'], [false]); + + +describe(`ArrayState`, () => { + beforeEach(() => { + options = new Options(['option1', 'option2', 'option3'], [false]); + }); + + it(`false()`, () => { + expect(options.false('option2', 'option3').state).toEqual([false, false, false]); + }); + + it(`toggle()`, () => { + expect(options.toggle('option2', 'option3').state).toEqual([false, false, false]); + expect(options.toggle('option1', 'option2', 'option3').state).toEqual([true, true, true]); + }); + + it(`true()`, () => { + expect(options.true('option1').state).toEqual([true, true, true]); + }); +}); + +console.group(`NamedBooleanArrayState`); +console.log(`NamedBooleanArrayState`, options); +console.groupEnd(); diff --git a/src/test/named-object-state.spec.ts b/src/test/named-object-state.spec.ts new file mode 100644 index 0000000..28e63b4 --- /dev/null +++ b/src/test/named-object-state.spec.ts @@ -0,0 +1,12 @@ +import { NamedObjectState as ANamedObjectState } from "../lib/object"; + +export class NamedObjectState< + Type = any, + Names extends PropertyKey = PropertyKey, +> extends ANamedObjectState {} + +const namedObjectState = new NamedObjectState(['a', 'b', 'c']); + +console.group(`NamedObjectState`); +console.log(`namedObjectState.pick('a')`, namedObjectState.pick('a')); +console.groupEnd(); diff --git a/src/test/null-state.spec.ts b/src/test/null-state.spec.ts index 5730341..44c1744 100644 --- a/src/test/null-state.spec.ts +++ b/src/test/null-state.spec.ts @@ -6,8 +6,9 @@ const nullified = new Nullified(); nullified.set(); -console.log(`NullState`); +console.group(`NullState`); console.log(nullified.state); nullified.unset(); console.log(nullified.state); +console.groupEnd(); diff --git a/src/test/number-state.spec.ts b/src/test/number-state.spec.ts index c9d129b..6c70e38 100644 --- a/src/test/number-state.spec.ts +++ b/src/test/number-state.spec.ts @@ -9,6 +9,7 @@ export class NumberedState extends NumberState { const numberedState = new NumberedState(); +console.group(`NumberState`); console.log(numberedState.state); numberedState.decrement(); console.log(numberedState.state); @@ -18,4 +19,4 @@ numberedState.increment(5); console.log(numberedState.state); numberedState.reset(); console.log(numberedState.state); - +console.groupEnd(); diff --git a/src/test/object-state.spec.ts b/src/test/object-state.spec.ts new file mode 100644 index 0000000..fc8ae58 --- /dev/null +++ b/src/test/object-state.spec.ts @@ -0,0 +1,22 @@ +import { ObjectState as AbstractObjectState } from "../lib/object/object-state.abstract"; + +export interface UserInterface { + id: number, + firstName: string; + surname: string; +} + +export class User extends AbstractObjectState {} + +const user = new User({ + id: 27, + firstName: 'Firstname', + surname: 'Surname' +}); + +user.update({'surname': 'test'}); +// user.state.firstName = 'a'; + +console.log(user); + +export class FormState extends AbstractObjectState {} diff --git a/src/test/selectable-group.spec.ts b/src/test/selectable-group.spec.ts new file mode 100644 index 0000000..0642944 --- /dev/null +++ b/src/test/selectable-group.spec.ts @@ -0,0 +1,36 @@ +import { Selectable } from "../lib/selectable"; +import { SelectableGroup } from "../lib/selectable/selectable-group.abstract"; + + +// new SelectableGroup(); + +// Initialize individual Selectable elements +export class Checkbox extends Selectable { + public check() { + super.select(); + } + + public uncheck() { + super.select(); + } +} + +export class Checkboxes extends SelectableGroup { + constructor(selectable: Selectable[], enabled = true) { + super(selectable, enabled); + } + + // You can add additional functionality if needed, for example: + public countSelected(): number { + return this.selectable.filter(selectable => selectable.isSelected()).length; + } +} + +const item1 = new Checkbox(); +const item2 = new Checkbox(); +const item3 = new Checkbox(); + +// Create a ConcreteSelectableGroup with the created Selectable items +const group = new Checkboxes([item1, item2, item3]); + +console.log(`group`, group); diff --git a/src/test/selectable.spec.ts b/src/test/selectable.spec.ts new file mode 100644 index 0000000..7e2d6d8 --- /dev/null +++ b/src/test/selectable.spec.ts @@ -0,0 +1,48 @@ +import { Selectable } from '../lib/selectable/selectable.abstract'; + +export class Checkbox extends Selectable {} + +let checkbox = new Checkbox(true); + +describe(`Selectable`, () => { + beforeEach(() => { + checkbox = new Checkbox(true); + }); + + it(`enable()`, () => { + expect(checkbox.isDisabled()).toBeFalse(); + checkbox.enable(); + expect(checkbox.isDisabled()).toBeFalse(); + expect(checkbox.isEnabled()).toBeTrue(); + }); + + it(`disable()`, () => { + checkbox.disable() + expect(checkbox.isDisabled()).toBeTrue(); + expect(checkbox.isEnabled()).toBeFalse(); + }); + + it(`isSelected()`, () => { + expect(checkbox.isSelected()).toBeTrue(); + }); + + it(`deselect)`, () => { + checkbox.deselect(); + expect(checkbox.isSelected()).toBeFalse(); + }); + + it(`select()`, () => { + checkbox.deselect(); + expect(checkbox.isSelected()).toBeFalse(); + checkbox.select(); + expect(checkbox.isSelected()).toBeTrue(); + }); + + it(`toggle()`, () => { + expect(checkbox.isSelected()).toBeTrue(); + checkbox.toggle(); + expect(checkbox.isSelected()).toBeFalse(); + checkbox.toggle(); + expect(checkbox.isSelected()).toBeTrue(); + }); +});