feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev
2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions
@@ -0,0 +1,85 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback } from 'react';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { Icon } from 'semantic-ui-react';
import { usePopup } from '../../../../lib/popup';
import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { HomeViews } from '../../../../constants/Enums';
import { HomeViewIcons, ProjectOrderIcons } from '../../../../constants/Icons';
import SelectOrderStep from './SelectOrderStep';
import styles from './RightSide.module.scss';
const RightSide = React.memo(() => {
const currentView = useSelector(selectors.selectHomeView); // TODO: rename?
const currentOrder = useSelector(selectors.selectProjectsOrder); // TODO: rename?
const isHiddenVisible = useSelector(selectors.selectIsHiddenProjectsVisible);
const dispatch = useDispatch();
const handleSelectViewClick = useCallback(
({ currentTarget: { value: view } }) => {
dispatch(entryActions.updateHomeView(view));
},
[dispatch],
);
const handleOrderSelect = useCallback(
(order) => {
dispatch(entryActions.updateProjectsOrder(order));
},
[dispatch],
);
const handleToggleHiddenClick = useCallback(() => {
dispatch(entryActions.toggleHiddenProjects(!isHiddenVisible));
}, [isHiddenVisible, dispatch]);
const SelectOrderPopup = usePopup(SelectOrderStep);
return (
<>
<div className={styles.action}>
<button
type="button"
className={classNames(styles.button)}
onClick={handleToggleHiddenClick}
>
<Icon fitted name={isHiddenVisible ? 'eye slash' : 'eye'} />
</button>
</div>
<div className={styles.action}>
<SelectOrderPopup value={currentOrder} onSelect={handleOrderSelect}>
<button type="button" className={styles.button}>
<Icon fitted name={ProjectOrderIcons[currentOrder]} />
</button>
</SelectOrderPopup>
</div>
<div className={styles.action}>
<div className={styles.buttonGroup}>
{[HomeViews.GRID_PROJECTS, HomeViews.GROUPED_PROJECTS].map((view) => (
<button
key={view}
type="button"
value={view}
disabled={view === currentView}
className={styles.button}
onClick={handleSelectViewClick}
>
<Icon fitted name={HomeViewIcons[view]} />
</button>
))}
</div>
</div>
</>
);
});
export default RightSide;
@@ -0,0 +1,52 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
:global(#app) {
.action:not(:last-child) {
margin-right: 10px;
}
.button {
background: rgba(0, 0, 0, 0.24);
border: none;
border-radius: 3px;
color: rgba(255, 255, 255, 0.72);
cursor: pointer;
display: inline-block;
font-size: 13px;
line-height: 20px;
outline: none;
padding: 8px 15px;
width: 43px;
&:hover {
background: rgba(0, 0, 0, 0.32);
}
}
.buttonGroup {
.button {
border-radius: 0;
&:enabled {
background: rgba(0, 0, 0, 0.08);
&:hover {
background: rgba(0, 0, 0, 0.12);
}
}
&:first-child {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
&:last-child {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
}
}
}
@@ -0,0 +1,68 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Icon, Menu } from 'semantic-ui-react';
import { Popup } from '../../../../lib/custom-ui';
import { ProjectOrders } from '../../../../constants/Enums';
import { ProjectOrderIcons } from '../../../../constants/Icons';
import styles from './SelectOrderStep.module.scss';
const SelectOrderStep = React.memo(({ value, onSelect, onClose }) => {
const [t] = useTranslation();
const handleSelectClick = useCallback(
(_, { value: nextValue }) => {
if (nextValue !== value) {
onSelect(nextValue);
}
onClose();
},
[value, onSelect, onClose],
);
return (
<>
<Popup.Header>
{t('common.selectOrder', {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<Menu secondary vertical className={styles.menu}>
{[
ProjectOrders.BY_DEFAULT,
ProjectOrders.ALPHABETICALLY,
ProjectOrders.BY_CREATION_TIME,
].map((order) => (
<Menu.Item
key={order}
value={order}
active={order === value}
className={styles.menuItem}
onClick={handleSelectClick}
>
<Icon name={ProjectOrderIcons[order]} className={styles.menuItemIcon} />
{t(`common.${order}`)}
</Menu.Item>
))}
</Menu>
</Popup.Content>
</>
);
});
SelectOrderStep.propTypes = {
value: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default SelectOrderStep;
@@ -0,0 +1,20 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
:global(#app) {
.menu {
margin: 0 auto;
width: 100%;
}
.menuItem:last-child {
margin-bottom: 0;
}
.menuItemIcon {
float: left;
margin: 0 8px 0 0;
}
}
@@ -0,0 +1,8 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import RightSide from './RightSide';
export default RightSide;