durandal composition animation - animation

is there any way transition could be applied to the composition binding with durandal like it is on rout binding? what I mean is that if I have
<div data-bind="compose: ActiveView, transition: 'entrance'"></div>
Active view is a Knockout Observable and When I change it I would like to have transition effect like while routing with some progress ring indicating transition process.
here is how my viewmodel
import ko = require('knockout');
class Shell implements iModule
{
activate: (context: Object) => void;
ActiveView: KnockoutObservable<any>;
SwitchView: () => void;
constructor() {
this.activate = this._activateCallback;
this.ActiveView = ko.observable<string>('parts/welcome');
this.SwitchView = this._activateView;
}
private _activateView() {
this.ActiveView('parts/about');
}
private _activateCallback (ctx: Object) {
}
}
export = Shell;

Transitions work with any compose binding. You have an error in your syntax though. It should look like this:
<div data-bind="compose: { model: ActiveView, transition: 'entrance' }"></div>

Related

actions in the header of an accordion component

I would like to add actions in the header of the accordion component. The only problem is that if you click the action, the accordion will state chnages between collapsed and expanded.
example:
<nb-accordion>
<nb-accordion-item #primaryItem expanded="true">
<nb-accordion-item-header>
Dashboard
<nb-actions size="small">
<nb-action icon="search">Search</nb-action>
<nb-action icon="star"></nb-action>
<nb-action icon="star" status="warning"></nb-action>
</nb-actions>
</nb-accordion-item-header>
<nb-accordion-item-body>
item content
</nb-accordion-item-body>
</nb-accordion-item>
</nb-accordion>
Looks like this:
I can see in the api that there is a collapsedChange event, but this emits after the change.
AM i out of luck here? IS there a way to intercept and cancel the collapse event?
thanks in advance
so seems I'm the only one looking for this. So I built my own custom card to deliver me the functionality I want. you can take this and use it in your own angular project. its a collapsible card with header and footer sections as well as a section for adding actions.
car component:
<nb-card accent="{{accentColor}}" status="{{statusColor}}">
<nb-card-header>
<span class="float-left card-title">
<ng-content select="[slot=title]"></ng-content>
</span>
<nb-actions size="small" class="float-right">
<nb-action><button type="button" status="basic" nbButton size="small" (click)="toggleExpand()">
<nb-icon icon="{{expandedIcon}}"></nb-icon>{{ expandedText }}
</button></nb-action>
<!-- icon="{{expandedIcon}}" -->
</nb-actions>
<span class="float-right" *ngIf="hasActions">
<ng-content select="[slot=actions]"></ng-content>
</span>
</nb-card-header>
<nb-card-body *ngIf="expandedState" class="content-body">
<ng-content select="[slot=body]"></ng-content>
</nb-card-body>
<nb-card-footer *ngIf="expandedState && hasFooter">
<ng-content select="[slot=footer]"></ng-content>
</nb-card-footer>
</nb-card>
component .ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-content-card',
templateUrl: './content-card.component.html',
styleUrls: ['./content-card.component.scss'],
})
export class ContentCardComponent implements OnInit {
// STATUS OPTIONS: basic, primary, info, success, warning, danger, control
// ACCENT OPTIONS: basic, primary, info, success, warning, danger, control
#Input() hasActions: boolean;
#Input() hasFooter: boolean;
#Input() accentColor: string;
#Input() statusColor: string;
public readonly upIcon = 'arrowhead-up-outline';
public readonly downIcon = 'arrowhead-down-outline';
public readonly hideText = 'hide';
public readonly showText = 'show';
public expandedState = true;
public expandedText: string;
public expandedIcon: string;
constructor() {}
ngOnInit() {
this.expandedIcon = this.upIcon;
this.expandedText = this.hideText;
}
public toggleExpand(): void {
this.expandedState = !this.expandedState;
if (this.expandedState) {
this.expandedIcon = this.upIcon;
this.expandedText = this.hideText;
} else {
this.expandedIcon = this.downIcon;
this.expandedText = this.showText;
}
}
}
component .scss
.float-right {
float: right;
}
.float-left {
float: left;
}
Sorry if this is a little late - I ran into the same issue and in short, no there is no API or direct way to stop the accordion item from firing the expand/collapse whenever you click anywhere in the header component. The reason is because nebular is attaching a click event to all rendered html children of the accordion header that will fire the expand/collapse event. You can actually see the attached event if you inspect your elements and look at the attached event listeners.
One hacky way is the attempt to remove the attached event.
A second hacky way to get around this and the one I used was to fire the toggle for the accordion a second time after clicking. The end result is that the user does not see the accordion expand (since an expand + collapse = no ui change):
component.html
<nb-accordion>
<nb-accordion-item #primaryItem expanded="true" *ngFor="let item in items; let i = index;">
<nb-accordion-item-header>
Dashboard
<nb-actions size="small">
<nb-action icon="search" (click)="cancelAccordionToggle(i)">Search</nb-action>
<nb-action icon="star" (click)="cancelAccordionToggle(i)"></nb-action>
<nb-action icon="star" status="warning" (click)="cancelAccordionToggle(i)"></nb-action>
</nb-actions>
</nb-accordion-item-header>
<nb-accordion-item-body>
item content
</nb-accordion-item-body>
</nb-accordion-item>
</nb-accordion>
component.ts
// use the following hack for accordian so action items do not trigger toggle
#ViewChildren(NbAccordionItemComponent) listItems: QueryList<NbAccordionItemComponent>;
cancelAccordionToggle(index: number) {
const listItem: NbAccordionItemComponent = this.listItems.toArray()[index];
listItem.toggle();
}
If you don't want hack it like this, the alternative solution is to make your own like you did.
I added the following to my nbButton inside of <nb-accordion-item-header>
(click)="$event.stopPropagation()"
and it worked for me.

