This commit is contained in:
Daniil Senkiv 2019-07-08 14:12:45 +03:00
commit a7c526b875
20 changed files with 412 additions and 178 deletions

View File

@ -3,8 +3,12 @@ echo "ASC.Web.Components"
cd ../web/ASC.Web.Components
call npm install
echo "ASC.Web.Components Storybook"
cd ../ASC.Web.Components/example
call npm install
echo "ASC.Web.sln"
cd ../../
cd ../../../
call dotnet build ASC.Web.sln /fl1 /flp1:LogFile=build/ASC.Web.log;Verbosity=Normal
echo "ASC.People"

View File

@ -42,46 +42,33 @@
location / {
proxy_pass http://localhost:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto "http";
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
}
location /api/2.0 {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto "http";
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
}
location /api/2.0/people {
proxy_pass http://localhost:5002;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto "http";
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
}
location /sockjs-node {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://localhost:5001;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

View File

@ -3,17 +3,32 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.3.1",
"asc-web-components": "file:../../../web/ASC.Web.Components/",
"axios": "^0.19.0",
"bootstrap": "4.3.1",
"connected-react-router": "6.5.0",
"history": "4.9.0",
"i18next": "17.0.6",
"i18next-browser-languagedetector": "3.0.1",
"i18next-xhr-backend": "3.0.0",
"jquery": "3.4.1",
"merge": "^1.2.1",
"oidc-client": "^1.7.1",
"node-sass": "^4.12.0",
"oidc-client": "^1.8.2",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-router-bootstrap": "^0.25.0",
"react-router-dom": "^5.0.0",
"react-scripts": "^3.0.1",
"reactstrap": "^8.0.0",
"rimraf": "^2.6.3"
"react-i18next": "10.11.3",
"react-redux": "7.1.0",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-scripts": "3.0.1",
"reactstrap": "8.0.0",
"redux": "4.0.1",
"redux-thunk": "2.3.0",
"universal-cookie": "^4.0.0",
"lodash": "4.17.11",
"lodash-es": "4.17.11"
},
"devDependencies": {
"ajv": "^6.10.0",
@ -21,10 +36,12 @@
"cross-env": "^5.2.0",
"eslint": "^5.16.0",
"eslint-config-react-app": "^4.0.1",
"eslint-plugin-flowtype": "^3.9.0",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.13.0"
"eslint-plugin-flowtype": "^3.11.1",
"eslint-plugin-import": "^2.18.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.14.2",
"redux-devtools-extension": "^2.13.8",
"rimraf": "2.6.3"
},
"eslintConfig": {
"extends": "react-app"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -11,6 +11,7 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i' rel='stylesheet' type='text/css'></link>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.

View File

@ -0,0 +1,26 @@
// Override default variables before the import
$font-family-base: 'Open Sans', sans-serif;
// Import Bootstrap and its default variables
@import '~bootstrap/scss/bootstrap.scss';
html, body {
height: 100%;
}
#root {
min-height: 100%;
position: relative;
main {
width: 100%;
height: 100%;
overflow: hidden;
.pageLoader {
position: fixed;
left: calc(50% - 32px);
top: 35%;
}
}
}

View File

@ -2,6 +2,7 @@ import 'bootstrap/dist/css/bootstrap.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './custom.scss';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

View File

@ -237,7 +237,7 @@ const peopleContent = (userName, department, phone, email, headDepartment, statu
</Container>
)};
storiesOf('EXAMPLES|Row', module)
storiesOf('EXAMPLES|ContentRow', module)
.add('people row', () => {
return(

View File

@ -0,0 +1,21 @@
# ErrorContainer
## Usage
```js
import { ErrorContainer } from 'asc-web-components';
```
#### Description
Used to display full page error
#### Usage
```js
<ErrorContainer>Some error has happened</ErrorContainer>
```
#### Properties
Only children props is available

View File

@ -0,0 +1,16 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import withReadme from 'storybook-readme/with-readme'
import Readme from './README.md'
import { withKnobs, text } from '@storybook/addon-knobs/react';
import { Text, ErrorContainer } from 'asc-web-components';
storiesOf('Components| ErrorContainer', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('base', () => (
<ErrorContainer>
<Text.Headline tag="h2">{text("Headline text", "Some error has happened")}</Text.Headline>
<Text.Body tag="span">{text("Body text", "Try again later")}</Text.Body>
</ErrorContainer>
));

View File

@ -0,0 +1,69 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, boolean, text, select, number } from '@storybook/addon-knobs/react';
import { BooleanValue, createBooleanValue } from 'react-values'
import withReadme from 'storybook-readme/with-readme'
import styled from '@emotion/styled';
import Readme from './README.md'
import { GroupButtonsMenu, Checkbox, DropDownItem, Button } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const createItems = (label, dropDownLabel, menuItemLabel, count) => {
var items =[
{
label: label,
isDropdown: true,
isSeparator: true,
fontWeight: 'bold',
children: [
<DropDownItem label={dropDownLabel}/>,
<DropDownItem label={dropDownLabel}/>,
<DropDownItem label={dropDownLabel}/>
]
}
];
for (var i=0; i<count; i++){
items.push({label:menuItemLabel});
}
return items;
}
storiesOf('Components|GroupButtonsMenu', module)
.addDecorator(withReadme(Readme))
.addDecorator(withKnobs)
.add('base', () => {
const elements = 10;
const selectLabel = 'Select';
const dropLabel = 'Dropdown item';
const menuItemLabel = 'Menu item';
const menuItems = createItems(selectLabel, dropLabel, menuItemLabel, elements);
return (
<GroupButtonsMenuContainer>
<GroupButtonsMenu checkBox={
<BooleanValue>
{({ value, toggle }) => (
<Checkbox isChecked={value}
onChange={e => {
console.log(e.target.value+' is checked');
toggle(e.target.checked);
}}
isDisabled={false}
value='Checkbox'
id='check1' />)}
</BooleanValue>}
menuItems={menuItems}
visible={true}
moreLabel={text('moreLabel', 'More')}
closeTitle={text('closeTitle', 'Close')}
/>
</GroupButtonsMenuContainer>
);
});

View File

@ -1,64 +0,0 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import withReadme from 'storybook-readme/with-readme'
import styled, { css } from '@emotion/styled';
import Readme from './README.md'
import { GroupButtonsMenu, GroupButton, DropDownItem } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const Checkbox = styled.input`
vertical-align: middle;
margin-left: 24px;
`;
storiesOf('Components|GroupButtonsMenu', module)
.addDecorator(withReadme(Readme))
.add('empty', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu />
</GroupButtonsMenuContainer>
))
.add('documents', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu needCollapse>
<Checkbox name="checkbox" type="checkbox" />
<GroupButton label='Select' isDropdown isSeparator fontWeight='bold'>
<DropDownItem label='All'/>
<DropDownItem label='Files'/>
<DropDownItem label='Folders'/>
<DropDownItem label='Documents'/>
<DropDownItem label='Presentations'/>
<DropDownItem label='Images'/>
<DropDownItem label='Archives'/>
</GroupButton>
<GroupButton label='Share'/>
<GroupButton label='Download' />
<GroupButton label='Download as'/>
<GroupButton label='Move'/>
<GroupButton label='Copy'/>
<GroupButton label='Delete'/>
</GroupButtonsMenu>
</GroupButtonsMenuContainer>
))
.add('people', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu needCollapse>
<Checkbox name="checkbox" type="checkbox" />
<GroupButton label='Select' isDropdown isSeparator fontWeight='bold'>
<DropDownItem label='Active'/>
<DropDownItem label='Disabled'/>
<DropDownItem label='Invited'/>
</GroupButton>
<GroupButton label='Make employee' />
<GroupButton label='Make guest' />
<GroupButton label='Set active' />
<GroupButton label='Set disabled' />
<GroupButton label='Invite again' />
<GroupButton label='Send e-mail' />
<GroupButton label='Delete' />
</GroupButtonsMenu>
</GroupButtonsMenuContainer>
));

View File

