Socket
Socket
Sign inDemoInstall

@sanity/state-router

Package Overview
Dependencies
1
Maintainers
6
Versions
702
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.99.4 to 0.99.6

lib/components/types.js

18

lib/components.js

@@ -34,2 +34,11 @@ 'use strict';

var _IntentLink = require('./components/IntentLink');
Object.defineProperty(exports, 'IntentLink', {
enumerable: true,
get: function get() {
return _interopRequireDefault(_IntentLink).default;
}
});
var _RouteScope = require('./components/RouteScope');

@@ -44,2 +53,11 @@

var _withRouter = require('./components/withRouter');
Object.defineProperty(exports, 'withRouter', {
enumerable: true,
get: function get() {
return _interopRequireDefault(_withRouter).default;
}
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

22

lib/components/IntentLink.js

@@ -31,2 +31,8 @@ 'use strict';

/*:: import type {RouterProviderContext} from './types'*/
/*:: type Props = {
intent: string,
params: Object
}*/
var IntentLink = function (_React$Component) {

@@ -46,5 +52,15 @@ _inherits(IntentLink, _React$Component);

intent = _props.intent,
params = _props.params;
params = _props.params,
children = _props.children;
// @todo Temporary hack
if (intent === 'edit' && params.type) {
return _react2.default.createElement(
_Link2.default,
{ href: '/desk/' + params.type + '/edit/' + params.id.replace(/\//g, '.') },
children
);
}
var url = this.context.__internalRouter.resolveIntentLink(intent, params);

@@ -59,6 +75,2 @@ var rest = (0, _omit2.default)(this.props, 'intent', 'params');

IntentLink.propTypes = {
intent: _react.PropTypes.string.isRequired,
params: _react.PropTypes.object
};
IntentLink.contextTypes = {

@@ -65,0 +77,0 @@ __internalRouter: _react.PropTypes.object

@@ -27,10 +27,20 @@ 'use strict';

function isLeftClickEvent(event) {
/*:: import type {RouterProviderContext} from './types'*/
function isLeftClickEvent(event /*: SyntheticMouseEvent*/) {
return event.button === 0;
}
function isModifiedEvent(event) {
function isModifiedEvent(event /*: SyntheticMouseEvent*/) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
/*:: type Props = {
replace: boolean,
onClick: (event : SyntheticMouseEvent) => void,
href: string,
target: string
}*/
var Link = function (_React$Component) {

@@ -40,25 +50,25 @@ _inherits(Link, _React$Component);

function Link() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, Link);
var _this = _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).call(this));
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this.handleClick = _this.handleClick.bind(_this);
return _this;
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Link.__proto__ || Object.getPrototypeOf(Link)).call.apply(_ref, [this].concat(args))), _this), _this.handleClick = function (event /*: SyntheticMouseEvent*/) /*: void*/ {
var _this$props = _this.props,
onClick = _this$props.onClick,
href = _this$props.href,
target = _this$props.target,
replace = _this$props.replace;
_createClass(Link, [{
key: 'handleClick',
value: function handleClick(e) {
var _props = this.props,
onClick = _props.onClick,
href = _props.href,
target = _props.target,
replace = _props.replace;
if (onClick) {
onClick(e);
onClick(event);
}
if (isModifiedEvent(e) || !isLeftClickEvent(e)) {
if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
return;

@@ -72,6 +82,9 @@ }

e.preventDefault();
this.context.__internalRouter.navigateUrl(href, { replace: replace });
}
}, {
event.preventDefault();
_this.context.__internalRouter.navigateUrl(href, { replace: replace });
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(Link, [{
key: 'render',

@@ -89,5 +102,2 @@ value: function render() {

};
Link.propTypes = {
replace: _react.PropTypes.bool
};
Link.contextTypes = {

@@ -94,0 +104,0 @@ __internalRouter: _react.PropTypes.object

@@ -21,2 +21,11 @@ 'use strict';

/*:: import type {Router} from '../types'*/
/*:: import type {RouterProviderContext, NavigateOptions} from './types'*/
/*:: type Props = {
onNavigate: () => void,
router: Router,
state: Object,
children: Element<*>
}*/
var RouterProvider = function (_React$Component) {

@@ -36,17 +45,14 @@ _inherits(RouterProvider, _React$Component);

return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = RouterProvider.__proto__ || Object.getPrototypeOf(RouterProvider)).call.apply(_ref, [this].concat(args))), _this), _this.navigateUrl = function (url) {
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
replace = _ref2.replace;
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = RouterProvider.__proto__ || Object.getPrototypeOf(RouterProvider)).call.apply(_ref, [this].concat(args))), _this), _this.navigateUrl = function (url /*: string*/) /*: void*/ {
var options /*: NavigateOptions*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var onNavigate = _this.props.onNavigate;
onNavigate(url, { replace: replace });
}, _this.navigateState = function (nextState) {
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
replace = _ref3.replace;
onNavigate(url, options);
}, _this.navigateState = function (nextState /*: Object*/) /*: void*/ {
var options /*: NavigateOptions*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_this.navigateUrl(_this.resolvePathFromState(nextState), { replace: replace });
}, _this.resolvePathFromState = function (state) {
_this.navigateUrl(_this.resolvePathFromState(nextState), options);
}, _this.resolvePathFromState = function (state /*: Object*/) /*: string*/ {
return _this.props.router.encode(state);
}, _this.resolveIntentLink = function (intent, params) {
}, _this.resolveIntentLink = function (intent /*: string*/, params /*: Object*/) /*: string*/ {
return _this.props.router.encode({ intent: intent, params: params });

@@ -58,3 +64,3 @@ }, _temp), _possibleConstructorReturn(_this, _ret);

key: 'getChildContext',
value: function getChildContext() {
value: function getChildContext() /*: RouterProviderContext*/ {
var state = this.props.state;

@@ -84,13 +90,6 @@

RouterProvider.propTypes = {
onNavigate: _react.PropTypes.func,
router: _react.PropTypes.object,
state: _react.PropTypes.object,
children: _react.PropTypes.node
};
exports.default = RouterProvider;
RouterProvider.childContextTypes = {
__internalRouter: _react.PropTypes.object,
router: _react.PropTypes.object
};
};
exports.default = RouterProvider;

@@ -9,2 +9,4 @@ 'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _react = require('react');

@@ -14,6 +16,8 @@

var _isEmpty = require('../utils/isEmpty');
var _isEmpty2 = _interopRequireDefault(_isEmpty);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

@@ -25,2 +29,15 @@

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/*:: import type {RouterProviderContext, NavigateOptions, InternalRouter, ContextRouter} from './types'*/
/*:: type Props = {
scope: string,
children: Element<*>
}*/
function addScope(routerState /*: Object*/, scope /*: string*/, scopedState /*: Object*/) {
return scopedState && _extends({}, routerState, _defineProperty({}, scope, scopedState));
}
var RouteScope = function (_React$Component) {

@@ -30,5 +47,24 @@ _inherits(RouteScope, _React$Component);

function RouteScope() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, RouteScope);
return _possibleConstructorReturn(this, (RouteScope.__proto__ || Object.getPrototypeOf(RouteScope)).apply(this, arguments));
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = RouteScope.__proto__ || Object.getPrototypeOf(RouteScope)).call.apply(_ref, [this].concat(args))), _this), _this.resolvePathFromState = function (nextState /*: Object*/) /*: string*/ {
var context = _this.context;
var scope = _this.props.scope;
var nextStateScoped /*: Object*/ = (0, _isEmpty2.default)(nextState) ? {} : addScope(context.router.state, scope, nextState);
return context.__internalRouter.resolvePathFromState(nextStateScoped);
}, _this.navigate = function (nextState /*: Object*/, options /*: NavigateOptions*/) /*: void*/ {
var scope = _this.props.scope;
var router = _this.context.router;
router.navigate(addScope(router.state, scope, nextState), options);
}, _temp), _possibleConstructorReturn(_this, _ret);
}

@@ -38,28 +74,19 @@

key: 'getChildContext',
value: function getChildContext() {
value: function getChildContext() /*: RouterProviderContext*/ {
var scope = this.props.scope;
var _context = this.context,
router = _context.router,
__internalRouter = _context.__internalRouter;
var internalRouter /*: InternalRouter*/ = this.context.__internalRouter;
var router /*: ContextRouter*/ = this.context.router;
return {
__internalRouter: {
resolvePathFromState: function resolvePathFromState(nextState) {
var empty = Object.keys(nextState).length === 0;
return __internalRouter.resolvePathFromState(empty ? {} : addScope(nextState));
},
resolveIntentLink: __internalRouter.resolveIntentLink,
navigateUrl: __internalRouter.navigateUrl
resolvePathFromState: this.resolvePathFromState,
resolveIntentLink: internalRouter.resolveIntentLink,
navigateUrl: internalRouter.navigateUrl
},
router: {
navigate: function navigate(nextState, options) {
router.navigate(addScope(nextState), options);
},
navigate: this.navigate,
state: router.state[scope]
}
};
function addScope(nextState) {
return nextState && Object.assign({}, router.state, _defineProperty({}, scope, nextState));
}
}

@@ -76,6 +103,2 @@ }, {

RouteScope.propTypes = {
scope: _react.PropTypes.string,
children: _react.PropTypes.node
};
RouteScope.childContextTypes = RouteScope.contextTypes = {

@@ -82,0 +105,0 @@ __internalRouter: _react.PropTypes.object,

@@ -31,4 +31,12 @@ 'use strict';

/*:: import type {RouterProviderContext} from './types'*/
var EMPTY_STATE = {};
/*:: type Props = {
state: string,
toIndex: boolean
}*/
var StateLink = function (_React$Component) {

@@ -45,3 +53,3 @@ _inherits(StateLink, _React$Component);

key: 'resolveUrl',
value: function resolveUrl() {
value: function resolveUrl() /*: string*/ {
var _props = this.props,

@@ -57,5 +65,8 @@ toIndex = _props.toIndex,

if (!state && !toIndex) {
// eslint-disable-next-line no-console
console.error(new Error('No state passed to StateLink. If you want to link to an empty state, its better to use the the `toIndex` property'));
}
var nextState = toIndex ? EMPTY_STATE : state || EMPTY_STATE;
return this.context.__internalRouter.resolvePathFromState(nextState);

@@ -66,3 +77,3 @@ }

value: function render() {
var rest = (0, _omit2.default)(this.props, 'state', 'toIndex');
var rest = (0, _omit2.default)(this.props, 'replace', 'state', 'toIndex');
return _react2.default.createElement(_Link2.default, _extends({ href: this.resolveUrl() }, rest));

@@ -75,7 +86,2 @@ }

StateLink.propTypes = {
state: _react.PropTypes.object,
replace: _react.PropTypes.bool,
toIndex: _react.PropTypes.bool
};
StateLink.defaultProps = {

@@ -82,0 +88,0 @@ replace: false,

@@ -29,7 +29,10 @@ 'use strict';

function createMatchResult(nodes, missing, remaining) {
/*:: import type {Node, MatchResult} from './types'*/
function createMatchResult(nodes /*: Node[]*/, missing /*: string[]*/, remaining /*: string[]*/) /*: MatchResult*/ {
return { nodes: nodes, missing: missing, remaining: remaining };
}
function findMatchingRoutes(node, _state) {
function findMatchingRoutes(node /*: Node*/, _state /*: ?Object*/) /*: MatchResult*/ {

@@ -50,23 +53,23 @@ if (_state === null || _state === undefined) {

var consumedKeys = (0, _intersection3.default)(stateKeys, requiredParams);
var missingKeys = (0, _difference3.default)(requiredParams, consumedKeys);
var remainingKeys = (0, _difference3.default)(stateKeys, consumedKeys);
var consumedParams = (0, _intersection3.default)(stateKeys, requiredParams);
var missingParams = (0, _difference3.default)(requiredParams, consumedParams);
var remainingParams = (0, _difference3.default)(stateKeys, consumedParams);
if (missingKeys.length > 0) {
return createMatchResult([], missingKeys, []);
if (missingParams.length > 0) {
return createMatchResult([], missingParams, []);
}
if (remainingKeys.length === 0) {
if (remainingParams.length === 0) {
return createMatchResult([node], [], []);
}
var children = typeof node.children === 'function' ? node.children(state) : node.children;
var children = (typeof node.children === 'function' ? node.children(state) : node.children) || [];
if (remainingKeys.length > 0 && children.length === 0) {
return createMatchResult([], remainingKeys, []);
if (remainingParams.length > 0 && children.length === 0) {
return createMatchResult([], remainingParams, []);
}
var remainingState = (0, _pick3.default)(state, remainingKeys);
var remainingState = (0, _pick3.default)(state, remainingParams);
var matchingChild = { nodes: [], remaining: [], missing: [] };
var matchingChild /*: MatchResult*/ = { nodes: [], remaining: [], missing: [] };

@@ -79,3 +82,3 @@ (0, _arrayify2.default)(children).some(function (childNode) {

if (matchingChild.nodes.length === 0) {
return createMatchResult([], missingKeys, remainingKeys);
return createMatchResult([], missingParams, remainingParams);
}

@@ -82,0 +85,0 @@

@@ -10,2 +10,3 @@ 'use strict';

exports.default = parseRoute;
/*:: import type {Route, Segment} from './types'*/

@@ -15,4 +16,3 @@

function createSegment(segment) {
function createSegment(segment /*: string*/) /*: ?Segment*/ {
if (!segment) {

@@ -33,3 +33,3 @@ return null;

function parseRoute(route) {
function parseRoute(route /*: string*/) /*: Route*/ {
var _route$split = route.split('?'),

@@ -39,3 +39,3 @@ _route$split2 = _slicedToArray(_route$split, 1),

var segments = pathname.split('/').map(createSegment).filter(Boolean);
var segments /*: Segment[]*/ = pathname.split('/').map(createSegment).filter(Boolean);

@@ -42,0 +42,0 @@ return {

@@ -21,11 +21,10 @@ 'use strict';

function resolvePathFromState(node, state) {
/*:: import type {Node, MatchResult} from './types'*/
function resolvePathFromState(node /*: Node*/, state /*: Object*/) /*: string*/ {
(0, _debug.debug)('Resolving path from state %o', state);
var match = (0, _findMatchingNodes2.default)(node, state);
var match /*: MatchResult*/ = (0, _findMatchingNodes2.default)(node, state);
if (match.remaining.length > 0) {
var formatted = match.remaining.map(function (key) {
return key + ' (=' + JSON.stringify(state[key]) + ')';
}).join(', ');
throw new Error('State key' + (match.remaining.length == 1 ? '' : 's') + ' not mapped to url params: ' + formatted);
var remaining = match.remaining;
throw new Error('Unable to find matching route for state. Could not map the following state key' + (remaining.length == 1 ? '' : 's') + ' to a valid url: ' + remaining.join(', '));
}

@@ -32,0 +31,0 @@

@@ -21,3 +21,6 @@ 'use strict';

function matchPath(node, path) {
/*:: import type {Node} from './types'*/
function matchPath(node /*: Node*/, path /*: string*/) /*: ?{[key: string]: string}*/ {
var parts = path.split('/').filter(Boolean);

@@ -62,3 +65,3 @@ var segmentsLength = node.route.segments.length;

function resolveStateFromPath(node, path) {
function resolveStateFromPath(node /*: Node*/, path /*: string*/) /*: ?Object*/ {
(0, _debug.debug)('resolving state from path %s', path);

@@ -65,0 +68,0 @@

@@ -31,3 +31,12 @@ 'use strict';

function normalizeChildren(children) {
/*:: import type {Transform, Router, RouteChildren} from './types'*/
/*:: type NodeOptions = {
path?: string,
children?: RouteChildren,
transform?: {[key: string] : Transform<*>},
scope?: string
}*/
function normalizeChildren(children /*: any*/) /*: RouteChildren*/ {
if (Array.isArray(children) || typeof children === 'function') {

@@ -39,7 +48,7 @@ return children;

function isRoute(val) {
function isRoute(val /*: NodeOptions | Router | RouteChildren*/) {
return val && '_isRoute' in val;
}
function normalizeArgs(path, childrenOrOpts, children) {
function normalizeArgs(path /*: string | NodeOptions*/, childrenOrOpts /*: NodeOptions | Router | RouteChildren*/, children /*: Router | RouteChildren*/) /*: NodeOptions*/ {
if ((typeof path === 'undefined' ? 'undefined' : _typeof(path)) === 'object') {

@@ -57,7 +66,7 @@ return path;

function route(routeOrOpts, childrenOrOpts, children) {
function route(routeOrOpts /*: string | NodeOptions*/, childrenOrOpts /*: NodeOptions | RouteChildren*/, children /*: Router | RouteChildren*/) /*: Router*/ {
return createNode(normalizeArgs(routeOrOpts, childrenOrOpts, children));
}
route.scope = function scope(scopeName) {
route.scope = function scope(scopeName /*: string*/) /*: Router*/ {
for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

@@ -97,3 +106,3 @@ rest[_key - 1] = arguments[_key];

var EMPTY_STATE = {};
function isRoot(pathname) {
function isRoot(pathname /*: string*/) /*: boolean*/ {
var parts = pathname.split('/');

@@ -108,3 +117,3 @@ for (var i = 0; i < parts.length; i++) {

function createNode(options) {
function createNode(options /*: NodeOptions*/) /*: Router*/ {
var path = options.path,

@@ -134,9 +143,9 @@ scope = options.scope,

isRoot: isRoot,
getBasePath: function getBasePath() {
getBasePath: function getBasePath() /*: boolean*/ {
return this.encode(EMPTY_STATE);
},
isNotFound: function isNotFound(pathname) {
isNotFound: function isNotFound(pathname /*: string*/) /*: boolean*/ {
return this.decode(pathname) === null;
},
getRedirectBase: function getRedirectBase(pathname) {
getRedirectBase: function getRedirectBase(pathname /*: string*/) /*: ?string*/ {
if (isRoot(pathname)) {

@@ -143,0 +152,0 @@ var basePath = this.getBasePath();

@@ -1,1 +0,33 @@

'use strict';
'use strict';
/*:: export type Segment = {
name: string,
type: 'dir' | 'param'
}*/
/*:: export type Transform<T> = {
toState: (value: string) => T,
toPath: (value : T) => string
}*/
// eslint-disable-next-line no-use-before-define
/*:: export type Route = {
raw: string,
segments: Segment[]
}*/
/*:: export type RouteChildren = Node[] | (state : Object) => Node[]*/
/*:: export type Node = {
route: Route,
scope?: string,
transform?: {[key: string] : Transform<*>},
children: RouteChildren
}*/
/*:: export type Router = Node & {
encode: (state : Object) => string,
decode: (path : string) => ?Object
}*/
/*:: export type MatchResult = {
nodes: Node[],
missing: string[],
remaining: string[]
}*/

@@ -7,3 +7,3 @@ 'use strict';

exports.default = arrayify;
function arrayify(val) {
function arrayify /*:: <T>*/(val /*: Array<T> | T*/) /*: Array<T>*/ {
if (Array.isArray(val)) {

@@ -10,0 +10,0 @@ return val;

{
"name": "@sanity/state-router",
"version": "0.99.4",
"version": "0.99.6",
"description": "A path pattern => state object bidirectional mapper",

@@ -23,21 +23,23 @@ "main": "lib/index.js",

"dependencies": {
"lodash": "^4.17.2"
"lodash": "^4.17.4"
},
"devDependencies": {
"babel-plugin-syntax-flow": "^6.18.0",
"babel-plugin-transform-flow-strip-types": "^6.18.0",
"babel-plugin-transform-flow-comments": "^6.22.0",
"babelify": "^7.3.0",
"browserify": "^13.1.1",
"browserify": "^13.3.0",
"error-capture-middleware": "0.0.2",
"eslint-config-bengler": "^2.0.0",
"eslint": "^3.14.0",
"eslint-config-bengler": "^3.0.1",
"eslint-plugin-flowtype": "^2.30.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-react": "^6.7.1",
"eslint-plugin-react": "^6.9.0",
"express": "^4.14.0",
"flow-bin": "^0.35.0",
"history": "^4.4.0",
"flow-bin": "^0.38.0",
"history": "^4.5.1",
"in-publish": "^2.0.0",
"object-inspect": "^1.2.1",
"quickreload": "^2.1.2",
"react": "^15.4.0",
"react-dom": "^15.4.0",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"rebundler": "^0.3.0",

@@ -47,3 +49,3 @@ "remon": "^1.0.2",

"staticr": "^4.0.2",
"tap": "^8.0.0"
"tap": "^9.0.3"
},

@@ -50,0 +52,0 @@ "repository": {

@@ -10,3 +10,3 @@ ## @sanity/state-router

## Usage
## API Usage

@@ -48,2 +48,49 @@ Define the routes for your application and how they should map to application state

## React usage
### Setup routes and provider
```jsx
import {route} from '@sanity/state-router'
import {RouterProvider, withRouter} from '@sanity/state-router/components'
const router = route('/', [
route('/bikes/:bikeId')
])
const history = createHistory()
function handleNavigate(nextUrl, {replace} = {}) {
if (replace) {
history.replace(nextUrl)
} else {
history.push(nextUrl)
}
}
const App = withRouter(function App({router}) {
if (router.state.bikeId) {
return <BikePage id={router.state.bikeId} />
}
return (
<div>
<h1>Welcome</h1>
<StateLink state={{bikeId: 22}}>Go to bike 22</StateLink>
</div>
)
})
function render(location) {
ReactDOM.render((
<RouterProvider
router={router}
onNavigate={handleNavigate}
state={router.decode(location.pathname)}>
<App />
</RouterProvider>
), document.getElementById('container'))
}
history.listen(() => render(document.location))
```
## API

@@ -185,2 +232,38 @@

## Intents
An _intent_ is a kind of global route that can be used for dispatching user actions. The intent route can be mounted with
```js
route.intents(<basePath>)
```
Intent links bypasses scoping, and will always be mapped to the configured `basePath`.
An intent consists of a name, e.g. `open` and a set of parameters, e.g. `{id: 'abc33'}` and the easiest way to make a link to an intent is using the `IntentLink` React component:
```jsx
<IntentLink intent="open" params={{id: abc33}}>Open document</IntentLink>
```
This will generate an `<a` tag with a href like `/<base path>/open/id=abc33` depending on where the intent handler is mounted
State router comes with a built in intent-route parser that decodes an intent route to route state.
Full example:
```
const router = route('/', [
route('/users/:username'),
route.intents('/intents') // <-- sets up intent routes at the /intents base path
])
```
Decoding the url `/intents/open/id=abc33` will produce the following state:
```js
{
intent: 'open',
params: {id: 'abc33'}
}
```
It is now up to your application logic to translate this intent into an action, and redirect accordingly.
## 404s

@@ -187,0 +270,0 @@

export {default as RouterProvider} from './components/RouterProvider'
export {default as Link} from './components/Link'
export {default as StateLink} from './components/StateLink'
export {default as IntentLink} from './components/IntentLink'
export {default as RouteScope} from './components/RouteScope'
export {default as withRouter} from './components/withRouter'

@@ -0,10 +1,15 @@

// @flow
import React, {PropTypes} from 'react'
import omit from 'lodash/omit'
import Link from './Link'
import type {RouterProviderContext} from './types'
type Props = {
intent: string,
params: Object
}
export default class IntentLink extends React.Component {
static propTypes = {
intent: PropTypes.string.isRequired,
params: PropTypes.object
}
props: Props;
context: RouterProviderContext

@@ -16,4 +21,9 @@ static contextTypes = {

render() {
const {intent, params} = this.props
const {intent, params, children} = this.props
// @todo Temporary hack
if (intent === 'edit' && params.type) {
return <Link href={`/desk/${params.type}/edit/${params.id.replace(/\//g, '.')}`}>{children}</Link>
}
const url = this.context.__internalRouter.resolveIntentLink(intent, params)

@@ -20,0 +30,0 @@ const rest = omit(this.props, 'intent', 'params')

@@ -0,19 +1,29 @@

// @flow
import React, {PropTypes} from 'react'
import omit from 'lodash/omit'
import type {RouterProviderContext} from './types'
function isLeftClickEvent(event) {
function isLeftClickEvent(event : SyntheticMouseEvent) {
return event.button === 0
}
function isModifiedEvent(event) {
function isModifiedEvent(event : SyntheticMouseEvent) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
}
type Props = {
replace: boolean,
onClick: (event : SyntheticMouseEvent) => void,
href: string,
target: string
}
export default class Link extends React.Component {
props: Props
context: RouterProviderContext
static defaultProps = {
replace: false,
}
static propTypes = {
replace: PropTypes.bool
}
static contextTypes = {

@@ -23,16 +33,11 @@ __internalRouter: PropTypes.object

constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
handleClick = (event : SyntheticMouseEvent) : void => {
handleClick(e) {
const {onClick, href, target, replace} = this.props
if (onClick) {
onClick(e)
onClick(event)
}
if (isModifiedEvent(e) || !isLeftClickEvent(e)) {
if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
return

@@ -46,3 +51,4 @@ }

e.preventDefault()
event.preventDefault()
this.context.__internalRouter.navigateUrl(href, {replace})

@@ -49,0 +55,0 @@ }

@@ -1,29 +0,39 @@

import React, {PropTypes} from 'react'
// @flow
import React, {PropTypes, Element} from 'react'
import type {Router} from '../types'
import type {RouterProviderContext, NavigateOptions} from './types'
type Props = {
onNavigate: () => void,
router: Router,
state: Object,
children: Element<*>
}
export default class RouterProvider extends React.Component {
static propTypes = {
onNavigate: PropTypes.func,
router: PropTypes.object,
state: PropTypes.object,
children: PropTypes.node
props: Props
static childContextTypes = {
__internalRouter: PropTypes.object,
router: PropTypes.object
}
navigateUrl = (url, {replace} = {}) => {
navigateUrl = (url : string, options : NavigateOptions = {}) : void => {
const {onNavigate} = this.props
onNavigate(url, {replace})
onNavigate(url, options)
}
navigateState = (nextState, {replace} = {}) => {
this.navigateUrl(this.resolvePathFromState(nextState), {replace})
navigateState = (nextState : Object, options : NavigateOptions = {}) : void => {
this.navigateUrl(this.resolvePathFromState(nextState), options)
}
resolvePathFromState = state => {
resolvePathFromState = (state : Object) : string => {
return this.props.router.encode(state)
}
resolveIntentLink = (intent, params) => {
resolveIntentLink = (intent : string, params : Object) : string => {
return this.props.router.encode({intent, params})
}
getChildContext() {
getChildContext() : RouterProviderContext {
const {state} = this.props

@@ -47,5 +57,1 @@ return {

}
RouterProvider.childContextTypes = {
__internalRouter: PropTypes.object,
router: PropTypes.object
}

@@ -1,8 +0,21 @@

import React, {PropTypes} from 'react'
// @flow
import React, {PropTypes, Element} from 'react'
import isEmpty from '../utils/isEmpty'
import type {RouterProviderContext, NavigateOptions, InternalRouter, ContextRouter} from './types'
type Props = {
scope: string,
children: Element<*>
}
function addScope(routerState : Object, scope : string, scopedState : Object) {
return scopedState && {
...routerState,
[scope]: scopedState
}
}
export default class RouteScope extends React.Component {
static propTypes = {
scope: PropTypes.string,
children: PropTypes.node
}
props: Props
context: RouterProviderContext

@@ -13,28 +26,37 @@ static childContextTypes = RouteScope.contextTypes = {

}
getChildContext() : RouterProviderContext {
const {scope} = this.props
const internalRouter: InternalRouter = this.context.__internalRouter
const router: ContextRouter = this.context.router
getChildContext() {
const {scope} = this.props
const {router, __internalRouter} = this.context
return {
__internalRouter: {
resolvePathFromState: nextState => {
const empty = Object.keys(nextState).length === 0
return __internalRouter.resolvePathFromState(empty ? {} : addScope(nextState))
},
resolveIntentLink: __internalRouter.resolveIntentLink,
navigateUrl: __internalRouter.navigateUrl
resolvePathFromState: this.resolvePathFromState,
resolveIntentLink: internalRouter.resolveIntentLink,
navigateUrl: internalRouter.navigateUrl
},
router: {
navigate: (nextState, options) => {
router.navigate(addScope(nextState), options)
},
navigate: this.navigate,
state: router.state[scope]
}
}
}
function addScope(nextState) {
return nextState && Object.assign({}, router.state, {[scope]: nextState})
}
resolvePathFromState = (nextState: Object): string => {
const context = this.context
const scope = this.props.scope
const nextStateScoped : Object = isEmpty(nextState)
? {}
: addScope(context.router.state, scope, nextState)
return context.__internalRouter.resolvePathFromState(nextStateScoped)
}
navigate = (nextState: Object, options?: NavigateOptions) : void => {
const scope = this.props.scope
const router = this.context.router
router.navigate(addScope(router.state, scope, nextState), options)
}
render() {

@@ -41,0 +63,0 @@ return this.props.children

@@ -0,13 +1,16 @@

// @flow
import React, {PropTypes} from 'react'
import omit from 'lodash/omit'
import Link from './Link'
import type {RouterProviderContext} from './types'
const EMPTY_STATE = {}
type Props = {
state: string,
toIndex: boolean
}
export default class StateLink extends React.Component {
static propTypes = {
state: PropTypes.object,
replace: PropTypes.bool,
toIndex: PropTypes.bool
}
props: Props
context: RouterProviderContext

@@ -23,3 +26,3 @@ static defaultProps = {

resolveUrl() {
resolveUrl() : string {
const {toIndex, state} = this.props

@@ -32,11 +35,14 @@

if (!state && !toIndex) {
// eslint-disable-next-line no-console
console.error(new Error('No state passed to StateLink. If you want to link to an empty state, its better to use the the `toIndex` property'))
}
const nextState = toIndex ? EMPTY_STATE : (state || EMPTY_STATE)
return this.context.__internalRouter.resolvePathFromState(nextState)
}
render() {
const rest = omit(this.props, 'state', 'toIndex')
const rest = omit(this.props, 'replace', 'state', 'toIndex')
return <Link href={this.resolveUrl()} {...rest} />
}
}

@@ -24,21 +24,21 @@ // @flow

const consumedKeys = intersection(stateKeys, requiredParams)
const missingKeys = difference(requiredParams, consumedKeys)
const remainingKeys = difference(stateKeys, consumedKeys)
const consumedParams = intersection(stateKeys, requiredParams)
const missingParams = difference(requiredParams, consumedParams)
const remainingParams = difference(stateKeys, consumedParams)
if (missingKeys.length > 0) {
return createMatchResult([], missingKeys, [])
if (missingParams.length > 0) {
return createMatchResult([], missingParams, [])
}
if (remainingKeys.length === 0) {
if (remainingParams.length === 0) {
return createMatchResult([node], [], [])
}
const children = (typeof node.children === 'function') ? node.children(state) : node.children
const children = ((typeof node.children === 'function') ? node.children(state) : node.children) || []
if (remainingKeys.length > 0 && children.length === 0) {
return createMatchResult([], remainingKeys, [])
if (remainingParams.length > 0 && children.length === 0) {
return createMatchResult([], remainingParams, [])
}
const remainingState = pick(state, remainingKeys)
const remainingState = pick(state, remainingParams)

@@ -53,3 +53,3 @@ let matchingChild : MatchResult = {nodes: [], remaining: [], missing: []}

if (matchingChild.nodes.length === 0) {
return createMatchResult([], missingKeys, remainingKeys)
return createMatchResult([], missingParams, remainingParams)
}

@@ -56,0 +56,0 @@

@@ -27,3 +27,3 @@ // @flow

const segments: [Segment] = pathname
const segments: Segment[] = pathname
.split('/')

@@ -30,0 +30,0 @@ .map(createSegment)

@@ -12,4 +12,8 @@ // @flow

if (match.remaining.length > 0) {
const formatted = match.remaining.map(key => `${key} (=${JSON.stringify(state[key])})`).join(', ')
throw new Error(`State key${match.remaining.length == 1 ? '' : 's'} not mapped to url params: ${formatted}`)
const remaining = match.remaining
throw new Error(
`Unable to find matching route for state. Could not map the following state key${
remaining.length == 1 ? '' : 's'
} to a valid url: ${remaining.join(', ')}`
)
}

@@ -16,0 +20,0 @@

@@ -58,3 +58,3 @@ // @flow

const examples : [[Object, MatchResult]] = [
const examples : [Object, MatchResult][] = [
[{}, {

@@ -61,0 +61,0 @@ nodes: [], missing: ['bar'], remaining: []

@@ -16,3 +16,3 @@ // @flow

test('throws on unresolvable state', {todo: false}, t => {
test('throws on unresolvable state', t => {
const rootRoute = route('/root', [

@@ -23,5 +23,33 @@ route('/:page', [

])
t.throws(() => resolvePathFromState(rootRoute, {foo: 'bar'}), /.*not mapped .* params.*foo.*/)
t.throws(
() => resolvePathFromState(rootRoute, {foo: 'bar'}),
new Error('Unable to find matching route for state. Could not map the following state key to a valid url: foo')
)
})
test('points to unmapped keys', t => {
const routesDef = route('/:dataset', [
route('/settings/:setting'),
route('/tools/:tool', params => {
if (params.tool === 'desk') {
return [route.scope('desk', '/collections/:collection')]
}
if (params.tool === 'another-tool') {
return [route.scope('foo', '/omg/:nope')]
}
})
])
const state = {
dataset: 'some-dataset',
tool: 'another-tool',
foo: {
nop: 'bar'
},
}
t.throws(
() => resolvePathFromState(routesDef, state),
new Error('Unable to find matching route for state. Could not map the following state keys to a valid url: tool, foo')
)
})
test('Resolves this', t => {

@@ -28,0 +56,0 @@ const routesDef = route('/:dataset', [

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc