{"id":13399404,"url":"https://github.com/alexkrolick/mdx-observable","last_synced_at":"2025-02-28T05:14:32.261Z","repository":{"id":32917345,"uuid":"146137335","full_name":"alexkrolick/mdx-observable","owner":"alexkrolick","description":"Global state for Markdown documents","archived":false,"fork":false,"pushed_at":"2024-08-29T08:11:58.000Z","size":1420,"stargazers_count":45,"open_issues_count":18,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-24T09:39:21.174Z","etag":null,"topics":["callbag","jsx","markdown","md","mdx","observable","react","state"],"latest_commit_sha":null,"homepage":"https://mdx-observable.netlify.com/","language":"JavaScript","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/alexkrolick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2018-08-25T23:43:16.000Z","updated_at":"2024-08-29T08:12:01.000Z","dependencies_parsed_at":"2024-09-18T23:32:20.129Z","dependency_job_id":null,"html_url":"https://github.com/alexkrolick/mdx-observable","commit_stats":{"total_commits":23,"total_committers":3,"mean_commits":7.666666666666667,"dds":"0.30434782608695654","last_synced_commit":"af7168250146cda4796bcc2a9ec47362f9a4cea2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Fmdx-observable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Fmdx-observable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Fmdx-observable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Fmdx-observable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexkrolick","download_url":"https://codeload.github.com/alexkrolick/mdx-observable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240780572,"owners_count":19856382,"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":["callbag","jsx","markdown","md","mdx","observable","react","state"],"created_at":"2024-07-30T19:00:37.369Z","updated_at":"2025-02-28T05:14:32.239Z","avatar_url":"https://github.com/alexkrolick.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# MDX-Observable\n\n_**alpha project**, API may change significantly_\n\n_0.2.0 does not actually use observables so the name may change 😬_\n\nInteractive documents powered by Markdown, React, ~~and Observables~~\n\nShare state between JSX blocks in a [MDX](https://mdxjs.com/) document\n\n- **Declarative** React automatically updates observers when data changes\n- **Write with Markdown** store documents in plain text that can be revision-controlled\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Examples](#examples)\n  - [Dev Server](#dev-server)\n  - [Static Build](#static-build)\n- [API](#api)\n  - [State](#state)\n    - [Using render prop](#using-render-prop)\n    - [Using context to connect Observe components](#using-context-to-connect-observe-components)\n  - [Observe](#observe)\n- [Alternatives](#alternatives)\n  - [Notebooks](#notebooks)\n  - [Other state management libraries for JS](#other-state-management-libraries-for-js)\n- [Roadmap](#roadmap)\n- [Potential Issues](#potential-issues)\n  - [Usage outside MDX](#usage-outside-mdx)\n  - [Warning about blank lines in JSX](#warning-about-blank-lines-in-jsx)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Examples\n\nSee demos: https://mdx-observable.netlify.app/\n\n- [Counter w/Observer](./demo/counter.mdx)\n- [Counter w/Render Prop](./demo/counter-child-function.mdx)\n- [Toggle](./demo/toggle.mdx)\n- [Dataviz](./demo/dataviz.mdx)\n\n```\ngit clone git@github.com:alexkrolick/mdx-observable.git\ncd mdx-observable\nyarn install\n```\n\n### Dev Server\n\nStart the dev server with live reloading\n\n```sh\nyarn run demo:parcel:dev\n```\n\n### Static Build\n\nThe output files in `dist/` can be hosted on a static web server\n\n```\nyarn run build:parcel\n```\n\n```jsx\n// notebook.mdx\nimport { State, Observe } from 'mdx-observable';\n\n# Counter\n\n\u003cState initialState={{ count: 0 }}\u003e\n\n\u003cObserve\u003e\n  {({ setState }) =\u003e (\n    \u003cbutton onClick={() =\u003e setState(s =\u003e ({ count: s.count + 1 }))}\u003e\n      Click me\n    \u003c/button\u003e\n  )}\n\u003c/Observe\u003e\n\nThe button has been clicked:\n\n\u003cObserve\u003e\n  { ({...state}) =\u003e (\u003cspan\u003e{state.count} times\u003c/span\u003e) }\n\u003c/Observe\u003e\n\n\u003c/State\u003e\n```\n\nExample with a form, table, and graph running in [OK-MDX](https://github.com/jxnblk/ok-mdx):\n\n\u003cimg width=\"1311\" alt=\"screen shot 2018-08-25 at 11 33 32 pm\" src=\"https://user-images.githubusercontent.com/1571667/44625478-a8dbd800-a8bf-11e8-8a27-4f56e89f40f2.png\"\u003e\n\n## API\n\n### State\n\nState container component\n\nProps:\n\n- `initialState: Object` - initial state\n- `children: React.Children | function` Can either be:\n  - React children: JSX or Markdown node(s)\n  - A render prop: a single function that gets called with `{...state, setState}` as the argument\n\n#### Using render prop\n\n_Very similar to [React Powerplug's State](https://github.com/renatorib/react-powerplug/blob/master/docs/components/State.md)_\n\n_Note: whitespace is sensitive in MDX,\nso the awkward spacing below is important._\n\n```mdx\n\u003cState initialState={{}}\u003e\n{({setState, ...state}) =\u003e \u003cReact.Fragment\u003e\n\n\u003ch1\u003eHello, World!\u003c/h1\u003e\n\nSome markdown\n\n## Some header\n\n- item a\n- item b\n\n\u003c/React.Fragment\u003e}\n\u003c/State\u003e\n```\n\n#### Using context to connect Observe components\n\n```mdx\n\u003cState initialState={{}}\u003e\n\n...child nodes...\n\n\u003cObserve\u003e\n  {({ ...state}) =\u003e \u003ch1\u003eHello, World!\u003c/h1\u003e}\n\u003c/Observe\u003e\n\n...more child nodes...\n\n\u003c/State\u003e\n```\n\n### Observe\n\nComponent that re-renders when the global state changes.\n\nProps:\n\n- `children: ({...state, setState}) =\u003e React.Node`\n  function that accepts an object with:\n  - `setState`: function like React `setState`, can take an object or an updater function (`state =\u003e patch`); result is _shallow merged_ with current state\n  - the rest of the global state\n\n```js\n\u003cObserve\u003e\n  {({ setState, ...state }) =\u003e {\n    return \u003cdiv\u003e{state.something}\u003c/div\u003e;\n  }}\n\u003c/Observe\u003e\n\n\u003cObserve\u003e\n  {({ setState, something }) =\u003e {\n    return \u003cdiv\u003e{something\u003c/div\u003e;\n  }}\n\u003c/Observe\u003e\n```\n\n## Alternatives\n\n### Notebooks\n\nAdvantages of MDX-Observable over [Jupyter](https://jupyter.org/) or [ObservableHQ](https://beta.observablehq.com/scratchpad):\n\n- No cells to run; entire document is live\n- Interactivity powered by predictable one-way data flow\n- Use standard JS imports and any React component\n- Produces static bundles\n- Edit using preferred JS tooling\n- Bundle with anything that supports [MDX](https://mdxjs.com/getting-started/), like Webpack, Gatsby, Parcel, etc.\n\n### Other state management libraries for JS\n\nMost state management libraries don't work with MDX because you can't define variables, meaning APIs like `const myStore = createStore();` are inaccessible. You can work around this by doing this work in another JS file and importing it, but the logic is hard to follow.\n\nSome renderless/headless libraries thatwork fully inline are:\n\n- https://github.com/renatorib/react-powerplug\n- https://github.com/ianstormtaylor/react-values\n\nHowever the whitespace sensitivity may make them difficult to use.\n\n## Roadmap\n\n- [x] See if `\u003cInit /\u003e` could work as a wrapper instead of sibling of `\u003cObserver /\u003e`. This would allow better scoping and safer setup/teardown.\n\n- [ ] Some way to define functions inline. This might map well to the concept of \"selectors\" from Redux. Currently you can work around this gap by defining utilities in external JS files, but this makes it hard to write self-contained notebooks.\n\nPossible API:\n\n```js\n\u003cInit state={} selectors={{ selectCheapest: state =\u003e {/* compute */} }}\u003e\n```\n\n- [x] Better live-reload support. MDX utils like `ok-mdx` do a full remount when the live editor changes or navigation occures; we could add a `restoreKey` to persist a namespaced cache within the module.\n\n- [ ] **Add tests**\n\n## Potential Issues\n\n### Usage outside MDX\n\n~~Technically `mdx-observable` doesn't depend on MDX for anything, but since it uses a singleton for a cache, it is not a good fit for state management in an app.~~ Fixed\n\n### Warning about blank lines in JSX\n\nCurrently (Aug 2018) the MDX parser doesn't allow putting blank lines inside of JSX blocks. If you see an error about \"adjacent elements\", this is probably why.\n\n## License\n\nSee [LICENSE](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexkrolick%2Fmdx-observable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexkrolick%2Fmdx-observable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexkrolick%2Fmdx-observable/lists"}