@ -0,0 +1,76 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, text} from '@storybook/addon-knobs/react';
import { BooleanValue } from 'react-values'
import styled from '@emotion/styled';
import { GroupButtonsMenu, Checkbox, DropDownItem } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const peopleItems = [
{
label: 'Select',
isDropdown: true,
isSeparator: true,
fontWeight: 'bold',
children: [
<DropDownItem label='Active'/>,
<DropDownItem label='Disabled'/>,
<DropDownItem label='Invited'/>
]
},
{
label: 'Make employee',
action: () => console.log('Make employee action')
},
{
label: 'Make guest',
action: () => console.log('Make guest action')
},
{
label: 'Set active',
action: () => console.log('Set active action')
},
{
label: 'Set disabled',
action: () => console.log('Set disabled action')
},
{
label: 'Invite again',
action: () => console.log('Invite again action')
},
{
label: 'Send e-mail',
action: () => console.log('Send e-mail action')
},
{
label: 'Delete',
action: () => console.log('Delete action')
}
];
storiesOf('EXAMPLES|GroupButtonsMenu', module)
.addDecorator(withKnobs)
.add('people', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu checkBox={
<BooleanValue>
{({ value, toggle }) => (
<Checkbox isChecked={value}
onChange={e => {
console.log(e.target.value);
toggle(e.target.checked);
}}
isDisabled={false}
value='Checkbox'
id='check1' />)}
</BooleanValue>}
menuItems={peopleItems}
visible={true}
moreLabel={text('moreLabel', 'More')}
closeTitle={text('closeTitle', 'Close')}
/>
</GroupButtonsMenuContainer>
));

View File

