@shopify/react-async
Advanced tools
Comparing version 1.0.1 to 1.0.2
@@ -10,2 +10,8 @@ # Changelog | ||
## 1.0.2 - 2019-02-10 | ||
### Fixed | ||
- Fixed an issue where the `<Prefetcher />` would not watch user interactions by default. | ||
## 1.0.1 - 2019-02-10 | ||
@@ -12,0 +18,0 @@ |
import * as React from 'react'; | ||
import { IfAllOptionalKeys } from '@shopify/useful-types'; | ||
import { Prefetchable } from '../shared'; | ||
declare type PropMapper<Props> = (url: string) => Props; | ||
declare type RegisterOptions<Props> = { | ||
url: string | RegExp; | ||
} & IfAllOptionalKeys<Props, { | ||
mapUrlToProps?: PropMapper<Props>; | ||
}, { | ||
mapUrlToProps: PropMapper<Props>; | ||
}>; | ||
export declare type Record<Props> = RegisterOptions<Props> & { | ||
component: Prefetchable<Props>; | ||
}; | ||
export interface Registration { | ||
path: string | RegExp; | ||
render(url: URL): React.ReactNode; | ||
} | ||
export declare class PrefetchManager { | ||
registered: Set<Record<any>>; | ||
constructor(registered?: Record<any>[]); | ||
register<Props>(component: Prefetchable<Props>, options: RegisterOptions<Props>): () => boolean; | ||
registered: Set<Registration>; | ||
constructor(registered?: Registration[]); | ||
register(registration: Registration): () => boolean; | ||
} | ||
export declare const PrefetchContext: React.Context<PrefetchManager | undefined>; | ||
export {}; | ||
export declare const PrefetchContext: React.Context<PrefetchManager>; |
@@ -9,7 +9,6 @@ "use strict"; | ||
} | ||
PrefetchManager.prototype.register = function (component, options) { | ||
PrefetchManager.prototype.register = function (registration) { | ||
var _this = this; | ||
var record = tslib_1.__assign({ component: component }, options); | ||
this.registered.add(record); | ||
return function () { return _this.registered.delete(record); }; | ||
this.registered.add(registration); | ||
return function () { return _this.registered.delete(registration); }; | ||
}; | ||
@@ -19,2 +18,2 @@ return PrefetchManager; | ||
exports.PrefetchManager = PrefetchManager; | ||
exports.PrefetchContext = React.createContext(undefined); | ||
exports.PrefetchContext = React.createContext(new PrefetchManager()); |
@@ -7,4 +7,4 @@ /// <reference types="react" /> | ||
} | ||
export declare const HOVER_DELAY_MS = 150; | ||
export declare const INTENTION_DELAY_MS = 150; | ||
export declare function Prefetcher(props: Omit<Props, 'manager'>): JSX.Element; | ||
export {}; |
@@ -7,3 +7,3 @@ "use strict"; | ||
var EventListener_1 = require("./EventListener"); | ||
exports.HOVER_DELAY_MS = 150; | ||
exports.INTENTION_DELAY_MS = 150; | ||
var ConnectedPrefetcher = /** @class */ (function (_super) { | ||
@@ -21,3 +21,3 @@ tslib_1.__extends(ConnectedPrefetcher, _super); | ||
} | ||
var url = closestHref(target); | ||
var url = closestUrlFromNode(target); | ||
if (url != null) { | ||
@@ -40,10 +40,10 @@ _this.setState({ url: url }); | ||
} | ||
var closestUrl = closestHref(target); | ||
var relatedUrl = relatedTarget && closestHref(relatedTarget); | ||
var closestUrl = closestUrlFromNode(target); | ||
var relatedUrl = relatedTarget && closestUrlFromNode(relatedTarget); | ||
if (timeout != null && | ||
closestUrl === timeoutUrl && | ||
relatedUrl !== timeoutUrl) { | ||
urlsEqual(closestUrl, timeoutUrl) && | ||
!urlsEqual(relatedUrl, timeoutUrl)) { | ||
_this.clearTimeout(); | ||
} | ||
if (closestUrl === url && relatedUrl !== url) { | ||
if (urlsEqual(closestUrl, url) && !urlsEqual(relatedUrl, url)) { | ||
_this.setState({ url: undefined }); | ||
@@ -58,3 +58,3 @@ } | ||
var _b = _this, timeoutUrl = _b.timeoutUrl, timeout = _b.timeout; | ||
var url = closestHref(target); | ||
var url = closestUrlFromNode(target); | ||
if (url == null) { | ||
@@ -64,3 +64,3 @@ return; | ||
if (timeout) { | ||
if (url === timeoutUrl) { | ||
if (urlsEqual(url, timeoutUrl)) { | ||
return; | ||
@@ -76,3 +76,3 @@ } | ||
_this.setState({ url: url }); | ||
}, exports.HOVER_DELAY_MS); | ||
}, exports.INTENTION_DELAY_MS); | ||
}; | ||
@@ -84,12 +84,7 @@ return _this; | ||
var manager = this.props.manager; | ||
var preloadMarkup = url | ||
? findMatches(manager.registered, url).map(function (_a) { | ||
var matchUrl = _a.url, mapUrlToProps = _a.mapUrlToProps, Component = _a.component; | ||
var additionalProps = mapUrlToProps | ||
? mapUrlToProps(url) || {} | ||
: {}; | ||
var Prefetch = 'Prefetch' in Component ? Component.Prefetch : Component; | ||
return React.createElement(Prefetch, tslib_1.__assign({}, additionalProps, { key: String(matchUrl) })); | ||
}) | ||
: null; | ||
var preloadMarkup = url ? (React.createElement("div", { style: { visibility: 'hidden' } }, findMatches(manager.registered, url).map(function (_a, index) { | ||
var render = _a.render, path = _a.path; | ||
// eslint-disable-next-line react/no-array-index-key | ||
return React.createElement("div", { key: "" + path + index }, render(url)); | ||
}))) : null; | ||
var expensiveListeners = this.prefetchAgressively ? (React.createElement(React.Fragment, null, | ||
@@ -115,5 +110,3 @@ React.createElement(EventListener_1.EventListener, { passive: true, event: "mouseover", handler: this.handleMouseOver }), | ||
function Prefetcher(props) { | ||
return (React.createElement(prefetch_1.PrefetchContext.Consumer, null, function (manager) { | ||
return manager ? React.createElement(ConnectedPrefetcher, tslib_1.__assign({}, props, { manager: manager })) : null; | ||
})); | ||
return (React.createElement(prefetch_1.PrefetchContext.Consumer, null, function (manager) { return React.createElement(ConnectedPrefetcher, tslib_1.__assign({}, props, { manager: manager })); })); | ||
} | ||
@@ -126,5 +119,9 @@ exports.Prefetcher = Prefetcher; | ||
} | ||
function urlsEqual(first, second) { | ||
return ((first == null && first === second) || | ||
(first != null && second != null && first.href === second.href)); | ||
} | ||
function findMatches(records, url) { | ||
return tslib_1.__spread(records).filter(function (_a) { | ||
var match = _a.url; | ||
var match = _a.path; | ||
return matches(url, match); | ||
@@ -134,5 +131,7 @@ }); | ||
function matches(url, matcher) { | ||
return typeof matcher === 'string' ? matcher === url : matcher.test(url); | ||
return typeof matcher === 'string' | ||
? matcher === url.pathname | ||
: matcher.test(url.pathname); | ||
} | ||
function closestHref(element) { | ||
function closestUrlFromNode(element) { | ||
if (!(element instanceof HTMLElement)) { | ||
@@ -142,9 +141,9 @@ return undefined; | ||
// data-href is a hack for resource list doing the <a> as a sibling | ||
var closestHref = element.closest('[href], [data-href]'); | ||
if (closestHref == null || !(closestHref instanceof HTMLElement)) { | ||
var closestUrl = element.closest('[href], [data-href]'); | ||
if (closestUrl == null || !(closestUrl instanceof HTMLElement)) { | ||
return undefined; | ||
} | ||
var url = closestHref.getAttribute('href') || closestHref.getAttribute('data-href'); | ||
var url = closestUrl.getAttribute('href') || closestUrl.getAttribute('data-href'); | ||
try { | ||
return url ? new URL(url, window.location.href).pathname : undefined; | ||
return url ? new URL(url, window.location.href) : undefined; | ||
} | ||
@@ -151,0 +150,0 @@ catch (error) { |
@@ -1,18 +0,10 @@ | ||
/// <reference types="react" /> | ||
import { Omit, IfAllOptionalKeys } from '@shopify/useful-types'; | ||
import { Prefetchable } from './shared'; | ||
import * as React from 'react'; | ||
import { Omit } from '@shopify/useful-types'; | ||
import { PrefetchManager } from './context/prefetch'; | ||
interface UrlMapperPropsNonOptional<Props> { | ||
mapUrlToProps(path: string): Props; | ||
} | ||
interface UrlMapperPropsOptional<Props> { | ||
mapUrlToProps?(path: string): Props | void; | ||
} | ||
declare type UrlMapperProps<Props> = IfAllOptionalKeys<Props, UrlMapperPropsOptional<Props>, UrlMapperPropsNonOptional<Props>>; | ||
interface Props<PrefetchProps> { | ||
interface Props { | ||
manager: PrefetchManager; | ||
url: string | RegExp; | ||
component: Prefetchable<PrefetchProps>; | ||
path: string | RegExp; | ||
render(url: URL): React.ReactNode; | ||
} | ||
export declare function PrefetchRoute<PrefetchProps>(props: Omit<Props<PrefetchProps>, 'manager'> & UrlMapperProps<PrefetchProps>): JSX.Element; | ||
export declare function PrefetchRoute(props: Omit<Props, 'manager'>): JSX.Element; | ||
export {}; |
@@ -12,6 +12,6 @@ "use strict"; | ||
ConnectedPrefetchRoute.prototype.componentDidMount = function () { | ||
var _a = this.props, manager = _a.manager, component = _a.component, url = _a.url, mapUrlToProps = _a.mapUrlToProps; | ||
this.unregister = manager.register(component, { | ||
url: url, | ||
mapUrlToProps: mapUrlToProps, | ||
var _a = this.props, manager = _a.manager, path = _a.path, render = _a.render; | ||
this.unregister = manager.register({ | ||
path: path, | ||
render: render, | ||
}); | ||
@@ -30,6 +30,4 @@ }; | ||
function PrefetchRoute(props) { | ||
return (React.createElement(prefetch_1.PrefetchContext.Consumer, null, function (manager) { | ||
return manager ? (React.createElement(ConnectedPrefetchRoute, tslib_1.__assign({ manager: manager }, props))) : null; | ||
})); | ||
return (React.createElement(prefetch_1.PrefetchContext.Consumer, null, function (manager) { return (React.createElement(ConnectedPrefetchRoute, tslib_1.__assign({ manager: manager }, props))); })); | ||
} | ||
exports.PrefetchRoute = PrefetchRoute; |
@@ -1,4 +0,4 @@ | ||
import { PrefetchManager, Record } from '../context/prefetch'; | ||
import { PrefetchManager, Registration } from '../context/prefetch'; | ||
import { AsyncAssetManager } from '../context/assets'; | ||
export declare function createPrefetchManager(registered?: Record<any>[]): PrefetchManager; | ||
export declare function createPrefetchManager(registered?: Registration[]): PrefetchManager; | ||
export declare function createAsyncAssetManager(): AsyncAssetManager; |
{ | ||
"name": "@shopify/react-async", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "Tools for creating powerful, asynchronously-loaded React components.", |
@@ -102,3 +102,3 @@ # `@shopify/react-async` | ||
The `PrefetchRoute` component allows you to use the asynchronous component you generated with `createAsyncComponent` and automatically render its `Prefetch` component when the user looks like they are going to navigate to a page that uses it. This component takes as its props the asynchronous component, a URL pattern to look for (a string or `RegExp` that is compared against the target pathname), and an optional function that can map the URL to a set of props for your prefetch component. | ||
The `PrefetchRoute` component allows you to use the asynchronous component you generated with `createAsyncComponent` and automatically render its `Prefetch` component when the user looks like they are going to navigate to a page that uses it. This component takes as its props the asynchronous component, a path pattern to look for (a string or `RegExp` that is compared against the target pathname), and an optional function that can map the URL to a set of props for your prefetch component. | ||
@@ -118,9 +118,6 @@ Consider this async component: | ||
<PrefetchRoute | ||
component={ProductDetails} | ||
url={/^\/products\/(\d+)$/} | ||
mapUrlToProps={pathname => { | ||
// If you don’t pass the right types, or don’t pass mapUrlToProps() | ||
// at all, you’ll get a type error. | ||
const id = pathname.split('/').pop(); | ||
return {id}; | ||
path={/^\/products\/(\d+)$/} | ||
render={url => { | ||
const id = url.pathname.split('/').pop(); | ||
return <ProductDetails.Prefetch id={id} />; | ||
}} | ||
@@ -127,0 +124,0 @@ /> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33585
553
183