Skip to content

Commit

Permalink
Fixed #2242 - Added StyleClass component
Browse files Browse the repository at this point in the history
  • Loading branch information
mcandu committed Aug 12, 2021
1 parent ca747b9 commit d53135b
Show file tree
Hide file tree
Showing 9 changed files with 798 additions and 0 deletions.
77 changes: 77 additions & 0 deletions api-generator/components/styleclass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const StyleClassProps = [
{
name: 'selector',
type: 'string',
default: 'null',
description: 'Selector to define the target element.'
},
{
name: 'nodeRef',
type: 'any',
default: 'null',
description: 'A React reference to DOM element that need to specify.'
},
{
name: 'enterClassName',
type: 'string',
default: 'null',
description: 'Style class to add when item begins to get displayed.'
},
{
name: 'enterActiveClassName ',
type: 'string',
default: 'null',
description: 'Style class to add during enter animation.'
},
{
name: 'enterToClassName',
type: 'string',
default: 'null',
description: 'Style class to add when enter animation is completed.'
},
{
name: 'leaveClassName',
type: 'string',
default: 'null',
description: 'Style class to add when item begins to get hidden.'
},
{
name: 'leaveActiveClassName',
type: 'string',
default: 'null',
description: 'Style class to add during leave animation.'
},
{
name: 'leaveToClassName',
type: 'string',
default: 'null',
description: 'Style class to add when leave animation is completed..'
},
{
name: 'hideOnOutsideClick',
type: 'boolean',
default: 'false',
description: 'Whether to trigger leave animation when outside of the element is clicked.'
},
{
name: 'toggleClassName',
type: 'string',
default: 'null',
description: 'Adds or removes a class when no enter-leave animation is required.'
}
];

const StyleClassEvents = [];

const StyleClassStyles = [];

module.exports = {
steps: {
name: 'StyleClass',
description: 'StyleClass manages css classes declaratively to during enter/leave animations or just to toggle classes on an element.',
docUrl: 'https://primefaces.org/primereact/showcase/#/styleclass',
props: StyleClassProps,
events: StyleClassEvents,
styles: StyleClassStyles
}
};
8 changes: 8 additions & 0 deletions public/showcase/menu/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,14 @@
"terminal"
],
"badge": "new"
},
{
"name": "StyleClass",
"to": "/styleclass",
"meta": [
"styleclass"
],
"badge": "new"
}
]
}
Expand Down
2 changes: 2 additions & 0 deletions src/AppRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ import { BlockUIDemo } from './showcase/blockui/BlockUIDemo';
import { TerminalDemo } from './showcase/terminal/TerminalDemo';
import { DockDemo } from './showcase/dock/DockDemo';
import { MentionDemo } from './showcase/mention/MentionDemo';
import { StyleClassDemo } from './showcase/styleclass/StyleClassDemo';

class AppRouter extends Component {

Expand Down Expand Up @@ -387,6 +388,7 @@ class AppRouter extends Component {
<Route path="/terminal" component={TerminalDemo} />
<Route path="/dock" component={DockDemo} />
<Route path="/mention" component={MentionDemo} />
<Route path="/styleclass" component={StyleClassDemo} />
</>
)
}
Expand Down
19 changes: 19 additions & 0 deletions src/components/styleclass/StyleClass.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';
import { StyleClass } from '../styleclass';

type StyleClassSelectorType = '@next' | '@prev' | '@parent' | '@grandparent' | string ;

export interface StyleClassProps {
nodeRef?: React.ReactNode,
selector?: StyleClassSelectorType,
enterClassName?: string,
enterActiveClassName?: string,
enterToClassName?: string,
leaveClassName?: string,
leaveActiveClassName?: string,
leaveToClassName?: string,
hideOnOutsideClick?: boolean,
toggleClassName?: string
}

export declare class StyleClass extends React.Component<StyleClassProps, any> { }
231 changes: 231 additions & 0 deletions src/components/styleclass/StyleClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import { DomHandler } from '../utils/Utils';