@ -16,6 +16,7 @@ const StyledDropdownItem = styled.button`
margin: ${props => (props.isSeparator ? '0 16px' : '0')};
padding: ${props => (props.isUserPreview ? '0px' : '0 16px')};
text-decoration: none;
display: block;
user-select: none;
-o-user-select: none;

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@ import styled, { css } from 'styled-components'
import PropTypes from 'prop-types'
import { Icons } from '../icons'
import DropDown from '../drop-down'
import Checkbox from '../checkbox'
const textColor = '#333333',
disabledTextColor = '#A3A9AE';
@ -88,7 +89,7 @@ const useOuterClickNotifier = (onOuterClick, ref) => {
}
const GroupButton = (props) => {
const { label, isDropdown, opened, disabled, action, isSeparator } = props;
const { label, isDropdown, opened, disabled, action, isSeparator} = props;
const [isOpen, toggle] = useState(opened);
const ref = useRef(null);

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect, useRef } from 'react'
import React from 'react'
import styled from 'styled-components'
import PropTypes from 'prop-types'
import GroupButton from '../group-button'
import DropDownItem from '../drop-down-item'
const StyledGroupButtonsMenu = styled.div`
position: sticky;
@ -11,10 +12,9 @@ const StyledGroupButtonsMenu = styled.div`
height: 56px;
list-style: none;
padding: 0 18px 19px 0;
.hidden {
display: none;
}
width: 100%;
white-space: nowrap;
display: ${state => state.visible ? 'block' : 'none'};
`;
const CloseButton = styled.div`
@ -46,84 +46,108 @@ const CloseButton = styled.div`
}
`;
const padding = 4;
var lastWidth = 0;
const CheckBox = styled.div`
display: inline-block;
margin-left: 20px;
vertical-align: middle;
const collapseButtons = (menu) => {
if (menu == undefined) return;
& > * {
margin: 0px;
}
`;
class GroupButtonsMenu extends React.Component {
constructor(props) {
super(props);
this.updateMenu = this.updateMenu.bind(this);
this.state = {
priorityItems: [],
moreItems: [],
visible: true
}
this.fullMenuArray = this.props.menuItems;
this.checkBox = this.props.checkBox;
}
var groupMenu = menu,
groupMenuWidth = groupMenu.clientWidth,
groupButtons = groupMenu.querySelectorAll('div[class*="-0"]:not(.more):not(.hidden)'),
groupButtonsMore = groupMenu.querySelector('div.more[class*="-0"]'),
groupButtonsMoreWidth = groupButtonsMore.clientWidth || 0,
groupButtonsWidthArray = getButtonsWidthArray(groupButtons),
groupButtonsWidth = getButtonsWidth(groupButtonsWidthArray),
lastHidden = getLastHidden(groupMenu),
lastHiddenWidth = (lastHidden != undefined) ? lastHidden.clientWidth : 0,
lastGroupButton = groupButtons[groupButtons.length -1],
moreThanMenu = groupButtonsWidth + groupButtonsMoreWidth - lastHiddenWidth,
lessThanMenu = groupButtonsWidth + groupButtonsMoreWidth + lastHiddenWidth + lastGroupButton.clientWidth * 1.3;
if (lastWidth !== 0 && lastWidth > groupMenuWidth){
if (moreThanMenu > groupMenuWidth) {
lastGroupButton.classList.add('hidden');
}
} else {
if (lessThanMenu < groupMenuWidth && lastHidden != undefined) {
lastHidden.classList.remove('hidden');
}
componentWillMount() {
this.setState({
priorityItems: this.props.menuItems
})
}
if (lastHidden == undefined){
groupButtonsMore.classList.add('hidden');
} else {
groupButtonsMore.classList.remove('hidden');
componentDidMount() {
this.widthsArray = Array.from(this.refs.groupMenu.children).map(item => item.getBoundingClientRect().width);
window.addEventListener('resize', _.throttle(this.updateMenu), 100);
this.updateMenu();
}
howManyItemsInMenuArray(array, outerWidth, initialWidth, minimumNumberInNav) {
let total = (initialWidth+150);
for(let i = 0; i < array.length; i++) {
if(total + array[i] > outerWidth) {
return i < minimumNumberInNav ? minimumNumberInNav : i;
} else {
total += array[i];
}
}
}
lastWidth = groupMenuWidth;
}
updateMenu() {
this.outerWidth = this.refs.groupMenuOuter ? this.refs.groupMenuOuter.getBoundingClientRect().width : 0;
this.moreMenu = this.refs.moreMenu ? this.refs.moreMenu.getBoundingClientRect().width : 0;
const getButtonsWidthArray = (buttons) => {
return Array.prototype.slice.call(buttons).map((button => button.clientWidth + padding * 2));
}
const arrayAmount = this.howManyItemsInMenuArray(this.widthsArray, this.outerWidth, this.moreMenu, 1);
const navItemsCopy = this.fullMenuArray;
const priorityItems = navItemsCopy.slice(0, arrayAmount);
this.setState({
priorityItems: priorityItems,
moreItems: priorityItems.length !== navItemsCopy.length ? navItemsCopy.slice(arrayAmount, navItemsCopy.length) : []
});
}
const getButtonsWidth = (buttonsWidthArray) => {
return buttonsWidthArray.reduce((a,b) => a + b);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateMenu());
}
const getLastHidden = (buttons) => {
return buttons.querySelectorAll('div[class*="-0"].hidden:not(.more)')[0];
}
render() {
const { priorityItems, moreItems } = this.state;
const GroupButtonsMenu = props => {
const { children, needCollapse } = props;
const ref = useRef(null);
const toggle = () => this.setState({visible: !this.props.visible});
const collapseEvent = (e) => (React.Children.toArray(props.children).length && needCollapse)
? collapseButtons(ref.current)
: e.preventDefault();
useEffect(() => {
window.addEventListener('load', collapseEvent(event));
window.onresize = (e) => collapseEvent(e);
});
return (
<StyledGroupButtonsMenu ref={ref} {...props}>
{children}
{needCollapse && <GroupButton className="more" isDropdown label='More'>{children}</GroupButton>}
<CloseButton/>
</StyledGroupButtonsMenu>
);
}
GroupButtonsMenu.propTypes = {
needCollapse: PropTypes.bool
}
GroupButtonsMenu.defaultProps = {
needCollapse: false
return (
<StyledGroupButtonsMenu ref="groupMenuOuter" visible={this.state.visible} {...this.state}>
{this.checkBox &&
<CheckBox>{this.checkBox}</CheckBox>
}
<div ref="groupMenu" style={{display: 'inline-block'}}>
{priorityItems.map((item, i) =>
<GroupButton key={`navItem-${i}`}
label={item.label}
isDropdown={item.isDropdown}
isSeparator={item.isSeparator}
fontWeight={item.fontWeight}
action={item.action}>
{item.children}
</GroupButton>
)}
</div>
{moreItems.length > 0 &&
<GroupButton ref="moreMenu" isDropdown label={this.props.moreLabel}>
{moreItems.map((item, i) =>
<DropDownItem
key={`moreNavItem-${i}`}
label={item.label}
onClick={item.onClick} />
)}
</GroupButton>
}
<CloseButton title={this.props.closeTitle} onClick={() => toggle()} />
</StyledGroupButtonsMenu>
);
}
}
export default GroupButtonsMenu;

View File

@ -22,4 +22,5 @@ export { Text } from './components/text'
export { default as ModalDialog } from './components/modal-dialog'
export { default as Layout } from './components/layout'
export { default as ContentRow } from './components/content-row'
export { default as Badge } from './components/badge'
export { default as Badge } from './components/badge'
export { default as ErrorContainer } from './components/error-container'