diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..899177c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +node_modules/ +packages/**/node_modules +packages/**/dist +packages/**/build diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..75a894a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/.vscode/settings.json b/.vscode/settings.json index ca7777b..e278ff6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,18 @@ "typescript.tsdk": "./node_modules/typescript/lib", "editor.tabSize": 2, "editor.detectIndentation": false, - "tslint.nodePath": "./node_modules" + "tslint.nodePath": "./node_modules", + "[typescript]": { + "editor.formatOnSave": true + }, + "[typescriptreact]": { + "editor.formatOnSave": true + }, + "prettier.printWidth": 100, + "prettier.semi": false, + "prettier.singleQuote": true, + "prettier.typescriptEnable": [ + "typescript", + "typescriptreact" + ], } diff --git a/package.json b/package.json index 2a90f52..f1f7368 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,16 @@ "build": "yarn workspace @grammarly/focal build", "package": "yarn workspace @grammarly/focal pack", "release": "yarn workspace @grammarly/focal release", - "test": "yarn workspace @grammarly/focal test && yarn workspace focal-todomvc build && yarn workspace focal-examples build && yarn workspace focal-manual-tests build", - "postinstall": "yarn build" + "test": "yarn workspace @grammarly/focal test && yarn workspace focal-todomvc build && yarn workspace focal-examples build && yarn workspace focal-manual-tests build && yarn prettier:check", + "postinstall": "yarn build", + "prettier:fix": "prettier --write './packages/**/*.ts?(x)'", + "prettier:check": "prettier --list-different './packages/**/*.ts?(x)'" }, "devDependencies": { "@grammarly/tslint-config": "0.5.1", "tslint": "5.20.0", - "typescript": "3.6.4" + "typescript": "3.6.4", + "prettier": "1.18.2" }, "workspaces": [ "packages/focal", @@ -21,4 +24,4 @@ "resolutions": { "@types/react": "16.9.11" } -} +} \ No newline at end of file diff --git a/packages/examples/all/custom_typings/css/index.d.ts b/packages/examples/all/custom_typings/css/index.d.ts index 74ad49c..f70595d 100644 --- a/packages/examples/all/custom_typings/css/index.d.ts +++ b/packages/examples/all/custom_typings/css/index.d.ts @@ -1,4 +1,4 @@ declare module '*.css' { const e: any - export = e; + export = e } diff --git a/packages/examples/all/src/add-input/index.tsx b/packages/examples/all/src/add-input/index.tsx index ce4ad92..a11daac 100644 --- a/packages/examples/all/src/add-input/index.tsx +++ b/packages/examples/all/src/add-input/index.tsx @@ -25,16 +25,13 @@ const App = (props: { state: Atom }) => { return (
- - props.state.modify(addItem)} /> + + props.state.modify(addItem)} />
- { - reactiveList( - values.view(x => x.map((_, index) => index)), - index => {values.view(x => x[index])} - ) - } + {reactiveList(values.view(x => x.map((_, index) => index)), index => ( + {values.view(x => x[index])} + ))}
) diff --git a/packages/examples/all/src/animated-counter/index.tsx b/packages/examples/all/src/animated-counter/index.tsx index b3d195f..0b3972b 100644 --- a/packages/examples/all/src/animated-counter/index.tsx +++ b/packages/examples/all/src/animated-counter/index.tsx @@ -2,9 +2,7 @@ import * as React from 'react' import { F, Atom } from '@grammarly/focal' import * as Model from './model' -const Counter = ({ - counterState = Atom.create(Model.defaultCounterState) -}) => { +const Counter = ({ counterState = Atom.create(Model.defaultCounterState) }) => { const count = counterState.view('count') const absoluteCount = counterState.view('absoluteCount') @@ -13,19 +11,13 @@ const Counter = ({ Count: {count}, abs = {absoluteCount} - - + + ) } -const AnimatedDiv = ({ - counterState = Atom.create(Model.defaultCounterState) -}) => { +const AnimatedDiv = ({ counterState = Atom.create(Model.defaultCounterState) }) => { const displayState = counterState.lens('display') const count = counterState.view('count') @@ -36,7 +28,7 @@ const AnimatedDiv = ({ style={{ background: '#f00', position: 'absolute', - opacity: count.view(x => Model.shouldHideCounter(x) ? 0.3 : 1.0), + opacity: count.view(x => (Model.shouldHideCounter(x) ? 0.3 : 1.0)), left: count.view(x => x * 25), transition: 'left 0.5s linear, opacity 1.5s linear' }} @@ -46,7 +38,7 @@ const AnimatedDiv = ({ } }} > - + ) } else { @@ -54,20 +46,16 @@ const AnimatedDiv = ({ } }) - return ( - {animDiv} - ) + return {animDiv} } -const App = ({ - state = Atom.create(Model.defaultAppState) -}) => { +const App = ({ state = Atom.create(Model.defaultAppState) }) => { const counterState = state.lens('counter') return (
- - + +
) } diff --git a/packages/examples/all/src/animated-counter/model.ts b/packages/examples/all/src/animated-counter/model.ts index d897609..478dc7a 100644 --- a/packages/examples/all/src/animated-counter/model.ts +++ b/packages/examples/all/src/animated-counter/model.ts @@ -34,8 +34,7 @@ export function shouldHideCounter(count: number) { export function hideAfterFadeOut(counter: Atom) { // check if we still should hide - if (shouldHideCounter(counter.get().count)) - counter.lens('display').set(false) + if (shouldHideCounter(counter.get().count)) counter.lens('display').set(false) } export function resetCounter(counter: Atom) { diff --git a/packages/examples/all/src/app.tsx b/packages/examples/all/src/app.tsx index c5b2c4f..3a88d49 100644 --- a/packages/examples/all/src/app.tsx +++ b/packages/examples/all/src/app.tsx @@ -8,7 +8,7 @@ interface ExampleComponent { declare const require: (path: string) => any -const examples: { name: string, example: ExampleComponent }[] = [ +const examples: { name: string; example: ExampleComponent }[] = [ 'counter', 'clock', 'checkbox', @@ -45,10 +45,7 @@ examples.forEach(ex => { defaultState[ex.name] = ex.example.defaultState }) -export const AppComponent = ({ - state = Atom.create(defaultState) -}) => { - +export const AppComponent = ({ state = Atom.create(defaultState) }) => { return (

Focal examples

diff --git a/packages/examples/all/src/big-table/index.tsx b/packages/examples/all/src/big-table/index.tsx index 49a5d02..93bce63 100644 --- a/packages/examples/all/src/big-table/index.tsx +++ b/packages/examples/all/src/big-table/index.tsx @@ -1,36 +1,36 @@ import { fromEvent } from 'rxjs' import { startWith, distinctUntilChanged, map } from 'rxjs/operators' -import { - F, Atom, ReadOnlyAtom, bindElementProps, reactiveList -} from '@grammarly/focal' +import { F, Atom, ReadOnlyAtom, bindElementProps, reactiveList } from '@grammarly/focal' import * as React from 'react' const Window = { - innerWidth: - fromEvent(window, 'resize').pipe( - map(() => window.innerWidth), - distinctUntilChanged(), - startWith(0)) + innerWidth: fromEvent(window, 'resize').pipe( + map(() => window.innerWidth), + distinctUntilChanged(), + startWith(0) + ) } const defaultState = { tableHeight: 250, rowHeight: 30, rowCount: 10000, - columns: [ 'ID', 'ID * 10', 'Random Number' ] + columns: ['ID', 'ID * 10', 'Random Number'] } function getRowContent(id: number) { - return [ `${id}`, `${id * 10}`, `${Math.floor(Math.random() * 100)}` ] + return [`${id}`, `${id * 10}`, `${Math.floor(Math.random() * 100)}`] } function cellWidthStyle(columns: string[]) { - return Window.innerWidth.pipe(map(innerWidth => - ({ width: innerWidth / columns.length + 'px' }))) + return Window.innerWidth.pipe(map(innerWidth => ({ width: innerWidth / columns.length + 'px' }))) } function getVisibleRows( - tableHeight: number, rowHeight: number, rowCount: number, scrollTop: number + tableHeight: number, + rowHeight: number, + rowCount: number, + scrollTop: number ) { return { begin: Math.floor(scrollTop / rowHeight), @@ -38,24 +38,30 @@ function getVisibleRows( } } -const THead = (props: { columns: ReadOnlyAtom }) => +const THead = (props: { columns: ReadOnlyAtom }) => ( {props.columns.view(cols => - cols.map((col, i) => - {col}))} + cols.map((col, i) => ( + + {col} + + )) + )} +) const TBody = (props: { - state: Atom, + state: Atom visibleRows: ReadOnlyAtom<{ begin: number; end: number }> -}) => +}) => ( {reactiveList( props.visibleRows.view(({ begin, end }) => - Array.from(Array(end - begin), (_, i) => i + begin)), - i => + Array.from(Array(end - begin), (_, i) => i + begin) + ), + i => ( {props.state.view(({ columns }) => - getRowContent(i).map((column, i) => - {column}))} + getRowContent(i).map((column, i) => ( + + {column} + + )) + )} + ) )} +) const App = ({ state, scrollTop = Atom.create(0) }: { - state: Atom, + state: Atom scrollTop: Atom -}) => +}) => (
- +
getVisibleRows(m.tableHeight, m.rowHeight, m.rowCount, s) + visibleRows: Atom.combine(state, scrollTop, (m, s) => + getVisibleRows(m.tableHeight, m.rowHeight, m.rowCount, s) ) }} />
+) export default { Component: App, diff --git a/packages/examples/all/src/bmi/index.tsx b/packages/examples/all/src/bmi/index.tsx index b799857..991fcaa 100644 --- a/packages/examples/all/src/bmi/index.tsx +++ b/packages/examples/all/src/bmi/index.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { Atom, F, bind } from '@grammarly/focal' interface AppState { - weightKg: number, + weightKg: number heightCm: number } @@ -15,7 +15,7 @@ namespace AppState { function getBmi(weight: number, height: number) { const heightMeters = height * 0.01 - return Math.round(weight / (heightMeters ** 2)) + return Math.round(weight / heightMeters ** 2) } const App = (props: { state: Atom }) => { @@ -25,14 +25,12 @@ const App = (props: { state: Atom }) => { return (
- Weight (kg) + Weight (kg)
- Height (cm) + Height (cm)
- - BMI is {Atom.combine(weight, height, getBmi)} - + BMI is {Atom.combine(weight, height, getBmi)}
) } diff --git a/packages/examples/all/src/change-color/index.tsx b/packages/examples/all/src/change-color/index.tsx index f20ca30..4bf92dc 100644 --- a/packages/examples/all/src/change-color/index.tsx +++ b/packages/examples/all/src/change-color/index.tsx @@ -12,7 +12,7 @@ const BOLD = 'bold' interface AppState { style: { - color: string, + color: string font: string } } @@ -34,43 +34,31 @@ const App = (props: { state: Atom }) => { return (
- { - [GREEN, RED, BLUE].map((x, index) => ( - - )) - } + {[GREEN, RED, BLUE].map((x, index) => ( + + ))}
- { - [NORMAL, ITALIC, BOLD].map((x, index) => ( - - )) - } + {[NORMAL, ITALIC, BOLD].map((x, index) => ( + + ))}
- styles[x]), - font.view(x => styles[x]) - ) - } - > + styles[x]), font.view(x => styles[x]))}> Some text
diff --git a/packages/examples/all/src/checkbox-undo-redo/index.tsx b/packages/examples/all/src/checkbox-undo-redo/index.tsx index 1fa7077..240cee2 100644 --- a/packages/examples/all/src/checkbox-undo-redo/index.tsx +++ b/packages/examples/all/src/checkbox-undo-redo/index.tsx @@ -3,9 +3,10 @@ import { Atom, F, Lens, reactiveList } from '@grammarly/focal' import { App as Checkbox, AppState as CheckboxState } from '../checkbox' import { History, HistoryState } from '../utils/history' -interface AppState extends HistoryState<{ - checkboxes: CheckboxState[] -}> { } +interface AppState + extends HistoryState<{ + checkboxes: CheckboxState[] + }> {} namespace AppState { export const defaultState: AppState = HistoryState.createDefault({ @@ -21,45 +22,40 @@ const App = (props: { state: Atom }) => {
checkboxes.modify(x => [...x, CheckboxState.defaultState])} /> history.undo()} /> history.redo()} />
- { - reactiveList( - checkboxes.view(x => x.map((_, index) => index)), - index => { - const state = checkboxes - .lens(Lens.index(index)) - .lens(Lens.withDefault(CheckboxState.defaultState)) + {reactiveList(checkboxes.view(x => x.map((_, index) => index)), index => { + const state = checkboxes + .lens(Lens.index(index)) + .lens(Lens.withDefault(CheckboxState.defaultState)) - return ( -
- checkboxes.modify(x => x.filter((_, i) => i !== index))} - /> - -
- ) - } + return ( +
+ checkboxes.modify(x => x.filter((_, i) => i !== index))} + /> + +
) - } + })}
) diff --git a/packages/examples/all/src/checkbox/index.tsx b/packages/examples/all/src/checkbox/index.tsx index 8f6daa7..2134b68 100644 --- a/packages/examples/all/src/checkbox/index.tsx +++ b/packages/examples/all/src/checkbox/index.tsx @@ -11,17 +11,18 @@ export namespace AppState { } } -export const App = (props: { state: Atom }) => +export const App = (props: { state: Atom }) => (
- {props.state.view(x => x.checked ? 'ON' : 'OFF')} + {props.state.view(x => (x.checked ? 'ON' : 'OFF'))}
+) export default { Component: App, diff --git a/packages/examples/all/src/clock/index.tsx b/packages/examples/all/src/clock/index.tsx index 4e7bd34..e3167c7 100644 --- a/packages/examples/all/src/clock/index.tsx +++ b/packages/examples/all/src/clock/index.tsx @@ -3,14 +3,14 @@ import { interval } from 'rxjs' import { map, startWith } from 'rxjs/operators' import { F } from '@grammarly/focal' -const App = () => +const App = () => ( - { - interval(1000).pipe( - startWith(0), - map(() => new Date().toString())) - } + {interval(1000).pipe( + startWith(0), + map(() => new Date().toString()) + )} +) export default { Component: App diff --git a/packages/examples/all/src/convert-inputs/index.tsx b/packages/examples/all/src/convert-inputs/index.tsx index cbf3247..9a0b154 100644 --- a/packages/examples/all/src/convert-inputs/index.tsx +++ b/packages/examples/all/src/convert-inputs/index.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import { Atom, F } from '@grammarly/focal' -const toFahrenheit = (celsius: number) => celsius * 9 / 5 + 32 -const toCelsius = (fahrenheit: number) => (fahrenheit - 32) * 5 / 9 +const toFahrenheit = (celsius: number) => (celsius * 9) / 5 + 32 +const toCelsius = (fahrenheit: number) => ((fahrenheit - 32) * 5) / 9 interface AppState { - celsius: number, + celsius: number fahrenheit: number } @@ -18,29 +18,36 @@ namespace AppState { } } -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (

x.celsius.toString())} - onChange={e => props.state.set({ - celsius: +e.currentTarget.value, - fahrenheit: toFahrenheit(+e.currentTarget.value) - })} - />°C + onChange={e => + props.state.set({ + celsius: +e.currentTarget.value, + fahrenheit: toFahrenheit(+e.currentTarget.value) + }) + } + /> + °C

