├── Procfile ├── globals.d.ts ├── .eslintignore ├── .gitignore ├── periqles-logo.png ├── public ├── assets │ ├── ian.jpeg │ ├── joe.jpeg │ ├── FVAlmelo.ttf │ ├── cameron.jpeg │ ├── kelly.jpeg │ ├── FVAlmelo.woff │ ├── FVAlmelo.woff2 │ ├── periqles-logo.png │ ├── npm-logo-black.png │ └── Github-Mark-64px.png ├── index.html └── index.css ├── ts ├── apolloCache.ts ├── components │ ├── LogoSection.tsx │ ├── CreatorCard.tsx │ ├── Creators.tsx │ ├── LinksSection.tsx │ ├── Demo.tsx │ ├── ApolloUserProfile.tsx │ └── relay │ │ └── UserProfile.tsx ├── app.tsx └── mutations │ └── AddUserMutation.js ├── testing.js ├── types ├── index.d.ts ├── relay-runtime_types.js ├── periqles │ └── index.d.ts └── react-relay_types.js ├── relay.config.js ├── scripts └── updateSchema.js ├── .babelrc ├── data ├── schema │ ├── queries │ │ └── demoUserQuery.js │ ├── index.js │ ├── mutations │ │ └── AddUserMutation.js │ └── nodes.js ├── schema.graphql └── database.js ├── .eslintrc.cjs ├── README.md ├── apolloSchema.js ├── apolloResolvers.js ├── LICENSE ├── tsconfig.json ├── jest.config.js ├── server.js ├── webpack.config.js ├── package.json └── __generated__ └── relay ├── UserProfileQuery.graphql.js └── UserProfile_AddUserMutation.graphql.js /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run quick-start -------------------------------------------------------------------------------- /globals.d.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect'; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | flow-typed 4 | types 5 | __generated__ 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | .env 4 | node_modules 5 | dist 6 | periqles-**.tgz -------------------------------------------------------------------------------- /periqles-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/periqles-logo.png -------------------------------------------------------------------------------- /public/assets/ian.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/ian.jpeg -------------------------------------------------------------------------------- /public/assets/joe.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/joe.jpeg -------------------------------------------------------------------------------- /public/assets/FVAlmelo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/FVAlmelo.ttf -------------------------------------------------------------------------------- /public/assets/cameron.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/cameron.jpeg -------------------------------------------------------------------------------- /public/assets/kelly.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/kelly.jpeg -------------------------------------------------------------------------------- /public/assets/FVAlmelo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/FVAlmelo.woff -------------------------------------------------------------------------------- /public/assets/FVAlmelo.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/FVAlmelo.woff2 -------------------------------------------------------------------------------- /public/assets/periqles-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/periqles-logo.png -------------------------------------------------------------------------------- /public/assets/npm-logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/npm-logo-black.png -------------------------------------------------------------------------------- /public/assets/Github-Mark-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/periqles-demo/HEAD/public/assets/Github-Mark-64px.png -------------------------------------------------------------------------------- /ts/apolloCache.ts: -------------------------------------------------------------------------------- 1 | import { InMemoryCache, Reference } from '@apollo/client'; 2 | 3 | export const cache: InMemoryCache = new InMemoryCache({}); 4 | -------------------------------------------------------------------------------- /testing.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const title = 'Hello React'; 4 | 5 | const App = () => { 6 | return
{title}
; 7 | }; 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/object-type-delimiter */ 2 | type FlatObject = Record; 3 | 4 | // commitMutation parameters 5 | type Input = FlatObject; 6 | interface Variables { 7 | input: Input; 8 | } 9 | -------------------------------------------------------------------------------- /relay.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Configuration options accepted by the `relay-compiler` command-line tool and `babel-plugin-relay`. 3 | src: './ts', 4 | schema: './data/schema.graphql', 5 | exclude: ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], 6 | // include: './dist', 7 | }; 8 | -------------------------------------------------------------------------------- /scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const {schema} = require('../data/schema/index.js'); 5 | const {printSchema} = require('graphql'); 6 | 7 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 8 | 9 | fs.writeFileSync(schemaPath, printSchema(schema)); 10 | 11 | console.log('Wrote ' + schemaPath); 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["relay", {"artifactDirectory": "./__generated__/relay/"}], 4 | "@babel/plugin-transform-runtime", 5 | "@babel/plugin-proposal-class-properties", 6 | "@babel/plugin-transform-typescript", 7 | "transform-react-jsx-img-import" 8 | ], 9 | "presets": [ 10 | "@babel/preset-react", 11 | "@babel/preset-env", 12 | "@babel/preset-typescript" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /data/schema/queries/demoUserQuery.js: -------------------------------------------------------------------------------- 1 | const {demoGraphQLUser} = require('../nodes.js'); 2 | const { 3 | DemoUser, 4 | getLastDemoUserOrThrow, 5 | getAllUsers, 6 | } = require('../../database.js'); 7 | 8 | const demoUserQuery = { 9 | type: demoGraphQLUser, 10 | resolve: (root) => { 11 | 12 | getAllUsers(); 13 | return getLastDemoUserOrThrow(); 14 | }, 15 | }; 16 | 17 | module.exports = {demoUserQuery}; 18 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Periqles Demo 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ts/components/LogoSection.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const LogoSection = (): JSX.Element => { 4 | 5 | return ( 6 |
7 | 10 |

11 | Painless forms for GraphQL. 12 |