react-native: disable animations

We are using Animated and react-native-animatable quite heavily and starting to notice slowness on some old devices. All animations set useNativeDriver which makes us believe that we may have a few too many animations.
Is there a way to overwrite the Animated prototype to completely disable animations? I looked into this and it didn't seem simple.
Another option I'm considering is to leave my fade animations in but set the initial value in the constructor to the final value. This approach definitely doesn't show any animations but would it also bypass the animation in the native bridge as the value isn't changing?
class Item extends Component {
constructor(props) {
super(props);
this.state = {
opacity: 1 // Notice how this is set to 1
}
}
componentDidMount() {
setTimeout(() => {
this.setState({opacity: 1})
}, 1000)
}
render() {
return (
<Animatable.View style={{opacity}} easing='ease-in' transition='opacity' duration={500} useNativeDriver={true} />
)
}
}
Just create a wrapping component for it and use that instead of Animated.View
export default const AnimatedViewWrapper = (props) => {
if (/* slow device check */) {
return React.createElement(View, props)
} else {
return React.createElement(Animated.View, props)
}
}
You might need to filter the props you receive because View does not have many of the props that Animated.View has. You can get them through View.propTypes. You might need to do this only if __DEV__ is true as propTypes are stripped out in production builds

React - delay heavy sub components rendering

