├── .travis.yml
├── src
├── react-app-env.d.ts
├── .eslintrc
├── index.ts
├── context.ts
├── observable.ts
├── test-utils.ts
├── utils
│ ├── memoize.ts
│ └── subscription.ts
├── batched.ts
├── transaction.ts
├── observe.ts
├── observe.spec.ts
├── derived.spec.ts
├── atom.ts
├── molecule.spec.ts
├── derived.ts
├── atom.spec.ts
└── molecule.ts
├── .eslintignore
├── example
├── src
│ ├── react-app-env.d.ts
│ ├── index.tsx
│ ├── setupTests.ts
│ ├── App.test.tsx
│ ├── theme.ts
│ ├── react
│ │ ├── usePrevious.ts
│ │ ├── useConstant.ts
│ │ ├── useObservable.ts
│ │ └── useConstructor.ts
│ ├── index.css
│ ├── Loader.tsx
│ ├── state
│ │ ├── interval.ts
│ │ ├── window-size.ts
│ │ ├── pagination.ts
│ │ ├── dialog.ts
│ │ └── request.ts
│ ├── ListItem.tsx
│ ├── App.tsx
│ ├── Folder.tsx
│ ├── Folders.tsx
│ ├── Note.tsx
│ └── api.ts
├── public
│ ├── favicon.ico
│ ├── manifest.json
│ ├── index.html
│ └── empty.svg
├── README.md
├── tsconfig.json
└── package.json
├── tsconfig.test.json
├── .editorconfig
├── .prettierrc
├── .gitignore
├── .eslintrc
├── tsconfig.json
├── package.json
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12
4 | - 10
5 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | node_modules/
4 | .snapshots/
5 | *.min.js
--------------------------------------------------------------------------------
/example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/malerba118/elementos/HEAD/example/public/favicon.ico
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import './index.css'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import App from './App'
6 |
7 | ReactDOM.render(, document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atom'
2 | export * from './molecule'
3 | export * from './derived'
4 | export * from './observe'
5 | export * from './observable'
6 | export * from './batched'
7 | export * from './context'
8 | export * from './transaction'
9 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "jsxSingleQuote": true,
4 | "semi": false,
5 | "tabWidth": 2,
6 | "bracketSpacing": true,
7 | "jsxBracketSameLine": false,
8 | "arrowParens": "always",
9 | "trailingComma": "none"
10 | }
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | It is linked to the elementos package in the parent directory for development purposes.
4 |
5 | You can run `npm install` and then `npm start` to test your package.
6 |
--------------------------------------------------------------------------------
/example/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/example/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div')
7 | ReactDOM.render(, div)
8 | ReactDOM.unmountComponentAtNode(div)
9 | })
10 |
--------------------------------------------------------------------------------
/example/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { extendTheme } from '@chakra-ui/react'
2 |
3 | // 2. Call `extendTheme` and pass your custom values
4 | export const theme = extendTheme({
5 | fonts: {
6 | body: 'Poppins, sans-serif',
7 | heading: 'Poppins, serif',
8 | mono: 'Menlo, monospace'
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/src/context.ts:
--------------------------------------------------------------------------------
1 | import { Transaction } from './transaction'
2 |
3 | let currentTransaction: Transaction | undefined
4 |
5 | export const getCurrentTransaction = () => currentTransaction
6 | export const setCurrentTransaction = (transaction: Transaction | undefined) => {
7 | currentTransaction = transaction
8 | }
9 |
--------------------------------------------------------------------------------
/example/src/react/usePrevious.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from 'react'
2 |
3 | function usePrevious(value: T): T | undefined {
4 | const ref = useRef()
5 | useEffect(() => {
6 | ref.current = value
7 | }, [value])
8 | return ref.current
9 | }
10 |
11 | export default usePrevious
12 |
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "elementos",
3 | "name": "elementos",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/example/src/react/useConstant.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 |
3 | const useConstant = (fn: () => T): T => {
4 | const ref = useRef(null)
5 | if (ref.current == null) {
6 | // we instantiate { value } to not conflict with returned null
7 | ref.current = { value: fn() }
8 | }
9 | return ref.current.value
10 | }
11 |
12 | export default useConstant
13 |
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | html, body, #root {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | margin: 0;
7 | padding: 0;
8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
9 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
10 | sans-serif;
11 | -webkit-font-smoothing: antialiased;
12 | -moz-osx-font-smoothing: grayscale;
13 | }
14 |
15 | code {
16 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
17 | monospace;
18 | }
19 |
--------------------------------------------------------------------------------
/example/src/react/useObservable.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { Observable, observe, ExtractObservableType } from 'elementos'
3 |
4 | export const useObservable = >(
5 | observable: T
6 | ): ExtractObservableType => {
7 | const [state, setState] = useState>(observable.get())
8 |
9 | useEffect(() => {
10 | return observe(observable, (value) => {
11 | setState(value)
12 | })
13 | }, [observable])
14 |
15 | return state
16 | }
17 |
--------------------------------------------------------------------------------
/example/src/Loader.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Spinner, Flex } from '@chakra-ui/react'
3 |
4 | interface LoaderProps {
5 | active: boolean
6 | }
7 |
8 | const Loader: FC = ({ active }) => {
9 | if (!active) {
10 | return null
11 | }
12 | return (
13 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default Loader
27 |
--------------------------------------------------------------------------------
/example/src/state/interval.ts:
--------------------------------------------------------------------------------
1 | import { atom, observe } from 'elementos'
2 |
3 | export const createInterval = (
4 | initialCallback: () => void,
5 | interval: number
6 | ) => {
7 | const interval$ = atom(interval)
8 | let callback = initialCallback
9 |
10 | const dispose = observe(interval$, (ms) => {
11 | const id = setInterval(() => {
12 | callback()
13 | }, ms)
14 | return () => {
15 | clearInterval(id)
16 | }
17 | })
18 |
19 | return {
20 | setInterval: (milliseconds: number) => {
21 | interval$.actions.set(milliseconds)
22 | },
23 | setCallback: (nextCallback: () => void) => {
24 | callback = nextCallback
25 | },
26 | dispose
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "standard",
5 | "standard-react",
6 | "plugin:prettier/recommended",
7 | "prettier/standard",
8 | "prettier/react",
9 | "plugin:@typescript-eslint/eslint-recommended"
10 | ],
11 | "env": {
12 | "node": true
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 2020,
16 | "ecmaFeatures": {
17 | "legacyDecorators": true,
18 | "jsx": true
19 | }
20 | },
21 | "settings": {
22 | "react": {
23 | "version": "16"
24 | }
25 | },
26 | "rules": {
27 | "space-before-function-paren": 0,
28 | "react/prop-types": 0,
29 | "react/jsx-handler-names": 0,
30 | "react/jsx-fragments": 0,
31 | "react/no-unused-prop-types": 0,
32 | "import/export": 0
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/src/state/window-size.ts:
--------------------------------------------------------------------------------
1 | import { atom } from 'elementos'
2 |
3 | type Size = {
4 | width: number
5 | height: number
6 | }
7 |
8 | export const createWindowSize$ = () => {
9 | const size$ = atom(null)
10 |
11 | let listener: EventListener
12 | size$.onObserverChange(({ count }) => {
13 | // if there are no observers, remove listener
14 | if (count === 0 && listener) {
15 | window.removeEventListener('resize', listener)
16 | } else if (count > 0 && !listener) {
17 | // if there are observers, add listener
18 | listener = () => {
19 | size$.actions.set({
20 | height: window.innerHeight,
21 | width: window.innerWidth
22 | })
23 | }
24 | window.addEventListener('resize', listener)
25 | }
26 | })
27 |
28 | return size$
29 | }
30 |
--------------------------------------------------------------------------------
/src/observable.ts:
--------------------------------------------------------------------------------
1 | import { Transaction } from './transaction'
2 |
3 | export type ObservableUnsubscriber = () => void
4 | export type ObservableSubscriber = (transaction: Transaction) => void
5 | export type ObserverChangeSubscriber = (params: { count: number }) => void
6 |
7 | export interface Observable {
8 | get: (
9 | selector?: (val: State) => Selection,
10 | transaction?: Transaction
11 | ) => Selection
12 | subscribe: (subscriber: ObservableSubscriber) => ObservableUnsubscriber
13 | onObserverChange: (
14 | subscriber: ObserverChangeSubscriber
15 | ) => ObservableUnsubscriber
16 | }
17 |
18 | export type ExtractObservableType = Type extends Observable
19 | ? X
20 | : never
21 |
22 | export type ExtractObservableTypes