13 |
14 | ); 15 | }; 16 | 17 | export default LogoSection; 18 | -------------------------------------------------------------------------------- /types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /ts/components/CreatorCard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface CreatorCardProps { 4 | key: number; 5 | author: { 6 | img: string; 7 | name: string; 8 | link: string; 9 | } 10 | } 11 | 12 | const CreatorCard = ({author}: CreatorCardProps): JSX.Element => { 13 | const {img, name, link} = author; 14 | 15 | return ( 16 |

17 | {/* {name} */} 21 | {name} 22 |

23 | ); 24 | }; 25 | 26 | export default CreatorCard; -------------------------------------------------------------------------------- /data/schema/index.js: -------------------------------------------------------------------------------- 1 | const {GraphQLObjectType, GraphQLSchema} = require('graphql'); 2 | const {nodeField} = require('./nodes.js'); 3 | const {demoUserQuery} = require('./queries/demoUserQuery.js'); 4 | const {AddUserMutation} = require('./mutations/AddUserMutation.js'); 5 | 6 | const Query = new GraphQLObjectType({ 7 | name: 'Query', 8 | fields: { 9 | demoUser: demoUserQuery, 10 | node: nodeField, 11 | }, 12 | }); 13 | 14 | const Mutation = new GraphQLObjectType({ 15 | name: 'Mutation', 16 | fields: { 17 | addUser: AddUserMutation, 18 | }, 19 | }); 20 | 21 | const schema = new GraphQLSchema({ 22 | query: Query, 23 | mutation: Mutation, 24 | }); 25 | 26 | module.exports = {schema}; 27 | -------------------------------------------------------------------------------- /ts/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Demo from './components/Demo'; 5 | import Creators from './components/Creators'; 6 | import LogoSection from './components/LogoSection'; 7 | import LinksSection from './components/LinksSection'; 8 | 9 | const rootElement = document.getElementById('root'); 10 | 11 | 12 | if (rootElement) { 13 | ReactDOM.render( 14 | 15 |
16 | {/* header returns user to top of page on click */} 17 |

periqles

18 |
19 | 20 | 21 | 22 | 23 |
, 24 | rootElement, 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'fbjs/strict', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:relay/recommended', 8 | 'prettier/react', 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | ], 12 | parser: 'babel-eslint', 13 | plugins: ['babel', 'prettier', 'react', 'relay', '@typescript-eslint'], 14 | rules: { 15 | 'no-console': 'off', 16 | 'one-var': 'off', 17 | 'react/prop-types': 'off', // Replaced by flow types 18 | 19 | // Mutations aren't located in the same file as Components 20 | 'relay/unused-fields': 'off', 21 | 'relay/generated-flow-types': 'off', 22 | }, 23 | settings: { 24 | react: { 25 | version: '16.8.1', 26 | flowVersion: '0.94.0', 27 | }, 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | logo 3 |

4 | 5 |

periqles

6 |

7 | Painless forms for GraphQL. 8 |

9 | 10 | This is a demo of [periqles](https://github.com/oslabs-beta/periqles/blob/main/README.md), a form library for GraphQL that supports Relay and Apollo clients. 11 | 12 | [See it deployed →](https://periqles.herokuapp.com/) 13 | 14 | This repo uses Relay and Apollo clients side-by-side on the same Express server to demonstrate how to use periqles in either client. As such, it's a little bootstrappy on the backend; your own backend will probably look very different. Please consult [the periqles readme](https://github.com/oslabs-beta/periqles/blob/main/README.md) for documentation. 15 | 16 | If there's anything else about periqles you'd like to see demonstrated, please write an [issue](https://github.com/oslabs-beta/periqles-demo/issues) or open a pull request. 17 | -------------------------------------------------------------------------------- /ts/components/Creators.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import CreatorCard from './CreatorCard'; 3 | 4 | const Creators = (): JSX.Element => { 5 | const authors = [ 6 | { 7 | img: '../../public/assets/cameron.jpeg', 8 | name: 'Cameron Baumgartner', 9 | link: 'https://github.com/cameronbaumgartner', 10 | }, 11 | { 12 | img: '../../public/assets/ian.jpeg', 13 | name: 'Ian Garrett', 14 | link: 'https://github.com/eeeeean', 15 | }, 16 | { 17 | img: '../../public/assets/kelly.jpeg', 18 | name: 'Kelly Porter', 19 | link: 'https://github.com/kporter101', 20 | }, 21 | { 22 | img: '../../public/assets/joe.jpeg', 23 | name: 'Joe Toledano', 24 | link: 'https://github.com/JosephToledano', 25 | }, 26 | ]; 27 | 28 | return ( 29 | 35 | ); 36 | }; 37 | 38 | export default Creators; -------------------------------------------------------------------------------- /apolloSchema.js: -------------------------------------------------------------------------------- 1 | // const { gql } = require('apollo-server-express'); 2 | 3 | 4 | 5 | // const typeDefs = gql` 6 | // type Query { 7 | // demoUser: DemoUser 8 | // } 9 | 10 | // type Mutation { 11 | // addUser( 12 | // input: AddUserInput!): DemoUser 13 | // } 14 | 15 | // input AddUserInput { 16 | // username: String! 17 | // password: String! 18 | // email: String! 19 | // gender: GenderEnum! 20 | // pizzaTopping: PizzaToppingEnum! 21 | // age: Int! 22 | // } 23 | 24 | // type DemoUser { 25 | // id: ID! 26 | // userId: String! 27 | // username: String! 28 | // password: String! 29 | // email: String! 30 | // gender: GenderEnum! 31 | // pizzaTopping: PizzaToppingEnum! 32 | // age: Int! 33 | // } 34 | 35 | // enum GenderEnum { 36 | // NON_BINARY 37 | // FEMALE 38 | // MALE 39 | // } 40 | 41 | // enum PizzaToppingEnum { 42 | // BUFFALO_CHICKEN 43 | // PEPPERONI 44 | // MEATLOVERS 45 | // EGGPLANT_PARM 46 | // OLIVES 47 | // HAWAIIAN 48 | // } 49 | // `; 50 | 51 | // export default typeDefs; 52 | -------------------------------------------------------------------------------- /apolloResolvers.js: -------------------------------------------------------------------------------- 1 | const { getLastDemoUserOrThrow, addUser } = require('./data/database.js'); 2 | 3 | module.exports = { 4 | Query: { 5 | // demoUser: (_, __, { database }) => database.getLastDemoUserOrThrow(), 6 | demoUser: (_, __, ___) => getLastDemoUserOrThrow(), 7 | }, 8 | Mutation: { 9 | // addUser: (_, { 10 | // username, 11 | // password, 12 | // email, 13 | // gender, 14 | // pizzaTopping, 15 | // age, 16 | // }, { database }) => database.addUser(username, password, email, gender, pizzaTopping, age), 17 | addUser: (_, { input }, ___) => { 18 | return addUser(input); 19 | }, 20 | }, 21 | }; 22 | 23 | // mutation AddUser { 24 | // addUser(username: "asdf", password: "asdf", email: "asdf@asdf", gender: "NON_BINARY", pizzaTopping: "HAWAIIAN", age: 1) { 25 | // username 26 | // } 27 | // } 28 | // query DemoUser { 29 | // demoUser { 30 | // username 31 | // password 32 | // email 33 | // gender 34 | // pizzaTopping 35 | // age 36 | // } 37 | // } 38 | // "eslintConfig": { 39 | // "extends": "react-app" 40 | // }, 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 OSLabs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ts/components/LinksSection.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const LinksSection = (): JSX.Element => { 4 | 5 | return ( 6 |
7 |

8 | Periqles is a component library for Relay and Apollo that makes it easy to collect user input. 9 |
10 |
Periqles abstracts away the dirty work of form creation — with override switches built in for the design-conscious developer — so you can be free to focus on business logic. 11 |

12 |
13 | 14 | NPM 18 | 19 | 20 | Github 24 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | export default LinksSection; 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", // path to output directory 4 | "sourceMap": true, // allow sourcemap support 5 | "strictNullChecks": true, // enable strict null checks as a best practice 6 | "module": "commonjs", // specify module code generation 7 | "jsx": "react", // use typescript to transpile jsx to js 8 | "target": "es5", // specify ECMAScript target version 9 | "allowJs": true, // allow a partial TypeScript and JavaScript codebase 10 | "moduleResolution": "node", // use Node.js's module resolution strategy to resolve non-relative imports 11 | "allowSyntheticDefaultImports": true, 12 | // Ensure that .d.ts files are created by tsc, but not .js files 13 | "declaration": true, // generate .d.ts automatically from types throughout project 14 | "emitDeclarationOnly": true, // only generate .d.ts, not .js 15 | // Ensure that Babel can safely transpile files in the TypeScript project 16 | "isolatedModules": true, 17 | "esModuleInterop": true, //flag enabled for compatibility with Jest (and Babel): 18 | "typeRoots": [ 19 | "./types", 20 | "node_modules/@types" 21 | ] 22 | }, 23 | "paths": { 24 | "components/*": ["ts/components/*"] 25 | }, 26 | "include": ["./ts/"] 27 | } 28 | -------------------------------------------------------------------------------- /data/schema.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | demoUser: DemoUser 3 | 4 | """Fetches an object given its ID""" 5 | node( 6 | """The ID of an object""" 7 | id: ID! 8 | ): Node 9 | } 10 | 11 | type DemoUser { 12 | """The ID of an object""" 13 | id: ID! 14 | userId: String! 15 | username: String! 16 | password: String! 17 | email: String! 18 | gender: GenderEnum! 19 | pizzaTopping: PizzaToppingEnum! 20 | age: Int! 21 | } 22 | 23 | enum GenderEnum { 24 | NON_BINARY 25 | FEMALE 26 | MALE 27 | } 28 | 29 | enum PizzaToppingEnum { 30 | BUFFALO_CHICKEN 31 | PEPPERONI 32 | MEATLOVERS 33 | EGGPLANT_PARM 34 | OLIVES 35 | HAWAIIAN 36 | } 37 | 38 | """An object with an ID""" 39 | interface Node { 40 | """The id of the object.""" 41 | id: ID! 42 | } 43 | 44 | type Mutation { 45 | addUser(input: AddUserInput!): AddUserPayload 46 | } 47 | 48 | type AddUserPayload { 49 | userId: String! 50 | username: String! 51 | password: String! 52 | email: String! 53 | gender: GenderEnum! 54 | pizzaTopping: PizzaToppingEnum! 55 | age: Int! 56 | clientMutationId: String 57 | } 58 | 59 | input AddUserInput { 60 | username: String! 61 | password: String! 62 | email: String! 63 | gender: GenderEnum! 64 | pizzaTopping: PizzaToppingEnum! 65 | age: Int! 66 | clientMutationId: String 67 | } 68 | -------------------------------------------------------------------------------- /ts/mutations/AddUserMutation.js: -------------------------------------------------------------------------------- 1 | import {commitMutation, graphql} from 'react-relay'; 2 | 3 | const mutation = graphql` 4 | mutation AddUserMutation($input: AddUserInput!) { 5 | addUser(input: $input) { 6 | userId 7 | username 8 | password 9 | email 10 | gender 11 | pizzaTopping 12 | age 13 | } 14 | } 15 | `; 16 | 17 | let tempID = 0; 18 | 19 | function commit( 20 | environment, 21 | username, 22 | password, 23 | email, 24 | gender, 25 | pizzaTopping, 26 | age, 27 | ) { 28 | const input = { 29 | username, 30 | password, 31 | email, 32 | gender, 33 | pizzaTopping, 34 | age, 35 | clientMutationId: `${tempID++}`, 36 | }; 37 | 38 | return commitMutation(environment, { 39 | mutation, 40 | variables: { 41 | input, 42 | }, 43 | updater: (store) => { 44 | const payload = store.getRootField('addUser'); 45 | const newUserId = payload.getValue('userId'); 46 | const newUsername = payload.getValue('username'); 47 | const newPassword = payload.getValue('password'); 48 | const newEmail = payload.getValue('email'); 49 | const newGender = payload.getValue('gender'); 50 | const newPizzaTopping = payload.getValue('pizzaTopping'); 51 | const newAge = payload.getValue('age'); 52 | }, 53 | }); 54 | } 55 | 56 | export default {commit}; 57 | -------------------------------------------------------------------------------- /ts/components/Demo.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import UserProfile from './relay/UserProfile'; 3 | import ApolloUserProfile from './ApolloUserProfile'; 4 | import { 5 | ApolloClient, 6 | NormalizedCacheObject, 7 | ApolloProvider 8 | } from '@apollo/client'; 9 | import { cache } from '../apolloCache'; 10 | 11 | let URI = ''; 12 | if (process.env.NODE_ENV === 'development') { 13 | URI = 'http://localhost:8080/graphql'; 14 | } 15 | else URI = 'https://periqles.herokuapp.com/graphql'; 16 | 17 | const client: ApolloClient = new ApolloClient({ 18 | cache, 19 | uri: URI, 20 | }); 21 | 22 | const Demo = (): JSX.Element => { 23 | const [relay, setRelay] = useState(true); 24 | 25 | return ( 26 |
27 | {relay 28 | ?

Relay Demo

29 | :

Apollo Demo

30 | } 31 |
32 | Apollo 33 | 39 | Relay 40 |
41 |

This site is just for demonstration purposes. Please don't enter personal information.

42 | {relay 43 | ? 44 | : ( 45 | 46 | 47 | ) 48 | } 49 |
50 | ) 51 | } 52 | 53 | export default Demo; -------------------------------------------------------------------------------- /data/schema/mutations/AddUserMutation.js: -------------------------------------------------------------------------------- 1 | const graphqlRelay = require('graphql-relay'); 2 | const graphql = require('graphql'); 3 | const {GenderEnum, PizzaToppingEnum} = require('../nodes.js'); 4 | const {addUser, getDemoUserOrThrow} = require('../../database.js'); 5 | 6 | const {mutationWithClientMutationId} = graphqlRelay; 7 | const {GraphQLNonNull, GraphQLString, GraphQLInt} = graphql; 8 | 9 | const AddUserMutation = mutationWithClientMutationId({ 10 | name: 'AddUser', 11 | inputFields: { 12 | username: {type: new GraphQLNonNull(GraphQLString)}, 13 | password: {type: new GraphQLNonNull(GraphQLString)}, 14 | email: {type: new GraphQLNonNull(GraphQLString)}, 15 | gender: {type: new GraphQLNonNull(GenderEnum)}, 16 | pizzaTopping: {type: new GraphQLNonNull(PizzaToppingEnum)}, 17 | age: {type: new GraphQLNonNull(GraphQLInt)}, 18 | }, 19 | outputFields: { 20 | userId: { 21 | type: new GraphQLNonNull(GraphQLString), 22 | resolve: ({userId}) => userId, 23 | }, 24 | username: { 25 | type: new GraphQLNonNull(GraphQLString), 26 | resolve: ({username}) => username, 27 | }, 28 | password: { 29 | type: new GraphQLNonNull(GraphQLString), 30 | resolve: ({password}) => password, 31 | }, 32 | email: { 33 | type: new GraphQLNonNull(GraphQLString), 34 | resolve: ({email}) => email, 35 | }, 36 | gender: { 37 | type: new GraphQLNonNull(GenderEnum), 38 | resolve: ({gender}) => gender, 39 | }, 40 | pizzaTopping: { 41 | type: new GraphQLNonNull(PizzaToppingEnum), 42 | resolve: ({pizzaTopping}) => pizzaTopping, 43 | }, 44 | age: { 45 | type: new GraphQLNonNull(GraphQLInt), 46 | resolve: ({age}) => age, 47 | }, 48 | }, 49 | mutateAndGetPayload: (input) => { 50 | const newUser = addUser(input); 51 | return newUser; 52 | }, 53 | }); 54 | 55 | module.exports = {AddUserMutation}; 56 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // import jestConfig from 'jest-config'; 2 | // const {defaults} = jestConfig; 3 | const {defaults} = require('jest-config'); 4 | 5 | module.exports = { 6 | roots: ['./__tests__'], 7 | globals: { 8 | 'ts-jest': { 9 | // Tell ts-jest about our typescript config. 10 | // You can specify a path to your tsconfig.json file, 11 | // but since we're compiling specifically for node here, 12 | // this works too. 13 | tsConfig: { 14 | target: './tsconfig.json', 15 | }, 16 | }, 17 | }, 18 | // Transforms tell jest how to process our non-javascript files. 19 | // Here we're using babel for .js and .jsx files, and ts-jest for 20 | // .ts and .tsx files. You *can* just use babel-jest for both, if 21 | // you already have babel set up to compile typescript files. 22 | transform: { 23 | // '^.+\\.jsx?$': 'babel-jest', 24 | // '^.+\\.tsx?$': 'ts-jest', 25 | // If you're using babel for both: 26 | '^.+\\.[jt]sx?$': 'babel-jest', 27 | }, 28 | // In webpack projects, we often allow importing things like css files or jpg 29 | // files, and let a webpack loader plugin take care of loading these resources. 30 | // In a unit test, though, we're running in node.js which doesn't know how 31 | // to import these, so this tells jest what to do for these. 32 | moduleNameMapper: { 33 | // Resolve .css and similar files to identity-obj-proxy instead. 34 | '.+\\.(css|styl|less|sass|scss)$': `identity-obj-proxy`, 35 | // Resolve .jpg and similar files to __mocks__/file-mock.js 36 | '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `/__mocks__/file-mock.js`, 37 | }, 38 | // Tells Jest what folders to ignore for tests 39 | testPathIgnorePatterns: [`node_modules`, `\\.cache`], 40 | testURL: `http://localhost`, 41 | moduleFileExtensions: [ 42 | ...defaults.moduleFileExtensions, 43 | '.ts', 44 | '.tsx', 45 | '.js', 46 | '.jsx', 47 | ], 48 | }; 49 | -------------------------------------------------------------------------------- /data/database.js: -------------------------------------------------------------------------------- 1 | //DEMO USER CLASSES, FUNCTIONS AND INFO 2 | 3 | // DemoUser class: 4 | class DemoUser { 5 | constructor(userId, username, password, email, gender, pizzaTopping, age) { 6 | this.userId = userId; 7 | this.username = username; 8 | this.password = password; 9 | this.email = email; 10 | this.gender = gender; 11 | this.pizzaTopping = pizzaTopping; 12 | this.age = age; 13 | } 14 | } 15 | 16 | // Mock database seeded with initial user 17 | const demoUsersById = new Map([ 18 | [0, new DemoUser('0', 'bob', 'anna', 'bob@bob.io', 'MALE', 'HAWAIIAN', 10)], 19 | ]); 20 | 21 | // Seed initial user 22 | let nextUserId = 1; 23 | 24 | function getDemoUser(userId) { 25 | return demoUsersById.get(userId); 26 | } 27 | function getDemoUserOrThrow(userId) { 28 | const demoUser = getDemoUser(userId); 29 | if (!demoUser) { 30 | throw new Error(`Invariant exception, DemoUser ${userId} not found`); 31 | } 32 | 33 | return demoUser; 34 | } 35 | 36 | function getLastDemoUserOrThrow() { 37 | let lastDemoUser; 38 | const demoUsersIterator = demoUsersById[Symbol.iterator](); 39 | 40 | for (const userItem of demoUsersIterator) { 41 | lastDemoUser = userItem[1]; 42 | } 43 | 44 | return lastDemoUser; 45 | } 46 | 47 | function getAllUsers() { 48 | let demoUserList = []; 49 | const demoUsersIterator = demoUsersById[Symbol.iterator](); 50 | 51 | for (const userItem of demoUsersIterator) { 52 | demoUserList.push(userItem[1]); 53 | } 54 | 55 | return demoUserList; 56 | } 57 | 58 | // addUser function 59 | function addUser({ 60 | userId, 61 | username, 62 | password, 63 | email, 64 | gender, 65 | pizzaTopping, 66 | age, 67 | }) { 68 | const newUser = new DemoUser( 69 | `${nextUserId++}`, 70 | username, 71 | password, 72 | email, 73 | gender, 74 | pizzaTopping, 75 | age, 76 | ); 77 | demoUsersById.set(newUser.userId, newUser); 78 | return newUser; 79 | } 80 | 81 | module.exports = { 82 | DemoUser, 83 | getDemoUserOrThrow, 84 | getLastDemoUserOrThrow, 85 | getAllUsers, 86 | addUser, 87 | }; 88 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const {graphqlHTTP} = require('express-graphql'); 3 | const path = require('path'); 4 | const cors = require('cors'); 5 | const {schema} = require('./data/schema/index.js'); 6 | 7 | const app = express(); 8 | const PORT = process.env.PORT || 3000; 9 | 10 | app.use(cors()); 11 | 12 | app.use('*', (req, res, next) => { 13 | console.log('Incoming request:', req.method, req.baseUrl); 14 | return next(); 15 | }); 16 | 17 | // only needed when in production mode 18 | if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === undefined) { 19 | app.get('/', (req, res) => { 20 | return res.status(200) 21 | .sendFile(path.join(__dirname, '/public/index.html')); 22 | }); 23 | app.get('/index.css', (req, res) => { 24 | return res.status(200) 25 | .set('Content-Type', 'text/css') 26 | .sendFile(path.join(__dirname, '/public/index.css')); 27 | }); 28 | app.use('/dist/', express.static(path.join(__dirname, 'dist'))); 29 | } 30 | 31 | 32 | 33 | // Set up GraphQL endpoint for POSTs 34 | app.post( 35 | '/graphql', 36 | graphqlHTTP({ 37 | schema: schema, 38 | pretty: true, // pretty-print JSON responses 39 | }), 40 | ); 41 | 42 | // Send a GET to /graphql to use GraphiQL in the dev environment 43 | if (process.env.NODE_ENV === 'development'){ 44 | app.get( 45 | '/graphql', 46 | graphqlHTTP({ 47 | schema: schema, 48 | pretty: true, 49 | graphiql: true, 50 | }), 51 | ); 52 | } 53 | 54 | app.use(express.json()); 55 | 56 | //ERROR HANDLING 57 | app.use((err, req, res, next) => { 58 | const error = { 59 | log: 'Express error handler caught unknown middleware error', 60 | status: 500, 61 | message: { 62 | err: 'A server error occured', 63 | }, 64 | }; 65 | error.message = err.message; 66 | if (err.status) error.status = err.status; 67 | 68 | console.log('SERVER ERROR: ', error.message); 69 | res.status(error.status).send(error.message); 70 | }); 71 | 72 | app.listen(PORT, () => { 73 | console.log(`Backend server listening on port: ${PORT}`); 74 | }); 75 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = { 5 | mode: process.env.NODE_ENV, 6 | entry: './ts/app.tsx', 7 | output: { 8 | filename: './bundle.js', 9 | path: path.resolve(__dirname, 'dist'), 10 | publicPath: '/dist/', // location of bundle.js 11 | }, 12 | devServer: { 13 | contentBase: path.join(__dirname, 'public'), // static assets: serve html/css 14 | publicPath: '/dist/', // location of bundle.js 15 | port: 8080, // port used in development mode 16 | hot: true, // hot module replacement 17 | open: true, // opens the page when server starts 18 | // TODO: a more secure setting for allowed origins 19 | headers: {'Access-Control-Allow-Origin': '*'}, // allow cors from any host 20 | historyApiFallback: true, // if 404, serve index.html 21 | proxy: { 22 | // '/graphql/*': 'http://localhost:3000', 23 | '/graphql': 'http://localhost:3000', 24 | }, 25 | onListening: function (server) { 26 | const port = server.listeningApp.address().port; 27 | console.log('Frontend listening on port:', port); 28 | }, 29 | }, 30 | resolve: { 31 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 32 | }, 33 | module: { 34 | rules: [ 35 | { 36 | test: /\.(t|j)sx?$/, 37 | enforce: 'pre', 38 | loader: 'babel-loader', 39 | exclude: /node_modules/, 40 | }, 41 | { 42 | test: /\.(css)$/, 43 | use: ['style-loader', 'css-loader'], 44 | }, 45 | // use this loader if you want to require assets into files where they are used 46 | { 47 | test: /\.(png|jpg|jpeg|gif|ttf|woff|woff2)$/, 48 | loader: 'file-loader', 49 | options: { 50 | publicPath: 'dist' // after build, static assets currently in ./public will be findable in ./dist/public 51 | } 52 | }, 53 | ], 54 | }, 55 | plugins: [ 56 | new CopyWebpackPlugin({ 57 | patterns: [ 58 | { from: 'public/assets' } 59 | ] 60 | }) 61 | ], 62 | devtool: 'source-map', 63 | }; -------------------------------------------------------------------------------- /data/schema/nodes.js: -------------------------------------------------------------------------------- 1 | const { 2 | GraphQLNonNull, 3 | GraphQLObjectType, 4 | GraphQLString, 5 | GraphQLEnumType, 6 | GraphQLInt, 7 | } = require('graphql'); 8 | const {fromGlobalId, globalIdField, nodeDefinitions} = require('graphql-relay'); 9 | const {DemoUser, getLastDemoUserOrThrow} = require('../database.js'); 10 | 11 | const {nodeInterface, nodeField} = nodeDefinitions( 12 | (globalId) => { 13 | const {type, id} = fromGlobalId(globalId); 14 | if (type === 'DemoUser') { 15 | return getLastDemoUserOrThrow(); 16 | } 17 | return null; 18 | }, 19 | (obj) => { 20 | if (obj instanceof DemoUser) { 21 | return obj; 22 | } 23 | 24 | return null; 25 | }, 26 | ); 27 | 28 | const GenderEnum = new GraphQLEnumType({ 29 | name: 'GenderEnum', 30 | values: { 31 | NON_BINARY: { 32 | value: 'NON_BINARY', 33 | }, 34 | FEMALE: { 35 | value: 'FEMALE', 36 | }, 37 | MALE: { 38 | value: 'MALE', 39 | }, 40 | }, 41 | }); 42 | 43 | const PizzaToppingEnum = new GraphQLEnumType({ 44 | name: 'PizzaToppingEnum', 45 | values: { 46 | BUFFALO_CHICKEN: { 47 | value: 'BUFFALO_CHICKEN', 48 | }, 49 | PEPPERONI: { 50 | value: 'PEPPERONI', 51 | }, 52 | MEATLOVERS: { 53 | value: 'MEATLOVERS', 54 | }, 55 | EGGPLANT_PARM: { 56 | value: 'EGGPLANT_PARM', 57 | }, 58 | OLIVES: { 59 | value: 'OLIVES', 60 | }, 61 | HAWAIIAN: { 62 | value: 'HAWAIIAN', 63 | }, 64 | }, 65 | }); 66 | 67 | const demoGraphQLUser = new GraphQLObjectType({ 68 | name: 'DemoUser', 69 | fields: { 70 | id: globalIdField('DemoUser'), 71 | userId: { 72 | type: new GraphQLNonNull(GraphQLString), 73 | resolve: (demoUser) => demoUser.userId, 74 | }, 75 | username: { 76 | type: new GraphQLNonNull(GraphQLString), 77 | resolve: (demoUser) => demoUser.username, 78 | }, 79 | password: { 80 | type: new GraphQLNonNull(GraphQLString), 81 | resolve: (demoUser) => demoUser.password, 82 | }, 83 | email: { 84 | type: new GraphQLNonNull(GraphQLString), 85 | resolve: (demoUser) => demoUser.email, 86 | }, 87 | gender: { 88 | type: new GraphQLNonNull(GenderEnum), 89 | resolve: (demoUser) => demoUser.gender, 90 | }, 91 | pizzaTopping: { 92 | type: new GraphQLNonNull(PizzaToppingEnum), 93 | resolve: (demoUser) => demoUser.pizzaTopping, 94 | }, 95 | age: { 96 | type: new GraphQLNonNull(GraphQLInt), 97 | resolve: (demoUser) => demoUser.age, 98 | }, 99 | }, 100 | }); 101 | 102 | module.exports = {nodeField, demoGraphQLUser, GenderEnum, PizzaToppingEnum}; 103 | -------------------------------------------------------------------------------- /types/periqles/index.d.ts: -------------------------------------------------------------------------------- 1 | type Scalar = number | boolean | string; 2 | type FormState = Record; 3 | 4 | 5 | // PROPS FOR PERIQLES COMPONENTS 6 | interface PeriqlesSpecifications { 7 | header?: string; 8 | fields?: Record; 9 | } 10 | 11 | interface PeriqlesFieldSpecs { 12 | element: string; 13 | label: string | JSX.Element; 14 | options?: PeriqlesOptionSpec[]; 15 | render?: ( 16 | formState: FormState, 17 | setFormState: React.Dispatch>, 18 | handleChange: (e) => void, 19 | ) => JSX.Element; 20 | src?: string; 21 | min?: number; 22 | max?: number; 23 | } 24 | 25 | interface PeriqlesOptionSpec { 26 | label: string | JSX.Element; 27 | value: number | string; 28 | } 29 | 30 | // objects returned by generateFieldsArray 31 | interface PeriqlesField { 32 | name: string; 33 | label?: string | JSX.Element; 34 | type?: string; 35 | options?: PeriqlesFieldOption[]; 36 | required?: boolean; 37 | } 38 | 39 | // options objects prepared for input fields represented by dropdowns/radios 40 | interface PeriqlesFieldOption { 41 | name: string; 42 | label: string | JSX.Element; 43 | value: number | string; 44 | type: string; 45 | } 46 | 47 | interface PeriqlesCallbacks { 48 | onSuccess?: (response: object) => any; 49 | onFailure?: (err: object) => any; 50 | } 51 | 52 | type PeriqlesMutationArgs = Record; 53 | 54 | interface RelayEnvironment { 55 | store: any; 56 | networkLayer: any; 57 | handlerProvider?: any; 58 | } 59 | 60 | interface PeriqlesFormProps { 61 | // eventually: this environment will accept RelayEnvironment | ApolloClient 62 | environment?: RelayEnvironment; 63 | mutationName: string; 64 | mutationGQL?: string | object; 65 | specifications?: PeriqlesSpecifications; 66 | args?: PeriqlesMutationArgs; 67 | callbacks?: PeriqlesCallbacks; 68 | useMutation?: any; 69 | } 70 | 71 | interface PeriqlesFieldProps { 72 | field: PeriqlesField; 73 | formState: FormState; 74 | handleChange: (e) => void; 75 | specs?: PeriqlesFieldSpecs; 76 | setFormState: React.Dispatch>; 77 | } 78 | 79 | // PERIQLES HELPER FUNCTIONS 80 | type GenerateDefaultElement = (params: { 81 | field: PeriqlesField, 82 | formState: FormState, 83 | handleChange: (e) => void, 84 | }) => JSX.Element; 85 | 86 | type GenerateSpecifiedElement = (params: { 87 | field: PeriqlesField, 88 | specs: PeriqlesFieldSpecs, 89 | formState: FormState, 90 | handleChange: (e) => void, 91 | setFormState: React.Dispatch>, 92 | }) => JSX.Element; 93 | 94 | // RESULT OF INTROSPECTION QUERY 95 | 96 | interface InputType { 97 | name: string; 98 | inputFields: InputField[]; 99 | } 100 | 101 | interface InputField { 102 | name: string; 103 | type: GraphQLType; 104 | } 105 | 106 | interface GraphQLType { 107 | name: string; 108 | kind: string; 109 | ofType?: GraphQLOfType; 110 | enumValues?: EnumValue[]; 111 | } 112 | 113 | interface GraphQLOfType { 114 | name: string; 115 | kind: string; 116 | enumValues?: EnumValue[]; 117 | } 118 | 119 | // Although EnumValue's one propety is called "name", it actuallly holds a value. 120 | interface EnumValue { 121 | name: number | string; 122 | } 123 | 124 | // commitMutation parameters 125 | type Input = Record; 126 | interface Variables { 127 | input: Input; 128 | } 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": "12.x", 5 | "npm": "6.x" 6 | }, 7 | "scripts": { 8 | "tsc": "tsc --watch", 9 | "start": "NODE_ENV='development' webpack serve & nodemon -r dotenv/config server.js", 10 | "start:prod": "shx rm -rf dist && NODE_ENV='production' webpack & node -r dotenv/config server.js", 11 | "quick-start": "NODE_ENV='production' webpack & node -r dotenv/config server.js", 12 | "build:relay": "relay-compiler --src ./ts/ --schema ./data/schema.graphql --artifactDirectory ./__generated__/relay --extensions js jsx ts tsx --exclude ./__generated__/", 13 | "update-schema": "babel-node ./scripts/updateSchema.js", 14 | "install-periqles": "npm install ~/periqles-2.1.0.tgz", 15 | "lint": "eslint .", 16 | "test": "jest --verbose", 17 | "jest": "node --experimental-vm-modules node_modules/.bin/jest" 18 | }, 19 | "dependencies": { 20 | "@apollo/client": "^3.3.11", 21 | "@babel/core": "^7.12.9", 22 | "@babel/node": "^7.12.6", 23 | "@babel/plugin-proposal-class-properties": "^7.12.1", 24 | "@babel/plugin-transform-runtime": "^7.12.1", 25 | "@babel/preset-env": "^7.12.17", 26 | "@babel/preset-react": "^7.12.7", 27 | "@babel/preset-typescript": "^7.12.17", 28 | "@babel/runtime": "^7.12.5", 29 | "@testing-library/jest-dom": "^5.11.9", 30 | "@types/react": "^17.0.2", 31 | "@types/react-dom": "^17.0.1", 32 | "apollo-server-express": "^2.21.0", 33 | "babel-loader": "^8.2.2", 34 | "babel-plugin-relay": "^10.1.0", 35 | "babel-plugin-transform-react-jsx-img-import": "^0.1.4", 36 | "classnames": "2.2.6", 37 | "copy-webpack-plugin": "^7.0.0", 38 | "cors": "^2.8.5", 39 | "css-loader": "^5.0.2", 40 | "dotenv": "^8.2.0", 41 | "express": "^4.16.4", 42 | "express-graphql": "^0.12.0", 43 | "file-loader": "^6.2.0", 44 | "graphql": "^15.4.0", 45 | "graphql-relay": "^0.6.0", 46 | "graphql-tag": "^2.11.0", 47 | "periqles": "^2.1.0", 48 | "react": "^17.0.1", 49 | "react-dom": "^17.0.1", 50 | "react-relay": "^10.1.0", 51 | "react-syntax-highlighter": "^15.4.3", 52 | "shx": "^0.3.3", 53 | "style-loader": "^2.0.0", 54 | "ts-graphql-plugin": "^2.1.3", 55 | "ts-loader": "^8.0.17", 56 | "webpack": "^5.0.0", 57 | "webpack-cli": "^4.5.0", 58 | "yarn": "^1.22.10" 59 | }, 60 | "devDependencies": { 61 | "@babel/eslint-parser": "^7.12.17", 62 | "@testing-library/dom": "^7.29.6", 63 | "@testing-library/react": "^11.2.5", 64 | "@types/jest": "^26.0.20", 65 | "@typescript-eslint/eslint-plugin": "^4.15.1", 66 | "@typescript-eslint/parser": "^4.15.1", 67 | "babel-jest": "^26.6.3", 68 | "enzyme": "^3.11.0", 69 | "enzyme-adapter-react-16": "^1.15.6", 70 | "enzyme-to-json": "^3.6.1", 71 | "eslint": "^7.14.0", 72 | "eslint-config-fbjs": "^3.1.1", 73 | "eslint-config-prettier": "^6.15.0", 74 | "eslint-plugin-babel": "^5.3.1", 75 | "eslint-plugin-jsx-a11y": "^6.4.1", 76 | "eslint-plugin-prettier": "^3.2.0", 77 | "eslint-plugin-react": "^7.21.5", 78 | "eslint-plugin-relay": "^1.8.1", 79 | "jest": "^26.6.3", 80 | "jest-webpack-resolver": "^0.3.0", 81 | "nodemon": "^2.0.7", 82 | "prettier": "^2.2.1", 83 | "relay-compiler": "^10.1.0", 84 | "ts-jest": "^26.5.1", 85 | "typescript": "^4.1.5", 86 | "webpack-dev-server": "3.10.3", 87 | "webpack-livereload-plugin": "^2.3.0" 88 | }, 89 | "prettier": { 90 | "singleQuote": true, 91 | "trailingComma": "all", 92 | "bracketSpacing": false, 93 | "jsxBracketSameLine": true 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /__generated__/relay/UserProfileQuery.graphql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | /* eslint-disable */ 6 | 7 | 'use strict'; 8 | 9 | /*:: 10 | import type { ConcreteRequest } from 'relay-runtime'; 11 | export type GenderEnum = "FEMALE" | "MALE" | "NON_BINARY" | "%future added value"; 12 | export type PizzaToppingEnum = "BUFFALO_CHICKEN" | "EGGPLANT_PARM" | "HAWAIIAN" | "MEATLOVERS" | "OLIVES" | "PEPPERONI" | "%future added value"; 13 | export type UserProfileQueryVariables = {||}; 14 | export type UserProfileQueryResponse = {| 15 | +demoUser: ?{| 16 | +userId: string, 17 | +username: string, 18 | +password: string, 19 | +email: string, 20 | +gender: GenderEnum, 21 | +pizzaTopping: PizzaToppingEnum, 22 | +age: number, 23 | |} 24 | |}; 25 | export type UserProfileQuery = {| 26 | variables: UserProfileQueryVariables, 27 | response: UserProfileQueryResponse, 28 | |}; 29 | */ 30 | 31 | 32 | /* 33 | query UserProfileQuery { 34 | demoUser { 35 | userId 36 | username 37 | password 38 | email 39 | gender 40 | pizzaTopping 41 | age 42 | id 43 | } 44 | } 45 | */ 46 | 47 | const node/*: ConcreteRequest*/ = (function(){ 48 | var v0 = { 49 | "alias": null, 50 | "args": null, 51 | "kind": "ScalarField", 52 | "name": "userId", 53 | "storageKey": null 54 | }, 55 | v1 = { 56 | "alias": null, 57 | "args": null, 58 | "kind": "ScalarField", 59 | "name": "username", 60 | "storageKey": null 61 | }, 62 | v2 = { 63 | "alias": null, 64 | "args": null, 65 | "kind": "ScalarField", 66 | "name": "password", 67 | "storageKey": null 68 | }, 69 | v3 = { 70 | "alias": null, 71 | "args": null, 72 | "kind": "ScalarField", 73 | "name": "email", 74 | "storageKey": null 75 | }, 76 | v4 = { 77 | "alias": null, 78 | "args": null, 79 | "kind": "ScalarField", 80 | "name": "gender", 81 | "storageKey": null 82 | }, 83 | v5 = { 84 | "alias": null, 85 | "args": null, 86 | "kind": "ScalarField", 87 | "name": "pizzaTopping", 88 | "storageKey": null 89 | }, 90 | v6 = { 91 | "alias": null, 92 | "args": null, 93 | "kind": "ScalarField", 94 | "name": "age", 95 | "storageKey": null 96 | }; 97 | return { 98 | "fragment": { 99 | "argumentDefinitions": [], 100 | "kind": "Fragment", 101 | "metadata": null, 102 | "name": "UserProfileQuery", 103 | "selections": [ 104 | { 105 | "alias": null, 106 | "args": null, 107 | "concreteType": "DemoUser", 108 | "kind": "LinkedField", 109 | "name": "demoUser", 110 | "plural": false, 111 | "selections": [ 112 | (v0/*: any*/), 113 | (v1/*: any*/), 114 | (v2/*: any*/), 115 | (v3/*: any*/), 116 | (v4/*: any*/), 117 | (v5/*: any*/), 118 | (v6/*: any*/) 119 | ], 120 | "storageKey": null 121 | } 122 | ], 123 | "type": "Query", 124 | "abstractKey": null 125 | }, 126 | "kind": "Request", 127 | "operation": { 128 | "argumentDefinitions": [], 129 | "kind": "Operation", 130 | "name": "UserProfileQuery", 131 | "selections": [ 132 | { 133 | "alias": null, 134 | "args": null, 135 | "concreteType": "DemoUser", 136 | "kind": "LinkedField", 137 | "name": "demoUser", 138 | "plural": false, 139 | "selections": [ 140 | (v0/*: any*/), 141 | (v1/*: any*/), 142 | (v2/*: any*/), 143 | (v3/*: any*/), 144 | (v4/*: any*/), 145 | (v5/*: any*/), 146 | (v6/*: any*/), 147 | { 148 | "alias": null, 149 | "args": null, 150 | "kind": "ScalarField", 151 | "name": "id", 152 | "storageKey": null 153 | } 154 | ], 155 | "storageKey": null 156 | } 157 | ] 158 | }, 159 | "params": { 160 | "cacheID": "008d2a939fead39076a8f348e1e16a70", 161 | "id": null, 162 | "metadata": {}, 163 | "name": "UserProfileQuery", 164 | "operationKind": "query", 165 | "text": "query UserProfileQuery {\n demoUser {\n userId\n username\n password\n email\n gender\n pizzaTopping\n age\n id\n }\n}\n" 166 | } 167 | }; 168 | })(); 169 | // prettier-ignore 170 | (node/*: any*/).hash = '580eda27b22841abb40c50c703c7ed8d'; 171 | 172 | module.exports = node; 173 | -------------------------------------------------------------------------------- /__generated__/relay/UserProfile_AddUserMutation.graphql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | /* eslint-disable */ 6 | 7 | 'use strict'; 8 | 9 | /*:: 10 | import type { ConcreteRequest } from 'relay-runtime'; 11 | export type GenderEnum = "FEMALE" | "MALE" | "NON_BINARY" | "%future added value"; 12 | export type PizzaToppingEnum = "BUFFALO_CHICKEN" | "EGGPLANT_PARM" | "HAWAIIAN" | "MEATLOVERS" | "OLIVES" | "PEPPERONI" | "%future added value"; 13 | export type AddUserInput = {| 14 | username: string, 15 | password: string, 16 | email: string, 17 | gender: GenderEnum, 18 | pizzaTopping: PizzaToppingEnum, 19 | age: number, 20 | clientMutationId?: ?string, 21 | |}; 22 | export type UserProfile_AddUserMutationVariables = {| 23 | input: AddUserInput 24 | |}; 25 | export type UserProfile_AddUserMutationResponse = {| 26 | +addUser: ?{| 27 | +userId: string, 28 | +username: string, 29 | +password: string, 30 | +email: string, 31 | +gender: GenderEnum, 32 | +pizzaTopping: PizzaToppingEnum, 33 | +age: number, 34 | |} 35 | |}; 36 | export type UserProfile_AddUserMutation = {| 37 | variables: UserProfile_AddUserMutationVariables, 38 | response: UserProfile_AddUserMutationResponse, 39 | |}; 40 | */ 41 | 42 | 43 | /* 44 | mutation UserProfile_AddUserMutation( 45 | $input: AddUserInput! 46 | ) { 47 | addUser(input: $input) { 48 | userId 49 | username 50 | password 51 | email 52 | gender 53 | pizzaTopping 54 | age 55 | } 56 | } 57 | */ 58 | 59 | const node/*: ConcreteRequest*/ = (function(){ 60 | var v0 = [ 61 | { 62 | "defaultValue": null, 63 | "kind": "LocalArgument", 64 | "name": "input" 65 | } 66 | ], 67 | v1 = [ 68 | { 69 | "alias": null, 70 | "args": [ 71 | { 72 | "kind": "Variable", 73 | "name": "input", 74 | "variableName": "input" 75 | } 76 | ], 77 | "concreteType": "AddUserPayload", 78 | "kind": "LinkedField", 79 | "name": "addUser", 80 | "plural": false, 81 | "selections": [ 82 | { 83 | "alias": null, 84 | "args": null, 85 | "kind": "ScalarField", 86 | "name": "userId", 87 | "storageKey": null 88 | }, 89 | { 90 | "alias": null, 91 | "args": null, 92 | "kind": "ScalarField", 93 | "name": "username", 94 | "storageKey": null 95 | }, 96 | { 97 | "alias": null, 98 | "args": null, 99 | "kind": "ScalarField", 100 | "name": "password", 101 | "storageKey": null 102 | }, 103 | { 104 | "alias": null, 105 | "args": null, 106 | "kind": "ScalarField", 107 | "name": "email", 108 | "storageKey": null 109 | }, 110 | { 111 | "alias": null, 112 | "args": null, 113 | "kind": "ScalarField", 114 | "name": "gender", 115 | "storageKey": null 116 | }, 117 | { 118 | "alias": null, 119 | "args": null, 120 | "kind": "ScalarField", 121 | "name": "pizzaTopping", 122 | "storageKey": null 123 | }, 124 | { 125 | "alias": null, 126 | "args": null, 127 | "kind": "ScalarField", 128 | "name": "age", 129 | "storageKey": null 130 | } 131 | ], 132 | "storageKey": null 133 | } 134 | ]; 135 | return { 136 | "fragment": { 137 | "argumentDefinitions": (v0/*: any*/), 138 | "kind": "Fragment", 139 | "metadata": null, 140 | "name": "UserProfile_AddUserMutation", 141 | "selections": (v1/*: any*/), 142 | "type": "Mutation", 143 | "abstractKey": null 144 | }, 145 | "kind": "Request", 146 | "operation": { 147 | "argumentDefinitions": (v0/*: any*/), 148 | "kind": "Operation", 149 | "name": "UserProfile_AddUserMutation", 150 | "selections": (v1/*: any*/) 151 | }, 152 | "params": { 153 | "cacheID": "ae10689a7b5a621a1a09d833975f7f73", 154 | "id": null, 155 | "metadata": {}, 156 | "name": "UserProfile_AddUserMutation", 157 | "operationKind": "mutation", 158 | "text": "mutation UserProfile_AddUserMutation(\n $input: AddUserInput!\n) {\n addUser(input: $input) {\n userId\n username\n password\n email\n gender\n pizzaTopping\n age\n }\n}\n" 159 | } 160 | }; 161 | })(); 162 | // prettier-ignore 163 | (node/*: any*/).hash = '4ad681667ade44b20d61b8c3be8a43f8'; 164 | 165 | module.exports = node; 166 | -------------------------------------------------------------------------------- /ts/components/ApolloUserProfile.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable*/ 2 | import React, {useState} from 'react'; 3 | import { gql, useQuery, useMutation } from '@apollo/client'; 4 | import PeriqlesForm from 'periqles'; 5 | import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; 6 | import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx'; 7 | import js from 'react-syntax-highlighter/dist/esm/languages/prism/graphql'; 8 | import {vscDarkPlus} from 'react-syntax-highlighter/dist/esm/styles/prism'; 9 | 10 | SyntaxHighlighter.registerLanguage('jsx', jsx); 11 | SyntaxHighlighter.registerLanguage('js', js); 12 | 13 | export const USER_DATA = gql` 14 | fragment UserData on DemoUser { 15 | username 16 | password 17 | email 18 | gender 19 | pizzaTopping 20 | age 21 | } 22 | `; 23 | 24 | export const GET_USER = gql` 25 | query DemoUser { 26 | demoUser { 27 | ...UserData 28 | } 29 | } 30 | ${USER_DATA} 31 | `; 32 | 33 | export const ADD_USER = gql` 34 | mutation AddUser($input: AddUserInput!){ 35 | addUser(input: $input){ 36 | username 37 | email 38 | gender 39 | pizzaTopping 40 | age 41 | } 42 | } 43 | `; 44 | 45 | const ApolloUserProfile = () => { 46 | const [updated, setUpdate] = useState(false); 47 | const { 48 | data, 49 | loading, 50 | error, 51 | refetch 52 | } = useQuery(GET_USER); 53 | const [ 54 | addUser, 55 | respObj 56 | ] = useMutation( 57 | ADD_USER, 58 | ); 59 | 60 | const specifications: PeriqlesSpecifications = { 61 | header: 'Sign Up', 62 | fields: { 63 | gender: { 64 | element: 'radio', 65 | label: 'Gender', 66 | options: [ 67 | {label: 'Non-binary', value: 'NON_BINARY'}, 68 | {label: 'Male', value: 'MALE'}, 69 | {label: 'Female', value: 'FEMALE'}, 70 | ], 71 | }, 72 | pizzaTopping: { 73 | label: 'Favorite pizza topping:', 74 | element: 'select', 75 | options: [ 76 | {label: 'Buffalo chicken', value: 'BUFFALO_CHICKEN'}, 77 | {label: 'Pepperoni', value: 'PEPPERONI'}, 78 | {label: 'Meat lovers', value: 'MEATLOVERS'}, 79 | {label: 'Eggplant parmesan', value: 'EGGPLANT_PARM'}, 80 | {label: 'Olives', value: 'OLIVES'}, 81 | {label: 'Hawaiian', value: 'HAWAIIAN'}, 82 | ], 83 | }, 84 | }, 85 | }; 86 | 87 | const args = {clientMutationId: '0000'}; 88 | 89 | const onSuccess = (response) => { 90 | refetch(GET_USER); 91 | }; 92 | 93 | const onFailure = (error) => { 94 | alert(`Problem submitting form: ${error.toString()}`); 95 | }; 96 | 97 | const renderUser = (demoUser) => { 98 | return ( 99 |
100 |

{demoUser.username}

101 |

{demoUser.email}

102 |

{demoUser.gender}

103 |

{demoUser.pizzaTopping}

104 |

{demoUser.age}

105 |
106 | ) 107 | } 108 | 109 | return ( 110 |
111 |
112 | 119 |
120 |

Most Recently Added User

121 | {loading ?

Loading data...

: null} 122 | {error ?

ERROR: {JSON.stringify(error)}

: null} 123 | {data && data.demoUser ? renderUser(data.demoUser):

Sign up...

} 124 |
125 |
126 |
127 |

Apollo Code Examples

128 |
129 |
130 |

Mutation Schema

131 | 132 | {"mutation AddUser($input: AddUserInput!){\n"+ 133 | " addUser(input: $input) {\n"+ 134 | " username\n"+ 135 | " password\n"+ 136 | " email\n"+ 137 | " gender\n"+ 138 | " pizzaTopping\n"+ 139 | " age\n"+ 140 | " }\n"+ 141 | "}"} 142 | 143 |
144 |
145 |

PeriqlesForm Tag

146 | 147 | {"\n"+ 154 | "\n"+ 155 | "\n"} 156 | 157 |
158 |
159 |
160 |
161 | ); 162 | }; 163 | 164 | export default ApolloUserProfile; 165 | -------------------------------------------------------------------------------- /ts/components/relay/UserProfile.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {QueryRenderer, graphql} from 'react-relay'; 3 | import {Environment, Network, RecordSource, Store} from 'relay-runtime'; 4 | import PeriqlesForm from 'periqles'; 5 | import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; 6 | import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx'; 7 | import js from 'react-syntax-highlighter/dist/esm/languages/prism/graphql'; 8 | import {vscDarkPlus} from 'react-syntax-highlighter/dist/esm/styles/prism'; 9 | 10 | SyntaxHighlighter.registerLanguage('jsx', jsx); 11 | SyntaxHighlighter.registerLanguage('js', js); 12 | interface QueryResponse { 13 | demoUser?: Record; 14 | } 15 | 16 | const UserProfile = (): JSX.Element => { 17 | const [updated, setUpdate] = useState(false); 18 | 19 | async function fetchQuery(operation, variables): Promise<{}> { 20 | const response = await fetch('/graphql', { 21 | method: 'POST', 22 | headers: { 23 | 'Content-Type': 'application/json', 24 | }, 25 | body: JSON.stringify({ 26 | query: operation.text, 27 | variables, 28 | }), 29 | }); 30 | 31 | return response.json(); 32 | } 33 | 34 | const modernEnvironment: Environment = new Environment({ 35 | network: Network.create(fetchQuery), 36 | store: new Store(new RecordSource()), 37 | }); 38 | 39 | const mutationGQL = graphql` 40 | mutation UserProfile_AddUserMutation($input: AddUserInput!) { 41 | addUser(input: $input) { 42 | userId 43 | username 44 | password 45 | email 46 | gender 47 | pizzaTopping 48 | age 49 | } 50 | } 51 | `; 52 | 53 | const specifications: PeriqlesSpecifications = { 54 | header: 'Sign Up', 55 | fields: { 56 | gender: { 57 | element: 'radio', 58 | label: 'Gender', 59 | options: [ 60 | {label: 'Non-binary', value: 'NON_BINARY'}, 61 | {label: 'Male', value: 'MALE'}, 62 | {label: 'Female', value: 'FEMALE'}, 63 | ], 64 | }, 65 | pizzaTopping: { 66 | label: 'Favorite pizza topping', 67 | element: 'select', 68 | options: [ 69 | {label: 'Buffalo chicken', value: 'BUFFALO_CHICKEN'}, 70 | {label: 'Pepperoni', value: 'PEPPERONI'}, 71 | {label: 'Meat lovers', value: 'MEATLOVERS'}, 72 | {label: 'Eggplant parmesan', value: 'EGGPLANT_PARM'}, 73 | {label: 'Olives', value: 'OLIVES'}, 74 | {label: 'Hawaiian', value: 'HAWAIIAN'}, 75 | ], 76 | }, 77 | }, 78 | }; 79 | 80 | const onSuccess = (response) => { 81 | setUpdate(!updated); 82 | }; 83 | 84 | const onFailure = (error) => { 85 | alert(`Problem submitting form: ${error.toString()}`); 86 | }; 87 | 88 | const args = {clientMutationId: '0000'}; 89 | 90 | return ( 91 |
92 |
93 | 101 |
102 |