I'm using React and React Router, and on some pages, I have some big lists rendered (as sub components) of the pages. When clicking on a link to change the page, it waits for all the subcomponents to be rendered which implies a delay between the click and the visual feedback. Is there a simple way not to wait for some sub components to be rendered?
I'm looking for a best practice, but maybe using a boolean and a setTimeout() is the only way. Thx.
When you have big lists of components, you often don't see all of them at the same time. You can use React Virtualized to actually render only visible components.
Check the code below:
const LightComponent = () => <div>LightComponent</div>
const HeavyComponent = () => <div>HeavyComponent</div>
class App extends React.Component {
constructor(props) {
super();
this.state = { shouldRenderHeavyComponent: false };
}
componentDidMount() {
this.setState({ shouldRenderHeavyComponent: true });
}
render() {
console.log("Render", this.state.shouldRenderHeavyComponent);
return (
<div>
<LightComponent/>
{ this.state.shouldRenderHeavyComponent && <HeavyComponent/> }
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
I had the same challenges. Finally I discovered this wonderful library, doing the trick for me: https://github.com/seatgeek/react-infinite

Which component should control the loading state of a lower component?

Let's say I have these components:
Translator
TranslationList
Translator determines translation context, has translate function.
TranslationList must show these "visual states": loading, result list, no results.
The Translator moves around the page (one instance): on focusing an input, it moves "below" it and gives a dropdown with suggestion.
So each time it moves, it has to:
Show that it's loading translations
Show translation list or no results message.
So my question is:
Which component should control the "loading" visual state?
If the Translator component controls it, it has to pass loading=true translations=[] as props to Translation list. Then later it has to rerender it again with new props loading=false translations=[...]. This seems a bit counter-intuitive, because loading feels like the state of the TranslationList component.
If we the TranslationList component has loading state, then it also has to have a way to translate things, meaning that I have to pass translate function as prop. I would then hold translations and loading as state. This all gets a bit messy, since it must now also receive string to translate, context.
I also don't want to have separate components for loading message, no results message. I'd rather keep these inside the TranslationList, because these 3 share that same wrapper <div class="list-group"></div>
Perhaps there should be one more Component in between these two components, responsible only for fetching translation data?
Translator component should control the loading state of a lower component list component. hold the loading and translating logic but with help by wrapping it in a high order component where you should put most of the logic. link for HOC https://www.youtube.com/watch?v=ymJOm5jY1tQ.
const translateSelected = wrappedComponent =>
//return Translator component
class extends React.Component {
state = {translatedText: [], loading:true}
componentDidMount(){
fetch("text to translate")
.then(transText => this.setState({translatedText: transText, loading: false}))
}
render() {
const {translatedText} = this.state
return <WrappedComponent {..this.props} {...translatedText}
}
}
const Translator_HOC = translateSelected(Translator);
You could introduce a Higher Order Component to control the switching of the loading state and the TranslationList. That way you separate the loading display away from your TranslationList as being it's concern. This also allows you to use the HOC in other areas.
The Translator can act as "container" component which does the data fetching/passing.
For example:
// The Loadable HOC
function Loadable(WrappedComponent) {
return function LoadableComponent({ loaded, ...otherProps }) {
return loaded
? <WrappedComponent {...otherProps} />
: <div>Loading...</div>
}
}
// Translation list doesn't need to know about "loaded" prop
function TranslationList({ translations }) {
return (
<ul>
{
translations.map((translation, index) =>
<li key={index}>{translation}</li>
)
}
</ul>
)
}
// We create our new composed component here.
const LoadableTranslationList = Loadable(TranslationList)
class Translator extends React.Component {
state = {
loaded: false,
translations: []
}
componentDidMount() {
// Let's simulate a data fetch, typically you are going to access
// a prop like this.props.textToTranslate and then pass that to
// an API or redux action to fetch the respective translations.
setTimeout(() => {
this.setState({
loaded: true,
translations: [ 'Bonjour', 'Goddag', 'Hola' ]
});
}, 2000);
}
render() {
const { loaded, translations } = this.state;
return (
<div>
<h3>Translations for "{this.props.textToTranslate}"</h3>
<LoadableTranslationList loaded={loaded} translations={translations} />
</div>
)
}
}
ReactDOM.render(<Translate textToTranslate="Hello" />)
Running example here: http://www.webpackbin.com/NyQnWe54W

Re-rendering a single row of a list without re-rendering the entire list

we're trying to implement a contact list that works just like the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
Specifically we're trying to show a checkbox instead of the avatar on row hover.
We'd like to catch and re-render only the interested row (when hovered) and show the avatar/checkbox accordingly... this seems an easy task but we're not able to isolate the render to the hovered row (instead of re-rendering the entire list)
Do you have any suggestion on how to do something like this?
Our temporary solution uses a container component that handles the table:
When a row is hovered we capture it from onRowHover of the Table component and save it in the container state. This triggers a re-render of the entire list with really poor perfomance.
You can see a video of the issue here.
Here is a code sample:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
Thank you in advance.
You could wrap your rows into a new component implementing shouldComponentUpdate like so :
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
Then you can use it in your ContactList component like so :
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
If you don't want to manually implement shouldComponentUpdate, you can use React's PureRenderMixin or check a lib like recompose which provides useful helpers like pure to do so.
EDIT
As pointed out by the OP and #Denis, the approach above doesn't play well with some features of the Table component. Specifically, TableBody does some manipulation on its children's children. A better approach would be to define your ContactRow component like so:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
and then to use it like this
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
But I guess having TableRow re-render only when necessary is a feature everyone would benefit from, so maybe a PR would be in order :)

Resources