export class StyleClass extends Component {

static defaultProps = {
nodeRef: null,
selector: null,
enterClassName: null,
enterActiveClassName: null,
enterToClassName: null,
leaveClassName: null,
leaveActiveClassName: null,
leaveToClassName: null,
hideOnOutsideClick: false,
toggleClassName: null
}

static propTypes = {
nodeRef: PropTypes.any,
selector: PropTypes.string,
enterClassName: PropTypes.string,
enterActiveClassName: PropTypes.string,
enterToClassName: PropTypes.string,
leaveClassName: PropTypes.string,
leaveActiveClassName: PropTypes.string,
leaveToClassName: PropTypes.string,
hideOnOutsideClick: PropTypes.bool,
toggleClassName: PropTypes.string
}

enter() {
if (this.props.enterActiveClassName) {
if (!this.animating) {
this.animating = true;

if (this.props.enterActiveClassName === 'slidedown') {
this.target.style.height = '0px';
DomHandler.removeClass(this.target, 'hidden');
this.target.style.maxHeight = this.target.scrollHeight + 'px';
DomHandler.addClass(this.target, 'hidden');
this.target.style.height = '';
}

DomHandler.addClass(this.target, this.props.enterActiveClassName);
if (this.props.enterClassName) {
DomHandler.removeClass(this.target, this.props.enterClassName);
}

this.enterListener = () => {
DomHandler.removeClass(this.target, this.props.enterActiveClassName);
if (this.props.enterToClassName) {
DomHandler.addClass(this.target, this.props.enterToClassName);
}
this.target.removeEventListener('animationend', this.enterListener);

if (this.props.enterActiveClassName === 'slidedown') {
this.target.style.maxHeight = '';
}
this.animating = false;
};

this.target.addEventListener('animationend', this.enterListener);
}
}
else {
if (this.props.enterClassName) {
DomHandler.removeClass(this.target, this.props.enterClassName);
}

if (this.props.enterToClassName) {
DomHandler.addClass(this.target, this.props.enterToClassName);
}
}

if (this.props.hideOnOutsideClick) {
this.bindDocumentListener();
}
}

leave() {
if (this.props.leaveActiveClassName) {
if (!this.animating) {
this.animating = true;
DomHandler.addClass(this.target, this.props.leaveActiveClassName);
if (this.props.leaveClassName) {
DomHandler.removeClass(this.target, this.props.leaveClassName);
}

this.leaveListener = () => {
DomHandler.removeClass(this.target, this.props.leaveActiveClassName);
if (this.props.leaveToClassName) {
DomHandler.addClass(this.target, this.props.leaveToClassName);
}
this.target.removeEventListener('animationend', this.leaveListener);
this.animating = false;
};

this.target.addEventListener('animationend', this.leaveListener);
}
}
else {
if (this.props.leaveClassName) {
DomHandler.removeClass(this.target, this.props.leaveClassName);
}

if (this.props.leaveToClassName) {
DomHandler.addClass(this.target, this.props.leaveToClassName);
}
}

if (this.props.hideOnOutsideClick) {
this.unbindDocumentListener();
}
}

resolveTarget() {
if (this.target) {
return this.target;
}

switch (this.props.selector) {
case '@next':
return this.el.nextElementSibling;

case '@prev':
return this.el.previousElementSibling;

case '@parent':
return this.el.parentElement;

case '@grandparent':
return this.el.parentElement.parentElement;

default:
return document.querySelector(this.props.selector);
}
}

bindDocumentListener() {
if (!this.documentListener) {
this.documentListener = (event) => {
if (getComputedStyle(this.target).getPropertyValue('position') === 'static') {
this.unbindDocumentListener();
}
else if (!this.el.isSameNode(event.target) && !this.el.contains(event.target) && !this.target.contains(event.target)) {
this.leave();
}
};

this.el.ownerDocument.addEventListener('click', this.documentListener);
}
}

unbindDocumentListener() {
if (this.documentListener) {
this.el.ownerDocument.removeEventListener('click', this.documentListener);
this.documentListener = null;
}
}

bindEvents() {
this.clickListener = () => {
this.target = this.resolveTarget();

if (this.props.toggleClassName) {
if (DomHandler.hasClass(this.target, this.props.toggleClassName))
DomHandler.removeClass(this.target, this.props.toggleClassName);
else
DomHandler.addClass(this.target, this.props.toggleClassName);
}
else {
if (this.target.offsetParent === null)
this.enter();
else
this.leave();
}
}

this.el.addEventListener('click', this.clickListener);
}

unbindEvents() {
if (this.clickListener) {
this.el.removeEventListener('click', this.clickListener);
this.clickListener = null;
}
}

get el() {
const ref = this.props.nodeRef;
if (ref) {
return typeof ref === 'object' && ref.hasOwnProperty('current') ? ref.current : ref;
}

return null;
}

init() {
if (this.el) {
this.bindEvents();
}
}

destroy() {
this.unbindEvents();
this.unbindDocumentListener();
this.target = null;
}

componentDidMount() {
this.init();
}

componentDidUpdate(prevProps, prevState) {
if (prevProps.nodeRef !== this.props.nodeRef) {
this.destroy();
this.init();
}
}

componentWillUnmount() {
this.destroy();
}

render() {
return this.props.children;
}
}
6 changes: 6 additions & 0 deletions src/components/styleclass/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"main": "./styleclass.cjs.js",
"module": "./styleclass.esm.js",
"unpkg": "./styleclass.min.js",
"types": "./styleclass.d.ts"
}
Loading

0 comments on commit d53135b

Please sign in to comment.