Most Recently Added User

103 | { 119 | if (props && !props.demoUser) { 120 | return

Sign up...

; 121 | } 122 | if (props && props.demoUser) { 123 | const {demoUser} = props; 124 | return ( 125 |
126 |

{demoUser.username}

127 |

{demoUser.email}

128 |

{demoUser.gender}

129 |

{demoUser.pizzaTopping}

130 |

{demoUser.age}

131 |
132 | ); 133 | } else if (error) { 134 | console.error(error); 135 | return

Something went wrong...

; 136 | } 137 | 138 | return

Loading...

; 139 | }} 140 | /> 141 |
142 |
143 |
144 |

Relay Code Examples

145 |
146 |
147 |

Mutation Schema

148 | 149 | {"mutation UserProfile_AddUserMutation($input: AddUserInput!) {\n"+ 150 | " addUser(input: $input) {\n"+ 151 | " userId\n"+ 152 | " username\n"+ 153 | " password\n"+ 154 | " email\n"+ 155 | " gender\n"+ 156 | " pizzaTopping\n"+ 157 | " age\n"+ 158 | " }\n"+ 159 | "}"} 160 | 161 |
162 |
163 |

PeriqlesForm Tag

164 | 165 | {"\n"+ 173 | "\n"+ 174 | "\n"} 175 | 176 |
177 |
178 |
179 |
180 | ); 181 | }; 182 | 183 | export default UserProfile; 184 | -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'FV Almelo'; 3 | src: url('FVAlmelo.woff2') format('woff2'), 4 | url('FVAlmelo.woff') format('woff'), 5 | url('FVAlmelo.ttf') format('truetype'); 6 | font-weight: 500; 7 | font-style: normal; 8 | font-display: swap; 9 | } 10 | 11 | html, 12 | body, #root { 13 | margin: 0; 14 | padding: 0; 15 | width: 100%; 16 | height: 100%; 17 | } 18 | 19 | 20 | body { 21 | font: 18px 'Helvetica Neue', Helvetica, Arial, sans-serif; 22 | line-height: 1.4em; 23 | background: #ffffff; 24 | color: black; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | font-weight: 400; 28 | position: relative; 29 | } 30 | 31 | body * { 32 | box-sizing: border-box; 33 | } 34 | 35 | button { 36 | margin: 0; 37 | padding: 0; 38 | border: 0; 39 | width: 100%; 40 | background: none; 41 | font-size: 100%; 42 | vertical-align: baseline; 43 | font-family: inherit; 44 | font-weight: inherit; 45 | color: inherit; 46 | -webkit-appearance: none; 47 | appearance: none; 48 | -webkit-font-smoothing: antialiased; 49 | -moz-osx-font-smoothing: grayscale; 50 | background-color: #C9E465; 51 | color: black; 52 | } 53 | 54 | button:hover { 55 | background-color: #b1c955; 56 | color: white; 57 | } 58 | 59 | header { 60 | font-family: 'FVAlmelo', Helvetica, Arial, sans-serif; 61 | background-color: black; 62 | color: #b1c955; 63 | margin: 0; 64 | padding: 25px 50px; 65 | height: 75px; 66 | position: fixed; 67 | right: 0; 68 | left: 0; 69 | z-index: 999; 70 | } 71 | 72 | header h1 { 73 | margin: 0; 74 | text-align: right; 75 | color: #b1c955; 76 | } 77 | 78 | header a, footer a { 79 | color: inherit; 80 | text-decoration: none; 81 | } 82 | 83 | header a:hover, footer a:hover { 84 | color: white; 85 | } 86 | 87 | a { 88 | color: inherit; 89 | } 90 | 91 | a:visited { 92 | color: inherit; 93 | } 94 | 95 | .LogoSection { 96 | background-color: black; 97 | color: white; 98 | padding: 75px 30px 30px 30px; 99 | width: 100vw; 100 | height: 85vh; 101 | display: flex; 102 | flex-direction: column; 103 | justify-content: center; 104 | align-items: center; 105 | } 106 | 107 | #periqles-logo { 108 | width: 90%; 109 | max-width: 500px; 110 | } 111 | 112 | .LinksSection { 113 | background-color: #b1c955; 114 | color: white; 115 | padding: 30px; 116 | width: 100vw; 117 | min-height: 400px; 118 | display: flex; 119 | flex-direction: column; 120 | align-items: center; 121 | } 122 | 123 | #long-pitch { 124 | margin-top: 100px; 125 | } 126 | 127 | .repo-logos { 128 | display: flex; 129 | flex-direction: row; 130 | align-items: center; 131 | } 132 | 133 | .marketing-logo { 134 | margin: 0 10px; 135 | } 136 | 137 | .Demo { 138 | width: 100%; 139 | margin: 0 auto 50px auto; 140 | padding: 10px; 141 | text-align: center; 142 | } 143 | 144 | .UserProfile { 145 | margin-top: 50px; 146 | display: flex; 147 | flex-direction: column; 148 | justify-content: space-around; 149 | align-items: center; 150 | } 151 | 152 | .PeriqlesForm { 153 | border: 1px solid rgba(0, 0, 0, 0.16); 154 | width: 90%; 155 | } 156 | 157 | .PeriqlesForm, .PeriqlesForm * { 158 | border-radius: 10px; 159 | } 160 | 161 | /* one method to hide label text */ 162 | /* .PeriqlesForm label:not(.periqles-radio-option-label) { 163 | color:white; 164 | } */ 165 | 166 | .UserProfile-main { 167 | text-align:left; 168 | margin-top: 20px; 169 | border: 1px solid rgba(0, 0, 0, 0.16); 170 | border-radius: 10px; 171 | padding: 4%; 172 | } 173 | 174 | .UserProfile-main label { 175 | font-weight: bold; 176 | } 177 | 178 | .CodeDemo { 179 | background-color: #b1c955; 180 | color: white; 181 | display: flex; 182 | flex-direction: column; 183 | align-items: center; 184 | margin-top: 75px; 185 | } 186 | 187 | .CodeDemo h1 { 188 | margin-block-start: 0.75em; 189 | margin-block-end: 0.25em; 190 | } 191 | 192 | .CodeDemo h3 { 193 | margin-block-end: 0; 194 | } 195 | 196 | .Codeblocks { 197 | display: flex; 198 | flex-direction: column; 199 | justify-content: space-evenly; 200 | align-items: center; 201 | width: 100%; 202 | padding: 10px 10px 20px 10px; 203 | } 204 | 205 | .SchemaCode, .PeriqlesCode { 206 | width: 90%; 207 | max-width: 750px; 208 | overflow-x: auto; 209 | } 210 | 211 | footer { 212 | font-family: 'FVAlmelo', Helvetica, Arial, sans-serif; 213 | background-color: black; 214 | color: #C9E465; 215 | margin: 0; 216 | padding: 25px; 217 | height: auto; 218 | width: 100%; 219 | text-align: center; 220 | } 221 | 222 | h1 { 223 | font-size: 36px; 224 | line-height: 45px; 225 | } 226 | 227 | h2 { 228 | font-size: 28px; 229 | line-height: 35px; 230 | } 231 | 232 | /* Relay/Apollo toggle */ 233 | /* The switch - the box around the slider */ 234 | .switch { 235 | position: relative; 236 | display: inline-block; 237 | width: 60px; 238 | height: 34px; 239 | margin: 0 10px; 240 | } 241 | 242 | /* Hide default HTML checkbox */ 243 | .switch input { 244 | opacity: 0; 245 | width: 0; 246 | height: 0; 247 | } 248 | 249 | /* The slider */ 250 | .slider { 251 | position: absolute; 252 | cursor: pointer; 253 | top: 0; 254 | left: 0; 255 | right: 0; 256 | bottom: 0; 257 | background-color: #b1c955; 258 | -webkit-transition: .4s; 259 | transition: .4s; 260 | } 261 | 262 | .slider:before { 263 | position: absolute; 264 | content: ""; 265 | height: 26px; 266 | width: 26px; 267 | left: 4px; 268 | bottom: 4px; 269 | background-color: white; 270 | -webkit-transition: .4s; 271 | transition: .4s; 272 | } 273 | 274 | input:checked + .slider { 275 | background-color: #C9E465; 276 | } 277 | 278 | input:focus + .slider { 279 | box-shadow: 0 0 1px #b1c955; 280 | } 281 | 282 | input:checked + .slider:before { 283 | -webkit-transform: translateX(26px); 284 | -ms-transform: translateX(26px); 285 | transform: translateX(26px); 286 | } 287 | 288 | /* Rounded sliders */ 289 | .slider.round { 290 | border-radius: 34px; 291 | } 292 | 293 | .slider.round:before { 294 | border-radius: 50%; 295 | } 296 | 297 | @media (min-width: 700px) { 298 | button { 299 | width: fit-content; 300 | } 301 | 302 | .LogoSection, .LinksSection { 303 | flex-direction: row; 304 | justify-content: space-evenly; 305 | padding: 30px 15%; 306 | } 307 | 308 | .Demo { 309 | padding: 30px; 310 | } 311 | 312 | #periqles-logo { 313 | width: 40%; 314 | } 315 | 316 | #short-pitch, #long-pitch { 317 | max-width: 500px; 318 | } 319 | 320 | #short-pitch { 321 | margin-left: 40px; 322 | } 323 | 324 | #long-pitch { 325 | margin-right: 40px; 326 | margin-top: 10px; 327 | } 328 | 329 | .marketing-logo { 330 | margin: 0 20px; 331 | } 332 | 333 | .UserProfile { 334 | flex-direction: row; 335 | justify-content: space-evenly; 336 | align-items: flex-start; 337 | } 338 | 339 | .PeriqlesForm { 340 | width: 500px; 341 | margin-right: 30px; 342 | } 343 | 344 | .UserProfile-main { 345 | margin: 0; 346 | } 347 | 348 | footer { 349 | text-align: left; 350 | padding: 30px 15%; 351 | } 352 | } 353 | 354 | @media (min-width: 1690px) { 355 | .Codeblocks { 356 | height: 440px; 357 | flex-direction: row; 358 | justify-content: space-evenly; 359 | align-items: center; 360 | padding: 30px 30px 60px 30px; 361 | } 362 | 363 | .SchemaCode { 364 | margin-right: 30px; 365 | } 366 | } -------------------------------------------------------------------------------- /types/react-relay_types.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 311012da78555b97190c86fa4c196bf3 2 | // flow-typed version: 443dcee08c/react-relay_v1.x.x/flow_>=v0.47.x 3 | 4 | // https://raw.githubusercontent.com/mrtnzlml/quick-payments/8a0fa46a490647aec676fc823c88079e8319dee5/frontend/flow-typed/npm/react-relay_v1.x.x.js 5 | 6 | import * as React from 'react'; 7 | 8 | import {FragmentReference} from 'relay-runtime'; 9 | 10 | declare module 'react-relay' { 11 | declare export type RecordState = 'EXISTENT' | 'NONEXISTENT' | 'UNKNOWN'; 12 | 13 | declare export type onCompleted = ( 14 | response: ?Object, 15 | errors: ?Array, 16 | ) => void; 17 | declare export type onError = (error: Error) => void; 18 | 19 | declare export type CommitOptions = { 20 | onCompleted: onCompleted, 21 | onError: onError, 22 | }; 23 | 24 | /** 25 | * Ideally this would be a union of Field/Fragment/Mutation/Query/Subscription, 26 | * but that causes lots of Flow errors. 27 | */ 28 | declare export type ConcreteBatchCallVariable = { 29 | jsonPath: string, 30 | kind: 'BatchCallVariable', 31 | sourceQueryID: string, 32 | }; 33 | declare export type ConcreteCall = { 34 | kind: 'Call', 35 | metadata: { 36 | type?: ?string, 37 | }, 38 | name: string, 39 | value: ?ConcreteValue, 40 | }; 41 | declare export type ConcreteCallValue = { 42 | callValue: mixed, 43 | kind: 'CallValue', 44 | }; 45 | declare export type ConcreteCallVariable = { 46 | callVariableName: string, 47 | kind: 'CallVariable', 48 | }; 49 | declare export type ConcreteDirective = { 50 | args: Array, 51 | kind: 'Directive', 52 | name: string, 53 | }; 54 | declare export type ConcreteDirectiveArgument = { 55 | name: string, 56 | value: ?ConcreteDirectiveValue, 57 | }; 58 | declare export type ConcreteDirectiveValue = 59 | | ConcreteCallValue 60 | | ConcreteCallVariable 61 | | Array; 62 | declare export type ConcreteFieldMetadata = { 63 | canHaveSubselections?: ?boolean, 64 | inferredPrimaryKey?: ?string, 65 | inferredRootCallName?: ?string, 66 | isAbstract?: boolean, 67 | isConnection?: boolean, 68 | isConnectionWithoutNodeID?: boolean, 69 | isFindable?: boolean, 70 | isGenerated?: boolean, 71 | isPlural?: boolean, 72 | isRequisite?: boolean, 73 | }; 74 | declare export type ConcreteFragmentMetadata = { 75 | isAbstract?: boolean, 76 | pattern?: boolean, 77 | plural?: boolean, 78 | }; 79 | declare export type ConcreteMutation = { 80 | calls: Array, 81 | children?: ?Array, 82 | directives?: ?Array, 83 | kind: 'Mutation', 84 | metadata: { 85 | inputType?: ?string, 86 | }, 87 | name: string, 88 | responseType: string, 89 | }; 90 | declare export type ConcreteOperationMetadata = { 91 | inputType?: ?string, 92 | }; 93 | declare export type ConcreteQuery = { 94 | calls?: ?Array, 95 | children?: ?Array, 96 | directives?: ?Array, 97 | fieldName: string, 98 | isDeferred?: boolean, 99 | kind: 'Query', 100 | metadata: { 101 | identifyingArgName?: ?string, 102 | identifyingArgType?: ?string, 103 | isAbstract?: ?boolean, 104 | isPlural?: ?boolean, 105 | }, 106 | name: string, 107 | type: string, 108 | }; 109 | declare export type ConcreteQueryMetadata = { 110 | identifyingArgName: ?string, 111 | identifyingArgType: ?string, 112 | isAbstract: ?boolean, 113 | isDeferred: ?boolean, 114 | isPlural: ?boolean, 115 | }; 116 | declare export type ConcreteSubscription = { 117 | calls: Array, 118 | children?: ?Array, 119 | directives?: ?Array, 120 | kind: 'Subscription', 121 | name: string, 122 | responseType: string, 123 | metadata: { 124 | inputType?: ?string, 125 | }, 126 | }; 127 | declare export type ConcreteValue = 128 | | ConcreteBatchCallVariable 129 | | ConcreteCallValue 130 | | ConcreteCallVariable 131 | | Array; 132 | 133 | /** 134 | * The output of a graphql-tagged fragment definition. 135 | */ 136 | declare export type ConcreteFragmentDefinition = { 137 | kind: 'FragmentDefinition', 138 | argumentDefinitions: Array, 139 | node: ConcreteFragment, 140 | }; 141 | 142 | declare export type ConcreteLocalArgumentDefinition = { 143 | kind: 'LocalArgument', 144 | name: string, 145 | defaultValue: mixed, 146 | }; 147 | 148 | declare export type ConcreteRootArgumentDefinition = { 149 | kind: 'RootArgument', 150 | name: string, 151 | }; 152 | 153 | /** 154 | * The output of a graphql-tagged operation definition. 155 | */ 156 | declare export type ConcreteOperationDefinition = { 157 | kind: 'OperationDefinition', 158 | argumentDefinitions: Array, 159 | name: string, 160 | operation: 'mutation' | 'query' | 'subscription', 161 | node: ConcreteFragment | ConcreteMutation | ConcreteSubscription, 162 | }; 163 | 164 | declare export type ConcreteArgument = ConcreteLiteral | ConcreteVariable; 165 | declare export type ConcreteArgumentDefinition = 166 | | ConcreteLocalArgument 167 | | ConcreteRootArgument; 168 | /** 169 | * Represents a single ConcreteRoot along with metadata for processing it at 170 | * runtime. The persisted `id` (or `text`) can be used to fetch the query, 171 | * the `fragment` can be used to read the root data (masking data from child 172 | * fragments), and the `query` can be used to normalize server responses. 173 | * 174 | * NOTE: The use of "batch" in the name is intentional, as this wrapper around 175 | * the ConcreteRoot will provide a place to store multiple concrete nodes that 176 | * are part of the same batch, e.g. in the case of deferred nodes or 177 | * for streaming connections that are represented as distinct concrete roots but 178 | * are still conceptually tied to one source query. 179 | */ 180 | declare export type ConcreteBatch = { 181 | kind: 'Batch', 182 | fragment: ConcreteFragment, 183 | id: ?string, 184 | metadata: {[key: string]: mixed}, 185 | name: string, 186 | query: ConcreteRoot, 187 | text: ?string, 188 | }; 189 | declare export type ConcreteCondition = { 190 | kind: 'Condition', 191 | passingValue: boolean, 192 | condition: string, 193 | selections: Array, 194 | }; 195 | declare export type ConcreteField = ConcreteScalarField | ConcreteLinkedField; 196 | declare export type ConcreteFragment = { 197 | argumentDefinitions: Array, 198 | kind: 'Fragment', 199 | metadata: ?{[key: string]: mixed}, 200 | name: string, 201 | selections: Array, 202 | type: string, 203 | }; 204 | declare export type ConcreteFragmentSpread = { 205 | args: ?Array, 206 | kind: 'FragmentSpread', 207 | name: string, 208 | }; 209 | declare export type ConcreteHandle = 210 | | ConcreteScalarHandle 211 | | ConcreteLinkedHandle; 212 | declare export type ConcreteRootArgument = { 213 | kind: 'RootArgument', 214 | name: string, 215 | type: ?string, 216 | }; 217 | declare export type ConcreteInlineFragment = { 218 | kind: 'InlineFragment', 219 | selections: Array, 220 | type: string, 221 | }; 222 | declare export type ConcreteLinkedField = { 223 | alias: ?string, 224 | args: ?Array, 225 | concreteType: ?string, 226 | kind: 'LinkedField', 227 | name: string, 228 | plural: boolean, 229 | selections: Array, 230 | storageKey: ?string, 231 | }; 232 | declare export type ConcreteLinkedHandle = { 233 | alias: ?string, 234 | args: ?Array, 235 | kind: 'LinkedHandle', 236 | name: string, 237 | handle: string, 238 | key: string, 239 | filters: ?Array, 240 | }; 241 | declare export type ConcreteLiteral = { 242 | kind: 'Literal', 243 | name: string, 244 | type: ?string, 245 | value: mixed, 246 | }; 247 | declare export type ConcreteLocalArgument = { 248 | defaultValue: mixed, 249 | kind: 'LocalArgument', 250 | name: string, 251 | type: string, 252 | }; 253 | declare export type ConcreteNode = 254 | | ConcreteCondition 255 | | ConcreteLinkedField 256 | | ConcreteFragment 257 | | ConcreteInlineFragment 258 | | ConcreteRoot; 259 | declare export type ConcreteRoot = { 260 | argumentDefinitions: Array, 261 | kind: 'Root', 262 | name: string, 263 | operation: 'mutation' | 'query' | 'subscription', 264 | selections: Array, 265 | }; 266 | declare export type ConcreteScalarField = { 267 | alias: ?string, 268 | args: ?Array, 269 | kind: 'ScalarField', 270 | name: string, 271 | storageKey: ?string, 272 | }; 273 | declare export type ConcreteScalarHandle = { 274 | alias: ?string, 275 | args: ?Array, 276 | kind: 'ScalarHandle', 277 | name: string, 278 | handle: string, 279 | key: string, 280 | filters: ?Array, 281 | }; 282 | declare export type ConcreteSelection = 283 | | ConcreteCondition 284 | | ConcreteField 285 | | ConcreteFragmentSpread 286 | | ConcreteHandle 287 | | ConcreteInlineFragment; 288 | declare export type ConcreteVariable = { 289 | kind: 'Variable', 290 | name: string, 291 | type: ?string, 292 | variableName: string, 293 | }; 294 | declare export type ConcreteSelectableNode = ConcreteFragment | ConcreteRoot; 295 | declare export type GeneratedNode = ConcreteBatch | ConcreteFragment; 296 | 297 | // The type of a graphql`...` tagged template expression. 298 | declare export type GraphQLTaggedNode = 299 | | (() => ConcreteFragment | ConcreteBatch) 300 | | { 301 | modern: () => ConcreteFragment | ConcreteBatch, 302 | classic: () => ConcreteFragmentDefinition | ConcreteOperationDefinition, 303 | }; 304 | 305 | declare export function graphql(strings: Array): GraphQLTaggedNode; 306 | 307 | declare export type GeneratedNodeMap = {[key: string]: GraphQLTaggedNode}; 308 | 309 | declare export type RelayProp = { 310 | environment: Environment, 311 | }; 312 | 313 | declare export type $FragmentRef = { 314 | +$fragmentRefs: $PropertyType, 315 | }; 316 | 317 | // prettier-ignore 318 | declare export type $RelayProps = $ObjMap< 319 | $Diff, 320 | & (( T) => T) 321 | & ((?T) => ?T) 322 | & (( T ) => $FragmentRef ) 323 | & ((? T ) => ? $FragmentRef ) 324 | & (( $ReadOnlyArray< T>) => $ReadOnlyArray< $FragmentRef>) 325 | & ((?$ReadOnlyArray< T>) => ?$ReadOnlyArray< $FragmentRef>) 326 | & (( $ReadOnlyArray) => $ReadOnlyArray>) 327 | & ((?$ReadOnlyArray) => ?$ReadOnlyArray>) 328 | & ((T) => T), 329 | > 330 | 331 | declare export function createFragmentContainer< 332 | Props: {}, 333 | TComponent: React$ComponentType, 334 | >( 335 | Component: TComponent, 336 | fragmentSpec: GeneratedNodeMap, 337 | ): React$ComponentType< 338 | $RelayProps, RelayProp>, 339 | >; 340 | 341 | declare export function createRefetchContainer< 342 | Props: {}, 343 | TComponent: React$ComponentType, 344 | >( 345 | Component: TComponent, 346 | fragmentSpec: GeneratedNodeMap, 347 | taggedNode: GraphQLTaggedNode, 348 | ): React$ComponentType< 349 | $RelayProps, RelayRefetchProp>, 350 | >; 351 | 352 | declare type FragmentVariablesGetter = ( 353 | prevVars: Variables, 354 | totalCount: number, 355 | ) => Variables; 356 | 357 | declare export type PageInfo = { 358 | endCursor: ?string, 359 | hasNextPage: boolean, 360 | hasPreviousPage: boolean, 361 | startCursor: ?string, 362 | }; 363 | 364 | declare export type ConnectionData = { 365 | edges?: ?Array, 366 | pageInfo?: ?PageInfo, 367 | }; 368 | 369 | declare export type ConnectionConfig = { 370 | direction?: 'backward' | 'forward', 371 | getConnectionFromProps?: (props: Object) => ?ConnectionData, 372 | getFragmentVariables?: FragmentVariablesGetter, 373 | getVariables: ( 374 | props: Object, 375 | paginationInfo: {count: number, cursor: ?string}, 376 | fragmentVariables: Variables, 377 | ) => Variables, 378 | query: GraphQLTaggedNode, 379 | }; 380 | 381 | declare export function createPaginationContainer< 382 | Props: {}, 383 | TComponent: React$ComponentType, 384 | >( 385 | Component: TComponent, 386 | fragmentSpec: GeneratedNodeMap, 387 | connectionConfig: ConnectionConfig, 388 | ): React$ComponentType< 389 | $RelayProps, RelayPaginationProp>, 390 | >; 391 | 392 | declare type Variable = 393 | | string 394 | | null 395 | | boolean 396 | | number 397 | | Variables 398 | | void 399 | | Array 400 | | {}; 401 | declare export type Variables = ?{[string]: Variable}; 402 | declare export type DataID = string; 403 | 404 | declare type TEnvironment = Environment; 405 | declare type TFragment = ConcreteFragment; 406 | declare type TGraphQLTaggedNode = GraphQLTaggedNode; 407 | declare type TNode = ConcreteSelectableNode; 408 | declare export type TOperation = ConcreteBatch; 409 | declare type TPayload = RelayResponsePayload; 410 | 411 | declare export type FragmentMap = CFragmentMap; 412 | declare export type OperationSelector = COperationSelector; 413 | declare export type RelayContext = CRelayContext; 414 | declare export type Selector = CSelector; 415 | declare export type TSnapshot = CSnapshot; 416 | declare export type Snapshot = TSnapshot; 417 | declare export type ProxySnapshot = TSnapshot; 418 | declare export type UnstableEnvironmentCore = CUnstableEnvironmentCore< 419 | TEnvironment, 420 | TFragment, 421 | TGraphQLTaggedNode, 422 | TNode, 423 | TOperation, 424 | >; 425 | 426 | declare export interface IRecordSource { 427 | get(dataID: DataID): ?TRecord; 428 | } 429 | 430 | /** 431 | * A read-only interface for accessing cached graph data. 432 | */ 433 | declare export interface RecordSource extends IRecordSource { 434 | get(dataID: DataID): ?Record; 435 | 436 | getRecordIDs(): Array; 437 | 438 | getStatus(dataID: DataID): RecordState; 439 | 440 | has(dataID: DataID): boolean; 441 | 442 | load( 443 | dataID: DataID, 444 | callback: (error: ?Error, record: ?Record) => void, 445 | ): void; 446 | 447 | size(): number; 448 | } 449 | 450 | /** 451 | * A read/write interface for accessing and updating graph data. 452 | */ 453 | declare export interface MutableRecordSource extends RecordSource { 454 | clear(): void; 455 | 456 | delete(dataID: DataID): void; 457 | 458 | remove(dataID: DataID): void; 459 | 460 | set(dataID: DataID, record: Record): void; 461 | } 462 | 463 | /** 464 | * An interface for keeping multiple views of data consistent across an 465 | * application. 466 | */ 467 | declare export interface Store { 468 | /** 469 | * Get a read-only view of the store's internal RecordSource. 470 | */ 471 | getSource(): RecordSource; 472 | 473 | /** 474 | * Determine if the selector can be resolved with data in the store (i.e. no 475 | * fields are missing). 476 | */ 477 | check(selector: Selector): boolean; 478 | 479 | /** 480 | * Read the results of a selector from in-memory records in the store. 481 | */ 482 | lookup(selector: Selector): Snapshot; 483 | 484 | /** 485 | * Notify subscribers (see `subscribe`) of any data that was published 486 | * (`publish()`) since the last time `notify` was called. 487 | */ 488 | notify(): void; 489 | 490 | /** 491 | * Publish new information (e.g. from the network) to the store, updating its 492 | * internal record source. Subscribers are not immediately notified - this 493 | * occurs when `notify()` is called. 494 | */ 495 | publish(source: RecordSource): void; 496 | 497 | /** 498 | * Attempts to load all the records necessary to fulfill the selector into the 499 | * target record source. 500 | */ 501 | resolve( 502 | target: MutableRecordSource, 503 | selector: Selector, 504 | callback: AsyncLoadCallback, 505 | ): void; 506 | 507 | /** 508 | * Ensure that all the records necessary to fulfill the given selector are 509 | * retained in-memory. The records will not be eligible for garbage collection 510 | * until the returned reference is disposed. 511 | */ 512 | retain(selector: Selector): Disposable; 513 | 514 | /** 515 | * Subscribe to changes to the results of a selector. The callback is called 516 | * when `notify()` is called *and* records have been published that affect the 517 | * selector results relative to the last `notify()`. 518 | */ 519 | subscribe( 520 | snapshot: Snapshot, 521 | callback: (snapshot: Snapshot) => void, 522 | ): Disposable; 523 | } 524 | 525 | /** 526 | * An interface for imperatively getting/setting properties of a `Record`. This interface 527 | * is designed to allow the appearance of direct Record manipulation while 528 | * allowing different implementations that may e.g. create a changeset of 529 | * the modifications. 530 | */ 531 | declare export interface RecordProxy { 532 | copyFieldsFrom(source: RecordProxy): void; 533 | 534 | getDataID(): DataID; 535 | 536 | getLinkedRecord(name: string, args?: ?Variables): RecordProxy; 537 | 538 | getLinkedRecords(name: string, args?: ?Variables): ?Array; 539 | 540 | getOrCreateLinkedRecord( 541 | name: string, 542 | typeName: string, 543 | args?: ?Variables, 544 | ): RecordProxy; 545 | 546 | getType(): string; 547 | 548 | getValue(name: string, args?: ?Variables): mixed; 549 | 550 | setLinkedRecord( 551 | record: RecordProxy, 552 | name: string, 553 | args?: ?Variables, 554 | ): RecordProxy; 555 | 556 | setLinkedRecords( 557 | records: Array, 558 | name: string, 559 | args?: ?Variables, 560 | ): RecordProxy; 561 | 562 | setValue(value: mixed, name: string, args?: ?Variables): RecordProxy; 563 | } 564 | 565 | /** 566 | * An interface for imperatively getting/setting properties of a `RecordSource`. This interface 567 | * is designed to allow the appearance of direct RecordSource manipulation while 568 | * allowing different implementations that may e.g. create a changeset of 569 | * the modifications. 570 | */ 571 | declare export interface RecordSourceProxy 572 | extends IRecordSource { 573 | create(dataID: DataID, typeName: string): RecordProxy; 574 | 575 | delete(dataID: DataID): void; 576 | 577 | get(dataID: DataID): ?RecordProxy; 578 | 579 | getRoot(): RecordProxy; 580 | } 581 | 582 | /** 583 | * Extends the RecordSourceProxy interface with methods for accessing the root 584 | * fields of a Selector. 585 | */ 586 | declare export interface RecordSourceSelectorProxy 587 | extends IRecordSource { 588 | create(dataID: DataID, typeName: string): RecordProxy; 589 | 590 | delete(dataID: DataID): void; 591 | 592 | get(dataID: DataID): ?RecordProxy; 593 | 594 | getRoot(): RecordProxy; 595 | 596 | getRootField(fieldName: string): RecordProxy; 597 | 598 | getPluralRootField(fieldName: string): ?Array; 599 | 600 | getResponse(): ?Object; 601 | } 602 | 603 | declare export interface IRecordReader { 604 | getDataID(record: TRecord): DataID; 605 | 606 | getType(record: TRecord): string; 607 | 608 | getValue(record: TRecord, name: string, args?: ?Variables): mixed; 609 | 610 | getLinkedRecordID( 611 | record: TRecord, 612 | name: string, 613 | args?: ?Variables, 614 | ): ?DataID; 615 | 616 | getLinkedRecordIDs( 617 | record: TRecord, 618 | name: string, 619 | args?: ?Variables, 620 | ): ?Array; 621 | } 622 | 623 | /** 624 | * Settings for how a query response may be cached. 625 | * 626 | * - `force`: causes a query to be issued unconditionally, irrespective of the 627 | * state of any configured response cache. 628 | * - `poll`: causes a query to live update by polling at the specified interval 629 | in milliseconds. (This value will be passed to setTimeout.) 630 | */ 631 | declare export type CacheConfig = { 632 | force?: ?boolean, 633 | poll?: ?number, 634 | }; 635 | 636 | /** 637 | * Represents any resource that must be explicitly disposed of. The most common 638 | * use-case is as a return value for subscriptions, where calling `dispose()` 639 | * would cancel the subscription. 640 | */ 641 | declare export type Disposable = { 642 | dispose(): void, 643 | }; 644 | 645 | /** 646 | * Arbitrary data e.g. received by a container as props. 647 | */ 648 | declare export type Props = {[key: string]: mixed}; 649 | 650 | /* 651 | * An individual cached graph object. 652 | */ 653 | declare export type Record = {[key: string]: mixed}; 654 | 655 | /** 656 | * A collection of records keyed by id. 657 | */ 658 | declare export type RecordMap = {[dataID: DataID]: ?T}; 659 | 660 | /** 661 | * A selector defines the starting point for a traversal into the graph for the 662 | * purposes of targeting a subgraph. 663 | */ 664 | declare export type CSelector = { 665 | dataID: DataID, 666 | node: TNode, 667 | variables: Variables, 668 | }; 669 | 670 | /** 671 | * A representation of a selector and its results at a particular point in time. 672 | */ 673 | declare export type CSnapshot = CSelector & { 674 | data: ?SelectorData, 675 | seenRecords: RecordMap, 676 | }; 677 | 678 | /** 679 | * The results of a selector given a store/RecordSource. 680 | */ 681 | declare export type SelectorData = {[key: string]: mixed}; 682 | 683 | /** 684 | * The results of reading the results of a FragmentMap given some input 685 | * `Props`. 686 | */ 687 | declare export type FragmentSpecResults = {[key: string]: mixed}; 688 | 689 | /** 690 | * A utility for resolving and subscribing to the results of a fragment spec 691 | * (key -> fragment mapping) given some "props" that determine the root ID 692 | * and variables to use when reading each fragment. When props are changed via 693 | * `setProps()`, the resolver will update its results and subscriptions 694 | * accordingly. Internally, the resolver: 695 | * - Converts the fragment map & props map into a map of `Selector`s. 696 | * - Removes any resolvers for any props that became null. 697 | * - Creates resolvers for any props that became non-null. 698 | * - Updates resolvers with the latest props. 699 | */ 700 | declare export interface FragmentSpecResolver { 701 | /** 702 | * Stop watching for changes to the results of the fragments. 703 | */ 704 | dispose(): void; 705 | 706 | /** 707 | * Get the current results. 708 | */ 709 | resolve(): FragmentSpecResults; 710 | 711 | /** 712 | * Update the resolver with new inputs. Call `resolve()` to get the updated 713 | * results. 714 | */ 715 | setProps(props: Props): void; 716 | 717 | /** 718 | * Override the variables used to read the results of the fragments. Call 719 | * `resolve()` to get the updated results. 720 | */ 721 | setVariables(variables: Variables): void; 722 | } 723 | 724 | declare export type CFragmentMap = {[key: string]: TFragment}; 725 | 726 | /** 727 | * An operation selector describes a specific instance of a GraphQL operation 728 | * with variables applied. 729 | * 730 | * - `root`: a selector intended for processing server results or retaining 731 | * response data in the store. 732 | * - `fragment`: a selector intended for use in reading or subscribing to 733 | * the results of the the operation. 734 | */ 735 | declare export type COperationSelector = { 736 | fragment: CSelector, 737 | node: TOperation, 738 | root: CSelector, 739 | variables: Variables, 740 | }; 741 | 742 | /** 743 | * The public API of Relay core. Represents an encapsulated environment with its 744 | * own in-memory cache. 745 | */ 746 | declare export interface CEnvironment< 747 | TEnvironment, 748 | TFragment, 749 | TGraphQLTaggedNode, 750 | TNode, 751 | TOperation, 752 | TPayload, 753 | > { 754 | /** 755 | * Read the results of a selector from in-memory records in the store. 756 | */ 757 | lookup(selector: CSelector): CSnapshot; 758 | 759 | /** 760 | * Subscribe to changes to the results of a selector. The callback is called 761 | * when data has been committed to the store that would cause the results of 762 | * the snapshot's selector to change. 763 | */ 764 | subscribe( 765 | snapshot: CSnapshot, 766 | callback: (snapshot: CSnapshot) => void, 767 | ): Disposable; 768 | 769 | /** 770 | * Ensure that all the records necessary to fulfill the given selector are 771 | * retained in-memory. The records will not be eligible for garbage collection 772 | * until the returned reference is disposed. 773 | * 774 | * Note: This is a no-op in the classic core. 775 | */ 776 | retain(selector: CSelector): Disposable; 777 | 778 | /** 779 | * Send a query to the server with request/response semantics: the query will 780 | * either complete successfully (calling `onNext` and `onCompleted`) or fail 781 | * (calling `onError`). 782 | * 783 | * Note: Most applications should use `streamQuery` in order to 784 | * optionally receive updated information over time, should that feature be 785 | * supported by the network/server. A good rule of thumb is to use this method 786 | * if you would otherwise immediately dispose the `streamQuery()` 787 | * after receving the first `onNext` result. 788 | */ 789 | sendQuery(config: {| 790 | cacheConfig?: ?CacheConfig, 791 | onCompleted?: ?() => void, 792 | onError?: ?(error: Error) => void, 793 | onNext?: ?(payload: TPayload) => void, 794 | operation: COperationSelector, 795 | |}): Disposable; 796 | 797 | /** 798 | * Send a query to the server with request/subscription semantics: one or more 799 | * responses may be returned (via `onNext`) over time followed by either 800 | * the request completing (`onCompleted`) or an error (`onError`). 801 | * 802 | * Networks/servers that support subscriptions may choose to hold the 803 | * subscription open indefinitely such that `onCompleted` is not called. 804 | */ 805 | streamQuery(config: {| 806 | cacheConfig?: ?CacheConfig, 807 | onCompleted?: ?() => void, 808 | onError?: ?(error: Error) => void, 809 | onNext?: ?(payload: TPayload) => void, 810 | operation: COperationSelector, 811 | |}): Disposable; 812 | 813 | unstable_internal: CUnstableEnvironmentCore< 814 | TEnvironment, 815 | TFragment, 816 | TGraphQLTaggedNode, 817 | TNode, 818 | TOperation, 819 | >; 820 | } 821 | 822 | declare export interface CUnstableEnvironmentCore< 823 | TEnvironment, 824 | TFragment, 825 | TGraphQLTaggedNode, 826 | TNode, 827 | TOperation, 828 | > { 829 | /** 830 | * Create an instance of a FragmentSpecResolver. 831 | * 832 | * TODO: The FragmentSpecResolver *can* be implemented via the other methods 833 | * defined here, so this could be moved out of core. It's convenient to have 834 | * separate implementations until the experimental core is in OSS. 835 | */ 836 | createFragmentSpecResolver: ( 837 | context: CRelayContext, 838 | containerName: string, 839 | fragments: CFragmentMap, 840 | props: Props, 841 | callback: () => void, 842 | ) => FragmentSpecResolver; 843 | 844 | /** 845 | * Creates an instance of an OperationSelector given an operation definition 846 | * (see `getOperation`) and the variables to apply. The input variables are 847 | * filtered to exclude variables that do not matche defined arguments on the 848 | * operation, and default values are populated for null values. 849 | */ 850 | createOperationSelector: ( 851 | operation: TOperation, 852 | variables: Variables, 853 | ) => COperationSelector; 854 | 855 | /** 856 | * Given a graphql`...` tagged template, extract a fragment definition usable 857 | * by this version of Relay core. Throws if the value is not a fragment. 858 | */ 859 | getFragment: (node: TGraphQLTaggedNode) => TFragment; 860 | 861 | /** 862 | * Given a graphql`...` tagged template, extract an operation definition 863 | * usable by this version of Relay core. Throws if the value is not an 864 | * operation. 865 | */ 866 | getOperation: (node: TGraphQLTaggedNode) => TOperation; 867 | 868 | /** 869 | * Determine if two selectors are equal (represent the same selection). Note 870 | * that this function returns `false` when the two queries/fragments are 871 | * different objects, even if they select the same fields. 872 | */ 873 | areEqualSelectors: (a: CSelector, b: CSelector) => boolean; 874 | 875 | /** 876 | * Given the result `item` from a parent that fetched `fragment`, creates a 877 | * selector that can be used to read the results of that fragment for that item. 878 | * 879 | * Example: 880 | * 881 | * Given two fragments as follows: 882 | * 883 | * ``` 884 | * fragment Parent on User { 885 | * id 886 | * ...Child 887 | * } 888 | * fragment Child on User { 889 | * name 890 | * } 891 | * ``` 892 | * 893 | * And given some object `parent` that is the results of `Parent` for id "4", 894 | * the results of `Child` can be accessed by first getting a selector and then 895 | * using that selector to `lookup()` the results against the environment: 896 | * 897 | * ``` 898 | * const childSelector = getSelector(queryVariables, Child, parent); 899 | * const childData = environment.lookup(childSelector).data; 900 | * ``` 901 | */ 902 | getSelector: ( 903 | operationVariables: Variables, 904 | fragment: TFragment, 905 | prop: mixed, 906 | ) => ?CSelector; 907 | 908 | /** 909 | * Given the result `items` from a parent that fetched `fragment`, creates a 910 | * selector that can be used to read the results of that fragment on those 911 | * items. This is similar to `getSelector` but for "plural" fragments that 912 | * expect an array of results and therefore return an array of selectors. 913 | */ 914 | getSelectorList: ( 915 | operationVariables: Variables, 916 | fragment: TFragment, 917 | props: Array, 918 | ) => ?Array>; 919 | 920 | /** 921 | * Given a mapping of keys -> results and a mapping of keys -> fragments, 922 | * extracts the selectors for those fragments from the results. 923 | * 924 | * The canonical use-case for this function are Relay Containers, which 925 | * use this function to convert (props, fragments) into selectors so that they 926 | * can read the results to pass to the inner component. 927 | */ 928 | getSelectorsFromObject: ( 929 | operationVariables: Variables, 930 | fragments: CFragmentMap, 931 | props: Props, 932 | ) => {[key: string]: ?(CSelector | Array>)}; 933 | 934 | /** 935 | * Given a mapping of keys -> results and a mapping of keys -> fragments, 936 | * extracts a mapping of keys -> id(s) of the results. 937 | * 938 | * Similar to `getSelectorsFromObject()`, this function can be useful in 939 | * determining the "identity" of the props passed to a component. 940 | */ 941 | getDataIDsFromObject: ( 942 | fragments: CFragmentMap, 943 | props: Props, 944 | ) => {[key: string]: ?(DataID | Array)}; 945 | 946 | /** 947 | * Given a mapping of keys -> results and a mapping of keys -> fragments, 948 | * extracts the merged variables that would be in scope for those 949 | * fragments/results. 950 | * 951 | * This can be useful in determing what varaibles were used to fetch the data 952 | * for a Relay container, for example. 953 | */ 954 | getVariablesFromObject: ( 955 | operationVariables: Variables, 956 | fragments: CFragmentMap, 957 | props: Props, 958 | ) => Variables; 959 | } 960 | 961 | /** 962 | * The type of the `relay` property set on React context by the React/Relay 963 | * integration layer (e.g. QueryRenderer, FragmentContainer, etc). 964 | */ 965 | declare export type CRelayContext = { 966 | environment: TEnvironment, 967 | variables: Variables, 968 | }; 969 | 970 | /** 971 | * The public API of Relay core. Represents an encapsulated environment with its 972 | * own in-memory cache. 973 | */ 974 | declare export interface Environment 975 | extends CEnvironment< 976 | TEnvironment, 977 | TFragment, 978 | TGraphQLTaggedNode, 979 | TNode, 980 | TOperation, 981 | TPayload, 982 | > { 983 | /** 984 | * Applies an optimistic mutation to the store without committing it to the 985 | * server. The returned Disposable can be used to revert this change at a 986 | * later time. 987 | */ 988 | applyMutation(config: {| 989 | configs: Array, 990 | operation: ConcreteOperationDefinition, 991 | optimisticResponse: Object, 992 | variables: Variables, 993 | |}): Disposable; 994 | 995 | /** 996 | * Applies an optimistic mutation if provided and commits the mutation to the 997 | * server. The returned Disposable can be used to bypass the `onCompleted` 998 | * and `onError` callbacks when the server response is returned. 999 | */ 1000 | sendMutation(config: {| 1001 | configs: Array, 1002 | onCompleted?: ?(response: ResponseType) => void, 1003 | onError?: ?(error: Error) => void, 1004 | operation: ConcreteOperationDefinition, 1005 | optimisticOperation?: ?ConcreteOperationDefinition, 1006 | optimisticResponse?: ?Object, 1007 | variables: Variables, 1008 | uploadables?: UploadableMap, 1009 | |}): Disposable; 1010 | } 1011 | 1012 | declare export type Observer = { 1013 | onCompleted?: ?() => void, 1014 | onError?: ?(error: Error) => void, 1015 | onNext?: ?(data: T) => void, 1016 | }; 1017 | 1018 | /** 1019 | * The results of reading data for a fragment. This is similar to a `Selector`, 1020 | * but references the (fragment) node by name rather than by value. 1021 | */ 1022 | declare export type FragmentPointer = { 1023 | __id: DataID, 1024 | __fragments: {[fragmentName: string]: Variables}, 1025 | }; 1026 | 1027 | /** 1028 | * A callback for resolving a Selector from a source. 1029 | */ 1030 | declare export type AsyncLoadCallback = (loadingState: LoadingState) => void; 1031 | declare export type LoadingState = $Exact<{ 1032 | status: 'aborted' | 'complete' | 'error' | 'missing', 1033 | error?: Error, 1034 | }>; 1035 | 1036 | /** 1037 | * A map of records affected by an update operation. 1038 | */ 1039 | declare export type UpdatedRecords = {[dataID: DataID]: boolean}; 1040 | 1041 | /** 1042 | * A function that updates a store (via a proxy) given the results of a "handle" 1043 | * field payload. 1044 | */ 1045 | declare export type Handler = { 1046 | update: ( 1047 | store: RecordSourceProxy, 1048 | fieldPayload: HandleFieldPayload, 1049 | ) => void, 1050 | }; 1051 | 1052 | /** 1053 | * A payload that is used to initialize or update a "handle" field with 1054 | * information from the server. 1055 | */ 1056 | declare export type HandleFieldPayload = $Exact<{ 1057 | // The arguments that were fetched. 1058 | args: Variables, 1059 | // The __id of the record containing the source/handle field. 1060 | dataID: DataID, 1061 | // The (storage) key at which the original server data was written. 1062 | fieldKey: string, 1063 | // The name of the handle. 1064 | handle: string, 1065 | // The (storage) key at which the handle's data should be written by the 1066 | // handler. 1067 | handleKey: string, 1068 | }>; 1069 | 1070 | /** 1071 | * A function that receives a proxy over the store and may trigger side-effects 1072 | * (indirectly) by calling `set*` methods on the store or its record proxies. 1073 | */ 1074 | declare export type StoreUpdater = (store: RecordSourceProxy) => void; 1075 | 1076 | /** 1077 | * Similar to StoreUpdater, but accepts a proxy tied to a specific selector in 1078 | * order to easily access the root fields of a query/mutation. 1079 | */ 1080 | declare export type SelectorStoreUpdater = ( 1081 | store: RecordSourceSelectorProxy, 1082 | ) => void; 1083 | 1084 | declare export type CallValue = ?( 1085 | | boolean 1086 | | number 1087 | | string 1088 | | {[key: string]: CallValue} 1089 | | Array 1090 | ); 1091 | 1092 | declare export type RangeBehaviorsFunction = (connectionArgs: { 1093 | [argName: string]: CallValue, 1094 | }) => 1095 | | 'APPEND' 1096 | | 'IGNORE' 1097 | | 'PREPEND' 1098 | | 'REFETCH' 1099 | | 'REMOVE' 1100 | | 'NODE_DELETE_HANDLER' 1101 | | 'RANGE_ADD_HANDLER' 1102 | | 'RANGE_DELETE_HANDLER' 1103 | | 'HANDLER_TYPES' 1104 | | 'OPTIMISTIC_UPDATE' 1105 | | 'SERVER_UPDATE' 1106 | | 'POLLER_UPDATE' 1107 | | 'UPDATE_TYPES' 1108 | | 'RANGE_OPERATIONS'; 1109 | 1110 | declare export type RangeBehaviorsObject = { 1111 | [key: string]: 'APPEND' | 'IGNORE' | 'PREPEND' | 'REFETCH' | 'REMOVE', 1112 | }; 1113 | 1114 | declare export type RangeBehaviors = 1115 | | RangeBehaviorsFunction 1116 | | RangeBehaviorsObject; 1117 | 1118 | declare export type RelayConcreteNode = mixed; 1119 | 1120 | declare export type RelayMutationConfig = 1121 | | { 1122 | type: 'FIELDS_CHANGE', 1123 | fieldIDs: {[fieldName: string]: DataID | Array}, 1124 | } 1125 | | { 1126 | type: 'RANGE_ADD', 1127 | parentName?: string, 1128 | parentID?: string, 1129 | connectionInfo?: Array<{ 1130 | key: string, 1131 | filters?: Variables, 1132 | rangeBehavior: string, 1133 | }>, 1134 | connectionName?: string, 1135 | edgeName: string, 1136 | rangeBehaviors?: RangeBehaviors, 1137 | } 1138 | | { 1139 | type: 'NODE_DELETE', 1140 | parentName?: string, 1141 | parentID?: string, 1142 | connectionName?: string, 1143 | deletedIDFieldName: string, 1144 | } 1145 | | { 1146 | type: 'RANGE_DELETE', 1147 | parentName?: string, 1148 | parentID?: string, 1149 | connectionKeys?: Array<{ 1150 | key: string, 1151 | filters?: Variables, 1152 | }>, 1153 | connectionName?: string, 1154 | deletedIDFieldName: string | Array, 1155 | pathToConnection: Array, 1156 | } 1157 | | { 1158 | type: 'REQUIRED_CHILDREN', 1159 | children: Array, 1160 | }; 1161 | 1162 | declare export type MutationConfig = {| 1163 | configs?: Array, 1164 | mutation: GraphQLTaggedNode, 1165 | variables: Variables, 1166 | uploadables?: UploadableMap, 1167 | onCompleted?: ?(response: T, errors: ?Array) => void, 1168 | onError?: ?(error: Error) => void, 1169 | optimisticUpdater?: ?SelectorStoreUpdater, 1170 | optimisticResponse?: Object, 1171 | updater?: ?SelectorStoreUpdater, 1172 | |}; 1173 | 1174 | // a.k.a commitRelayModernMutation 1175 | declare export function commitMutation( 1176 | environment: Environment, 1177 | config: MutationConfig, 1178 | ): Disposable; 1179 | 1180 | declare export type ReadyState = { 1181 | error: ?Error, 1182 | props: ?Object, 1183 | retry: ?() => void, 1184 | }; 1185 | 1186 | /** 1187 | * Classic environment below here 1188 | */ 1189 | declare export class RelayQueryFragment { 1190 | // stub 1191 | } 1192 | 1193 | declare export class RelayQueryNode { 1194 | // stub 1195 | } 1196 | 1197 | declare export class RelayQueryRoot { 1198 | // stub 1199 | } 1200 | 1201 | declare export class RelayStoreData { 1202 | // stub 1203 | } 1204 | 1205 | declare export type RelayQuerySet = {[queryName: string]: ?RelayQueryRoot}; 1206 | declare export type ReadyStateChangeCallback = ( 1207 | readyState: ReadyState, 1208 | ) => void; 1209 | declare export type Abortable = { 1210 | abort(): void, 1211 | }; 1212 | declare export type StoreReaderData = Object; 1213 | declare export type StoreReaderOptions = { 1214 | traverseFragmentReferences?: boolean, 1215 | traverseGeneratedFields?: boolean, 1216 | }; 1217 | declare export type FragmentResolver = { 1218 | dispose(): void, 1219 | resolve( 1220 | fragment: RelayQueryFragment, 1221 | dataIDs: DataID | Array, 1222 | ): ?(StoreReaderData | Array), 1223 | }; 1224 | 1225 | declare export interface RelayEnvironmentInterface { 1226 | forceFetch( 1227 | querySet: RelayQuerySet, 1228 | onReadyStateChange: ReadyStateChangeCallback, 1229 | ): Abortable; 1230 | getFragmentResolver( 1231 | fragment: RelayQueryFragment, 1232 | onNext: () => void, 1233 | ): FragmentResolver; 1234 | getStoreData(): RelayStoreData; 1235 | primeCache( 1236 | querySet: RelayQuerySet, 1237 | onReadyStateChange: ReadyStateChangeCallback, 1238 | ): Abortable; 1239 | read( 1240 | node: RelayQueryNode, 1241 | dataID: DataID, 1242 | options?: StoreReaderOptions, 1243 | ): ?StoreReaderData; 1244 | readQuery( 1245 | root: RelayQueryRoot, 1246 | options?: StoreReaderOptions, 1247 | ): Array; 1248 | } 1249 | 1250 | declare export type ClassicEnvironment = RelayEnvironmentInterface; 1251 | 1252 | declare export class QueryRenderer extends React$Component<{ 1253 | cacheConfig?: ?CacheConfig, 1254 | environment: Environment | ClassicEnvironment, 1255 | query: ?GraphQLTaggedNode, 1256 | render: (readyState: ReadyState) => ?React$Element<*>, 1257 | variables: Variables, 1258 | }> {} 1259 | 1260 | // https://github.com/facebook/relay/blob/master/packages/relay-runtime/network/RelayNetworkTypes.js 1261 | /** 1262 | * A cache for saving respones to queries (by id) and variables. 1263 | */ 1264 | declare export interface ResponseCache { 1265 | get(id: string, variables: Variables): ?QueryPayload; 1266 | set(id: string, variables: Variables, payload: QueryPayload): void; 1267 | } 1268 | 1269 | /** 1270 | * An interface for fetching the data for one or more (possibly interdependent) 1271 | * queries. 1272 | */ 1273 | declare export interface Network { 1274 | fetch: FetchFunction; 1275 | request: RequestResponseFunction; 1276 | requestStream: RequestStreamFunction; 1277 | } 1278 | 1279 | declare export type PayloadData = {[key: string]: mixed}; 1280 | 1281 | declare export type PayloadError = { 1282 | message: string, 1283 | locations?: Array<{ 1284 | line: number, 1285 | column: number, 1286 | }>, 1287 | }; 1288 | 1289 | /** 1290 | * The shape of a GraphQL response as dictated by the 1291 | * [spec](http://facebook.github.io/graphql/#sec-Response) 1292 | */ 1293 | declare export type QueryPayload = {| 1294 | data?: ?PayloadData, 1295 | errors?: Array, 1296 | rerunVariables?: Variables, 1297 | |}; 1298 | 1299 | /** 1300 | * The shape of data that is returned by the Relay network layer for a given 1301 | * query. 1302 | */ 1303 | declare export type RelayResponsePayload = {| 1304 | fieldPayloads?: ?Array, 1305 | source: MutableRecordSource, 1306 | errors: ?Array, 1307 | |}; 1308 | 1309 | declare export type PromiseOrValue = Promise | T | Error; 1310 | 1311 | /** 1312 | * A function that executes a GraphQL operation with request/response semantics, 1313 | * with exactly one raw server response returned 1314 | */ 1315 | declare export type FetchFunction = ( 1316 | operation: ConcreteBatch, 1317 | variables: Variables, 1318 | cacheConfig: ?CacheConfig, 1319 | uploadables?: UploadableMap, 1320 | ) => PromiseOrValue; 1321 | 1322 | /** 1323 | * A function that executes a GraphQL operation with request/subscription 1324 | * semantics, returning one or more raw server responses over time. 1325 | */ 1326 | declare export type SubscribeFunction = ( 1327 | operation: ConcreteBatch, 1328 | variables: Variables, 1329 | cacheConfig: ?CacheConfig, 1330 | observer: Observer, 1331 | ) => Disposable; 1332 | 1333 | /** 1334 | * A function that executes a GraphQL operation with request/subscription 1335 | * semantics, returning one or more responses over time that include the 1336 | * initial result and optional updates e.g. as the results of the operation 1337 | * change. 1338 | */ 1339 | declare export type RequestStreamFunction = ( 1340 | operation: ConcreteBatch, 1341 | variables: Variables, 1342 | cacheConfig: ?CacheConfig, 1343 | observer: Observer, 1344 | ) => Disposable; 1345 | 1346 | /** 1347 | * A function that executes a GraphQL operation with request/response semantics, 1348 | * with exactly one response returned. 1349 | */ 1350 | declare export type RequestResponseFunction = ( 1351 | operation: ConcreteBatch, 1352 | variables: Variables, 1353 | cacheConfig?: ?CacheConfig, 1354 | uploadables?: UploadableMap, 1355 | ) => PromiseOrValue; 1356 | 1357 | declare export type Uploadable = File | Blob; 1358 | declare export type UploadableMap = {[key: string]: Uploadable}; 1359 | 1360 | declare export type RerunParam = { 1361 | param: string, 1362 | import: string, 1363 | max_runs: number, 1364 | }; 1365 | 1366 | declare export type RelayPaginationProp = { 1367 | ...RelayProp, 1368 | hasMore: () => boolean, 1369 | isLoading: () => boolean, 1370 | loadMore: ( 1371 | pageSize: number, 1372 | callback: ?(error: ?Error) => void, 1373 | options?: RefetchOptions, 1374 | ) => ?Disposable, 1375 | refetchConnection: ( 1376 | totalCount: number, 1377 | callback: (error: ?Error) => void, 1378 | refetchVariables: ?Variables, 1379 | ) => ?Disposable, 1380 | }; 1381 | 1382 | declare export type RelayRefetchProp = { 1383 | ...RelayProp, 1384 | refetch: ( 1385 | refetchVariables: 1386 | | Variables 1387 | | ((fragmentVariables: Variables) => Variables), 1388 | renderVariables: ?Variables, 1389 | callback: ?(error: ?Error) => void, 1390 | options?: RefetchOptions, 1391 | ) => Disposable, 1392 | }; 1393 | 1394 | declare export type RefetchOptions = { 1395 | force?: boolean, 1396 | rerunParamExperimental?: RerunParam, 1397 | }; 1398 | } 1399 | 1400 | declare module 'react-relay/compat' { 1401 | declare module.exports: any; 1402 | } 1403 | 1404 | declare module 'react-relay/classic' { 1405 | declare module.exports: any; 1406 | } 1407 | 1408 | declare module 'react-relay/lib/assertFragmentMap' { 1409 | declare module.exports: any; 1410 | } 1411 | 1412 | declare module 'react-relay/lib/buildReactRelayContainer' { 1413 | declare module.exports: any; 1414 | } 1415 | 1416 | declare module 'react-relay/lib/buildRQL' { 1417 | declare module.exports: any; 1418 | } 1419 | 1420 | declare module 'react-relay/lib/callsFromGraphQL' { 1421 | declare module.exports: any; 1422 | } 1423 | 1424 | declare module 'react-relay/lib/callsToGraphQL' { 1425 | declare module.exports: any; 1426 | } 1427 | 1428 | declare module 'react-relay/lib/checkRelayQueryData' { 1429 | declare module.exports: any; 1430 | } 1431 | 1432 | declare module 'react-relay/lib/ConcreteQuery' { 1433 | declare module.exports: any; 1434 | } 1435 | 1436 | declare module 'react-relay/lib/containsRelayQueryRootCall' { 1437 | declare module.exports: any; 1438 | } 1439 | 1440 | declare module 'react-relay/lib/createRelayQuery' { 1441 | declare module.exports: any; 1442 | } 1443 | 1444 | declare module 'react-relay/lib/dedent' { 1445 | declare module.exports: any; 1446 | } 1447 | 1448 | declare module 'react-relay/lib/deepFreeze' { 1449 | declare module.exports: any; 1450 | } 1451 | 1452 | declare module 'react-relay/lib/diffRelayQuery' { 1453 | declare module.exports: any; 1454 | } 1455 | 1456 | declare module 'react-relay/lib/directivesToGraphQL' { 1457 | declare module.exports: any; 1458 | } 1459 | 1460 | declare module 'react-relay/lib/filterExclusiveKeys' { 1461 | declare module.exports: any; 1462 | } 1463 | 1464 | declare module 'react-relay/lib/filterRelayQuery' { 1465 | declare module.exports: any; 1466 | } 1467 | 1468 | declare module 'react-relay/lib/findRelayQueryLeaves' { 1469 | declare module.exports: any; 1470 | } 1471 | 1472 | declare module 'react-relay/lib/flattenRelayQuery' { 1473 | declare module.exports: any; 1474 | } 1475 | 1476 | declare module 'react-relay/lib/flattenSplitRelayQueries' { 1477 | declare module.exports: any; 1478 | } 1479 | 1480 | declare module 'react-relay/lib/ForceRelayClassicContext' { 1481 | declare module.exports: any; 1482 | } 1483 | 1484 | declare module 'react-relay/lib/forEachRootCallArg' { 1485 | declare module.exports: any; 1486 | } 1487 | 1488 | declare module 'react-relay/lib/formatStorageKey' { 1489 | declare module.exports: any; 1490 | } 1491 | 1492 | declare module 'react-relay/lib/fromGraphQL' { 1493 | declare module.exports: any; 1494 | } 1495 | 1496 | declare module 'react-relay/lib/generateClientEdgeID' { 1497 | declare module.exports: any; 1498 | } 1499 | 1500 | declare module 'react-relay/lib/generateClientID' { 1501 | declare module.exports: any; 1502 | } 1503 | 1504 | declare module 'react-relay/lib/generateConcreteFragmentID' { 1505 | declare module.exports: any; 1506 | } 1507 | 1508 | declare module 'react-relay/lib/generateForceIndex' { 1509 | declare module.exports: any; 1510 | } 1511 | 1512 | declare module 'react-relay/lib/generateRQLFieldAlias' { 1513 | declare module.exports: any; 1514 | } 1515 | 1516 | declare module 'react-relay/lib/getRangeBehavior' { 1517 | declare module.exports: any; 1518 | } 1519 | 1520 | declare module 'react-relay/lib/getRelayHandleKey' { 1521 | declare module.exports: any; 1522 | } 1523 | 1524 | declare module 'react-relay/lib/getRelayQueries' { 1525 | declare module.exports: any; 1526 | } 1527 | 1528 | declare module 'react-relay/lib/GraphQLMutatorConstants' { 1529 | declare module.exports: any; 1530 | } 1531 | 1532 | declare module 'react-relay/lib/GraphQLQueryRunner' { 1533 | declare module.exports: any; 1534 | } 1535 | 1536 | declare module 'react-relay/lib/GraphQLRange' { 1537 | declare module.exports: any; 1538 | } 1539 | 1540 | declare module 'react-relay/lib/GraphQLSegment' { 1541 | declare module.exports: any; 1542 | } 1543 | 1544 | declare module 'react-relay/lib/GraphQLStoreChangeEmitter' { 1545 | declare module.exports: any; 1546 | } 1547 | 1548 | declare module 'react-relay/lib/GraphQLStoreQueryResolver' { 1549 | declare module.exports: any; 1550 | } 1551 | 1552 | declare module 'react-relay/lib/GraphQLStoreRangeUtils' { 1553 | declare module.exports: any; 1554 | } 1555 | 1556 | declare module 'react-relay/lib/intersectRelayQuery' { 1557 | declare module.exports: any; 1558 | } 1559 | 1560 | declare module 'react-relay/lib/isClassicRelayContext' { 1561 | declare module.exports: any; 1562 | } 1563 | 1564 | declare module 'react-relay/lib/isClassicRelayEnvironment' { 1565 | declare module.exports: any; 1566 | } 1567 | 1568 | declare module 'react-relay/lib/isCompatibleRelayFragmentType' { 1569 | declare module.exports: any; 1570 | } 1571 | 1572 | declare module 'react-relay/lib/isRelayContainer' { 1573 | declare module.exports: any; 1574 | } 1575 | 1576 | declare module 'react-relay/lib/isRelayContext' { 1577 | declare module.exports: any; 1578 | } 1579 | 1580 | declare module 'react-relay/lib/isRelayEnvironment' { 1581 | declare module.exports: any; 1582 | } 1583 | 1584 | declare module 'react-relay/lib/isRelayModernContext' { 1585 | declare module.exports: any; 1586 | } 1587 | 1588 | declare module 'react-relay/lib/isRelayVariables' { 1589 | declare module.exports: any; 1590 | } 1591 | 1592 | declare module 'react-relay/lib/isScalarAndEqual' { 1593 | declare module.exports: any; 1594 | } 1595 | 1596 | declare module 'react-relay/lib/prettyStringify' { 1597 | declare module.exports: any; 1598 | } 1599 | 1600 | declare module 'react-relay/lib/printRelayOSSQuery' { 1601 | declare module.exports: any; 1602 | } 1603 | 1604 | declare module 'react-relay/lib/printRelayQuery' { 1605 | declare module.exports: any; 1606 | } 1607 | 1608 | declare module 'react-relay/lib/QueryBuilder' { 1609 | declare module.exports: any; 1610 | } 1611 | 1612 | declare module 'react-relay/lib/rangeOperationToMetadataKey' { 1613 | declare module.exports: any; 1614 | } 1615 | 1616 | declare module 'react-relay/lib/ReactRelayClassicExports' { 1617 | declare module.exports: any; 1618 | } 1619 | 1620 | declare module 'react-relay/lib/ReactRelayCompatContainerBuilder' { 1621 | declare module.exports: any; 1622 | } 1623 | 1624 | declare module 'react-relay/lib/ReactRelayCompatPublic' { 1625 | declare module.exports: any; 1626 | } 1627 | 1628 | declare module 'react-relay/lib/ReactRelayContainerProfiler' { 1629 | declare module.exports: any; 1630 | } 1631 | 1632 | declare module 'react-relay/lib/ReactRelayFragmentContainer-flowtest' { 1633 | declare module.exports: any; 1634 | } 1635 | 1636 | declare module 'react-relay/lib/ReactRelayFragmentContainer' { 1637 | declare module.exports: any; 1638 | } 1639 | 1640 | declare module 'react-relay/lib/ReactRelayFragmentMockRenderer' { 1641 | declare module.exports: any; 1642 | } 1643 | 1644 | declare module 'react-relay/lib/ReactRelayPaginationContainer-flowtest' { 1645 | declare module.exports: any; 1646 | } 1647 | 1648 | declare module 'react-relay/lib/ReactRelayPaginationContainer' { 1649 | declare module.exports: any; 1650 | } 1651 | 1652 | declare module 'react-relay/lib/ReactRelayPropTypes' { 1653 | declare module.exports: any; 1654 | } 1655 | 1656 | declare module 'react-relay/lib/ReactRelayPublic' { 1657 | declare module.exports: any; 1658 | } 1659 | 1660 | declare module 'react-relay/lib/ReactRelayQueryRenderer' { 1661 | declare module.exports: any; 1662 | } 1663 | 1664 | declare module 'react-relay/lib/ReactRelayRefetchContainer-flowtest' { 1665 | declare module.exports: any; 1666 | } 1667 | 1668 | declare module 'react-relay/lib/ReactRelayRefetchContainer' { 1669 | declare module.exports: any; 1670 | } 1671 | 1672 | declare module 'react-relay/lib/ReactRelayTypes' { 1673 | declare module.exports: any; 1674 | } 1675 | 1676 | declare module 'react-relay/lib/readRelayQueryData' { 1677 | declare module.exports: any; 1678 | } 1679 | 1680 | declare module 'react-relay/lib/recycleNodesInto' { 1681 | declare module.exports: any; 1682 | } 1683 | 1684 | declare module 'react-relay/lib/RelayCacheProcessor' { 1685 | declare module.exports: any; 1686 | } 1687 | 1688 | declare module 'react-relay/lib/RelayChangeTracker' { 1689 | declare module.exports: any; 1690 | } 1691 | 1692 | declare module 'react-relay/lib/RelayClassicCore' { 1693 | declare module.exports: any; 1694 | } 1695 | 1696 | declare module 'react-relay/lib/RelayClassicRecordState' { 1697 | declare module.exports: any; 1698 | } 1699 | 1700 | declare module 'react-relay/lib/RelayCombinedEnvironmentTypes' { 1701 | declare module.exports: any; 1702 | } 1703 | 1704 | declare module 'react-relay/lib/RelayCompatContainer' { 1705 | declare module.exports: any; 1706 | } 1707 | 1708 | declare module 'react-relay/lib/RelayCompatEnvironment' { 1709 | declare module.exports: any; 1710 | } 1711 | 1712 | declare module 'react-relay/lib/RelayCompatMutations' { 1713 | declare module.exports: any; 1714 | } 1715 | 1716 | declare module 'react-relay/lib/RelayCompatPaginationContainer' { 1717 | declare module.exports: any; 1718 | } 1719 | 1720 | declare module 'react-relay/lib/RelayCompatRefetchContainer' { 1721 | declare module.exports: any; 1722 | } 1723 | 1724 | declare module 'react-relay/lib/RelayCompatTypes' { 1725 | declare module.exports: any; 1726 | } 1727 | 1728 | declare module 'react-relay/lib/RelayConcreteNode' { 1729 | declare module.exports: any; 1730 | } 1731 | 1732 | declare module 'react-relay/lib/RelayConnectionInterface' { 1733 | declare module.exports: any; 1734 | } 1735 | 1736 | declare module 'react-relay/lib/RelayContainer' { 1737 | declare module.exports: any; 1738 | } 1739 | 1740 | declare module 'react-relay/lib/RelayContainerComparators' { 1741 | declare module.exports: any; 1742 | } 1743 | 1744 | declare module 'react-relay/lib/RelayContainerProxy' { 1745 | declare module.exports: any; 1746 | } 1747 | 1748 | declare module 'react-relay/lib/RelayContainerUtils' { 1749 | declare module.exports: any; 1750 | } 1751 | 1752 | declare module 'react-relay/lib/RelayDefaultHandleKey' { 1753 | declare module.exports: any; 1754 | } 1755 | 1756 | declare module 'react-relay/lib/RelayDefaultNetworkLayer' { 1757 | declare module.exports: any; 1758 | } 1759 | 1760 | declare module 'react-relay/lib/RelayEnvironment' { 1761 | declare module.exports: any; 1762 | } 1763 | 1764 | declare module 'react-relay/lib/RelayEnvironmentSerializer' { 1765 | declare module.exports: any; 1766 | } 1767 | 1768 | declare module 'react-relay/lib/RelayEnvironmentTypes' { 1769 | declare module.exports: any; 1770 | } 1771 | 1772 | declare module 'react-relay/lib/RelayError' { 1773 | declare module.exports: any; 1774 | } 1775 | 1776 | declare module 'react-relay/lib/RelayEventStatus' { 1777 | declare module.exports: any; 1778 | } 1779 | 1780 | declare module 'react-relay/lib/RelayFetchMode' { 1781 | declare module.exports: any; 1782 | } 1783 | 1784 | declare module 'react-relay/lib/RelayFragmentPointer' { 1785 | declare module.exports: any; 1786 | } 1787 | 1788 | declare module 'react-relay/lib/RelayFragmentReference' { 1789 | declare module.exports: any; 1790 | } 1791 | 1792 | declare module 'react-relay/lib/RelayFragmentSpecResolver' { 1793 | declare module.exports: any; 1794 | } 1795 | 1796 | declare module 'react-relay/lib/RelayGarbageCollection' { 1797 | declare module.exports: any; 1798 | } 1799 | 1800 | declare module 'react-relay/lib/RelayGarbageCollector' { 1801 | declare module.exports: any; 1802 | } 1803 | 1804 | declare module 'react-relay/lib/RelayGraphQLMutation' { 1805 | declare module.exports: any; 1806 | } 1807 | 1808 | declare module 'react-relay/lib/RelayGraphQLTag' { 1809 | declare module.exports: any; 1810 | } 1811 | 1812 | declare module 'react-relay/lib/RelayInternals' { 1813 | declare module.exports: any; 1814 | } 1815 | 1816 | declare module 'react-relay/lib/RelayInternalTypes' { 1817 | declare module.exports: any; 1818 | } 1819 | 1820 | declare module 'react-relay/lib/RelayMetaRoute' { 1821 | declare module.exports: any; 1822 | } 1823 | 1824 | declare module 'react-relay/lib/RelayMetricsRecorder' { 1825 | declare module.exports: any; 1826 | } 1827 | 1828 | declare module 'react-relay/lib/RelayMockRenderer' { 1829 | declare module.exports: any; 1830 | } 1831 | 1832 | declare module 'react-relay/lib/RelayMutation' { 1833 | declare module.exports: any; 1834 | } 1835 | 1836 | declare module 'react-relay/lib/RelayMutationDebugPrinter' { 1837 | declare module.exports: any; 1838 | } 1839 | 1840 | declare module 'react-relay/lib/RelayMutationQuery' { 1841 | declare module.exports: any; 1842 | } 1843 | 1844 | declare module 'react-relay/lib/RelayMutationQueue' { 1845 | declare module.exports: any; 1846 | } 1847 | 1848 | declare module 'react-relay/lib/RelayMutationRequest' { 1849 | declare module.exports: any; 1850 | } 1851 | 1852 | declare module 'react-relay/lib/RelayMutationTracker' { 1853 | declare module.exports: any; 1854 | } 1855 | 1856 | declare module 'react-relay/lib/RelayMutationTransaction' { 1857 | declare module.exports: any; 1858 | } 1859 | 1860 | declare module 'react-relay/lib/RelayMutationTransactionStatus' { 1861 | declare module.exports: any; 1862 | } 1863 | 1864 | declare module 'react-relay/lib/RelayMutationType' { 1865 | declare module.exports: any; 1866 | } 1867 | 1868 | declare module 'react-relay/lib/RelayNetworkDebug' { 1869 | declare module.exports: any; 1870 | } 1871 | 1872 | declare module 'react-relay/lib/RelayNetworkLayer' { 1873 | declare module.exports: any; 1874 | } 1875 | 1876 | declare module 'react-relay/lib/RelayNodeInterface' { 1877 | declare module.exports: any; 1878 | } 1879 | 1880 | declare module 'react-relay/lib/RelayOperationSelector' { 1881 | declare module.exports: any; 1882 | } 1883 | 1884 | declare module 'react-relay/lib/RelayOptimisticMutationUtils' { 1885 | declare module.exports: any; 1886 | } 1887 | 1888 | declare module 'react-relay/lib/RelayOSSConnectionInterface' { 1889 | declare module.exports: any; 1890 | } 1891 | 1892 | declare module 'react-relay/lib/RelayPendingQueryTracker' { 1893 | declare module.exports: any; 1894 | } 1895 | 1896 | declare module 'react-relay/lib/RelayProfiler' { 1897 | declare module.exports: any; 1898 | } 1899 | 1900 | declare module 'react-relay/lib/RelayPropTypes' { 1901 | declare module.exports: any; 1902 | } 1903 | 1904 | declare module 'react-relay/lib/RelayPublic' { 1905 | declare module.exports: any; 1906 | } 1907 | 1908 | declare module 'react-relay/lib/RelayQL_GENERATED' { 1909 | declare module.exports: any; 1910 | } 1911 | 1912 | declare module 'react-relay/lib/RelayQL' { 1913 | declare module.exports: any; 1914 | } 1915 | 1916 | declare module 'react-relay/lib/RelayQuery' { 1917 | declare module.exports: any; 1918 | } 1919 | 1920 | declare module 'react-relay/lib/RelayQueryCaching' { 1921 | declare module.exports: any; 1922 | } 1923 | 1924 | declare module 'react-relay/lib/RelayQueryConfig' { 1925 | declare module.exports: any; 1926 | } 1927 | 1928 | declare module 'react-relay/lib/RelayQueryPath' { 1929 | declare module.exports: any; 1930 | } 1931 | 1932 | declare module 'react-relay/lib/RelayQueryRequest' { 1933 | declare module.exports: any; 1934 | } 1935 | 1936 | declare module 'react-relay/lib/RelayQueryResultObservable' { 1937 | declare module.exports: any; 1938 | } 1939 | 1940 | declare module 'react-relay/lib/RelayQueryTracker' { 1941 | declare module.exports: any; 1942 | } 1943 | 1944 | declare module 'react-relay/lib/RelayQueryTransform' { 1945 | declare module.exports: any; 1946 | } 1947 | 1948 | declare module 'react-relay/lib/RelayQueryVisitor' { 1949 | declare module.exports: any; 1950 | } 1951 | 1952 | declare module 'react-relay/lib/RelayQueryWriter' { 1953 | declare module.exports: any; 1954 | } 1955 | 1956 | declare module 'react-relay/lib/RelayReadyState' { 1957 | declare module.exports: any; 1958 | } 1959 | 1960 | declare module 'react-relay/lib/RelayReadyStateRenderer' { 1961 | declare module.exports: any; 1962 | } 1963 | 1964 | declare module 'react-relay/lib/RelayRecord' { 1965 | declare module.exports: any; 1966 | } 1967 | 1968 | declare module 'react-relay/lib/RelayRecordStatusMap' { 1969 | declare module.exports: any; 1970 | } 1971 | 1972 | declare module 'react-relay/lib/RelayRecordStore' { 1973 | declare module.exports: any; 1974 | } 1975 | 1976 | declare module 'react-relay/lib/RelayRecordWriter' { 1977 | declare module.exports: any; 1978 | } 1979 | 1980 | declare module 'react-relay/lib/RelayRefQueryDescriptor' { 1981 | declare module.exports: any; 1982 | } 1983 | 1984 | declare module 'react-relay/lib/RelayRenderer' { 1985 | declare module.exports: any; 1986 | } 1987 | 1988 | declare module 'react-relay/lib/RelayRootContainer' { 1989 | declare module.exports: any; 1990 | } 1991 | 1992 | declare module 'react-relay/lib/RelayRoute' { 1993 | declare module.exports: any; 1994 | } 1995 | 1996 | declare module 'react-relay/lib/RelayRouteFragment' { 1997 | declare module.exports: any; 1998 | } 1999 | 2000 | declare module 'react-relay/lib/RelaySelector' { 2001 | declare module.exports: any; 2002 | } 2003 | 2004 | declare module 'react-relay/lib/RelayShallowMock' { 2005 | declare module.exports: any; 2006 | } 2007 | 2008 | declare module 'react-relay/lib/RelayStore' { 2009 | declare module.exports: any; 2010 | } 2011 | 2012 | declare module 'react-relay/lib/RelayStoreConstants' { 2013 | declare module.exports: any; 2014 | } 2015 | 2016 | declare module 'react-relay/lib/RelayStoreData' { 2017 | declare module.exports: any; 2018 | } 2019 | 2020 | declare module 'react-relay/lib/RelayTaskQueue' { 2021 | declare module.exports: any; 2022 | } 2023 | 2024 | declare module 'react-relay/lib/RelayTypes' { 2025 | declare module.exports: any; 2026 | } 2027 | 2028 | declare module 'react-relay/lib/relayUnstableBatchedUpdates' { 2029 | declare module.exports: any; 2030 | } 2031 | 2032 | declare module 'react-relay/lib/relayUnstableBatchedUpdates.native' { 2033 | declare module.exports: any; 2034 | } 2035 | 2036 | declare module 'react-relay/lib/RelayVariable' { 2037 | declare module.exports: any; 2038 | } 2039 | 2040 | declare module 'react-relay/lib/RelayVariables' { 2041 | declare module.exports: any; 2042 | } 2043 | 2044 | declare module 'react-relay/lib/restoreRelayCacheData' { 2045 | declare module.exports: any; 2046 | } 2047 | 2048 | declare module 'react-relay/lib/serializeRelayQueryCall' { 2049 | declare module.exports: any; 2050 | } 2051 | 2052 | declare module 'react-relay/lib/simpleClone' { 2053 | declare module.exports: any; 2054 | } 2055 | 2056 | declare module 'react-relay/lib/splitDeferredRelayQueries' { 2057 | declare module.exports: any; 2058 | } 2059 | 2060 | declare module 'react-relay/lib/stableJSONStringify' { 2061 | declare module.exports: any; 2062 | } 2063 | 2064 | declare module 'react-relay/lib/stableStringify' { 2065 | declare module.exports: any; 2066 | } 2067 | 2068 | declare module 'react-relay/lib/testEditDistance' { 2069 | declare module.exports: any; 2070 | } 2071 | 2072 | declare module 'react-relay/lib/throwFailedPromise' { 2073 | declare module.exports: any; 2074 | } 2075 | 2076 | declare module 'react-relay/lib/toGraphQL' { 2077 | declare module.exports: any; 2078 | } 2079 | 2080 | declare module 'react-relay/lib/transformRelayQueryPayload' { 2081 | declare module.exports: any; 2082 | } 2083 | 2084 | declare module 'react-relay/lib/validateMutationConfig' { 2085 | declare module.exports: any; 2086 | } 2087 | 2088 | declare module 'react-relay/lib/validateRelayReadQuery' { 2089 | declare module.exports: any; 2090 | } 2091 | 2092 | declare module 'react-relay/lib/writeRelayQueryPayload' { 2093 | declare module.exports: any; 2094 | } 2095 | 2096 | declare module 'react-relay/lib/writeRelayUpdatePayload' { 2097 | declare module.exports: any; 2098 | } 2099 | 2100 | // Manually added: 2101 | 2102 | declare module 'relay-runtime/lib/RelayNetworkLogger' { 2103 | declare module.exports: any; 2104 | } 2105 | 2106 | declare module 'relay-runtime/lib/RelayQueryResponseCache' { 2107 | declare module.exports: any; 2108 | } 2109 | --------------------------------------------------------------------------------