x.fahrenheit.toString())} - onChange={e => props.state.set({ - fahrenheit: +e.currentTarget.value, - celsius: toCelsius(+e.currentTarget.value) - })} - />°F + onChange={e => + props.state.set({ + fahrenheit: +e.currentTarget.value, + celsius: toCelsius(+e.currentTarget.value) + }) + } + /> + °F

+) export default { Component: App, diff --git a/packages/examples/all/src/convert/index.tsx b/packages/examples/all/src/convert/index.tsx index 68d05e4..0e0eb98 100644 --- a/packages/examples/all/src/convert/index.tsx +++ b/packages/examples/all/src/convert/index.tsx @@ -15,8 +15,8 @@ const App = (props: { state: Atom }) => { const temperature = props.state.lens('temperature') return (
- - °C is {temperature.view(x => +x * 9 / 5 + 32 )}°F + + °C is {temperature.view(x => (+x * 9) / 5 + 32)}°F
) } diff --git a/packages/examples/all/src/counter/index.tsx b/packages/examples/all/src/counter/index.tsx index 4516e73..2e9892b 100644 --- a/packages/examples/all/src/counter/index.tsx +++ b/packages/examples/all/src/counter/index.tsx @@ -15,20 +15,19 @@ namespace AppState { } } -const Counter = (props: { count: Atom }) => +const Counter = (props: { count: Atom }) => ( You have clicked this button {props.count} time(s). - - + +) -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (
Hello, world!
+) export default { Component: App, diff --git a/packages/examples/all/src/hello/index.tsx b/packages/examples/all/src/hello/index.tsx index 0418088..2c88bd2 100644 --- a/packages/examples/all/src/hello/index.tsx +++ b/packages/examples/all/src/hello/index.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { Atom, F, bind } from '@grammarly/focal' interface AppState { - firstName: string, + firstName: string lastName: string } @@ -18,18 +18,16 @@ const App = (props: { state: Atom }) => { const lastName = props.state.lens('lastName') return (
- First Name: + First Name:
- Last Name: + Last Name:
- { - Atom.combine( + {Atom.combine( firstName, lastName, (x, y) => x.length > 0 && y.length > 0 && `Hello, ${x} ${y}` - ) - } + )}
) diff --git a/packages/examples/all/src/http-search-github/index.tsx b/packages/examples/all/src/http-search-github/index.tsx index 2d85112..9347152 100644 --- a/packages/examples/all/src/http-search-github/index.tsx +++ b/packages/examples/all/src/http-search-github/index.tsx @@ -7,9 +7,17 @@ enum ResultKind { InProgress } -interface Success { kind: ResultKind.Success, value: T } -interface Failure { kind: ResultKind.Failure, error: any } -interface InProgress { kind: ResultKind.InProgress } +interface Success { + kind: ResultKind.Success + value: T +} +interface Failure { + kind: ResultKind.Failure + error: any +} +interface InProgress { + kind: ResultKind.InProgress +} type Result = Success | Failure | InProgress @@ -33,7 +41,7 @@ namespace Result { interface AppState { searchString: string - result: undefined | Result<{ url: string, name: string }[]> + result: undefined | Result<{ url: string; name: string }[]> } namespace AppState { @@ -48,70 +56,64 @@ function runSearch(result: Atom, query: str fetch(`https://api.github.com/search/repositories?q=${query}`) .then(res => - res.json() - .then(json => res.status === 200 - ? Promise.resolve(json) - : Promise.reject(json.message))) + res + .json() + .then(json => (res.status === 200 ? Promise.resolve(json) : Promise.reject(json.message))) + ) .then((json: any) => json.items.map((repo: any) => ({ url: repo.html_url, name: repo.full_name - }))) + })) + ) .then(r => result.set(Result.success(r))) .catch(err => result.set(Result.failure(err))) } export const App = (props: { state: Atom }) => { const result = props.state.lens('result') - const repos = result.view(x => x && x.kind === ResultKind.Success ? x.value : []) + const repos = result.view(x => (x && x.kind === ResultKind.Success ? x.value : [])) return (
- + runSearch(result, props.state.get().searchString)} />
- { - result.view(r => { - if (r === undefined) { - return 'Enter a search query and click "Search"' - } else { - switch (r.kind) { - case ResultKind.InProgress: - return 'Searching...' - - case ResultKind.Failure: - return `Error: ${r.error}` - - case ResultKind.Success: - return 'Search results:' - - default: - const _: never = r - return undefined - } + {result.view(r => { + if (r === undefined) { + return 'Enter a search query and click "Search"' + } else { + switch (r.kind) { + case ResultKind.InProgress: + return 'Searching...' + + case ResultKind.Failure: + return `Error: ${r.error}` + + case ResultKind.Success: + return 'Search results:' + + default: + const _: never = r + return undefined } - }) - } + } + })} - { - reactiveList( - repos.view(x => x.map((_, index) => index)), - index => ( -
  • - x[index] && x[index].url)}> - {repos.view(x => x[index] && x[index].name)} - -
  • - ) - ) - } + {reactiveList(repos.view(x => x.map((_, index) => index)), index => ( +
  • + x[index] && x[index].url)}> + {repos.view(x => x[index] && x[index].name)} + +
  • + ))}
    ) diff --git a/packages/examples/all/src/increment-number/index.tsx b/packages/examples/all/src/increment-number/index.tsx index 0cddff8..a203ee4 100644 --- a/packages/examples/all/src/increment-number/index.tsx +++ b/packages/examples/all/src/increment-number/index.tsx @@ -13,52 +13,56 @@ namespace AppState { } } -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (
    - { - props.state.view(x => - props.state.set({ isRunning: !x.isRunning })} - /> - ) - } + {props.state.view(x => ( + props.state.set({ isRunning: !x.isRunning })} + /> + ))}

    with Observable.combineLatest

    - { - combineLatest( - interval(1000).pipe(startWith(0), mapTo(1)), - props.state.view('isRunning') - ).pipe( - scan<[number, boolean], number>( - (acc, [val, shouldIncrement]) => shouldIncrement ? acc + val : acc, 0 - ) + {combineLatest( + interval(1000).pipe( + startWith(0), + mapTo(1) + ), + props.state.view('isRunning') + ).pipe( + scan<[number, boolean], number>( + (acc, [val, shouldIncrement]) => (shouldIncrement ? acc + val : acc), + 0 ) - } + )}

    with Observable.switchMap

    - { - interval(1000).pipe( - startWith(0), - switchMap(() => props.state.view(x => x.isRunning ? 1 : 0)), - scan((acc, val) => acc + val, 0) - ) - } + {interval(1000).pipe( + startWith(0), + switchMap(() => props.state.view(x => (x.isRunning ? 1 : 0))), + scan((acc, val) => acc + val, 0) + )}

    Atom.switchMap Observable

    - { - props.state.pipe( - switchMap(x => x.isRunning ? interval(1000).pipe(startWith(0), mapTo(1)) : of(0)), - scan((acc, val) => acc + val, 0) - ) - } + {props.state.pipe( + switchMap(x => + x.isRunning + ? interval(1000).pipe( + startWith(0), + mapTo(1) + ) + : of(0) + ), + scan((acc, val) => acc + val, 0) + )}
    +) export default { Component: App, diff --git a/packages/examples/all/src/index.tsx b/packages/examples/all/src/index.tsx index badf8a6..f0fcdcd 100644 --- a/packages/examples/all/src/index.tsx +++ b/packages/examples/all/src/index.tsx @@ -16,12 +16,11 @@ appState.subscribe(s => { }) // inject app state in global for debugging -; (window as any).appState = appState +;(window as any).appState = appState function startApp(C: typeof App.AppComponent) { const targetEl = document.getElementById('app') - if (targetEl == null) - throw new Error('React app target element not found. Wrong HTML file?') + if (targetEl == null) throw new Error('React app target element not found. Wrong HTML file?') ReactDOM.render(, targetEl) } diff --git a/packages/examples/all/src/input/index.tsx b/packages/examples/all/src/input/index.tsx index b36cfa1..bd4aaef 100644 --- a/packages/examples/all/src/input/index.tsx +++ b/packages/examples/all/src/input/index.tsx @@ -11,11 +11,12 @@ namespace AppState { } } -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (
    - + {props.state.view('entry')}
    +) export default { Component: App, diff --git a/packages/examples/all/src/list-search/index.tsx b/packages/examples/all/src/list-search/index.tsx index 560ade4..da6200c 100644 --- a/packages/examples/all/src/list-search/index.tsx +++ b/packages/examples/all/src/list-search/index.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { interval, Subscription } from 'rxjs' +import { interval, Subscription } from 'rxjs' import { Atom, F, bind } from '@grammarly/focal' const defaultSearchList = Object.keys(window) @@ -11,8 +11,8 @@ function getRandomSearchList() { } interface AppState { - searchString: string, - searchList: string[], + searchString: string + searchList: string[] timer: number } @@ -24,20 +24,18 @@ namespace AppState { } } -class App extends React.Component<{ state: Atom }, {}> { +class App extends React.Component<{ state: Atom }, {}> { private _subscription: Subscription componentDidMount() { const { state } = this.props const timer = state.lens('timer') - this._subscription = interval(1000) - .subscribe(_ => { - state.lens('timer').modify(x => x === 0 ? 5 : x - 1) + this._subscription = interval(1000).subscribe(_ => { + state.lens('timer').modify(x => (x === 0 ? 5 : x - 1)) - if (timer.get() === 5) - state.lens('searchList').set(getRandomSearchList()) - }) + if (timer.get() === 5) state.lens('searchList').set(getRandomSearchList()) + }) } componentWillUnmount() { @@ -53,17 +51,15 @@ class App extends React.Component<{ state: Atom }, {}> { Timer: {state.view('timer')} - { - Atom.combine(search, state.lens('searchList'), (searchValue, list) => ( -
    - { - list - .filter(x => x.toLowerCase().includes(searchValue.toLowerCase())) - .map((x, i) =>
  • {x}
  • ) - } -
    - )) - } + {Atom.combine(search, state.lens('searchList'), (searchValue, list) => ( +
    + {list + .filter(x => x.toLowerCase().includes(searchValue.toLowerCase())) + .map((x, i) => ( +
  • {x}
  • + ))} +
    + ))}
    ) diff --git a/packages/examples/all/src/player/index.tsx b/packages/examples/all/src/player/index.tsx index f4195ff..2eba1c7 100644 --- a/packages/examples/all/src/player/index.tsx +++ b/packages/examples/all/src/player/index.tsx @@ -5,14 +5,14 @@ import { Status, AudioModel, AppState, audioSrc, defaultState } from './model' const Volume = ({ volume }: { volume: Atom }) => (
    x >= 10)} onClick={() => volume.modify(x => x + 1)} /> x <= 0)} onClick={() => volume.modify(x => x - 1)} /> @@ -22,12 +22,9 @@ const Volume = ({ volume }: { volume: Atom }) => ( const Play = ({ status }: { status: Atom }) => ( (x === Status.playing ? 'Stop' : 'Play'))} - onClick={() => - status.modify( - x => (x === Status.playing ? Status.pause : Status.playing) - )} + onClick={() => status.modify(x => (x === Status.playing ? Status.pause : Status.playing))} /> ) @@ -35,16 +32,11 @@ const TimeLine = ({ currentTime, maxDuration }: { - currentTime: Atom, + currentTime: Atom maxDuration: ReadOnlyAtom }) => (
    - + {currentTime}s
    ) @@ -64,10 +56,7 @@ class App extends React.Component<{ state: Atom }, {}> { const { state } = this.props return (
    - +
    diff --git a/packages/examples/all/src/player/model.ts b/packages/examples/all/src/player/model.ts index 952f8e9..6afd1d7 100644 --- a/packages/examples/all/src/player/model.ts +++ b/packages/examples/all/src/player/model.ts @@ -34,20 +34,13 @@ export class AudioModel { constructor(atom: Atom, audioSrc: string) { this.audio = new Audio(audioSrc) - this.durationSubscription = fromEvent(this.audio, 'canplaythrough') - .subscribe(() => - atom.lens('maxDuration').set(this.audio.duration) - ) + this.durationSubscription = fromEvent(this.audio, 'canplaythrough').subscribe(() => + atom.lens('maxDuration').set(this.audio.duration) + ) this.timeSubscription = atom .view(x => x.status) - .pipe( - switchMap(x => - x === Status.playing - ? interval(1000).pipe(mapTo(1)) - : of(0) - ) - ) + .pipe(switchMap(x => (x === Status.playing ? interval(1000).pipe(mapTo(1)) : of(0)))) .subscribe(x => atom.modify(y => { const newCurTime = parseInt(y.currentTime, 10) + x @@ -67,17 +60,13 @@ export class AudioModel { this.statusSubscription = atom .view(x => x.status) - .subscribe( - x => x === Status.playing ? this.audio.play() : this.audio.pause() - ) + .subscribe(x => (x === Status.playing ? this.audio.play() : this.audio.pause())) - this.volumeSubscription = atom - .lens('volume') - .subscribe(x => this.audio.volume = x / 10) + this.volumeSubscription = atom.lens('volume').subscribe(x => (this.audio.volume = x / 10)) this.currentTimeSubscription = atom .lens('currentTime') - .subscribe(x => this.audio.currentTime = parseInt(x, 10)) + .subscribe(x => (this.audio.currentTime = parseInt(x, 10))) } unsubscribe() { diff --git a/packages/examples/all/src/scroll/index.tsx b/packages/examples/all/src/scroll/index.tsx index 473f514..87b6895 100644 --- a/packages/examples/all/src/scroll/index.tsx +++ b/packages/examples/all/src/scroll/index.tsx @@ -37,15 +37,13 @@ const pattern = ` . : :.:::::::.: :. ` -const Scroller = (props: { scrollTop: Atom, scrollLeft: Atom }) => +const Scroller = (props: { scrollTop: Atom; scrollLeft: Atom }) => (
    , scrollLeft: Atom }) >
    {pattern}
    +) -const ScrollInput = (props: { value: Atom, label: string }) => +const ScrollInput = (props: { value: Atom; label: string }) => (
    +) const App = (props: { state: Atom }) => { const scrollTop = props.state.lens('scrollTop') @@ -75,8 +75,8 @@ const App = (props: { state: Atom }) => {
    - - + +
    ) diff --git a/packages/examples/all/src/slider/index.tsx b/packages/examples/all/src/slider/index.tsx index b06dbbb..a4ff5fa 100644 --- a/packages/examples/all/src/slider/index.tsx +++ b/packages/examples/all/src/slider/index.tsx @@ -11,11 +11,12 @@ namespace AppState { } } -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (
    - + Value: {props.state.view('range')}
    +) export default { Component: App, diff --git a/packages/examples/all/src/timer/index.tsx b/packages/examples/all/src/timer/index.tsx index e25e3cc..6094499 100644 --- a/packages/examples/all/src/timer/index.tsx +++ b/packages/examples/all/src/timer/index.tsx @@ -10,14 +10,14 @@ enum Status { } interface Time { - seconds: number, - milliseconds: number, + seconds: number + milliseconds: number minutes: number } interface AppState { - time: Time, - laps: Time[], + time: Time + laps: Time[] status: Status } @@ -59,19 +59,15 @@ function updateTime(time: Time, val: number) { return { milliseconds, seconds, minutes } } -const Laps = ({ laps }: { laps: ReadOnlyAtom }) => +const Laps = ({ laps }: { laps: ReadOnlyAtom }) => ( - { - reactiveList( - laps.view(x => x.map((_, index) => index)), - index => ( - - {laps.view(x => x[index] ? 'Lap ' + (index + 1) + ': ' + getTime(x[index]) : '')} - - ) - ) - } + {reactiveList(laps.view(x => x.map((_, index) => index)), index => ( + + {laps.view(x => (x[index] ? 'Lap ' + (index + 1) + ': ' + getTime(x[index]) : ''))} + + ))} +) class App extends React.Component<{ state: Atom }, {}> { private _subscription: Subscription @@ -80,9 +76,10 @@ class App extends React.Component<{ state: Atom }, {}> { const status = this.props.state.view('status') this._subscription = combineLatest( - status.pipe(switchMap(x => x === Status.STARTED ? interval(1).pipe(mapTo(1)) : of(0))), - status - ).pipe( + status.pipe(switchMap(x => (x === Status.STARTED ? interval(1).pipe(mapTo(1)) : of(0)))), + status + ) + .pipe( scan<[number, Status], Time>( (time, [val, status]) => status === Status.RESET ? defaultTimeState : updateTime(time, val), @@ -108,45 +105,45 @@ class App extends React.Component<{ state: Atom }, {}> { {time.view(getTime)}

    - { - isStarted.view(x => - x - ? status.set(Status.STOPPED)} - /> - : status.set(Status.STARTED)} - /> - ) - } - { - status.view(x => - x === Status.STOPPED && + {isStarted.view(x => + x ? ( state.set(AppState.defaultState)} + key="stop" + type="submit" + value="Stop" + onClick={() => status.set(Status.STOPPED)} /> - ) - } - { - isStarted.view(x => - x && + ) : ( state.modify(x => ({ ...x, laps: [...x.laps, x.time] }))} + key="start" + type="submit" + value="Start" + onClick={() => status.set(Status.STARTED)} /> ) - } + )} + {status.view( + x => + x === Status.STOPPED && ( + state.set(AppState.defaultState)} + /> + ) + )} + {isStarted.view( + x => + x && ( + state.modify(x => ({ ...x, laps: [...x.laps, x.time] }))} + /> + ) + )} diff --git a/packages/examples/all/src/todos-with-undo/index.tsx b/packages/examples/all/src/todos-with-undo/index.tsx index b798ec5..02c5d4f 100644 --- a/packages/examples/all/src/todos-with-undo/index.tsx +++ b/packages/examples/all/src/todos-with-undo/index.tsx @@ -4,7 +4,7 @@ import { App as TodoApp } from '../todos' import { History, HistoryState } from '../utils/history' import * as TodosModel from '../todos/model' -interface AppState extends HistoryState { } +interface AppState extends HistoryState {} const App = (props: { state: Atom }) => { const history = History.create(props.state) @@ -13,14 +13,14 @@ const App = (props: { state: Atom }) => {
    history.undo()} /> history.redo()} /> diff --git a/packages/examples/all/src/todos/index.tsx b/packages/examples/all/src/todos/index.tsx index 076ba27..b049831 100644 --- a/packages/examples/all/src/todos/index.tsx +++ b/packages/examples/all/src/todos/index.tsx @@ -1,17 +1,23 @@ import * as React from 'react' import { Atom, F, Lens, bind, reactiveList } from '@grammarly/focal' import { - AppState, TodoState, - defaultAppState, defaultTodoState, - addTodo, toggleTodo, filterTodos, - SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED + AppState, + TodoState, + defaultAppState, + defaultTodoState, + addTodo, + toggleTodo, + filterTodos, + SHOW_ALL, + SHOW_ACTIVE, + SHOW_COMPLETED } from './model' -const Todo = (props: { id: string, todo: Atom }) => { +const Todo = (props: { id: string; todo: Atom }) => { const { todo, id } = props const todoStyle = { cursor: 'pointer', - textDecoration: todo.view(x => x.completed ? 'line-through' : 'none') + textDecoration: todo.view(x => (x.completed ? 'line-through' : 'none')) } return ( todo.modify(toggleTodo)}> @@ -24,50 +30,42 @@ const TodoList = (props: { state: Atom }) => { const todos = props.state.lens('todos') return ( - { - reactiveList( - props.state.view(x => filterTodos(x.todos, x.filter)), - id => { - const todo = todos - .lens(Lens.key(id)) - .lens(Lens.withDefault(defaultTodoState)) - return - } - ) - } + {reactiveList(props.state.view(x => filterTodos(x.todos, x.filter)), id => { + const todo = todos.lens(Lens.key(id)).lens(Lens.withDefault(defaultTodoState)) + return + })} ) } const Footer = (props: { filter: Atom }) => (
    - { - [SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(value => ( - { - e.preventDefault() - props.filter.set(value) - }} - > - {value.replace('_', ' ')} - - )) - } + {[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(value => ( + { + e.preventDefault() + props.filter.set(value) + }} + > + {value.replace('_', ' ')} + + ))}
    ) -export const App = (props: { state: Atom }) => +export const App = (props: { state: Atom }) => (
    - - props.state.modify(addTodo)} /> + + props.state.modify(addTodo)} />
    +) export default { Component: App, diff --git a/packages/examples/all/src/todos/model.ts b/packages/examples/all/src/todos/model.ts index 099a5a0..49d0d5c 100644 --- a/packages/examples/all/src/todos/model.ts +++ b/packages/examples/all/src/todos/model.ts @@ -1,5 +1,5 @@ export interface TodoState { - value: string, + value: string completed: boolean } @@ -11,14 +11,13 @@ export const SHOW_ALL = 'SHOW_ALL' export const SHOW_ACTIVE = 'SHOW_ACTIVE' export const SHOW_COMPLETED = 'SHOW_COMPLETED' -export type ShowMode = - typeof SHOW_ALL | typeof SHOW_ACTIVE | typeof SHOW_COMPLETED +export type ShowMode = typeof SHOW_ALL | typeof SHOW_ACTIVE | typeof SHOW_COMPLETED export interface AppState { - todos: TodoListState, - value: string, - filter: ShowMode, - nextTodoId: number + todos: TodoListState + value: string + filter: ShowMode + nextTodoId: number } export const defaultAppState: AppState = { diff --git a/packages/examples/all/src/tree/index.tsx b/packages/examples/all/src/tree/index.tsx index 0cb19fb..f78e3d1 100644 --- a/packages/examples/all/src/tree/index.tsx +++ b/packages/examples/all/src/tree/index.tsx @@ -1,61 +1,53 @@ import * as React from 'react' import { Atom, F, Lens, reactiveList } from '@grammarly/focal' -import { - defaultAppState, defaultNodeState, - AppState, NodeState -} from './model' +import { defaultAppState, defaultNodeState, AppState, NodeState } from './model' -const Counter = (props: { value: Atom }) => +const Counter = (props: { value: Atom }) => (
    Counter {props.value} - props.value.modify(x => x + 1)} /> - props.value.modify(x => x - 1)} /> + props.value.modify(x => x + 1)} /> + props.value.modify(x => x - 1)} />
    +) -const Node = (props: { state: Atom, removeNode?(): void }): JSX.Element => { +const Node = (props: { state: Atom; removeNode?(): void }): JSX.Element => { const children = props.state.lens('children') return (
    - { - props.removeNode && + {props.removeNode && ( props.removeNode && props.removeNode()} /> - } + )} - { - reactiveList( - children.view(x => x.map((_, index) => index)), - index => ( -
  • - (index)) - .lens(Lens.withDefault(defaultNodeState)) - } - removeNode={() => children.modify(x => x.filter((_, i) => i !== index))} - /> -
  • - ) - ) - } + {reactiveList(children.view(x => x.map((_, index) => index)), index => ( +
  • + (index)) + .lens(Lens.withDefault(defaultNodeState))} + removeNode={() => children.modify(x => x.filter((_, i) => i !== index))} + /> +
  • + ))}
    props.state.lens('children').modify(x => [...x, defaultNodeState])} />
    ) } -const App = (props: { state: Atom }) => +const App = (props: { state: Atom }) => (
    +) export default { Component: App, diff --git a/packages/examples/all/src/tree/model.ts b/packages/examples/all/src/tree/model.ts index 4b11013..9835540 100644 --- a/packages/examples/all/src/tree/model.ts +++ b/packages/examples/all/src/tree/model.ts @@ -1,10 +1,10 @@ export interface NodeState { - value: number, + value: number children: NodeState[] } export interface AppState { - tree: NodeState + tree: NodeState } export const defaultNodeState = { diff --git a/packages/examples/all/src/update-number/index.tsx b/packages/examples/all/src/update-number/index.tsx index 436afcf..0c9cc9e 100644 --- a/packages/examples/all/src/update-number/index.tsx +++ b/packages/examples/all/src/update-number/index.tsx @@ -13,7 +13,7 @@ import * as React from 'react' import { Atom, F, bind } from '@grammarly/focal' interface AppState { - inputValue: string, + inputValue: string value: number } @@ -50,33 +50,29 @@ const App = (props: { state: Atom }) => { const value = props.state.view('value') return (
    - + props.state.modify(x => ({ ...x, value: parseInt(x.inputValue, 10) }))} />

    Observable.scan

    - { - value.pipe( - scan(([_, curr], val) => [curr, val], [0, value.get()]), - switchMap(getUpdateNumberObservable) - ) - } + {value.pipe( + scan(([_, curr], val) => [curr, val], [0, value.get()]), + switchMap(getUpdateNumberObservable) + )}

    Observable.bufferCount

    - { - value.pipe( - bufferCount(2, 1), - switchMap(getUpdateNumberObservable), - merge(value.pipe(first())) - ) - } + {value.pipe( + bufferCount(2, 1), + switchMap(getUpdateNumberObservable), + merge(value.pipe(first())) + )}
    diff --git a/packages/examples/all/src/utils/history.ts b/packages/examples/all/src/utils/history.ts index 94f9048..f466f75 100644 --- a/packages/examples/all/src/utils/history.ts +++ b/packages/examples/all/src/utils/history.ts @@ -39,9 +39,7 @@ export namespace History { return { position: newPosition, - history: s.history - .slice(0, newPosition) - .concat([v]) + history: s.history.slice(0, newPosition).concat([v]) } } ) @@ -50,12 +48,10 @@ export namespace History { canRedo: redoCount.view(x => x === 0), undo() { - if (position.get() > 0) - position.modify(x => x - 1) + if (position.get() > 0) position.modify(x => x - 1) }, redo() { - if (redoCount.get() > 0) - position.modify(x => x + 1) + if (redoCount.get() > 0) position.modify(x => x + 1) } } } diff --git a/packages/examples/all/tslint.json b/packages/examples/all/tslint.json index afb3ca4..447a569 100644 --- a/packages/examples/all/tslint.json +++ b/packages/examples/all/tslint.json @@ -1,3 +1,12 @@ { - "extends": "@grammarly/tslint-config" + "extends": "@grammarly/tslint-config", + "rules": { + "whitespace": [false], + "quotemark": [ + true, + "single", + "jsx-double", + "avoid-escape" + ] + } } \ No newline at end of file diff --git a/packages/examples/todomvc/src/app.tsx b/packages/examples/todomvc/src/app.tsx index 8bdd480..43f70d3 100644 --- a/packages/examples/todomvc/src/app.tsx +++ b/packages/examples/todomvc/src/app.tsx @@ -28,35 +28,29 @@ interface TodoProps { remove(): void } -const Todo = ({ todo, editing, remove }: TodoProps) => +const Todo = ({ todo, editing, remove }: TodoProps) => ( x && x.completed && 'completed'), - editing.view(x => x && 'editing')) - } + {...classes(todo.view(x => x && x.completed && 'completed'), editing.view(x => x && 'editing'))} > -