├── .gitignore ├── DevEnvironmentSetup.md ├── Logic └── README.md ├── README.md ├── ReactComponents ├── README.md └── spinners-examples.png ├── SyntaxHighlighting └── README.md ├── WhyNotViews └── README.md ├── automated-ui-testing.js ├── handyCommands.md ├── images ├── Tools.jpg └── ViewsToolsOverview.png ├── knownIssues.md └── map-fonts.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | # views 24 | **/*.data.js 25 | **/*.view.js 26 | **/*.view.css 27 | **/*.view.tests.js 28 | -------------------------------------------------------------------------------- /DevEnvironmentSetup.md: -------------------------------------------------------------------------------- 1 | # Mac 2 | 3 | ## If you don't have any development environment setup: 4 | 5 | 1. Update (or download) Xcode in Software Update section in AppStore app 6 | 2. Open Xcode and follow installation steps to get all the modules (we need iOS simulator) 7 | 3. Open the terminal (in Applications/Utilities) and run the following commands: 8 | 4. Install homebrew to manage packages (select both lines, it's one command) 9 | `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"` 10 | 5. Install n to manage node 11 | `curl -L https://git.io/n-install | bash` 12 | 6. Install watchman for react native 13 | `brew install watchman` 14 | 7. Install git to manage code 15 | `brew install git` 16 | 8. Install yarn to manage node npm packages 17 | `curl -o- -L https://yarnpkg.com/install.sh | bash` 18 | 9. Install expo-cli to manage react native apps 19 | `yarn global add expo-cli` 20 | 21 | ## If you want to create new React DOM project: 22 | 1. Create your project using `yarn create react-app my-app`. Change `my-app` to whatever it's called. 23 | 2. Go to the project directory, type `cd ` (with space after cd) and drag project folder on the terminal window 24 | 3. Run `yarn create views-app` to enable Views on your project 25 | 4. Ensure all dependencies are installed `yarn` 26 | 5. To start the project run `yarn start` - the project will open in the browser 27 | 28 | ## If you want to create new React Native project: 29 | 1. Create your project using `yarn create react-native-app my-app` 30 | 2. Go to the project directory, type `cd ` (with space after cd) and drag project folder on the terminal window 31 | 3. Run `yarn create views-app` to enable Views on your project 32 | 4. To start project run yarn start - you will get a QR code and URL. You can scan the code with your mobile or copy the code 33 | 5. In a new tab (CMD+t) run expo ios - it will open the simulator 34 | 6. Swipe to the right on the simulator screen and open Expo app 35 | 7. Click on the clipboard shortcut in Terminal and your project will open 36 | 8. Run `expo client:install:ios` to install the Expo app into the iOS simulator. You might need to run `sudo xcode-select -s /Applications/Xcode.app` before. 37 | 38 | # Windows for React DOM 39 | 40 | 1. Download NodeJS from https://nodejs.org/en/ and install it. 41 | 2. Download and install Yarn https://yarnpkg.com/lang/en/docs/install/. 42 | 3. Download and install GIT https://gist.github.com/derhuerst/1b15ff4652a867391f03#file-windows-md. 43 | 4. Open the terminal (in Start/Program Files/Accessories/Command Prompt). 44 | 5. Go to the project directory, type `cd` (yes, with space after cd) and drag project folder on the terminal window 45 | 6. You might be missing dependencies, so run `yarn` 46 | 7. To start the project run `yarn start` - if it's a web project it will open in the browser 47 | 8. [Enable long file paths](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#enable-long-paths-in-windows-10-version-1607-and-later) 48 | 49 | # Linter and syntax highlighing 50 | 51 | 1. You can get the syntax highlighting for Vim, Atom, VSCode, Sublime here [Syntax Highlighting](SyntaxHighlighting/README.md) 52 | 2. We've kicked off a basic linter for the Views syntax. I covers few most important use cases for now and we will be adding more in time. You can get it here [Views Linter](https://marketplace.visualstudio.com/items?itemName=uxtemple.views-lsp) 53 | 54 | Happy building! You are all set. 55 | Reach out with questions via Github Issues [Github Issues](https://github.com/viewstools/docs/issues). 56 | Mention `@tombrewsviews` or `@dariocravero` to make sure that we get your notifications. 57 | -------------------------------------------------------------------------------- /Logic/README.md: -------------------------------------------------------------------------------- 1 | # Custom logic 2 | 3 | When Views Morpher runs morphs a `.view` file, it automatically creates `.view.js` file 4 | next to it. While it might be tempting to edit that file and add your own logic, 5 | you will find that Views Morpher will override your changes every time it runs! 6 | 7 | So, how do you add your own custom logic to a `.view` file? With a `.view.logic.js` file. 8 | 9 | With `Counter.view` file like: 10 | 11 | ``` 12 | Text 13 | color black 14 | text { 25 | let [number, setNumber] = useState(0) 26 | 27 | useEffect(() => { 28 | let interval = setInterval(() => setNumber(number + 1), 1000) 29 | return () => clearInterval(interval) 30 | }) 31 | 32 | return 33 | } 34 | export default CounterLogic 35 | ``` 36 | 37 | A logic file is an extension to your view and Views imports that it if it's 38 | available. In other words, if you then use your `Counter` in other views, you 39 | will actually be using the `Counter.view.logic.js` instead. 40 | 41 | This is where you would add any intermediate state to your views or connect them 42 | to an external store of data. 43 | 44 | *It's important to note that logic files are expected to import the `.view.js` file 45 | and export a new component that adds extra logic to it.* 46 | 47 | When you have a logic file for a view, Views Morpher will automatically import that file 48 | instead of the raw view. Eg, say you have a `Button.view` and add a `Button.view.logic.js`. 49 | When you use `Button` in another view like `App`, the logic file would be imported instead. 50 | 51 | ## Example on showing a list data coming from a service 52 | 53 | We would use logic files for that containing the logic to fetch external data. 54 | 55 | Say you have a `Posts.view` like: 56 | ``` 57 | Posts List 58 | from < 59 | Post 60 | ``` 61 | 62 | And another one `Post.view` (to show the actual post): 63 | ``` 64 | Post Vertical 65 | Text 66 | text { 79 | fetch('https://jsonplaceholder.typicode.com/posts') 80 | .then(res => res.json()) 81 | .then(setPosts) 82 | }, []) 83 | 84 | return <Posts {...props} from={posts} /> 85 | } 86 | ``` 87 | 88 | So say you’re then using the `Posts` in `App.view` like: 89 | ``` 90 | App Vertical 91 | Posts 92 | ``` 93 | 94 | Views Morpher will recognise the logic file automatically for you and use that instead of the view. 95 | 96 | In Views Tools you will see three elements rendered on the list with placeholder data on it so you can design it. 97 | 98 | ## Hover Manual 99 | Hover manual will force the `hover` state on the companent. Use when you want to show a block on hover. 100 | 101 | ``` 102 | import useHoveredManual from 'useHoveredManual.js' 103 | 104 | export default function DataAutoCentredLogic(props) { 105 | let hoveredManual = useHoveredManual() 106 | 107 | return <DataAutoCentred {...props} {...hoveredManual} /> 108 | } 109 | ``` 110 | [See useHoverManual](../useHoveredManual.js) 111 | 112 | Reach out with questions via Github Issues [Github Issues](https://github.com/viewstools/docs/issues). 113 | Mention `@tombrewsviews` or `@dariocravero` to make sure that we get your notifications. 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Make beautiful software! 2 | # Obsess about every detail! 3 | # Upskill on the job! 4 | 5 | ## Views is a creative toolset for professional product teams 6 | 7 | * Views Framework is our response to the complexities and silos we see and experience every day in the software delivery process 8 | * We're trying to simplify development to make it more inclusive for non-developers 9 | * We achieve simplicity through smart automation and carefully designed creative idioms and constraints 10 | * We noticed that when designers and product owners have access to the code-base the quality of the final solution is higher 11 | * Better collaboration enables knowledge transfer, designers become better at engineering and engineers better at design 12 | * Faster delivery and shared responibility lower the risk of faluire 13 | 14 | We don't have everything figured out yet, but [the things we've done](https://design.views.tools/) up to now give us high confidence towards the future. 15 | 16 | ## Direct benefits of Views 17 | 18 | * Consistent and predictable state,  19 | * Easy front-end code for everyone on the team to contribute to, 20 | * Separation of concerns between layout and logic, 21 | * Full engineering flexibility, 22 | * Portable patterns. 23 | 24 | ## Start 25 | 26 | To get your free public beta version of Views Tools fill out this [Typeform](https://tom734512.typeform.com/to/yyz1Ja). 27 | 28 | Full documentation is included in Views Tools. ![Views Tools](/images/Tools.jpg) 29 | 30 | ## More info 31 | 32 | * [Website](https://design.views.tools/) 33 | * [Medium Publication](https://medium.com/viewstools) 34 | * [Youtube Channel](https://www.youtube.com/watch?v=HyLWk813xYw) 35 | 36 | ## 😍 How can you help? 37 | 38 | [Guidelines on contributions to the Morphers](https://github.com/viewstools/morph/blob/master/CONTRIBUTING.md) 39 | 40 | ### Thank you to our main contributors ⊃━☆゚. \* ・ 。゚ 41 | 42 | Amy https://github.com/amymc</br> 43 | Dario https://github.com/dariocravero</br> 44 | Larrisa https://github.com/callogerasl</br> 45 | Neil https://github.com/neil-buckley</br> 46 | Tom https://github.com/tomatuxtemple</br></br> 47 | 48 | License BSD-3-Clause.<br> 49 | Copyright 2017 by UXtemple Ltd. 50 | -------------------------------------------------------------------------------- /ReactComponents/README.md: -------------------------------------------------------------------------------- 1 | # Views and your React components 2 | 3 | If you want to use an existing React component as is, you can import it by 4 | defining it as a `.js` file and adding a `// @view` pragma at the top. 5 | 6 | ## Integration pattern 7 | 8 | E.g., say you have a file called `Magic.js`: 9 | 10 | ``` 11 | // @view 12 | import React from 'react' 13 | 14 | export default props => props.word 15 | ``` 16 | 17 | You can now use that component as you would use any other view in your system: 18 | 19 | ``` 20 | Magic 21 | word hey! 22 | ``` 23 | 24 | We'll be working on bridging NPM modules at some stage, but for now you can 25 | bridge any external modules by hand. 26 | 27 | ## Google Maps integration 28 | 29 | Here's an example with 30 | [react-google-maps](https://tomchentw.github.io/react-google-maps/). 31 | 32 | `GoogleMap.js` 33 | 34 | ``` 35 | // @view 36 | import { GoogleMap } from 'react-google-maps' 37 | export default ({ defaultCenterLat: lat, defaultCenterLng: lng, ...props }) => ( 38 | <GoogleMap defaultCenter={{ lat, lng }} {...props}> 39 | {props.children} 40 | </GoogleMap> 41 | ) 42 | ``` 43 | 44 | `Marker.js` 45 | 46 | ``` 47 | // @view 48 | import { Marker } from 'react-google-maps' 49 | export default ({ positionLat: lat, positionLng: lng, ...props }) => ( 50 | <Marker position={{ lat, lng }} {...props} /> 51 | ) 52 | ``` 53 | 54 | Then in your view, following [the example from their docs](https://tomchentw.github.io/react-google-maps/#usage--configuration): 55 | 56 | ```views 57 | GoogleMap 58 | defaultZoom 0 59 | defaultCenterLat -34.397 60 | defaultCenterLng 150.644 61 | Marker 62 | onWhen <isMarkerShown 63 | positionLat -34.397 64 | positionLng 150.644 65 | ``` 66 | 67 | You will notice that we've renamed certain props and flattened them out. In 68 | order to reduce the complexity of the language we made a decision not to allow 69 | complex objects in views as props for now. This isn't set in stone though, so if 70 | you feel strong about it, let us know and we can work to enable a syntax like: 71 | 72 | _Warning: The following is not real Views code_ 73 | ```views 74 | GoogleMap 75 | defaultZoom 0 76 | defaultCenter 77 | lat -34.397 78 | lng 150.644 79 | Marker 80 | onWhen <isMarkerShown 81 | position 82 | lat -34.397 83 | lng 150.644 84 | ``` 85 | 86 | ## Loaders 87 | 88 | Create `Spinner.js` in your project and add this code to it: 89 | 90 | ``` 91 | // @view 92 | import * as Spinners from 'react-spinners'; 93 | import React from 'react'; 94 | 95 | const Spinner = ({ type, ...props }) => { 96 | const Type = Spinners[type]; 97 | return <Type loading={true} {...props} />; 98 | }; 99 | Spinner.defaultProps = { 100 | type: 'ClipLoader', 101 | }; 102 | export default Spinner; 103 | ``` 104 | 105 | Views Morpher will auto-generate `Spinner.view.js` file for you. 106 | 107 | You can now use a `Spinner` block in any view on its own or together with setting a type of spinner: 108 | 109 | ``` 110 | Spinner 111 | type ClipLoader 112 | ``` 113 | 114 | Here's the link to NPM package with all [spinner options](https://www.npmjs.com/package/react-spinners) 115 | And here's the [demo page](http://www.davidhu.io/react-spinners/) 116 | 117 | ![Some spinners examples](spinners-examples.png) 118 | 119 | Customise your loader with additional properties: 120 | 121 | ``` 122 | Spinner 123 | color #ff8300 124 | height 4 125 | type BarLoader 126 | width 100 127 | ``` 128 | 129 | ## .view.fake 130 | 131 | When you add a `.js` view, our toolset will let you define a `.view.fake` file 132 | that mocks your `.js` component. A `.js` component like our `Magic` or 133 | `GoogleMap` components above are external and as such can't be modified in 134 | Views. However, it's handy to have some kind of representation of them in your 135 | preview. That's when the `.view.fake` comes into play. Take `GoogleMap` for 136 | example, it's fake view could look like: 137 | 138 | ``` 139 | GoogleMapFake Image 140 | source ./google-map-fake.jpg 141 | ``` 142 | 143 | Reach out with questions via Github Issues [Github Issues](https://github.com/viewstools/docs/issues). 144 | Mention `@tombrewsviews` or `@dariocravero` to make sure that we get your notifications. -------------------------------------------------------------------------------- /ReactComponents/spinners-examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viewstools/docs/cee417461658a3639f33b0208f074718d77b8c26/ReactComponents/spinners-examples.png -------------------------------------------------------------------------------- /SyntaxHighlighting/README.md: -------------------------------------------------------------------------------- 1 | # Syntax highlighting 2 | 3 | We’ve created the following packages to help you understand `.view` files better: 4 | 5 | * [Atom](http://atom.io/packages/language-views), 6 | * [Sublime](https://github.com/viewstools/syntax-sublime), 7 | * [VIM](https://github.com/viewstools/syntax-vim), and 8 | * [VSCode](https://marketplace.visualstudio.com/items?itemName=uxtemple.views). 9 | 10 | We also have a Language Server for VSCode that lints your Views code and warns 11 | you about issues. [Get it 12 | here](https://marketplace.visualstudio.com/items?itemName=uxtemple.views-lsp). 13 | 14 | We currently highlight block names, numbers, props, and strings. Our highlighters 15 | aren’t perfect, but they should get us started. Feel free to submit fixes and suggestions! 16 | 17 | If you’re using other editors and come up with a syntax highlighter for it, reach out, 18 | and we’ll add it to this list. 19 | 20 | Happy editing! 21 | 22 | Reach out with questions via Github Issues [Github Issues](https://github.com/viewstools/docs/issues). 23 | Mention `@tombrewsviews` or `@dariocravero` to make sure that we get your notifications. 24 | -------------------------------------------------------------------------------- /WhyNotViews/README.md: -------------------------------------------------------------------------------- 1 | # Why not Views? 2 | 3 | ## I use different target platform 4 | 5 | Views morphs to React and React Native. If you use Vue, Angular, or any other 6 | front-end library that Views doesn't currently morph to you will have to write 7 | new morpher, and we can guide you through the process. 8 | 9 | ## I need more integration with my current code editor 10 | 11 | The current level of integration in the code editors stops at syntax highlighting 12 | for Views language. 13 | 14 | You can contribute to linting for Views [Contribute here](https://github.com/viewstools/morph/issues/19). 15 | 16 | Your logic files will work with your current linting tools, because they are Javascript. 17 | 18 | ## I want to use Views Tools for React Native app 19 | 20 | The morphing process is not yet 100% accurate between React Web and React Native, 21 | and results in some discrepancies between what you see in the Tools and in the app running 22 | on a real device, or iOS simulator. [Contribute here](https://github.com/viewstools/morph/issues/47) 23 | 24 | ## I want to mix logic and interface styling in the same component 25 | 26 | Views has a specific pattern to separate logic from interface composition. 27 | We do that because the interface changes more often than the logic. That means, 28 | once the `slots` (props) contract is set, the changes in design are decoupled from the logic, 29 | and both teams can work in parallel. 30 | 31 | ## ~~My designers want to control animations~~ 32 | > This is already implemented in the v14 of the morpher and tools! 🎉 33 | 34 | ~~We are working on cross-platform animations in the Views language [Contribute here](https://github.com/viewstools/morph/issues/11))~~ 35 | 36 | ~~Fully custom animations are always possible with Javascript integration of 37 | React Animated, or any in React Web projects transforms and transitions are possible today.~~ 38 | 39 | 40 | Reach out with questions via Github Issues [Github Issues](https://github.com/viewstools/docs/issues). 41 | Mention `@tombrewsviews` or `@dariocravero` to make sure that we get your notifications. -------------------------------------------------------------------------------- /automated-ui-testing.js: -------------------------------------------------------------------------------- 1 | import { morph } from 'views-morph' 2 | import { shallow } from 'enzyme' 3 | import fs from 'fs' 4 | import globule from 'globule' 5 | import toJson from 'enzyme-to-json' 6 | import path from 'path' 7 | import React from 'react' 8 | 9 | const paths = globule.find('**/*.view', { 10 | filter: f => !/node_modules/.test(f), 11 | }) 12 | 13 | paths.forEach(fileRaw => { 14 | const file = path.join(process.cwd(), fileRaw) 15 | const viewName = path.basename(file, '.view') 16 | 17 | try { 18 | const View = require(`${file}.js`).default 19 | 20 | const testsFile = `${file}.tests` 21 | const code = morph(fs.readFileSync(testsFile, 'utf8'), { 22 | as: 'tests', 23 | name: `${viewName}.view.tests`, 24 | file: { 25 | raw: testsFile, 26 | }, 27 | }).code.replace(/export const /g, 'out.') 28 | 29 | const out = {} 30 | // eslint-disable-next 31 | new Function('out', code)(out) 32 | const tests = out.make(() => {}) 33 | 34 | for (let test in tests) { 35 | if (test === '_main') continue 36 | 37 | it(`${viewName} (${test})`, () => { 38 | const wrapper = shallow(<View {...tests[test]} />) 39 | expect(toJson(wrapper)).toMatchSnapshot() 40 | }) 41 | } 42 | } catch (err) { 43 | it.skip(viewName) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /handyCommands.md: -------------------------------------------------------------------------------- 1 | # Handy commands 2 | 3 | * `findview` - i.e. `findview "color " | sort | uniq` - will find a string across all view files within the directory 4 | * `findviewr` - i.e. `findviewr "#404040" "#112233"` - will find and replace the first string with the second one 5 | * `findjs` - i.e `findjs "Invalid fields"` - will find the string in JavaScript files 6 | -------------------------------------------------------------------------------- /images/Tools.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viewstools/docs/cee417461658a3639f33b0208f074718d77b8c26/images/Tools.jpg -------------------------------------------------------------------------------- /images/ViewsToolsOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viewstools/docs/cee417461658a3639f33b0208f074718d77b8c26/images/ViewsToolsOverview.png -------------------------------------------------------------------------------- /knownIssues.md: -------------------------------------------------------------------------------- 1 | 1. 2 | The issue: The dynamic props `<` don't update the default value after changing it (Issue with React Refresh). 3 | The temporary solution: After changing the default value inside of the component copy the prop with the new value and add it at the point of use. The component will receive new value and rerender. Then remove it the override prop from the point of use. 4 | -------------------------------------------------------------------------------- /map-fonts.js: -------------------------------------------------------------------------------- 1 | let weights = { 2 | Thin: 100, 3 | Light: 200, 4 | Regular: 300, 5 | Medium: 400, 6 | Semibold: 500, 7 | Bold: 600, 8 | Extrabold: 700, 9 | Black: 800, 10 | } 11 | let weightsKeys = Object.keys(weights) 12 | 13 | let fs = require('fs') 14 | let path = require('path') 15 | 16 | let fonts = fs.readdirSync(process.argv[2]) 17 | fonts.forEach(oldFile => { 18 | let file = path.basename(oldFile) 19 | 20 | let weight = weightsKeys.find(weight => file.includes(weight)) 21 | 22 | file = file.replace(weight, weights[weight]).replace('Italic', '-italic') 23 | 24 | fs.copyFileSync( 25 | path.join(process.argv[2], oldFile), 26 | path.join(process.argv[3], file) 27 | ) 28 | }) 29 | --------------------------------------------------------------------------------