Merge branch 'master' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore
This commit is contained in:
commit
f4b161b65f
@ -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"
|
||||
|
@ -3,6 +3,7 @@ using System.Security.Authentication;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using ASC.Core;
|
||||
using ASC.Web.Core;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -26,6 +27,10 @@ namespace ASC.Web.Api.Handlers
|
||||
{
|
||||
var token = Context.Request.Cookies["asc_auth_key"] ?? Context.Request.Headers["Authorization"];
|
||||
var result = SecurityContext.AuthenticateMe(token);
|
||||
if (result)
|
||||
{
|
||||
Context.SetCookies(CookiesType.AuthKey, token);
|
||||
}
|
||||
|
||||
return Task.FromResult(
|
||||
result ?
|
||||
|
@ -68,7 +68,8 @@ namespace ASC.Common.DependencyInjection
|
||||
void LoadAssembly(string type)
|
||||
{
|
||||
var dll = type.Substring(type.IndexOf(",") + 1).Trim();
|
||||
var path = Directory.GetFiles(productsDir, $"{dll}.dll", SearchOption.AllDirectories).FirstOrDefault();
|
||||
var productPath = Path.Combine(productsDir, dll);
|
||||
var path = GetPath(Path.Combine(productPath, "bin"), dll, SearchOption.AllDirectories) ?? GetPath(productPath, dll, SearchOption.TopDirectoryOnly);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
@ -83,6 +84,13 @@ namespace ASC.Common.DependencyInjection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string GetPath(string dirPath, string dll, SearchOption searchOption)
|
||||
{
|
||||
if (!Directory.Exists(dirPath)) return null;
|
||||
|
||||
return Directory.GetFiles(dirPath, $"{dll}.dll", searchOption).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,14 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using ASC.Common.Utils;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ASC.Core.Common.Resources
|
||||
{
|
||||
public class JsonResourceManager
|
||||
{
|
||||
private static string DirName { get; set; }
|
||||
static JsonResourceManager()
|
||||
{
|
||||
DirName = "ClientApp";
|
||||
}
|
||||
private const string ClientApp = "ClientApp";
|
||||
private const string Locales = "locales";
|
||||
|
||||
public string FileName { get; }
|
||||
|
||||
@ -56,8 +52,8 @@ namespace ASC.Core.Common.Resources
|
||||
|
||||
JObject FromFile(string culture)
|
||||
{
|
||||
var dirPath = Path.GetFullPath(DirName);
|
||||
if(!Directory.Exists(dirPath)) return new JObject();
|
||||
var dirPath = GetDirName(Path.Combine(ClientApp, "build", Locales)) ?? GetDirName(Path.Combine(ClientApp, "public", Locales));
|
||||
if(string.IsNullOrEmpty(dirPath)) return new JObject();
|
||||
|
||||
var files = Directory.GetFiles(dirPath, FileName, SearchOption.AllDirectories);
|
||||
if (!files.Any()) return new JObject();
|
||||
@ -67,5 +63,11 @@ namespace ASC.Core.Common.Resources
|
||||
|
||||
return JObject.Parse(File.ReadAllText(filePath));
|
||||
}
|
||||
|
||||
string GetDirName(string dirName)
|
||||
{
|
||||
var dirPath = Path.GetFullPath(dirName);
|
||||
return Directory.Exists(dirPath) ? dirPath : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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 |
@ -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.
|
||||
|
26
products/ASC.People/ClientApp/src/custom.scss
Normal file
26
products/ASC.People/ClientApp/src/custom.scss
Normal 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%;
|
||||
}
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
||||
|
@ -71,7 +71,11 @@ namespace ASC.People
|
||||
.AddWebItemManager()
|
||||
.AddScoped<MessageService>()
|
||||
.AddScoped<QueueWorkerReassign>()
|
||||
.AddScoped<QueueWorkerRemove>();
|
||||
.AddScoped<QueueWorkerRemove>()
|
||||
.AddSpaStaticFiles(configuration =>
|
||||
{
|
||||
configuration.RootPath = "ClientApp/build";
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
@ -113,6 +117,19 @@ namespace ASC.People
|
||||
app.UseCSP();
|
||||
app.UseCm();
|
||||
app.UseWebItemManager();
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseSpaStaticFiles();
|
||||
|
||||
app.UseSpa(spa =>
|
||||
{
|
||||
spa.Options.SourcePath = "ClientApp";
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
spa.UseReactDevelopmentServer(npmScript: "start");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
@ -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>
|
||||
));
|
@ -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>
|
||||
);
|
||||
});
|
@ -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>
|
||||
));
|
@ -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>
|
||||
));
|
@ -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
@ -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);
|
||||
|
||||
|
@ -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;
|
@ -1,26 +1,24 @@
|
||||
import React from 'react';
|
||||
import React, {useState, useRef, useEffect} from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icons } from '../icons'
|
||||
|
||||
const SimpleLink = ({ rel, isBold, fontSize, isTextOverflow, isHovered, isSemitransparent, type, color, text, target, dropdownType, ...props }) => <a {...props}>{text}</a>;
|
||||
|
||||
const arrowDropdown = css`
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: ${props =>
|
||||
(props.color === 'black' && '4px solid #333333') ||
|
||||
(props.color === 'gray' && '4px solid #A3A9AE') ||
|
||||
(props.color === 'blue' && '4px solid #316DAA')
|
||||
};
|
||||
content: "";
|
||||
height: 0;
|
||||
position: absolute;
|
||||
right: -15px;
|
||||
top: 1px;
|
||||
bottom: 0px;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
margin-top: -2px;
|
||||
const getDropdownColor = color => {
|
||||
switch (color) {
|
||||
case 'gray':
|
||||
return '#A3A9AE';
|
||||
case 'blue':
|
||||
return '#316DAA';
|
||||
default:
|
||||
return '#333333';
|
||||
}
|
||||
}
|
||||
|
||||
const opacityCss = css `
|
||||
opacity: ${props =>
|
||||
(props.isSemitransparent && '0.5')};
|
||||
`;
|
||||
|
||||
const colorCss = css`
|
||||
@ -45,6 +43,13 @@ const dottedCss = css`
|
||||
border-bottom: 1px dotted;
|
||||
`;
|
||||
|
||||
const Caret = styled(Icons.ExpanderDownIcon)`
|
||||
width: 10px;
|
||||
margin-left: 5px;
|
||||
margin-top: -4px;
|
||||
${opacityCss};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(SimpleLink).attrs((props) => ({
|
||||
href: props.href,
|
||||
target: props.target,
|
||||
@ -52,6 +57,7 @@ const StyledLink = styled(SimpleLink).attrs((props) => ({
|
||||
title: props.title
|
||||
}))`
|
||||
${colorCss};
|
||||
${opacityCss};
|
||||
font-size: ${props => props.fontSize}px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@ -91,13 +97,6 @@ ${props => (props.type === 'action' && (props.isHovered || props.dropdownType ==
|
||||
${dottedCss}
|
||||
`)
|
||||
}
|
||||
|
||||
${props => (props.type === 'action' && props.dropdownType === 'alwaysDotted' &&
|
||||
css`
|
||||
&:after {
|
||||
${arrowDropdown}
|
||||
}`)
|
||||
}
|
||||
|
||||
${props => (props.isTextOverflow &&
|
||||
css`
|
||||
@ -110,26 +109,20 @@ ${props => (props.isTextOverflow &&
|
||||
`)
|
||||
}
|
||||
|
||||
${props => (props.type === 'action' && props.dropdownType === 'appearDottedAfterHover' &&
|
||||
css`
|
||||
&:hover {
|
||||
:after {
|
||||
${arrowDropdown}
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
${props => (props.isSemitransparent
|
||||
&&
|
||||
css`
|
||||
opacity: 0.5;
|
||||
`)
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const Link = props => <StyledLink {...props} />;
|
||||
const Link = props => {
|
||||
const [isHovered, toggle] = useState(false);
|
||||
|
||||
return (
|
||||
<span
|
||||
onMouseEnter={() => {props.dropdownType === 'appearDottedAfterHover' && toggle(!isHovered)}}
|
||||
onMouseLeave={() => {props.dropdownType === 'appearDottedAfterHover' && toggle(!isHovered)}}>
|
||||
<StyledLink {...props} />
|
||||
{(props.dropdownType === 'alwaysDotted' || (isHovered && props.dropdownType === 'appearDottedAfterHover')) && <Caret {...props} size='small' isfill={true} color={getDropdownColor(props.color)} /> }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
Link.propTypes = {
|
||||
color: PropTypes.oneOf(['gray', 'black', 'blue']),
|
||||
@ -149,6 +142,7 @@ Link.propTypes = {
|
||||
|
||||
Link.defaultProps = {
|
||||
color: 'black',
|
||||
dropdownType: 'none',
|
||||
fontSize: 12,
|
||||
href: undefined,
|
||||
isBold: false,
|
||||
|
@ -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'
|
@ -27,6 +27,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Web;
|
||||
using ASC.Core;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Core.Users;
|
||||
@ -71,36 +72,45 @@ namespace ASC.Web.Core
|
||||
{
|
||||
if (httpContext == null) return;
|
||||
|
||||
//TODO
|
||||
//httpContext.Response.Cookies[GetCookiesName(type)].Value = value;
|
||||
//httpContext.Response.Cookies[GetCookiesName(type)].Expires = GetExpiresDate(session);
|
||||
var options = new CookieOptions
|
||||
{
|
||||
Expires = GetExpiresDate(session)
|
||||
};
|
||||
|
||||
//if (type == CookiesType.AuthKey)
|
||||
//{
|
||||
// httpContext.Response.Cookies[GetCookiesName(type)].HttpOnly = true;
|
||||
if (type == CookiesType.AuthKey)
|
||||
{
|
||||
options.HttpOnly = true;
|
||||
|
||||
// if (httpContext.Request.GetUrlRewriter().Scheme == "https")
|
||||
// httpContext.Response.Cookies[GetCookiesName(type)].Secure = true;
|
||||
if (httpContext.Request.GetUrlRewriter().Scheme == "https")
|
||||
{
|
||||
options.Secure = true;
|
||||
}
|
||||
}
|
||||
|
||||
//}
|
||||
httpContext.Response.Cookies.Append(GetCookiesName(type), value, options);
|
||||
}
|
||||
|
||||
public static void SetCookies(this HttpContext httpContext, CookiesType type, string value, string domain, bool session = false)
|
||||
{
|
||||
if (httpContext == null) return;
|
||||
|
||||
//TODO
|
||||
//httpContext.Response.Cookies[GetCookiesName(type)].Value = value;
|
||||
//httpContext.Response.Cookies[GetCookiesName(type)].Domain = domain;
|
||||
//httpContext.Response.Cookies[GetCookiesName(type)].Expires = GetExpiresDate(session);
|
||||
var options = new CookieOptions
|
||||
{
|
||||
Expires = GetExpiresDate(session),
|
||||
Domain = domain
|
||||
};
|
||||
|
||||
//if (type == CookiesType.AuthKey)
|
||||
//{
|
||||
// httpContext.Response.Cookies[GetCookiesName(type)].HttpOnly = true;
|
||||
if (type == CookiesType.AuthKey)
|
||||
{
|
||||
options.HttpOnly = true;
|
||||
|
||||
// if (httpContext.Request.GetUrlRewriter().Scheme == "https")
|
||||
// httpContext.Response.Cookies[GetCookiesName(type)].Secure = true;
|
||||
//}
|
||||
if (httpContext.Request.GetUrlRewriter().Scheme == "https")
|
||||
{
|
||||
options.Secure = true;
|
||||
}
|
||||
}
|
||||
|
||||
httpContext.Response.Cookies.Append(GetCookiesName(type), value, options);
|
||||
}
|
||||
|
||||
public static string GetCookies(this HttpContext httpContext, CookiesType type)
|
||||
@ -109,9 +119,8 @@ namespace ASC.Web.Core
|
||||
{
|
||||
var cookieName = GetCookiesName(type);
|
||||
|
||||
//TODO
|
||||
//if (httpContext.Request.Cookies[cookieName] != null)
|
||||
// return httpContext.Request.Cookies[cookieName].Value ?? "";
|
||||
if (httpContext.Request.Cookies.ContainsKey(cookieName))
|
||||
return httpContext.Request.Cookies[cookieName] ?? "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@ -120,9 +129,10 @@ namespace ASC.Web.Core
|
||||
{
|
||||
if (httpContext == null) return;
|
||||
|
||||
//TODO
|
||||
//if (httpContext.Request.Cookies[GetCookiesName(type)] != null)
|
||||
// httpContext.Response.Cookies[GetCookiesName(type)].Expires = DateTime.Now.AddDays(-3);
|
||||
if (httpContext.Request.Cookies.ContainsKey(GetCookiesName(type)))
|
||||
{
|
||||
httpContext.Response.Cookies.Delete(GetCookiesName(type), new CookieOptions() { Expires = DateTime.Now.AddDays(-3) });
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTime GetExpiresDate(bool session)
|
||||
|
Loading…
Reference in New Issue
Block a user