{"id":15015938,"url":"https://github.com/nullvoxpopuli/ember-statechart-component","last_synced_at":"2025-07-31T02:34:04.500Z","repository":{"id":36979124,"uuid":"360006986","full_name":"NullVoxPopuli/ember-statechart-component","owner":"NullVoxPopuli","description":"Statecharts as components. No classes. Pure declarative state transitions.","archived":false,"fork":false,"pushed_at":"2025-04-07T02:16:27.000Z","size":6774,"stargazers_count":36,"open_issues_count":16,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-11T04:12:48.244Z","etag":null,"topics":["ember","emberjs","hacktoberfest","statechart","statemachine","xstate"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NullVoxPopuli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-21T02:18:02.000Z","updated_at":"2025-03-31T06:28:49.000Z","dependencies_parsed_at":"2024-01-21T22:24:25.386Z","dependency_job_id":"a70e627d-ed43-46aa-bc92-2df52ef2f0e2","html_url":"https://github.com/NullVoxPopuli/ember-statechart-component","commit_stats":{"total_commits":531,"total_committers":9,"mean_commits":59.0,"dds":"0.39924670433145004","last_synced_commit":"b47b66bd736f62219498c229e52d733fee9c3f6e"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-statechart-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-statechart-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-statechart-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-statechart-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NullVoxPopuli","download_url":"https://codeload.github.com/NullVoxPopuli/ember-statechart-component/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248339225,"owners_count":21087215,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ember","emberjs","hacktoberfest","statechart","statemachine","xstate"],"created_at":"2024-09-24T19:48:11.030Z","updated_at":"2025-04-11T04:12:58.780Z","avatar_url":"https://github.com/NullVoxPopuli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cp align=\"center\"\u003e\n  \u003cbr /\u003e\n\n  \u003cpicture\u003e\n    \u003cimg alt=\"XState logotype\" src=\"/logo-dark.png\"\u003e\n  \u003c/picture\u003e\n  \u003cbr /\u003e\n    \u003cstrong\u003eActor-based state-management as components.\u003c/strong\u003e \u003ca href=\"https://stately.ai/docs\"\u003e→ Documentation\u003c/a\u003e\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n\u003c/p\u003e\n\n\n[![CI](https://github.com/NullVoxPopuli/ember-statechart-component/actions/workflows/ci.yml/badge.svg)](https://github.com/NullVoxPopuli/ember-statechart-component/actions/workflows/ci.yml)\n[![npm version](https://badge.fury.io/js/ember-statechart-component.svg)](https://www.npmjs.com/package/ember-statechart-component)\n\n\nSupport\n------------------------------------------------------------------------------\n\n- XState \u003e= 5\n- TypeScript \u003e= 5.2\n- ember-source \u003e= 5.1\n- Glint \u003e= 1.2.1\n\nInstallation\n------------------------------------------------------------------------------\n\n```bash\nnpm install ember-statechart-component\n```\n\nAnywhere in your app, though, if you don't have anywhere specific in mind, the `app/app.js` will be just fine:\n\n```ts\nimport 'ember-statechart-component';\n```\n\nThis instructs Ember how to render and create actors from state machines.\n\n## Migrating from XState v4?\n\nSee: https://stately.ai/docs/migration\n\n\nUsage\n------------------------------------------------------------------------------\n\n```gjs\nimport { createMachine } from 'xstate';\n\nconst Toggler = createMachine({\n  initial: 'inactive',\n  states: {\n    inactive: { on: { TOGGLE: 'active' } },\n    active: { on: { TOGGLE: 'inactive' } },\n  },\n});\n\n\u003ctemplate\u003e\n  \u003cToggler as |toggler|\u003e\n    {{toggler.statePath}}\n\n    \u003cbutton {{on 'click' (fn toggler.send 'TOGGLE')}}\u003e\n      Toggle\n    \u003c/button\u003e\n  \u003c/Toggler\u003e\n\u003c/template\u003e\n```\n\n### Accessing Ember Services\n\n```gjs\nimport { getService } from 'ember-statechart-component';\nimport { setup } from 'xstate';\n\nconst AuthenticatedToggle = setup({\n  actions: {\n    notify: ({ context }) =\u003e {\n      getService(context, 'toasts').notify('You must be logged in');\n    },\n  },\n  guards: {\n    isAuthenticated: ({ context }) =\u003e getService(context, 'session').isAuthenticated,\n  },\n}).createMachine({\n  initial: 'inactive',\n  states: {\n    inactive: {\n      on: {\n        TOGGLE: [\n          {\n            target: 'active',\n            guard: 'isAuthenticated',\n          },\n          { actions: ['notify'] },\n        ],\n      },\n    },\n    active: { on: { TOGGLE: 'inactive' } },\n  },\n});\n\n\u003ctemplate\u003e\n  \u003cAuthenticatedToggle as |toggle|\u003e\n    {{toggle.statePath}}\n\n    \u003cbutton {{on 'click' (fn toggle.send 'TOGGLE')}}\u003e\n      Toggle\n    \u003c/button\u003e\n  \u003c/AuthenticatedToggle\u003e\n\u003c/template\u003e\n```\n\n\n### Matching States\n\n```hbs\n\u003cToggle as |toggle|\u003e\n  {{#if (toggle.matches 'inactive')}}\n    The inactive state\n  {{else if (toggle.matches 'active')}}\n    The active state\n  {{else}}\n    Unknown state\n  {{/if}}\n\n  \u003cbutton {{on 'click' (fn toggle.send 'TOGGLE')}}\u003e\n    Toggle\n  \u003c/button\u003e\n\u003c/Toggle\u003e\n```\n\n### API\n\n\n#### `@config`\n\nThis argument allows you to pass a [MachineOptions](https://xstate.js.org/docs/packages/xstate-fsm/#api) for [actions](https://xstate.js.org/docs/guides/actions.html), [services](https://xstate.js.org/docs/guides/communication.html#configuring-services), [guards](https://xstate.js.org/docs/guides/guards.html#serializing-guards), etc.\n\nUsage:\n\n\u003cdetails\u003e\u003csummary\u003eToggle machine that needs a config\u003c/summary\u003e\n\n```js\n// app/components/toggle.js\nimport { createMachine, assign } from 'xstate';\n\nexport default createMachine({\n  initial: 'inactive',\n  states: {\n    inactive: { on: { TOGGLE: 'active' } },\n    active: {\n      on: {\n        TOGGLE: {\n          target: 'inactive',\n          actions: ['toggleIsOn']\n        }\n      }\n    },\n  },\n});\n```\n\n\u003c/details\u003e\n\n```hbs\n\u003cToggle\n  @config={{hash\n    actions=(hash\n      toggleIsOn=@onRoomIlluminated\n    )\n  }}\nas |toggle|\u003e\n  \u003cbutton {{on 'click' (fn toggle.send 'TOGGLE')}}\u003e\n    Toggle\n  \u003c/button\u003e\n\u003c/Toggle\u003e\n```\n\n#### `@input`\n\nProviding inputs from arguments works as you expect, following docs from [XState: Input](https://stately.ai/docs/input)\n\n```glimmer-ts \nconst Toggle = createMachine({\n  types: {\n    input: {} as { numCalled?: number },\n  },\n  initial: 'inactive',\n  context: ({ input }) =\u003e {\n    return {\n      numCalled: input.numCalled ?? 0,\n    };\n  },\n  states: {\n    inactive: {\n      entry: assign({\n        numCalled: ({ context }) =\u003e context.numCalled + 1,\n      }),\n      on: { TOGGLE: 'active' },\n    },\n    active: {\n      entry: assign({\n        numCalled: ({ context }) =\u003e context.numCalled + 1,\n      }),\n      on: { TOGGLE: 'inactive' },\n    },\n  },\n});\n\nconst input = {\n  numCalled: 10,\n};\n\n\u003ctemplate\u003e\n  \u003cToggle @input={{input}} as |toggle|\u003e\n    {{toggle.statePath}}\n\n    \u003cbutton type=\"button\" {{on \"click\" (fn toggle.send \"TOGGLE\")}}\u003e\n      Toggle\n    \u003c/button\u003e\n  \u003c/Toggle\u003e\n\u003c/template\u003e\n```\n\n#### `@context`\n\nSets the initial context. The current value of the context can then be accessed via `state.context`.\n\nUsage:\n\n\u003cdetails\u003e\u003csummary\u003eToggle machine that interacts with context\u003c/summary\u003e\n\n```js\nimport { createMachine, assign } from 'xstate';\n\nexport default createMachine({\n  initial: 'inactive',\n  states: {\n    inactive: {\n      on: {\n        TOGGLE: {\n          target: 'active',\n          actions: ['increaseCounter']\n        }\n      }\n    },\n    active: {\n      on: {\n        TOGGLE: {\n          target: 'inactive',\n          actions: ['increaseCounter']\n        }\n      }\n    },\n  },\n}, {\n  actions: {\n    increaseCounter: assign({\n      counter: (context) =\u003e context.counter + 1\n    })\n  }\n});\n```\n\n\u003c/details\u003e\n\n```hbs\n\u003cToggle @context=(hash counter=0) as |toggle|\u003e\n  \u003cbutton {{on 'click' (fn toggle.send 'TOGGLE')}}\u003e\n    Toggle\n  \u003c/button\u003e\n\n  \u003cp\u003e\n    Toggled: {{toggle.snapshot.context.counter}} times.\n  \u003c/p\u003e\n\u003c/Toggle\u003e\n```\n\n#### `@snapshot`\n\nThe machine will use `@snapshot` as the initial state.\nAny changes to this argument\nare not automatically propagated to the machine.\nAn update event (see details below) is sent instead.\n\n### What happens if any of the passed args change?\n\nAn event will be sent to the machine for you, along\nwith all named arguments used to invoke the component.\n\nTo work with this event, use the constant provided by this library:\n\n```js \n\nimport { UPDATE_EVENT_NAME } from 'ember-statechart-component';\n\n\nconst MyMachine = createMachine({\n  initial: 'inactive',\n  states: {\n    [UPDATE_EVENT_NAME]: { /* ... */\n    /* ... */\n  },\n});\n```\n\nThe value of this constant is just `EXTERNAL_UPDATE`, but the import makes it clear _why_ it exists, as the name does need to exactly match how the ember component manager is implemented for machines.\n\n\n### API\n\nThe yielded value from an invoked state machine has some properties on it as well as the actor that allows you to \"just defer to XState\" for most situations. \n\nGiven this a machine and its invocation, \n```gjs\nimport { createMachine } from 'xstate';\n\nconst Authenticator = createMachine({ /* ... */ });\n\n\u003ctemplate\u003e\n  \u003cAuthenticator as |auth|\u003e\n\n    what is available on `auth`? \n  \u003c/Authenticator\u003e\n\u003c/template\u003e\n```\n\n- `actor` - The underlying actor that XState manages, see: [The Actor Docs](https://stately.ai/docs/category/actors)\n- `snapshot` - The most recent snapshot available from the actor\n- `value` - alias for `snapshot.value`, which represents the name of the state, or an array of states, if the current state is nested.\n- `statePath` - a dot-separated string representing the current `value`\n- `matches` - The [matches function](https://stately.ai/docs/states#statematchesstatevalue)\n- `onTransition` - A way to arbitrarily run code when the machine transitions. \n\n\n\nContributing\n------------------------------------------------------------------------------\n\nSee the [Contributing](CONTRIBUTING.md) guide for details.\n\n\nLicense\n------------------------------------------------------------------------------\n\nThis project is licensed under the [MIT License](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullvoxpopuli%2Fember-statechart-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnullvoxpopuli%2Fember-statechart-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullvoxpopuli%2Fember-statechart-component/lists"}