Finally give in and use React, minor tweeks.
Preact (a minimalistic React alternative) had too many bugs ...
This commit is contained in:
parent
3f0ee289c4
commit
b2128609ac
2
.gitignore
vendored
2
.gitignore
vendored
@ -27,3 +27,5 @@ taler-wallet-*.sig
|
||||
# Even though node_modules are tracked in git,
|
||||
# per default we don't want them to show up in git status
|
||||
node_modules
|
||||
|
||||
coverage
|
||||
|
11
README
11
README
@ -13,12 +13,13 @@ containing the extension in ./build/taler-wallet-$VERSION.zip
|
||||
Dependencies
|
||||
============
|
||||
|
||||
This project has the following third-party runtime dependencies:
|
||||
See thirdparty/README for a list of dependencies that are checked into this
|
||||
repository as a git subtree.
|
||||
|
||||
This project also has the following third-party runtime dependencies, which
|
||||
are checked in compiled or minified form:
|
||||
- React (Facebook Inc., BSD License)
|
||||
|
||||
* URI.js: URL parsing and construction
|
||||
* jed: gettext parsing for JavaScript
|
||||
* system.js: Module loader for browsers that don't support
|
||||
ES6 modules
|
||||
|
||||
Emscripten compiled dependencies:
|
||||
* libgnunetutil
|
||||
|
@ -128,7 +128,7 @@ const paths = {
|
||||
const tsBaseArgs = {
|
||||
target: "es6",
|
||||
jsx: "react",
|
||||
reactNamespace: "preact",
|
||||
reactNamespace: "React",
|
||||
experimentalDecorators: true,
|
||||
module: "system",
|
||||
sourceMap: true,
|
||||
|
@ -30,16 +30,15 @@ export interface StateHolder<T> {
|
||||
* Component that doesn't hold its state in one object,
|
||||
* but has multiple state holders.
|
||||
*/
|
||||
export abstract class ImplicitStateComponent<PropType> extends preact.Component<PropType, any> {
|
||||
export abstract class ImplicitStateComponent<PropType> extends React.Component<PropType, any> {
|
||||
makeState<StateType>(initial: StateType): StateHolder<StateType> {
|
||||
let state: StateType = initial;
|
||||
return (s?: StateType): StateType => {
|
||||
if (s !== undefined) {
|
||||
state = s;
|
||||
// In preact, this will always schedule a (debounced) redraw
|
||||
this.setState({} as any);
|
||||
}
|
||||
return state;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
560
lib/decl/preact.d.ts
vendored
560
lib/decl/preact.d.ts
vendored
@ -1,560 +0,0 @@
|
||||
declare namespace preact {
|
||||
interface ComponentProps {
|
||||
children?:JSX.Element[];
|
||||
key?:string;
|
||||
}
|
||||
|
||||
interface PreactHTMLAttributes {
|
||||
key?:string;
|
||||
}
|
||||
|
||||
interface VNode {
|
||||
nodeName:ComponentConstructor<any, any>|string;
|
||||
attributes:{[name:string]:any};
|
||||
children:VNode[];
|
||||
key:string;
|
||||
}
|
||||
|
||||
interface ComponentLifecycle<PropsType, StateType> {
|
||||
componentWillMount?():void;
|
||||
|
||||
componentDidMount?():void;
|
||||
|
||||
componentWillUnmount?():void;
|
||||
|
||||
componentDidUnmount?():void;
|
||||
|
||||
componentWillReceiveProps?(props:PropsType):void;
|
||||
|
||||
shouldComponentUpdate?(props:PropsType):boolean;
|
||||
|
||||
componentWillUpdate?():void;
|
||||
|
||||
componentDidUpdate?():void;
|
||||
}
|
||||
|
||||
interface ComponentConstructor<PropsType, StateType> {
|
||||
new (props?:PropsType):Component<PropsType, StateType>;
|
||||
}
|
||||
|
||||
abstract class Component<PropsType, StateType> implements ComponentLifecycle<PropsType, StateType> {
|
||||
constructor(props?:PropsType);
|
||||
|
||||
state:StateType;
|
||||
props:PropsType & ComponentProps;
|
||||
base:HTMLElement;
|
||||
|
||||
linkState:(name:string) => void;
|
||||
|
||||
setState(state:StateType, opts?:any):void;
|
||||
|
||||
forceUpdate():void;
|
||||
|
||||
abstract render(props:PropsType & ComponentProps, state:any):JSX.Element;
|
||||
}
|
||||
|
||||
function h<PropsType>(node:ComponentConstructor<PropsType, any>, params:PropsType, ...children:(JSX.Element|string)[]):JSX.Element;
|
||||
function h(node:string, params:JSX.HTMLAttributes|JSX.SVGAttributes, ...children:(JSX.Element|string)[]):JSX.Element;
|
||||
|
||||
function createElement<PropsType>(node:ComponentConstructor<PropsType, any>, params:PropsType, ...children:(JSX.Element|string)[]):JSX.Element;
|
||||
function createElement(node:string, params:JSX.HTMLAttributes|JSX.SVGAttributes, ...children:(JSX.Element|string)[]):JSX.Element;
|
||||
|
||||
function render(node:JSX.Element, parent:Element, merge?:boolean):Element;
|
||||
|
||||
function rerender():void;
|
||||
|
||||
function cloneElement(element:JSX.Element, props:any):JSX.Element;
|
||||
|
||||
|
||||
var options:{
|
||||
syncComponentUpdates?:boolean;
|
||||
debounceRendering?:(render:() => void) => void;
|
||||
vnode?:(vnode:VNode) => void;
|
||||
event?:(event:Event) => Event;
|
||||
};
|
||||
}
|
||||
|
||||
declare module "preact" {
|
||||
export = preact;
|
||||
}
|
||||
|
||||
declare namespace JSX {
|
||||
interface Element extends preact.VNode {
|
||||
|
||||
}
|
||||
|
||||
interface ElementClass extends preact.Component<any, any> {
|
||||
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props:any;
|
||||
}
|
||||
|
||||
interface SVGAttributes {
|
||||
clipPath?:string;
|
||||
cx?:number | string;
|
||||
cy?:number | string;
|
||||
d?:string;
|
||||
dx?:number | string;
|
||||
dy?:number | string;
|
||||
fill?:string;
|
||||
fillOpacity?:number | string;
|
||||
fontFamily?:string;
|
||||
fontSize?:number | string;
|
||||
fx?:number | string;
|
||||
fy?:number | string;
|
||||
gradientTransform?:string;
|
||||
gradientUnits?:string;
|
||||
markerEnd?:string;
|
||||
markerMid?:string;
|
||||
markerStart?:string;
|
||||
offset?:number | string;
|
||||
opacity?:number | string;
|
||||
patternContentUnits?:string;
|
||||
patternUnits?:string;
|
||||
points?:string;
|
||||
preserveAspectRatio?:string;
|
||||
r?:number | string;
|
||||
rx?:number | string;
|
||||
ry?:number | string;
|
||||
spreadMethod?:string;
|
||||
stopColor?:string;
|
||||
stopOpacity?:number | string;
|
||||
stroke?:string;
|
||||
strokeDasharray?:string;
|
||||
strokeLinecap?:string;
|
||||
strokeMiterlimit?:string;
|
||||
strokeOpacity?:number | string;
|
||||
strokeWidth?:number | string;
|
||||
textAnchor?:string;
|
||||
transform?:string;
|
||||
version?:string;
|
||||
viewBox?:string;
|
||||
x1?:number | string;
|
||||
x2?:number | string;
|
||||
x?:number | string;
|
||||
xlinkActuate?:string;
|
||||
xlinkArcrole?:string;
|
||||
xlinkHref?:string;
|
||||
xlinkRole?:string;
|
||||
xlinkShow?:string;
|
||||
xlinkTitle?:string;
|
||||
xlinkType?:string;
|
||||
xmlBase?:string;
|
||||
xmlLang?:string;
|
||||
xmlSpace?:string;
|
||||
y1?:number | string;
|
||||
y2?:number | string;
|
||||
y?:number | string;
|
||||
}
|
||||
|
||||
interface PathAttributes {
|
||||
d:string;
|
||||
}
|
||||
|
||||
interface EventHandler<E extends Event> {
|
||||
(event:E):void;
|
||||
}
|
||||
|
||||
type ClipboardEventHandler = EventHandler<ClipboardEvent>;
|
||||
type CompositionEventHandler = EventHandler<CompositionEvent>;
|
||||
type DragEventHandler = EventHandler<DragEvent>;
|
||||
type FocusEventHandler = EventHandler<FocusEvent>;
|
||||
type KeyboardEventHandler = EventHandler<KeyboardEvent>;
|
||||
type MouseEventHandler = EventHandler<MouseEvent>;
|
||||
type TouchEventHandler = EventHandler<TouchEvent>;
|
||||
type UIEventHandler = EventHandler<UIEvent>;
|
||||
type WheelEventHandler = EventHandler<WheelEvent>;
|
||||
type AnimationEventHandler = EventHandler<AnimationEvent>;
|
||||
type TransitionEventHandler = EventHandler<TransitionEvent>;
|
||||
|
||||
type GenericEventHandler = EventHandler<Event>;
|
||||
|
||||
interface DOMAttributed {
|
||||
// Clipboard Events
|
||||
onCopy?:ClipboardEventHandler;
|
||||
onCut?:ClipboardEventHandler;
|
||||
onPaste?:ClipboardEventHandler;
|
||||
|
||||
// Composition Events
|
||||
onCompositionEnd?:CompositionEventHandler;
|
||||
onCompositionStart?:CompositionEventHandler;
|
||||
onCompositionUpdate?:CompositionEventHandler;
|
||||
|
||||
// Focus Events
|
||||
onFocus?:FocusEventHandler;
|
||||
onBlur?:FocusEventHandler;
|
||||
|
||||
// Form Events
|
||||
onChange?:GenericEventHandler;
|
||||
onInput?:GenericEventHandler;
|
||||
onSubmit?:GenericEventHandler;
|
||||
|
||||
// Keyboard Events
|
||||
onKeyDown?:KeyboardEventHandler;
|
||||
onKeyPress?:KeyboardEventHandler;
|
||||
onKeyUp?:KeyboardEventHandler;
|
||||
|
||||
// Media Events
|
||||
onAbort?:GenericEventHandler;
|
||||
onCanPlay?:GenericEventHandler;
|
||||
onCanPlayThrough?:GenericEventHandler;
|
||||
onDurationChange?:GenericEventHandler;
|
||||
onEmptied?:GenericEventHandler;
|
||||
onEncrypted?:GenericEventHandler;
|
||||
onEnded?:GenericEventHandler;
|
||||
onLoadedData?:GenericEventHandler;
|
||||
onLoadedMetadata?:GenericEventHandler;
|
||||
onLoadStart?:GenericEventHandler;
|
||||
onPause?:GenericEventHandler;
|
||||
onPlay?:GenericEventHandler;
|
||||
onPlaying?:GenericEventHandler;
|
||||
onProgress?:GenericEventHandler;
|
||||
onRateChange?:GenericEventHandler;
|
||||
onSeeked?:GenericEventHandler;
|
||||
onSeeking?:GenericEventHandler;
|
||||
onStalled?:GenericEventHandler;
|
||||
onSuspend?:GenericEventHandler;
|
||||
onTimeUpdate?:GenericEventHandler;
|
||||
onVolumeChange?:GenericEventHandler;
|
||||
onWaiting?:GenericEventHandler;
|
||||
|
||||
// MouseEvents
|
||||
onClick?:MouseEventHandler;
|
||||
onContextMenu?:MouseEventHandler;
|
||||
onDoubleClick?:MouseEventHandler;
|
||||
onDrag?:DragEventHandler;
|
||||
onDragEnd?:DragEventHandler;
|
||||
onDragEnter?:DragEventHandler;
|
||||
onDragExit?:DragEventHandler;
|
||||
onDragLeave?:DragEventHandler;
|
||||
onDragOver?:DragEventHandler;
|
||||
onDragStart?:DragEventHandler;
|
||||
onDrop?:DragEventHandler;
|
||||
onMouseDown?:MouseEventHandler;
|
||||
onMouseEnter?:MouseEventHandler;
|
||||
onMouseLeave?:MouseEventHandler;
|
||||
onMouseMove?:MouseEventHandler;
|
||||
onMouseOut?:MouseEventHandler;
|
||||
onMouseOver?:MouseEventHandler;
|
||||
onMouseUp?:MouseEventHandler;
|
||||
|
||||
// Selection Events
|
||||
onSelect?:GenericEventHandler;
|
||||
|
||||
// Touch Events
|
||||
onTouchCancel?:TouchEventHandler;
|
||||
onTouchEnd?:TouchEventHandler;
|
||||
onTouchMove?:TouchEventHandler;
|
||||
onTouchStart?:TouchEventHandler;
|
||||
|
||||
// UI Events
|
||||
onScroll?:UIEventHandler;
|
||||
|
||||
// Wheel Events
|
||||
onWheel?:WheelEventHandler;
|
||||
|
||||
// Animation Events
|
||||
onAnimationStart?:AnimationEventHandler;
|
||||
onAnimationEnd?:AnimationEventHandler;
|
||||
onAnimationIteration?:AnimationEventHandler;
|
||||
|
||||
// Transition Events
|
||||
onTransitionEnd?:TransitionEventHandler;
|
||||
}
|
||||
|
||||
interface HTMLAttributes extends preact.PreactHTMLAttributes, DOMAttributed {
|
||||
// Standard HTML Attributes
|
||||
accept?:string;
|
||||
acceptCharset?:string;
|
||||
accessKey?:string;
|
||||
action?:string;
|
||||
allowFullScreen?:boolean;
|
||||
allowTransparency?:boolean;
|
||||
alt?:string;
|
||||
async?:boolean;
|
||||
autocomplete?:string;
|
||||
autofocus?:boolean;
|
||||
autoPlay?:boolean;
|
||||
capture?:boolean;
|
||||
cellPadding?:number | string;
|
||||
cellSpacing?:number | string;
|
||||
charSet?:string;
|
||||
challenge?:string;
|
||||
checked?:boolean;
|
||||
class?:string;
|
||||
className?:string;
|
||||
cols?:number;
|
||||
colSpan?:number;
|
||||
content?:string;
|
||||
contentEditable?:boolean;
|
||||
contextMenu?:string;
|
||||
controls?:boolean;
|
||||
coords?:string;
|
||||
crossOrigin?:string;
|
||||
data?:string;
|
||||
dateTime?:string;
|
||||
default?:boolean;
|
||||
defer?:boolean;
|
||||
dir?:string;
|
||||
disabled?:boolean;
|
||||
download?:any;
|
||||
draggable?:boolean;
|
||||
encType?:string;
|
||||
form?:string;
|
||||
formAction?:string;
|
||||
formEncType?:string;
|
||||
formMethod?:string;
|
||||
formNoValidate?:boolean;
|
||||
formTarget?:string;
|
||||
frameBorder?:number | string;
|
||||
headers?:string;
|
||||
height?:number | string;
|
||||
hidden?:boolean;
|
||||
high?:number;
|
||||
href?:string;
|
||||
hrefLang?:string;
|
||||
for?:string;
|
||||
httpEquiv?:string;
|
||||
icon?:string;
|
||||
id?:string;
|
||||
inputMode?:string;
|
||||
integrity?:string;
|
||||
is?:string;
|
||||
keyParams?:string;
|
||||
keyType?:string;
|
||||
kind?:string;
|
||||
label?:string;
|
||||
lang?:string;
|
||||
list?:string;
|
||||
loop?:boolean;
|
||||
low?:number;
|
||||
manifest?:string;
|
||||
marginHeight?:number;
|
||||
marginWidth?:number;
|
||||
max?:number | string;
|
||||
maxLength?:number;
|
||||
media?:string;
|
||||
mediaGroup?:string;
|
||||
method?:string;
|
||||
min?:number | string;
|
||||
minLength?:number;
|
||||
multiple?:boolean;
|
||||
muted?:boolean;
|
||||
name?:string;
|
||||
noValidate?:boolean;
|
||||
open?:boolean;
|
||||
optimum?:number;
|
||||
pattern?:string;
|
||||
placeholder?:string;
|
||||
poster?:string;
|
||||
preload?:string;
|
||||
radioGroup?:string;
|
||||
readOnly?:boolean;
|
||||
rel?:string;
|
||||
required?:boolean;
|
||||
role?:string;
|
||||
rows?:number;
|
||||
rowSpan?:number;
|
||||
sandbox?:string;
|
||||
scope?:string;
|
||||
scoped?:boolean;
|
||||
scrolling?:string;
|
||||
seamless?:boolean;
|
||||
selected?:boolean;
|
||||
shape?:string;
|
||||
size?:number;
|
||||
sizes?:string;
|
||||
span?:number;
|
||||
spellCheck?:boolean;
|
||||
src?:string;
|
||||
srcset?:string;
|
||||
srcDoc?:string;
|
||||
srcLang?:string;
|
||||
srcSet?:string;
|
||||
start?:number;
|
||||
step?:number | string;
|
||||
style?:any;
|
||||
summary?:string;
|
||||
tabIndex?:number;
|
||||
target?:string;
|
||||
title?:string;
|
||||
type?:string;
|
||||
useMap?:string;
|
||||
value?:string | string[];
|
||||
width?:number | string;
|
||||
wmode?:string;
|
||||
wrap?:string;
|
||||
|
||||
// RDFa Attributes
|
||||
about?:string;
|
||||
datatype?:string;
|
||||
inlist?:any;
|
||||
prefix?:string;
|
||||
property?:string;
|
||||
resource?:string;
|
||||
typeof?:string;
|
||||
vocab?:string;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// HTML
|
||||
a:HTMLAttributes;
|
||||
abbr:HTMLAttributes;
|
||||
address:HTMLAttributes;
|
||||
area:HTMLAttributes;
|
||||
article:HTMLAttributes;
|
||||
aside:HTMLAttributes;
|
||||
audio:HTMLAttributes;
|
||||
b:HTMLAttributes;
|
||||
base:HTMLAttributes;
|
||||
bdi:HTMLAttributes;
|
||||
bdo:HTMLAttributes;
|
||||
big:HTMLAttributes;
|
||||
blockquote:HTMLAttributes;
|
||||
body:HTMLAttributes;
|
||||
br:HTMLAttributes;
|
||||
button:HTMLAttributes;
|
||||
canvas:HTMLAttributes;
|
||||
caption:HTMLAttributes;
|
||||
cite:HTMLAttributes;
|
||||
code:HTMLAttributes;
|
||||
col:HTMLAttributes;
|
||||
colgroup:HTMLAttributes;
|
||||
data:HTMLAttributes;
|
||||
datalist:HTMLAttributes;
|
||||
dd:HTMLAttributes;
|
||||
del:HTMLAttributes;
|
||||
details:HTMLAttributes;
|
||||
dfn:HTMLAttributes;
|
||||
dialog:HTMLAttributes;
|
||||
div:HTMLAttributes;
|
||||
dl:HTMLAttributes;
|
||||
dt:HTMLAttributes;
|
||||
em:HTMLAttributes;
|
||||
embed:HTMLAttributes;
|
||||
fieldset:HTMLAttributes;
|
||||
figcaption:HTMLAttributes;
|
||||
figure:HTMLAttributes;
|
||||
footer:HTMLAttributes;
|
||||
form:HTMLAttributes;
|
||||
h1:HTMLAttributes;
|
||||
h2:HTMLAttributes;
|
||||
h3:HTMLAttributes;
|
||||
h4:HTMLAttributes;
|
||||
h5:HTMLAttributes;
|
||||
h6:HTMLAttributes;
|
||||
head:HTMLAttributes;
|
||||
header:HTMLAttributes;
|
||||
hr:HTMLAttributes;
|
||||
html:HTMLAttributes;
|
||||
i:HTMLAttributes;
|
||||
iframe:HTMLAttributes;
|
||||
img:HTMLAttributes;
|
||||
input:HTMLAttributes;
|
||||
ins:HTMLAttributes;
|
||||
kbd:HTMLAttributes;
|
||||
keygen:HTMLAttributes;
|
||||
label:HTMLAttributes;
|
||||
legend:HTMLAttributes;
|
||||
li:HTMLAttributes;
|
||||
link:HTMLAttributes;
|
||||
main:HTMLAttributes;
|
||||
map:HTMLAttributes;
|
||||
mark:HTMLAttributes;
|
||||
menu:HTMLAttributes;
|
||||
menuitem:HTMLAttributes;
|
||||
meta:HTMLAttributes;
|
||||
meter:HTMLAttributes;
|
||||
nav:HTMLAttributes;
|
||||
noscript:HTMLAttributes;
|
||||
object:HTMLAttributes;
|
||||
ol:HTMLAttributes;
|
||||
optgroup:HTMLAttributes;
|
||||
option:HTMLAttributes;
|
||||
output:HTMLAttributes;
|
||||
p:HTMLAttributes;
|
||||
param:HTMLAttributes;
|
||||
picture:HTMLAttributes;
|
||||
pre:HTMLAttributes;
|
||||
progress:HTMLAttributes;
|
||||
q:HTMLAttributes;
|
||||
rp:HTMLAttributes;
|
||||
rt:HTMLAttributes;
|
||||
ruby:HTMLAttributes;
|
||||
s:HTMLAttributes;
|
||||
samp:HTMLAttributes;
|
||||
script:HTMLAttributes;
|
||||
section:HTMLAttributes;
|
||||
select:HTMLAttributes;
|
||||
small:HTMLAttributes;
|
||||
source:HTMLAttributes;
|
||||
span:HTMLAttributes;
|
||||
strong:HTMLAttributes;
|
||||
style:HTMLAttributes;
|
||||
sub:HTMLAttributes;
|
||||
summary:HTMLAttributes;
|
||||
sup:HTMLAttributes;
|
||||
table:HTMLAttributes;
|
||||
tbody:HTMLAttributes;
|
||||
td:HTMLAttributes;
|
||||
textarea:HTMLAttributes;
|
||||
tfoot:HTMLAttributes;
|
||||
th:HTMLAttributes;
|
||||
thead:HTMLAttributes;
|
||||
time:HTMLAttributes;
|
||||
title:HTMLAttributes;
|
||||
tr:HTMLAttributes;
|
||||
track:HTMLAttributes;
|
||||
u:HTMLAttributes;
|
||||
ul:HTMLAttributes;
|
||||
"var":HTMLAttributes;
|
||||
video:HTMLAttributes;
|
||||
wbr:HTMLAttributes;
|
||||
|
||||
//SVG
|
||||
svg:SVGAttributes;
|
||||
|
||||
circle:SVGAttributes;
|
||||
clipPath:SVGAttributes;
|
||||
defs:SVGAttributes;
|
||||
ellipse:SVGAttributes;
|
||||
feBlend:SVGAttributes;
|
||||
feColorMatrix:SVGAttributes;
|
||||
feComponentTransfer:SVGAttributes;
|
||||
feComposite:SVGAttributes;
|
||||
feConvolveMatrix:SVGAttributes;
|
||||
feDiffuseLighting:SVGAttributes;
|
||||
feDisplacementMap:SVGAttributes;
|
||||
feFlood:SVGAttributes;
|
||||
feGaussianBlur:SVGAttributes;
|
||||
feImage:SVGAttributes;
|
||||
feMerge:SVGAttributes;
|
||||
feMergeNode:SVGAttributes;
|
||||
feMorphology:SVGAttributes;
|
||||
feOffset:SVGAttributes;
|
||||
feSpecularLighting:SVGAttributes;
|
||||
feTile:SVGAttributes;
|
||||
feTurbulence:SVGAttributes;
|
||||
filter:SVGAttributes;
|
||||
foreignObject:SVGAttributes;
|
||||
g:SVGAttributes;
|
||||
image:SVGAttributes;
|
||||
line:SVGAttributes;
|
||||
linearGradient:SVGAttributes;
|
||||
marker:SVGAttributes;
|
||||
mask:SVGAttributes;
|
||||
path:SVGAttributes;
|
||||
pattern:SVGAttributes;
|
||||
polygon:SVGAttributes;
|
||||
polyline:SVGAttributes;
|
||||
radialGradient:SVGAttributes;
|
||||
rect:SVGAttributes;
|
||||
stop:SVGAttributes;
|
||||
symbol:SVGAttributes;
|
||||
text:SVGAttributes;
|
||||
tspan:SVGAttributes;
|
||||
use:SVGAttributes;
|
||||
}
|
||||
}
|
75
lib/decl/react-dom.d.ts
vendored
Normal file
75
lib/decl/react-dom.d.ts
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Type definitions for React v0.14 (react-dom)
|
||||
// Project: http://facebook.github.io/react/
|
||||
// Definitions by: Asana <https://asana.com>, AssureSign <http://www.assuresign.com>, Microsoft <https://microsoft.com>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
/// <reference path="react.d.ts" />
|
||||
|
||||
declare namespace __React {
|
||||
namespace __DOM {
|
||||
function findDOMNode<E extends Element>(instance: ReactInstance): E;
|
||||
function findDOMNode(instance: ReactInstance): Element;
|
||||
|
||||
function render<P extends DOMAttributes, T extends Element>(
|
||||
element: DOMElement<P, T>,
|
||||
container: Element,
|
||||
callback?: (element: T) => any): T;
|
||||
function render<P>(
|
||||
element: SFCElement<P>,
|
||||
container: Element,
|
||||
callback?: () => any): void;
|
||||
function render<P, T extends Component<P, ComponentState>>(
|
||||
element: CElement<P, T>,
|
||||
container: Element,
|
||||
callback?: (component: T) => any): T;
|
||||
function render<P>(
|
||||
element: ReactElement<P>,
|
||||
container: Element,
|
||||
callback?: (component?: Component<P, ComponentState> | Element) => any): Component<P, ComponentState> | Element | void;
|
||||
|
||||
function unmountComponentAtNode(container: Element): boolean;
|
||||
|
||||
var version: string;
|
||||
|
||||
function unstable_batchedUpdates<A, B>(callback: (a: A, b: B) => any, a: A, b: B): void;
|
||||
function unstable_batchedUpdates<A>(callback: (a: A) => any, a: A): void;
|
||||
function unstable_batchedUpdates(callback: () => any): void;
|
||||
|
||||
function unstable_renderSubtreeIntoContainer<P extends DOMAttributes, T extends Element>(
|
||||
parentComponent: Component<any, any>,
|
||||
element: DOMElement<P, T>,
|
||||
container: Element,
|
||||
callback?: (element: T) => any): T;
|
||||
function unstable_renderSubtreeIntoContainer<P, T extends Component<P, ComponentState>>(
|
||||
parentComponent: Component<any, any>,
|
||||
element: CElement<P, T>,
|
||||
container: Element,
|
||||
callback?: (component: T) => any): T;
|
||||
function render<P>(
|
||||
parentComponent: Component<any, any>,
|
||||
element: SFCElement<P>,
|
||||
container: Element,
|
||||
callback?: () => any): void;
|
||||
function unstable_renderSubtreeIntoContainer<P>(
|
||||
parentComponent: Component<any, any>,
|
||||
element: ReactElement<P>,
|
||||
container: Element,
|
||||
callback?: (component?: Component<P, ComponentState> | Element) => any): Component<P, ComponentState> | Element | void;
|
||||
}
|
||||
|
||||
namespace __DOMServer {
|
||||
function renderToString(element: ReactElement<any>): string;
|
||||
function renderToStaticMarkup(element: ReactElement<any>): string;
|
||||
var version: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "react-dom" {
|
||||
import DOM = __React.__DOM;
|
||||
export = DOM;
|
||||
}
|
||||
|
||||
declare module "react-dom/server" {
|
||||
import DOMServer = __React.__DOMServer;
|
||||
export = DOMServer;
|
||||
}
|
11
lib/decl/react-global.d.ts
vendored
Normal file
11
lib/decl/react-global.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// Type definitions for React v0.14 (namespace)
|
||||
// Project: http://facebook.github.io/react/
|
||||
// Definitions by: Asana <https://asana.com>, AssureSign <http://www.assuresign.com>, Microsoft <https://microsoft.com>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
/// <reference path="react.d.ts" />
|
||||
/// <reference path="react-dom.d.ts" />
|
||||
|
||||
import React = __React;
|
||||
import ReactDOM = __React.__DOM;
|
||||
|
2547
lib/decl/react.d.ts
vendored
Normal file
2547
lib/decl/react.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
lib/refs.d.ts
vendored
1
lib/refs.d.ts
vendored
@ -4,3 +4,4 @@
|
||||
/// <reference path="decl/lib.es6.d.ts" />
|
||||
/// <reference path="decl/urijs/URIjs.d.ts" />
|
||||
/// <reference path="decl/systemjs/systemjs.d.ts" />
|
||||
/// <reference path="decl/react-global.d.ts" />
|
||||
|
480
lib/vendor/preact.js
vendored
480
lib/vendor/preact.js
vendored
@ -1,480 +0,0 @@
|
||||
!function(global, factory) {
|
||||
'object' == typeof exports && 'undefined' != typeof module ? factory(exports) : 'function' == typeof define && define.amd ? define([ 'exports' ], factory) : factory(global.preact = global.preact || {});
|
||||
}(this, function(exports) {
|
||||
function VNode(nodeName, attributes, children) {
|
||||
this.nodeName = nodeName;
|
||||
this.attributes = attributes;
|
||||
this.children = children;
|
||||
this.key = attributes && attributes.key;
|
||||
}
|
||||
function h(nodeName, attributes) {
|
||||
var lastSimple, child, simple, i, children = [];
|
||||
for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
|
||||
if (attributes && attributes.children) {
|
||||
if (!stack.length) stack.push(attributes.children);
|
||||
delete attributes.children;
|
||||
}
|
||||
while (stack.length) if ((child = stack.pop()) instanceof Array) for (i = child.length; i--; ) stack.push(child[i]); else if (null != child && child !== !1) {
|
||||
if ('number' == typeof child || child === !0) child = String(child);
|
||||
simple = 'string' == typeof child;
|
||||
if (simple && lastSimple) children[children.length - 1] += child; else {
|
||||
children.push(child);
|
||||
lastSimple = simple;
|
||||
}
|
||||
}
|
||||
var p = new VNode(nodeName, attributes || void 0, children);
|
||||
if (options.vnode) options.vnode(p);
|
||||
return p;
|
||||
}
|
||||
function extend(obj, props) {
|
||||
if (props) for (var i in props) obj[i] = props[i];
|
||||
return obj;
|
||||
}
|
||||
function clone(obj) {
|
||||
return extend({}, obj);
|
||||
}
|
||||
function delve(obj, key) {
|
||||
for (var p = key.split('.'), i = 0; i < p.length && obj; i++) obj = obj[p[i]];
|
||||
return obj;
|
||||
}
|
||||
function isFunction(obj) {
|
||||
return 'function' == typeof obj;
|
||||
}
|
||||
function isString(obj) {
|
||||
return 'string' == typeof obj;
|
||||
}
|
||||
function hashToClassName(c) {
|
||||
var str = '';
|
||||
for (var prop in c) if (c[prop]) {
|
||||
if (str) str += ' ';
|
||||
str += prop;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function cloneElement(vnode, props) {
|
||||
return h(vnode.nodeName, extend(clone(vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
|
||||
}
|
||||
function createLinkedState(component, key, eventPath) {
|
||||
var path = key.split('.');
|
||||
return function(e) {
|
||||
var t = e && e.target || this, state = {}, obj = state, v = isString(eventPath) ? delve(e, eventPath) : t.nodeName ? t.type.match(/^che|rad/) ? t.checked : t.value : e, i = 0;
|
||||
for (;i < path.length - 1; i++) obj = obj[path[i]] || (obj[path[i]] = !i && component.state[path[i]] || {});
|
||||
obj[path[i]] = v;
|
||||
component.setState(state);
|
||||
};
|
||||
}
|
||||
function enqueueRender(component) {
|
||||
if (!component._dirty && (component._dirty = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
|
||||
}
|
||||
function rerender() {
|
||||
var p, list = items;
|
||||
items = [];
|
||||
while (p = list.pop()) if (p._dirty) renderComponent(p);
|
||||
}
|
||||
function isFunctionalComponent(vnode) {
|
||||
var nodeName = vnode && vnode.nodeName;
|
||||
return nodeName && isFunction(nodeName) && !(nodeName.prototype && nodeName.prototype.render);
|
||||
}
|
||||
function buildFunctionalComponent(vnode, context) {
|
||||
return vnode.nodeName(getNodeProps(vnode), context || EMPTY);
|
||||
}
|
||||
function isSameNodeType(node, vnode) {
|
||||
if (isString(vnode)) return node instanceof Text;
|
||||
if (isString(vnode.nodeName)) return isNamedNode(node, vnode.nodeName);
|
||||
if (isFunction(vnode.nodeName)) return node._componentConstructor === vnode.nodeName || isFunctionalComponent(vnode); else ;
|
||||
}
|
||||
function isNamedNode(node, nodeName) {
|
||||
return node.normalizedNodeName === nodeName || toLowerCase(node.nodeName) === toLowerCase(nodeName);
|
||||
}
|
||||
function getNodeProps(vnode) {
|
||||
var props = clone(vnode.attributes);
|
||||
props.children = vnode.children;
|
||||
var defaultProps = vnode.nodeName.defaultProps;
|
||||
if (defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
|
||||
return props;
|
||||
}
|
||||
function removeNode(node) {
|
||||
var p = node.parentNode;
|
||||
if (p) p.removeChild(node);
|
||||
}
|
||||
function setAccessor(node, name, old, value, isSvg) {
|
||||
if ('className' === name) name = 'class';
|
||||
if ('class' === name && value && 'object' == typeof value) value = hashToClassName(value);
|
||||
if ('key' === name) ; else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
|
||||
if (!value || isString(value) || isString(old)) node.style.cssText = value || '';
|
||||
if (value && 'object' == typeof value) {
|
||||
if (!isString(old)) for (var i in old) if (!(i in value)) node.style[i] = '';
|
||||
for (var i in value) node.style[i] = 'number' == typeof value[i] && !NON_DIMENSION_PROPS[i] ? value[i] + 'px' : value[i];
|
||||
}
|
||||
} else if ('dangerouslySetInnerHTML' === name) {
|
||||
if (value) node.innerHTML = value.__html;
|
||||
} else if ('o' == name[0] && 'n' == name[1]) {
|
||||
var l = node._listeners || (node._listeners = {});
|
||||
name = toLowerCase(name.substring(2));
|
||||
if (value) {
|
||||
if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
|
||||
} else if (l[name]) node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
|
||||
l[name] = value;
|
||||
} else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
|
||||
setProperty(node, name, null == value ? '' : value);
|
||||
if (null == value || value === !1) node.removeAttribute(name);
|
||||
} else {
|
||||
var ns = isSvg && name.match(/^xlink\:?(.+)/);
|
||||
if (null == value || value === !1) if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1])); else node.removeAttribute(name); else if ('object' != typeof value && !isFunction(value)) if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value); else node.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
function setProperty(node, name, value) {
|
||||
try {
|
||||
node[name] = value;
|
||||
} catch (e) {}
|
||||
}
|
||||
function eventProxy(e) {
|
||||
return this._listeners[e.type](options.event && options.event(e) || e);
|
||||
}
|
||||
function collectNode(node) {
|
||||
removeNode(node);
|
||||
if (node instanceof Element) {
|
||||
node._component = node._componentConstructor = null;
|
||||
var _name = node.normalizedNodeName || toLowerCase(node.nodeName);
|
||||
(nodes[_name] || (nodes[_name] = [])).push(node);
|
||||
}
|
||||
}
|
||||
function createNode(nodeName, isSvg) {
|
||||
var name = toLowerCase(nodeName), node = nodes[name] && nodes[name].pop() || (isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName));
|
||||
node.normalizedNodeName = name;
|
||||
return node;
|
||||
}
|
||||
function flushMounts() {
|
||||
var c;
|
||||
while (c = mounts.pop()) {
|
||||
if (options.afterMount) options.afterMount(c);
|
||||
if (c.componentDidMount) c.componentDidMount();
|
||||
}
|
||||
}
|
||||
function diff(dom, vnode, context, mountAll, parent, componentRoot) {
|
||||
if (!diffLevel++) isSvgMode = parent instanceof SVGElement;
|
||||
var ret = idiff(dom, vnode, context, mountAll);
|
||||
if (parent && ret.parentNode !== parent) parent.appendChild(ret);
|
||||
if (!--diffLevel && !componentRoot) flushMounts();
|
||||
return ret;
|
||||
}
|
||||
function idiff(dom, vnode, context, mountAll) {
|
||||
var originalAttributes = vnode && vnode.attributes;
|
||||
while (isFunctionalComponent(vnode)) vnode = buildFunctionalComponent(vnode, context);
|
||||
if (null == vnode) vnode = '';
|
||||
if (isString(vnode)) {
|
||||
if (dom) {
|
||||
if (dom instanceof Text && dom.parentNode) {
|
||||
if (dom.nodeValue != vnode) dom.nodeValue = vnode;
|
||||
return dom;
|
||||
}
|
||||
recollectNodeTree(dom);
|
||||
}
|
||||
return document.createTextNode(vnode);
|
||||
}
|
||||
if (isFunction(vnode.nodeName)) return buildComponentFromVNode(dom, vnode, context, mountAll);
|
||||
var out = dom, nodeName = vnode.nodeName, prevSvgMode = isSvgMode, vchildren = vnode.children;
|
||||
if (!isString(nodeName)) nodeName = String(nodeName);
|
||||
isSvgMode = 'svg' === nodeName ? !0 : 'foreignObject' === nodeName ? !1 : isSvgMode;
|
||||
if (!dom) out = createNode(nodeName, isSvgMode); else if (!isNamedNode(dom, nodeName)) {
|
||||
out = createNode(nodeName, isSvgMode);
|
||||
while (dom.firstChild) out.appendChild(dom.firstChild);
|
||||
recollectNodeTree(dom);
|
||||
}
|
||||
if (vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && 1 === out.childNodes.length && out.firstChild instanceof Text) {
|
||||
if (out.firstChild.nodeValue != vchildren[0]) out.firstChild.nodeValue = vchildren[0];
|
||||
} else if (vchildren && vchildren.length || out.firstChild) innerDiffNode(out, vchildren, context, mountAll);
|
||||
var props = out[ATTR_KEY];
|
||||
if (!props) {
|
||||
out[ATTR_KEY] = props = {};
|
||||
for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
|
||||
}
|
||||
diffAttributes(out, vnode.attributes, props);
|
||||
if (originalAttributes && 'function' == typeof originalAttributes.ref) (props.ref = originalAttributes.ref)(out);
|
||||
isSvgMode = prevSvgMode;
|
||||
return out;
|
||||
}
|
||||
function innerDiffNode(dom, vchildren, context, mountAll) {
|
||||
var j, c, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren && vchildren.length;
|
||||
if (len) for (var i = 0; i < len; i++) {
|
||||
var _child = originalChildren[i], key = vlen ? (c = _child._component) ? c.__key : (c = _child[ATTR_KEY]) ? c.key : null : null;
|
||||
if (key || 0 === key) {
|
||||
keyedLen++;
|
||||
keyed[key] = _child;
|
||||
} else children[childrenLen++] = _child;
|
||||
}
|
||||
if (vlen) for (var i = 0; i < vlen; i++) {
|
||||
vchild = vchildren[i];
|
||||
child = null;
|
||||
var key = vchild.key;
|
||||
if (null != key) {
|
||||
if (keyedLen && key in keyed) {
|
||||
child = keyed[key];
|
||||
keyed[key] = void 0;
|
||||
keyedLen--;
|
||||
}
|
||||
} else if (!child && min < childrenLen) {
|
||||
for (j = min; j < childrenLen; j++) {
|
||||
c = children[j];
|
||||
if (c && isSameNodeType(c, vchild)) {
|
||||
child = c;
|
||||
children[j] = void 0;
|
||||
if (j === childrenLen - 1) childrenLen--;
|
||||
if (j === min) min++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!child && min < childrenLen && isFunction(vchild.nodeName) && mountAll) {
|
||||
child = children[min];
|
||||
children[min++] = void 0;
|
||||
}
|
||||
}
|
||||
child = idiff(child, vchild, context, mountAll);
|
||||
if (child && child !== dom && child !== originalChildren[i]) dom.insertBefore(child, originalChildren[i] || null);
|
||||
}
|
||||
if (keyedLen) for (var i in keyed) if (keyed[i]) recollectNodeTree(keyed[i]);
|
||||
if (min < childrenLen) removeOrphanedChildren(children);
|
||||
}
|
||||
function removeOrphanedChildren(children, unmountOnly) {
|
||||
for (var i = children.length; i--; ) if (children[i]) recollectNodeTree(children[i], unmountOnly);
|
||||
}
|
||||
function recollectNodeTree(node, unmountOnly) {
|
||||
var component = node._component;
|
||||
if (component) unmountComponent(component, !unmountOnly); else {
|
||||
if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
|
||||
if (!unmountOnly) collectNode(node);
|
||||
if (node.childNodes && node.childNodes.length) removeOrphanedChildren(node.childNodes, unmountOnly);
|
||||
}
|
||||
}
|
||||
function diffAttributes(dom, attrs, old) {
|
||||
for (var _name in old) if (!(attrs && _name in attrs) && null != old[_name]) setAccessor(dom, _name, old[_name], old[_name] = void 0, isSvgMode);
|
||||
if (attrs) for (var _name2 in attrs) if (!('children' === _name2 || 'innerHTML' === _name2 || _name2 in old && attrs[_name2] === ('value' === _name2 || 'checked' === _name2 ? dom[_name2] : old[_name2]))) setAccessor(dom, _name2, old[_name2], old[_name2] = attrs[_name2], isSvgMode);
|
||||
}
|
||||
function collectComponent(component) {
|
||||
var name = component.constructor.name, list = components[name];
|
||||
if (list) list.push(component); else components[name] = [ component ];
|
||||
}
|
||||
function createComponent(Ctor, props, context) {
|
||||
var inst = new Ctor(props, context), list = components[Ctor.name];
|
||||
Component.call(inst, props, context);
|
||||
if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {
|
||||
inst.nextBase = list[i].nextBase;
|
||||
list.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
function setComponentProps(component, props, opts, context, mountAll) {
|
||||
if (!component._disable) {
|
||||
component._disable = !0;
|
||||
if (component.__ref = props.ref) delete props.ref;
|
||||
if (component.__key = props.key) delete props.key;
|
||||
if (!component.base || mountAll) {
|
||||
if (component.componentWillMount) component.componentWillMount();
|
||||
} else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
|
||||
if (context && context !== component.context) {
|
||||
if (!component.prevContext) component.prevContext = component.context;
|
||||
component.context = context;
|
||||
}
|
||||
if (!component.prevProps) component.prevProps = component.props;
|
||||
component.props = props;
|
||||
component._disable = !1;
|
||||
if (0 !== opts) if (1 === opts || options.syncComponentUpdates !== !1 || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
|
||||
if (component.__ref) component.__ref(component);
|
||||
}
|
||||
}
|
||||
function renderComponent(component, opts, mountAll, isChild) {
|
||||
if (!component._disable) {
|
||||
var skip, rendered, inst, cbase, props = component.props, state = component.state, context = component.context, previousProps = component.prevProps || props, previousState = component.prevState || state, previousContext = component.prevContext || context, isUpdate = component.base, nextBase = component.nextBase, initialBase = isUpdate || nextBase, initialChildComponent = component._component;
|
||||
if (isUpdate) {
|
||||
component.props = previousProps;
|
||||
component.state = previousState;
|
||||
component.context = previousContext;
|
||||
if (2 !== opts && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === !1) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
|
||||
component.props = props;
|
||||
component.state = state;
|
||||
component.context = context;
|
||||
}
|
||||
component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
|
||||
component._dirty = !1;
|
||||
if (!skip) {
|
||||
if (component.render) rendered = component.render(props, state, context);
|
||||
if (component.getChildContext) context = extend(clone(context), component.getChildContext());
|
||||
while (isFunctionalComponent(rendered)) rendered = buildFunctionalComponent(rendered, context);
|
||||
var toUnmount, base, childComponent = rendered && rendered.nodeName;
|
||||
if (isFunction(childComponent)) {
|
||||
inst = initialChildComponent;
|
||||
var childProps = getNodeProps(rendered);
|
||||
if (inst && inst.constructor === childComponent) setComponentProps(inst, childProps, 1, context); else {
|
||||
toUnmount = inst;
|
||||
inst = createComponent(childComponent, childProps, context);
|
||||
inst.nextBase = inst.nextBase || nextBase;
|
||||
inst._parentComponent = component;
|
||||
component._component = inst;
|
||||
setComponentProps(inst, childProps, 0, context);
|
||||
renderComponent(inst, 1, mountAll, !0);
|
||||
}
|
||||
base = inst.base;
|
||||
} else {
|
||||
cbase = initialBase;
|
||||
toUnmount = initialChildComponent;
|
||||
if (toUnmount) cbase = component._component = null;
|
||||
if (initialBase || 1 === opts) {
|
||||
if (cbase) cbase._component = null;
|
||||
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
|
||||
}
|
||||
}
|
||||
if (initialBase && base !== initialBase && inst !== initialChildComponent) {
|
||||
var baseParent = initialBase.parentNode;
|
||||
if (baseParent && base !== baseParent) {
|
||||
baseParent.replaceChild(base, initialBase);
|
||||
if (!toUnmount) {
|
||||
initialBase._component = null;
|
||||
recollectNodeTree(initialBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toUnmount) unmountComponent(toUnmount, base !== initialBase);
|
||||
component.base = base;
|
||||
if (base && !isChild) {
|
||||
var componentRef = component, t = component;
|
||||
while (t = t._parentComponent) (componentRef = t).base = base;
|
||||
base._component = componentRef;
|
||||
base._componentConstructor = componentRef.constructor;
|
||||
}
|
||||
}
|
||||
if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
|
||||
if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);
|
||||
if (options.afterUpdate) options.afterUpdate(component);
|
||||
}
|
||||
var fn, cb = component._renderCallbacks;
|
||||
if (cb) while (fn = cb.pop()) fn.call(component);
|
||||
if (!diffLevel && !isChild) flushMounts();
|
||||
}
|
||||
}
|
||||
function buildComponentFromVNode(dom, vnode, context, mountAll) {
|
||||
var c = dom && dom._component, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
|
||||
while (c && !isOwner && (c = c._parentComponent)) isOwner = c.constructor === vnode.nodeName;
|
||||
if (c && isOwner && (!mountAll || c._component)) {
|
||||
setComponentProps(c, props, 3, context, mountAll);
|
||||
dom = c.base;
|
||||
} else {
|
||||
if (c && !isDirectOwner) {
|
||||
unmountComponent(c, !0);
|
||||
dom = oldDom = null;
|
||||
}
|
||||
c = createComponent(vnode.nodeName, props, context);
|
||||
if (dom && !c.nextBase) {
|
||||
c.nextBase = dom;
|
||||
oldDom = null;
|
||||
}
|
||||
setComponentProps(c, props, 1, context, mountAll);
|
||||
dom = c.base;
|
||||
if (oldDom && dom !== oldDom) {
|
||||
oldDom._component = null;
|
||||
recollectNodeTree(oldDom);
|
||||
}
|
||||
}
|
||||
return dom;
|
||||
}
|
||||
function unmountComponent(component, remove) {
|
||||
if (options.beforeUnmount) options.beforeUnmount(component);
|
||||
var base = component.base;
|
||||
component._disable = !0;
|
||||
if (component.componentWillUnmount) component.componentWillUnmount();
|
||||
component.base = null;
|
||||
var inner = component._component;
|
||||
if (inner) unmountComponent(inner, remove); else if (base) {
|
||||
if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);
|
||||
component.nextBase = base;
|
||||
if (remove) {
|
||||
removeNode(base);
|
||||
collectComponent(component);
|
||||
}
|
||||
removeOrphanedChildren(base.childNodes, !remove);
|
||||
}
|
||||
if (component.__ref) component.__ref(null);
|
||||
if (component.componentDidUnmount) component.componentDidUnmount();
|
||||
}
|
||||
function Component(props, context) {
|
||||
this._dirty = !0;
|
||||
this.context = context;
|
||||
this.props = props;
|
||||
if (!this.state) this.state = {};
|
||||
}
|
||||
function render(vnode, parent, merge) {
|
||||
return diff(merge, vnode, {}, !1, parent);
|
||||
}
|
||||
var options = {};
|
||||
var stack = [];
|
||||
var lcCache = {};
|
||||
var toLowerCase = function(s) {
|
||||
return lcCache[s] || (lcCache[s] = s.toLowerCase());
|
||||
};
|
||||
var resolved = 'undefined' != typeof Promise && Promise.resolve();
|
||||
var defer = resolved ? function(f) {
|
||||
resolved.then(f);
|
||||
} : setTimeout;
|
||||
var EMPTY = {};
|
||||
var ATTR_KEY = 'undefined' != typeof Symbol ? Symbol.for('preactattr') : '__preactattr_';
|
||||
var NON_DIMENSION_PROPS = {
|
||||
boxFlex: 1,
|
||||
boxFlexGroup: 1,
|
||||
columnCount: 1,
|
||||
fillOpacity: 1,
|
||||
flex: 1,
|
||||
flexGrow: 1,
|
||||
flexPositive: 1,
|
||||
flexShrink: 1,
|
||||
flexNegative: 1,
|
||||
fontWeight: 1,
|
||||
lineClamp: 1,
|
||||
lineHeight: 1,
|
||||
opacity: 1,
|
||||
order: 1,
|
||||
orphans: 1,
|
||||
strokeOpacity: 1,
|
||||
widows: 1,
|
||||
zIndex: 1,
|
||||
zoom: 1
|
||||
};
|
||||
var NON_BUBBLING_EVENTS = {
|
||||
blur: 1,
|
||||
error: 1,
|
||||
focus: 1,
|
||||
load: 1,
|
||||
resize: 1,
|
||||
scroll: 1
|
||||
};
|
||||
var items = [];
|
||||
var nodes = {};
|
||||
var mounts = [];
|
||||
var diffLevel = 0;
|
||||
var isSvgMode = !1;
|
||||
var components = {};
|
||||
extend(Component.prototype, {
|
||||
linkState: function(key, eventPath) {
|
||||
var c = this._linkedStates || (this._linkedStates = {});
|
||||
return c[key + eventPath] || (c[key + eventPath] = createLinkedState(this, key, eventPath));
|
||||
},
|
||||
setState: function(state, callback) {
|
||||
var s = this.state;
|
||||
if (!this.prevState) this.prevState = clone(s);
|
||||
extend(s, isFunction(state) ? state(s, this.props) : state);
|
||||
if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);
|
||||
enqueueRender(this);
|
||||
},
|
||||
forceUpdate: function() {
|
||||
renderComponent(this, 2);
|
||||
},
|
||||
render: function() {}
|
||||
});
|
||||
exports.h = h;
|
||||
exports.createElement = h;
|
||||
exports.cloneElement = cloneElement;
|
||||
exports.Component = Component;
|
||||
exports.render = render;
|
||||
exports.rerender = rerender;
|
||||
exports.options = options;
|
||||
});
|
||||
//# sourceMappingURL=preact.js.map
|
18525
lib/vendor/react-dom.js
vendored
Normal file
18525
lib/vendor/react-dom.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3783
lib/vendor/react.js
vendored
Normal file
3783
lib/vendor/react.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,157 +35,154 @@ const GNUNET_NO = 0;
|
||||
const GNUNET_SYSERR = -1;
|
||||
|
||||
|
||||
function myCcall(name: string, ret: any, argTypes: any[], args: any[]) {
|
||||
return Module.ccall(name, ret, argTypes, args);
|
||||
}
|
||||
|
||||
let getEmsc: EmscFunGen = (name: string, ret: any,
|
||||
argTypes: any[]) => {
|
||||
const getEmsc: EmscFunGen = (name: string, ret: any, argTypes: any[]) => {
|
||||
return (...args: any[]) => {
|
||||
return myCcall(name, ret, argTypes, args);
|
||||
return Module.ccall(name, ret, argTypes, args);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var emsc = {
|
||||
/**
|
||||
* Wrapped emscripten functions that do not allocate any memory.
|
||||
*/
|
||||
const emsc = {
|
||||
free: (ptr: number) => Module._free(ptr),
|
||||
get_value: getEmsc('TALER_WR_get_value',
|
||||
'number',
|
||||
['number']),
|
||||
get_fraction: getEmsc('TALER_WR_get_fraction',
|
||||
'number',
|
||||
['number']),
|
||||
get_currency: getEmsc('TALER_WR_get_currency',
|
||||
'string',
|
||||
['number']),
|
||||
amount_add: getEmsc('TALER_amount_add',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
amount_subtract: getEmsc('TALER_amount_subtract',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
amount_normalize: getEmsc('TALER_amount_normalize',
|
||||
'void',
|
||||
['number']),
|
||||
amount_get_zero: getEmsc('TALER_amount_get_zero',
|
||||
'number',
|
||||
['string', 'number']),
|
||||
amount_cmp: getEmsc('TALER_amount_cmp',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
amount_hton: getEmsc('TALER_amount_hton',
|
||||
'void',
|
||||
['number', 'number']),
|
||||
amount_ntoh: getEmsc('TALER_amount_ntoh',
|
||||
'void',
|
||||
['number', 'number']),
|
||||
hash: getEmsc('GNUNET_CRYPTO_hash',
|
||||
'void',
|
||||
['number', 'number', 'number']),
|
||||
memmove: getEmsc('memmove',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free',
|
||||
'void',
|
||||
['number']),
|
||||
rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free',
|
||||
'void',
|
||||
['number']),
|
||||
string_to_data: getEmsc('GNUNET_STRINGS_string_to_data',
|
||||
'number',
|
||||
['number', 'number', 'number', 'number']),
|
||||
eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
eddsa_verify: getEmsc('GNUNET_CRYPTO_eddsa_verify',
|
||||
'number',
|
||||
['number', 'number', 'number', 'number']),
|
||||
hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random',
|
||||
'void',
|
||||
['number', 'number']),
|
||||
rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free',
|
||||
'void',
|
||||
['number']),
|
||||
random_block: getEmsc('GNUNET_CRYPTO_random_block',
|
||||
'void',
|
||||
['number', 'number', 'number']),
|
||||
hash_context_abort: getEmsc('GNUNET_CRYPTO_hash_context_abort',
|
||||
'void',
|
||||
['number']),
|
||||
hash_context_read: getEmsc('GNUNET_CRYPTO_hash_context_read',
|
||||
'void',
|
||||
['number', 'number', 'number']),
|
||||
hash_context_finish: getEmsc('GNUNET_CRYPTO_hash_context_finish',
|
||||
'void',
|
||||
['number', 'number']),
|
||||
ecdh_eddsa: getEmsc(
|
||||
"GNUNET_CRYPTO_ecdh_eddsa",
|
||||
'number',
|
||||
["number", "number", "number"]),
|
||||
get_value: getEmsc("TALER_WR_get_value",
|
||||
"number",
|
||||
["number"]),
|
||||
get_fraction: getEmsc("TALER_WR_get_fraction",
|
||||
"number",
|
||||
["number"]),
|
||||
get_currency: getEmsc("TALER_WR_get_currency",
|
||||
"string",
|
||||
["number"]),
|
||||
amount_add: getEmsc("TALER_amount_add",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
amount_subtract: getEmsc("TALER_amount_subtract",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
amount_normalize: getEmsc("TALER_amount_normalize",
|
||||
"void",
|
||||
["number"]),
|
||||
amount_get_zero: getEmsc("TALER_amount_get_zero",
|
||||
"number",
|
||||
["string", "number"]),
|
||||
amount_cmp: getEmsc("TALER_amount_cmp",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
amount_hton: getEmsc("TALER_amount_hton",
|
||||
"void",
|
||||
["number", "number"]),
|
||||
amount_ntoh: getEmsc("TALER_amount_ntoh",
|
||||
"void",
|
||||
["number", "number"]),
|
||||
hash: getEmsc("GNUNET_CRYPTO_hash",
|
||||
"void",
|
||||
["number", "number", "number"]),
|
||||
memmove: getEmsc("memmove",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
rsa_public_key_free: getEmsc("GNUNET_CRYPTO_rsa_public_key_free",
|
||||
"void",
|
||||
["number"]),
|
||||
rsa_signature_free: getEmsc("GNUNET_CRYPTO_rsa_signature_free",
|
||||
"void",
|
||||
["number"]),
|
||||
string_to_data: getEmsc("GNUNET_STRINGS_string_to_data",
|
||||
"number",
|
||||
["number", "number", "number", "number"]),
|
||||
eddsa_sign: getEmsc("GNUNET_CRYPTO_eddsa_sign",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
eddsa_verify: getEmsc("GNUNET_CRYPTO_eddsa_verify",
|
||||
"number",
|
||||
["number", "number", "number", "number"]),
|
||||
hash_create_random: getEmsc("GNUNET_CRYPTO_hash_create_random",
|
||||
"void",
|
||||
["number", "number"]),
|
||||
rsa_blinding_key_destroy: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_free",
|
||||
"void",
|
||||
["number"]),
|
||||
random_block: getEmsc("GNUNET_CRYPTO_random_block",
|
||||
"void",
|
||||
["number", "number", "number"]),
|
||||
hash_context_abort: getEmsc("GNUNET_CRYPTO_hash_context_abort",
|
||||
"void",
|
||||
["number"]),
|
||||
hash_context_read: getEmsc("GNUNET_CRYPTO_hash_context_read",
|
||||
"void",
|
||||
["number", "number", "number"]),
|
||||
hash_context_finish: getEmsc("GNUNET_CRYPTO_hash_context_finish",
|
||||
"void",
|
||||
["number", "number"]),
|
||||
ecdh_eddsa: getEmsc("GNUNET_CRYPTO_ecdh_eddsa",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
|
||||
setup_fresh_coin: getEmsc(
|
||||
"TALER_setup_fresh_coin",
|
||||
'void',
|
||||
"void",
|
||||
["number", "number", "number"]),
|
||||
};
|
||||
|
||||
var emscAlloc = {
|
||||
get_amount: getEmsc('TALER_WRALL_get_amount',
|
||||
'number',
|
||||
['number', 'number', 'number', 'string']),
|
||||
eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create',
|
||||
'number', []),
|
||||
ecdsa_key_create: getEmsc('GNUNET_CRYPTO_ecdsa_key_create',
|
||||
'number', []),
|
||||
ecdhe_key_create: getEmsc('GNUNET_CRYPTO_ecdhe_key_create',
|
||||
'number', []),
|
||||
const emscAlloc = {
|
||||
get_amount: getEmsc("TALER_WRALL_get_amount",
|
||||
"number",
|
||||
["number", "number", "number", "string"]),
|
||||
eddsa_key_create: getEmsc("GNUNET_CRYPTO_eddsa_key_create",
|
||||
"number", []),
|
||||
ecdsa_key_create: getEmsc("GNUNET_CRYPTO_ecdsa_key_create",
|
||||
"number", []),
|
||||
ecdhe_key_create: getEmsc("GNUNET_CRYPTO_ecdhe_key_create",
|
||||
"number", []),
|
||||
eddsa_public_key_from_private: getEmsc(
|
||||
'TALER_WRALL_eddsa_public_key_from_private',
|
||||
'number',
|
||||
['number']),
|
||||
"TALER_WRALL_eddsa_public_key_from_private",
|
||||
"number",
|
||||
["number"]),
|
||||
ecdsa_public_key_from_private: getEmsc(
|
||||
'TALER_WRALL_ecdsa_public_key_from_private',
|
||||
'number',
|
||||
['number']),
|
||||
"TALER_WRALL_ecdsa_public_key_from_private",
|
||||
"number",
|
||||
["number"]),
|
||||
ecdhe_public_key_from_private: getEmsc(
|
||||
'TALER_WRALL_ecdhe_public_key_from_private',
|
||||
'number',
|
||||
['number']),
|
||||
data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
purpose_create: getEmsc('TALER_WRALL_purpose_create',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind',
|
||||
'number',
|
||||
['number', 'number', 'number', 'number', 'number']),
|
||||
rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create',
|
||||
'number',
|
||||
['number']),
|
||||
rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode',
|
||||
'number',
|
||||
['number', 'number']),
|
||||
rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind',
|
||||
'number',
|
||||
['number', 'number', 'number']),
|
||||
hash_context_start: getEmsc('GNUNET_CRYPTO_hash_context_start',
|
||||
'number',
|
||||
"TALER_WRALL_ecdhe_public_key_from_private",
|
||||
"number",
|
||||
["number"]),
|
||||
data_to_string_alloc: getEmsc("GNUNET_STRINGS_data_to_string_alloc",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
purpose_create: getEmsc("TALER_WRALL_purpose_create",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
rsa_blind: getEmsc("GNUNET_CRYPTO_rsa_blind",
|
||||
"number",
|
||||
["number", "number", "number", "number", "number"]),
|
||||
rsa_blinding_key_create: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_create",
|
||||
"number",
|
||||
["number"]),
|
||||
rsa_blinding_key_encode: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_encode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_signature_encode: getEmsc("GNUNET_CRYPTO_rsa_signature_encode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_blinding_key_decode: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_decode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_public_key_decode: getEmsc("GNUNET_CRYPTO_rsa_public_key_decode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_signature_decode: getEmsc("GNUNET_CRYPTO_rsa_signature_decode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_public_key_encode: getEmsc("GNUNET_CRYPTO_rsa_public_key_encode",
|
||||
"number",
|
||||
["number", "number"]),
|
||||
rsa_unblind: getEmsc("GNUNET_CRYPTO_rsa_unblind",
|
||||
"number",
|
||||
["number", "number", "number"]),
|
||||
hash_context_start: getEmsc("GNUNET_CRYPTO_hash_context_start",
|
||||
"number",
|
||||
[]),
|
||||
malloc: (size: number) => Module._malloc(size),
|
||||
};
|
||||
@ -198,7 +195,7 @@ export enum SignaturePurpose {
|
||||
WALLET_COIN_MELT = 1202,
|
||||
}
|
||||
|
||||
enum RandomQuality {
|
||||
export enum RandomQuality {
|
||||
WEAK = 0,
|
||||
STRONG = 1,
|
||||
NONCE = 2
|
||||
|
@ -67,3 +67,74 @@ export function parsePrettyAmount(pretty: string): AmountJson|undefined {
|
||||
currency: res[3]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert object to JSON with canonical ordering of keys
|
||||
* and whitespace omitted.
|
||||
*/
|
||||
export function canonicalJson(obj: any): string {
|
||||
// Check for cycles, etc.
|
||||
JSON.stringify(obj);
|
||||
if (typeof obj == "string" || typeof obj == "number" || obj === null) {
|
||||
return JSON.stringify(obj)
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
let objs: string[] = obj.map((e) => canonicalJson(e));
|
||||
return `[${objs.join(',')}]`;
|
||||
}
|
||||
let keys: string[] = [];
|
||||
for (let key in obj) {
|
||||
keys.push(key);
|
||||
}
|
||||
keys.sort();
|
||||
let s = "{";
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
s += JSON.stringify(key) + ":" + canonicalJson(obj[key]);
|
||||
if (i != keys.length - 1) {
|
||||
s += ",";
|
||||
}
|
||||
}
|
||||
return s + "}";
|
||||
}
|
||||
|
||||
|
||||
export function deepEquals(x: any, y: any): boolean {
|
||||
if (x === y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(x) && x.length !== y.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = Object.keys(x);
|
||||
return Object.keys(y).every((i) => p.indexOf(i) !== -1) &&
|
||||
p.every((i) => deepEquals(x[i], y[i]));
|
||||
}
|
||||
|
||||
|
||||
export function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
|
||||
return xs.reduce((acc: U[], next: T) => [...f(next), ...acc], []);
|
||||
}
|
||||
|
||||
|
||||
export function getTalerStampSec(stamp: string): number | null {
|
||||
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
||||
if (!m) {
|
||||
return null;
|
||||
}
|
||||
return parseInt(m[1]);
|
||||
}
|
||||
|
||||
|
||||
export function getTalerStampDate(stamp: string): Date | null {
|
||||
let sec = getTalerStampSec(stamp);
|
||||
if (sec == null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(sec * 1000);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,19 @@ export interface HttpResponse {
|
||||
}
|
||||
|
||||
|
||||
export interface HttpRequestLibrary {
|
||||
req(method: string,
|
||||
url: string | uri.URI,
|
||||
options?: any): Promise<HttpResponse>;
|
||||
|
||||
get(url: string | uri.URI): Promise<HttpResponse>;
|
||||
|
||||
postJson(url: string | uri.URI, body: any): Promise<HttpResponse>;
|
||||
|
||||
postForm(url: string | uri.URI, form: any): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
|
||||
export class BrowserHttpLib {
|
||||
req(method: string,
|
||||
url: string|uri.URI,
|
||||
|
@ -60,4 +60,4 @@ export function abbrev(s: string, n: number = 5) {
|
||||
{sAbbrev}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -23,25 +23,47 @@
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
CheckRepurchaseResult,
|
||||
Coin,
|
||||
CoinPaySig,
|
||||
Contract,
|
||||
CreateReserveResponse,
|
||||
IExchangeInfo,
|
||||
Denomination,
|
||||
ExchangeHandle,
|
||||
IExchangeInfo,
|
||||
Notifier,
|
||||
WireInfo, RefreshSession, ReserveRecord, CoinPaySig, WalletBalance,
|
||||
WalletBalanceEntry
|
||||
PayCoinInfo,
|
||||
PreCoin,
|
||||
RefreshSession,
|
||||
ReserveCreationInfo,
|
||||
ReserveRecord,
|
||||
WalletBalance,
|
||||
WalletBalanceEntry,
|
||||
WireInfo,
|
||||
} from "./types";
|
||||
import {HttpResponse, RequestException} from "./http";
|
||||
import {QueryRoot, Store, Index, JoinResult, AbortTransaction} from "./query";
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
HttpResponse,
|
||||
RequestException,
|
||||
} from "./http";
|
||||
import {
|
||||
AbortTransaction,
|
||||
Index,
|
||||
JoinResult,
|
||||
QueryRoot,
|
||||
Store,
|
||||
} from "./query";
|
||||
import {Checkable} from "./checkable";
|
||||
import {canonicalizeBaseUrl, amountToPretty} from "./helpers";
|
||||
import {ReserveCreationInfo, Amounts} from "./types";
|
||||
import {PreCoin} from "./types";
|
||||
import {
|
||||
amountToPretty,
|
||||
canonicalizeBaseUrl,
|
||||
canonicalJson,
|
||||
deepEquals,
|
||||
flatMap,
|
||||
getTalerStampSec,
|
||||
} from "./helpers";
|
||||
import {CryptoApi} from "./cryptoApi";
|
||||
import {Coin} from "./types";
|
||||
import {PayCoinInfo} from "./types";
|
||||
import {CheckRepurchaseResult} from "./types";
|
||||
import {Contract} from "./types";
|
||||
import {ExchangeHandle} from "./types";
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -182,61 +204,6 @@ export interface Badge {
|
||||
stopBusy(): void;
|
||||
}
|
||||
|
||||
export function canonicalJson(obj: any): string {
|
||||
// Check for cycles, etc.
|
||||
JSON.stringify(obj);
|
||||
if (typeof obj == "string" || typeof obj == "number" || obj === null) {
|
||||
return JSON.stringify(obj)
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
let objs: string[] = obj.map((e) => canonicalJson(e));
|
||||
return `[${objs.join(',')}]`;
|
||||
}
|
||||
let keys: string[] = [];
|
||||
for (let key in obj) {
|
||||
keys.push(key);
|
||||
}
|
||||
keys.sort();
|
||||
let s = "{";
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
s += JSON.stringify(key) + ":" + canonicalJson(obj[key]);
|
||||
if (i != keys.length - 1) {
|
||||
s += ",";
|
||||
}
|
||||
}
|
||||
return s + "}";
|
||||
}
|
||||
|
||||
|
||||
function deepEquals(x: any, y: any): boolean {
|
||||
if (x === y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(x) && x.length !== y.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = Object.keys(x);
|
||||
return Object.keys(y).every((i) => p.indexOf(i) !== -1) &&
|
||||
p.every((i) => deepEquals(x[i], y[i]));
|
||||
}
|
||||
|
||||
|
||||
function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
|
||||
return xs.reduce((acc: U[], next: T) => [...f(next), ...acc], []);
|
||||
}
|
||||
|
||||
|
||||
function getTalerStampSec(stamp: string): number | null {
|
||||
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
||||
if (!m) {
|
||||
return null;
|
||||
}
|
||||
return parseInt(m[1]);
|
||||
}
|
||||
|
||||
|
||||
function setTimeout(f: any, t: number) {
|
||||
return chrome.extension.getBackgroundPage().setTimeout(f, t);
|
||||
@ -255,23 +222,6 @@ function isWithdrawableDenom(d: Denomination) {
|
||||
}
|
||||
|
||||
|
||||
interface HttpRequestLibrary {
|
||||
req(method: string,
|
||||
url: string | uri.URI,
|
||||
options?: any): Promise<HttpResponse>;
|
||||
|
||||
get(url: string | uri.URI): Promise<HttpResponse>;
|
||||
|
||||
postJson(url: string | uri.URI, body: any): Promise<HttpResponse>;
|
||||
|
||||
postForm(url: string | uri.URI, form: any): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
|
||||
function copy(o: any) {
|
||||
return JSON.parse(JSON.stringify(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of updating exisiting information
|
||||
* about an exchange with a new '/keys' response.
|
||||
@ -956,7 +906,7 @@ export class Wallet {
|
||||
if (!reserve.current_amount) {
|
||||
throw Error("can't withdraw when amount is unknown");
|
||||
}
|
||||
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
|
||||
let denomsAvailable: Denomination[] = Array.from(exchange.active_denoms);
|
||||
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount!,
|
||||
denomsAvailable);
|
||||
|
||||
@ -1603,8 +1553,7 @@ export class Wallet {
|
||||
.toArray();
|
||||
}
|
||||
|
||||
|
||||
async hashContract(contract: any): Promise<string> {
|
||||
async hashContract(contract: Contract): Promise<string> {
|
||||
return this.cryptoApi.hashString(canonicalJson(contract));
|
||||
}
|
||||
|
||||
|
@ -72,4 +72,4 @@ export async function getPreCoins(exchangeBaseUrl: string): Promise<PreCoin[]> {
|
||||
|
||||
export async function refresh(coinPub: string): Promise<void> {
|
||||
return await callBackend("refresh-coin", { coinPub });
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@
|
||||
<link rel="icon" href="../img/icon.png">
|
||||
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
<script src="../lib/vendor/preact.js"></script>
|
||||
<script src="../lib/vendor/react.js"></script>
|
||||
<script src="../lib/vendor/react-dom.js"></script>
|
||||
<script src="../lib/vendor/system-csp-production.src.js"></script>
|
||||
<!-- <script src="../lib/vendor/jed.js"></script> -->
|
||||
<script src="../lib/i18n.js"></script>
|
||||
|
@ -22,7 +22,6 @@
|
||||
*/
|
||||
|
||||
|
||||
/// <reference path="../lib/decl/preact.d.ts" />
|
||||
import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
|
||||
import {Contract, AmountJson, IExchangeInfo} from "../lib/wallet/types";
|
||||
import {renderContract, prettyAmount} from "../lib/wallet/renderHtml";
|
||||
@ -41,7 +40,7 @@ interface DetailProps {
|
||||
}
|
||||
|
||||
|
||||
class Details extends preact.Component<DetailProps, DetailState> {
|
||||
class Details extends React.Component<DetailProps, DetailState> {
|
||||
constructor(props: DetailProps) {
|
||||
super(props);
|
||||
this.setState({
|
||||
@ -63,10 +62,10 @@ class Details extends preact.Component<DetailProps, DetailState> {
|
||||
this.setState({exchanges} as any);
|
||||
}
|
||||
|
||||
render(props: DetailProps, state: DetailState) {
|
||||
console.log("details collapsed (state)", state.collapsed);
|
||||
console.log("details collapsed (prop)", props.collapsed);
|
||||
if (state.collapsed) {
|
||||
render() {
|
||||
console.log("details collapsed (state)", this.state.collapsed);
|
||||
console.log("details collapsed (prop)", this.props.collapsed);
|
||||
if (this.state.collapsed) {
|
||||
return (
|
||||
<div>
|
||||
<button className="linky"
|
||||
@ -85,12 +84,12 @@ class Details extends preact.Component<DetailProps, DetailState> {
|
||||
<div>
|
||||
Accepted exchanges:
|
||||
<ul>
|
||||
{props.contract.exchanges.map(
|
||||
{this.props.contract.exchanges.map(
|
||||
e => <li>{`${e.url}: ${e.master_pub}`}</li>)}
|
||||
</ul>
|
||||
Exchanges in the wallet:
|
||||
<ul>
|
||||
{(state.exchanges || []).map(
|
||||
{(this.state.exchanges || []).map(
|
||||
(e: IExchangeInfo) =>
|
||||
<li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)}
|
||||
</ul>
|
||||
@ -109,7 +108,7 @@ interface ContractPromptState {
|
||||
payDisabled: boolean;
|
||||
}
|
||||
|
||||
class ContractPrompt extends preact.Component<ContractPromptProps, ContractPromptState> {
|
||||
class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
@ -179,18 +178,18 @@ class ContractPrompt extends preact.Component<ContractPromptProps, ContractPromp
|
||||
}
|
||||
|
||||
|
||||
render(props: ContractPromptProps, state: ContractPromptState) {
|
||||
let c = props.offer.contract;
|
||||
render() {
|
||||
let c = this.props.offer.contract;
|
||||
return (
|
||||
<div>
|
||||
{renderContract(c)}
|
||||
<button onClick={() => this.doPayment()}
|
||||
disabled={state.payDisabled}
|
||||
disabled={this.state.payDisabled}
|
||||
className="accept">
|
||||
Confirm payment
|
||||
</button>
|
||||
{(state.error ? <p className="errorbox">{state.error}</p> : <p />)}
|
||||
<Details contract={c} collapsed={!state.error}/>
|
||||
{(this.state.error ? <p className="errorbox">{this.state.error}</p> : <p />)}
|
||||
<Details contract={c} collapsed={!this.state.error}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -204,6 +203,6 @@ export function main() {
|
||||
console.dir(offer);
|
||||
let contract = offer.contract;
|
||||
|
||||
preact.render(<ContractPrompt offer={offer}/>, document.getElementById(
|
||||
ReactDOM.render(<ContractPrompt offer={offer}/>, document.getElementById(
|
||||
"contract")!);
|
||||
}
|
||||
|
@ -7,7 +7,8 @@
|
||||
<link rel="icon" href="../img/icon.png">
|
||||
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
<script src="../lib/vendor/preact.js"></script>
|
||||
<script src="../lib/vendor/react.js"></script>
|
||||
<script src="../lib/vendor/react-dom.js"></script>
|
||||
|
||||
<!-- i18n -->
|
||||
<script src="../lib/vendor/jed.js"></script>
|
||||
|
@ -31,7 +31,6 @@ import {ImplicitStateComponent, StateHolder} from "../lib/components";
|
||||
|
||||
"use strict";
|
||||
|
||||
let h = preact.h;
|
||||
|
||||
function delay<T>(delayMs: number, value: T): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
@ -227,7 +226,7 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
if (this.statusString()) {
|
||||
return (
|
||||
<p>
|
||||
<strong style="color: red;">A problem occured, see below.</strong>
|
||||
<strong style={{color: "red"}}>A problem occured, see below.</strong>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@ -238,12 +237,12 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
);
|
||||
}
|
||||
|
||||
render(props: ExchangeSelectionProps): JSX.Element {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{"You are about to withdraw "}
|
||||
<strong>{amountToPretty(props.amount)}</strong>
|
||||
<strong>{amountToPretty(this.props.amount)}</strong>
|
||||
{" from your bank account into your wallet."}
|
||||
</p>
|
||||
{this.renderFeeStatus()}
|
||||
@ -359,7 +358,7 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
|
||||
renderStatus(): any {
|
||||
if (this.statusString()) {
|
||||
return <p><strong style="color: red;">{this.statusString()}</strong></p>;
|
||||
return <p><strong style={{color: "red"}}>{this.statusString()}</strong></p>;
|
||||
} else if (!this.reserveCreationInfo()) {
|
||||
return <p>Checking URL, please wait ...</p>;
|
||||
}
|
||||
@ -384,7 +383,7 @@ export async function main() {
|
||||
amount
|
||||
};
|
||||
|
||||
preact.render(<ExchangeSelection {...args} />, document.getElementById(
|
||||
ReactDOM.render(<ExchangeSelection {...args} />, document.getElementById(
|
||||
"exchange-selection")!);
|
||||
|
||||
} catch (e) {
|
||||
|
@ -10,7 +10,8 @@
|
||||
<link rel="icon" href="../img/icon.png">
|
||||
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
<script src="../lib/vendor/preact.js"></script>
|
||||
<script src="../lib/vendor/react.js"></script>
|
||||
<script src="../lib/vendor/react-dom.js"></script>
|
||||
|
||||
<!-- i18n -->
|
||||
<script src="../lib/vendor/jed.js"></script>
|
||||
@ -29,4 +30,4 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
@ -20,7 +20,6 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
/// <reference path="../lib/decl/preact.d.ts" />
|
||||
|
||||
import { IExchangeInfo } from "../lib/wallet/types";
|
||||
import { ReserveRecord, Coin, PreCoin, Denomination } from "../lib/wallet/types";
|
||||
@ -30,12 +29,13 @@ import {
|
||||
refresh
|
||||
} from "../lib/wallet/wxApi";
|
||||
import { prettyAmount, abbrev } from "../lib/wallet/renderHtml";
|
||||
import { getTalerStampDate } from "../lib/wallet/helpers";
|
||||
|
||||
interface ReserveViewProps {
|
||||
reserve: ReserveRecord;
|
||||
}
|
||||
|
||||
class ReserveView extends preact.Component<ReserveViewProps, void> {
|
||||
class ReserveView extends React.Component<ReserveViewProps, void> {
|
||||
render(): JSX.Element {
|
||||
let r: ReserveRecord = this.props.reserve;
|
||||
return (
|
||||
@ -78,7 +78,7 @@ class Toggle extends ImplicitStateComponent<ToggleProps> {
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div style="display:inline;">
|
||||
<div style={{display: "inline"}}>
|
||||
{this.renderButton()}
|
||||
{this.props.expanded() ? this.props.children : []}
|
||||
</div>);
|
||||
@ -99,7 +99,7 @@ class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> {
|
||||
render(): JSX.Element {
|
||||
if (!this.refreshRequested()) {
|
||||
return (
|
||||
<div style="display:inline;">
|
||||
<div style={{display: "inline"}}>
|
||||
<button onClick={() => this.refreshRequested(true)}>refresh</button>
|
||||
</div>
|
||||
);
|
||||
@ -114,7 +114,7 @@ class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> {
|
||||
}
|
||||
}
|
||||
|
||||
class CoinView extends preact.Component<CoinViewProps, void> {
|
||||
class CoinView extends React.Component<CoinViewProps, void> {
|
||||
render() {
|
||||
let c = this.props.coin;
|
||||
return (
|
||||
@ -137,7 +137,7 @@ interface PreCoinViewProps {
|
||||
precoin: PreCoin;
|
||||
}
|
||||
|
||||
class PreCoinView extends preact.Component<PreCoinViewProps, void> {
|
||||
class PreCoinView extends React.Component<PreCoinViewProps, void> {
|
||||
render() {
|
||||
let c = this.props.precoin;
|
||||
return (
|
||||
@ -160,14 +160,18 @@ class CoinList extends ImplicitStateComponent<CoinListProps> {
|
||||
|
||||
constructor(props: CoinListProps) {
|
||||
super(props);
|
||||
this.update();
|
||||
this.update(props);
|
||||
}
|
||||
|
||||
async update() {
|
||||
let coins = await getCoins(this.props.exchangeBaseUrl);
|
||||
async update(props: CoinListProps) {
|
||||
let coins = await getCoins(props.exchangeBaseUrl);
|
||||
this.coins(coins);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps: CoinListProps) {
|
||||
this.update(newProps);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
if (!this.coins()) {
|
||||
return <div>...</div>;
|
||||
@ -223,6 +227,49 @@ interface DenominationListProps {
|
||||
exchange: IExchangeInfo;
|
||||
}
|
||||
|
||||
interface ExpanderTextProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
|
||||
expanded = this.makeState<boolean>(false);
|
||||
textArea: any = undefined;
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.expanded() && this.textArea) {
|
||||
this.textArea.focus();
|
||||
this.textArea.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
if (!this.expanded()) {
|
||||
return (
|
||||
<span onClick={() => { this.expanded(true); }}>
|
||||
{(this.props.text.length <= 10)
|
||||
? this.props.text
|
||||
: (
|
||||
<span>
|
||||
{this.props.text.substring(0,10)}
|
||||
<span style={{textDecoration: "underline"}}>...</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<textarea
|
||||
readOnly
|
||||
style={{display: "block"}}
|
||||
onBlur={() => this.expanded(false)}
|
||||
ref={(e) => this.textArea = e}>
|
||||
{this.props.text}
|
||||
</textarea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DenominationList extends ImplicitStateComponent<DenominationListProps> {
|
||||
expanded = this.makeState<boolean>(false);
|
||||
|
||||
@ -235,6 +282,11 @@ class DenominationList extends ImplicitStateComponent<DenominationListProps> {
|
||||
<li>Refresh fee: {prettyAmount(d.fee_refresh)}</li>
|
||||
<li>Deposit fee: {prettyAmount(d.fee_deposit)}</li>
|
||||
<li>Refund fee: {prettyAmount(d.fee_refund)}</li>
|
||||
<li>Start: {getTalerStampDate(d.stamp_start)!.toString()}</li>
|
||||
<li>Withdraw expiration: {getTalerStampDate(d.stamp_expire_withdraw)!.toString()}</li>
|
||||
<li>Legal expiration: {getTalerStampDate(d.stamp_expire_legal)!.toString()}</li>
|
||||
<li>Deposit expiration: {getTalerStampDate(d.stamp_expire_deposit)!.toString()}</li>
|
||||
<li>Denom pub: <ExpanderText text={d.denom_pub} /></li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
@ -287,12 +339,15 @@ interface ExchangeProps {
|
||||
exchange: IExchangeInfo;
|
||||
}
|
||||
|
||||
class ExchangeView extends preact.Component<ExchangeProps, void> {
|
||||
class ExchangeView extends React.Component<ExchangeProps, void> {
|
||||
render(): JSX.Element {
|
||||
let e = this.props.exchange;
|
||||
return (
|
||||
<div className="tree-item">
|
||||
Url: {this.props.exchange.baseUrl}
|
||||
<ul>
|
||||
<li>Exchange Base Url: {this.props.exchange.baseUrl}</li>
|
||||
<li>Master public key: <ExpanderText text={this.props.exchange.masterPublicKey} /></li>
|
||||
</ul>
|
||||
<DenominationList exchange={e} />
|
||||
<ReserveList exchangeBaseUrl={this.props.exchange.baseUrl} />
|
||||
<CoinList exchangeBaseUrl={this.props.exchange.baseUrl} />
|
||||
@ -306,9 +361,17 @@ interface ExchangesListState {
|
||||
exchanges: IExchangeInfo[];
|
||||
}
|
||||
|
||||
class ExchangesList extends preact.Component<any, ExchangesListState> {
|
||||
class ExchangesList extends React.Component<any, ExchangesListState> {
|
||||
constructor() {
|
||||
super();
|
||||
let port = chrome.runtime.connect();
|
||||
port.onMessage.addListener((msg: any) => {
|
||||
if (msg.notify) {
|
||||
console.log("got notified");
|
||||
this.update();
|
||||
}
|
||||
});
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
@ -332,5 +395,5 @@ class ExchangesList extends preact.Component<any, ExchangesListState> {
|
||||
}
|
||||
|
||||
export function main() {
|
||||
preact.render(<ExchangesList />, document.body);
|
||||
ReactDOM.render(<ExchangesList />, document.body);
|
||||
}
|
||||
|
@ -7,7 +7,8 @@
|
||||
<link rel="stylesheet" type="text/css" href="../style/lang.css">
|
||||
<link rel="stylesheet" type="text/css" href="popup.css">
|
||||
|
||||
<script src="../lib/vendor/preact.js"></script>
|
||||
<script src="../lib/vendor/react.js"></script>
|
||||
<script src="../lib/vendor/react-dom.js"></script>
|
||||
<script src="../lib/vendor/URI.js"></script>
|
||||
|
||||
<script src="../lib/vendor/jed.js"></script>
|
||||
|
@ -36,15 +36,19 @@ import {abbrev, prettyAmount} from "../lib/wallet/renderHtml";
|
||||
|
||||
declare var i18n: any;
|
||||
|
||||
function onUpdateNotification(f: () => void) {
|
||||
function onUpdateNotification(f: () => void): () => void {
|
||||
let port = chrome.runtime.connect({name: "notifications"});
|
||||
port.onMessage.addListener((msg, port) => {
|
||||
let listener = (msg: any, port: any) => {
|
||||
f();
|
||||
});
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
return () => {
|
||||
port.onMessage.removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Router extends preact.Component<any,any> {
|
||||
class Router extends React.Component<any,any> {
|
||||
static setRoute(s: string): void {
|
||||
window.location.hash = s;
|
||||
}
|
||||
@ -79,24 +83,29 @@ class Router extends preact.Component<any,any> {
|
||||
}
|
||||
|
||||
|
||||
render(props: any, state: any): JSX.Element {
|
||||
render(): JSX.Element {
|
||||
let route = window.location.hash.substring(1);
|
||||
console.log("rendering route", route);
|
||||
let defaultChild: JSX.Element|null = null;
|
||||
for (let child of props.children) {
|
||||
if (child.attributes["default"]) {
|
||||
let defaultChild: React.ReactChild|null = null;
|
||||
let foundChild: React.ReactChild|null = null;
|
||||
React.Children.forEach(this.props.children, (child) => {
|
||||
let childProps: any = (child as any).props;
|
||||
if (!childProps) {
|
||||
return;
|
||||
}
|
||||
if (childProps["default"]) {
|
||||
defaultChild = child;
|
||||
}
|
||||
if (child.attributes["route"] == route) {
|
||||
return <div>{child}</div>;
|
||||
if (childProps["route"] == route) {
|
||||
foundChild = child;
|
||||
}
|
||||
}
|
||||
if (defaultChild == null) {
|
||||
})
|
||||
let child: React.ReactChild | null = foundChild || defaultChild;
|
||||
if (!child) {
|
||||
throw Error("unknown route");
|
||||
}
|
||||
console.log("rendering default route");
|
||||
Router.setRoute(defaultChild.attributes["route"]);
|
||||
return <div>{defaultChild}</div>;
|
||||
Router.setRoute((child as any).props["route"]);
|
||||
return <div>{child}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +115,7 @@ export function main() {
|
||||
let el = (
|
||||
<div>
|
||||
<WalletNavBar />
|
||||
<div style="margin:1em">
|
||||
<div style={{margin: "1em"}}>
|
||||
<Router>
|
||||
<WalletBalanceView route="/balance" default/>
|
||||
<WalletHistory route="/history"/>
|
||||
@ -116,11 +125,12 @@ export function main() {
|
||||
</div>
|
||||
);
|
||||
|
||||
preact.render(el, document.getElementById("content")!);
|
||||
ReactDOM.render(el, document.getElementById("content")!);
|
||||
}
|
||||
|
||||
interface TabProps extends preact.ComponentProps {
|
||||
interface TabProps {
|
||||
target: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
function Tab(props: TabProps) {
|
||||
@ -128,7 +138,7 @@ function Tab(props: TabProps) {
|
||||
if (props.target == Router.getRoute()) {
|
||||
cssClass = "active";
|
||||
}
|
||||
let onClick = (e: Event) => {
|
||||
let onClick = (e: React.MouseEvent) => {
|
||||
Router.setRoute(props.target);
|
||||
e.preventDefault();
|
||||
};
|
||||
@ -140,7 +150,7 @@ function Tab(props: TabProps) {
|
||||
}
|
||||
|
||||
|
||||
class WalletNavBar extends preact.Component<any,any> {
|
||||
class WalletNavBar extends React.Component<any,any> {
|
||||
cancelSubscription: any;
|
||||
|
||||
componentWillMount() {
|
||||
@ -158,7 +168,7 @@ class WalletNavBar extends preact.Component<any,any> {
|
||||
render() {
|
||||
console.log("rendering nav bar");
|
||||
return (
|
||||
<div class="nav" id="header">
|
||||
<div className="nav" id="header">
|
||||
<Tab target="/balance">
|
||||
Balance
|
||||
</Tab>
|
||||
@ -174,7 +184,7 @@ class WalletNavBar extends preact.Component<any,any> {
|
||||
|
||||
|
||||
function ExtensionLink(props: any) {
|
||||
let onClick = (e: Event) => {
|
||||
let onClick = (e: React.MouseEvent) => {
|
||||
chrome.tabs.create({
|
||||
"url": chrome.extension.getURL(props.target)
|
||||
});
|
||||
@ -186,18 +196,30 @@ function ExtensionLink(props: any) {
|
||||
</a>)
|
||||
}
|
||||
|
||||
class WalletBalanceView extends preact.Component<any, any> {
|
||||
class WalletBalanceView extends React.Component<any, any> {
|
||||
balance: WalletBalance;
|
||||
gotError = false;
|
||||
canceler: (() => void) | undefined = undefined;
|
||||
unmount = false;
|
||||
|
||||
componentWillMount() {
|
||||
this.canceler = onUpdateNotification(() => this.updateBalance());
|
||||
this.updateBalance();
|
||||
}
|
||||
|
||||
onUpdateNotification(() => this.updateBalance());
|
||||
componentWillUnmount() {
|
||||
console.log("component WalletBalanceView will unmount");
|
||||
if (this.canceler) {
|
||||
this.canceler();
|
||||
}
|
||||
this.unmount = true;
|
||||
}
|
||||
|
||||
updateBalance() {
|
||||
chrome.runtime.sendMessage({type: "balances"}, (resp) => {
|
||||
if (this.unmount) {
|
||||
return;
|
||||
}
|
||||
if (resp.error) {
|
||||
this.gotError = true;
|
||||
console.error("could not retrieve balances", resp);
|
||||
@ -232,7 +254,7 @@ class WalletBalanceView extends preact.Component<any, any> {
|
||||
if (Amounts.isNonZero(entry.pendingIncoming)) {
|
||||
incoming = (
|
||||
<span>
|
||||
<span style="color: darkgreen">
|
||||
<span style={{color: "darkgreen"}}>
|
||||
{"+"}
|
||||
{prettyAmount(entry.pendingIncoming)}
|
||||
</span>
|
||||
@ -244,7 +266,7 @@ class WalletBalanceView extends preact.Component<any, any> {
|
||||
if (Amounts.isNonZero(entry.pendingPayment)) {
|
||||
payment = (
|
||||
<span>
|
||||
<span style="color: darkblue">
|
||||
<span style={{color: "darkblue"}}>
|
||||
{prettyAmount(entry.pendingPayment)}
|
||||
</span>
|
||||
{" "}
|
||||
@ -349,17 +371,26 @@ function formatHistoryItem(historyItem: HistoryRecord) {
|
||||
}
|
||||
|
||||
|
||||
class WalletHistory extends preact.Component<any, any> {
|
||||
class WalletHistory extends React.Component<any, any> {
|
||||
myHistory: any[];
|
||||
gotError = false;
|
||||
unmounted = false;
|
||||
|
||||
componentWillMount() {
|
||||
this.update();
|
||||
onUpdateNotification(() => this.update());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
console.log("history component unmounted");
|
||||
this.unmounted = true;
|
||||
}
|
||||
|
||||
update() {
|
||||
chrome.runtime.sendMessage({type: "get-history"}, (resp) => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
console.log("got history response");
|
||||
if (resp.error) {
|
||||
this.gotError = true;
|
||||
|
6
thirdparty/README
vendored
6
thirdparty/README
vendored
@ -5,12 +5,6 @@ symlinks, or via the build system.
|
||||
|
||||
The following third party modules are used:
|
||||
|
||||
Directory: preact
|
||||
Copyright: Jason Miller
|
||||
License: The MIT License
|
||||
Description: Lightweight DOM rendering library
|
||||
Repository: https://github.com/developit/preact
|
||||
|
||||
Directory: jed
|
||||
Copyright: jQuery Foundation and other contributors, https://jquery.org/
|
||||
License: See jed/LICENSE
|
||||
|
15
thirdparty/preact/.editorconfig
vendored
15
thirdparty/preact/.editorconfig
vendored
@ -1,15 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{package.json,.*rc,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
10
thirdparty/preact/.gitignore
vendored
10
thirdparty/preact/.gitignore
vendored
@ -1,10 +0,0 @@
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
.DS_Store
|
||||
/dist
|
||||
/_dev
|
||||
/coverage
|
||||
|
||||
# Additional bundles
|
||||
/*.js
|
||||
/*.js.map
|
36
thirdparty/preact/.travis.yml
vendored
36
thirdparty/preact/.travis.yml
vendored
@ -1,36 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "6"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
# Make chrome browser available for testing
|
||||
before_install:
|
||||
- export CHROME_BIN=chromium-browser
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- npm run build
|
||||
- npm run test
|
||||
- BROWSER=true COVERAGE=false FLAKEY=false PERFORMANCE=false npm run test:karma
|
||||
|
||||
# Necessary to compile native modules for io.js v3 or Node.js v4
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
|
||||
# Necessary to compile native modules for io.js v3 or Node.js v4
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
21
thirdparty/preact/LICENSE
vendored
21
thirdparty/preact/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Jason Miller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
372
thirdparty/preact/README.md
vendored
372
thirdparty/preact/README.md
vendored
@ -1,372 +0,0 @@
|
||||
<a href="https://preactjs.com">
|
||||
<img alt="Preact" title="Preact" src="https://cdn.rawgit.com/developit/b4416d5c92b743dbaec1e68bc4c27cda/raw/3235dc508f7eb834ebf48418aea212a05df13db1/preact-logo-trans.svg" width="550">
|
||||
</a>
|
||||
|
||||
**Preact is a fast, `3kB` alternative to React, with the same ES2015 API.**
|
||||
|
||||
Preact retains a large amount of compatibility with React, but only the modern ([ES6 Classes] and [stateless functional components](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components)) interfaces.
|
||||
As one would expect coming from React, Components are simple building blocks for composing a User Interface.
|
||||
|
||||
### :information_desk_person: Full documentation is available at the [Preact Website ➞](https://preactjs.com)
|
||||
|
||||
[![CDNJS](https://img.shields.io/cdnjs/v/preact.svg)](https://cdnjs.com/libraries/preact)
|
||||
[![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
|
||||
[![travis](https://travis-ci.org/developit/preact.svg?branch=master)](https://travis-ci.org/developit/preact)
|
||||
[![gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/developit/preact)
|
||||
|
||||
[![Browsers](https://saucelabs.com/browser-matrix/preact.svg)](https://saucelabs.com/u/preact)
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Demos
|
||||
|
||||
- [**ESBench**](http://esbench.com) is built using Preact.
|
||||
- [**Nectarine.rocks**](http://nectarine.rocks) _([Github Project](https://github.com/developit/nectarine))_ :peach:
|
||||
- [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([Github Project](https://github.com/developit/documentation-viewer))_
|
||||
- [**TodoMVC**](https://preact-todomvc.surge.sh) _([Github Project](https://github.com/developit/preact-todomvc))_
|
||||
- [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([Github Project](https://github.com/developit/hn_minimal))_
|
||||
- [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([Github Project](https://github.com/developit/preact-boilerplate))_ :zap:
|
||||
- [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
|
||||
- [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen)
|
||||
- [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen)
|
||||
- [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen)
|
||||
- [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle)
|
||||
- [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen)
|
||||
- [**Stock Ticker**](http://codepen.io/developit/pen/wMYoBb?editors=0010) (@ CodePen)
|
||||
- [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle)
|
||||
- [**Preact Coffeescript**](https://github.com/crisward/preact-coffee)
|
||||
- [**GuriVR**](https://gurivr.com) _([Github Project](https://github.com/opennewslabs/guri-vr))_
|
||||
- [**V2EX Preact**](https://github.com/yanni4night/v2ex-preact)
|
||||
|
||||
## Libraries & Add-ons
|
||||
|
||||
- :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact *([full example](http://git.io/preact-compat-example))*
|
||||
- :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact
|
||||
- :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering.
|
||||
- :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components
|
||||
- :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components
|
||||
- :satellite: [**preact-portal**](https://git.io/preact-portal): Render Preact components into (a) SPACE :milky_way:
|
||||
- :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component
|
||||
- :bookmark: [**preact-token-input**](https://github.com/developit/preact-token-input): Text field that tokenizes input, for things like tags
|
||||
- :card_index: [**preact-virtual-list**](https://github.com/developit/preact-virtual-list): Easily render lists with millions of rows ([demo](https://jsfiddle.net/developit/qqan9pdo/))
|
||||
- :triangular_ruler: [**preact-layout**](https://download.github.io/preact-layout/): Small and simple layout library
|
||||
- :thought_balloon: [**preact-socrates**](https://github.com/matthewmueller/preact-socrates): Preact plugin for [Socrates](http://github.com/matthewmueller/socrates)
|
||||
- :rowboat: [**preact-flyd**](https://github.com/xialvjun/preact-flyd): Use [flyd](https://github.com/paldepind/flyd) FRP streams in Preact + JSX
|
||||
- :speech_balloon: [**preact-i18nline**](https://github.com/download/preact-i18nline): Integrates the ecosystem around [i18n-js](https://github.com/everydayhero/i18n-js) with Preact via [i18nline](https://github.com/download/i18nline).
|
||||
- :white_square_button: [**preact-mdl**](https://git.io/preact-mdl): Use [MDL](https://getmdl.io) as Preact components
|
||||
- :rocket: [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com)
|
||||
- :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM, right in Node)_
|
||||
- :tophat: [**preact-classless-component**](https://github.com/ld0rman/preact-classless-component): create preact components without the class keyword
|
||||
- :hammer: [**preact-hyperscript**](https://github.com/queckezz/preact-hyperscript): Hyperscript-like syntax for creating elements
|
||||
- :white_check_mark: [**shallow-compare**](https://github.com/tkh44/shallow-compare): simplified `shouldComponentUpdate` helper.
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
> :information_desk_person: You [don't _have_ to use ES2015 to use Preact](https://github.com/developit/preact-without-babel)... but you should.
|
||||
|
||||
The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc. If you don't, start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010).
|
||||
|
||||
|
||||
### Import what you need
|
||||
|
||||
The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals:
|
||||
|
||||
##### Named:
|
||||
|
||||
```js
|
||||
import { h, render, Component } from 'preact';
|
||||
|
||||
// Tell Babel to transform JSX into h() calls:
|
||||
/** @jsx h */
|
||||
```
|
||||
|
||||
##### Default:
|
||||
|
||||
```js
|
||||
import preact from 'preact';
|
||||
|
||||
// Tell Babel to transform JSX into preact.h() calls:
|
||||
/** @jsx preact.h */
|
||||
```
|
||||
|
||||
> Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library.
|
||||
>
|
||||
> Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
|
||||
>
|
||||
> **For Babel 5 and prior:**
|
||||
>
|
||||
> ```json
|
||||
> { "jsxPragma": "h" }
|
||||
> ```
|
||||
>
|
||||
> **For Babel 6:**
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "plugins": [
|
||||
> ["transform-react-jsx", { "pragma":"h" }]
|
||||
> ]
|
||||
> }
|
||||
> ```
|
||||
|
||||
|
||||
### Rendering JSX
|
||||
|
||||
Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM.
|
||||
|
||||
To render some JSX, just import those two functions and use them like so:
|
||||
|
||||
```js
|
||||
import { h, render } from 'preact';
|
||||
|
||||
render((
|
||||
<div id="foo">
|
||||
<span>Hello, world!</span>
|
||||
<button onClick={ e => alert("hi!") }>Click Me</button>
|
||||
</div>
|
||||
), document.body);
|
||||
```
|
||||
|
||||
This should seem pretty straightforward if you've used [hyperscript] or one of its many friends.
|
||||
|
||||
Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2:
|
||||
|
||||
|
||||
### Components
|
||||
|
||||
Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface. Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`. Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update.
|
||||
|
||||
Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX.
|
||||
|
||||
Let's take a look at a very simple `Clock` component, which shows the current time.
|
||||
|
||||
```js
|
||||
import { h, render, Component } from 'preact';
|
||||
|
||||
class Clock extends Component {
|
||||
render() {
|
||||
let time = new Date().toLocaleTimeString();
|
||||
return <span>{ time }</span>;
|
||||
}
|
||||
}
|
||||
|
||||
// render an instance of Clock into <body>:
|
||||
render(<Clock />, document.body);
|
||||
```
|
||||
|
||||
|
||||
That's great. Running this produces the following HTML DOM structure:
|
||||
|
||||
```html
|
||||
<span>10:28:57 PM</span>
|
||||
```
|
||||
|
||||
In order to have the clock's time update every second, we need to know when `<Clock>` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component:
|
||||
|
||||
| Lifecycle method | When it gets called |
|
||||
|-----------------------------|--------------------------------------------------|
|
||||
| `componentWillMount` | before the component gets mounted to the DOM |
|
||||
| `componentDidMount` | after the component gets mounted to the DOM |
|
||||
| `componentWillUnmount` | prior to removal from the DOM |
|
||||
| `componentDidUnmount` | after removal from the DOM |
|
||||
| `componentWillReceiveProps` | before new props get accepted |
|
||||
| `shouldComponentUpdate` | before `render()`. Return `false` to skip render |
|
||||
| `componentWillUpdate` | before `render()` |
|
||||
| `componentDidUpdate` | after `render()` |
|
||||
|
||||
|
||||
|
||||
So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component.
|
||||
|
||||
```js
|
||||
import { h, render, Component } from 'preact';
|
||||
|
||||
class Clock extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
// set initial time:
|
||||
this.state.time = Date.now();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// update time every second
|
||||
this.timer = setInterval(() => {
|
||||
this.setState({ time: Date.now() });
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// stop when not renderable
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
render(props, state) {
|
||||
let time = new Date(state.time).toLocaleTimeString();
|
||||
return <span>{ time }</span>;
|
||||
}
|
||||
}
|
||||
|
||||
// render an instance of Clock into <body>:
|
||||
render(<Clock />, document.body);
|
||||
```
|
||||
|
||||
Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)!
|
||||
|
||||
|
||||
### Props & State
|
||||
|
||||
The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes. You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Linked State
|
||||
|
||||
One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events. Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary.
|
||||
|
||||
One solution to this is to bind component methods declaratively.
|
||||
Here is an example using [decko](http://git.io/decko):
|
||||
|
||||
```js
|
||||
class Foo extends Component {
|
||||
@bind
|
||||
updateText(e) {
|
||||
this.setState({ text: e.target.value });
|
||||
}
|
||||
render({ }, { text }) {
|
||||
return <input value={text} onInput={this.updateText} />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI.
|
||||
|
||||
Fortunately there is a solution, in the form of `linkState()`. Calling `component.linkState('text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to linkState() with the same state property are cached, so there is no performance penalty. Here is the previous example rewritten using _Linked State_:
|
||||
|
||||
```js
|
||||
class Foo extends Component {
|
||||
render({ }, { text }) {
|
||||
return <input value={text} onInput={this.linkState('text')} />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Here is a somewhat verbose Preact `<Link>` component:
|
||||
|
||||
```js
|
||||
class Link extends Component {
|
||||
render(props, state) {
|
||||
return <a href={ props.href }>{ props.children }</a>;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since this is ES6/ES2015, we can further simplify:
|
||||
|
||||
```js
|
||||
class Link extends Component {
|
||||
render({ href, children }) {
|
||||
return <a {...{ href, children }} />;
|
||||
}
|
||||
}
|
||||
|
||||
// or, for wide-open props support:
|
||||
class Link extends Component {
|
||||
render(props) {
|
||||
return <a {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
// or, as a stateless functional component:
|
||||
const Link = ({ children, ...props }) => (
|
||||
<a {...props}>{ children }</a>
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## Extensions
|
||||
|
||||
It is likely that some projects based on Preact would wish to extend Component with great new functionality.
|
||||
|
||||
Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`. Just use ES2015 inheritance:
|
||||
|
||||
```js
|
||||
class BoundComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.bind();
|
||||
}
|
||||
bind() {
|
||||
this.binds = {};
|
||||
for (let i in this) {
|
||||
this.binds[i] = this[i].bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// example usage
|
||||
class Link extends BoundComponent {
|
||||
click() {
|
||||
open(this.href);
|
||||
}
|
||||
render() {
|
||||
let { click } = this.binds;
|
||||
return <span onclick={ click }>{ children }</span>;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The possibilities are pretty endless here. You could even add support for rudimentary mixins:
|
||||
|
||||
```js
|
||||
class MixedComponent extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
(this.mixins || []).forEach( m => Object.assign(this, m) );
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Developer Tools
|
||||
|
||||
You can inspect and modify the state of your Preact UI components at runtime using the
|
||||
[React Developer Tools](https://github.com/facebook/react-devtools) browser extension.
|
||||
|
||||
1. Install the [React Developer Tools](https://github.com/facebook/react-devtools) extension
|
||||
2. Import the "preact/devtools" module in your app
|
||||
3. Reload and go to the 'React' tab in the browser's development tools
|
||||
|
||||
|
||||
```js
|
||||
import { h, Component, render } from 'preact';
|
||||
|
||||
// Enable devtools. You can reduce the size of your app by only including this
|
||||
// module in development builds. eg. In Webpack, wrap this with an `if (module.hot) {...}`
|
||||
// check.
|
||||
require('preact/devtools');
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
|
||||
|
||||
[![Preact](http://i.imgur.com/YqCHvEW.gif)](https://preactjs.com)
|
||||
|
||||
|
||||
|
||||
[ES6 Classes]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
|
||||
[hyperscript]: https://github.com/dominictarr/hyperscript
|
||||
[preact-boilerplate]: https://github.com/developit/preact-boilerplate
|
||||
[lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html
|
39
thirdparty/preact/config/codemod-const.js
vendored
39
thirdparty/preact/config/codemod-const.js
vendored
@ -1,39 +0,0 @@
|
||||
/* eslint no-console:0 */
|
||||
|
||||
/** Find constants (identified by ALL_CAPS_DECLARATIONS), and inline them globally.
|
||||
* This is safe because Preact *only* uses global constants.
|
||||
*/
|
||||
export default (file, api) => {
|
||||
let j = api.jscodeshift,
|
||||
code = j(file.source),
|
||||
constants = {},
|
||||
found = 0;
|
||||
|
||||
code.find(j.VariableDeclaration)
|
||||
.filter( decl => {
|
||||
for (let i=decl.value.declarations.length; i--; ) {
|
||||
let node = decl.value.declarations[i],
|
||||
name = node.id && node.id.name,
|
||||
init = node.init;
|
||||
if (name && init && name.match(/^[A-Z0-9_$]+$/g)) {
|
||||
if (init.type==='Literal') {
|
||||
console.log(`Inlining constant: ${name}=${init.raw}`);
|
||||
found++;
|
||||
constants[name] = init;
|
||||
// remove declaration
|
||||
decl.value.declarations.splice(i, 1);
|
||||
// if it's the last, we'll remove the whole statement
|
||||
return !decl.value.declarations.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.remove();
|
||||
|
||||
code.find(j.Identifier)
|
||||
.filter( path => path.value.name && constants.hasOwnProperty(path.value.name) )
|
||||
.replaceWith( path => (found++, constants[path.value.name]) );
|
||||
|
||||
return found ? code.toSource({ quote: 'single' }) : null;
|
||||
};
|
56
thirdparty/preact/config/codemod-strip-tdz.js
vendored
56
thirdparty/preact/config/codemod-strip-tdz.js
vendored
@ -1,56 +0,0 @@
|
||||
/* eslint no-console:0 */
|
||||
|
||||
|
||||
// parent node types that we don't want to remove pointless initializations from (because it breaks hoisting)
|
||||
const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement'
|
||||
|
||||
/** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */
|
||||
export default (file, api) => {
|
||||
let { jscodeshift } = api,
|
||||
found = 0;
|
||||
|
||||
let code = jscodeshift(file.source)
|
||||
.find(jscodeshift.VariableDeclaration)
|
||||
.forEach(handleDeclaration);
|
||||
|
||||
function handleDeclaration(decl) {
|
||||
let p = decl,
|
||||
remove = true;
|
||||
|
||||
while ((p = p.parentPath)) {
|
||||
if (~BLOCKED.indexOf(p.value.type)) {
|
||||
remove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
decl.value.declarations.filter(isPointless).forEach( node => {
|
||||
if (remove===false) {
|
||||
console.log(`> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}`);
|
||||
}
|
||||
else {
|
||||
removeNodeInitialization(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeNodeInitialization(node) {
|
||||
node.init = null;
|
||||
found++;
|
||||
}
|
||||
|
||||
function isPointless(node) {
|
||||
let { init } = node;
|
||||
if (init) {
|
||||
if (init.type==='UnaryExpression' && init.operator==='void' && init.argument.value==0) {
|
||||
return true;
|
||||
}
|
||||
if (init.type==='Identifier' && init.name==='undefined') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return found ? code.toSource({ quote: 'single' }) : null;
|
||||
};
|
66
thirdparty/preact/config/eslint-config.js
vendored
66
thirdparty/preact/config/eslint-config.js
vendored
@ -1,66 +0,0 @@
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
extends: 'eslint:recommended',
|
||||
plugins: [
|
||||
'react'
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
mocha: true,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
modules: true,
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
globals: {
|
||||
sinon: true,
|
||||
expect: true
|
||||
},
|
||||
rules: {
|
||||
'react/jsx-uses-react': 2,
|
||||
'react/jsx-uses-vars': 2,
|
||||
'no-unused-vars': [1, { varsIgnorePattern: '^h$' }],
|
||||
'no-cond-assign': 1,
|
||||
'no-empty': 0,
|
||||
'no-console': 1,
|
||||
semi: 2,
|
||||
camelcase: 0,
|
||||
'comma-style': 2,
|
||||
'comma-dangle': [2, 'never'],
|
||||
indent: [2, 'tab', {SwitchCase: 1}],
|
||||
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
|
||||
'no-trailing-spaces': [2, { skipBlankLines: true }],
|
||||
'max-nested-callbacks': [2, 3],
|
||||
'no-eval': 2,
|
||||
'no-implied-eval': 2,
|
||||
'no-new-func': 2,
|
||||
'guard-for-in': 0,
|
||||
eqeqeq: 0,
|
||||
'no-else-return': 2,
|
||||
'no-redeclare': 2,
|
||||
'no-dupe-keys': 2,
|
||||
radix: 2,
|
||||
strict: [2, 'never'],
|
||||
'no-shadow': 0,
|
||||
'callback-return': [1, ['callback', 'cb', 'next', 'done']],
|
||||
'no-delete-var': 2,
|
||||
'no-undef-init': 2,
|
||||
'no-shadow-restricted-names': 2,
|
||||
'handle-callback-err': 0,
|
||||
'no-lonely-if': 2,
|
||||
'keyword-spacing': 2,
|
||||
'constructor-super': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-dupe-class-members': 2,
|
||||
'no-const-assign': 2,
|
||||
'prefer-spread': 2,
|
||||
'no-useless-concat': 2,
|
||||
'no-var': 2,
|
||||
'object-shorthand': 2,
|
||||
'prefer-arrow-callback': 2
|
||||
}
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
import memory from 'rollup-plugin-memory';
|
||||
import rollupConfig from './rollup.config';
|
||||
|
||||
export default Object.assign({}, rollupConfig, {
|
||||
plugins: [
|
||||
memory({
|
||||
path: 'src/preact',
|
||||
contents: `import { h } from './preact';export * from './preact';export { h as createElement };`
|
||||
}),
|
||||
...rollupConfig.plugins.slice(1)
|
||||
]
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
||||
export default {
|
||||
entry: 'devtools/index.js',
|
||||
external: ['preact'],
|
||||
format: 'umd',
|
||||
globals: {
|
||||
preact: 'preact'
|
||||
},
|
||||
moduleName: 'preactDevTools',
|
||||
plugins: [
|
||||
babel({
|
||||
sourceMap: true,
|
||||
loose: 'all',
|
||||
blacklist: ['es6.tailCall'],
|
||||
exclude: 'node_modules/**'
|
||||
})
|
||||
]
|
||||
}
|
23
thirdparty/preact/config/rollup.config.js
vendored
23
thirdparty/preact/config/rollup.config.js
vendored
@ -1,23 +0,0 @@
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import memory from 'rollup-plugin-memory';
|
||||
|
||||
export default {
|
||||
exports: 'named',
|
||||
useStrict: false,
|
||||
plugins: [
|
||||
memory({
|
||||
path: 'src/preact',
|
||||
contents: "export * from './preact';"
|
||||
}),
|
||||
nodeResolve({
|
||||
main: true
|
||||
}),
|
||||
babel({
|
||||
sourceMap: true,
|
||||
loose: 'all',
|
||||
blacklist: ['es6.tailCall'],
|
||||
exclude: 'node_modules/**'
|
||||
})
|
||||
]
|
||||
};
|
427
thirdparty/preact/devtools/devtools.js
vendored
427
thirdparty/preact/devtools/devtools.js
vendored
@ -1,427 +0,0 @@
|
||||
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
|
||||
|
||||
import { options, Component } from 'preact';
|
||||
|
||||
// Internal helpers from preact
|
||||
import { ATTR_KEY } from '../src/constants';
|
||||
import { isFunctionalComponent } from '../src/vdom/functional-component';
|
||||
|
||||
/**
|
||||
* Return a ReactElement-compatible object for the current state of a preact
|
||||
* component.
|
||||
*/
|
||||
function createReactElement(component) {
|
||||
return {
|
||||
type: component.constructor,
|
||||
key: component.key,
|
||||
ref: null, // Unsupported
|
||||
props: component.props
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ReactDOMComponent-compatible object for a given DOM node rendered
|
||||
* by preact.
|
||||
*
|
||||
* This implements the subset of the ReactDOMComponent interface that
|
||||
* React DevTools requires in order to display DOM nodes in the inspector with
|
||||
* the correct type and properties.
|
||||
*
|
||||
* @param {Node} node
|
||||
*/
|
||||
function createReactDOMComponent(node) {
|
||||
const childNodes = node.nodeType === Node.ELEMENT_NODE ?
|
||||
Array.from(node.childNodes) : [];
|
||||
|
||||
const isText = node.nodeType === Node.TEXT_NODE;
|
||||
|
||||
return {
|
||||
// --- ReactDOMComponent interface
|
||||
_currentElement: isText ? node.textContent : {
|
||||
type: node.nodeName.toLowerCase(),
|
||||
props: node[ATTR_KEY]
|
||||
},
|
||||
_renderedChildren: childNodes.map(child => {
|
||||
if (child._component) {
|
||||
return updateReactComponent(child._component);
|
||||
}
|
||||
return updateReactComponent(child);
|
||||
}),
|
||||
_stringText: isText ? node.textContent : null,
|
||||
|
||||
// --- Additional properties used by preact devtools
|
||||
|
||||
// A flag indicating whether the devtools have been notified about the
|
||||
// existence of this component instance yet.
|
||||
// This is used to send the appropriate notifications when DOM components
|
||||
// are added or updated between composite component updates.
|
||||
_inDevTools: false,
|
||||
node
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of a component created by a `ReactElement`-like object.
|
||||
*
|
||||
* @param {ReactElement} element
|
||||
*/
|
||||
function typeName(element) {
|
||||
if (typeof element.type === 'function') {
|
||||
return element.type.displayName || element.type.name;
|
||||
}
|
||||
return element.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ReactCompositeComponent-compatible object for a given preact
|
||||
* component instance.
|
||||
*
|
||||
* This implements the subset of the ReactCompositeComponent interface that
|
||||
* the DevTools requires in order to walk the component tree and inspect the
|
||||
* component's properties.
|
||||
*
|
||||
* See https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/getData.js
|
||||
*/
|
||||
function createReactCompositeComponent(component) {
|
||||
const _currentElement = createReactElement(component);
|
||||
const node = component.base;
|
||||
|
||||
let instance = {
|
||||
// --- ReactDOMComponent properties
|
||||
getName() {
|
||||
return typeName(_currentElement);
|
||||
},
|
||||
_currentElement: createReactElement(component),
|
||||
props: component.props,
|
||||
state: component.state,
|
||||
forceUpdate: component.forceUpdate.bind(component),
|
||||
setState: component.setState.bind(component),
|
||||
|
||||
// --- Additional properties used by preact devtools
|
||||
node
|
||||
};
|
||||
|
||||
// React DevTools exposes the `_instance` field of the selected item in the
|
||||
// component tree as `$r` in the console. `_instance` must refer to a
|
||||
// React Component (or compatible) class instance with `props` and `state`
|
||||
// fields and `setState()`, `forceUpdate()` methods.
|
||||
instance._instance = component;
|
||||
|
||||
// If the root node returned by this component instance's render function
|
||||
// was itself a composite component, there will be a `_component` property
|
||||
// containing the child component instance.
|
||||
if (component._component) {
|
||||
instance._renderedComponent = updateReactComponent(component._component);
|
||||
} else {
|
||||
// Otherwise, if the render() function returned an HTML/SVG element,
|
||||
// create a ReactDOMComponent-like object for the DOM node itself.
|
||||
instance._renderedComponent = updateReactComponent(node);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of Component|Node to ReactDOMComponent|ReactCompositeComponent-like
|
||||
* object.
|
||||
*
|
||||
* The same React*Component instance must be used when notifying devtools
|
||||
* about the initial mount of a component and subsequent updates.
|
||||
*/
|
||||
let instanceMap = new Map();
|
||||
|
||||
/**
|
||||
* Update (and create if necessary) the ReactDOMComponent|ReactCompositeComponent-like
|
||||
* instance for a given preact component instance or DOM Node.
|
||||
*
|
||||
* @param {Component|Node} componentOrNode
|
||||
*/
|
||||
function updateReactComponent(componentOrNode) {
|
||||
const newInstance = componentOrNode instanceof Node ?
|
||||
createReactDOMComponent(componentOrNode) :
|
||||
createReactCompositeComponent(componentOrNode);
|
||||
if (instanceMap.has(componentOrNode)) {
|
||||
let inst = instanceMap.get(componentOrNode);
|
||||
Object.assign(inst, newInstance);
|
||||
return inst;
|
||||
}
|
||||
instanceMap.set(componentOrNode, newInstance);
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
function nextRootKey(roots) {
|
||||
return '.' + Object.keys(roots).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all root component instances rendered by preact in `node`'s children
|
||||
* and add them to the `roots` map.
|
||||
*
|
||||
* @param {DOMElement} node
|
||||
* @param {[key: string] => ReactDOMComponent|ReactCompositeComponent}
|
||||
*/
|
||||
function findRoots(node, roots) {
|
||||
Array.from(node.childNodes).forEach(child => {
|
||||
if (child._component) {
|
||||
roots[nextRootKey(roots)] = updateReactComponent(child._component);
|
||||
} else {
|
||||
findRoots(child, roots);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of functional component name -> wrapper class.
|
||||
*/
|
||||
let functionalComponentWrappers = new Map();
|
||||
|
||||
/**
|
||||
* Wrap a functional component with a stateful component.
|
||||
*
|
||||
* preact does not record any information about the original hierarchy of
|
||||
* functional components in the rendered DOM nodes. Wrapping functional components
|
||||
* with a trivial wrapper allows us to recover information about the original
|
||||
* component structure from the DOM.
|
||||
*
|
||||
* @param {VNode} vnode
|
||||
*/
|
||||
function wrapFunctionalComponent(vnode) {
|
||||
const originalRender = vnode.nodeName;
|
||||
const name = vnode.nodeName.name || '(Function.name missing)';
|
||||
const wrappers = functionalComponentWrappers;
|
||||
if (!wrappers.has(originalRender)) {
|
||||
let wrapper = class extends Component {
|
||||
render(props, state, context) {
|
||||
return originalRender(props, context);
|
||||
}
|
||||
};
|
||||
|
||||
// Expose the original component name. React Dev Tools will use
|
||||
// this property if it exists or fall back to Function.name
|
||||
// otherwise.
|
||||
wrapper.displayName = name;
|
||||
|
||||
wrappers.set(originalRender, wrapper);
|
||||
}
|
||||
vnode.nodeName = wrappers.get(originalRender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bridge for exposing preact's component tree to React DevTools.
|
||||
*
|
||||
* It creates implementations of the interfaces that ReactDOM passes to
|
||||
* devtools to enable it to query the component tree and hook into component
|
||||
* updates.
|
||||
*
|
||||
* See https://github.com/facebook/react/blob/59ff7749eda0cd858d5ee568315bcba1be75a1ca/src/renderers/dom/ReactDOM.js
|
||||
* for how ReactDOM exports its internals for use by the devtools and
|
||||
* the `attachRenderer()` function in
|
||||
* https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/attachRenderer.js
|
||||
* for how the devtools consumes the resulting objects.
|
||||
*/
|
||||
function createDevToolsBridge() {
|
||||
// The devtools has different paths for interacting with the renderers from
|
||||
// React Native, legacy React DOM and current React DOM.
|
||||
//
|
||||
// Here we emulate the interface for the current React DOM (v15+) lib.
|
||||
|
||||
// ReactDOMComponentTree-like object
|
||||
const ComponentTree = {
|
||||
getNodeFromInstance(instance) {
|
||||
return instance.node;
|
||||
},
|
||||
getClosestInstanceFromNode(node) {
|
||||
while (node && !node._component) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
return node ? updateReactComponent(node._component) : null;
|
||||
}
|
||||
};
|
||||
|
||||
// Map of root ID (the ID is unimportant) to component instance.
|
||||
let roots = {};
|
||||
findRoots(document.body, roots);
|
||||
|
||||
// ReactMount-like object
|
||||
//
|
||||
// Used by devtools to discover the list of root component instances and get
|
||||
// notified when new root components are rendered.
|
||||
const Mount = {
|
||||
_instancesByReactRootID: roots,
|
||||
|
||||
// Stub - React DevTools expects to find this method and replace it
|
||||
// with a wrapper in order to observe new root components being added
|
||||
_renderNewRootComponent(/* instance, ... */) { }
|
||||
};
|
||||
|
||||
// ReactReconciler-like object
|
||||
const Reconciler = {
|
||||
// Stubs - React DevTools expects to find these methods and replace them
|
||||
// with wrappers in order to observe components being mounted, updated and
|
||||
// unmounted
|
||||
mountComponent(/* instance, ... */) { },
|
||||
performUpdateIfNecessary(/* instance, ... */) { },
|
||||
receiveComponent(/* instance, ... */) { },
|
||||
unmountComponent(/* instance, ... */) { }
|
||||
};
|
||||
|
||||
/** Notify devtools that a new component instance has been mounted into the DOM. */
|
||||
const componentAdded = component => {
|
||||
const instance = updateReactComponent(component);
|
||||
if (isRootComponent(component)) {
|
||||
instance._rootID = nextRootKey(roots);
|
||||
roots[instance._rootID] = instance;
|
||||
Mount._renderNewRootComponent(instance);
|
||||
}
|
||||
visitNonCompositeChildren(instance, childInst => {
|
||||
childInst._inDevTools = true;
|
||||
Reconciler.mountComponent(childInst);
|
||||
});
|
||||
Reconciler.mountComponent(instance);
|
||||
};
|
||||
|
||||
/** Notify devtools that a component has been updated with new props/state. */
|
||||
const componentUpdated = component => {
|
||||
const prevRenderedChildren = [];
|
||||
visitNonCompositeChildren(instanceMap.get(component), childInst => {
|
||||
prevRenderedChildren.push(childInst);
|
||||
});
|
||||
|
||||
// Notify devtools about updates to this component and any non-composite
|
||||
// children
|
||||
const instance = updateReactComponent(component);
|
||||
Reconciler.receiveComponent(instance);
|
||||
visitNonCompositeChildren(instance, childInst => {
|
||||
if (!childInst._inDevTools) {
|
||||
// New DOM child component
|
||||
childInst._inDevTools = true;
|
||||
Reconciler.mountComponent(childInst);
|
||||
} else {
|
||||
// Updated DOM child component
|
||||
Reconciler.receiveComponent(childInst);
|
||||
}
|
||||
});
|
||||
|
||||
// For any non-composite children that were removed by the latest render,
|
||||
// remove the corresponding ReactDOMComponent-like instances and notify
|
||||
// the devtools
|
||||
prevRenderedChildren.forEach(childInst => {
|
||||
if (!document.body.contains(childInst.node)) {
|
||||
instanceMap.delete(childInst.node);
|
||||
Reconciler.unmountComponent(childInst);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** Notify devtools that a component has been unmounted from the DOM. */
|
||||
const componentRemoved = component => {
|
||||
const instance = updateReactComponent(component);
|
||||
visitNonCompositeChildren(childInst => {
|
||||
instanceMap.delete(childInst.node);
|
||||
Reconciler.unmountComponent(childInst);
|
||||
});
|
||||
Reconciler.unmountComponent(instance);
|
||||
instanceMap.delete(component);
|
||||
if (instance._rootID) {
|
||||
delete roots[instance._rootID];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
componentAdded,
|
||||
componentUpdated,
|
||||
componentRemoved,
|
||||
|
||||
// Interfaces passed to devtools via __REACT_DEVTOOLS_GLOBAL_HOOK__.inject()
|
||||
ComponentTree,
|
||||
Mount,
|
||||
Reconciler
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if a preact component is a top level component rendered by
|
||||
* `render()` into a container Element.
|
||||
*/
|
||||
function isRootComponent(component) {
|
||||
return !component.base.parentElement || !component.base.parentElement[ATTR_KEY];
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all child instances of a ReactCompositeComponent-like object that are
|
||||
* not composite components (ie. they represent DOM elements or text)
|
||||
*
|
||||
* @param {Component} component
|
||||
* @param {(Component) => void} visitor
|
||||
*/
|
||||
function visitNonCompositeChildren(component, visitor) {
|
||||
if (component._renderedComponent) {
|
||||
if (!component._renderedComponent._component) {
|
||||
visitor(component._renderedComponent);
|
||||
visitNonCompositeChildren(component._renderedComponent, visitor);
|
||||
}
|
||||
} else if (component._renderedChildren) {
|
||||
component._renderedChildren.forEach(child => {
|
||||
visitor(child);
|
||||
if (!child._component) visitNonCompositeChildren(child, visitor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bridge between the preact component tree and React's dev tools
|
||||
* and register it.
|
||||
*
|
||||
* After this function is called, the React Dev Tools should be able to detect
|
||||
* "React" on the page and show the component tree.
|
||||
*
|
||||
* This function hooks into preact VNode creation in order to expose functional
|
||||
* components correctly, so it should be called before the root component(s)
|
||||
* are rendered.
|
||||
*
|
||||
* Returns a cleanup function which unregisters the hooks.
|
||||
*/
|
||||
export function initDevTools() {
|
||||
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
||||
// React DevTools are not installed
|
||||
return;
|
||||
}
|
||||
|
||||
// Hook into preact element creation in order to wrap functional components
|
||||
// with stateful ones in order to make them visible in the devtools
|
||||
const nextVNode = options.vnode;
|
||||
options.vnode = (vnode) => {
|
||||
if (isFunctionalComponent(vnode)) wrapFunctionalComponent(vnode);
|
||||
if (nextVNode) return nextVNode(vnode);
|
||||
};
|
||||
|
||||
// Notify devtools when preact components are mounted, updated or unmounted
|
||||
const bridge = createDevToolsBridge();
|
||||
|
||||
const nextAfterMount = options.afterMount;
|
||||
options.afterMount = component => {
|
||||
bridge.componentAdded(component);
|
||||
if (nextAfterMount) nextAfterMount(component);
|
||||
};
|
||||
|
||||
const nextAfterUpdate = options.afterUpdate;
|
||||
options.afterUpdate = component => {
|
||||
bridge.componentUpdated(component);
|
||||
if (nextAfterUpdate) nextAfterUpdate(component);
|
||||
};
|
||||
|
||||
const nextBeforeUnmount = options.beforeUnmount;
|
||||
options.beforeUnmount = component => {
|
||||
bridge.componentRemoved(component);
|
||||
if (nextBeforeUnmount) nextBeforeUnmount(component);
|
||||
};
|
||||
|
||||
// Notify devtools about this instance of "React"
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(bridge);
|
||||
|
||||
return () => {
|
||||
options.afterMount = nextAfterMount;
|
||||
options.afterUpdate = nextAfterUpdate;
|
||||
options.beforeUnmount = nextBeforeUnmount;
|
||||
};
|
||||
}
|
4
thirdparty/preact/devtools/index.js
vendored
4
thirdparty/preact/devtools/index.js
vendored
@ -1,4 +0,0 @@
|
||||
import { initDevTools } from './devtools';
|
||||
|
||||
initDevTools();
|
||||
|
111
thirdparty/preact/package.json
vendored
111
thirdparty/preact/package.json
vendored
@ -1,111 +0,0 @@
|
||||
{
|
||||
"name": "preact",
|
||||
"amdName": "preact",
|
||||
"version": "6.4.0",
|
||||
"description": "Tiny & fast Component-based virtual DOM framework.",
|
||||
"main": "dist/preact.js",
|
||||
"jsnext:main": "src/preact.js",
|
||||
"aliases:main": "aliases.js",
|
||||
"dev:main": "dist/preact.dev.js",
|
||||
"minified:main": "dist/preact.min.js",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/ aliases.js aliases.js.map devtools.js devtools.js.map",
|
||||
"copy-flow-definition": "copyfiles src/preact.js.flow dist/preact.js.flow",
|
||||
"copy-typescript-definition": "copyfiles src/preact.d.ts dist/preact.d.ts",
|
||||
"build": "npm-run-all --silent clean transpile copy-flow-definition copy-typescript-definition strip optimize minify size",
|
||||
"transpile:main": "rollup -c config/rollup.config.js -m dist/preact.dev.js.map -f umd -n preact src/preact.js -o dist/preact.dev.js",
|
||||
"transpile:devtools": "rollup -c config/rollup.config.devtools.js -o devtools.js -m devtools.js.map",
|
||||
"transpile:aliases": "rollup -c config/rollup.config.aliases.js -m aliases.js.map -f umd -n preact src/preact.js -o aliases.js",
|
||||
"transpile": "npm-run-all transpile:main transpile:aliases transpile:devtools",
|
||||
"optimize": "uglifyjs dist/preact.dev.js -c conditionals=false,sequences=false,loops=false,join_vars=false,collapse_vars=false --pure-funcs=Object.defineProperty -b width=120,quote_style=3 -o dist/preact.js -p relative --in-source-map dist/preact.dev.js.map --source-map dist/preact.js.map",
|
||||
"minify": "uglifyjs dist/preact.js -c collapse_vars,evaluate,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code -m -o dist/preact.min.js -p relative --in-source-map dist/preact.js.map --source-map dist/preact.min.js.map",
|
||||
"strip": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.dev.js",
|
||||
"size": "node -e \"process.stdout.write('gzip size: ')\" && gzip-size dist/preact.min.js",
|
||||
"test": "npm-run-all lint --parallel test:mocha test:karma",
|
||||
"test:mocha": "mocha --recursive --compilers js:babel/register test/shared test/node",
|
||||
"test:karma": "karma start test/karma.conf.js --single-run",
|
||||
"test:mocha:watch": "npm run test:mocha -- --watch",
|
||||
"test:karma:watch": "npm run test:karma -- no-single-run",
|
||||
"lint": "eslint devtools src test",
|
||||
"prepublish": "npm run build",
|
||||
"smart-release": "npm run build && npm test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish",
|
||||
"release": "cross-env npm run smart-release"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "./config/eslint-config.js"
|
||||
},
|
||||
"typings": "./src/preact.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/developit/preact.git"
|
||||
},
|
||||
"files": [
|
||||
"devtools",
|
||||
"src",
|
||||
"dist",
|
||||
"aliases.js",
|
||||
"aliases.js.map",
|
||||
"devtools.js",
|
||||
"devtools.js.map",
|
||||
"typings.json"
|
||||
],
|
||||
"author": "Jason Miller <jason@developit.ca>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/developit/preact/issues"
|
||||
},
|
||||
"homepage": "https://github.com/developit/preact",
|
||||
"devDependencies": {
|
||||
"babel": "^5.8.23",
|
||||
"babel-core": "^5.8.24",
|
||||
"babel-eslint": "^6.1.0",
|
||||
"babel-loader": "^5.3.2",
|
||||
"babel-runtime": "^5.8.24",
|
||||
"chai": "^3.4.1",
|
||||
"copyfiles": "^1.0.0",
|
||||
"core-js": "^2.4.1",
|
||||
"cross-env": "^3.1.3",
|
||||
"diff": "^3.0.0",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-plugin-react": "^6.0.0",
|
||||
"gzip-size-cli": "^1.0.0",
|
||||
"isparta-loader": "^2.0.0",
|
||||
"jscodeshift": "^0.3.25",
|
||||
"karma": "^1.1.0",
|
||||
"karma-babel-preprocessor": "^5.2.2",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chai-sinon": "^0.1.5",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.0.4",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-sauce-launcher": "^1.1.0",
|
||||
"karma-source-map-support": "^1.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.6",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"mocha": "^3.0.1",
|
||||
"npm-run-all": "^3.0.0",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"rimraf": "^2.5.3",
|
||||
"rollup": "^0.34.1",
|
||||
"rollup-plugin-babel": "^1.0.0",
|
||||
"rollup-plugin-memory": "^2.0.0",
|
||||
"rollup-plugin-node-resolve": "^2.0.0",
|
||||
"sinon": "^1.17.4",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"uglify-js": "^2.7.0",
|
||||
"webpack": "^1.13.1"
|
||||
},
|
||||
"greenkeeper": {
|
||||
"ignore": [
|
||||
"rollup-plugin-babel",
|
||||
"babel",
|
||||
"babel-core",
|
||||
"babel-eslint",
|
||||
"babel-loader",
|
||||
"babel-runtime",
|
||||
"jscodeshift"
|
||||
]
|
||||
}
|
||||
}
|
10
thirdparty/preact/src/clone-element.js
vendored
10
thirdparty/preact/src/clone-element.js
vendored
@ -1,10 +0,0 @@
|
||||
import { clone, extend } from './util';
|
||||
import { h } from './h';
|
||||
|
||||
export function cloneElement(vnode, props) {
|
||||
return h(
|
||||
vnode.nodeName,
|
||||
extend(clone(vnode.attributes), props),
|
||||
arguments.length>2 ? [].slice.call(arguments, 2) : vnode.children
|
||||
);
|
||||
}
|
102
thirdparty/preact/src/component.js
vendored
102
thirdparty/preact/src/component.js
vendored
@ -1,102 +0,0 @@
|
||||
import { FORCE_RENDER } from './constants';
|
||||
import { extend, clone, isFunction } from './util';
|
||||
import { createLinkedState } from './linked-state';
|
||||
import { renderComponent } from './vdom/component';
|
||||
import { enqueueRender } from './render-queue';
|
||||
|
||||
/** Base Component class, for he ES6 Class method of creating Components
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* class MyFoo extends Component {
|
||||
* render(props, state) {
|
||||
* return <div />;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export function Component(props, context) {
|
||||
/** @private */
|
||||
this._dirty = true;
|
||||
// /** @public */
|
||||
// this._disableRendering = false;
|
||||
// /** @public */
|
||||
// this.prevState = this.prevProps = this.prevContext = this.base = this.nextBase = this._parentComponent = this._component = this.__ref = this.__key = this._linkedStates = this._renderCallbacks = null;
|
||||
/** @public */
|
||||
this.context = context;
|
||||
/** @type {object} */
|
||||
this.props = props;
|
||||
/** @type {object} */
|
||||
if (!this.state) this.state = {};
|
||||
}
|
||||
|
||||
|
||||
extend(Component.prototype, {
|
||||
|
||||
/** Returns a `boolean` value indicating if the component should re-render when receiving the given `props` and `state`.
|
||||
* @param {object} nextProps
|
||||
* @param {object} nextState
|
||||
* @param {object} nextContext
|
||||
* @returns {Boolean} should the component re-render
|
||||
* @name shouldComponentUpdate
|
||||
* @function
|
||||
*/
|
||||
// shouldComponentUpdate() {
|
||||
// return true;
|
||||
// },
|
||||
|
||||
|
||||
/** Returns a function that sets a state property when called.
|
||||
* Calling linkState() repeatedly with the same arguments returns a cached link function.
|
||||
*
|
||||
* Provides some built-in special cases:
|
||||
* - Checkboxes and radio buttons link their boolean `checked` value
|
||||
* - Inputs automatically link their `value` property
|
||||
* - Event paths fall back to any associated Component if not found on an element
|
||||
* - If linked value is a function, will invoke it and use the result
|
||||
*
|
||||
* @param {string} key The path to set - can be a dot-notated deep key
|
||||
* @param {string} [eventPath] If set, attempts to find the new state value at a given dot-notated path within the object passed to the linkedState setter.
|
||||
* @returns {function} linkStateSetter(e)
|
||||
*
|
||||
* @example Update a "text" state value when an input changes:
|
||||
* <input onChange={ this.linkState('text') } />
|
||||
*
|
||||
* @example Set a deep state value on click
|
||||
* <button onClick={ this.linkState('touch.coords', 'touches.0') }>Tap</button
|
||||
*/
|
||||
linkState(key, eventPath) {
|
||||
let c = this._linkedStates || (this._linkedStates = {});
|
||||
return c[key+eventPath] || (c[key+eventPath] = createLinkedState(this, key, eventPath));
|
||||
},
|
||||
|
||||
|
||||
/** Update component state by copying properties from `state` to `this.state`.
|
||||
* @param {object} state A hash of state properties to update with new values
|
||||
*/
|
||||
setState(state, callback) {
|
||||
let s = this.state;
|
||||
if (!this.prevState) this.prevState = clone(s);
|
||||
extend(s, isFunction(state) ? state(s, this.props) : state);
|
||||
if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
|
||||
enqueueRender(this);
|
||||
},
|
||||
|
||||
|
||||
/** Immediately perform a synchronous re-render of the component.
|
||||
* @private
|
||||
*/
|
||||
forceUpdate() {
|
||||
renderComponent(this, FORCE_RENDER);
|
||||
},
|
||||
|
||||
|
||||
/** Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
|
||||
* Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
|
||||
* @param {object} props Props (eg: JSX attributes) received from parent element/component
|
||||
* @param {object} state The component's current state
|
||||
* @param {object} context Context object (if a parent component has provided context)
|
||||
* @returns VNode
|
||||
*/
|
||||
render() {}
|
||||
|
||||
});
|
20
thirdparty/preact/src/constants.js
vendored
20
thirdparty/preact/src/constants.js
vendored
@ -1,20 +0,0 @@
|
||||
// render modes
|
||||
|
||||
export const NO_RENDER = 0;
|
||||
export const SYNC_RENDER = 1;
|
||||
export const FORCE_RENDER = 2;
|
||||
export const ASYNC_RENDER = 3;
|
||||
|
||||
export const EMPTY = {};
|
||||
|
||||
export const ATTR_KEY = typeof Symbol!=='undefined' ? Symbol.for('preactattr') : '__preactattr_';
|
||||
|
||||
// DOM properties that should NOT have "px" added when numeric
|
||||
export const NON_DIMENSION_PROPS = {
|
||||
boxFlex:1, boxFlexGroup:1, columnCount:1, fillOpacity:1, flex:1, flexGrow:1,
|
||||
flexPositive:1, flexShrink:1, flexNegative:1, fontWeight:1, lineClamp:1, lineHeight:1,
|
||||
opacity:1, order:1, orphans:1, strokeOpacity:1, widows:1, zIndex:1, zoom:1
|
||||
};
|
||||
|
||||
// DOM event types that do not bubble and should be attached via useCapture
|
||||
export const NON_BUBBLING_EVENTS = { blur:1, error:1, focus:1, load:1, resize:1, scroll:1 };
|
99
thirdparty/preact/src/dom/index.js
vendored
99
thirdparty/preact/src/dom/index.js
vendored
@ -1,99 +0,0 @@
|
||||
import { NON_DIMENSION_PROPS, NON_BUBBLING_EVENTS } from '../constants';
|
||||
import options from '../options';
|
||||
import { toLowerCase, isString, isFunction, hashToClassName } from '../util';
|
||||
|
||||
|
||||
|
||||
|
||||
/** Removes a given DOM Node from its parent. */
|
||||
export function removeNode(node) {
|
||||
let p = node.parentNode;
|
||||
if (p) p.removeChild(node);
|
||||
}
|
||||
|
||||
|
||||
/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
|
||||
* If `value` is `null`, the attribute/handler will be removed.
|
||||
* @param {Element} node An element to mutate
|
||||
* @param {string} name The name/key to set, such as an event or attribute name
|
||||
* @param {any} value An attribute value, such as a function to be used as an event handler
|
||||
* @param {any} previousValue The last value that was set for this name/node pair
|
||||
* @private
|
||||
*/
|
||||
export function setAccessor(node, name, old, value, isSvg) {
|
||||
|
||||
if (name==='className') name = 'class';
|
||||
|
||||
if (name==='class' && value && typeof value==='object') {
|
||||
value = hashToClassName(value);
|
||||
}
|
||||
|
||||
if (name==='key') {
|
||||
// ignore
|
||||
}
|
||||
else if (name==='class' && !isSvg) {
|
||||
node.className = value || '';
|
||||
}
|
||||
else if (name==='style') {
|
||||
if (!value || isString(value) || isString(old)) {
|
||||
node.style.cssText = value || '';
|
||||
}
|
||||
if (value && typeof value==='object') {
|
||||
if (!isString(old)) {
|
||||
for (let i in old) if (!(i in value)) node.style[i] = '';
|
||||
}
|
||||
for (let i in value) {
|
||||
node.style[i] = typeof value[i]==='number' && !NON_DIMENSION_PROPS[i] ? (value[i]+'px') : value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (name==='dangerouslySetInnerHTML') {
|
||||
if (value) node.innerHTML = value.__html;
|
||||
}
|
||||
else if (name[0]=='o' && name[1]=='n') {
|
||||
let l = node._listeners || (node._listeners = {});
|
||||
name = toLowerCase(name.substring(2));
|
||||
// @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
|
||||
// if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
|
||||
if (value) {
|
||||
if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
|
||||
}
|
||||
else if (l[name]) {
|
||||
node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
|
||||
}
|
||||
l[name] = value;
|
||||
}
|
||||
else if (name!=='list' && name!=='type' && !isSvg && name in node) {
|
||||
setProperty(node, name, value==null ? '' : value);
|
||||
if (value==null || value===false) node.removeAttribute(name);
|
||||
}
|
||||
else {
|
||||
let ns = isSvg && name.match(/^xlink\:?(.+)/);
|
||||
if (value==null || value===false) {
|
||||
if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));
|
||||
else node.removeAttribute(name);
|
||||
}
|
||||
else if (typeof value!=='object' && !isFunction(value)) {
|
||||
if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value);
|
||||
else node.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Attempt to set a DOM property to the given value.
|
||||
* IE & FF throw for certain property-value combinations.
|
||||
*/
|
||||
function setProperty(node, name, value) {
|
||||
try {
|
||||
node[name] = value;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
|
||||
/** Proxy an event to hooked event handlers
|
||||
* @private
|
||||
*/
|
||||
function eventProxy(e) {
|
||||
return this._listeners[e.type](options.event && options.event(e) || e);
|
||||
}
|
25
thirdparty/preact/src/dom/recycler.js
vendored
25
thirdparty/preact/src/dom/recycler.js
vendored
@ -1,25 +0,0 @@
|
||||
import { toLowerCase } from '../util';
|
||||
import { removeNode } from './index';
|
||||
|
||||
/** DOM node pool, keyed on nodeName. */
|
||||
|
||||
const nodes = {};
|
||||
|
||||
export function collectNode(node) {
|
||||
removeNode(node);
|
||||
|
||||
if (node instanceof Element) {
|
||||
node._component = node._componentConstructor = null;
|
||||
|
||||
let name = node.normalizedNodeName || toLowerCase(node.nodeName);
|
||||
(nodes[name] || (nodes[name] = [])).push(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function createNode(nodeName, isSvg) {
|
||||
let name = toLowerCase(nodeName),
|
||||
node = nodes[name] && nodes[name].pop() || (isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName));
|
||||
node.normalizedNodeName = name;
|
||||
return node;
|
||||
}
|
50
thirdparty/preact/src/h.js
vendored
50
thirdparty/preact/src/h.js
vendored
@ -1,50 +0,0 @@
|
||||
import { VNode } from './vnode';
|
||||
import options from './options';
|
||||
|
||||
|
||||
const stack = [];
|
||||
|
||||
|
||||
/** JSX/hyperscript reviver
|
||||
* Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0
|
||||
* @see http://jasonformat.com/wtf-is-jsx
|
||||
* @public
|
||||
* @example
|
||||
* /** @jsx h *\/
|
||||
* import { render, h } from 'preact';
|
||||
* render(<span>foo</span>, document.body);
|
||||
*/
|
||||
export function h(nodeName, attributes) {
|
||||
let children = [],
|
||||
lastSimple, child, simple, i;
|
||||
for (i=arguments.length; i-- > 2; ) {
|
||||
stack.push(arguments[i]);
|
||||
}
|
||||
if (attributes && attributes.children) {
|
||||
if (!stack.length) stack.push(attributes.children);
|
||||
delete attributes.children;
|
||||
}
|
||||
while (stack.length) {
|
||||
if ((child = stack.pop()) instanceof Array) {
|
||||
for (i=child.length; i--; ) stack.push(child[i]);
|
||||
}
|
||||
else if (child!=null && child!==false) {
|
||||
if (typeof child=='number' || child===true) child = String(child);
|
||||
simple = typeof child=='string';
|
||||
if (simple && lastSimple) {
|
||||
children[children.length-1] += child;
|
||||
}
|
||||
else {
|
||||
children.push(child);
|
||||
lastSimple = simple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let p = new VNode(nodeName, attributes || undefined, children);
|
||||
|
||||
// if a "vnode hook" is defined, pass every created VNode to it
|
||||
if (options.vnode) options.vnode(p);
|
||||
|
||||
return p;
|
||||
}
|
24
thirdparty/preact/src/linked-state.js
vendored
24
thirdparty/preact/src/linked-state.js
vendored
@ -1,24 +0,0 @@
|
||||
import { isString, delve } from './util';
|
||||
|
||||
/** Create an Event handler function that sets a given state property.
|
||||
* @param {Component} component The component whose state should be updated
|
||||
* @param {string} key A dot-notated key path to update in the component's state
|
||||
* @param {string} eventPath A dot-notated key path to the value that should be retrieved from the Event or component
|
||||
* @returns {function} linkedStateHandler
|
||||
* @private
|
||||
*/
|
||||
export function createLinkedState(component, key, eventPath) {
|
||||
let path = key.split('.');
|
||||
return function(e) {
|
||||
let t = e && e.target || this,
|
||||
state = {},
|
||||
obj = state,
|
||||
v = isString(eventPath) ? delve(e, eventPath) : t.nodeName ? (t.type.match(/^che|rad/) ? t.checked : t.value) : e,
|
||||
i = 0;
|
||||
for ( ; i<path.length-1; i++) {
|
||||
obj = obj[path[i]] || (obj[path[i]] = !i && component.state[path[i]] || {});
|
||||
}
|
||||
obj[path[i]] = v;
|
||||
component.setState(state);
|
||||
};
|
||||
}
|
27
thirdparty/preact/src/options.js
vendored
27
thirdparty/preact/src/options.js
vendored
@ -1,27 +0,0 @@
|
||||
/** Global options
|
||||
* @public
|
||||
* @namespace options {Object}
|
||||
*/
|
||||
export default {
|
||||
|
||||
/** If `true`, `prop` changes trigger synchronous component updates.
|
||||
* @name syncComponentUpdates
|
||||
* @type Boolean
|
||||
* @default true
|
||||
*/
|
||||
//syncComponentUpdates: true,
|
||||
|
||||
/** Processes all created VNodes.
|
||||
* @param {VNode} vnode A newly-created VNode to normalize/process
|
||||
*/
|
||||
//vnode(vnode) { }
|
||||
|
||||
/** Hook invoked after a component is mounted. */
|
||||
// afterMount(component) { }
|
||||
|
||||
/** Hook invoked after the DOM is updated with a component's latest render. */
|
||||
// afterUpdate(component) { }
|
||||
|
||||
/** Hook invoked immediately before a component is unmounted. */
|
||||
// beforeUnmount(component) { }
|
||||
};
|
565
thirdparty/preact/src/preact.d.ts
vendored
565
thirdparty/preact/src/preact.d.ts
vendored
@ -1,565 +0,0 @@
|
||||
declare namespace preact {
|
||||
interface ComponentProps {
|
||||
children?:JSX.Element[];
|
||||
key?:string;
|
||||
}
|
||||
|
||||
interface DangerouslySetInnerHTML {
|
||||
__html: string;
|
||||
}
|
||||
|
||||
interface PreactHTMLAttributes {
|
||||
dangerouslySetInnerHTML?:DangerouslySetInnerHTML;
|
||||
key?:string;
|
||||
ref?:(el?: Element) => void;
|
||||
}
|
||||
|
||||
interface VNode {
|
||||
nodeName:ComponentConstructor<any, any>|string;
|
||||
attributes:{[name:string]:any};
|
||||
children:VNode[];
|
||||
key:string;
|
||||
}
|
||||
|
||||
interface ComponentLifecycle<PropsType, StateType> {
|
||||
componentWillMount?():void;
|
||||
|
||||
componentDidMount?():void;
|
||||
|
||||
componentWillUnmount?():void;
|
||||
|
||||
componentDidUnmount?():void;
|
||||
|
||||
componentWillReceiveProps?(props:PropsType):void;
|
||||
|
||||
shouldComponentUpdate?(props:PropsType):boolean;
|
||||
|
||||
componentWillUpdate?():void;
|
||||
|
||||
componentDidUpdate?():void;
|
||||
}
|
||||
|
||||
interface ComponentConstructor<PropsType, StateType> {
|
||||
new (props?:PropsType):Component<PropsType, StateType>;
|
||||
}
|
||||
|
||||
abstract class Component<PropsType, StateType> implements ComponentLifecycle<PropsType, StateType> {
|
||||
constructor(props?:PropsType);
|
||||
|
||||
state:StateType;
|
||||
props:PropsType & ComponentProps;
|
||||
base:HTMLElement;
|
||||
|
||||
linkState:(name:string) => void;
|
||||
|
||||
setState(state:StateType, opts?:any):void;
|
||||
|
||||
abstract render(props:PropsType & ComponentProps, state:any):JSX.Element;
|
||||
}
|
||||
|
||||
function h<PropsType>(node:ComponentConstructor<PropsType, any>, params:PropsType, ...children:(JSX.Element|JSX.Element[]|string)[]):JSX.Element;
|
||||
function h(node:string, params:JSX.HTMLAttributes&JSX.SVGAttributes&{[propName: string]: any}, ...children:(JSX.Element|JSX.Element[]|string)[]):JSX.Element;
|
||||
|
||||
function render(node:JSX.Element, parent:Element, merge?:boolean):Element;
|
||||
|
||||
function rerender():void;
|
||||
|
||||
function cloneElement(element:JSX.Element, props:any):JSX.Element;
|
||||
|
||||
var options:{
|
||||
syncComponentUpdates?:boolean;
|
||||
debounceRendering?:(render:() => void) => void;
|
||||
vnode?:(vnode:VNode) => void;
|
||||
event?:(event:Event) => Event;
|
||||
};
|
||||
}
|
||||
|
||||
declare module "preact" {
|
||||
export = preact;
|
||||
}
|
||||
|
||||
declare module "preact/devtools" {
|
||||
// Empty. This module initializes the React Developer Tools integration
|
||||
// when imported.
|
||||
}
|
||||
|
||||
declare namespace JSX {
|
||||
interface Element extends preact.VNode {
|
||||
|
||||
}
|
||||
|
||||
interface ElementClass extends preact.Component<any, any> {
|
||||
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props:any;
|
||||
}
|
||||
|
||||
interface SVGAttributes {
|
||||
clipPath?:string;
|
||||
cx?:number | string;
|
||||
cy?:number | string;
|
||||
d?:string;
|
||||
dx?:number | string;
|
||||
dy?:number | string;
|
||||
fill?:string;
|
||||
fillOpacity?:number | string;
|
||||
fontFamily?:string;
|
||||
fontSize?:number | string;
|
||||
fx?:number | string;
|
||||
fy?:number | string;
|
||||
gradientTransform?:string;
|
||||
gradientUnits?:string;
|
||||
markerEnd?:string;
|
||||
markerMid?:string;
|
||||
markerStart?:string;
|
||||
offset?:number | string;
|
||||
opacity?:number | string;
|
||||
patternContentUnits?:string;
|
||||
patternUnits?:string;
|
||||
points?:string;
|
||||
preserveAspectRatio?:string;
|
||||
r?:number | string;
|
||||
rx?:number | string;
|
||||
ry?:number | string;
|
||||
spreadMethod?:string;
|
||||
stopColor?:string;
|
||||
stopOpacity?:number | string;
|
||||
stroke?:string;
|
||||
strokeDasharray?:string;
|
||||
strokeLinecap?:string;
|
||||
strokeMiterlimit?:string;
|
||||
strokeOpacity?:number | string;
|
||||
strokeWidth?:number | string;
|
||||
textAnchor?:string;
|
||||
transform?:string;
|
||||
version?:string;
|
||||
viewBox?:string;
|
||||
x1?:number | string;
|
||||
x2?:number | string;
|
||||
x?:number | string;
|
||||
xlinkActuate?:string;
|
||||
xlinkArcrole?:string;
|
||||
xlinkHref?:string;
|
||||
xlinkRole?:string;
|
||||
xlinkShow?:string;
|
||||
xlinkTitle?:string;
|
||||
xlinkType?:string;
|
||||
xmlBase?:string;
|
||||
xmlLang?:string;
|
||||
xmlSpace?:string;
|
||||
y1?:number | string;
|
||||
y2?:number | string;
|
||||
y?:number | string;
|
||||
}
|
||||
|
||||
interface PathAttributes {
|
||||
d:string;
|
||||
}
|
||||
|
||||
interface EventHandler<E extends Event> {
|
||||
(event:E):void;
|
||||
}
|
||||
|
||||
type ClipboardEventHandler = EventHandler<ClipboardEvent>;
|
||||
type CompositionEventHandler = EventHandler<CompositionEvent>;
|
||||
type DragEventHandler = EventHandler<DragEvent>;
|
||||
type FocusEventHandler = EventHandler<FocusEvent>;
|
||||
type KeyboardEventHandler = EventHandler<KeyboardEvent>;
|
||||
type MouseEventHandler = EventHandler<MouseEvent>;
|
||||
type TouchEventHandler = EventHandler<TouchEvent>;
|
||||
type UIEventHandler = EventHandler<UIEvent>;
|
||||
type WheelEventHandler = EventHandler<WheelEvent>;
|
||||
type AnimationEventHandler = EventHandler<AnimationEvent>;
|
||||
type TransitionEventHandler = EventHandler<TransitionEvent>;
|
||||
|
||||
type GenericEventHandler = EventHandler<Event>;
|
||||
|
||||
interface DOMAttributed {
|
||||
// Clipboard Events
|
||||
onCopy?:ClipboardEventHandler;
|
||||
onCut?:ClipboardEventHandler;
|
||||
onPaste?:ClipboardEventHandler;
|
||||
|
||||
// Composition Events
|
||||
onCompositionEnd?:CompositionEventHandler;
|
||||
onCompositionStart?:CompositionEventHandler;
|
||||
onCompositionUpdate?:CompositionEventHandler;
|
||||
|
||||
// Focus Events
|
||||
onFocus?:FocusEventHandler;
|
||||
onBlur?:FocusEventHandler;
|
||||
|
||||
// Form Events
|
||||
onChange?:GenericEventHandler;
|
||||
onInput?:GenericEventHandler;
|
||||
onSubmit?:GenericEventHandler;
|
||||
|
||||
// Keyboard Events
|
||||
onKeyDown?:KeyboardEventHandler;
|
||||
onKeyPress?:KeyboardEventHandler;
|
||||
onKeyUp?:KeyboardEventHandler;
|
||||
|
||||
// Media Events
|
||||
onAbort?:GenericEventHandler;
|
||||
onCanPlay?:GenericEventHandler;
|
||||
onCanPlayThrough?:GenericEventHandler;
|
||||
onDurationChange?:GenericEventHandler;
|
||||
onEmptied?:GenericEventHandler;
|
||||
onEncrypted?:GenericEventHandler;
|
||||
onEnded?:GenericEventHandler;
|
||||
onLoadedData?:GenericEventHandler;
|
||||
onLoadedMetadata?:GenericEventHandler;
|
||||
onLoadStart?:GenericEventHandler;
|
||||
onPause?:GenericEventHandler;
|
||||
onPlay?:GenericEventHandler;
|
||||
onPlaying?:GenericEventHandler;
|
||||
onProgress?:GenericEventHandler;
|
||||
onRateChange?:GenericEventHandler;
|
||||
onSeeked?:GenericEventHandler;
|
||||
onSeeking?:GenericEventHandler;
|
||||
onStalled?:GenericEventHandler;
|
||||
onSuspend?:GenericEventHandler;
|
||||
onTimeUpdate?:GenericEventHandler;
|
||||
onVolumeChange?:GenericEventHandler;
|
||||
onWaiting?:GenericEventHandler;
|
||||
|
||||
// MouseEvents
|
||||
onClick?:MouseEventHandler;
|
||||
onContextMenu?:MouseEventHandler;
|
||||
onDoubleClick?:MouseEventHandler;
|
||||
onDrag?:DragEventHandler;
|
||||
onDragEnd?:DragEventHandler;
|
||||
onDragEnter?:DragEventHandler;
|
||||
onDragExit?:DragEventHandler;
|
||||
onDragLeave?:DragEventHandler;
|
||||
onDragOver?:DragEventHandler;
|
||||
onDragStart?:DragEventHandler;
|
||||
onDrop?:DragEventHandler;
|
||||
onMouseDown?:MouseEventHandler;
|
||||
onMouseEnter?:MouseEventHandler;
|
||||
onMouseLeave?:MouseEventHandler;
|
||||
onMouseMove?:MouseEventHandler;
|
||||
onMouseOut?:MouseEventHandler;
|
||||
onMouseOver?:MouseEventHandler;
|
||||
onMouseUp?:MouseEventHandler;
|
||||
|
||||
// Selection Events
|
||||
onSelect?:GenericEventHandler;
|
||||
|
||||
// Touch Events
|
||||
onTouchCancel?:TouchEventHandler;
|
||||
onTouchEnd?:TouchEventHandler;
|
||||
onTouchMove?:TouchEventHandler;
|
||||
onTouchStart?:TouchEventHandler;
|
||||
|
||||
// UI Events
|
||||
onScroll?:UIEventHandler;
|
||||
|
||||
// Wheel Events
|
||||
onWheel?:WheelEventHandler;
|
||||
|
||||
// Animation Events
|
||||
onAnimationStart?:AnimationEventHandler;
|
||||
onAnimationEnd?:AnimationEventHandler;
|
||||
onAnimationIteration?:AnimationEventHandler;
|
||||
|
||||
// Transition Events
|
||||
onTransitionEnd?:TransitionEventHandler;
|
||||
}
|
||||
|
||||
interface HTMLAttributes extends preact.PreactHTMLAttributes, DOMAttributed {
|
||||
// Standard HTML Attributes
|
||||
accept?:string;
|
||||
acceptCharset?:string;
|
||||
accessKey?:string;
|
||||
action?:string;
|
||||
allowFullScreen?:boolean;
|
||||
allowTransparency?:boolean;
|
||||
alt?:string;
|
||||
async?:boolean;
|
||||
autocomplete?:string;
|
||||
autofocus?:boolean;
|
||||
autoPlay?:boolean;
|
||||
capture?:boolean;
|
||||
cellPadding?:number | string;
|
||||
cellSpacing?:number | string;
|
||||
charSet?:string;
|
||||
challenge?:string;
|
||||
checked?:boolean;
|
||||
class?:string | { [key:string]: boolean };
|
||||
className?:string | { [key:string]: boolean };
|
||||
cols?:number;
|
||||
colSpan?:number;
|
||||
content?:string;
|
||||
contentEditable?:boolean;
|
||||
contextMenu?:string;
|
||||
controls?:boolean;
|
||||
coords?:string;
|
||||
crossOrigin?:string;
|
||||
data?:string;
|
||||
dateTime?:string;
|
||||
default?:boolean;
|
||||
defer?:boolean;
|
||||
dir?:string;
|
||||
disabled?:boolean;
|
||||
download?:any;
|
||||
draggable?:boolean;
|
||||
encType?:string;
|
||||
form?:string;
|
||||
formAction?:string;
|
||||
formEncType?:string;
|
||||
formMethod?:string;
|
||||
formNoValidate?:boolean;
|
||||
formTarget?:string;
|
||||
frameBorder?:number | string;
|
||||
headers?:string;
|
||||
height?:number | string;
|
||||
hidden?:boolean;
|
||||
high?:number;
|
||||
href?:string;
|
||||
hrefLang?:string;
|
||||
for?:string;
|
||||
httpEquiv?:string;
|
||||
icon?:string;
|
||||
id?:string;
|
||||
inputMode?:string;
|
||||
integrity?:string;
|
||||
is?:string;
|
||||
keyParams?:string;
|
||||
keyType?:string;
|
||||
kind?:string;
|
||||
label?:string;
|
||||
lang?:string;
|
||||
list?:string;
|
||||
loop?:boolean;
|
||||
low?:number;
|
||||
manifest?:string;
|
||||
marginHeight?:number;
|
||||
marginWidth?:number;
|
||||
max?:number | string;
|
||||
maxLength?:number;
|
||||
media?:string;
|
||||
mediaGroup?:string;
|
||||
method?:string;
|
||||
min?:number | string;
|
||||
minLength?:number;
|
||||
multiple?:boolean;
|
||||
muted?:boolean;
|
||||
name?:string;
|
||||
noValidate?:boolean;
|
||||
open?:boolean;
|
||||
optimum?:number;
|
||||
pattern?:string;
|
||||
placeholder?:string;
|
||||
poster?:string;
|
||||
preload?:string;
|
||||
radioGroup?:string;
|
||||
readOnly?:boolean;
|
||||
rel?:string;
|
||||
required?:boolean;
|
||||
role?:string;
|
||||
rows?:number;
|
||||
rowSpan?:number;
|
||||
sandbox?:string;
|
||||
scope?:string;
|
||||
scoped?:boolean;
|
||||
scrolling?:string;
|
||||
seamless?:boolean;
|
||||
selected?:boolean;
|
||||
shape?:string;
|
||||
size?:number;
|
||||
sizes?:string;
|
||||
span?:number;
|
||||
spellCheck?:boolean;
|
||||
src?:string;
|
||||
srcset?:string;
|
||||
srcDoc?:string;
|
||||
srcLang?:string;
|
||||
srcSet?:string;
|
||||
start?:number;
|
||||
step?:number | string;
|
||||
style?:any;
|
||||
summary?:string;
|
||||
tabIndex?:number;
|
||||
target?:string;
|
||||
title?:string;
|
||||
type?:string;
|
||||
useMap?:string;
|
||||
value?:string | string[];
|
||||
width?:number | string;
|
||||
wmode?:string;
|
||||
wrap?:string;
|
||||
|
||||
// RDFa Attributes
|
||||
about?:string;
|
||||
datatype?:string;
|
||||
inlist?:any;
|
||||
prefix?:string;
|
||||
property?:string;
|
||||
resource?:string;
|
||||
typeof?:string;
|
||||
vocab?:string;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// HTML
|
||||
a:HTMLAttributes;
|
||||
abbr:HTMLAttributes;
|
||||
address:HTMLAttributes;
|
||||
area:HTMLAttributes;
|
||||
article:HTMLAttributes;
|
||||
aside:HTMLAttributes;
|
||||
audio:HTMLAttributes;
|
||||
b:HTMLAttributes;
|
||||
base:HTMLAttributes;
|
||||
bdi:HTMLAttributes;
|
||||
bdo:HTMLAttributes;
|
||||
big:HTMLAttributes;
|
||||
blockquote:HTMLAttributes;
|
||||
body:HTMLAttributes;
|
||||
br:HTMLAttributes;
|
||||
button:HTMLAttributes;
|
||||
canvas:HTMLAttributes;
|
||||
caption:HTMLAttributes;
|
||||
cite:HTMLAttributes;
|
||||
code:HTMLAttributes;
|
||||
col:HTMLAttributes;
|
||||
colgroup:HTMLAttributes;
|
||||
data:HTMLAttributes;
|
||||
datalist:HTMLAttributes;
|
||||
dd:HTMLAttributes;
|
||||
del:HTMLAttributes;
|
||||
details:HTMLAttributes;
|
||||
dfn:HTMLAttributes;
|
||||
dialog:HTMLAttributes;
|
||||
div:HTMLAttributes;
|
||||
dl:HTMLAttributes;
|
||||
dt:HTMLAttributes;
|
||||
em:HTMLAttributes;
|
||||
embed:HTMLAttributes;
|
||||
fieldset:HTMLAttributes;
|
||||
figcaption:HTMLAttributes;
|
||||
figure:HTMLAttributes;
|
||||
footer:HTMLAttributes;
|
||||
form:HTMLAttributes;
|
||||
h1:HTMLAttributes;
|
||||
h2:HTMLAttributes;
|
||||
h3:HTMLAttributes;
|
||||
h4:HTMLAttributes;
|
||||
h5:HTMLAttributes;
|
||||
h6:HTMLAttributes;
|
||||
head:HTMLAttributes;
|
||||
header:HTMLAttributes;
|
||||
hr:HTMLAttributes;
|
||||
html:HTMLAttributes;
|
||||
i:HTMLAttributes;
|
||||
iframe:HTMLAttributes;
|
||||
img:HTMLAttributes;
|
||||
input:HTMLAttributes;
|
||||
ins:HTMLAttributes;
|
||||
kbd:HTMLAttributes;
|
||||
keygen:HTMLAttributes;
|
||||
label:HTMLAttributes;
|
||||
legend:HTMLAttributes;
|
||||
li:HTMLAttributes;
|
||||
link:HTMLAttributes;
|
||||
main:HTMLAttributes;
|
||||
map:HTMLAttributes;
|
||||
mark:HTMLAttributes;
|
||||
menu:HTMLAttributes;
|
||||
menuitem:HTMLAttributes;
|
||||
meta:HTMLAttributes;
|
||||
meter:HTMLAttributes;
|
||||
nav:HTMLAttributes;
|
||||
noscript:HTMLAttributes;
|
||||
object:HTMLAttributes;
|
||||
ol:HTMLAttributes;
|
||||
optgroup:HTMLAttributes;
|
||||
option:HTMLAttributes;
|
||||
output:HTMLAttributes;
|
||||
p:HTMLAttributes;
|
||||
param:HTMLAttributes;
|
||||
picture:HTMLAttributes;
|
||||
pre:HTMLAttributes;
|
||||
progress:HTMLAttributes;
|
||||
q:HTMLAttributes;
|
||||
rp:HTMLAttributes;
|
||||
rt:HTMLAttributes;
|
||||
ruby:HTMLAttributes;
|
||||
s:HTMLAttributes;
|
||||
samp:HTMLAttributes;
|
||||
script:HTMLAttributes;
|
||||
section:HTMLAttributes;
|
||||
select:HTMLAttributes;
|
||||
small:HTMLAttributes;
|
||||
source:HTMLAttributes;
|
||||
span:HTMLAttributes;
|
||||
strong:HTMLAttributes;
|
||||
style:HTMLAttributes;
|
||||
sub:HTMLAttributes;
|
||||
summary:HTMLAttributes;
|
||||
sup:HTMLAttributes;
|
||||
table:HTMLAttributes;
|
||||
tbody:HTMLAttributes;
|
||||
td:HTMLAttributes;
|
||||
textarea:HTMLAttributes;
|
||||
tfoot:HTMLAttributes;
|
||||
th:HTMLAttributes;
|
||||
thead:HTMLAttributes;
|
||||
time:HTMLAttributes;
|
||||
title:HTMLAttributes;
|
||||
tr:HTMLAttributes;
|
||||
track:HTMLAttributes;
|
||||
u:HTMLAttributes;
|
||||
ul:HTMLAttributes;
|
||||
"var":HTMLAttributes;
|
||||
video:HTMLAttributes;
|
||||
wbr:HTMLAttributes;
|
||||
|
||||
//SVG
|
||||
svg:SVGAttributes;
|
||||
|
||||
circle:SVGAttributes;
|
||||
clipPath:SVGAttributes;
|
||||
defs:SVGAttributes;
|
||||
ellipse:SVGAttributes;
|
||||
feBlend:SVGAttributes;
|
||||
feColorMatrix:SVGAttributes;
|
||||
feComponentTransfer:SVGAttributes;
|
||||
feComposite:SVGAttributes;
|
||||
feConvolveMatrix:SVGAttributes;
|
||||
feDiffuseLighting:SVGAttributes;
|
||||
feDisplacementMap:SVGAttributes;
|
||||
feFlood:SVGAttributes;
|
||||
feGaussianBlur:SVGAttributes;
|
||||
feImage:SVGAttributes;
|
||||
feMerge:SVGAttributes;
|
||||
feMergeNode:SVGAttributes;
|
||||
feMorphology:SVGAttributes;
|
||||
feOffset:SVGAttributes;
|
||||
feSpecularLighting:SVGAttributes;
|
||||
feTile:SVGAttributes;
|
||||
feTurbulence:SVGAttributes;
|
||||
filter:SVGAttributes;
|
||||
foreignObject:SVGAttributes;
|
||||
g:SVGAttributes;
|
||||
image:SVGAttributes;
|
||||
line:SVGAttributes;
|
||||
linearGradient:SVGAttributes;
|
||||
marker:SVGAttributes;
|
||||
mask:SVGAttributes;
|
||||
path:SVGAttributes;
|
||||
pattern:SVGAttributes;
|
||||
polygon:SVGAttributes;
|
||||
polyline:SVGAttributes;
|
||||
radialGradient:SVGAttributes;
|
||||
rect:SVGAttributes;
|
||||
stop:SVGAttributes;
|
||||
symbol:SVGAttributes;
|
||||
text:SVGAttributes;
|
||||
tspan:SVGAttributes;
|
||||
use:SVGAttributes;
|
||||
}
|
||||
}
|
24
thirdparty/preact/src/preact.js
vendored
24
thirdparty/preact/src/preact.js
vendored
@ -1,24 +0,0 @@
|
||||
import { h } from './h';
|
||||
import { cloneElement } from './clone-element';
|
||||
import { Component } from './component';
|
||||
import { render } from './render';
|
||||
import { rerender } from './render-queue';
|
||||
import options from './options';
|
||||
|
||||
export default {
|
||||
h,
|
||||
cloneElement,
|
||||
Component,
|
||||
render,
|
||||
rerender,
|
||||
options
|
||||
};
|
||||
|
||||
export {
|
||||
h,
|
||||
cloneElement,
|
||||
Component,
|
||||
render,
|
||||
rerender,
|
||||
options
|
||||
};
|
9
thirdparty/preact/src/preact.js.flow
vendored
9
thirdparty/preact/src/preact.js.flow
vendored
@ -1,9 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
import { createElement as h, cloneElement, Component, render } from 'react';
|
||||
|
||||
export { h, cloneElement, Component, render };
|
||||
export default { h, cloneElement, Component, render };
|
||||
|
||||
declare export function rerender(): void;
|
||||
declare export var options: Object;
|
23
thirdparty/preact/src/render-queue.js
vendored
23
thirdparty/preact/src/render-queue.js
vendored
@ -1,23 +0,0 @@
|
||||
import options from './options';
|
||||
import { defer } from './util';
|
||||
import { renderComponent } from './vdom/component';
|
||||
|
||||
/** Managed queue of dirty components to be re-rendered */
|
||||
|
||||
// items/itemsOffline swap on each rerender() call (just a simple pool technique)
|
||||
let items = [];
|
||||
|
||||
export function enqueueRender(component) {
|
||||
if (!component._dirty && (component._dirty = true) && items.push(component)==1) {
|
||||
(options.debounceRendering || defer)(rerender);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function rerender() {
|
||||
let p, list = items;
|
||||
items = [];
|
||||
while ( (p = list.pop()) ) {
|
||||
if (p._dirty) renderComponent(p);
|
||||
}
|
||||
}
|
20
thirdparty/preact/src/render.js
vendored
20
thirdparty/preact/src/render.js
vendored
@ -1,20 +0,0 @@
|
||||
import { diff } from './vdom/diff';
|
||||
|
||||
/** Render JSX into a `parent` Element.
|
||||
* @param {VNode} vnode A (JSX) VNode to render
|
||||
* @param {Element} parent DOM element to render into
|
||||
* @param {Element} [merge] Attempt to re-use an existing DOM tree rooted at `merge`
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* // render a div into <body>:
|
||||
* render(<div id="hello">hello!</div>, document.body);
|
||||
*
|
||||
* @example
|
||||
* // render a "Thing" component into #foo:
|
||||
* const Thing = ({ name }) => <span>{ name }</span>;
|
||||
* render(<Thing name="one" />, document.querySelector('#foo'));
|
||||
*/
|
||||
export function render(vnode, parent, merge) {
|
||||
return diff(merge, vnode, {}, false, parent);
|
||||
}
|
68
thirdparty/preact/src/util.js
vendored
68
thirdparty/preact/src/util.js
vendored
@ -1,68 +0,0 @@
|
||||
/** Copy own-properties from `props` onto `obj`.
|
||||
* @returns obj
|
||||
* @private
|
||||
*/
|
||||
export function extend(obj, props) {
|
||||
if (props) {
|
||||
for (let i in props) obj[i] = props[i];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/** Fast clone. Note: does not filter out non-own properties.
|
||||
* @see https://esbench.com/bench/56baa34f45df6895002e03b6
|
||||
*/
|
||||
export function clone(obj) {
|
||||
return extend({}, obj);
|
||||
}
|
||||
|
||||
|
||||
/** Get a deep property value from the given object, expressed in dot-notation.
|
||||
* @private
|
||||
*/
|
||||
export function delve(obj, key) {
|
||||
for (let p=key.split('.'), i=0; i<p.length && obj; i++) {
|
||||
obj = obj[p[i]];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/** @private is the given object a Function? */
|
||||
export function isFunction(obj) {
|
||||
return 'function'===typeof obj;
|
||||
}
|
||||
|
||||
|
||||
/** @private is the given object a String? */
|
||||
export function isString(obj) {
|
||||
return 'string'===typeof obj;
|
||||
}
|
||||
|
||||
|
||||
/** Convert a hashmap of CSS classes to a space-delimited className string
|
||||
* @private
|
||||
*/
|
||||
export function hashToClassName(c) {
|
||||
let str = '';
|
||||
for (let prop in c) {
|
||||
if (c[prop]) {
|
||||
if (str) str += ' ';
|
||||
str += prop;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/** Just a memoized String#toLowerCase */
|
||||
let lcCache = {};
|
||||
export const toLowerCase = s => lcCache[s] || (lcCache[s] = s.toLowerCase());
|
||||
|
||||
|
||||
/** Call a function asynchronously, as soon as possible.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
let resolved = typeof Promise!=='undefined' && Promise.resolve();
|
||||
export const defer = resolved ? (f => { resolved.then(f); }) : setTimeout;
|
32
thirdparty/preact/src/vdom/component-recycler.js
vendored
32
thirdparty/preact/src/vdom/component-recycler.js
vendored
@ -1,32 +0,0 @@
|
||||
import { Component } from '../component';
|
||||
|
||||
/** Retains a pool of Components for re-use, keyed on component name.
|
||||
* Note: since component names are not unique or even necessarily available, these are primarily a form of sharding.
|
||||
* @private
|
||||
*/
|
||||
const components = {};
|
||||
|
||||
|
||||
export function collectComponent(component) {
|
||||
let name = component.constructor.name,
|
||||
list = components[name];
|
||||
if (list) list.push(component);
|
||||
else components[name] = [component];
|
||||
}
|
||||
|
||||
|
||||
export function createComponent(Ctor, props, context) {
|
||||
let inst = new Ctor(props, context),
|
||||
list = components[Ctor.name];
|
||||
Component.call(inst, props, context);
|
||||
if (list) {
|
||||
for (let i=list.length; i--; ) {
|
||||
if (list[i].constructor===Ctor) {
|
||||
inst.nextBase = list[i].nextBase;
|
||||
list.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inst;
|
||||
}
|
281
thirdparty/preact/src/vdom/component.js
vendored
281
thirdparty/preact/src/vdom/component.js
vendored
@ -1,281 +0,0 @@
|
||||
import { SYNC_RENDER, NO_RENDER, FORCE_RENDER, ASYNC_RENDER, ATTR_KEY } from '../constants';
|
||||
import options from '../options';
|
||||
import { isFunction, clone, extend } from '../util';
|
||||
import { enqueueRender } from '../render-queue';
|
||||
import { getNodeProps } from './index';
|
||||
import { diff, mounts, diffLevel, flushMounts, removeOrphanedChildren, recollectNodeTree } from './diff';
|
||||
import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';
|
||||
import { createComponent, collectComponent } from './component-recycler';
|
||||
import { removeNode } from '../dom/index';
|
||||
|
||||
|
||||
|
||||
/** Set a component's `props` (generally derived from JSX attributes).
|
||||
* @param {Object} props
|
||||
* @param {Object} [opts]
|
||||
* @param {boolean} [opts.renderSync=false] If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
|
||||
* @param {boolean} [opts.render=true] If `false`, no render will be triggered.
|
||||
*/
|
||||
export function setComponentProps(component, props, opts, context, mountAll) {
|
||||
if (component._disable) return;
|
||||
component._disable = true;
|
||||
|
||||
if ((component.__ref = props.ref)) delete props.ref;
|
||||
if ((component.__key = props.key)) delete props.key;
|
||||
|
||||
if (!component.base || mountAll) {
|
||||
if (component.componentWillMount) component.componentWillMount();
|
||||
}
|
||||
else if (component.componentWillReceiveProps) {
|
||||
component.componentWillReceiveProps(props, context);
|
||||
}
|
||||
|
||||
if (context && context!==component.context) {
|
||||
if (!component.prevContext) component.prevContext = component.context;
|
||||
component.context = context;
|
||||
}
|
||||
|
||||
if (!component.prevProps) component.prevProps = component.props;
|
||||
component.props = props;
|
||||
|
||||
component._disable = false;
|
||||
|
||||
if (opts!==NO_RENDER) {
|
||||
if (opts===SYNC_RENDER || options.syncComponentUpdates!==false || !component.base) {
|
||||
renderComponent(component, SYNC_RENDER, mountAll);
|
||||
}
|
||||
else {
|
||||
enqueueRender(component);
|
||||
}
|
||||
}
|
||||
|
||||
if (component.__ref) component.__ref(component);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
|
||||
* @param {Component} component
|
||||
* @param {Object} [opts]
|
||||
* @param {boolean} [opts.build=false] If `true`, component will build and store a DOM node if not already associated with one.
|
||||
* @private
|
||||
*/
|
||||
export function renderComponent(component, opts, mountAll, isChild) {
|
||||
if (component._disable) return;
|
||||
|
||||
let skip, rendered,
|
||||
props = component.props,
|
||||
state = component.state,
|
||||
context = component.context,
|
||||
previousProps = component.prevProps || props,
|
||||
previousState = component.prevState || state,
|
||||
previousContext = component.prevContext || context,
|
||||
isUpdate = component.base,
|
||||
nextBase = component.nextBase,
|
||||
initialBase = isUpdate || nextBase,
|
||||
initialChildComponent = component._component,
|
||||
inst, cbase;
|
||||
|
||||
// if updating
|
||||
if (isUpdate) {
|
||||
component.props = previousProps;
|
||||
component.state = previousState;
|
||||
component.context = previousContext;
|
||||
if (opts!==FORCE_RENDER
|
||||
&& component.shouldComponentUpdate
|
||||
&& component.shouldComponentUpdate(props, state, context) === false) {
|
||||
skip = true;
|
||||
}
|
||||
else if (component.componentWillUpdate) {
|
||||
component.componentWillUpdate(props, state, context);
|
||||
}
|
||||
component.props = props;
|
||||
component.state = state;
|
||||
component.context = context;
|
||||
}
|
||||
|
||||
component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
|
||||
component._dirty = false;
|
||||
|
||||
if (!skip) {
|
||||
if (component.render) rendered = component.render(props, state, context);
|
||||
|
||||
// context to pass to the child, can be updated via (grand-)parent component
|
||||
if (component.getChildContext) {
|
||||
context = extend(clone(context), component.getChildContext());
|
||||
}
|
||||
|
||||
while (isFunctionalComponent(rendered)) {
|
||||
rendered = buildFunctionalComponent(rendered, context);
|
||||
}
|
||||
|
||||
let childComponent = rendered && rendered.nodeName,
|
||||
toUnmount, base;
|
||||
|
||||
if (isFunction(childComponent)) {
|
||||
// set up high order component link
|
||||
|
||||
|
||||
inst = initialChildComponent;
|
||||
let childProps = getNodeProps(rendered);
|
||||
|
||||
if (inst && inst.constructor===childComponent) {
|
||||
setComponentProps(inst, childProps, SYNC_RENDER, context);
|
||||
}
|
||||
else {
|
||||
toUnmount = inst;
|
||||
|
||||
inst = createComponent(childComponent, childProps, context);
|
||||
inst.nextBase = inst.nextBase || nextBase;
|
||||
inst._parentComponent = component;
|
||||
component._component = inst;
|
||||
setComponentProps(inst, childProps, NO_RENDER, context);
|
||||
renderComponent(inst, SYNC_RENDER, mountAll, true);
|
||||
}
|
||||
|
||||
base = inst.base;
|
||||
}
|
||||
else {
|
||||
cbase = initialBase;
|
||||
|
||||
// destroy high order component link
|
||||
toUnmount = initialChildComponent;
|
||||
if (toUnmount) {
|
||||
cbase = component._component = null;
|
||||
}
|
||||
|
||||
if (initialBase || opts===SYNC_RENDER) {
|
||||
if (cbase) cbase._component = null;
|
||||
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (initialBase && base!==initialBase && inst!==initialChildComponent) {
|
||||
let baseParent = initialBase.parentNode;
|
||||
if (baseParent && base!==baseParent) {
|
||||
baseParent.replaceChild(base, initialBase);
|
||||
|
||||
if (!toUnmount) {
|
||||
initialBase._component = null;
|
||||
recollectNodeTree(initialBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toUnmount) {
|
||||
unmountComponent(toUnmount, base!==initialBase);
|
||||
}
|
||||
|
||||
component.base = base;
|
||||
if (base && !isChild) {
|
||||
let componentRef = component,
|
||||
t = component;
|
||||
while ((t=t._parentComponent)) {
|
||||
(componentRef = t).base = base;
|
||||
}
|
||||
base._component = componentRef;
|
||||
base._componentConstructor = componentRef.constructor;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUpdate || mountAll) {
|
||||
mounts.unshift(component);
|
||||
}
|
||||
else if (!skip) {
|
||||
if (component.componentDidUpdate) {
|
||||
component.componentDidUpdate(previousProps, previousState, previousContext);
|
||||
}
|
||||
if (options.afterUpdate) options.afterUpdate(component);
|
||||
}
|
||||
|
||||
let cb = component._renderCallbacks, fn;
|
||||
if (cb) while ( (fn = cb.pop()) ) fn.call(component);
|
||||
|
||||
if (!diffLevel && !isChild) flushMounts();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Apply the Component referenced by a VNode to the DOM.
|
||||
* @param {Element} dom The DOM node to mutate
|
||||
* @param {VNode} vnode A Component-referencing VNode
|
||||
* @returns {Element} dom The created/mutated element
|
||||
* @private
|
||||
*/
|
||||
export function buildComponentFromVNode(dom, vnode, context, mountAll) {
|
||||
let c = dom && dom._component,
|
||||
oldDom = dom,
|
||||
isDirectOwner = c && dom._componentConstructor===vnode.nodeName,
|
||||
isOwner = isDirectOwner,
|
||||
props = getNodeProps(vnode);
|
||||
while (c && !isOwner && (c=c._parentComponent)) {
|
||||
isOwner = c.constructor===vnode.nodeName;
|
||||
}
|
||||
|
||||
if (c && isOwner && (!mountAll || c._component)) {
|
||||
setComponentProps(c, props, ASYNC_RENDER, context, mountAll);
|
||||
dom = c.base;
|
||||
}
|
||||
else {
|
||||
if (c && !isDirectOwner) {
|
||||
unmountComponent(c, true);
|
||||
dom = oldDom = null;
|
||||
}
|
||||
|
||||
c = createComponent(vnode.nodeName, props, context);
|
||||
if (dom && !c.nextBase) {
|
||||
c.nextBase = dom;
|
||||
// passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L241:
|
||||
oldDom = null;
|
||||
}
|
||||
setComponentProps(c, props, SYNC_RENDER, context, mountAll);
|
||||
dom = c.base;
|
||||
|
||||
if (oldDom && dom!==oldDom) {
|
||||
oldDom._component = null;
|
||||
recollectNodeTree(oldDom);
|
||||
}
|
||||
}
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Remove a component from the DOM and recycle it.
|
||||
* @param {Element} dom A DOM node from which to unmount the given Component
|
||||
* @param {Component} component The Component instance to unmount
|
||||
* @private
|
||||
*/
|
||||
export function unmountComponent(component, remove) {
|
||||
if (options.beforeUnmount) options.beforeUnmount(component);
|
||||
|
||||
// console.log(`${remove?'Removing':'Unmounting'} component: ${component.constructor.name}`);
|
||||
let base = component.base;
|
||||
|
||||
component._disable = true;
|
||||
|
||||
if (component.componentWillUnmount) component.componentWillUnmount();
|
||||
|
||||
component.base = null;
|
||||
|
||||
// recursively tear down & recollect high-order component children:
|
||||
let inner = component._component;
|
||||
if (inner) {
|
||||
unmountComponent(inner, remove);
|
||||
}
|
||||
else if (base) {
|
||||
if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);
|
||||
|
||||
component.nextBase = base;
|
||||
|
||||
if (remove) {
|
||||
removeNode(base);
|
||||
collectComponent(component);
|
||||
}
|
||||
removeOrphanedChildren(base.childNodes, !remove);
|
||||
}
|
||||
|
||||
if (component.__ref) component.__ref(null);
|
||||
if (component.componentDidUnmount) component.componentDidUnmount();
|
||||
}
|
254
thirdparty/preact/src/vdom/diff.js
vendored
254
thirdparty/preact/src/vdom/diff.js
vendored
@ -1,254 +0,0 @@
|
||||
import { ATTR_KEY } from '../constants';
|
||||
import { isString, isFunction } from '../util';
|
||||
import { isSameNodeType, isNamedNode } from './index';
|
||||
import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';
|
||||
import { buildComponentFromVNode } from './component';
|
||||
import { setAccessor } from '../dom/index';
|
||||
import { createNode, collectNode } from '../dom/recycler';
|
||||
import { unmountComponent } from './component';
|
||||
import options from '../options';
|
||||
|
||||
|
||||
/** Diff recursion count, used to track the end of the diff cycle. */
|
||||
export const mounts = [];
|
||||
|
||||
/** Diff recursion count, used to track the end of the diff cycle. */
|
||||
export let diffLevel = 0;
|
||||
|
||||
let isSvgMode = false;
|
||||
|
||||
|
||||
export function flushMounts() {
|
||||
let c;
|
||||
while ((c=mounts.pop())) {
|
||||
if (options.afterMount) options.afterMount(c);
|
||||
if (c.componentDidMount) c.componentDidMount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
|
||||
* @param {Element} [dom=null] A DOM node to mutate into the shape of the `vnode`
|
||||
* @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure
|
||||
* @returns {Element} dom The created/mutated element
|
||||
* @private
|
||||
*/
|
||||
export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
|
||||
if (!diffLevel++) isSvgMode = parent instanceof SVGElement;
|
||||
let ret = idiff(dom, vnode, context, mountAll);
|
||||
if (parent && ret.parentNode!==parent) parent.appendChild(ret);
|
||||
if (!--diffLevel && !componentRoot) flushMounts();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
function idiff(dom, vnode, context, mountAll) {
|
||||
let originalAttributes = vnode && vnode.attributes;
|
||||
|
||||
while (isFunctionalComponent(vnode)) {
|
||||
vnode = buildFunctionalComponent(vnode, context);
|
||||
}
|
||||
|
||||
if (vnode==null) vnode = '';
|
||||
|
||||
if (isString(vnode)) {
|
||||
if (dom) {
|
||||
if (dom instanceof Text && dom.parentNode) {
|
||||
if (dom.nodeValue!=vnode) {
|
||||
dom.nodeValue = vnode;
|
||||
}
|
||||
return dom;
|
||||
}
|
||||
recollectNodeTree(dom);
|
||||
}
|
||||
return document.createTextNode(vnode);
|
||||
}
|
||||
|
||||
if (isFunction(vnode.nodeName)) {
|
||||
return buildComponentFromVNode(dom, vnode, context, mountAll);
|
||||
}
|
||||
|
||||
let out = dom,
|
||||
nodeName = vnode.nodeName,
|
||||
prevSvgMode = isSvgMode,
|
||||
vchildren = vnode.children;
|
||||
|
||||
if (!isString(nodeName)) {
|
||||
nodeName = String(nodeName);
|
||||
}
|
||||
|
||||
isSvgMode = nodeName==='svg' ? true : nodeName==='foreignObject' ? false : isSvgMode;
|
||||
|
||||
if (!dom) {
|
||||
out = createNode(nodeName, isSvgMode);
|
||||
}
|
||||
else if (!isNamedNode(dom, nodeName)) {
|
||||
out = createNode(nodeName, isSvgMode);
|
||||
// move children into the replacement node
|
||||
while (dom.firstChild) out.appendChild(dom.firstChild);
|
||||
// reclaim element nodes
|
||||
recollectNodeTree(dom);
|
||||
}
|
||||
|
||||
// fast-path for elements containing a single TextNode:
|
||||
if (vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && out.childNodes.length===1 && out.firstChild instanceof Text) {
|
||||
if (out.firstChild.nodeValue!=vchildren[0]) {
|
||||
out.firstChild.nodeValue = vchildren[0];
|
||||
}
|
||||
}
|
||||
else if (vchildren && vchildren.length || out.firstChild) {
|
||||
innerDiffNode(out, vchildren, context, mountAll);
|
||||
}
|
||||
|
||||
let props = out[ATTR_KEY];
|
||||
if (!props) {
|
||||
out[ATTR_KEY] = props = {};
|
||||
for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value;
|
||||
}
|
||||
|
||||
diffAttributes(out, vnode.attributes, props);
|
||||
|
||||
if (originalAttributes && typeof originalAttributes.ref==='function') {
|
||||
(props.ref = originalAttributes.ref)(out);
|
||||
}
|
||||
|
||||
isSvgMode = prevSvgMode;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/** Apply child and attribute changes between a VNode and a DOM Node to the DOM. */
|
||||
function innerDiffNode(dom, vchildren, context, mountAll) {
|
||||
let originalChildren = dom.childNodes,
|
||||
children = [],
|
||||
keyed = {},
|
||||
keyedLen = 0,
|
||||
min = 0,
|
||||
len = originalChildren.length,
|
||||
childrenLen = 0,
|
||||
vlen = vchildren && vchildren.length,
|
||||
j, c, vchild, child;
|
||||
|
||||
if (len) {
|
||||
for (let i=0; i<len; i++) {
|
||||
let child = originalChildren[i],
|
||||
key = vlen ? ((c = child._component) ? c.__key : (c = child[ATTR_KEY]) ? c.key : null) : null;
|
||||
if (key || key===0) {
|
||||
keyedLen++;
|
||||
keyed[key] = child;
|
||||
}
|
||||
else {
|
||||
children[childrenLen++] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vlen) {
|
||||
for (let i=0; i<vlen; i++) {
|
||||
vchild = vchildren[i];
|
||||
child = null;
|
||||
|
||||
// if (isFunctionalComponent(vchild)) {
|
||||
// vchild = buildFunctionalComponent(vchild);
|
||||
// }
|
||||
|
||||
// attempt to find a node based on key matching
|
||||
let key = vchild.key;
|
||||
if (key!=null) {
|
||||
if (keyedLen && key in keyed) {
|
||||
child = keyed[key];
|
||||
keyed[key] = undefined;
|
||||
keyedLen--;
|
||||
}
|
||||
}
|
||||
// attempt to pluck a node of the same type from the existing children
|
||||
else if (!child && min<childrenLen) {
|
||||
for (j=min; j<childrenLen; j++) {
|
||||
c = children[j];
|
||||
if (c && isSameNodeType(c, vchild)) {
|
||||
child = c;
|
||||
children[j] = undefined;
|
||||
if (j===childrenLen-1) childrenLen--;
|
||||
if (j===min) min++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!child && min<childrenLen && isFunction(vchild.nodeName) && mountAll) {
|
||||
child = children[min];
|
||||
children[min++] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// morph the matched/found/created DOM child to match vchild (deep)
|
||||
child = idiff(child, vchild, context, mountAll);
|
||||
|
||||
if (child && child!==dom && child!==originalChildren[i]) {
|
||||
dom.insertBefore(child, originalChildren[i] || null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (keyedLen) {
|
||||
for (let i in keyed) if (keyed[i]) recollectNodeTree(keyed[i]);
|
||||
}
|
||||
|
||||
// remove orphaned children
|
||||
if (min<childrenLen) {
|
||||
removeOrphanedChildren(children);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Reclaim children that were unreferenced in the desired VTree */
|
||||
export function removeOrphanedChildren(children, unmountOnly) {
|
||||
for (let i=children.length; i--; ) {
|
||||
if (children[i]) {
|
||||
recollectNodeTree(children[i], unmountOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Reclaim an entire tree of nodes, starting at the root. */
|
||||
export function recollectNodeTree(node, unmountOnly) {
|
||||
// @TODO: Need to make a call on whether Preact should remove nodes not created by itself.
|
||||
// Currently it *does* remove them. Discussion: https://github.com/developit/preact/issues/39
|
||||
//if (!node[ATTR_KEY]) return;
|
||||
|
||||
let component = node._component;
|
||||
if (component) {
|
||||
unmountComponent(component, !unmountOnly);
|
||||
}
|
||||
else {
|
||||
if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
|
||||
|
||||
if (!unmountOnly) {
|
||||
collectNode(node);
|
||||
}
|
||||
|
||||
if (node.childNodes && node.childNodes.length) {
|
||||
removeOrphanedChildren(node.childNodes, unmountOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Apply differences in attributes from a VNode to the given DOM Node. */
|
||||
function diffAttributes(dom, attrs, old) {
|
||||
for (let name in old) {
|
||||
if (!(attrs && name in attrs) && old[name]!=null) {
|
||||
setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode);
|
||||
}
|
||||
}
|
||||
|
||||
// new & updated
|
||||
if (attrs) {
|
||||
for (let name in attrs) {
|
||||
if (name!=='children' && name!=='innerHTML' && (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name]))) {
|
||||
setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { EMPTY } from '../constants';
|
||||
import { getNodeProps } from './index';
|
||||
import { isFunction } from '../util';
|
||||
|
||||
|
||||
/** Check if a VNode is a reference to a stateless functional component.
|
||||
* A function component is represented as a VNode whose `nodeName` property is a reference to a function.
|
||||
* If that function is not a Component (ie, has no `.render()` method on a prototype), it is considered a stateless functional component.
|
||||
* @param {VNode} vnode A VNode
|
||||
* @private
|
||||
*/
|
||||
export function isFunctionalComponent(vnode) {
|
||||
let nodeName = vnode && vnode.nodeName;
|
||||
return nodeName && isFunction(nodeName) && !(nodeName.prototype && nodeName.prototype.render);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Construct a resultant VNode from a VNode referencing a stateless functional component.
|
||||
* @param {VNode} vnode A VNode with a `nodeName` property that is a reference to a function.
|
||||
* @private
|
||||
*/
|
||||
export function buildFunctionalComponent(vnode, context) {
|
||||
return vnode.nodeName(getNodeProps(vnode), context || EMPTY);
|
||||
}
|
49
thirdparty/preact/src/vdom/index.js
vendored
49
thirdparty/preact/src/vdom/index.js
vendored
@ -1,49 +0,0 @@
|
||||
import { clone, isString, isFunction, toLowerCase } from '../util';
|
||||
import { isFunctionalComponent } from './functional-component';
|
||||
|
||||
|
||||
/** Check if two nodes are equivalent.
|
||||
* @param {Element} node
|
||||
* @param {VNode} vnode
|
||||
* @private
|
||||
*/
|
||||
export function isSameNodeType(node, vnode) {
|
||||
if (isString(vnode)) {
|
||||
return node instanceof Text;
|
||||
}
|
||||
if (isString(vnode.nodeName)) {
|
||||
return isNamedNode(node, vnode.nodeName);
|
||||
}
|
||||
if (isFunction(vnode.nodeName)) {
|
||||
return node._componentConstructor===vnode.nodeName || isFunctionalComponent(vnode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isNamedNode(node, nodeName) {
|
||||
return node.normalizedNodeName===nodeName || toLowerCase(node.nodeName)===toLowerCase(nodeName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reconstruct Component-style `props` from a VNode.
|
||||
* Ensures default/fallback values from `defaultProps`:
|
||||
* Own-properties of `defaultProps` not present in `vnode.attributes` are added.
|
||||
* @param {VNode} vnode
|
||||
* @returns {Object} props
|
||||
*/
|
||||
export function getNodeProps(vnode) {
|
||||
let props = clone(vnode.attributes);
|
||||
props.children = vnode.children;
|
||||
|
||||
let defaultProps = vnode.nodeName.defaultProps;
|
||||
if (defaultProps) {
|
||||
for (let i in defaultProps) {
|
||||
if (props[i]===undefined) {
|
||||
props[i] = defaultProps[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
14
thirdparty/preact/src/vnode.js
vendored
14
thirdparty/preact/src/vnode.js
vendored
@ -1,14 +0,0 @@
|
||||
/** Virtual DOM Node */
|
||||
export function VNode(nodeName, attributes, children) {
|
||||
/** @type {string|function} */
|
||||
this.nodeName = nodeName;
|
||||
|
||||
/** @type {object<string>|undefined} */
|
||||
this.attributes = attributes;
|
||||
|
||||
/** @type {array<VNode>|undefined} */
|
||||
this.children = children;
|
||||
|
||||
/** Reference to the given key. */
|
||||
this.key = attributes && attributes.key;
|
||||
}
|
712
thirdparty/preact/test/browser/components.js
vendored
712
thirdparty/preact/test/browser/components.js
vendored
@ -1,712 +0,0 @@
|
||||
import { h, render, rerender, Component } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) );
|
||||
|
||||
function getAttributes(node) {
|
||||
let attrs = {};
|
||||
if (node.attributes) {
|
||||
for (let i=node.attributes.length; i--; ) {
|
||||
attrs[node.attributes[i].name] = node.attributes[i].value;
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
// hacky normalization of attribute order across browsers.
|
||||
function sortAttributes(html) {
|
||||
return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
|
||||
let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
|
||||
if (~after.indexOf('/')) after = '></'+pre+'>';
|
||||
return '<' + pre + list.join('') + after;
|
||||
});
|
||||
}
|
||||
|
||||
const Empty = () => null;
|
||||
|
||||
describe('Components', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
let c = scratch.firstElementChild;
|
||||
if (c) render(<Empty />, scratch, c);
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should render components', () => {
|
||||
class C1 extends Component {
|
||||
render() {
|
||||
return <div>C1</div>;
|
||||
}
|
||||
}
|
||||
sinon.spy(C1.prototype, 'render');
|
||||
render(<C1 />, scratch);
|
||||
|
||||
expect(C1.prototype.render)
|
||||
.to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithMatch({}, {})
|
||||
.and.to.have.returned(sinon.match({ nodeName:'div' }));
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div>C1</div>');
|
||||
});
|
||||
|
||||
|
||||
it('should render functional components', () => {
|
||||
const PROPS = { foo:'bar', onBaz:()=>{} };
|
||||
|
||||
const C3 = sinon.spy( props => <div {...props} /> );
|
||||
|
||||
render(<C3 {...PROPS} />, scratch);
|
||||
|
||||
expect(C3)
|
||||
.to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithMatch(PROPS)
|
||||
.and.to.have.returned(sinon.match({
|
||||
nodeName: 'div',
|
||||
attributes: PROPS
|
||||
}));
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div foo="bar"></div>');
|
||||
});
|
||||
|
||||
|
||||
it('should render components with props', () => {
|
||||
const PROPS = { foo:'bar', onBaz:()=>{} };
|
||||
let constructorProps;
|
||||
|
||||
class C2 extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructorProps = props;
|
||||
}
|
||||
render(props) {
|
||||
return <div {...props} />;
|
||||
}
|
||||
}
|
||||
sinon.spy(C2.prototype, 'render');
|
||||
|
||||
render(<C2 {...PROPS} />, scratch);
|
||||
|
||||
expect(constructorProps).to.deep.equal(PROPS);
|
||||
|
||||
expect(C2.prototype.render)
|
||||
.to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithMatch(PROPS, {})
|
||||
.and.to.have.returned(sinon.match({
|
||||
nodeName: 'div',
|
||||
attributes: PROPS
|
||||
}));
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div foo="bar"></div>');
|
||||
});
|
||||
|
||||
|
||||
// Test for Issue #73
|
||||
it('should remove orphaned elements replaced by Components', () => {
|
||||
class Comp extends Component {
|
||||
render() {
|
||||
return <span>span in a component</span>;
|
||||
}
|
||||
}
|
||||
|
||||
let root;
|
||||
function test(content) {
|
||||
root = render(content, scratch, root);
|
||||
}
|
||||
|
||||
test(<Comp />);
|
||||
test(<div>just a div</div>);
|
||||
test(<Comp />);
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<span>span in a component</span>');
|
||||
});
|
||||
|
||||
|
||||
// Test for Issue #176
|
||||
it('should remove children when root changes to text node', () => {
|
||||
let comp;
|
||||
|
||||
class Comp extends Component {
|
||||
render(_, { alt }) {
|
||||
return alt ? 'asdf' : <div>test</div>;
|
||||
}
|
||||
}
|
||||
|
||||
render(<Comp ref={c=>comp=c} />, scratch);
|
||||
|
||||
comp.setState({ alt:true });
|
||||
comp.forceUpdate();
|
||||
expect(scratch.innerHTML, 'switching to textnode').to.equal('asdf');
|
||||
|
||||
comp.setState({ alt:false });
|
||||
comp.forceUpdate();
|
||||
expect(scratch.innerHTML, 'switching to element').to.equal('<div>test</div>');
|
||||
|
||||
comp.setState({ alt:true });
|
||||
comp.forceUpdate();
|
||||
expect(scratch.innerHTML, 'switching to textnode 2').to.equal('asdf');
|
||||
});
|
||||
|
||||
|
||||
describe('props.children', () => {
|
||||
it('should support passing children as a prop', () => {
|
||||
const Foo = props => <div {...props} />;
|
||||
|
||||
render(<Foo a="b" children={[
|
||||
<span class="bar">bar</span>,
|
||||
'123',
|
||||
456
|
||||
]} />, scratch);
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div a="b"><span class="bar">bar</span>123456</div>');
|
||||
});
|
||||
|
||||
it('should be ignored when explicit children exist', () => {
|
||||
const Foo = props => <div {...props}>a</div>;
|
||||
|
||||
render(<Foo children={'b'} />, scratch);
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div>a</div>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('High-Order Components', () => {
|
||||
it('should render nested functional components', () => {
|
||||
const PROPS = { foo:'bar', onBaz:()=>{} };
|
||||
|
||||
const Outer = sinon.spy(
|
||||
props => <Inner {...props} />
|
||||
);
|
||||
|
||||
const Inner = sinon.spy(
|
||||
props => <div {...props}>inner</div>
|
||||
);
|
||||
|
||||
render(<Outer {...PROPS} />, scratch);
|
||||
|
||||
expect(Outer)
|
||||
.to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithMatch(PROPS)
|
||||
.and.to.have.returned(sinon.match({
|
||||
nodeName: Inner,
|
||||
attributes: PROPS
|
||||
}));
|
||||
|
||||
expect(Inner)
|
||||
.to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithMatch(PROPS)
|
||||
.and.to.have.returned(sinon.match({
|
||||
nodeName: 'div',
|
||||
attributes: PROPS,
|
||||
children: ['inner']
|
||||
}));
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div foo="bar">inner</div>');
|
||||
});
|
||||
|
||||
it('should re-render nested functional components', () => {
|
||||
let doRender = null;
|
||||
class Outer extends Component {
|
||||
componentDidMount() {
|
||||
let i = 1;
|
||||
doRender = () => this.setState({ i: ++i });
|
||||
}
|
||||
componentWillUnmount() {}
|
||||
render(props, { i }) {
|
||||
return <Inner i={i} {...props} />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Outer.prototype, 'render');
|
||||
sinon.spy(Outer.prototype, 'componentWillUnmount');
|
||||
|
||||
let j = 0;
|
||||
const Inner = sinon.spy(
|
||||
props => <div j={ ++j } {...props}>inner</div>
|
||||
);
|
||||
|
||||
render(<Outer foo="bar" />, scratch);
|
||||
|
||||
// update & flush
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Outer.prototype.componentWillUnmount)
|
||||
.not.to.have.been.called;
|
||||
|
||||
expect(Inner).to.have.been.calledTwice;
|
||||
|
||||
expect(Inner.secondCall)
|
||||
.to.have.been.calledWithMatch({ foo:'bar', i:2 })
|
||||
.and.to.have.returned(sinon.match({
|
||||
attributes: {
|
||||
j: 2,
|
||||
i: 2,
|
||||
foo: 'bar'
|
||||
}
|
||||
}));
|
||||
|
||||
expect(getAttributes(scratch.firstElementChild)).to.eql({
|
||||
j: '2',
|
||||
i: '2',
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
// update & flush
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Inner).to.have.been.calledThrice;
|
||||
|
||||
expect(Inner.thirdCall)
|
||||
.to.have.been.calledWithMatch({ foo:'bar', i:3 })
|
||||
.and.to.have.returned(sinon.match({
|
||||
attributes: {
|
||||
j: 3,
|
||||
i: 3,
|
||||
foo: 'bar'
|
||||
}
|
||||
}));
|
||||
|
||||
expect(getAttributes(scratch.firstElementChild)).to.eql({
|
||||
j: '3',
|
||||
i: '3',
|
||||
foo: 'bar'
|
||||
});
|
||||
});
|
||||
|
||||
it('should re-render nested components', () => {
|
||||
let doRender = null,
|
||||
alt = false;
|
||||
|
||||
class Outer extends Component {
|
||||
componentDidMount() {
|
||||
let i = 1;
|
||||
doRender = () => this.setState({ i: ++i });
|
||||
}
|
||||
componentWillUnmount() {}
|
||||
render(props, { i }) {
|
||||
if (alt) return <div is-alt />;
|
||||
return <Inner i={i} {...props} />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Outer.prototype, 'render');
|
||||
sinon.spy(Outer.prototype, 'componentDidMount');
|
||||
sinon.spy(Outer.prototype, 'componentWillUnmount');
|
||||
|
||||
let j = 0;
|
||||
class Inner extends Component {
|
||||
constructor(...args) {
|
||||
super();
|
||||
this._constructor(...args);
|
||||
}
|
||||
_constructor() {}
|
||||
componentWillMount() {}
|
||||
componentDidMount() {}
|
||||
componentWillUnmount() {}
|
||||
componentDidUnmount() {}
|
||||
render(props) {
|
||||
return <div j={ ++j } {...props}>inner</div>;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, '_constructor');
|
||||
sinon.spy(Inner.prototype, 'render');
|
||||
sinon.spy(Inner.prototype, 'componentWillMount');
|
||||
sinon.spy(Inner.prototype, 'componentDidMount');
|
||||
sinon.spy(Inner.prototype, 'componentDidUnmount');
|
||||
sinon.spy(Inner.prototype, 'componentWillUnmount');
|
||||
|
||||
render(<Outer foo="bar" />, scratch);
|
||||
|
||||
expect(Outer.prototype.componentDidMount).to.have.been.calledOnce;
|
||||
|
||||
// update & flush
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Outer.prototype.componentWillUnmount).not.to.have.been.called;
|
||||
|
||||
expect(Inner.prototype._constructor).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillUnmount).not.to.have.been.called;
|
||||
expect(Inner.prototype.componentDidUnmount).not.to.have.been.called;
|
||||
expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.render).to.have.been.calledTwice;
|
||||
|
||||
expect(Inner.prototype.render.secondCall)
|
||||
.to.have.been.calledWithMatch({ foo:'bar', i:2 })
|
||||
.and.to.have.returned(sinon.match({
|
||||
attributes: {
|
||||
j: 2,
|
||||
i: 2,
|
||||
foo: 'bar'
|
||||
}
|
||||
}));
|
||||
|
||||
expect(getAttributes(scratch.firstElementChild)).to.eql({
|
||||
j: '2',
|
||||
i: '2',
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('<div foo="bar" j="2" i="2">inner</div>'));
|
||||
|
||||
// update & flush
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Inner.prototype.componentWillUnmount).not.to.have.been.called;
|
||||
expect(Inner.prototype.componentDidUnmount).not.to.have.been.called;
|
||||
expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.render).to.have.been.calledThrice;
|
||||
|
||||
expect(Inner.prototype.render.thirdCall)
|
||||
.to.have.been.calledWithMatch({ foo:'bar', i:3 })
|
||||
.and.to.have.returned(sinon.match({
|
||||
attributes: {
|
||||
j: 3,
|
||||
i: 3,
|
||||
foo: 'bar'
|
||||
}
|
||||
}));
|
||||
|
||||
expect(getAttributes(scratch.firstElementChild)).to.eql({
|
||||
j: '3',
|
||||
i: '3',
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
|
||||
// update & flush
|
||||
alt = true;
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidUnmount).to.have.been.calledOnce;
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div is-alt="true"></div>');
|
||||
|
||||
// update & flush
|
||||
alt = false;
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('<div foo="bar" j="4" i="5">inner</div>'));
|
||||
});
|
||||
|
||||
it('should resolve intermediary functional component', () => {
|
||||
let ctx = {};
|
||||
class Root extends Component {
|
||||
getChildContext() {
|
||||
return { ctx };
|
||||
}
|
||||
render() {
|
||||
return <Func />;
|
||||
}
|
||||
}
|
||||
const Func = sinon.spy( () => <Inner /> );
|
||||
class Inner extends Component {
|
||||
componentWillMount() {}
|
||||
componentDidMount() {}
|
||||
componentWillUnmount() {}
|
||||
componentDidUnmount() {}
|
||||
render() {
|
||||
return <div>inner</div>;
|
||||
}
|
||||
}
|
||||
|
||||
spyAll(Inner.prototype);
|
||||
|
||||
let root = render(<Root />, scratch);
|
||||
|
||||
expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillMount).to.have.been.calledBefore(Inner.prototype.componentDidMount);
|
||||
|
||||
render(<asdf />, scratch, root);
|
||||
|
||||
expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidUnmount).to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillUnmount).to.have.been.calledBefore(Inner.prototype.componentDidUnmount);
|
||||
});
|
||||
|
||||
it('should unmount children of high-order components without unmounting parent', () => {
|
||||
let outer, inner2, counter=0;
|
||||
|
||||
class Outer extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
outer = this;
|
||||
this.state = {
|
||||
child: this.props.child
|
||||
};
|
||||
}
|
||||
componentWillUnmount(){}
|
||||
componentDidUnmount(){}
|
||||
componentWillMount(){}
|
||||
componentDidMount(){}
|
||||
render(_, { child:C }) {
|
||||
return <C />;
|
||||
}
|
||||
}
|
||||
spyAll(Outer.prototype);
|
||||
|
||||
class Inner extends Component {
|
||||
componentWillUnmount(){}
|
||||
componentDidUnmount(){}
|
||||
componentWillMount(){}
|
||||
componentDidMount(){}
|
||||
render() {
|
||||
return h('element'+(++counter));
|
||||
}
|
||||
}
|
||||
spyAll(Inner.prototype);
|
||||
|
||||
class Inner2 extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
inner2 = this;
|
||||
}
|
||||
componentWillUnmount(){}
|
||||
componentDidUnmount(){}
|
||||
componentWillMount(){}
|
||||
componentDidMount(){}
|
||||
render() {
|
||||
return h('element'+(++counter));
|
||||
}
|
||||
}
|
||||
spyAll(Inner2.prototype);
|
||||
|
||||
render(<Outer child={Inner} />, scratch);
|
||||
|
||||
// outer should only have been mounted once
|
||||
expect(Outer.prototype.componentWillMount, 'outer initial').to.have.been.calledOnce;
|
||||
expect(Outer.prototype.componentDidMount, 'outer initial').to.have.been.calledOnce;
|
||||
expect(Outer.prototype.componentWillUnmount, 'outer initial').not.to.have.been.called;
|
||||
expect(Outer.prototype.componentDidUnmount, 'outer initial').not.to.have.been.called;
|
||||
|
||||
// inner should only have been mounted once
|
||||
expect(Inner.prototype.componentWillMount, 'inner initial').to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentDidMount, 'inner initial').to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillUnmount, 'inner initial').not.to.have.been.called;
|
||||
expect(Inner.prototype.componentDidUnmount, 'inner initial').not.to.have.been.called;
|
||||
|
||||
outer.setState({ child:Inner2 });
|
||||
outer.forceUpdate();
|
||||
|
||||
expect(Inner2.prototype.render).to.have.been.calledOnce;
|
||||
|
||||
// outer should still only have been mounted once
|
||||
expect(Outer.prototype.componentWillMount, 'outer swap').to.have.been.calledOnce;
|
||||
expect(Outer.prototype.componentDidMount, 'outer swap').to.have.been.calledOnce;
|
||||
expect(Outer.prototype.componentWillUnmount, 'outer swap').not.to.have.been.called;
|
||||
expect(Outer.prototype.componentDidUnmount, 'outer swap').not.to.have.been.called;
|
||||
|
||||
// inner should only have been mounted once
|
||||
expect(Inner2.prototype.componentWillMount, 'inner2 swap').to.have.been.calledOnce;
|
||||
expect(Inner2.prototype.componentDidMount, 'inner2 swap').to.have.been.calledOnce;
|
||||
expect(Inner2.prototype.componentWillUnmount, 'inner2 swap').not.to.have.been.called;
|
||||
expect(Inner2.prototype.componentDidUnmount, 'inner2 swap').not.to.have.been.called;
|
||||
|
||||
inner2.forceUpdate();
|
||||
|
||||
expect(Inner2.prototype.render, 'inner2 update').to.have.been.calledTwice;
|
||||
expect(Inner2.prototype.componentWillMount, 'inner2 update').to.have.been.calledOnce;
|
||||
expect(Inner2.prototype.componentDidMount, 'inner2 update').to.have.been.calledOnce;
|
||||
expect(Inner2.prototype.componentWillUnmount, 'inner2 update').not.to.have.been.called;
|
||||
expect(Inner2.prototype.componentDidUnmount, 'inner2 update').not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should remount when swapping between HOC child types', () => {
|
||||
class Outer extends Component {
|
||||
render({ child: Child }) {
|
||||
return <Child />;
|
||||
}
|
||||
}
|
||||
|
||||
class Inner extends Component {
|
||||
componentWillMount() {}
|
||||
componentWillUnmount() {}
|
||||
render() {
|
||||
return <div class="inner">foo</div>;
|
||||
}
|
||||
}
|
||||
spyAll(Inner.prototype);
|
||||
|
||||
const InnerFunc = () => (
|
||||
<div class="inner-func">bar</div>
|
||||
);
|
||||
|
||||
let root = render(<Outer child={Inner} />, scratch, root);
|
||||
|
||||
expect(Inner.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillUnmount, 'initial mount').not.to.have.been.called;
|
||||
|
||||
Inner.prototype.componentWillMount.reset();
|
||||
root = render(<Outer child={InnerFunc} />, scratch, root);
|
||||
|
||||
expect(Inner.prototype.componentWillMount, 'unmount').not.to.have.been.called;
|
||||
expect(Inner.prototype.componentWillUnmount, 'unmount').to.have.been.calledOnce;
|
||||
|
||||
Inner.prototype.componentWillUnmount.reset();
|
||||
root = render(<Outer child={Inner} />, scratch, root);
|
||||
|
||||
expect(Inner.prototype.componentWillMount, 'remount').to.have.been.calledOnce;
|
||||
expect(Inner.prototype.componentWillUnmount, 'remount').not.to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component Nesting', () => {
|
||||
let useIntermediary = false;
|
||||
|
||||
let createComponent = (Intermediary) => {
|
||||
class C extends Component {
|
||||
componentWillMount() {}
|
||||
componentDidUnmount() {}
|
||||
render({ children }) {
|
||||
if (!useIntermediary) return children[0];
|
||||
let I = useIntermediary===true ? Intermediary : useIntermediary;
|
||||
return <I>{children}</I>;
|
||||
}
|
||||
}
|
||||
spyAll(C.prototype);
|
||||
return C;
|
||||
};
|
||||
|
||||
let createFunction = () => sinon.spy( ({ children }) => children[0] );
|
||||
|
||||
let root;
|
||||
let rndr = n => root = render(n, scratch, root);
|
||||
|
||||
let F1 = createFunction();
|
||||
let F2 = createFunction();
|
||||
let F3 = createFunction();
|
||||
|
||||
let C1 = createComponent(F1);
|
||||
let C2 = createComponent(F2);
|
||||
let C3 = createComponent(F3);
|
||||
|
||||
let reset = () => [C1, C2, C3].reduce(
|
||||
(acc, c) => acc.concat( Object.keys(c.prototype).map(key => c.prototype[key]) ),
|
||||
[F1, F2, F3]
|
||||
).forEach( c => c.reset && c.reset() );
|
||||
|
||||
|
||||
it('should handle lifecycle for no intermediary in component tree', () => {
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
|
||||
expect(C2.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2>Some Text</C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'unmount innermost, C1').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'unmount innermost, C2').not.to.have.been.called;
|
||||
expect(C3.prototype.componentDidUnmount, 'unmount innermost, C3').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C3>Some Text</C3></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'swap innermost').not.to.have.been.called;
|
||||
expect(C2.prototype.componentDidUnmount, 'swap innermost').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'swap innermost').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentDidUnmount, 'inject between, C1').not.to.have.been.called;
|
||||
expect(C1.prototype.componentWillMount, 'inject between, C1').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'inject between, C2').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentDidUnmount, 'inject between, C3').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'inject between, C3').to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
|
||||
it('should handle lifecycle for nested intermediary functional components', () => {
|
||||
useIntermediary = true;
|
||||
|
||||
rndr(<div />);
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary fn, C1').to.have.been.calledOnce;
|
||||
expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary fn, C2').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary fn, C3').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2>Some Text</C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C1').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C2').not.to.have.been.called;
|
||||
expect(C3.prototype.componentDidUnmount, 'unmount innermost w/ intermediary fn, C3').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C3>Some Text</C3></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary fn').not.to.have.been.called;
|
||||
expect(C2.prototype.componentDidUnmount, 'swap innermost w/ intermediary fn').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary fn').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentDidUnmount, 'inject between, C1 w/ intermediary fn').not.to.have.been.called;
|
||||
expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary fn').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary fn').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentDidUnmount, 'inject between, C3 w/ intermediary fn').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary fn').to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
|
||||
it('should handle lifecycle for nested intermediary elements', () => {
|
||||
useIntermediary = 'div';
|
||||
|
||||
rndr(<div />);
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary div, C1').to.have.been.calledOnce;
|
||||
expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary div, C2').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary div, C3').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2>Some Text</C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C1').not.to.have.been.called;
|
||||
expect(C2.prototype.componentDidUnmount, 'unmount innermost w/ intermediary div, C2 ummount').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C2').not.to.have.been.called;
|
||||
expect(C3.prototype.componentDidUnmount, 'unmount innermost w/ intermediary div, C3').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C3>Some Text</C3></C1>);
|
||||
|
||||
expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary div').not.to.have.been.called;
|
||||
expect(C2.prototype.componentDidUnmount, 'swap innermost w/ intermediary div').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary div').to.have.been.calledOnce;
|
||||
|
||||
reset();
|
||||
rndr(<C1><C2><C3>Some Text</C3></C2></C1>);
|
||||
|
||||
expect(C1.prototype.componentDidUnmount, 'inject between, C1 w/ intermediary div').not.to.have.been.called;
|
||||
expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary div').not.to.have.been.called;
|
||||
expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary div').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentDidUnmount, 'inject between, C3 w/ intermediary div').to.have.been.calledOnce;
|
||||
expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary div').to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
174
thirdparty/preact/test/browser/context.js
vendored
174
thirdparty/preact/test/browser/context.js
vendored
@ -1,174 +0,0 @@
|
||||
import { h, render, Component } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
const CHILDREN_MATCHER = sinon.match( v => v==null || Array.isArray(v) && !v.length , '[empty children]');
|
||||
|
||||
describe('context', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should pass context to grandchildren', () => {
|
||||
const CONTEXT = { a:'a' };
|
||||
const PROPS = { b:'b' };
|
||||
// let inner;
|
||||
|
||||
class Outer extends Component {
|
||||
getChildContext() {
|
||||
return CONTEXT;
|
||||
}
|
||||
render(props) {
|
||||
return <div><Inner {...props} /></div>;
|
||||
}
|
||||
}
|
||||
sinon.spy(Outer.prototype, 'getChildContext');
|
||||
|
||||
class Inner extends Component {
|
||||
// constructor() {
|
||||
// super();
|
||||
// inner = this;
|
||||
// }
|
||||
shouldComponentUpdate() { return true; }
|
||||
componentWillReceiveProps() {}
|
||||
componentWillUpdate() {}
|
||||
componentDidUpdate() {}
|
||||
render(props, state, context) {
|
||||
return <div>{ context && context.a }</div>;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, 'shouldComponentUpdate');
|
||||
sinon.spy(Inner.prototype, 'componentWillReceiveProps');
|
||||
sinon.spy(Inner.prototype, 'componentWillUpdate');
|
||||
sinon.spy(Inner.prototype, 'componentDidUpdate');
|
||||
sinon.spy(Inner.prototype, 'render');
|
||||
|
||||
render(<Outer />, scratch, scratch.lastChild);
|
||||
|
||||
expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
|
||||
|
||||
// initial render does not invoke anything but render():
|
||||
expect(Inner.prototype.render).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {}, CONTEXT);
|
||||
|
||||
CONTEXT.foo = 'bar';
|
||||
render(<Outer {...PROPS} />, scratch, scratch.lastChild);
|
||||
|
||||
expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
|
||||
|
||||
let props = { children: CHILDREN_MATCHER, ...PROPS };
|
||||
expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT);
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT);
|
||||
expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {});
|
||||
expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {});
|
||||
expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT);
|
||||
|
||||
|
||||
/* Future:
|
||||
* Newly created context objects are *not* currently cloned.
|
||||
* This test checks that they *are* cloned.
|
||||
*/
|
||||
// Inner.prototype.render.reset();
|
||||
// CONTEXT.foo = 'baz';
|
||||
// inner.forceUpdate();
|
||||
// expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, { a:'a', foo:'bar' });
|
||||
});
|
||||
|
||||
it('should pass context to direct children', () => {
|
||||
const CONTEXT = { a:'a' };
|
||||
const PROPS = { b:'b' };
|
||||
|
||||
class Outer extends Component {
|
||||
getChildContext() {
|
||||
return CONTEXT;
|
||||
}
|
||||
render(props) {
|
||||
return <Inner {...props} />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Outer.prototype, 'getChildContext');
|
||||
|
||||
class Inner extends Component {
|
||||
shouldComponentUpdate() { return true; }
|
||||
componentWillReceiveProps() {}
|
||||
componentWillUpdate() {}
|
||||
componentDidUpdate() {}
|
||||
render(props, state, context) {
|
||||
return <div>{ context && context.a }</div>;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, 'shouldComponentUpdate');
|
||||
sinon.spy(Inner.prototype, 'componentWillReceiveProps');
|
||||
sinon.spy(Inner.prototype, 'componentWillUpdate');
|
||||
sinon.spy(Inner.prototype, 'componentDidUpdate');
|
||||
sinon.spy(Inner.prototype, 'render');
|
||||
|
||||
render(<Outer />, scratch, scratch.lastChild);
|
||||
|
||||
expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
|
||||
|
||||
// initial render does not invoke anything but render():
|
||||
expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, CONTEXT);
|
||||
|
||||
CONTEXT.foo = 'bar';
|
||||
render(<Outer {...PROPS} />, scratch, scratch.lastChild);
|
||||
|
||||
expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
|
||||
|
||||
let props = { children: CHILDREN_MATCHER, ...PROPS };
|
||||
expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT);
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT);
|
||||
expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {});
|
||||
expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {});
|
||||
expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT);
|
||||
|
||||
// make sure render() could make use of context.a
|
||||
expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] }));
|
||||
});
|
||||
|
||||
it('should preserve existing context properties when creating child contexts', () => {
|
||||
let outerContext = { outer:true },
|
||||
innerContext = { inner:true };
|
||||
class Outer extends Component {
|
||||
getChildContext() {
|
||||
return { outerContext };
|
||||
}
|
||||
render() {
|
||||
return <div><Inner /></div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Inner extends Component {
|
||||
getChildContext() {
|
||||
return { innerContext };
|
||||
}
|
||||
render() {
|
||||
return <InnerMost />;
|
||||
}
|
||||
}
|
||||
|
||||
class InnerMost extends Component {
|
||||
render() {
|
||||
return <strong>test</strong>;
|
||||
}
|
||||
}
|
||||
|
||||
sinon.spy(Inner.prototype, 'render');
|
||||
sinon.spy(InnerMost.prototype, 'render');
|
||||
|
||||
render(<Outer />, scratch);
|
||||
|
||||
expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext });
|
||||
expect(InnerMost.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext, innerContext });
|
||||
});
|
||||
});
|
234
thirdparty/preact/test/browser/devtools.js
vendored
234
thirdparty/preact/test/browser/devtools.js
vendored
@ -1,234 +0,0 @@
|
||||
import { h, Component, render } from '../../src/preact';
|
||||
import { initDevTools } from '../../devtools/devtools';
|
||||
import { unmountComponent } from '../../src/vdom/component';
|
||||
|
||||
class StatefulComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {count: 0};
|
||||
}
|
||||
|
||||
render() {
|
||||
return h('span', {}, String(this.state.count));
|
||||
}
|
||||
}
|
||||
|
||||
function FunctionalComponent() {
|
||||
return h('span', {class: 'functional'}, 'Functional');
|
||||
}
|
||||
|
||||
function Label({label}) {
|
||||
return label;
|
||||
}
|
||||
|
||||
class MultiChild extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {count: props.initialCount};
|
||||
}
|
||||
|
||||
render() {
|
||||
return h('div', {}, Array(this.state.count).fill('child'));
|
||||
}
|
||||
}
|
||||
|
||||
let describe_ = describe;
|
||||
if (!('name' in Function.prototype)) {
|
||||
// Skip these tests under Internet Explorer
|
||||
describe_ = describe.skip;
|
||||
}
|
||||
|
||||
describe_('React Developer Tools integration', () => {
|
||||
let cleanup;
|
||||
let container;
|
||||
let renderer;
|
||||
|
||||
// Maps of DOM node to React*Component-like objects.
|
||||
// For composite components, there will be two instances for each node, one
|
||||
// for the composite component (instanceMap) and one for the root child DOM
|
||||
// component rendered by that component (domInstanceMap)
|
||||
let instanceMap = new Map();
|
||||
let domInstanceMap = new Map();
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
const onMount = instance => {
|
||||
if (instance._renderedChildren) {
|
||||
domInstanceMap.set(instance.node, instance);
|
||||
} else {
|
||||
instanceMap.set(instance.node, instance);
|
||||
}
|
||||
};
|
||||
|
||||
const onUnmount = instance => {
|
||||
instanceMap.delete(instance.node);
|
||||
domInstanceMap.delete(instance.node);
|
||||
};
|
||||
|
||||
global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
|
||||
inject: sinon.spy(_renderer => {
|
||||
renderer = _renderer;
|
||||
renderer.Mount._renderNewRootComponent = sinon.stub();
|
||||
renderer.Reconciler.mountComponent = sinon.spy(onMount);
|
||||
renderer.Reconciler.unmountComponent = sinon.spy(onUnmount);
|
||||
renderer.Reconciler.receiveComponent = sinon.stub();
|
||||
})
|
||||
};
|
||||
cleanup = initDevTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
container.remove();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('registers preact as a renderer with the React DevTools hook', () => {
|
||||
expect(global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject).to.be.called;
|
||||
});
|
||||
|
||||
// Basic component addition/update/removal tests
|
||||
it('notifies dev tools about new components', () => {
|
||||
render(h(StatefulComponent), container);
|
||||
expect(renderer.Reconciler.mountComponent).to.be.called;
|
||||
});
|
||||
|
||||
it('notifies dev tools about component updates', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
node._component.forceUpdate();
|
||||
expect(renderer.Reconciler.receiveComponent).to.be.called;
|
||||
});
|
||||
|
||||
it('notifies dev tools when components are removed', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
unmountComponent(node._component, true);
|
||||
expect(renderer.Reconciler.unmountComponent).to.be.called;
|
||||
});
|
||||
|
||||
// Test properties of DOM components exposed to devtools via
|
||||
// ReactDOMComponent-like instances
|
||||
it('exposes the tag name of DOM components', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
const domInstance = domInstanceMap.get(node);
|
||||
expect(domInstance._currentElement.type).to.equal('span');
|
||||
});
|
||||
|
||||
it('exposes DOM component props', () => {
|
||||
const node = render(h(FunctionalComponent), container);
|
||||
const domInstance = domInstanceMap.get(node);
|
||||
expect(domInstance._currentElement.props.class).to.equal('functional');
|
||||
});
|
||||
|
||||
it('exposes text component contents', () => {
|
||||
const node = render(h(Label, {label: 'Text content'}), container);
|
||||
const textInstance = domInstanceMap.get(node);
|
||||
expect(textInstance._stringText).to.equal('Text content');
|
||||
});
|
||||
|
||||
// Test properties of composite components exposed to devtools via
|
||||
// ReactCompositeComponent-like instances
|
||||
it('exposes the name of composite component classes', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
expect(instanceMap.get(node).getName()).to.equal('StatefulComponent');
|
||||
});
|
||||
|
||||
it('exposes composite component props', () => {
|
||||
const node = render(h(Label, {label: 'Text content'}), container);
|
||||
const instance = instanceMap.get(node);
|
||||
expect(instance._currentElement.props.label).to.equal('Text content');
|
||||
});
|
||||
|
||||
it('exposes composite component state', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
|
||||
node._component.setState({count: 42});
|
||||
node._component.forceUpdate();
|
||||
|
||||
expect(instanceMap.get(node).state).to.deep.equal({count: 42});
|
||||
});
|
||||
|
||||
// Test setting state via devtools
|
||||
it('updates component when setting state from devtools', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
|
||||
instanceMap.get(node).setState({count: 10});
|
||||
instanceMap.get(node).forceUpdate();
|
||||
|
||||
expect(node.textContent).to.equal('10');
|
||||
});
|
||||
|
||||
// Test that the original instance is exposed via `_instance` so it can
|
||||
// be accessed conveniently via `$r` in devtools
|
||||
|
||||
// Functional component handling tests
|
||||
it('wraps functional components with stateful ones', () => {
|
||||
const vnode = h(FunctionalComponent);
|
||||
expect(vnode.nodeName.prototype).to.have.property('render');
|
||||
});
|
||||
|
||||
it('exposes the name of functional components', () => {
|
||||
const node = render(h(FunctionalComponent), container);
|
||||
const instance = instanceMap.get(node);
|
||||
expect(instance.getName()).to.equal('FunctionalComponent');
|
||||
});
|
||||
|
||||
it('exposes a fallback name if the component has no useful name', () => {
|
||||
const node = render(h(() => h('div')), container);
|
||||
const instance = instanceMap.get(node);
|
||||
expect(instance.getName()).to.equal('(Function.name missing)');
|
||||
});
|
||||
|
||||
// Test handling of DOM children
|
||||
it('notifies dev tools about DOM children', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
const domInstance = domInstanceMap.get(node);
|
||||
expect(renderer.Reconciler.mountComponent).to.have.been.calledWith(domInstance);
|
||||
});
|
||||
|
||||
it('notifies dev tools when a component update adds DOM children', () => {
|
||||
const node = render(h(MultiChild, {initialCount: 2}), container);
|
||||
|
||||
node._component.setState({count: 4});
|
||||
node._component.forceUpdate();
|
||||
|
||||
expect(renderer.Reconciler.mountComponent).to.have.been.called.twice;
|
||||
});
|
||||
|
||||
it('notifies dev tools when a component update modifies DOM children', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
|
||||
instanceMap.get(node).setState({count: 10});
|
||||
instanceMap.get(node).forceUpdate();
|
||||
|
||||
const textInstance = domInstanceMap.get(node.childNodes[0]);
|
||||
expect(textInstance._stringText).to.equal('10');
|
||||
});
|
||||
|
||||
it('notifies dev tools when a component update removes DOM children', () => {
|
||||
const node = render(h(MultiChild, {initialCount: 1}), container);
|
||||
|
||||
node._component.setState({count: 0});
|
||||
node._component.forceUpdate();
|
||||
|
||||
expect(renderer.Reconciler.unmountComponent).to.be.called;
|
||||
});
|
||||
|
||||
// Root component info
|
||||
it('exposes root components on the _instancesByReactRootID map', () => {
|
||||
render(h(StatefulComponent), container);
|
||||
expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1);
|
||||
});
|
||||
|
||||
it('notifies dev tools when new root components are mounted', () => {
|
||||
render(h(StatefulComponent), container);
|
||||
expect(renderer.Mount._renderNewRootComponent).to.be.called;
|
||||
});
|
||||
|
||||
it('removes root components when they are unmounted', () => {
|
||||
const node = render(h(StatefulComponent), container);
|
||||
unmountComponent(node._component, true);
|
||||
expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(0);
|
||||
});
|
||||
});
|
85
thirdparty/preact/test/browser/keys.js
vendored
85
thirdparty/preact/test/browser/keys.js
vendored
@ -1,85 +0,0 @@
|
||||
import { h, Component, render } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
describe('keys', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
// See developit/preact-compat#21
|
||||
it('should remove orphaned keyed nodes', () => {
|
||||
let root = render((
|
||||
<div>
|
||||
<div>1</div>
|
||||
<li key="a">a</li>
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
root = render((
|
||||
<div>
|
||||
<div>2</div>
|
||||
<li key="b">b</li>
|
||||
</div>
|
||||
), scratch, root);
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div><div>2</div><li>b</li></div>');
|
||||
});
|
||||
|
||||
it('should set VNode#key property', () => {
|
||||
expect(<div />).to.have.property('key').that.is.empty;
|
||||
expect(<div a="a" />).to.have.property('key').that.is.empty;
|
||||
expect(<div key="1" />).to.have.property('key', '1');
|
||||
});
|
||||
|
||||
it('should remove keyed nodes (#232)', () => {
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
setTimeout(() => this.setState({opened: true,loading: true}), 10);
|
||||
setTimeout(() => this.setState({opened: true,loading: false}), 20);
|
||||
}
|
||||
|
||||
render({ opened, loading }) {
|
||||
return (
|
||||
<BusyIndicator id="app" busy={loading}>
|
||||
<div>This div needs to be here for this to break</div>
|
||||
{ opened && !loading && <div>{[]}</div> }
|
||||
</BusyIndicator>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BusyIndicator extends Component {
|
||||
render({ children, busy }) {
|
||||
return <div class={busy ? "busy" : ""}>
|
||||
{ children && children.length ? children : <div class="busy-placeholder"></div> }
|
||||
<div class="indicator">
|
||||
<div>indicator</div>
|
||||
<div>indicator</div>
|
||||
<div>indicator</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
let root;
|
||||
|
||||
root = render(<App />, scratch, root);
|
||||
root = render(<App opened loading />, scratch, root);
|
||||
root = render(<App opened />, scratch, root);
|
||||
|
||||
let html = String(root.innerHTML).replace(/ class=""/g, '');
|
||||
expect(html).to.equal('<div>This div needs to be here for this to break</div><div></div><div class="indicator"><div>indicator</div><div>indicator</div><div>indicator</div></div>');
|
||||
});
|
||||
});
|
495
thirdparty/preact/test/browser/lifecycle.js
vendored
495
thirdparty/preact/test/browser/lifecycle.js
vendored
@ -1,495 +0,0 @@
|
||||
import { h, render, rerender, Component } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) );
|
||||
|
||||
const EMPTY_CHILDREN = [];
|
||||
|
||||
describe('Lifecycle methods', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
|
||||
describe('#componentWillUpdate', () => {
|
||||
it('should NOT be called on initial render', () => {
|
||||
class ReceivePropsComponent extends Component {
|
||||
componentWillUpdate() {}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate');
|
||||
render(<ReceivePropsComponent />, scratch);
|
||||
expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be called when rerender with new props from parent', () => {
|
||||
let doRender;
|
||||
class Outer extends Component {
|
||||
constructor(p, c) {
|
||||
super(p, c);
|
||||
this.state = { i: 0 };
|
||||
}
|
||||
componentDidMount() {
|
||||
doRender = () => this.setState({ i: this.state.i + 1 });
|
||||
}
|
||||
render(props, { i }) {
|
||||
return <Inner i={i} {...props} />;
|
||||
}
|
||||
}
|
||||
class Inner extends Component {
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
expect(nextProps).to.be.deep.equal({ children:EMPTY_CHILDREN, i: 1 });
|
||||
expect(nextState).to.be.deep.equal({});
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, 'componentWillUpdate');
|
||||
sinon.spy(Outer.prototype, 'componentDidMount');
|
||||
|
||||
// Initial render
|
||||
render(<Outer />, scratch);
|
||||
expect(Inner.prototype.componentWillUpdate).not.to.have.been.called;
|
||||
|
||||
// Rerender inner with new props
|
||||
doRender();
|
||||
rerender();
|
||||
expect(Inner.prototype.componentWillUpdate).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be called on new state', () => {
|
||||
let doRender;
|
||||
class ReceivePropsComponent extends Component {
|
||||
componentWillUpdate() {}
|
||||
componentDidMount() {
|
||||
doRender = () => this.setState({ i: this.state.i + 1 });
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate');
|
||||
render(<ReceivePropsComponent />, scratch);
|
||||
expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have.been.called;
|
||||
|
||||
doRender();
|
||||
rerender();
|
||||
expect(ReceivePropsComponent.prototype.componentWillUpdate).to.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#componentWillReceiveProps', () => {
|
||||
it('should NOT be called on initial render', () => {
|
||||
class ReceivePropsComponent extends Component {
|
||||
componentWillReceiveProps() {}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(ReceivePropsComponent.prototype, 'componentWillReceiveProps');
|
||||
render(<ReceivePropsComponent />, scratch);
|
||||
expect(ReceivePropsComponent.prototype.componentWillReceiveProps).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be called when rerender with new props from parent', () => {
|
||||
let doRender;
|
||||
class Outer extends Component {
|
||||
constructor(p, c) {
|
||||
super(p, c);
|
||||
this.state = { i: 0 };
|
||||
}
|
||||
componentDidMount() {
|
||||
doRender = () => this.setState({ i: this.state.i + 1 });
|
||||
}
|
||||
render(props, { i }) {
|
||||
return <Inner i={i} {...props} />;
|
||||
}
|
||||
}
|
||||
class Inner extends Component {
|
||||
componentWillMount() {
|
||||
expect(this.props.i).to.be.equal(0);
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
expect(nextProps.i).to.be.equal(1);
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, 'componentWillReceiveProps');
|
||||
sinon.spy(Outer.prototype, 'componentDidMount');
|
||||
|
||||
// Initial render
|
||||
render(<Outer />, scratch);
|
||||
expect(Inner.prototype.componentWillReceiveProps).not.to.have.been.called;
|
||||
|
||||
// Rerender inner with new props
|
||||
doRender();
|
||||
rerender();
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be called in right execution order', () => {
|
||||
let doRender;
|
||||
class Outer extends Component {
|
||||
constructor(p, c) {
|
||||
super(p, c);
|
||||
this.state = { i: 0 };
|
||||
}
|
||||
componentDidMount() {
|
||||
doRender = () => this.setState({ i: this.state.i + 1 });
|
||||
}
|
||||
render(props, { i }) {
|
||||
return <Inner i={i} {...props} />;
|
||||
}
|
||||
}
|
||||
class Inner extends Component {
|
||||
componentDidUpdate() {
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
|
||||
expect(Inner.prototype.componentWillUpdate).to.have.been.called;
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
expect(Inner.prototype.componentWillUpdate).not.to.have.been.called;
|
||||
expect(Inner.prototype.componentDidUpdate).not.to.have.been.called;
|
||||
}
|
||||
componentWillUpdate() {
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
|
||||
expect(Inner.prototype.componentDidUpdate).not.to.have.been.called;
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
sinon.spy(Inner.prototype, 'componentWillReceiveProps');
|
||||
sinon.spy(Inner.prototype, 'componentDidUpdate');
|
||||
sinon.spy(Inner.prototype, 'componentWillUpdate');
|
||||
sinon.spy(Outer.prototype, 'componentDidMount');
|
||||
|
||||
render(<Outer />, scratch);
|
||||
doRender();
|
||||
rerender();
|
||||
|
||||
expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledBefore(Inner.prototype.componentWillUpdate);
|
||||
expect(Inner.prototype.componentWillUpdate).to.have.been.calledBefore(Inner.prototype.componentDidUpdate);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
let _it = it;
|
||||
describe('#constructor and component(Did|Will)(Mount|Unmount)', () => {
|
||||
/* global DISABLE_FLAKEY */
|
||||
let it = DISABLE_FLAKEY ? xit : _it;
|
||||
|
||||
let setState;
|
||||
class Outer extends Component {
|
||||
constructor(p, c) {
|
||||
super(p, c);
|
||||
this.state = { show:true };
|
||||
setState = s => this.setState(s);
|
||||
}
|
||||
render(props, { show }) {
|
||||
return (
|
||||
<div>
|
||||
{ show && (
|
||||
<Inner {...props} />
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LifecycleTestComponent extends Component {
|
||||
constructor(p, c) { super(p, c); this._constructor(); }
|
||||
_constructor() {}
|
||||
componentWillMount() {}
|
||||
componentDidMount() {}
|
||||
componentWillUnmount() {}
|
||||
componentDidUnmount() {}
|
||||
render() { return <div />; }
|
||||
}
|
||||
|
||||
class Inner extends LifecycleTestComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<InnerMost />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InnerMost extends LifecycleTestComponent {
|
||||
render() { return <div />; }
|
||||
}
|
||||
|
||||
let spies = ['_constructor', 'componentWillMount', 'componentDidMount', 'componentWillUnmount', 'componentDidUnmount'];
|
||||
|
||||
let verifyLifycycleMethods = (TestComponent) => {
|
||||
let proto = TestComponent.prototype;
|
||||
spies.forEach( s => sinon.spy(proto, s) );
|
||||
let reset = () => spies.forEach( s => proto[s].reset() );
|
||||
|
||||
it('should be invoked for components on initial render', () => {
|
||||
reset();
|
||||
render(<Outer />, scratch);
|
||||
expect(proto._constructor).to.have.been.called;
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be invoked for components on unmount', () => {
|
||||
reset();
|
||||
setState({ show:false });
|
||||
rerender();
|
||||
|
||||
expect(proto.componentDidUnmount).to.have.been.called;
|
||||
expect(proto.componentWillUnmount).to.have.been.calledBefore(proto.componentDidUnmount);
|
||||
expect(proto.componentDidUnmount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be invoked for components on re-render', () => {
|
||||
reset();
|
||||
setState({ show:true });
|
||||
rerender();
|
||||
|
||||
expect(proto._constructor).to.have.been.called;
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
});
|
||||
};
|
||||
|
||||
describe('inner components', () => {
|
||||
verifyLifycycleMethods(Inner);
|
||||
});
|
||||
|
||||
describe('innermost components', () => {
|
||||
verifyLifycycleMethods(InnerMost);
|
||||
});
|
||||
|
||||
describe('when shouldComponentUpdate() returns false', () => {
|
||||
let setState;
|
||||
|
||||
class Outer extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { show:true };
|
||||
setState = s => this.setState(s);
|
||||
}
|
||||
render(props, { show }) {
|
||||
return (
|
||||
<div>
|
||||
{ show && (
|
||||
<div>
|
||||
<Inner {...props} />
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Inner extends Component {
|
||||
shouldComponentUpdate(){ return false; }
|
||||
componentWillMount() {}
|
||||
componentDidMount() {}
|
||||
componentWillUnmount() {}
|
||||
componentDidUnmount() {}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
let proto = Inner.prototype;
|
||||
let spies = ['componentWillMount', 'componentDidMount', 'componentWillUnmount', 'componentDidUnmount'];
|
||||
spies.forEach( s => sinon.spy(proto, s) );
|
||||
|
||||
let reset = () => spies.forEach( s => proto[s].reset() );
|
||||
|
||||
beforeEach( () => reset() );
|
||||
|
||||
it('should be invoke normally on initial mount', () => {
|
||||
render(<Outer />, scratch);
|
||||
expect(proto.componentWillMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should be invoked normally on unmount', () => {
|
||||
setState({ show:false });
|
||||
rerender();
|
||||
|
||||
expect(proto.componentWillUnmount).to.have.been.called;
|
||||
expect(proto.componentWillUnmount).to.have.been.calledBefore(proto.componentDidUnmount);
|
||||
expect(proto.componentDidUnmount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should still invoke mount for shouldComponentUpdate():false', () => {
|
||||
setState({ show:true });
|
||||
rerender();
|
||||
|
||||
expect(proto.componentWillMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should still invoke unmount for shouldComponentUpdate():false', () => {
|
||||
setState({ show:false });
|
||||
rerender();
|
||||
|
||||
expect(proto.componentWillUnmount).to.have.been.called;
|
||||
expect(proto.componentWillUnmount).to.have.been.calledBefore(proto.componentDidUnmount);
|
||||
expect(proto.componentDidUnmount).to.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Lifecycle DOM Timing', () => {
|
||||
it('should be invoked when dom does (DidMount, WillUnmount) or does not (WillMount, DidUnmount) exist', () => {
|
||||
let setState;
|
||||
class Outer extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { show:true };
|
||||
setState = s => {
|
||||
this.setState(s);
|
||||
this.forceUpdate();
|
||||
};
|
||||
}
|
||||
componentWillMount() {
|
||||
expect(document.getElementById('OuterDiv'), 'Outer componentWillMount').to.not.exist;
|
||||
}
|
||||
componentDidMount() {
|
||||
expect(document.getElementById('OuterDiv'), 'Outer componentDidMount').to.exist;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
expect(document.getElementById('OuterDiv'), 'Outer componentWillUnmount').to.exist;
|
||||
}
|
||||
componentDidUnmount() {
|
||||
expect(document.getElementById('OuterDiv'), 'Outer componentDidUnmount').to.not.exist;
|
||||
}
|
||||
render(props, { show }) {
|
||||
return (
|
||||
<div id="OuterDiv">
|
||||
{ show && (
|
||||
<div>
|
||||
<Inner {...props} />
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Inner extends Component {
|
||||
componentWillMount() {
|
||||
expect(document.getElementById('InnerDiv'), 'Inner componentWillMount').to.not.exist;
|
||||
}
|
||||
componentDidMount() {
|
||||
expect(document.getElementById('InnerDiv'), 'Inner componentDidMount').to.exist;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
// @TODO Component mounted into elements (non-components)
|
||||
// are currently unmounted after those elements, so their
|
||||
// DOM is unmounted prior to the method being called.
|
||||
//expect(document.getElementById('InnerDiv'), 'Inner componentWillUnmount').to.exist;
|
||||
}
|
||||
componentDidUnmount() {
|
||||
expect(document.getElementById('InnerDiv'), 'Inner componentDidUnmount').to.not.exist;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div id="InnerDiv" />;
|
||||
}
|
||||
}
|
||||
|
||||
let proto = Inner.prototype;
|
||||
let spies = ['componentWillMount', 'componentDidMount', 'componentWillUnmount', 'componentDidUnmount'];
|
||||
spies.forEach( s => sinon.spy(proto, s) );
|
||||
|
||||
let reset = () => spies.forEach( s => proto[s].reset() );
|
||||
|
||||
render(<Outer />, scratch);
|
||||
expect(proto.componentWillMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
|
||||
reset();
|
||||
setState({ show:false });
|
||||
|
||||
expect(proto.componentWillUnmount).to.have.been.called;
|
||||
expect(proto.componentWillUnmount).to.have.been.calledBefore(proto.componentDidUnmount);
|
||||
expect(proto.componentDidUnmount).to.have.been.called;
|
||||
|
||||
reset();
|
||||
setState({ show:true });
|
||||
|
||||
expect(proto.componentWillMount).to.have.been.called;
|
||||
expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount);
|
||||
expect(proto.componentDidMount).to.have.been.called;
|
||||
});
|
||||
|
||||
it('should remove this.base for HOC', () => {
|
||||
let createComponent = (name, fn) => {
|
||||
class C extends Component {
|
||||
componentWillUnmount() {
|
||||
expect(this.base, `${name}.componentWillUnmount`).to.exist;
|
||||
}
|
||||
componentDidUnmount() {
|
||||
expect(this.base, `${name}.componentDidUnmount`).not.to.exist;
|
||||
}
|
||||
render(props) { return fn(props); }
|
||||
}
|
||||
spyAll(C.prototype);
|
||||
return C;
|
||||
};
|
||||
|
||||
class Wrapper extends Component {
|
||||
render({ children }) {
|
||||
return <div class="wrapper">{children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
let One = createComponent('One', () => <Wrapper>one</Wrapper> );
|
||||
let Two = createComponent('Two', () => <Wrapper>two</Wrapper> );
|
||||
let Three = createComponent('Three', () => <Wrapper>three</Wrapper> );
|
||||
|
||||
let components = [One, Two, Three];
|
||||
|
||||
let Selector = createComponent('Selector', ({ page }) => {
|
||||
let Child = components[page];
|
||||
return <Child />;
|
||||
});
|
||||
|
||||
class App extends Component {
|
||||
render(_, { page }) {
|
||||
return <Selector page={page} />;
|
||||
}
|
||||
}
|
||||
|
||||
let app;
|
||||
render(<App ref={ c => app=c } />, scratch);
|
||||
|
||||
for (let i=0; i<20; i++) {
|
||||
app.setState({ page: i%components.length });
|
||||
app.forceUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
110
thirdparty/preact/test/browser/linked-state.js
vendored
110
thirdparty/preact/test/browser/linked-state.js
vendored
@ -1,110 +0,0 @@
|
||||
import { Component } from '../../src/preact';
|
||||
import { createLinkedState } from '../../src/linked-state';
|
||||
|
||||
describe('linked-state', () => {
|
||||
class TestComponent extends Component { }
|
||||
let testComponent, linkFunction;
|
||||
|
||||
before( () => {
|
||||
testComponent = new TestComponent();
|
||||
sinon.spy(TestComponent.prototype, 'setState');
|
||||
});
|
||||
|
||||
describe('createLinkedState without eventPath argument', () => {
|
||||
|
||||
before( () => {
|
||||
linkFunction = createLinkedState(testComponent,'testStateKey');
|
||||
expect(linkFunction).to.be.a('function');
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
TestComponent.prototype['setState'].reset();
|
||||
});
|
||||
|
||||
it('should use value attribute on text input when no eventPath is supplied', () => {
|
||||
let element = document.createElement('input');
|
||||
element.type= 'text';
|
||||
element.value = 'newValue';
|
||||
|
||||
linkFunction({
|
||||
currentTarget: element,
|
||||
target: element
|
||||
});
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledOnce;
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': 'newValue'});
|
||||
|
||||
linkFunction.call(element);
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledTwice;
|
||||
expect(TestComponent.prototype.setState.secondCall).to.have.been.calledWith({'testStateKey': 'newValue'});
|
||||
});
|
||||
|
||||
it('should use checked attribute on checkbox input when no eventPath is supplied', () => {
|
||||
let checkboxElement = document.createElement('input');
|
||||
checkboxElement.type= 'checkbox';
|
||||
checkboxElement.checked = true;
|
||||
|
||||
linkFunction({
|
||||
currentTarget: checkboxElement,
|
||||
target: checkboxElement
|
||||
});
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledOnce;
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': true});
|
||||
});
|
||||
|
||||
it('should use checked attribute on radio input when no eventPath is supplied', () => {
|
||||
let radioElement = document.createElement('input');
|
||||
radioElement.type= 'radio';
|
||||
radioElement.checked = true;
|
||||
|
||||
linkFunction({
|
||||
currentTarget: radioElement,
|
||||
target: radioElement
|
||||
});
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledOnce;
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': true});
|
||||
});
|
||||
|
||||
|
||||
it('should set dot notated state key appropriately', () => {
|
||||
linkFunction = createLinkedState(testComponent,'nested.state.key');
|
||||
let element = document.createElement('input');
|
||||
element.type= 'text';
|
||||
element.value = 'newValue';
|
||||
|
||||
linkFunction({
|
||||
currentTarget: element,
|
||||
target: element
|
||||
});
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledOnce;
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledWith({nested: {state: {key: 'newValue'}}});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createLinkedState with eventPath argument', () => {
|
||||
|
||||
before( () => {
|
||||
linkFunction = createLinkedState(testComponent,'testStateKey', 'nested.path');
|
||||
expect(linkFunction).to.be.a('function');
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
TestComponent.prototype['setState'].reset();
|
||||
});
|
||||
|
||||
it('should give precedence to nested.path on event over nested.path on component', () => {
|
||||
let event = {nested: {path: 'nestedPathValueFromEvent'}};
|
||||
let component = {_component: {nested: {path: 'nestedPathValueFromComponent'}}};
|
||||
|
||||
linkFunction.call(component, event);
|
||||
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledOnce;
|
||||
expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': 'nestedPathValueFromEvent'});
|
||||
});
|
||||
});
|
||||
});
|
245
thirdparty/preact/test/browser/performance.js
vendored
245
thirdparty/preact/test/browser/performance.js
vendored
@ -1,245 +0,0 @@
|
||||
/*global coverage, ENABLE_PERFORMANCE, NODE_ENV*/
|
||||
/*eslint no-console:0*/
|
||||
/** @jsx h */
|
||||
|
||||
let { h, Component, render } = require(NODE_ENV==='production' ? '../../dist/preact.min.js' : '../../src/preact');
|
||||
|
||||
const MULTIPLIER = ENABLE_PERFORMANCE ? (coverage ? 5 : 1) : 999999;
|
||||
|
||||
|
||||
let now = typeof performance!=='undefined' && performance.now ? () => performance.now() : () => +new Date();
|
||||
|
||||
function loop(iter, time) {
|
||||
let start = now(),
|
||||
count = 0;
|
||||
while ( now()-start < time ) {
|
||||
count++;
|
||||
iter();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
function benchmark(iter, callback) {
|
||||
let a = 0;
|
||||
function noop() {
|
||||
try { a++; } finally { a += Math.random(); }
|
||||
}
|
||||
|
||||
// warm
|
||||
for (let i=3; i--; ) noop(), iter();
|
||||
|
||||
let count = 5,
|
||||
time = 200,
|
||||
passes = 0,
|
||||
noops = loop(noop, time),
|
||||
iterations = 0;
|
||||
|
||||
function next() {
|
||||
iterations += loop(iter, time);
|
||||
setTimeout(++passes===count ? done : next, 10);
|
||||
}
|
||||
|
||||
function done() {
|
||||
let ticks = Math.round(noops / iterations * count),
|
||||
hz = iterations / count / time * 1000,
|
||||
message = `${hz|0}/s (${ticks} ticks)`;
|
||||
callback({ iterations, noops, count, time, ticks, hz, message });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
|
||||
describe('performance', function() {
|
||||
let scratch;
|
||||
|
||||
this.timeout(10000);
|
||||
|
||||
before( () => {
|
||||
if (coverage) {
|
||||
console.warn('WARNING: Code coverage is enabled, which dramatically reduces performance. Do not pay attention to these numbers.');
|
||||
}
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should rerender without changes fast', done => {
|
||||
let jsx = (
|
||||
<div class="foo bar" data-foo="bar" p={2}>
|
||||
<header>
|
||||
<h1 class="asdf">a {'b'} c {0} d</h1>
|
||||
<nav>
|
||||
<a href="/foo">Foo</a>
|
||||
<a href="/bar">Bar</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<form onSubmit={()=>{}}>
|
||||
<input type="checkbox" checked={true} />
|
||||
<input type="checkbox" checked={false} />
|
||||
<fieldset>
|
||||
<label><input type="radio" checked /></label>
|
||||
<label><input type="radio" /></label>
|
||||
</fieldset>
|
||||
<button-bar>
|
||||
<button style="width:10px; height:10px; border:1px solid #FFF;">Normal CSS</button>
|
||||
<button style="top:0 ; right: 20">Poor CSS</button>
|
||||
<button style="invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;" icon>Poorer CSS</button>
|
||||
<button style={{ margin:0, padding:'10px', overflow:'visible' }}>Object CSS</button>
|
||||
</button-bar>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
let root;
|
||||
benchmark( () => {
|
||||
root = render(jsx, scratch, root);
|
||||
}, ({ ticks, message }) => {
|
||||
console.log(`PERF: empty diff: ${message}`);
|
||||
expect(ticks).to.be.below(350 * MULTIPLIER);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should rerender repeated trees fast', done => {
|
||||
class Header extends Component {
|
||||
render() {
|
||||
return (
|
||||
<header>
|
||||
<h1 class="asdf">a {'b'} c {0} d</h1>
|
||||
<nav>
|
||||
<a href="/foo">Foo</a>
|
||||
<a href="/bar">Bar</a>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
class Form extends Component {
|
||||
render() {
|
||||
return (
|
||||
<form onSubmit={()=>{}}>
|
||||
<input type="checkbox" checked={true} />
|
||||
<input type="checkbox" checked={false} />
|
||||
<fieldset>
|
||||
<label><input type="radio" checked /></label>
|
||||
<label><input type="radio" /></label>
|
||||
</fieldset>
|
||||
<ButtonBar />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
class ButtonBar extends Component {
|
||||
render() {
|
||||
return (
|
||||
<button-bar>
|
||||
<Button style="width:10px; height:10px; border:1px solid #FFF;">Normal CSS</Button>
|
||||
<Button style="top:0 ; right: 20">Poor CSS</Button>
|
||||
<Button style="invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;" icon>Poorer CSS</Button>
|
||||
<Button style={{ margin:0, padding:'10px', overflow:'visible' }}>Object CSS</Button>
|
||||
</button-bar>
|
||||
);
|
||||
}
|
||||
}
|
||||
class Button extends Component {
|
||||
render(props) {
|
||||
return <button {...props} />;
|
||||
}
|
||||
}
|
||||
class Main extends Component {
|
||||
render() {
|
||||
return <Form />;
|
||||
}
|
||||
}
|
||||
class Root extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div class="foo bar" data-foo="bar" p={2}>
|
||||
<Header />
|
||||
<Main />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
class Empty extends Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
class Parent extends Component {
|
||||
render({ child:C }) {
|
||||
return <C />;
|
||||
}
|
||||
}
|
||||
|
||||
let root;
|
||||
benchmark( () => {
|
||||
root = render(<Parent child={Root} />, scratch, root);
|
||||
root = render(<Parent child={Empty} />, scratch, root);
|
||||
}, ({ ticks, message }) => {
|
||||
console.log(`PERF: repeat diff: ${message}`);
|
||||
expect(ticks).to.be.below(2000 * MULTIPLIER);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should construct large VDOM trees fast', done => {
|
||||
const FIELDS = [];
|
||||
for (let i=100; i--; ) FIELDS.push((i*999).toString(36));
|
||||
|
||||
let out = [];
|
||||
function digest(vnode) {
|
||||
out.push(vnode);
|
||||
out.length = 0;
|
||||
}
|
||||
benchmark( () => {
|
||||
digest(
|
||||
<div class="foo bar" data-foo="bar" p={2}>
|
||||
<header>
|
||||
<h1 class="asdf">a {'b'} c {0} d</h1>
|
||||
<nav>
|
||||
<a href="/foo">Foo</a>
|
||||
<a href="/bar">Bar</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<form onSubmit={()=>{}}>
|
||||
<input type="checkbox" checked />
|
||||
<input type="checkbox" />
|
||||
<fieldset>
|
||||
{ FIELDS.map( field => (
|
||||
<label>
|
||||
{field}:
|
||||
<input placeholder={field} />
|
||||
</label>
|
||||
)) }
|
||||
</fieldset>
|
||||
<button-bar>
|
||||
<button style="width:10px; height:10px; border:1px solid #FFF;">Normal CSS</button>
|
||||
<button style="top:0 ; right: 20">Poor CSS</button>
|
||||
<button style="invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;" icon>Poorer CSS</button>
|
||||
<button style={{ margin:0, padding:'10px', overflow:'visible' }}>Object CSS</button>
|
||||
</button-bar>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}, ({ ticks, message }) => {
|
||||
console.log(`PERF: large VTree: ${message}`);
|
||||
expect(ticks).to.be.below(2000 * MULTIPLIER);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
305
thirdparty/preact/test/browser/refs.js
vendored
305
thirdparty/preact/test/browser/refs.js
vendored
@ -1,305 +0,0 @@
|
||||
import { h, render, Component } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
// gives call count and argument errors names (otherwise sinon just uses "spy"):
|
||||
let spy = (name, ...args) => {
|
||||
let spy = sinon.spy(...args);
|
||||
spy.displayName = `spy('${name}')`;
|
||||
return spy;
|
||||
};
|
||||
|
||||
describe('refs', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should invoke refs in render()', () => {
|
||||
let ref = spy('ref');
|
||||
render(<div ref={ref} />, scratch);
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);
|
||||
});
|
||||
|
||||
it('should invoke refs in Component.render()', () => {
|
||||
let outer = spy('outer'),
|
||||
inner = spy('inner');
|
||||
class Foo extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div ref={outer}>
|
||||
<span ref={inner} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
render(<Foo />, scratch);
|
||||
|
||||
expect(outer).to.have.been.calledWith(scratch.firstChild);
|
||||
expect(inner).to.have.been.calledWith(scratch.firstChild.firstChild);
|
||||
});
|
||||
|
||||
it('should pass components to ref functions', () => {
|
||||
let ref = spy('ref'),
|
||||
instance;
|
||||
class Foo extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
instance = this;
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
render(<Foo ref={ref} />, scratch);
|
||||
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(instance);
|
||||
});
|
||||
|
||||
it('should pass rendered DOM from functional components to ref functions', () => {
|
||||
let ref = spy('ref');
|
||||
|
||||
const Foo = () => <div />;
|
||||
|
||||
let root = render(<Foo ref={ref} />, scratch);
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);
|
||||
|
||||
ref.reset();
|
||||
render(<Foo ref={ref} />, scratch, root);
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);
|
||||
|
||||
ref.reset();
|
||||
render(<span />, scratch, root);
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(null);
|
||||
});
|
||||
|
||||
it('should pass children to ref functions', () => {
|
||||
let outer = spy('outer'),
|
||||
inner = spy('inner'),
|
||||
rerender, inst;
|
||||
class Outer extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
rerender = () => this.forceUpdate();
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Inner ref={outer} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
class Inner extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
inst = this;
|
||||
}
|
||||
render() {
|
||||
return <span ref={inner} />;
|
||||
}
|
||||
}
|
||||
|
||||
let root = render(<Outer />, scratch);
|
||||
|
||||
expect(outer).to.have.been.calledOnce.and.calledWith(inst);
|
||||
expect(inner).to.have.been.calledOnce.and.calledWith(inst.base);
|
||||
|
||||
outer.reset();
|
||||
inner.reset();
|
||||
|
||||
rerender();
|
||||
|
||||
expect(outer).to.have.been.calledOnce.and.calledWith(inst);
|
||||
expect(inner).to.have.been.calledOnce.and.calledWith(inst.base);
|
||||
|
||||
outer.reset();
|
||||
inner.reset();
|
||||
|
||||
render(<div />, scratch, root);
|
||||
|
||||
expect(outer).to.have.been.calledOnce.and.calledWith(null);
|
||||
expect(inner).to.have.been.calledOnce.and.calledWith(null);
|
||||
});
|
||||
|
||||
it('should pass high-order children to ref functions', () => {
|
||||
let outer = spy('outer'),
|
||||
inner = spy('inner'),
|
||||
innermost = spy('innermost'),
|
||||
outerInst,
|
||||
innerInst;
|
||||
class Outer extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
outerInst = this;
|
||||
}
|
||||
render() {
|
||||
return <Inner ref={inner} />;
|
||||
}
|
||||
}
|
||||
class Inner extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
innerInst = this;
|
||||
}
|
||||
render() {
|
||||
return <span ref={innermost} />;
|
||||
}
|
||||
}
|
||||
|
||||
let root = render(<Outer ref={outer} />, scratch);
|
||||
|
||||
expect(outer, 'outer initial').to.have.been.calledOnce.and.calledWith(outerInst);
|
||||
expect(inner, 'inner initial').to.have.been.calledOnce.and.calledWith(innerInst);
|
||||
expect(innermost, 'innerMost initial').to.have.been.calledOnce.and.calledWith(innerInst.base);
|
||||
|
||||
outer.reset();
|
||||
inner.reset();
|
||||
innermost.reset();
|
||||
root = render(<Outer ref={outer} />, scratch, root);
|
||||
|
||||
expect(outer, 'outer update').to.have.been.calledOnce.and.calledWith(outerInst);
|
||||
expect(inner, 'inner update').to.have.been.calledOnce.and.calledWith(innerInst);
|
||||
expect(innermost, 'innerMost update').to.have.been.calledOnce.and.calledWith(innerInst.base);
|
||||
|
||||
outer.reset();
|
||||
inner.reset();
|
||||
innermost.reset();
|
||||
root = render(<div />, scratch, root);
|
||||
|
||||
expect(outer, 'outer unmount').to.have.been.calledOnce.and.calledWith(null);
|
||||
expect(inner, 'inner unmount').to.have.been.calledOnce.and.calledWith(null);
|
||||
expect(innermost, 'innerMost unmount').to.have.been.calledOnce.and.calledWith(null);
|
||||
});
|
||||
|
||||
it('should not pass ref into component as a prop', () => {
|
||||
let foo = spy('foo'),
|
||||
bar = spy('bar');
|
||||
|
||||
class Foo extends Component {
|
||||
render(){ return <div />; }
|
||||
}
|
||||
const Bar = spy('Bar', () => <div />);
|
||||
|
||||
sinon.spy(Foo.prototype, 'render');
|
||||
|
||||
render((
|
||||
<div>
|
||||
<Foo ref={foo} a="a" />
|
||||
<Bar ref={bar} b="b" />
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
expect(Foo.prototype.render).to.have.been.calledWithMatch({ ref:sinon.match.falsy, a:'a' }, { }, { });
|
||||
expect(Bar).to.have.been.calledWithMatch({ b:'b', ref:bar }, { });
|
||||
});
|
||||
|
||||
// Test for #232
|
||||
it('should only null refs after unmount', () => {
|
||||
let root, outer, inner;
|
||||
|
||||
class TestUnmount extends Component {
|
||||
componentWillUnmount() {
|
||||
expect(this).to.have.property('outer', outer);
|
||||
expect(this).to.have.property('inner', inner);
|
||||
}
|
||||
|
||||
componentDidUnmount() {
|
||||
expect(this).to.have.property('outer', null);
|
||||
expect(this).to.have.property('inner', null);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="outer" ref={ c => this.outer=c }>
|
||||
<div id="inner" ref={ c => this.inner=c } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sinon.spy(TestUnmount.prototype, 'componentWillUnmount');
|
||||
sinon.spy(TestUnmount.prototype, 'componentDidUnmount');
|
||||
|
||||
root = render(<div><TestUnmount /></div>, scratch, root);
|
||||
outer = scratch.querySelector('#outer');
|
||||
inner = scratch.querySelector('#inner');
|
||||
|
||||
expect(TestUnmount.prototype.componentWillUnmount).not.to.have.been.called;
|
||||
expect(TestUnmount.prototype.componentDidUnmount).not.to.have.been.called;
|
||||
|
||||
root = render(<div />, scratch, root);
|
||||
|
||||
expect(TestUnmount.prototype.componentWillUnmount).to.have.been.calledOnce;
|
||||
expect(TestUnmount.prototype.componentDidUnmount).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should null and re-invoke refs when swapping component root element type', () => {
|
||||
let inst;
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return <div><Child /></div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = { show:false };
|
||||
inst = this;
|
||||
}
|
||||
handleMount(){}
|
||||
render(_, { show }) {
|
||||
if (!show) return <div id="div" ref={this.handleMount}></div>;
|
||||
return <span id="span" ref={this.handleMount}>some test content</span>;
|
||||
}
|
||||
}
|
||||
sinon.spy(Child.prototype, 'handleMount');
|
||||
|
||||
render(<App />, scratch);
|
||||
expect(inst.handleMount).to.have.been.calledOnce.and.calledWith(scratch.querySelector('#div'));
|
||||
inst.handleMount.reset();
|
||||
|
||||
inst.setState({ show:true });
|
||||
inst.forceUpdate();
|
||||
expect(inst.handleMount).to.have.been.calledTwice;
|
||||
expect(inst.handleMount.firstCall).to.have.been.calledWith(null);
|
||||
expect(inst.handleMount.secondCall).to.have.been.calledWith(scratch.querySelector('#span'));
|
||||
inst.handleMount.reset();
|
||||
|
||||
inst.setState({ show:false });
|
||||
inst.forceUpdate();
|
||||
expect(inst.handleMount).to.have.been.calledTwice;
|
||||
expect(inst.handleMount.firstCall).to.have.been.calledWith(null);
|
||||
expect(inst.handleMount.secondCall).to.have.been.calledWith(scratch.querySelector('#div'));
|
||||
});
|
||||
|
||||
|
||||
it('should add refs to components representing DOM nodes with no attributes if they have been pre-rendered', () => {
|
||||
// Simulate pre-render
|
||||
let parent = document.createElement('div');
|
||||
let child = document.createElement('div');
|
||||
parent.appendChild(child);
|
||||
scratch.appendChild(parent); // scratch contains: <div><div></div></div>
|
||||
|
||||
let ref = spy('ref');
|
||||
|
||||
function Wrapper() {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
render(<div><Wrapper ref={ref} /></div>, scratch, scratch.firstChild);
|
||||
expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild.firstChild);
|
||||
});
|
||||
});
|
439
thirdparty/preact/test/browser/render.js
vendored
439
thirdparty/preact/test/browser/render.js
vendored
@ -1,439 +0,0 @@
|
||||
/* global DISABLE_FLAKEY */
|
||||
|
||||
import { h, render } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
function getAttributes(node) {
|
||||
let attrs = {};
|
||||
for (let i=node.attributes.length; i--; ) {
|
||||
attrs[node.attributes[i].name] = node.attributes[i].value;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
// hacky normalization of attribute order across browsers.
|
||||
function sortAttributes(html) {
|
||||
return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
|
||||
let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
|
||||
if (~after.indexOf('/')) after = '></'+pre+'>';
|
||||
return '<' + pre + list.join('') + after;
|
||||
});
|
||||
}
|
||||
|
||||
describe('render()', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should create empty nodes (<* />)', () => {
|
||||
render(<div />, scratch);
|
||||
expect(scratch.childNodes)
|
||||
.to.have.length(1)
|
||||
.and.to.have.deep.property('0.nodeName', 'DIV');
|
||||
|
||||
scratch.innerHTML = '';
|
||||
|
||||
render(<span />, scratch);
|
||||
expect(scratch.childNodes)
|
||||
.to.have.length(1)
|
||||
.and.to.have.deep.property('0.nodeName', 'SPAN');
|
||||
|
||||
scratch.innerHTML = '';
|
||||
|
||||
render(<foo />, scratch);
|
||||
render(<x-bar />, scratch);
|
||||
expect(scratch.childNodes).to.have.length(2);
|
||||
expect(scratch.childNodes[0]).to.have.property('nodeName', 'FOO');
|
||||
expect(scratch.childNodes[1]).to.have.property('nodeName', 'X-BAR');
|
||||
});
|
||||
|
||||
it('should nest empty nodes', () => {
|
||||
render((
|
||||
<div>
|
||||
<span />
|
||||
<foo />
|
||||
<x-bar />
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
expect(scratch.childNodes)
|
||||
.to.have.length(1)
|
||||
.and.to.have.deep.property('0.nodeName', 'DIV');
|
||||
|
||||
let c = scratch.childNodes[0].childNodes;
|
||||
expect(c).to.have.length(3);
|
||||
expect(c).to.have.deep.property('0.nodeName', 'SPAN');
|
||||
expect(c).to.have.deep.property('1.nodeName', 'FOO');
|
||||
expect(c).to.have.deep.property('2.nodeName', 'X-BAR');
|
||||
});
|
||||
|
||||
it('should not render falsey values', () => {
|
||||
render((
|
||||
<div>
|
||||
{null},{undefined},{false},{0},{NaN}
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
expect(scratch.firstChild).to.have.property('innerHTML', ',,,0,NaN');
|
||||
});
|
||||
|
||||
it('should clear falsey attributes', () => {
|
||||
let root = render((
|
||||
<div anull="anull" aundefined="aundefined" afalse="afalse" anan="aNaN" a0="a0" />
|
||||
), scratch);
|
||||
|
||||
root = render((
|
||||
<div anull={null} aundefined={undefined} afalse={false} anan={NaN} a0={0} />
|
||||
), scratch, root);
|
||||
|
||||
expect(getAttributes(scratch.firstChild), 'from previous truthy values').to.eql({
|
||||
a0: '0',
|
||||
anan: 'NaN'
|
||||
});
|
||||
|
||||
scratch.innerHTML = '';
|
||||
|
||||
root = render((
|
||||
<div anull={null} aundefined={undefined} afalse={false} anan={NaN} a0={0} />
|
||||
), scratch);
|
||||
|
||||
expect(getAttributes(scratch.firstChild), 'initial render').to.eql({
|
||||
a0: '0',
|
||||
anan: 'NaN'
|
||||
});
|
||||
});
|
||||
|
||||
it('should clear falsey input values', () => {
|
||||
let root = render((
|
||||
<div>
|
||||
<input value={0} />
|
||||
<input value={false} />
|
||||
<input value={null} />
|
||||
<input value={undefined} />
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
expect(root.children[0]).to.have.property('value', '0');
|
||||
expect(root.children[1]).to.have.property('value', 'false');
|
||||
expect(root.children[2]).to.have.property('value', '');
|
||||
expect(root.children[3]).to.have.property('value', '');
|
||||
});
|
||||
|
||||
it('should clear falsey DOM properties', () => {
|
||||
let root;
|
||||
function test(val) {
|
||||
root = render((
|
||||
<div>
|
||||
<input value={val} />
|
||||
<table border={val} />
|
||||
</div>
|
||||
), scratch, root);
|
||||
}
|
||||
|
||||
test('2');
|
||||
test(false);
|
||||
expect(scratch).to.have.property('innerHTML', '<div><input><table></table></div>', 'for false');
|
||||
|
||||
test('3');
|
||||
test(null);
|
||||
expect(scratch).to.have.property('innerHTML', '<div><input><table></table></div>', 'for null');
|
||||
|
||||
test('4');
|
||||
test(undefined);
|
||||
expect(scratch).to.have.property('innerHTML', '<div><input><table></table></div>', 'for undefined');
|
||||
});
|
||||
|
||||
it('should apply string attributes', () => {
|
||||
render(<div foo="bar" data-foo="databar" />, scratch);
|
||||
|
||||
let div = scratch.childNodes[0];
|
||||
expect(div).to.have.deep.property('attributes.length', 2);
|
||||
|
||||
expect(div).to.have.deep.property('attributes[0].name', 'foo');
|
||||
expect(div).to.have.deep.property('attributes[0].value', 'bar');
|
||||
|
||||
expect(div).to.have.deep.property('attributes[1].name', 'data-foo');
|
||||
expect(div).to.have.deep.property('attributes[1].value', 'databar');
|
||||
});
|
||||
|
||||
it('should apply class as String', () => {
|
||||
render(<div class="foo" />, scratch);
|
||||
expect(scratch.childNodes[0]).to.have.property('className', 'foo');
|
||||
});
|
||||
|
||||
it('should alias className to class', () => {
|
||||
render(<div className="bar" />, scratch);
|
||||
expect(scratch.childNodes[0]).to.have.property('className', 'bar');
|
||||
});
|
||||
|
||||
it('should apply style as String', () => {
|
||||
render(<div style="top:5px; position:relative;" />, scratch);
|
||||
expect(scratch.childNodes[0]).to.have.deep.property('style.cssText')
|
||||
.that.matches(/top\s*:\s*5px\s*/)
|
||||
.and.matches(/position\s*:\s*relative\s*/);
|
||||
});
|
||||
|
||||
it('should only register on* functions as handlers', () => {
|
||||
let click = () => {},
|
||||
onclick = () => {};
|
||||
|
||||
let proto = document.createElement('div').constructor.prototype;
|
||||
|
||||
sinon.spy(proto, 'addEventListener');
|
||||
|
||||
render(<div click={ click } onClick={ onclick } />, scratch);
|
||||
|
||||
expect(scratch.childNodes[0]).to.have.deep.property('attributes.length', 0);
|
||||
|
||||
expect(proto.addEventListener).to.have.been.calledOnce
|
||||
.and.to.have.been.calledWithExactly('click', sinon.match.func, false);
|
||||
|
||||
proto.addEventListener.restore();
|
||||
});
|
||||
|
||||
it('should add and remove event handlers', () => {
|
||||
let click = sinon.spy(),
|
||||
mousedown = sinon.spy();
|
||||
|
||||
let proto = document.createElement('div').constructor.prototype;
|
||||
sinon.spy(proto, 'addEventListener');
|
||||
sinon.spy(proto, 'removeEventListener');
|
||||
|
||||
function fireEvent(on, type) {
|
||||
let e = document.createEvent('Event');
|
||||
e.initEvent(type, true, true);
|
||||
on.dispatchEvent(e);
|
||||
}
|
||||
|
||||
render(<div onClick={ () => click(1) } onMouseDown={ mousedown } />, scratch);
|
||||
|
||||
expect(proto.addEventListener).to.have.been.calledTwice
|
||||
.and.to.have.been.calledWith('click')
|
||||
.and.calledWith('mousedown');
|
||||
|
||||
fireEvent(scratch.childNodes[0], 'click');
|
||||
expect(click).to.have.been.calledOnce
|
||||
.and.calledWith(1);
|
||||
|
||||
proto.addEventListener.reset();
|
||||
click.reset();
|
||||
|
||||
render(<div onClick={ () => click(2) } />, scratch, scratch.firstChild);
|
||||
|
||||
expect(proto.addEventListener).not.to.have.been.called;
|
||||
|
||||
expect(proto.removeEventListener)
|
||||
.to.have.been.calledOnce
|
||||
.and.calledWith('mousedown');
|
||||
|
||||
fireEvent(scratch.childNodes[0], 'click');
|
||||
expect(click).to.have.been.calledOnce
|
||||
.and.to.have.been.calledWith(2);
|
||||
|
||||
fireEvent(scratch.childNodes[0], 'mousedown');
|
||||
expect(mousedown).not.to.have.been.called;
|
||||
|
||||
proto.removeEventListener.reset();
|
||||
click.reset();
|
||||
mousedown.reset();
|
||||
|
||||
render(<div />, scratch, scratch.firstChild);
|
||||
|
||||
expect(proto.removeEventListener)
|
||||
.to.have.been.calledOnce
|
||||
.and.calledWith('click');
|
||||
|
||||
fireEvent(scratch.childNodes[0], 'click');
|
||||
expect(click).not.to.have.been.called;
|
||||
|
||||
proto.addEventListener.restore();
|
||||
proto.removeEventListener.restore();
|
||||
});
|
||||
|
||||
it('should use capturing for events that do not bubble', () => {
|
||||
let click = sinon.spy(),
|
||||
focus = sinon.spy();
|
||||
|
||||
let root = render((
|
||||
<div onClick={click} onFocus={focus}>
|
||||
<button />
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
root.firstElementChild.click();
|
||||
root.firstElementChild.focus();
|
||||
|
||||
expect(click, 'click').to.have.been.calledOnce;
|
||||
|
||||
if (DISABLE_FLAKEY!==true) {
|
||||
// Focus delegation requires a 50b hack I'm not sure we want to incur
|
||||
expect(focus, 'focus').to.have.been.calledOnce;
|
||||
|
||||
// IE doesn't set it
|
||||
expect(click).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
|
||||
expect(focus).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
|
||||
}
|
||||
});
|
||||
|
||||
it('should serialize style objects', () => {
|
||||
let root = render((
|
||||
<div style={{
|
||||
color: 'rgb(255, 255, 255)',
|
||||
background: 'rgb(255, 100, 0)',
|
||||
backgroundPosition: '10px 10px',
|
||||
'background-size': 'cover',
|
||||
padding: 5,
|
||||
top: 100,
|
||||
left: '100%'
|
||||
}}>
|
||||
test
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
let { style } = scratch.childNodes[0];
|
||||
expect(style).to.have.property('color').that.equals('rgb(255, 255, 255)');
|
||||
expect(style).to.have.property('background').that.contains('rgb(255, 100, 0)');
|
||||
expect(style).to.have.property('backgroundPosition').that.equals('10px 10px');
|
||||
expect(style).to.have.property('backgroundSize', 'cover');
|
||||
expect(style).to.have.property('padding', '5px');
|
||||
expect(style).to.have.property('top', '100px');
|
||||
expect(style).to.have.property('left', '100%');
|
||||
|
||||
root = render((
|
||||
<div style={{ color: 'rgb(0, 255, 255)' }}>test</div>
|
||||
), scratch, root);
|
||||
|
||||
expect(root).to.have.deep.property('style.cssText').that.equals('color: rgb(0, 255, 255);');
|
||||
|
||||
root = render((
|
||||
<div style="display: inline;">test</div>
|
||||
), scratch, root);
|
||||
|
||||
expect(root).to.have.deep.property('style.cssText').that.equals('display: inline;');
|
||||
|
||||
root = render((
|
||||
<div style={{ backgroundColor: 'rgb(0, 255, 255)' }}>test</div>
|
||||
), scratch, root);
|
||||
|
||||
expect(root).to.have.deep.property('style.cssText').that.equals('background-color: rgb(0, 255, 255);');
|
||||
});
|
||||
|
||||
it('should serialize class/className', () => {
|
||||
render(<div class={{
|
||||
no1: false,
|
||||
no2: 0,
|
||||
no3: null,
|
||||
no4: undefined,
|
||||
no5: '',
|
||||
yes1: true,
|
||||
yes2: 1,
|
||||
yes3: {},
|
||||
yes4: [],
|
||||
yes5: ' '
|
||||
}} />, scratch);
|
||||
|
||||
let { className } = scratch.childNodes[0];
|
||||
expect(className).to.be.a.string;
|
||||
expect(className.split(' '))
|
||||
.to.include.members(['yes1', 'yes2', 'yes3', 'yes4', 'yes5'])
|
||||
.and.not.include.members(['no1', 'no2', 'no3', 'no4', 'no5']);
|
||||
});
|
||||
|
||||
it('should support dangerouslySetInnerHTML', () => {
|
||||
let html = '<b>foo & bar</b>';
|
||||
let root = render(<div dangerouslySetInnerHTML={{ __html: html }} />, scratch);
|
||||
|
||||
expect(scratch.firstChild).to.have.property('innerHTML', html);
|
||||
expect(scratch.innerHTML).to.equal('<div>'+html+'</div>');
|
||||
|
||||
root = render(<div>a<strong>b</strong></div>, scratch, root);
|
||||
|
||||
expect(scratch).to.have.property('innerHTML', `<div>a<strong>b</strong></div>`);
|
||||
|
||||
root = render(<div dangerouslySetInnerHTML={{ __html: html }} />, scratch, root);
|
||||
|
||||
expect(scratch.innerHTML).to.equal('<div>'+html+'</div>');
|
||||
});
|
||||
|
||||
it('should reconcile mutated DOM attributes', () => {
|
||||
let check = p => render(<input type="checkbox" checked={p} />, scratch, scratch.lastChild),
|
||||
value = () => scratch.lastChild.checked,
|
||||
setValue = p => scratch.lastChild.checked = p;
|
||||
check(true);
|
||||
expect(value()).to.equal(true);
|
||||
check(false);
|
||||
expect(value()).to.equal(false);
|
||||
check(true);
|
||||
expect(value()).to.equal(true);
|
||||
setValue(true);
|
||||
check(false);
|
||||
expect(value()).to.equal(false);
|
||||
setValue(false);
|
||||
check(true);
|
||||
expect(value()).to.equal(true);
|
||||
});
|
||||
|
||||
it('should ignore props.children if children are manually specified', () => {
|
||||
expect(
|
||||
<div a children={['a', 'b']}>c</div>
|
||||
).to.eql(
|
||||
<div a>c</div>
|
||||
);
|
||||
});
|
||||
|
||||
it('should reorder child pairs', () => {
|
||||
let root = render((
|
||||
<div>
|
||||
<a>a</a>
|
||||
<b>b</b>
|
||||
</div>
|
||||
), scratch, root);
|
||||
|
||||
let a = scratch.firstChild.firstChild;
|
||||
let b = scratch.firstChild.lastChild;
|
||||
|
||||
expect(a).to.have.property('nodeName', 'A');
|
||||
expect(b).to.have.property('nodeName', 'B');
|
||||
|
||||
root = render((
|
||||
<div>
|
||||
<b>b</b>
|
||||
<a>a</a>
|
||||
</div>
|
||||
), scratch, root);
|
||||
|
||||
expect(scratch.firstChild.firstChild).to.have.property('nodeName', 'B');
|
||||
expect(scratch.firstChild.lastChild).to.have.property('nodeName', 'A');
|
||||
expect(scratch.firstChild.firstChild).to.equal(b);
|
||||
expect(scratch.firstChild.lastChild).to.equal(a);
|
||||
});
|
||||
|
||||
// Discussion: https://github.com/developit/preact/issues/287
|
||||
('HTMLDataListElement' in window ? it : xit)('should allow <input list /> to pass through as an attribute', () => {
|
||||
render((
|
||||
<div>
|
||||
<input type="range" min="0" max="100" list="steplist" />
|
||||
<datalist id="steplist">
|
||||
<option>0</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</datalist>
|
||||
</div>
|
||||
), scratch);
|
||||
|
||||
let html = scratch.firstElementChild.firstElementChild.outerHTML;
|
||||
expect(sortAttributes(html)).to.equal(sortAttributes('<input type="range" min="0" max="100" list="steplist">'));
|
||||
});
|
||||
});
|
127
thirdparty/preact/test/browser/spec.js
vendored
127
thirdparty/preact/test/browser/spec.js
vendored
@ -1,127 +0,0 @@
|
||||
import { h, render, rerender, Component } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
const EMPTY_CHILDREN = [];
|
||||
|
||||
describe('Component spec', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
describe('defaultProps', () => {
|
||||
it('should apply default props on initial render', () => {
|
||||
class WithDefaultProps extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
expect(props).to.be.deep.equal({
|
||||
children: EMPTY_CHILDREN,
|
||||
fieldA: 1, fieldB: 2,
|
||||
fieldC: 1, fieldD: 2
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 };
|
||||
render(<WithDefaultProps fieldA={1} fieldB={2} fieldD={2} />, scratch);
|
||||
});
|
||||
|
||||
it('should apply default props on rerender', () => {
|
||||
let doRender;
|
||||
class Outer extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { i:1 };
|
||||
}
|
||||
componentDidMount() {
|
||||
doRender = () => this.setState({ i: 2 });
|
||||
}
|
||||
render(props, { i }) {
|
||||
return <WithDefaultProps fieldA={1} fieldB={i} fieldD={i} />;
|
||||
}
|
||||
}
|
||||
class WithDefaultProps extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.ctor(props, context);
|
||||
}
|
||||
ctor(){}
|
||||
componentWillReceiveProps() {}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 };
|
||||
|
||||
let proto = WithDefaultProps.prototype;
|
||||
sinon.spy(proto, 'ctor');
|
||||
sinon.spy(proto, 'componentWillReceiveProps');
|
||||
sinon.spy(proto, 'render');
|
||||
|
||||
render(<Outer />, scratch);
|
||||
doRender();
|
||||
|
||||
const PROPS1 = {
|
||||
fieldA: 1, fieldB: 1,
|
||||
fieldC: 1, fieldD: 1
|
||||
};
|
||||
|
||||
const PROPS2 = {
|
||||
fieldA: 1, fieldB: 2,
|
||||
fieldC: 1, fieldD: 2
|
||||
};
|
||||
|
||||
expect(proto.ctor).to.have.been.calledWithMatch(PROPS1);
|
||||
expect(proto.render).to.have.been.calledWithMatch(PROPS1);
|
||||
|
||||
rerender();
|
||||
|
||||
// expect(proto.ctor).to.have.been.calledWith(PROPS2);
|
||||
expect(proto.componentWillReceiveProps).to.have.been.calledWithMatch(PROPS2);
|
||||
expect(proto.render).to.have.been.calledWithMatch(PROPS2);
|
||||
});
|
||||
|
||||
// @TODO: migrate this to preact-compat
|
||||
xit('should cache default props', () => {
|
||||
class WithDefaultProps extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
expect(props).to.be.deep.equal({
|
||||
fieldA: 1, fieldB: 2,
|
||||
fieldC: 1, fieldD: 2,
|
||||
fieldX: 10
|
||||
});
|
||||
}
|
||||
getDefaultProps() {
|
||||
return { fieldA: 1, fieldB: 1 };
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 };
|
||||
sinon.spy(WithDefaultProps.prototype, 'getDefaultProps');
|
||||
render((
|
||||
<div>
|
||||
<WithDefaultProps fieldB={2} fieldD={2} fieldX={10} />
|
||||
<WithDefaultProps fieldB={2} fieldD={2} fieldX={10} />
|
||||
<WithDefaultProps fieldB={2} fieldD={2} fieldX={10} />
|
||||
</div>
|
||||
), scratch);
|
||||
expect(WithDefaultProps.prototype.getDefaultProps).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
112
thirdparty/preact/test/browser/svg.js
vendored
112
thirdparty/preact/test/browser/svg.js
vendored
@ -1,112 +0,0 @@
|
||||
import { h, render } from '../../src/preact';
|
||||
/** @jsx h */
|
||||
|
||||
|
||||
// hacky normalization of attribute order across browsers.
|
||||
function sortAttributes(html) {
|
||||
return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
|
||||
let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
|
||||
if (~after.indexOf('/')) after = '></'+pre+'>';
|
||||
return '<' + pre + list.join('') + after;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
describe('svg', () => {
|
||||
let scratch;
|
||||
|
||||
before( () => {
|
||||
scratch = document.createElement('div');
|
||||
(document.body || document.documentElement).appendChild(scratch);
|
||||
});
|
||||
|
||||
beforeEach( () => {
|
||||
scratch.innerHTML = '';
|
||||
});
|
||||
|
||||
after( () => {
|
||||
scratch.parentNode.removeChild(scratch);
|
||||
scratch = null;
|
||||
});
|
||||
|
||||
it('should render SVG to string', () => {
|
||||
render((
|
||||
<svg viewBox="0 0 360 360">
|
||||
<path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" />
|
||||
</svg>
|
||||
), scratch);
|
||||
|
||||
let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
|
||||
expect(html).to.equal(sortAttributes(`
|
||||
<svg viewBox="0 0 360 360">
|
||||
<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white"></path>
|
||||
</svg>
|
||||
`.replace(/[\n\t]+/g,'')));
|
||||
});
|
||||
|
||||
it('should render SVG to DOM', () => {
|
||||
const Demo = () => (
|
||||
<svg viewBox="0 0 360 360">
|
||||
<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white" />
|
||||
</svg>
|
||||
);
|
||||
render(<Demo />, scratch);
|
||||
|
||||
let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
|
||||
expect(html).to.equal(sortAttributes('<svg viewBox="0 0 360 360"><path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z"></path></svg>'));
|
||||
});
|
||||
|
||||
it('should use attributes for className', () => {
|
||||
const Demo = ({ c }) => (
|
||||
<svg viewBox="0 0 360 360" {...(c ? {class:'foo_'+c} : {})}>
|
||||
<path class={c && ('bar_'+c)} stroke="white" fill="black" d="M347.1 357.9L183.3 256.5 13 357.9V1.7h334.1v356.2zM58.5 47.2v231.4l124.8-74.1 118.3 72.8V47.2H58.5z" />
|
||||
</svg>
|
||||
);
|
||||
let root = render(<Demo c="1" />, scratch, root);
|
||||
sinon.spy(root, 'removeAttribute');
|
||||
root = render(<Demo />, scratch, root);
|
||||
expect(root.removeAttribute).to.have.been.calledOnce.and.calledWith('class');
|
||||
root.removeAttribute.restore();
|
||||
|
||||
root = render(<div />, scratch, root);
|
||||
root = render(<Demo />, scratch, root);
|
||||
sinon.spy(root, 'setAttribute');
|
||||
root = render(<Demo c="2" />, scratch, root);
|
||||
expect(root.setAttribute).to.have.been.calledOnce.and.calledWith('class', 'foo_2');
|
||||
root.setAttribute.restore();
|
||||
root = render(<Demo c="3" />, scratch, root);
|
||||
root = render(<Demo />, scratch, root);
|
||||
});
|
||||
|
||||
it('should still support class attribute', () => {
|
||||
render((
|
||||
<svg viewBox="0 0 1 1" class="foo bar" />
|
||||
), scratch);
|
||||
|
||||
expect(scratch.innerHTML).to.contain(` class="foo bar"`);
|
||||
});
|
||||
|
||||
it('should serialize class', () => {
|
||||
render((
|
||||
<svg viewBox="0 0 1 1" class={{ foo: true, bar: false, other: 'hello' }} />
|
||||
), scratch);
|
||||
|
||||
expect(scratch.innerHTML).to.contain(` class="foo other"`);
|
||||
});
|
||||
|
||||
it('should switch back to HTML for <foreignObject>', () => {
|
||||
render((
|
||||
<svg>
|
||||
<g>
|
||||
<foreignObject>
|
||||
<a href="#foo">test</a>
|
||||
</foreignObject>
|
||||
</g>
|
||||
</svg>
|
||||
), scratch);
|
||||
|
||||
expect(scratch.getElementsByTagName('a'))
|
||||
.to.have.property('0')
|
||||
.that.is.a('HTMLAnchorElement');
|
||||
});
|
||||
});
|
155
thirdparty/preact/test/karma.conf.js
vendored
155
thirdparty/preact/test/karma.conf.js
vendored
@ -1,155 +0,0 @@
|
||||
/*eslint no-var:0, object-shorthand:0 */
|
||||
|
||||
var coverage = String(process.env.COVERAGE)!=='false',
|
||||
ci = String(process.env.CI).match(/^(1|true)$/gi),
|
||||
pullRequest = !String(process.env.TRAVIS_PULL_REQUEST).match(/^(0|false|undefined)$/gi),
|
||||
realBrowser = String(process.env.BROWSER).match(/^(1|true)$/gi),
|
||||
sauceLabs = realBrowser && ci && !pullRequest,
|
||||
performance = !coverage && !realBrowser && String(process.env.PERFORMANCE)!=='false',
|
||||
webpack = require('webpack');
|
||||
|
||||
var sauceLabsLaunchers = {
|
||||
sl_chrome: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
platform: 'Windows 10'
|
||||
},
|
||||
sl_firefox: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
platform: 'Windows 10'
|
||||
},
|
||||
sl_safari: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11'
|
||||
},
|
||||
sl_edge: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'MicrosoftEdge',
|
||||
platform: 'Windows 10'
|
||||
},
|
||||
sl_ie_11: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
version: '11.103',
|
||||
platform: 'Windows 10'
|
||||
},
|
||||
sl_ie_10: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
version: '10.0',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
sl_ie_9: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
version: '9.0',
|
||||
platform: 'Windows 7'
|
||||
}
|
||||
};
|
||||
|
||||
var travisLaunchers = {
|
||||
chrome_travis: {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox']
|
||||
}
|
||||
};
|
||||
|
||||
var localBrowsers = realBrowser ? Object.keys(travisLaunchers) : ['PhantomJS'];
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
browsers: sauceLabs ? Object.keys(sauceLabsLaunchers) : localBrowsers,
|
||||
|
||||
frameworks: ['source-map-support', 'mocha', 'chai-sinon'],
|
||||
|
||||
reporters: ['mocha'].concat(
|
||||
coverage ? 'coverage' : [],
|
||||
sauceLabs ? 'saucelabs' : []
|
||||
),
|
||||
|
||||
coverageReporter: {
|
||||
reporters: [
|
||||
{
|
||||
type: 'text-summary'
|
||||
},
|
||||
{
|
||||
type: 'html',
|
||||
dir: __dirname+'/../coverage'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
mochaReporter: {
|
||||
showDiff: true
|
||||
},
|
||||
|
||||
browserLogOptions: { terminal: true },
|
||||
browserConsoleLogOptions: { terminal: true },
|
||||
|
||||
browserNoActivityTimeout: 5 * 60 * 1000,
|
||||
|
||||
// Use only two browsers concurrently, works better with open source Sauce Labs remote testing
|
||||
concurrency: 2,
|
||||
|
||||
// sauceLabs: {
|
||||
// tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER || ('local'+require('./package.json').version),
|
||||
// startConnect: false
|
||||
// },
|
||||
|
||||
customLaunchers: sauceLabs ? sauceLabsLaunchers : travisLaunchers,
|
||||
|
||||
files: [
|
||||
{ pattern: 'polyfills.js', watched: false },
|
||||
{ pattern: '{browser,shared}/**.js', watched: false }
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'**/*': ['webpack', 'sourcemap']
|
||||
},
|
||||
|
||||
webpack: {
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
/* Transpile source and test files */
|
||||
preLoaders: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
loose: 'all',
|
||||
blacklist: ['es6.tailCall']
|
||||
}
|
||||
}
|
||||
],
|
||||
/* Only Instrument our source files for coverage */
|
||||
loaders: [].concat( coverage ? {
|
||||
test: /\.jsx?$/,
|
||||
loader: 'isparta',
|
||||
include: /src/
|
||||
} : [])
|
||||
},
|
||||
resolve: {
|
||||
// The React DevTools integration requires preact as a module
|
||||
// rather than referencing source files inside the module
|
||||
// directly
|
||||
alias: { preact: '../src/preact' },
|
||||
modulesDirectories: [__dirname, 'node_modules']
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
coverage: coverage,
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV || ''),
|
||||
ENABLE_PERFORMANCE: performance,
|
||||
DISABLE_FLAKEY: !!String(process.env.FLAKEY).match(/^(0|false)$/gi)
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
webpackMiddleware: {
|
||||
noInfo: true
|
||||
}
|
||||
});
|
||||
};
|
1
thirdparty/preact/test/node/index.js
vendored
1
thirdparty/preact/test/node/index.js
vendored
@ -1 +0,0 @@
|
||||
// this is just a placeholder
|
5
thirdparty/preact/test/polyfills.js
vendored
5
thirdparty/preact/test/polyfills.js
vendored
@ -1,5 +0,0 @@
|
||||
// ES2015 APIs used by developer tools integration
|
||||
import 'core-js/es6/map';
|
||||
import 'core-js/fn/array/fill';
|
||||
import 'core-js/fn/array/from';
|
||||
import 'core-js/fn/object/assign';
|
21
thirdparty/preact/test/shared/exports.js
vendored
21
thirdparty/preact/test/shared/exports.js
vendored
@ -1,21 +0,0 @@
|
||||
import preact, { h, Component, render, rerender, options } from '../../src/preact';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('preact', () => {
|
||||
it('should be available as a default export', () => {
|
||||
expect(preact).to.be.an('object');
|
||||
expect(preact).to.have.property('h', h);
|
||||
expect(preact).to.have.property('Component', Component);
|
||||
expect(preact).to.have.property('render', render);
|
||||
expect(preact).to.have.property('rerender', rerender);
|
||||
expect(preact).to.have.property('options', options);
|
||||
});
|
||||
|
||||
it('should be available as named exports', () => {
|
||||
expect(h).to.be.a('function');
|
||||
expect(Component).to.be.a('function');
|
||||
expect(render).to.be.a('function');
|
||||
expect(rerender).to.be.a('function');
|
||||
expect(options).to.exist.and.be.an('object');
|
||||
});
|
||||
});
|
196
thirdparty/preact/test/shared/h.js
vendored
196
thirdparty/preact/test/shared/h.js
vendored
@ -1,196 +0,0 @@
|
||||
import { h } from '../../src/preact';
|
||||
import { VNode } from '../../src/vnode';
|
||||
import { expect } from 'chai';
|
||||
|
||||
/*eslint-env browser, mocha */
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
const buildVNode = (nodeName, attributes, children=[]) => ({
|
||||
nodeName,
|
||||
children,
|
||||
attributes,
|
||||
key: attributes && attributes.key
|
||||
});
|
||||
|
||||
describe('h(jsx)', () => {
|
||||
it('should return a VNode', () => {
|
||||
let r;
|
||||
expect( () => r = h('foo') ).not.to.throw();
|
||||
expect(r).to.be.an('object');
|
||||
expect(r).to.be.an.instanceof(VNode);
|
||||
expect(r).to.have.property('nodeName', 'foo');
|
||||
expect(r).to.have.property('attributes', undefined);
|
||||
expect(r).to.have.property('children').that.eql([]);
|
||||
});
|
||||
|
||||
it('should perserve raw attributes', () => {
|
||||
let attrs = { foo:'bar', baz:10, func:()=>{} },
|
||||
r = h('foo', attrs);
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('attributes')
|
||||
.that.deep.equals(attrs);
|
||||
});
|
||||
|
||||
it('should support element children', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
h('bar'),
|
||||
h('baz')
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
buildVNode('bar'),
|
||||
buildVNode('baz')
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support multiple element children, given as arg list', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
h('bar'),
|
||||
h('baz', null, h('test'))
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
buildVNode('bar'),
|
||||
buildVNode('baz', undefined, [
|
||||
buildVNode('test')
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle multiple element children, given as an array', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
[
|
||||
h('bar'),
|
||||
h('baz', null, h('test'))
|
||||
]
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
buildVNode('bar'),
|
||||
buildVNode('baz', undefined, [
|
||||
buildVNode('test')
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle multiple children, flattening one layer as needed', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
h('bar'),
|
||||
[
|
||||
h('baz', null, h('test'))
|
||||
]
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
buildVNode('bar'),
|
||||
buildVNode('baz', undefined, [
|
||||
buildVNode('test')
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support nested children', () => {
|
||||
const m = x => h(x);
|
||||
expect(
|
||||
h('foo', null, m('a'), [m('b'), m('c')], m('d'))
|
||||
).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));
|
||||
|
||||
expect(
|
||||
h('foo', null, [m('a'), [m('b'), m('c')], m('d')])
|
||||
).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));
|
||||
|
||||
expect(
|
||||
h('foo', { children: [m('a'), [m('b'), m('c')], m('d')] })
|
||||
).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));
|
||||
|
||||
expect(
|
||||
h('foo', { children: [[m('a'), [m('b'), m('c')], m('d')]] })
|
||||
).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));
|
||||
|
||||
expect(
|
||||
h('foo', { children: m('a') })
|
||||
).to.have.property('children').that.eql([m('a')]);
|
||||
|
||||
expect(
|
||||
h('foo', { children: 'a' })
|
||||
).to.have.property('children').that.eql(['a']);
|
||||
});
|
||||
|
||||
it('should support text children', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
'textstuff'
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.with.length(1)
|
||||
.with.property('0')
|
||||
.that.equals('textstuff');
|
||||
});
|
||||
|
||||
it('should merge adjacent text children', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
'one',
|
||||
'two',
|
||||
h('bar'),
|
||||
'three',
|
||||
h('baz'),
|
||||
h('baz'),
|
||||
'four',
|
||||
null,
|
||||
'five',
|
||||
'six'
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
'onetwo',
|
||||
buildVNode('bar'),
|
||||
'three',
|
||||
buildVNode('baz'),
|
||||
buildVNode('baz'),
|
||||
'fourfivesix'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should merge nested adjacent text children', () => {
|
||||
let r = h(
|
||||
'foo',
|
||||
null,
|
||||
'one',
|
||||
['two', null, 'three'],
|
||||
null,
|
||||
['four', null, 'five', null],
|
||||
'six',
|
||||
null
|
||||
);
|
||||
|
||||
expect(r).to.be.an('object')
|
||||
.with.property('children')
|
||||
.that.deep.equals([
|
||||
'onetwothreefourfivesix'
|
||||
]);
|
||||
});
|
||||
});
|
5
thirdparty/preact/typings.json
vendored
5
thirdparty/preact/typings.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "preact",
|
||||
"main": "src/preact.d.ts",
|
||||
"version": false
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"reactNamespace": "preact",
|
||||
"reactNamespace": "React",
|
||||
"experimentalDecorators": true,
|
||||
"module": "system",
|
||||
"sourceMap": true,
|
||||
|
Loading…
Reference in New Issue
Block a user