Merge branch 'feature/workspaces' of https://github.com/ONLYOFFICE/AppServer into feature/workspaces

This commit is contained in:
Nikita Gopienko 2021-03-04 12:33:06 +03:00
commit 7cec3df259
113 changed files with 7096 additions and 1617 deletions

View File

@ -12,8 +12,9 @@
"scripts": {
"wipe": "rimraf node_modules yarn.lock web/**/node_modules products/**/node_modules",
"build": "yarn workspaces run build",
"start": "concurrently \"wsrun --parallel start\"",
"test": "yarn workspace @appserver/components test"
"start": "concurrently \"wsrun --parallel start\"",
"test": "yarn workspace @appserver/components test",
"sb-components": "yarn workspace @appserver/components storybook"
},
"devDependencies": {
"lerna": "^3.22.1",

View File

@ -15,7 +15,7 @@ import AdvancedSelector2 from ".";
import Section from "../../../.storybook/decorators/section";
import Button from "@appserver/components/button";
import equal from "fast-deep-equal/react";
import UserTooltip from "../PeopleSelector/sub-components/UserTooltip";
import UserTooltip from "studio/PeopleSelector/UserTooltip";
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;

View File

@ -10,8 +10,8 @@ import {
StyledFilterItemContent,
StyledCloseButtonBlock,
} from "../StyledFilterInput";
import GroupSelector from "people/GroupSelector"; //TODO: Move GroupSelector out of FilterItem
import PeopleSelector from "../../PeopleSelector";
import GroupSelector from "people/GroupSelector"; //TODO: Move out GroupSelector of FilterItem
import PeopleSelector from "studio/PeopleSelector"; //TODO: Move out PeopleSelector of FilterItem
class FilterItem extends React.Component {
constructor(props) {

View File

@ -1,11 +1,10 @@
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import Backdrop from "@appserver/components/backdrop";
import ProgressBar from "@appserver/components/progress-bar";
//import ProgressBar from "@appserver/components/progress-bar";
import { size } from "@appserver/components/utils/device";
import { Provider } from "@appserver/components/utils/context";
import store from "../../store";
import { withTranslation } from "react-i18next";
import { isMobile } from "react-device-detect";
import i18n from "./i18n";

View File

@ -1,82 +0,0 @@
/* eslint-disable react/prop-types */
import React from "react";
import { storiesOf } from "@storybook/react";
import { withKnobs, boolean } from "@storybook/addon-knobs/react";
import Section from "../../../.storybook/decorators/section";
import PeopleSelector from ".";
import Button from "@appserver/components/button";
import { text } from "@storybook/addon-knobs";
//import withReadme from "storybook-readme/with-readme";
//import Readme from "./README.md";
class PeopleSelectorExample extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
this.state = {
isOpen: false,
};
}
toggle = () => {
this.setState({
isOpen: !this.state.isOpen,
});
};
onCancel = (e) => {
if (this.buttonRef.current.contains(e.target)) {
console.log("onCancel skipped");
return;
}
console.log("onCancel");
this.toggle();
};
render() {
return (
<div style={{ position: "relative" }}>
<Button
label="Toggle dropdown"
onClick={this.toggle}
ref={this.buttonRef}
/>
<PeopleSelector
isOpen={this.state.isOpen}
useFake={true}
isMultiSelect={boolean("isMultiSelect", true)}
onSelect={(data) => {
console.log("onSelect", data);
this.toggle();
}}
onCancel={this.onCancel}
defaultOption={{
id: "777",
groups: [],
displayName: "Boris Johnson",
avatar: "",
title: "Prime Minister of the United Kingdom",
email: "boris.johnson@example.com",
}}
defaultOptionLabel={text("defaultOptionLabel", "Me")}
/>
</div>
);
}
}
storiesOf("Components|PeopleSelector", module)
.addDecorator(withKnobs)
//.addDecorator(withReadme(Readme))
.addParameters({ options: { addonPanelInRight: false } })
.add("base", () => {
return (
<Section>
<PeopleSelectorExample />
</Section>
);
});

View File

@ -1,19 +0,0 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { i18nBaseSettings } from "../../constants";
const newInstance = i18n.createInstance();
const resources = {
en: {
translation: en,
},
ru: {
translation: ru,
},
};
newInstance.init({ ...i18nBaseSettings, resources });
export default newInstance;

View File

@ -1,2 +0,0 @@
import PeopleSelector from "./PeopleSelector.js";
export default PeopleSelector;

View File

@ -1,19 +0,0 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { i18nBaseSettings } from "../../constants";
const newInstance = i18n.createInstance();
const resources = {
en: {
translation: en,
},
ru: {
translation: ru,
},
};
newInstance.init({ ...i18nBaseSettings, resources });
export default newInstance;

View File

@ -1 +0,0 @@
export default from "./toastr";

View File

@ -2,7 +2,6 @@ export { default as PrivateRoute } from "./PrivateRoute";
export { default as PublicRoute } from "./PublicRoute";
export { default as ExternalRedirect } from "./ExternalRedirect";
export { default as Headline } from "./Headline";
export { default as PeopleSelector } from "./PeopleSelector";
export { default as AdvancedSelector } from "./AdvancedSelector";
export { default as PageLayout } from "./PageLayout";
export { default as ErrorContainer } from "./ErrorContainer";

View File

@ -1,4 +1,4 @@
import toastr from "../components/Toast";
import toastr from "@appserver/components/toast/toastr";
import isEmpty from "lodash/isEmpty";
import omit from "lodash/omit";

View File

@ -137,15 +137,15 @@ class AuthStore {
try {
const response = await api.user.login(user, hash);
console.log("Login response", response);
if (!response || !response.token) throw "Empty API response";
setWithCredentialsStatus(true);
await this.init();
return true;
return Promise.resolve(true);
} catch (e) {
return false;
return Promise.reject(e);
}
};

View File

@ -118,6 +118,8 @@ class SettingsStore {
getPortalTimezones: action,
setHeaderVisible: action,
setIsTabletView: action,
setValue: action,
setArticlePinned: action,
});
}
@ -127,18 +129,21 @@ class SettingsStore {
return `https://helpcenter.onlyoffice.com/${lang}/installation/groups-authorization-keys.aspx`;
}
setValue = (key, value) => {
this[key] = value;
};
getSettings = async () => {
const newSettings = await api.settings.getSettings();
Object.keys(newSettings).map((key) => {
if (key in this) {
this[key] = newSettings[key];
this.setValue(key, newSettings[key]);
if (key === "culture" && !localStorage.getItem(LANGUAGE)) {
localStorage.setItem(LANGUAGE, newSettings[key]);
}
} else if (key === "passwordHash") {
this.hashSettings = newSettings[key];
this.setValue("hashSettings", newSettings[key]);
}
});

View File

@ -96,20 +96,37 @@ export function updateTempContent(isAuth = false) {
export function hideLoader() {
if (isMobile) return;
if (window.loadingTimeout) {
clearTimeout(window.loadingTimeout);
window.loadingTimeout = null;
}
// if (window.loadingTimeout) {
// clearTimeout(window.loadingTimeout);
// window.loadingTimeout = null;
// }
document.body.classList.remove("loading");
// document.body.classList.remove("loading");
const ele = document.getElementById("ipl-progress-indicator");
if (ele) {
// fade out
ele.classList.add("available");
ele.style.display = "";
// setTimeout(() => {
// // remove from DOM
// ele.outerHTML = "";
// }, 2000);
}
}
export function showLoader() {
if (isMobile) return;
window.loadingTimeout = setTimeout(() => {
document.body.classList.add("loading");
}, 1000);
// window.loadingTimeout = setTimeout(() => {
// document.body.classList.add("loading");
// }, 1000);
const ele = document.getElementById("ipl-progress-indicator");
if (ele) {
ele.classList.remove("available");
ele.style.display = "block";
}
}
export { withLayoutSize } from "./withLayoutSize";

View File

@ -0,0 +1,31 @@
module.exports = {
stories: [
"../backdrop/*.stories.@(js|jsx|ts|tsx)",
"../button/*.stories.@(js|jsx|ts|tsx)",
"../avatar/*.stories.@(js|jsx|ts|tsx)",
"../badge/*.stories.@(js|jsx|ts|tsx)",
"../box/*.stories.@(js|jsx|ts|tsx)",
"../avatar-editor/*.stories.@(js|jsx|ts|tsx)",
"../calendar/*.stories.@(js|jsx|ts|tsx)",
"../checkbox/*.stories.@(js|jsx|ts|tsx)",
"../combobox/*.stories.@(js|mdx)",
"../context-menu/*.stories.@(js|mdx)",
"../context-menu-button/*.stories.@(js|mdx)",
"../date-picker/*.stories.@(js|mdx)",
"../drag-and-drop/*.stories.@(js|mdx)",
"../drop-down/*.stories.@(js|mdx)",
"../drop-down-item/*.stories.@(js|mdx)",
"../email-input/*.stories.@(js|mdx)",
"../empty-screen-container/*.stories.@(js|mdx)",
"../field-container/*.stories.@(js|mdx)",
"../file-input/*.stories.@(js|mdx)",
"../grid/*.stories.@(js|mdx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-docs",
"@storybook/addon-essentials",
"@storybook/addon-actions",
"@storybook/addon-controls",
],
};

View File

@ -0,0 +1,17 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: { expanded: true },
};
export const globalTypes = {
theme: {
name: "Theme",
description: "Global theme for components",
defaultValue: "light",
toolbar: {
icon: "circlehollow",
// array of plain string values or MenuItem shape (see below)
items: ["light", "dark"],
},
},
};

View File

@ -0,0 +1,22 @@
module.exports = ({ config }) => {
const rules = config.module.rules;
const fileLoaderRule = rules.find((rule) => rule.test.test(".svg"));
fileLoaderRule.exclude = /\.react.svg$/;
rules.push({
test: /\.react.svg$/,
use: [
{
loader: "@svgr/webpack",
options: {
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
},
},
],
});
return config;
};

View File

@ -1,16 +1,87 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { withKnobs, text, select } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import AvatarEditor from ".";
import AvatarEditor from "./";
import Avatar from "../avatar";
import Section from "../../../.storybook/decorators/section";
import { boolean } from "@storybook/addon-knobs";
const displayType = ["auto", "modal", "aside"];
class AvatarEditorStory extends React.Component {
export default {
title: "Components/AvatarEditor",
component: AvatarEditor,
argTypes: {
visible: { description: "Display avatar editor" },
image: { description: "Display avatar editor" },
accept: { description: "Accepted file types" },
displayType: { description: "Display type" },
useModalDialog: {
description: "Use for the view of the modal dialog or not",
},
selectNewPhotoLabel: {
description: "Translation string for file selection",
},
orDropFileHereLabel: {
description:
"Translation string for file dropping (concat with selectNewPhotoLabel prop)",
},
headerLabel: { description: "Translation string for title" },
saveButtonLabel: { description: "Translation string for save button" },
saveButtonLoading: {
description: "Tells when the button should show loader icon",
},
cancelButtonLabel: { description: "Translation string for cancel button" },
maxSizeFileError: { description: "Translation string for size warning" },
unknownTypeError: {
description: "Translation string for file type warning",
},
onLoadFileError: {
description: "Translation string for load file warning",
},
unknownError: { description: "Translation string for warning" },
maxSize: { description: "Max size of image" },
onSave: { description: "Save event" },
onClose: { description: "Closing event " },
onDeleteImage: { description: "Image deletion event" },
onLoadFile: { description: "Image upload event" },
onImageChange: { description: "Image change event" },
className: { description: "Accepts class" },
id: { description: "Accepts id" },
style: { description: "Accepts css style" },
openEditor: { action: "onOpen", table: { disable: true } },
closeEditor: { action: "onClose", table: { disable: true } },
onSave: { action: "onSave", table: { disable: true } },
onLoadFile: { action: "onLoadFile", table: { disable: true } },
onImageChange: { action: "onImageChange", table: { disable: true } },
onDeleteImage: { action: "onDeleteImage", table: { disable: true } },
},
parameters: {
docs: {
description: {
component: "Used to display user avatar editor on page.",
},
source: {
code: `
import AvatarEditor from "@appserver/components/avatar-editor";
<AvatarEditor
visible={true}
onClose={() => {}}
onSave={() => {})}
onDeleteImage={() => {})}
onImageChange={() => {})}
onLoadFile={() => {}}
headerLabel="Edit Photo"
selectNewPhotoLabel="Select new photo"
orDropFileHereLabel="or drop file here"
saveButtonLabel="Save"
maxSizeFileError="Maximum file size exceeded"
unknownTypeError="Unknown image file type"
unknownError="Error"
displayType="auto"
/>
`,
},
},
},
};
class Wrapper extends React.Component {
constructor(props) {
super(props);
@ -25,40 +96,47 @@ class AvatarEditorStory extends React.Component {
this.onLoadFile = this.onLoadFile.bind(this);
this.onImageChange = this.onImageChange.bind(this);
this.onDeleteImage = this.onDeleteImage.bind(this);
this.onLoadFile = this.onLoadFile.bind(this);
}
onDeleteImage() {
action("onDeleteImage");
this.props.onDeleteImage();
}
onImageChange(img) {
action("onLoadFile");
this.props.onImageChange(img);
this.setState({
userImage: img,
});
}
onLoadFile(file) {
action("onLoadFile")(file);
this.props.onLoadFile(file);
}
onSave(isUpdate, data) {
action("onSave")(isUpdate, data);
this.props.onSave(isUpdate, data);
this.setState({
isOpen: false,
});
}
openEditor() {
openEditor(e) {
this.props.openEditor(e);
this.setState({
isOpen: true,
});
}
onClose() {
action("onClose");
this.props.closeEditor();
this.setState({
isOpen: false,
});
}
render() {
const {
unknownError,
unknownTypeError,
saveButtonLoading,
maxSizeFileError,
} = this.props;
return (
<div>
<>
<Avatar
size="max"
role="user"
@ -66,44 +144,27 @@ class AvatarEditorStory extends React.Component {
editing={true}
editAction={this.openEditor}
/>
{this.props.children}
<AvatarEditor
visible={this.state.isOpen}
{...this.props}
visible={this.state.isOpen || this.props.visible}
onClose={this.onClose}
onSave={this.onSave}
onDeleteImage={this.onDeleteImage}
onImageChange={this.onImageChange}
onLoadFile={this.onLoadFile}
headerLabel={text("headerLabel", "Edit Photo")}
chooseFileLabel={text(
"chooseFileLabel",
"Drop files here, or click to select files"
)}
chooseMobileFileLabel={text(
"chooseMobileFileLabel",
"Click to select files"
)}
saveButtonLabel={text("saveButtonLabel", "Save")}
saveButtonLoading={boolean("saveButtonLoading", false)}
maxSizeFileError={text(
"maxSizeFileError",
"Maximum file size exceeded"
)}
unknownTypeError={text("unknownTypeError", "Unknown image file type")}
unknownError={text("unknownError", "Error")}
displayType={select("displayType", displayType, "auto")}
chooseFileLabel={"Drop files here, or click to select files"}
chooseMobileFileLabel={"Click to select files"}
saveButtonLoading={saveButtonLoading}
maxSizeFileError={maxSizeFileError || "Maximum file size exceeded"}
unknownTypeError={unknownTypeError || "Unknown image file type"}
unknownError={unknownError || "Error"}
/>
</div>
</>
);
}
}
storiesOf("Components|AvatarEditor", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => {
return (
<Section>
<AvatarEditorStory />
</Section>
);
});
const Template = (args) => {
return <Wrapper {...args} />;
};
export const Default = Template.bind({});

View File

@ -57,7 +57,7 @@ const Slider = styled.input.attrs({
cursor: pointer;
-webkit-appearance: none;
-webkit-box-shadow: ${(props) =>
props.theme.avatarEditorBody.sliderThumb.boxShadow};
props.theme.avatarEditorBody.slider.sliderThumb.boxShadow};
box-shadow: ${(props) =>
props.theme.avatarEditorBody.slider.sliderThumb.boxShadow};
}

View File

@ -1,4 +1,44 @@
import React from "react";
import Avatar from "./";
const roleOptions = ["owner", "admin", "guest", "user"];
const sizeOptions = ["max", "big", "medium", "small", "min"];
const editAction = () => console.log("Edit action");
export default {
title: "Components/Avatar",
component: Avatar,
parameters: {
docs: {
description: {
component: "Used to display an avatar or brand.",
},
source: {
code: `
import Avatar from "@appserver/components/avatar";
<Avatar
size="max"
role="admin"
source=""
userName=""
editing={false}
/>
`,
},
},
},
};
const Template = (args) => <Avatar {...args} />;
export const Default = Template.bind({});
Default.args = {
role: 'admin'
}
/*import React from "react";
import { storiesOf } from "@storybook/react";
import { withKnobs, boolean, text, select } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
@ -32,4 +72,4 @@ storiesOf("Components|Avatar", module)
/>
</Section>
);
});
});*/

View File

@ -1,40 +1,64 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { BooleanValue } from "react-values";
import withReadme from "storybook-readme/with-readme";
import { withKnobs, number } from "@storybook/addon-knobs/react";
import Readme from "./README.md";
import Section from "../../../.storybook/decorators/section";
import Backdrop from ".";
import React, { useState } from "react";
import Backdrop from "./";
import Button from "../button";
storiesOf("Components|Backdrop", module)
.addDecorator(withReadme(Readme))
.addDecorator(withKnobs)
.add("base", () => (
<Section>
<BooleanValue>
{({ value, toggle }) => (
<div>
<Button
label="Show Backdrop"
primary={true}
onClick={(e) => {
action("onShow")(e);
toggle(true);
}}
/>
<Backdrop
visible={value}
zIndex={number("zIndex", 1)}
onClick={(e) => {
action("onHide")(e);
toggle(false);
}}
/>
</div>
)}
</BooleanValue>
</Section>
));
export default {
title: "Components/Backdrop",
component: Backdrop,
argTypes: {
visible: {
description: "Display or not",
},
zIndex: {
description: "CSS z-index",
},
className: { description: "Accepts class" },
id: { description: "Accepts id" },
style: { description: "Accepts CSS style" },
withBackground: {
description:
"The background is not displayed if the viewport width is less than 1024, set it to true for display",
},
isAside: { description: "Must be true if used with Aside component" },
onClick: { action: "On Hide", table: { disable: true } },
},
parameters: {
docs: {
description: {
component: "Backdrop for displaying modal dialogs or other components",
},
source: {
code: `
import Backdrop from "@appserver/components/backdrop";
<Backdrop visible={true} zIndex={200}/>`,
},
},
},
};
const Template = (args) => {
const [isVisible, setIsVisible] = useState(args.visible);
const toggleVisible = () => setIsVisible(!isVisible);
return (
<>
<Button
label="Show Backdrop"
primary
size="medium"
onClick={toggleVisible}
/>
<Backdrop
{...args}
visible={isVisible}
onClick={(e) => {
args.onClick(e);
toggleVisible(false);
}}
/>
</>
);
};
export const Default = Template.bind({});

View File

@ -1,66 +1,77 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import withReadme from "storybook-readme/with-readme";
import { withKnobs, number, color, text } from "@storybook/addon-knobs/react";
import Readme from "./README.md";
import Section from "../../../.storybook/decorators/section";
import Badge from ".";
storiesOf("Components|Badge", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
<Section>
<Badge
label={text("label", "10")}
backgroundColor={color("backgroundColor", "#ED7309")}
color={color("color", "#FFFFFF")}
fontSize={text("fontSize", "11px")}
fontWeight={number("fontWeight", 800)}
borderRadius={text("borderRadius", "11px")}
padding={text("padding", "0 5px")}
maxWidth={text("maxWidth", "50px")}
onClick={(e) => {
action("onClick")(e);
}}
/>
<br />
<Badge
label="New"
backgroundColor={color("backgroundColor", "#ED7309")}
color={color("color", "#FFFFFF")}
fontSize={text("fontSize", "11px")}
fontWeight={number("fontWeight", 800)}
borderRadius={text("borderRadius", "11px")}
padding={text("padding", "0 5px")}
maxWidth={text("maxWidth", "50px")}
onClick={(e) => {
action("onClick")(e);
}}
/>
<br />
<Badge
label="Ver.2"
backgroundColor="#A3A9AE"
color={color("color", "#FFFFFF")}
fontSize={text("fontSize", "11px")}
fontWeight={number("fontWeight", 800)}
borderRadius={text("borderRadius", "11px")}
padding={text("padding", "0 5px")}
maxWidth={text("maxWidth", "50px")}
onClick={(e) => {
action("onClick")(e);
}}
/>
<p>
Text with badge
<Badge
label="3"
onClick={(e) => {
action("onClick")(e);
}}
/>
</p>
</Section>
));
import Badge from "./";
export default {
title: "Components/Badge",
component: Badge,
parameters: {
docs: {
description: {
component: "Used for buttons, numbers or status markers next to icons.",
},
source: {
code: `
import Badge from "@appserver/components/badge";
<Badge
label="10"
backgroundColor="#ED7309"
color="#FFFFFF"
fontSize="11px"
fontWeight={800}
borderRadius="11px"
padding="0 5px"
maxWidth="50px"
onClick={() => {}}
/>
`,
},
},
},
argTypes: {
backgroundColor: { control: "color", description: "CSS background-color" },
color: { control: "color", description: "CSS color" },
label: { control: "text", description: "Value" },
borderRadius: { description: "CSS border-radius" },
className: { description: "Accepts class" },
fontSize: { description: "CSS font-size" },
fontWeight: { description: "CSS font-weight" },
id: { description: "Accepts id" },
maxWidth: { description: "CSS max-width" },
onClick: { description: "onClick event" },
padding: { description: "CSS padding" },
style: { description: "Accepts css style" },
},
};
const Template = (args) => <Badge {...args} />;
const NumberTemplate = (args) => <Badge {...args} />;
const TextTemplate = (args) => <Badge {...args} />;
const MixedTemplate = (args) => <Badge {...args} />;
export const Default = Template.bind({});
Default.args = {
label: 24,
};
export const NumberBadge = NumberTemplate.bind({});
NumberBadge.argTypes = {
label: { control: "number" },
};
NumberBadge.args = {
label: 3,
};
export const TextBadge = TextTemplate.bind({});
TextBadge.argTypes = {
label: { control: "text" },
};
TextBadge.args = {
label: "New",
};
export const MixedBadge = MixedTemplate.bind({});
MixedBadge.argTypes = {
label: { control: "text" },
};
MixedBadge.args = {
label: "Ver.2",
};

View File

@ -1,4 +1,219 @@
import React from "react";
//import { Box } from "./";
import Box from "./";
const containerProps = {
widthProp: "100%",
paddingProp: "10px",
displayProp: "flex",
flexDirection: "column",
alignItems: "flex-start",
};
const rowProps = {
displayProp: "flex",
flexDirection: "row",
};
const commonBoxProps = {
textAlign: "center",
marginProp: "10px",
paddingProp: "10px",
};
export default {
title: "Components/Box",
component: Box,
parameters: {
docs: {
description: {
component:
"A container that lays out its contents in one direction. Box provides general CSS capabilities like flexbox layout, paddings, background color, border, and animation.",
},
source: {
code: `import Box from "@appserver/components/box";
<Box />`,
},
},
},
};
const Template = (args) => (
<Box {...containerProps}>
<Box {...rowProps}>
<Box {...commonBoxProps} backgroundProp="gray">
color background
</Box>
<Box
{...commonBoxProps}
backgroundProp="linear-gradient(90deg, white, gray)"
>
linear gradient background
</Box>
<Box {...commonBoxProps} backgroundProp="radial-gradient(white, gray)">
radial gradient background
</Box>
</Box>
<Box {...rowProps}>
<Box {...commonBoxProps} borderProp="4px solid gray">
solid border
</Box>
<Box {...commonBoxProps} borderProp="4px dashed gray">
dashed border
</Box>
<Box {...commonBoxProps} borderProp="4px dotted gray">
dotted border
</Box>
<Box {...commonBoxProps} borderProp="4px double gray">
double border
</Box>
</Box>
<Box {...rowProps}>
<Box
{...commonBoxProps}
borderProp={{ style: "solid", width: "1px 0", color: "gray" }}
>
Horizontal border
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "0 1px",
color: "gray",
}}
>
vertical border
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "0 0 0 1px",
color: "gray",
}}
>
left border
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px 0 0 0",
color: "gray",
}}
>
top border
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "0 1px 0 0",
color: "gray",
}}
>
right border
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "0 0 1px 0",
color: "gray",
}}
>
bottom border
</Box>
</Box>
<Box {...rowProps}>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "100%",
}}
>
full round
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "5px",
}}
>
round
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "5px 0 0 5px",
}}
>
left round
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "5px 5px 0 0",
}}
>
top round
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "0 5px 5px 0",
}}
>
right round
</Box>
<Box
{...commonBoxProps}
borderProp={{
style: "solid",
width: "1px",
color: "gray",
radius: "0 0 5px 5px",
}}
>
bottom round
</Box>
</Box>
<Box {...rowProps}>
<Box
{...commonBoxProps}
borderProp={{
style: "dashed solid double dotted",
width: "2em 1rem 1px 2%",
color: "red yellow green blue",
radius: "10% 30% 50% 70%",
}}
>
Mix border
</Box>
</Box>
</Box>
);
export const Default = Template.bind({});
/*import React from "react";
import { storiesOf } from "@storybook/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
@ -196,3 +411,4 @@ storiesOf("Components|Box", module)
</Box>
</Box>
));
*/

View File

@ -1,113 +0,0 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import Button from ".";
import Section from "../../../.storybook/decorators/section";
function onClick(e) {
e = e || window.event;
var target = e.target || e.srcElement,
text = target.textContent || target.innerText;
console.log("onClick", text);
}
const getButtons = (primary) => {
const sizes = ["large", "big", "medium", "base"];
const states = ["isActivated", "isHovered", "isClicked", "isDisabled"];
const baseButton = {
size: "base",
primary: true,
isActivated: false,
isHovered: false,
isClicked: false,
isDisabled: false,
isLoading: false,
onClick: onClick,
label: "base button",
};
let buttons = [];
baseButton.primary = primary;
sizes.forEach((size) => {
let sizeButtons = [];
states.forEach((state) => {
let btn = {
...baseButton,
size: size,
label: primary ? (size === "base" ? "Save" : "Publish") : "Cancel",
};
btn[state] = true;
sizeButtons.push(btn);
});
buttons.push({
size: size,
buttons: sizeButtons,
});
});
console.log("buttons", buttons);
return buttons;
};
storiesOf("Components|Buttons", module)
.addParameters({ options: { showAddonPanel: false } })
.add("all", () => (
<Section>
<div>
<h1>Main buttons (primary action)</h1>
<table style={{ width: 584, borderCollapse: "separate" }}>
<thead>
<tr>
<th>Active</th>
<th>Hover</th>
<th>Click</th>
<th>Disable</th>
</tr>
</thead>
<tbody>
{Object.values(getButtons(true)).map((btnSize, i) => {
console.log(btnSize);
return (
<tr key={`row${i}`}>
{Object.values(btnSize.buttons).map((btn, j) => (
<td key={`col${i}${j}`} style={{ paddingBottom: 20 }}>
<Button key={`btn${i}${j}`} {...btn} />
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
<div style={{ marginTop: 47 }}>
<h1>Main buttons (secondary action)</h1>
<table style={{ width: 584, borderCollapse: "separate" }}>
<thead>
<tr>
<th>Active</th>
<th>Hover</th>
<th>Click</th>
<th>Disable</th>
</tr>
</thead>
<tbody>
{Object.values(getButtons(false)).map((btnSize, i) => {
console.log(btnSize);
return (
<tr key={`row${i}`}>
{Object.values(btnSize.buttons).map((btn, j) => (
<td key={`col${i}${j}`} style={{ paddingBottom: 20 }}>
<Button key={`btn${i}${j}`} {...btn} />
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
</Section>
));

View File

@ -1,45 +1,276 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { withKnobs, boolean, text, select } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import Button from ".";
import { Icons } from "../icons";
import Section from "../../../.storybook/decorators/section";
import { orderBy } from "lodash";
storiesOf("Components|Buttons", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => {
const sizeOptions = ["base", "medium", "big", "large"];
const iconNames = orderBy(
Object.keys(Icons),
[(name) => name.toLowerCase()],
["asc"]
);
import Icon from "../../../public/images/button.alert.react.svg";
import Button from "./";
const iconName = select("icon", ["", ...iconNames], "");
const hintIcon = iconName
? React.createElement(Icons[iconName])
: undefined;
export default {
title: "Components/Button",
component: Button,
argTypes: {
label: { description: "Button text" },
size: { description: "Size of button" },
primary: { description: "Tells when the button should be primary" },
scale: { description: "Scale width of button to 100%" },
isClicked: {
description: "Tells when the button should present a clicked state",
},
isDisabled: {
description: "Tells when the button should present a disabled state",
},
isHovered: {
description: "Tells when the button should present a hovered state",
},
isLoading: { description: "Tells when the button should show loader icon" },
disableHover: { description: "Disable hover effect" },
icon: { description: "Icon node element" },
onClick: { description: "What the button will trigger when clicked " },
className: { description: "Accepts class" },
id: { description: "Accepts id" },
style: { description: "Accepts CSS style" },
tabIndex: { description: "Button tab index" },
minwidth: { description: "Sets the nim width of the button" },
},
parameters: {
docs: {
description: {
component: "Button is used for a action on a page.",
},
source: {
code: `
import Button from "@appserver/components/button";
return (
<Section>
<Button
size="base"
isDisabled={false}
onClick={() => alert("Button clicked")}
label="OK"
/>
`
}
},
},
args: {
size: "base",
label: "Base Button",
},
};
const sizes = ["base", "medium", "big", "large"];
const Wrapper = (props) => (
<div
style={{
display: "grid",
gridTemplateColumns: props.isScale
? "1fr"
: "repeat( auto-fill, minmax(180px, 1fr) )",
gridGap: "16px",
alignItems: "center",
}}
>
{props.children}
</div>
);
const Template = (args) => <Button {...args} />;
const PrimaryTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
label={text("label", "Base button")}
primary={boolean("primary", true)}
size={select("size", sizeOptions, "big")}
scale={boolean("scale", false)}
isLoading={boolean("isLoading", false)}
isHovered={boolean("isHovered", false)}
isClicked={boolean("isClicked", false)}
isDisabled={boolean("isDisabled", false)}
minWidth={text("minWidth", "")}
onClick={action("clicked")}
icon={hintIcon}
key={`all-primary-${size}`}
{...args}
primary
scale={false}
size={size}
label={`Primary ${size[0].toUpperCase()}${size.slice(1)}`}
/>
</Section>
);
});
))}
</Wrapper>
);
};
const SecondaryTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-secondary-${size}`}
{...args}
scale={false}
size={size}
label={`Secondary ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const WithIconTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-icon-prim-${size}`}
{...args}
primary
size={size}
icon={<Icon />}
label={`With Icon ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-icon-sec-${size}`}
{...args}
size={size}
icon={<Icon />}
label={`With Icon ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const IsLoadingTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-load-prim-${size}`}
{...args}
primary
size={size}
isLoading={true}
label={`Loading ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-load-sec-${size}`}
{...args}
size={size}
isLoading={true}
label={`Loading ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const ScaleTemplate = (args) => {
return (
<Wrapper isScale>
{sizes.map((size) => (
<Button
key={`all-scale-prim-${size}`}
{...args}
primary
size={size}
isLoading={true}
label={`Scale ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-scale-sec-${size}`}
{...args}
size={size}
isLoading={true}
label={`Scale ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const DisabledTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-disabled-prim-${size}`}
{...args}
primary
size={size}
isDisabled={true}
label={`Disabled ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-disabled-sec-${size}`}
{...args}
size={size}
isDisabled={true}
label={`Disabled ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const ClickedTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-clicked-prim-${size}`}
{...args}
primary
size={size}
isClicked={true}
label={`Disabled ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-clicked-sec-${size}`}
{...args}
size={size}
isClicked={true}
label={`Clicked ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
const HoveredTemplate = (args) => {
return (
<Wrapper>
{sizes.map((size) => (
<Button
key={`all-hovered-prim-${size}`}
{...args}
primary
size={size}
isHovered={true}
label={`Disabled ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
{sizes.map((size) => (
<Button
key={`all-hovered-sec-${size}`}
{...args}
size={size}
isHovered={true}
label={`Clicked ${size[0].toUpperCase()}${size.slice(1)}`}
/>
))}
</Wrapper>
);
};
export const Default = Template.bind({});
export const PrimaryButtons = PrimaryTemplate.bind({});
export const SecondaryButtons = SecondaryTemplate.bind({});
export const WithIconButtons = WithIconTemplate.bind({});
export const IsLoadingButtons = IsLoadingTemplate.bind({});
export const ScaleButtons = ScaleTemplate.bind({});
export const DisabledButtons = DisabledTemplate.bind({});
export const ClickedButtons = ClickedTemplate.bind({});
export const HoveredButtons = HoveredTemplate.bind({});
HoveredButtons.argTypes = {
isHovered: { table: { disable: true } },
};

View File

@ -1,66 +1,111 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { withKnobs, color, select, date } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import Calendar from ".";
import Section from "../../../.storybook/decorators/section";
import Calendar from "./";
function myDateKnob(name, defaultValue) {
const stringTimestamp = date(name, defaultValue);
return new Date(stringTimestamp);
}
export default {
title: "Components/Calendar",
component: Calendar,
argTypes: {
themeColor: { control: "color", description: "Color of the selected day" },
maxDate: {
control: "date",
description: "Maximum date that the user can select",
},
selectedDate: { control: "date", description: "Selected date value" },
openToDate: {
control: "date",
description:
"The beginning of a period that shall be displayed by default",
},
minDate: {
control: "date",
description: "Minimum date that the user can select.",
},
locale: {
description: "Browser locale",
control: {
type: "select",
options: [
"az",
"zh-cn",
"cs",
"nl",
"en-gb",
"en",
"fi",
"fr",
"de",
"de-ch",
"el",
"it",
"ja",
"ko",
"lv",
"pl",
"pt",
"pt-br",
"ru",
"sk",
"sl",
"es",
"tr",
"uk",
"vi",
],
},
},
onChange: {
description: "Function called when the user select a day",
action: "onChange",
},
size: { description: "Calendar size" },
className: { description: "Accepts class" },
id: { description: "Accepts id" },
style: { description: "Accepts css style" },
},
parameters: {
docs: {
description: {
component: "Used to display custom calendar",
},
source: {
code: `
import Calendar from "@appserver/components/calendar";
const locales = [
"az",
"zh-cn",
"cs",
"nl",
"en-gb",
"en",
"fi",
"fr",
"de",
"de-ch",
"el",
"it",
"ja",
"ko",
"lv",
"pl",
"pt",
"pt-br",
"ru",
"sk",
"sl",
"es",
"tr",
"uk",
"vi",
];
<Calendar
onChange={(date) => {
console.log("Selected date:", date);
}}
disabled={false}
themeColor="#ED7309"
selectedDate={new Date()}
openToDate={new Date()}
minDate={new Date("1970/01/01")}
maxDate={new Date("3000/01/01")}
locale="ru"
/>
`,
},
},
},
};
const arraySize = ["base", "big"];
const Template = (args) => {
return (
<Calendar
{...args}
maxDate={new Date(args.maxDate)}
selectedDate={new Date(args.selectedDate)}
openToDate={new Date(args.openToDate)}
minDate={new Date(args.minDate)}
locale="en"
/>
);
};
storiesOf("Components|Calendar", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
<Section>
<Calendar
onChange={(date) => {
action("Selected date")(date);
}}
themeColor={color("themeColor", "#ED7309")}
selectedDate={myDateKnob("selectedDate", new Date())}
openToDate={myDateKnob("openToDate", new Date())}
minDate={myDateKnob("minDate", new Date("1970/01/01"))}
maxDate={myDateKnob(
"maxDate",
new Date(new Date().getFullYear() + 1 + "/01/01")
)}
locale={select("locale", locales, "en")}
size={select("size", arraySize, "base")}
/>
</Section>
));
export const Default = Template.bind({});
Default.args = {
maxDate: new Date(new Date().getFullYear() + 1 + "/01/01"),
minDate: new Date("1970/01/01"),
selectedDate: new Date(),
openToDate: new Date(),
};

View File

@ -77,7 +77,9 @@ const CalendarStyle = styled.div`
.calendar-month_selected-day {
background-color: ${(props) =>
props.theme.calendar.selectedDay.backgroundColor};
props.color
? props.color
: props.theme.calendar.selectedDay.backgroundColor};
border-radius: ${(props) => props.theme.calendar.selectedDay.borderRadius};
cursor: ${(props) => props.theme.calendar.selectedDay.cursor};
color: ${(props) => props.theme.calendar.selectedDay.color};
@ -152,6 +154,8 @@ const StyledWeekday = styled.div`
`;
StyledWeekday.defaultProps = { theme: Base };
StyledWeekday.defaultProps = { theme: Base };
export {
Month,
CalendarStyle,

View File

@ -1,34 +1,104 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { BooleanValue } from "react-values";
import { withKnobs, boolean, text } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import Checkbox from ".";
import Section from "../../../.storybook/decorators/section";
storiesOf("Components|Input", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("checkbox", () => (
<Section>
<BooleanValue>
{({ value, toggle }) => (
<Checkbox
id={text("id", "id")}
name={text("name", "name")}
value={text("value", "value")}
label={text("label", "label")}
isChecked={value}
isIndeterminate={boolean("isIndeterminate", false)}
isDisabled={boolean("isDisabled", false)}
onChange={(e) => {
action("onChange")(e);
toggle(e.target.checked);
}}
/>
)}
</BooleanValue>
</Section>
));
import Checkbox from "./";
export default {
title: "Components/Checkbox",
component: Checkbox,
parameters: {
docs: {
description: { component: "Custom checkbox input" },
source: {
code: `
import Checkbox from "@appserver/components/checkbox";
<Checkbox
id="id"
name="name"
value="value"
label="label"
isChecked={false}
isIndeterminate={false}
isDisabled={false}
onChange={() => {}}
/>
`,
},
},
},
argTypes: {
className: { description: "Accepts class" },
id: { description: "Used as HTML id property" },
isChecked: {
description: "The checked property sets the checked state of a checkbox",
},
isDisabled: { description: "Disables the Checkbox input " },
isIndeterminate: {
description:
"If true, this state is shown as a rectangle in the checkbox",
},
label: { description: "Label of the input" },
name: { description: "Used as HTML `name` property" },
onChange: {
description: "Will be triggered whenever an CheckboxInput is clicked ",
action: "onChange",
},
style: { description: "Accepts css style " },
value: { description: "Value of the input" },
title: { description: "Title " },
truncate: { description: "Disables word wrapping" },
},
};
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
isChecked: false,
};
}
onChange = (e) => {
this.props.onChange(e);
this.setState({ isChecked: !this.state.isChecked });
};
render() {
return (
<Checkbox
{...this.props}
isChecked={this.props.isChecked || this.state.isChecked}
onChange={this.onChange}
/>
);
}
}
const Template = (args) => {
return <Wrapper {...args} />;
};
const AllCheckboxesTemplate = (args) => {
return (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat( auto-fill, minmax(120px, 1fr) )",
gridGap: "16px",
alignItems: "center",
}}
>
<Checkbox />
<Checkbox isChecked={true} />
<Checkbox isDisabled={true} />
<Checkbox isIndeterminate={true} />
<Checkbox label="Some label" />
</div>
);
};
export const Default = Template.bind({});
Default.args = {
label: "Checkbox label",
};
export const AllCheckboxStates = AllCheckboxesTemplate.bind({});

View File

@ -1,174 +1,278 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import {
withKnobs,
boolean,
select,
number,
} from "@storybook/addon-knobs/react";
import { optionsKnob as options } from "@storybook/addon-knobs";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import ComboBox from ".";
import { Icons } from "../icons";
import Button from "../button";
import ComboBox from "./";
import RadioButton from "../radio-button";
import DropDownItem from "../drop-down-item";
import Section from "../../../.storybook/decorators/section";
import NavLogoIcon from "../../../public/images/nav.logo.opened.react.svg";
export default {
title: "Components/ComboBox",
component: ComboBox,
decorators: [
(Story) => (
<div style={{ height: "230px" }}>
<Story />
</div>
),
],
argTypes: {
advancedOptions: {
description: "If you need display options not basic options",
},
className: { description: "Accepts class" },
displayType: { description: "Component Display Type" },
dropDownMaxHeight: { description: "Height of Dropdown" },
id: { description: "Accepts id" },
isDisabled: { description: "Indicates that component is disabled" },
noBorder: {
description: "Indicates that component is displayed without borders",
},
onSelect: {
description: "Will be triggered whenever an ComboBox is selected option",
action: "onSelect",
},
options: { description: "Combo box options" },
scaledOptions: {
description:
"Indicates that component`s options is scaled by ComboButton",
},
scaled: { description: "Indicates that component is scaled by parent" },
selectedOption: { description: "Selected option" },
size: { description: "Select component width, one of default" },
style: { description: "Accepts css style" },
toggleAction: {
description:
"The event will be raised when using `displayType: toggle` when clicking on a component",
},
showDisabledItems: {
description: "Display disabled items or not when displayType !== toggle ",
},
children: { description: "Children element" },
directionX: { description: "X direction selection" },
directionY: { description: "Y direction selection" },
opened: { description: "Tells when a component is open" },
textOverflow: { description: "Accepts css text-overflow" },
disableIconClick: { description: "Вisables clicking on the icon" },
},
parameters: {
docs: {
description: { component: "Custom combo box input" },
source: {
code: `
### Usage
import ComboBox from "@appserver/components/combobox";
import NavLogoIcon from "../../../../../public/images/nav.logo.react.svg";
const iconNames = Object.keys(Icons);
const sizeOptions = ["base", "middle", "big", "huge", "content"];
iconNames.push("NONE");
const options = [
{
key: 1,
icon: "static/images/catalog.employee.react.svg", // optional item
label: "Option 1",
disabled: false, // optional item
onClick: clickFunction, // optional item
},
];
storiesOf("Components|Input", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("combo box", () => {
const comboOptions = [
{
key: 1,
icon: "static/images/catalog.employee.react.svg",
label: "Option 1",
},
{
key: 2,
icon: "CatalogGuestIcon",
label: "Option 2",
},
{
key: 3,
disabled: true,
label: "Option 3",
},
{
key: 4,
label: "Option 4",
},
{
key: 5,
icon: "static/images/copy.react.svg",
label: "Option 5",
},
{
key: 6,
label: "Option 6",
},
{
key: 7,
label: "Option 7",
},
];
#### Options have options:
const needScrollDropDown = boolean("Need scroll dropdown", false);
const dropDownMaxHeight = needScrollDropDown
? number("dropDownMaxHeight", 200)
: null;
const optionsMultiSelect = options(
"children",
{
button: "button",
icon: "icon",
- key - Item key, may be a string or a number
- label - Display text
- icon - Optional name of icon that will be displayed before label
- disabled - Make option disabled
- onClick - On click function
ComboBox perceives all property's for positioning from DropDown!
If you need to display a custom list of options, you must use advancedOptions property. Like this:
const advancedOptions = (
<Meta>
<DropDownItem>
<RadioButton value="asc" name="first" label="A-Z" isChecked={true} />
</DropDownItem>
<DropDownItem>
<RadioButton value="desc" name="first" label="Z-A" />
</DropDownItem>
<DropDownItem isSeparator />
<DropDownItem>
<RadioButton value="first" name="second" label="First name" />
</DropDownItem>
<DropDownItem>
<RadioButton
value="last"
name="second"
label="Last name"
isChecked={true}
/>
</DropDownItem>
</Meta>
);
<ComboBox
options={[]} // An empty array will enable advancedOptions
advancedOptions={advancedOptions}
onSelect={(option) => console.log("Selected option", option)}
selectedOption={{
key: 0,
label: "Select",
}}
isDisabled={false}
scaled={false}
size="content"
directionX="right"
>
<NavLogoIcon size="medium" key="comboIcon" />
</ComboBox>
To use Combobox as a toggle button, you must declare it according to the parameters:
<ComboBox
options={[]} // Required to display correctly
selectedOption={{
key: 0,
label: "Selected option",
}}
scaled={false}
size="content"
displayType="toggle"
toggleAction={alert("action")}
>
<NavLogoIcon size="medium" key="comboIcon" />
</ComboBox>
`,
},
[],
{
display: "multi-select",
}
);
},
},
};
let children = [];
optionsMultiSelect.forEach(function (item, i) {
switch (item) {
case "button":
children.push(<Button label="button" key={i} />);
break;
case "icon":
children.push(<NavLogoIcon size="medium" key={i} />);
break;
default:
break;
}
});
const comboOptions = [
{
key: 1,
icon: "static/images/catalog.employee.react.svg",
label: "Option 1",
},
{
key: 2,
icon: "CatalogGuestIcon",
label: "Option 2",
},
{
key: 3,
disabled: true,
label: "Option 3",
},
{
key: 4,
label: "Option 4",
},
{
key: 5,
icon: "static/images/copy.react.svg",
label: "Option 5",
},
{
key: 6,
label: "Option 6",
},
{
key: 7,
label: "Option 7",
},
];
const advancedOptions = (
<>
<DropDownItem key="1" noHover>
<RadioButton value="asc" name="first" label="A-Z" isChecked={true} />
</DropDownItem>
<DropDownItem key="2" noHover>
<RadioButton value="desc" name="first" label="Z-A" />
</DropDownItem>
<DropDownItem key="3" isSeparator />
<DropDownItem key="4" noHover>
<RadioButton value="first" name="second" label="First name" />
</DropDownItem>
<DropDownItem key="5" noHover>
<RadioButton
value="last"
name="second"
label="Last name"
isChecked={true}
/>
</DropDownItem>
</>
);
let children = [];
const childrenItems = children.length > 0 ? children : null;
const advancedOptions = (
<>
<DropDownItem key="1" noHover>
<RadioButton value="asc" name="first" label="A-Z" isChecked={true} />
</DropDownItem>
<DropDownItem key="2" noHover>
<RadioButton value="desc" name="first" label="Z-A" />
</DropDownItem>
<DropDownItem key="3" isSeparator />
<DropDownItem key="4" noHover>
<RadioButton value="first" name="second" label="First name" />
</DropDownItem>
<DropDownItem key="5" noHover>
<RadioButton
value="last"
name="second"
label="Last name"
isChecked={true}
/>
</DropDownItem>
</>
);
return (
<Section>
<table
style={{ width: 584, borderCollapse: "separate", textAlign: "left" }}
>
<thead>
<tr>
<th>Default</th>
<th>Advanced</th>
</tr>
</thead>
<tbody>
<tr>
<td style={{ paddingBottom: 20 }}>
<ComboBox
options={comboOptions}
onSelect={(option) => action("Selected option")(option)}
selectedOption={{
key: 0,
label: "Select",
default: true,
}}
isDisabled={boolean("isDisabled", false)}
noBorder={boolean("noBorder", false)}
dropDownMaxHeight={dropDownMaxHeight}
scaled={boolean("scaled", false)}
scaledOptions={boolean("scaledOptions", false)}
size={select("size", sizeOptions, "content")}
>
{childrenItems}
</ComboBox>
</td>
<td style={{ paddingBottom: 20 }}>
<ComboBox
options={[]}
advancedOptions={advancedOptions}
onSelect={(option) => action("Selected option")(option)}
selectedOption={{
key: 0,
label: "Select",
default: true,
}}
isDisabled={boolean("isDisabled", false)}
scaled={false}
size="content"
directionX="right"
>
<NavLogoIcon size="medium" key="comboIcon" />
</ComboBox>
</td>
</tr>
</tbody>
</table>
</Section>
);
});
const childrenItems = children.length > 0 ? children : null;
const Template = (args) => (
<ComboBox
{...args}
options={[
{ key: 1, label: "Option 1" },
{ key: 2, label: "Option 2" },
]}
selectedOption={{
key: 0,
label: "Select",
}}
/>
);
const BaseOptionsTemplate = (args) => (
<ComboBox
{...args}
options={comboOptions}
onSelect={(option) => args.onSelect(option)}
selectedOption={{
key: 0,
label: "Select",
default: true,
}}
>
{childrenItems}
</ComboBox>
);
const AdvancedOptionsTemplate = (args) => (
<ComboBox
{...args}
options={[]}
advancedOptions={advancedOptions}
onSelect={(option) => args.onSelect(option)}
selectedOption={{
key: 0,
label: "Select",
default: true,
}}
>
<NavLogoIcon size="medium" key="comboIcon" />
</ComboBox>
);
export const Default = Template.bind({});
Default.args = {
opened: true,
scaled: false
};
export const BaseOptions = BaseOptionsTemplate.bind({});
BaseOptions.args = {
scaledOptions: false,
scaled: false,
noBorder: false,
isDisabled: false,
opened: true,
};
export const AdvancedOptions = AdvancedOptionsTemplate.bind({});
AdvancedOptions.args = {
opened: true,
isDisabled: false,
scaled: false,
size: "content",
directionX: "right",
directionY: "bottom",
};

View File

@ -0,0 +1,117 @@
import React from "react";
import ContextMenuButton from "./";
export default {
title: "Components/ContextMenuButton",
component: ContextMenuButton,
argTypes: {
className: { description: "Accepts class" },
clickColor: {
description: "Specifies the icon click color",
control: "color",
},
color: { description: "Specifies the icon color", control: "color" },
data: { description: "Array of options for display " },
directionX: {
description: "What the button will trigger when mouse out of button",
},
getData: { description: "Function for converting to inner data " },
hoverColor: {
description: "Specifies the icon hover color",
control: "color",
},
iconClickName: { description: "Specifies the icon click name" },
iconHoverName: { description: "Specifies the icon hover name" },
iconName: { description: "Specifies the icon name" },
id: { description: "Accepts id" },
isDisabled: {
description: "Tells when the button should present a disabled state",
},
onMouseEnter: {
description: "What the button will trigger when mouse hovered",
action: "onMouseEnter",
},
onMouseLeave: {
description: "What the button will trigger when mouse leave",
action: "onMouseLeave",
},
onMouseOut: {
description: "What the button will trigger when mouse out of button",
action: "onMouseOut",
},
onMouseOver: {
description: "What the button will trigger when mouse over button",
action: "onMouseOver",
},
opened: {
description: "Tells when the button should present a opened state",
},
size: { description: "Specifies the icon size" },
style: { description: "Accepts css style" },
title: { description: "Specifies the icon title" },
iconOpenName: { description: "Specifies the icon open name" },
directionY: { description: "Direction Y" },
columnCount: { description: "Set the number of columns" },
displayType: { description: "Set the display type" },
onClickLabel: { action: "onClickLabel", table: { disable: true } },
},
parameters: {
docs: {
description: {
component: `ContextMenuButton is used for displaying context menu actions on a list's item`,
},
source: {
code: `
import ContextMenuButton from "@appserver/components/context-menu-button";
<ContextMenuButton
iconName="static/images/vertical-dots.react.svg"
size={16}
color="#A3A9AE"
isDisabled={false}
title="Actions"
getData={() => [
{
key: "key",
label: "label",
onClick: () => alert("label"),
},
]}
/>
`,
},
},
},
};
const Template = (args) => {
function getData() {
console.log("getData");
return [
{
key: "key1",
label: "label1",
onClick: () => args.onClickLabel("label1"),
},
{
key: "key2",
label: "label2",
onClick: () => args.onClickLabel("label2"),
},
];
}
return (
<ContextMenuButton
{...args}
title={"Actions"}
iconName={"/static/images/vertical-dots.react.svg"}
size={16}
color={"#A3A9AE"}
getData={getData}
isDisabled={false}
/>
);
};
export const Default = Template.bind({});

View File

@ -1,41 +0,0 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import {
withKnobs,
text,
select,
number,
color,
boolean,
} from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import Section from "../../../.storybook/decorators/section";
import ContextMenuButton from ".";
import { Icons } from "../icons";
const iconNames = Object.keys(Icons);
function getData() {
console.log("getData");
return [
{ key: "key1", label: "label1", onClick: () => console.log("label1") },
{ key: "key2", label: "label2", onClick: () => console.log("label2") },
];
}
storiesOf("Components|ContextMenuButton", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
<Section>
<ContextMenuButton
title={text("title", "Actions")}
iconName={select("iconName", iconNames, "VerticalDotsIcon")}
size={number("size", 16)}
color={color("loaderColor", "#A3A9AE")}
getData={getData}
isDisabled={boolean("isDisabled", false)}
/>
</Section>
));

View File

@ -1,48 +1,74 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import RowContainer from "../row-container";
import RowContent from "../row-content";
import Row from "../row";
import Section from "../../../.storybook/decorators/section";
import Readme from "./README.md";
import withReadme from "storybook-readme/with-readme";
import ContextMenu from "./index";
export default {
title: "Components/ContextMenu",
component: ContextMenu,
argTypes: {
className: { description: "Accepts class" },
id: { description: "Accepts id" },
options: { description: "DropDownItems collection" },
style: { description: "Accepts css style" },
targetAreaId: { description: "Id of container apply to" },
withBackdrop: { description: "Used to display backdrop" },
},
parameters: {
docs: {
description: {
component: `ContextMenu is used for a call context actions on a page.
Implemented as part of RowContainer component.
For use within separate component it is necessary to determine active zone and events for calling and transferring options in menu.
In particular case, state is created containing options for particular Row element and passed to component when called.
`,
},
source: {
code: `
import ContextMenu from "@appserver/components/context-menu";
<ContextMenu targetAreaId="rowContainer" options={[]} />
`,
},
},
},
};
const getRndString = (n) =>
Math.random()
.toString(36)
.substring(2, n + 2);
storiesOf("Components|ContextMenu", module)
.addDecorator(withReadme(Readme))
.add("base", () => {
const array = Array.from(Array(10).keys());
const array = Array.from(Array(10).keys());
const Template = (args) => (
<RowContainer {...args} manualHeight="300px">
{array.map((item, index) => {
return (
<Row
key={`${item + 1}`}
contextOptions={
index !== 3
? [
{ key: 1, label: getRndString(5) },
{ key: 2, label: getRndString(5) },
{ key: 3, label: getRndString(5) },
{ key: 4, label: getRndString(5) },
]
: []
}
>
<RowContent>
<span>{getRndString(5)}</span>
<></>
</RowContent>
</Row>
);
})}
</RowContainer>
);
return (
<Section>
<RowContainer manualHeight="300px">
{array.map((item, index) => {
return (
<Row
key={`${item + 1}`}
contextOptions={
index !== 3
? [
{ key: 1, label: getRndString(5) },
{ key: 2, label: getRndString(5) },
{ key: 3, label: getRndString(5) },
{ key: 4, label: getRndString(5) },
]
: []
}
>
<RowContent>
<span>{getRndString(5)}</span>
<></>
</RowContent>
</Row>
);
})}
</RowContainer>
</Section>
);
});
export const Default = Template.bind({});

View File

@ -1,80 +1,127 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import {
withKnobs,
boolean,
color,
select,
date,
number,
text,
} from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import DatePicker from ".";
import Section from "../../../.storybook/decorators/section";
import DatePicker from "./";
function myDateKnob(name, defaultValue) {
const stringTimestamp = date(name, defaultValue);
return new Date(stringTimestamp);
}
export default {
title: "Components/DatePicker",
component: DatePicker,
decorators: [
(Story) => (
<div style={{ height: "380px" }}>
<Story />
</div>
),
],
argTypes: {
themeColor: { description: "Color of the selected day", control: "color" },
selectedDate: { description: "Selected date value", control: "date" },
openToDate: { description: "Opened date value", control: "date" },
minDate: {
description: "Minimum date that the user can select.",
control: "date",
},
maxDate: {
description: "Maximum date that the user can select.",
control: "date",
},
calendarHeaderContent: {
description: "Calendar header content (calendar opened in aside)",
},
calendarSize: { description: "Calendar size" },
className: { description: "Accepts class " },
displayType: { description: "Calendar display type " },
hasError: { description: "Set error date-input style" },
id: { description: "Accepts id " },
isDisabled: { description: "Disabled react-calendar" },
isOpen: { description: "Opens calendar" },
isReadOnly: { description: "Set input type is read only" },
locale: { description: "Browser locale" },
onChange: {
description: "Function called when the user select a day ",
action: "onChange",
},
scaled: { description: "Selected calendar size" },
style: { description: "Accepts css style" },
zIndex: { description: "Calendar css z-index" },
},
parameters: {
docs: {
description: {
component: "Base DatePicker component",
},
source: {
code: `
import DatePicker from "@appserver/components/date-picker";
const locales = [
"az",
"zh-cn",
"cs",
"nl",
"en-gb",
"en",
"fi",
"fr",
"de",
"de-ch",
"el",
"it",
"ja",
"ko",
"lv",
"pl",
"pt",
"pt-br",
"ru",
"sk",
"sl",
"es",
"tr",
"uk",
"vi",
];
<DatePicker
onChange={(date) => {
console.log("Selected date", date);
}}
selectedDate={new Date()}
minDate={new Date("1970/01/01")}
maxDate={new Date(new Date().getFullYear() + 1 + "/01/01")}
isDisabled={false}
isReadOnly={false}
hasError={false}
isOpen={false}
themeColor="#ED7309"
locale="en"
/>
`,
},
},
},
};
const displayType = ["dropdown", "aside", "auto"];
const Template = (args) => {
const locales = [
"az",
"zh-cn",
"cs",
"nl",
"en-gb",
"en",
"fi",
"fr",
"de",
"de-ch",
"el",
"it",
"ja",
"ko",
"lv",
"pl",
"pt",
"pt-br",
"ru",
"sk",
"sl",
"es",
"tr",
"uk",
"vi",
];
return (
<DatePicker
{...args}
onChange={(date) => {
args.onChange(date);
}}
selectedDate={new Date(args.selectedDate)}
minDate={new Date(args.minDate)}
maxDate={new Date(args.maxDate)}
openToDate={new Date(args.openToDate)}
/>
);
};
storiesOf("Components|DatePicker", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
<Section>
<DatePicker
onChange={(date) => {
action("Selected date")(date);
}}
selectedDate={myDateKnob("selectedDate", new Date())}
minDate={myDateKnob("minDate", new Date("1970/01/01"))}
maxDate={myDateKnob(
"maxDate",
new Date(new Date().getFullYear() + 1 + "/01/01")
)}
isDisabled={boolean("isDisabled", false)}
isReadOnly={boolean("isReadOnly", false)}
hasError={boolean("hasError", false)}
isOpen={boolean("isOpen", false)}
themeColor={color("themeColor", "#ED7309")}
locale={select("locale", locales, "en")}
displayType={select("displayType", displayType, "auto")}
calendarSize={select("calendarSize", ["base", "big"], "base")}
zIndex={number("zIndex", 310)}
calendarHeaderContent={text("headerContent", "Select Date")}
/>
</Section>
));
export const Default = Template.bind({});
Default.args = {
isOpen: true,
calendarHeaderContent: "Select Date",
themeColor: "#ED7309",
minDate: new Date("1970/01/01"),
selectedDate: new Date(),
maxDate: new Date(new Date().getFullYear() + 1 + "/01/01"),
openToDate: new Date(),
calendarSize: "base",
};

View File

@ -1,30 +1,69 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import DragAndDrop from "./";
import Text from "../text";
storiesOf("Components| DragAndDrop", module)
.addDecorator(withReadme(Readme))
.add("base", () => {
const onDrop = (items) => {
console.log("onDrop", items);
for (let file of items) {
if (file) {
console.log("File:", file.name);
}
export default {
title: "Components/DragAndDrop",
component: DragAndDrop,
argTypes: {
dragging: { description: "Show that the item is being dragged now." },
isDropZone: { description: "Sets the component as a dropzone" },
onDrop: {
action: "onDrop",
description:
"Occurs when the dragged element is dropped on the drop target",
},
targetFile: { action: "File: ", table: { disable: true } },
className: { description: "Accepts class" },
onMouseDown: {
description: "Occurs when the mouse button is pressed",
action: "onMouseDown",
},
children: { table: { disable: true } },
},
parameters: {
docs: {
description: {
component: `Drag And Drop component can be used as Dropzone
See documentation: https://github.com/react-dropzone/react-dropzone
`,
},
source: {
code: `
import DragAndDrop from "@appserver/components/drag-and-drop";
<DragAndDrop onDrop={onDrop} style={width: 200, height: 200, border: "5px solid #999"}>
<Text style={textStyles} color="#999" fontSize="20px">
Drop items here
</Text>
</DragAndDrop>
`,
},
},
},
};
const Template = (args) => {
const onDrop = (items) => {
args.onDrop(items);
for (let file of items) {
if (file) {
args.targetFile(file.name);
}
};
}
};
const dropDownStyles = { margin: 16, width: 200, height: 200 };
const textStyles = { textAlign: "center", lineHeight: "9.5em" };
const dropDownStyles = { margin: 16, width: 200, height: 200 };
const textStyles = { textAlign: "center", lineHeight: "9.5em" };
return (
<DragAndDrop {...args} isDropZone onDrop={onDrop} style={dropDownStyles}>
<Text style={textStyles} color="#999" fontSize="20px">
Drop items here
</Text>
</DragAndDrop>
);
};
export const Default = Template.bind({});
return (
<DragAndDrop isDropZone onDrop={onDrop} style={dropDownStyles}>
<Text style={textStyles} color="#999" fontSize="20px">
Drop items here
</Text>
</DragAndDrop>
);
});

View File

@ -1,66 +1,127 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { withKnobs, boolean, select } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import DropDown from "../drop-down";
import DropDownItem from ".";
import Section from "../../../.storybook/decorators/section";
storiesOf("Components | DropDown", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base item", () => {
const isHeader = boolean("Show category`s", true);
const isSeparator = boolean("Show separator", true);
const useIcon = boolean("Show icons", true);
const direction = select("direction", ["left", "right"], "left");
export default {
title: "Components/DropDownItem",
component: DropDownItem,
decorators: [
(Story) => (
<div style={{ height: "200px", position: "relative" }}>
<Story />
</div>
),
],
argTypes: {
isHeader: {
description: "Tells when the dropdown item should display like header",
},
isSeparator: {
description: "Tells when the dropdown item should display like separator",
},
noHover: { description: "Disable default style hover effect" },
className: { description: "Accepts class" },
disabled: {
description: "Tells when the dropdown item should display like disabled",
},
icon: { description: "Dropdown item icon" },
id: { description: "Accepts id" },
label: { description: "Dropdown item text" },
onClick: {
description: "What the dropdown item will trigger when clicked",
action: "onClick",
},
style: { description: "Accepts css style" },
tabIndex: { table: { disable: true } },
children: { table: { disable: true } },
textOverflow: { table: { disable: true } },
},
parameters: {
docs: {
description: {
component: `Is a item of DropDown component
return (
<Section>
<DropDown directionX={direction} manualY="1%" open={true}>
<DropDownItem
isHeader={isHeader}
label={isHeader ? "Category" : ""}
/>
<DropDownItem
icon={useIcon ? "WindowsMsnIcon" : ""}
label="Button 1"
onClick={() => console.log("Button 1 clicked")}
/>
<DropDownItem
icon={useIcon ? "PlaneIcon" : ""}
label="Button 2"
onClick={() => console.log("Button 2 clicked")}
/>
<DropDownItem
disabled
icon={useIcon ? "static/images/copy.react.svg" : ""}
label="Button 3"
onClick={() => console.log("Button 3 clicked")}
/>
<DropDownItem
icon={useIcon ? "ActionsDocumentsIcon" : ""}
label="Button 4"
onClick={() => console.log("Button 4 clicked")}
/>
<DropDownItem isSeparator={isSeparator} />
<DropDownItem
isHeader={isHeader}
label={isHeader ? "Category" : ""}
/>
<DropDownItem
icon={useIcon ? "static/images/nav.logo.react.svg" : ""}
label="Button 5"
onClick={() => console.log("Button 5 clicked")}
/>
<DropDownItem
disabled
icon={useIcon ? "static/images/nav.logo.react.svg" : ""}
label="Button 6"
onClick={() => console.log("Button 6 clicked")}
/>
</DropDown>
</Section>
);
});
An item can act as separator, header, or container.
When used as container, it will retain all styling features and positioning. To disable hover effects in container mode, you can use _noHover_ property.`,
},
source: {
code: `import DropDownItem from "@appserver/components/drop-down-item";
<DropDownItem
isSeparator={false}
isHeader={false}
label="Button 1"
icon="static/images/nav.logo.react.svg"
onClick={() => console.log("Button 1 clicked")}
/>`,
},
},
},
};
const Template = (args) => {
const isHeader = args.isHeader;
const isSeparator = args.isSeparator;
const useIcon = args.useIcon;
const direction = "left";
const noHover = args.noHover;
const disabled = args.disabled;
const { onClick } = args;
return (
<DropDown directionX={direction} manualY="1%" open={true}>
<DropDownItem
isHeader={isHeader}
label={isHeader ? "Category" : ""}
noHover={noHover}
/>
<DropDownItem
icon={"WindowsMsnIcon"}
label="Button 1"
disabled={disabled}
onClick={() => onClick("Button 1 clicked")}
noHover={noHover}
/>
<DropDownItem
icon={"PlaneIcon"}
label="Button 2"
onClick={() => onClick("Button 2 clicked")}
noHover={noHover}
/>
<DropDownItem
disabled
icon={"static/images/copy.react.svg"}
label={args.label || "Button 3"}
disabled={disabled}
onClick={() => onClick("Button 3 clicked")}
noHover={noHover}
/>
<DropDownItem
icon={"ActionsDocumentsIcon"}
label="Button 4"
onClick={() => onClick("Button 4 clicked")}
noHover={noHover}
/>
<DropDownItem isSeparator={isSeparator} />
<DropDownItem
isHeader={isHeader}
label={isHeader ? "Category" : ""}
noHover={noHover}
/>
<DropDownItem
icon={"static/images/nav.logo.react.svg"}
label="Button 5"
onClick={() => onClick("Button 5 clicked")}
noHover={noHover}
/>
<DropDownItem
disabled
icon={"static/images/nav.logo.react.svg"}
label="Button 6"
onClick={() => console.log("Button 6 clicked")}
noHover={noHover}
/>
</DropDown>
);
};
export const Default = Template.bind({});

View File

@ -1,70 +1,209 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import DropDown from ".";
import React, { useState } from "react";
import DropDown from "./";
import DropDownItem from "../drop-down-item";
import GroupButton from "../group-button";
storiesOf("Components| DropDown", module)
.addDecorator(withReadme(Readme))
.add("base", () => (
<div
style={{
padding: "8px 0 0 60px",
display: "grid",
gridTemplateColumns: "1fr 1fr",
}}
export default {
title: "Components/DropDown",
component: DropDown,
subcomponents: { DropDownItem, GroupButton },
decorators: [
(Story) => (
<div style={{ height: "200px", position: "relative" }}>
<Story />
</div>
),
],
argTypes: {
open: { description: "Tells when the dropdown should be opened" },
className: { description: " Accepts class" },
clickOutsideAction: {
description:
"Required for determining a click outside DropDown with the withBackdrop parameter",
},
directionX: {
description: "Sets the opening direction relative to the parent",
},
directionY: {
description: "Sets the opening direction relative to the parent",
},
id: { description: "Accepts id " },
manualWidth: {
description:
"Required if you need to specify the exact width of the component, for example 100%",
},
manualX: {
description:
"Required if you need to specify the exact distance from the parent component",
},
manualY: {
description:
"Required if you need to specify the exact distance from the parent component",
},
maxHeight: { description: "Required if the scrollbar is displayed" },
style: { description: "Accepts css style" },
withBackdrop: { description: "Used to display backdrop" },
showDisabledItems: { description: "Display disabled items or not" },
children: { table: { disable: true } },
columnCount: { table: { disable: true } },
disableOnClickOutside: { table: { disable: true } },
enableOnClickOutside: { table: { disable: true } },
onClick: { action: "onClickItem", table: { disable: true } },
},
parameters: {
docs: {
description: {
component: `Is a dropdown with any number of action
By default, it is used with DropDownItem elements in role of children.
If you want to display something custom, you can put it in children, but take into account that all stylization is assigned to the implemented component.
When using component, it should be noted that parent must have CSS property _position: relative_. Otherwise, DropDown will appear outside parent's border.
`,
},
},
},
};
const Template = (args) => {
const [isOpen, setIsOpen] = useState(args.open);
const clickOutsideAction = (e) => {
setIsOpen(!isOpen);
};
return (
<DropDown
{...args}
open={isOpen}
clickOutsideAction={clickOutsideAction}
style={{ top: 0, left: 0 }}
onClick={() => {}}
>
<div style={{ position: "relative" }}>
<div>Only dropdown</div>
<div style={{ marginTop: 8 }}>Without active button</div>
<DropDown open={true}>
<DropDownItem isHeader label="Category 1" />
<DropDownItem
label="Button 1"
onClick={() => console.log("Button 1 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => console.log("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => console.log("Button 3 clicked")}
/>
<DropDownItem
label="Button 4"
onClick={() => console.log("Button 4 clicked")}
disabled={true}
/>
<DropDownItem isSeparator />
<DropDownItem
label="Button 5"
onClick={() => console.log("Button 5 clicked")}
/>
<DropDownItem
label="Button 6"
onClick={() => console.log("Button 6 clicked")}
/>
</DropDown>
</div>
<div style={{ position: "relative" }}>
<div style={{ marginLeft: 16 }}>With Button</div>
<GroupButton label="Dropdown demo" isDropdown={true}>
<DropDownItem
label="Button 1"
onClick={() => console.log("Button 1 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => console.log("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => console.log("Button 3 clicked")}
/>
</GroupButton>
</div>
</div>
));
<DropDownItem isHeader label="Category 1" />
<DropDownItem
label="Button 1"
onClick={() => args.onClick("Button 1 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => args.onClick("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => args.onClick("Button 3 clicked")}
/>
<DropDownItem
label="Button 4"
onClick={() => args.onClick("Button 4 clicked")}
disabled={true}
/>
<DropDownItem isSeparator />
<DropDownItem
label="Button 5"
onClick={() => args.onClick("Button 5 clicked")}
/>
<DropDownItem
label="Button 6"
onClick={() => args.onClick("Button 6 clicked")}
/>
</DropDown>
);
};
const WithButtonTemplate = (args) => {
return (
<GroupButton
label="Dropdown demo"
style={{ top: 0, left: 0 }}
isDropdown={true}
opened={args.open}
>
<DropDownItem
label="Button 1"
onClick={() => args.onClick("Button 2 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => args.onClick("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => args.onClick("Button 3 clicked")}
/>
</GroupButton>
);
};
export const Default = Template.bind({});
export const WithButton = WithButtonTemplate.bind({});
Default.args = { open: true };
Default.parameters = {
docs: {
source: {
code: `import DropDown from "@appserver/components/drop-down";
import DropDownItem from "@appserver/components/drop-down-item";
<DropDown {...props}>
<DropDownItem isHeader label="Category 1" />
<DropDownItem
label="Button 1"
onClick={() => action("Button 1 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => action("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => action("Button 3 clicked")}
/>
<DropDownItem
label="Button 4"
onClick={() => action("Button 4 clicked")}
disabled={true}
/>
<DropDownItem isSeparator />
<DropDownItem
label="Button 5"
onClick={() => action("Button 5 clicked")}
/>
<DropDownItem
label="Button 6"
onClick={() => action("Button 6 clicked")}
/>
</DropDown>`,
},
},
};
WithButton.args = {
open: true,
};
WithButton.parameters = {
docs: {
source: {
code: `import GroupButton from "@appserver/components/group-button";
import DropDownItem from "@appserver/components/drop-down-item";
<GroupButton label="Dropdown demo" isDropdown={true}>
<DropDownItem
label="Button 1"
onClick={() => console.log("Button 1 clicked")}
/>
<DropDownItem
label="Button 2"
onClick={() => console.log("Button 2 clicked")}
/>
<DropDownItem
label="Button 3"
onClick={() => console.log("Button 3 clicked")}
/>
</GroupButton>`,
},
},
};

View File

@ -171,33 +171,6 @@ class DropDown extends React.PureComponent {
}
}
DropDown.propTypes = {
children: PropTypes.any,
className: PropTypes.string,
clickOutsideAction: PropTypes.func,
directionX: PropTypes.oneOf(["left", "right"]), //TODO: make more informative
directionY: PropTypes.oneOf(["bottom", "top"]),
disableOnClickOutside: PropTypes.func,
enableOnClickOutside: PropTypes.func,
id: PropTypes.string,
manualWidth: PropTypes.string,
manualX: PropTypes.string,
manualY: PropTypes.string,
maxHeight: PropTypes.number,
open: PropTypes.bool,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
withBackdrop: PropTypes.bool,
columnCount: PropTypes.number,
showDisabledItems: PropTypes.bool,
};
DropDown.defaultProps = {
directionX: "left",
directionY: "bottom",
withBackdrop: false,
showDisabledItems: false,
};
const EnhancedComponent = onClickOutside(DropDown);
class DropDownContainer extends React.Component {
@ -226,8 +199,30 @@ class DropDownContainer extends React.Component {
}
DropDownContainer.propTypes = {
children: PropTypes.any,
className: PropTypes.string,
clickOutsideAction: PropTypes.func,
directionX: PropTypes.oneOf(["left", "right"]), //TODO: make more informative
directionY: PropTypes.oneOf(["bottom", "top"]),
disableOnClickOutside: PropTypes.func,
enableOnClickOutside: PropTypes.func,
id: PropTypes.string,
manualWidth: PropTypes.string,
manualX: PropTypes.string,
manualY: PropTypes.string,
maxHeight: PropTypes.number,
open: PropTypes.bool,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
withBackdrop: PropTypes.bool,
columnCount: PropTypes.number,
showDisabledItems: PropTypes.bool
};
DropDownContainer.defaultProps = {
directionX: "left",
directionY: "bottom",
withBackdrop: false,
showDisabledItems: false,
};
export default DropDownContainer;

View File

@ -1,67 +1,261 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import {
text,
boolean,
withKnobs,
select,
number,
} from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import EmailInput from ".";
import Section from "../../../.storybook/decorators/section";
import { action } from "@storybook/addon-actions";
import React, { useState } from "react";
import EmailInput from "./";
const sizeInput = ["base", "middle", "big", "huge", "large"];
export default {
title: "Components/EmailInput",
component: EmailInput,
argTypes: {
allowDomainPunycode: { control: "boolean" },
allowLocalPartPunycode: { control: "boolean" },
allowDomainIp: { control: "boolean" },
allowStrictLocalPart: { control: "boolean" },
allowSpaces: { control: "boolean" },
allowName: { control: "boolean" },
allowLocalDomainName: { control: "boolean" },
onValidateInput: { action: "onValidateInput" },
onChange: { action: "onChange" },
},
parameters: {
docs: {
description: {
component: `Email entry field with advanced capabilities for validation based on setting
storiesOf("Components|Input", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("email input", () => {
const placeholder = text("placeholder", "Input email");
const size = select("size", sizeInput, "base");
const scale = boolean("scale", false);
const isDisabled = boolean("isDisabled", false);
const isReadOnly = boolean("isReadOnly", false);
const maxLength = number("maxLength", 255);
const id = text("id", "emailId");
const name = text("name", "demoEmailInput");
### Properties
const allowDomainPunycode = boolean("allowDomainPunycode", false);
const allowLocalPartPunycode = boolean("allowLocalPartPunycode", false);
const allowDomainIp = boolean("allowDomainIp", false);
const allowStrictLocalPart = boolean("allowStrictLocalPart", true);
const allowSpaces = boolean("allowSpaces", false);
const allowName = boolean("allowName", false);
const allowLocalDomainName = boolean("allowLocalDomainName", false);
You can apply all properties of the 'TextInput' component to the component
| Props | Type | Required | Values | Default | Description |
| ----------------- | :-----------------------: | :------: | :-----------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| className | string | - | - | - | Accepts class |
| customValidate | func | - | - | - | Function for your custom validation input value. Function must return object with following parameters: 'value': string value of input, 'isValid': boolean result of validating, 'errors'(optional): array of errors |
| emailSettings | Object, EmailSettings | - | - | { allowDomainPunycode: false, allowLocalPartPunycode: false, allowDomainIp: false, allowStrictLocalPart: true, allowSpaces: false, allowName: false, allowLocalDomainName: false } | Settings for validating email |
| hasError | bool | - | - | - | Used in your custom validation |
| id | string | - | - | - | Accepts id |
| onChange | func | - | - | - | Function for your custom handling changes in input |
| onValidateInput | func | - | { isValid: bool, errors: array} | - | Will be validate our value, return object with following parameters: 'isValid': boolean result of validating, 'errors': array of errors |
| style | obj, array | - | - | - | Accepts css style
### Validate email
Our validation algorithm based on [RFC 5322 email address parser](https://www.npmjs.com/package/email-addresses).
For email validating you should use plain Object or EmailSettings with following settings:
const settings = {
allowDomainPunycode,
allowLocalPartPunycode,
allowDomainIp,
allowStrictLocalPart,
allowSpaces,
allowName,
allowLocalDomainName,
};
return (
<Section>
<EmailInput
placeholder={placeholder}
size={size}
scale={scale}
isDisabled={isDisabled}
isReadOnly={isReadOnly}
maxLength={maxLength}
id={id}
name={name}
emailSettings={settings}
onValidateInput={(isEmailValid) =>
action("onValidateInput")(isEmailValid)
}
/>
</Section>
);
});
### emailSettings prop
Plain object:
const emailSettings = {
allowDomainPunycode: false,
allowLocalPartPunycode: false,
allowDomainIp: false,
allowStrictLocalPart: true,
allowSpaces: false,
allowName: false,
allowLocalDomainName: false,
};
or instance of 'EmailSettings' class:
import { EmailInput, utils } from "@appserver/components";
const { EmailSettings } = utils.email;
const emailSettings = new EmailSettings();
emailSettings.toObject(); /* returned Object with default settings:
{
allowDomainPunycode: false,
allowLocalPartPunycode: false,
allowDomainIp: false,
allowStrictLocalPart: true,
allowSpaces: false,
allowName: false,
allowLocalDomainName: false
}
*/
email.allowName = true; // set allowName setting to true
emailSettings.toObject(); /* returned Object with NEW settings:
{
allowDomainPunycode: false,
allowLocalPartPunycode: false,
allowDomainIp: false,
allowStrictLocalPart: true,
allowSpaces: false,
allowName: true,
allowLocalDomainName: false
}
*/
### Custom validate email
You should use custom validation with the 'customValidate' prop. This prop contains function for your custom validation input value. Function must return object with following parameters: 'value': string value of input, 'isValid': boolean result of validating, 'errors'(optional): array of errors.
Base colors:
| Сomponent actions | isValid | border-color |
| ----------------- | :-----: | :--------------------------------------------------------------: |
| :focus | false | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
| :focus | true | ![#2DA7DB](https://placehold.it/15/2DA7DB/000000?text=+) #2DA7DB |
| :hover | false | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
| :hover | true | ![#D0D5DA](https://placehold.it/15/D0D5DA/000000?text=+) #D0D5DA |
| default | false | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
| default | true | ![#D0D5DA](https://placehold.it/15/D0D5DA/000000?text=+) #D0D5DA |
import React from "react";
import { EmailInput } from "@appserver/components";
const onChange = (e) => {
// your event handling
customValidate(e.target.value);
};
const customValidate = (value) => {
const isValid = !!(value && value.length > 0);
return {
value,
isValid: isValid,
errors: isValid ? [] : ["incorrect email"],
};
};
const onValidateInput = (result) => {
console.log("onValidateInput", result);
};
<EmailInput
customValidate={customValidate}
onChange={onChange}
onValidateInput={onValidateInput}
/>
`,
},
source: {
code: `import EmailInput from "@appserver/components/email-input";
import { EmailSettings } from "@appserver/components/utils/email";
const settings = new EmailSettings();
settings.allowDomainPunycode = true;
<EmailInput
name="email"
placeholder="email"
emailSettings={settings}
onValidateInput={result =>
console.log("onValidateInput", result.value, result.isValid, result.errors);
}
/>
`,
},
},
},
};
const Template = ({
allowDomainPunycode,
allowLocalPartPunycode,
allowDomainIp,
allowStrictLocalPart,
allowSpaces,
allowName,
allowLocalDomainName,
...rest
}) => {
const [emailValue, setEmailValue] = useState("");
const onChangeHandler = (value) => {
setEmailValue(value);
};
const settings = {
allowDomainPunycode,
allowLocalPartPunycode,
allowDomainIp,
allowStrictLocalPart,
allowSpaces,
allowName,
allowLocalDomainName,
};
return (
<EmailInput
{...rest}
value={emailValue}
emailSettings={settings}
onValidateInput={(isEmailValid) => rest.onValidateInput(isEmailValid)}
onChange={(e) => {
rest.onChange(e.target.value);
onChangeHandler(e.target.value);
}}
/>
);
};
export const Default = Template.bind({});
Default.args = {
allowDomainPunycode: false,
allowLocalPartPunycode: false,
allowDomainIp: false,
allowSpaces: false,
allowName: false,
allowLocalDomainName: false,
allowStrictLocalPart: true,
placeholder: "Input email",
size: "base",
};

View File

@ -122,11 +122,20 @@ EmailInput.propTypes = {
PropTypes.objectOf(PropTypes.bool),
]),
hasError: PropTypes.bool,
size: PropTypes.oneOf(["base", "middle", "big", "huge", "large"]),
id: PropTypes.string,
onChange: PropTypes.func,
onValidateInput: PropTypes.func,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
value: PropTypes.string,
autoComplete: PropTypes.string,
isDisabled: PropTypes.bool,
isReadOnly: PropTypes.bool,
name: PropTypes.string,
placeholder: PropTypes.string,
scale: PropTypes.bool,
size: "base",
size: PropTypes.oneOf(["base", "middle", "big", "huge", "large"]),
};
EmailInput.defaultProps = {

View File

@ -1,42 +1,72 @@
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 { action } from "@storybook/addon-actions";
import EmptyScreenContainer from ".";
import EmptyScreenContainer from "./";
import Link from "../link";
import CrossIcon from "../../../../../public/images/cross.react.svg"
storiesOf("Components| EmptyScreenContainer", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
import CrossIcon from "../../../public/images/cross.react.svg";
export default {
title: "Components/EmptyScreenContainer",
component: EmptyScreenContainer,
argTypes: { onClick: { action: "Reset filter clicked" } },
parameters: {
docs: {
description: {
component: `Used to display empty screen page
### Properties
| Props | Type | Required | Values | Default | Description |
| ----------------- | :------------: | :------: | :----: | :-----: | --------------------------------------- |
| buttons | element | - | - | - | Content of EmptyContentButtonsContainer |
| className | string | - | - | - | Accepts class |
| descriptionText | string | - | - | - | Description text |
| headerText | string | - | - | - | Header text |
| subheadingText | string | - | - | - | Subheading text |
| id | string | - | - | - | Accepts id |
| imageAlt | string | - | - | - | Alternative image text |
| imageSrc | string | - | - | - | Image url source |
| style | obj, array | - | - | - | Accepts css style |
`,
},
source: {
code: `
import EmptyScreenContainer from "@appserver/components/empty-screen-container";
<EmptyScreenContainer
imageSrc="empty_screen_filter.png"
imageAlt="Empty Screen Filter image"
headerText="No results matching your search could be found"
subheading="No files to be displayed in this section"
descriptionText="No results matching your search could be found"
buttons={<a href="/">Go to home</a>}
/>`,
},
},
},
};
const Template = (args) => {
return (
<EmptyScreenContainer
imageSrc={text("imageSrc", "empty_screen_filter.png")}
imageAlt={text("imageAlt", "Empty Screen Filter image")}
headerText={text(
"headerText",
"No results matching your search could be found"
)}
subheadingText={text(
"subheaderText",
"No files to be displayed in this section"
)}
descriptionText={text(
"descriptionText",
"No people matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the people in this section."
)}
{...args}
buttons={
<>
<CrossIcon size="small" style={{ marginRight: "4px" }} />
<Link
type="action"
isHovered={true}
onClick={(e) => action("Reset filter clicked")(e)}
>
<Link type="action" isHovered={true} onClick={(e) => args.onClick(e)}>
Reset filter
</Link>
</>
}
/>
));
);
};
export const Default = Template.bind({});
Default.args = {
imageSrc: "empty_screen_filter.png",
imageAlt: "Empty Screen Filter image",
headerText: "No results matching your search could be found",
subheadingText: "No files to be displayed in this section",
descriptionText:
"No people matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the people in this section.",
};

View File

@ -1,4 +1,91 @@
import React from "react";
import React, { useState } from "react";
import FieldContainer from ".";
import TextInput from "../text-input";
export default {
title: "Components/FieldContainer",
component: FieldContainer,
argTypes: {
errorColor: { control: "color" },
},
parameters: {
docs: {
description: {
component: `Responsive form field container
### Properties
| Props | Type | Required | Values | Default | Description |
| ------------------------- | :---------------: | :------: | :----: | :-------: | ------------------------------------------------ |
| className | string | - | - | - | Accepts class |
| errorColor | string | - | - | ![#C96C27](https://placehold.it/15/C96C27/000000?text=+) #C96C27 | Error text color |
| errorMessageWidth | string | - | - | 320px | Error text width |
| errorMessage | string | - | - | - | Error message text |
| hasError | bool | - | - | false | Indicates that the field is incorrect |
| helpButtonHeaderContent | string | - | - | - | Tooltip header content (tooltip opened in aside) |
| id | string | - | - | - | Accepts id |
| isRequired | bool | - | - | false | Indicates that the field is required to fill |
| isVertical | bool | - | - | false | Vertical or horizontal alignment |
| labelText | string | - | - | - | Field label text |
| labelVisible | bool | - | - | true | Sets visibility of field label section |
| maxLabelWidth | string | - | - | 110px | Max label width in horizontal alignment |
| style | obj, array | - | - | - | Accepts css style |
| tooltipContent | object, string | - | - | - | Tooltip content |
`,
},
source: {
code: `import FieldContainer from "@appserver/components/field-container";
<FieldContainer labelText="Name:">
<TextInput value="" onChange={(e) => console.log(e.target.value)} />
</FieldContainer>`,
},
},
},
};
const Template = (args) => {
const [value, setValue] = useState("");
return (
<FieldContainer {...args}>
<TextInput
value={value}
hasError={args.hasError}
className="field-input"
onChange={(e) => {
setValue(e.target.value);
}}
/>
</FieldContainer>
);
};
export const Default = Template.bind({});
Default.args = {
isVertical: false,
isRequired: false,
hasError: false,
labelVisible: true,
labelText: "Name:",
maxLabelWidth: "110px",
tooltipContent: "Paste you tooltip content here",
helpButtonHeaderContent: "Tooltip header",
place: "top",
errorMessage:
"Error text. Lorem ipsum dolor sit amet, consectetuer adipiscing elit",
errorColor: "#C96C27",
errorMessageWidth: "293px",
};
Default.parameters = {
decorators: [
(Story) => (
<div style={{ marginTop: 100, marginLeft: 50 }}>
<Story />
</div>
),
],
};
/*
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { StringValue } from "react-values";
@ -58,3 +145,4 @@ storiesOf("Components|FieldContainer", module)
)}
</StringValue>
));
*/

View File

@ -1,49 +1,80 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { text, boolean, withKnobs, select } from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import FileInput from ".";
import Section from "../../../.storybook/decorators/section";
import { action } from "@storybook/addon-actions";
import FileInput from "./";
const sizeInput = ["base", "middle", "big", "huge", "large"];
export default {
title: "Components/FileInput",
component: FileInput,
argTypes: {
onInput: { action: "onInput" },
},
parameters: {
docs: {
description: {
component: `File entry field
storiesOf("Components|Input", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("file input", () => {
const placeholder = text("placeholder", "Input file");
const size = select("size", sizeInput, "base");
const scale = boolean("scale", false);
const isDisabled = boolean("isDisabled", false);
const id = text("id", "fileInputId");
const name = text("name", "demoFileInputName");
const hasError = boolean("hasError", false);
const hasWarning = boolean("hasWarning", false);
const accept = text("accept", ".doc, .docx");
### Properties
return (
<Section>
<FileInput
id={id}
name={name}
placeholder={placeholder}
size={size}
scale={scale}
isDisabled={isDisabled}
hasError={hasError}
hasWarning={hasWarning}
accept={accept}
onInput={(file) => {
action("onInput")(file);
console.log(
`name: ${file.name}`,
`lastModified: ${file.lastModifiedDate}`,
`size: ${file.size}`
);
}}
/>
</Section>
| Props | Type | Required | Values | Default | Description |
| ------------- | :------------: | :------: | :--------------------------------------: | :-----: | ---------------------------------------------------------------------------------- |
| className | string | - | - | - | Accepts class |
| hasError | bool | - | - | false | Indicates the input field has an error |
| hasWarning | bool | - | - | false | Indicates the input field has a warning |
| id | string | - | - | - | Used as HTML 'id' property |
| isDisabled | bool | - | - | false | Indicates that the field cannot be used (e.g not authorised, or changes not saved) |
| name | string | - | - | - | Used as HTML 'name' property |
| onInput | func | - | - | - | Called when a file is selected |
| placeholder | string | - | - | - | Placeholder text for the input |
| scale | bool | - | - | false | Indicates the input field has scale |
| size | string | - | base, middle, big, huge, large | base | Supported size of the input fields. |
| style | obj, array | - | - | - | Accepts css style |
| accept | string | - | - | - | Specifies files visible for upload |
`,
},
source: {
code: `import FileInput from "@appserver/components/file-input";
<FileInput
placeholder="Input file"
accept=".doc, .docx"
onInput={(file) => {
console.log(
file,
"name: ", file.name},
"lastModified: ", file.lastModifiedDate},
"size: ", file.size}
);
});
}}
/>`,
},
},
},
};
const Template = (args) => {
return (
<FileInput
{...args}
onInput={(file) => {
args.onInput(
`File: ${file},
name: ${file.name},
lastModified: ${file.lastModifiedDate},
size: ${file.size}`
);
}}
/>
);
};
export const Default = Template.bind({});
Default.args = {
placeholder: "Input file",
size: "base",
scale: false,
isDisabled: false,
id: "file-input-id",
name: "demoFileInputName",
hasError: false,
hasWarning: false,
accept: ".doc, .docx",
};

View File

@ -1,4 +1,229 @@
import React from "react";
import Grid from "./";
import Box from "../box";
import Text from "../text";
export default {
title: "Components/Grid",
component: Grid,
subcomponents: { Box },
argTypes: {},
parameters: {
docs: {
description: {
component: `A container that lays out its contents in a 2-dimensional grid system. Use Box components to define rows and columns.
### Properties
| Props | Type | Required | Values | Default | Description |
| :--------------: | :-----------------------: | :------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| alignContent | string | - | - | - | sets the distribution of space between and around content items along a flexbox's cross-axis or a grid's block axis |
| alignItems | string | - | - | - | sets the align-self value on all direct children as a group. In Flexbox, it controls the alignment of items on the Cross Axis. In Grid Layout, it controls the alignment of items on the Block Axis within their grid area. |
| alignSelf | string | - | - | - | overrides a grid or flex item's align-items value. In Grid, it aligns the item inside the grid area. In Flexbox, it aligns the item on the cross axis. |
| areasProp | array | - | [["header","header"],["navbar","main"]], [{ name: "header", start: [0, 0], end: [1, 0] }, { name: "navbar", start: [0, 1], end: [0, 1] }, { name: "main", start: [1, 1], end: [1, 1] }] | - | specifies named grid areas. Takes value as array of string arrays that specify named grid areas. Or objects array, that contains names and coordinates of areas. |
| columnsProp | string,array,object | - | "25%", ["200px", ["100px","1fr"], "auto"], { count: 3, size: "100px" } | - | defines the sizing of the grid columns. Specifying a single string will repeat several columns of this size. Specifying an object allows you to specify the number of repetitions and the size of the column. Or you can specify an array with column sizes. The column size can be specified as an array of minimum and maximum widths. |
| gridArea | string | - | - | - | is a shorthand property for grid-row-start, grid-column-start, grid-row-end and grid-column-end, specifying a grid items size and location within the grid by contributing a line, a span, or nothing (automatic) to its grid placement, thereby specifying the edges of its grid area. |
| gridColumnGap | string | - | - | - | sets the size of the gap (gutter) between an element's columns. |
| gridGap | string | - | - | - | sets the gaps (gutters) between rows and columns. It is a shorthand for row-gap and column-gap. |
| gridRowGap | string | - | - | - | sets the size of the gap (gutter) between an element's grid rows. |
| heightProp | string | - | - | 100% | defines the height of the border of the element area. |
| justifyContent | string | - | - | - | defines how the browser distributes space between and around content items along the main-axis of a flex container, and the inline axis of a grid container. |
| justifyItems | string | - | - | - | defines the default justify-self for all items of the box, giving them all a default way of justifying each box along the appropriate axis. |
| justifySelf | string | - | - | - | sets the way a box is justified inside its alignment container along the appropriate axis. |
| marginProp | string | - | - | - | sets the margin area on all four sides of an element. It is a shorthand for margin-top, margin-right, margin-bottom, and margin-left. |
| paddingProp | string | - | - | - | sets the padding area on all four sides of an element. It is a shorthand for padding-top, padding-right, padding-bottom, and padding-left |
| rowsProp | string,array | - | "50px", ["100px", ["100px","1fr"], "auto"] | - | defines the sizing of the grid rows. Specifying a single string will repeat several rows of this size. Or you can specify an array with rows sizes. The row size can be specified as an array of minimum and maximum heights. |
| widthProp | string | - | - | 100% | defines the width of the border of the element area. |
`,
},
source: {
code: `import Grid from "@appserver/components/grid";
<Grid {...args} {...gridProps}>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>200px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>auto</Text>
</Box>
</Grid>`,
},
},
},
};
const gridProps = {
marginProp: "0 0 20px 0",
};
const boxProps = {
paddingProp: "10px",
displayProp: "flex",
alignItems: "center",
justifyContent: "center",
};
const Template = (args) => {
return (
<Grid {...args} {...gridProps}>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>200px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>auto</Text>
</Box>
</Grid>
);
};
const TemplateColumns = (args) => {
return (
<>
<Grid
{...args}
{...gridProps}
columnsProp={["200px", ["100px", "1fr"], "auto"]}
>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>200px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>auto</Text>
</Box>
</Grid>
<Grid {...args} {...gridProps} columnsProp="25%">
<Box {...boxProps} backgroundProp="#F4991A">
<Text>25%</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>25%</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>25%</Text>
</Box>
</Grid>
<Grid {...args} {...gridProps} columnsProp={{ count: 3, size: "100px" }}>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>100px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>100px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>100px</Text>
</Box>
</Grid>
<Grid
{...args}
{...gridProps}
columnsProp={{ count: 3, size: ["100px", "1fr"] }}
>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>minmax(100px,1fr)</Text>
</Box>
</Grid>
</>
);
};
const TemplateRows = (args) => {
return (
<>
<Grid
{...args}
{...gridProps}
rowsProp={["100px", ["100px", "1fr"], "auto"]}
>
<Box {...boxProps} backgroundProp="#F4991A">
<Text>100px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>minmax(100px,1fr)</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>auto</Text>
</Box>
</Grid>
<Grid {...args} {...gridProps} rowsProp="50px">
<Box {...boxProps} backgroundProp="#F4991A">
<Text>50px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F2EAD3">
<Text>50px</Text>
</Box>
<Box {...boxProps} backgroundProp="#F9F5F0">
<Text>50px</Text>
</Box>
</Grid>
</>
);
};
const TemplateLayout = (args) => {
return (
<Grid
{...args}
widthProp="100vw"
heightProp="100vh"
gridGap="10px"
rowsProp={["auto", "1fr", "auto"]}
columnsProp={[["100px", "1fr"], "3fr", ["100px", "1fr"]]}
areasProp={[
{ name: "header", start: [0, 0], end: [2, 0] },
{ name: "navbar", start: [0, 1], end: [0, 1] },
{ name: "main", start: [1, 1], end: [1, 1] },
{ name: "sidebar", start: [2, 1], end: [2, 1] },
{ name: "footer", start: [0, 2], end: [2, 2] },
]}
>
<Box {...boxProps} gridArea="header" backgroundProp="#F4991A">
<Text>header</Text>
</Box>
<Box {...boxProps} gridArea="navbar" backgroundProp="#F2EAD3">
<Text>navbar</Text>
</Box>
<Box {...boxProps} gridArea="main" backgroundProp="#F9F5F0">
<Text>main</Text>
</Box>
<Box {...boxProps} gridArea="sidebar" backgroundProp="#F2EAD3">
<Text>sidebar</Text>
</Box>
<Box {...boxProps} gridArea="footer" backgroundProp="#F4991A">
<Text>footer</Text>
</Box>
</Grid>
);
};
export const Default = Template.bind({});
Default.args = {
columnsProp: ["200px", ["100px", "1fr"], "auto"],
};
export const Columns = TemplateColumns.bind({});
export const Rows = TemplateRows.bind({});
export const Layout = TemplateLayout.bind({});
/*
import { storiesOf } from "@storybook/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
@ -129,3 +354,4 @@ storiesOf("Components|Grid", module)
</Box>
</Grid>
));
*/

View File

@ -7,9 +7,18 @@
"start": "echo 'skip it'",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
"test:coverage": "jest --coverage",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"devDependencies": {
"@storybook/addon-actions": "^6.1.20",
"@storybook/addon-controls": "^6.1.20",
"@storybook/addon-docs": "^6.1.20",
"@storybook/addon-essentials": "^6.1.20",
"@storybook/addon-links": "^6.1.20",
"@storybook/react": "^6.1.20",
"react-values": "^0.3.3",
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
@ -46,7 +55,6 @@
"prettier": "2.1.2",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-values": "^0.3.3",
"rollup": "^1.32.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-cleanup": "^3.2.1",
@ -87,4 +95,4 @@
"react-window-infinite-loader": "^1.0.5",
"resize-image": "^0.1.0"
}
}
}

View File

@ -14,6 +14,9 @@ import {
NewPasswordButton,
PasswordProgress,
StyledInput,
TooltipStyle,
StyledTooltipContainer,
StyledTooltipItem,
} from "./styled-password-input";
class PasswordInput extends React.Component {
@ -40,12 +43,16 @@ class PasswordInput extends React.Component {
};
}
hideTooltip = () => {
this.hideTooltip && this.refTooltip.current.hideTooltip();
};
onBlur = () => {
this.refTooltip.current.hideTooltip();
this.hideTooltip();
};
changeInputType = () => {
this.refTooltip.current.hideTooltip();
this.hideTooltip();
const newType = this.state.type === "text" ? "password" : "text";
this.setState({
@ -229,25 +236,25 @@ class PasswordInput extends React.Component {
return !equal(this.props, nextProps) || !equal(this.state, nextState);
}
// renderTextTooltip = (settings, length, digits, capital, special) => {
// return (
// <>
// <div className="break"></div>
// <Text
// className="text-tooltip"
// fontSize="10px"
// color="#A3A9AE"
// as="span"
// >
// {settings.minLength ? length : null}{" "}
// {settings.digits ? `, ${digits}` : null}{" "}
// {settings.upperCase ? `, ${capital}` : null}{" "}
// {settings.specSymbols ? `, ${special}` : null}
// </Text>
// <div className="break"></div>
// </>
// );
// };
renderTextTooltip = (settings, length, digits, capital, special) => {
return (
<>
<div className="break"></div>
<Text
className="text-tooltip"
fontSize="10px"
color="#A3A9AE"
as="span"
>
{settings.minLength ? length : null}{" "}
{settings.digits ? `, ${digits}` : null}{" "}
{settings.upperCase ? `, ${capital}` : null}{" "}
{settings.specSymbols ? `, ${special}` : null}
</Text>
<div className="break"></div>
</>
);
};
renderTextTooltip = () => {
const {
@ -277,46 +284,49 @@ class PasswordInput extends React.Component {
) : null;
};
// renderTooltipContent = () =>
// !isDisableTooltip && !isDisabled ? (
// <StyledTooltipContainer forwardedAs="div" title={tooltipPasswordTitle}>
// {tooltipPasswordTitle}
// <StyledTooltipItem
// forwardedAs="div"
// title={tooltipPasswordLength}
// valid={validLength}
// >
// {tooltipPasswordLength}
// </StyledTooltipItem>
// {passwordSettings.digits && (
// <StyledTooltipItem
// forwardedAs="div"
// title={tooltipPasswordDigits}
// valid={validDigits}
// >
// {tooltipPasswordDigits}
// </StyledTooltipItem>
// )}
// {passwordSettings.upperCase && (
// <StyledTooltipItem
// forwardedAs="div"
// title={tooltipPasswordCapital}
// valid={validCapital}
// >
// {tooltipPasswordCapital}
// </StyledTooltipItem>
// )}
// {passwordSettings.specSymbols && (
// <StyledTooltipItem
// forwardedAs="div"
// title={tooltipPasswordSpecial}
// valid={validSpecial}
// >
// {tooltipPasswordSpecial}
// </StyledTooltipItem>
// )}
// </StyledTooltipContainer>
// ) : null;
renderTooltipContent = () =>
!this.props.isDisableTooltip && !this.props.isDisabled ? (
<StyledTooltipContainer
forwardedAs="div"
title={this.props.tooltipPasswordTitle}
>
{this.props.tooltipPasswordTitle}
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordLength}
valid={this.validLength}
>
{this.props.tooltipPasswordLength}
</StyledTooltipItem>
{this.props.passwordSettings.digits && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordDigits}
valid={this.validDigits}
>
{this.props.tooltipPasswordDigits}
</StyledTooltipItem>
)}
{this.props.passwordSettings.upperCase && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordCapital}
valid={this.validCapital}
>
{this.props.tooltipPasswordCapital}
</StyledTooltipItem>
)}
{this.props.passwordSettings.specSymbols && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordSpecial}
valid={this.validSpecial}
>
{this.props.tooltipPasswordSpecial}
</StyledTooltipItem>
)}
</StyledTooltipContainer>
) : null;
renderInputGroup = () => {
const {
@ -366,17 +376,17 @@ class PasswordInput extends React.Component {
autoComplete={autoComplete}
theme={theme}
></InputBlock>
{/* <TooltipStyle>
<Tooltip
id="tooltipContent"
effect="solid"
place="top"
offsetLeft={tooltipOffsetLeft}
reference={this.refTooltip}
>
{this.renderTooltipContent()}
</Tooltip>
</TooltipStyle> */}
<TooltipStyle>
<Tooltip
id="tooltipContent"
effect="solid"
place="top"
offsetLeft={this.tooltipOffsetLeft}
reference={this.refTooltip}
>
{this.renderTooltipContent()}
</Tooltip>
</TooltipStyle>
<Progress
progressColor={progressColor}
progressWidth={progressWidth}

View File

@ -1,7 +1,7 @@
import React from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
import Text from "../text";
import { tablet, mobile } from "../utils/device";
import Base from "../themes/base";
// eslint-disable-next-line no-unused-vars
@ -133,19 +133,28 @@ const Progress = styled.div`
`;
Progress.defaultProps = { theme: Base };
// const TooltipStyle = styled.div`
// .__react_component_tooltip {
// }
// `;
const TooltipStyle = styled.div`
.__react_component_tooltip {
}
`;
// const StyledTooltipContainer = styled(Text)`
// //margin: 8px 16px 16px 16px;
// `;
const StyledTooltipContainer = styled(Text)`
//margin: 8px 16px 16px 16px;
`;
// const StyledTooltipItem = styled(Text)`
// margin-left: 8px;
// height: 24px;
// color: ${(props) => (props.valid ? "#44bb00" : "#B40404")};
// `;
const StyledTooltipItem = styled(Text)`
margin-left: 8px;
height: 24px;
color: ${(props) => (props.valid ? "#44bb00" : "#B40404")};
`;
export { Progress, CopyLink, NewPasswordButton, PasswordProgress, StyledInput };
export {
Progress,
CopyLink,
NewPasswordButton,
PasswordProgress,
StyledInput,
TooltipStyle,
StyledTooltipContainer,
StyledTooltipItem,
};

View File

@ -1,6 +1,6 @@
import styled from "styled-components";
import commonInputStyles from "./common-input-styles";
import Input from "./Input";
import Input from "./input";
import Base from "../themes/base";
/* eslint-enable react/prop-types, no-unused-vars */

View File

@ -94,6 +94,13 @@
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div class="ipl-progress-indicator" id="ipl-progress-indicator">
<div class="ipl-progress-indicator-head">
<div class="first-indicator"></div>
<div class="second-indicator"></div>
</div>
</div>
<div id="temp-content" style="display: none">
<header class="temp-header-container">
<div id="burger-loader-svg" class="burger-loader-svg">

View File

@ -8,7 +8,7 @@ import Settings from "./components/pages/Settings";
import VersionHistory from "./components/pages/VersionHistory";
import config from "../package.json";
import PrivateRoute from "@appserver/common/components/PrivateRoute";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import { updateTempContent } from "@appserver/common/utils";
import initFilesStore from "./store/InitFilesStore";
import filesStore from "./store/FilesStore";

View File

@ -5,7 +5,7 @@ import styled from "styled-components";
//import equal from "fast-deep-equal/react";
import { getFolder } from "@appserver/common/api/files";
import { FolderType, ShareAccessRights } from "@appserver/common/constants";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { onConvertFiles } from "../../../helpers/files-converter";
import { ReactSVG } from "react-svg";

View File

@ -1,6 +1,6 @@
import React from "react";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import TreeFolders from "./TreeFolders";
import TreeSettings from "./TreeSettings";
@ -119,7 +119,7 @@ export default inject(
const { treeFolders, setSelectedNode, setTreeFolders } = treeFoldersStore;
const selectedTreeNode = [selectedFolderStore.id + ""];
return {
return {
selectedFolderTitle: selectedFolderStore.title,
treeFolders,
selectedTreeNode,
@ -129,6 +129,6 @@ export default inject(
fetchFiles,
setSelectedNode,
setTreeFolders,
};
}
};
}
)(observer(ArticleBodyContent));

View File

@ -8,7 +8,7 @@ import Checkbox from "@appserver/components/checkbox";
import Scrollbar from "@appserver/components/scrollbar";
import { withTranslation } from "react-i18next";
import { getProgress, removeFiles } from "@appserver/common/api/files";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { TIMEOUT } from "../../../helpers/constants";
import { loopTreeFolders } from "../../../helpers/files-helpers";
import { inject, observer } from "mobx-react";

View File

@ -4,7 +4,7 @@ import ModalDialog from "@appserver/components/modal-dialog";
import Button from "@appserver/components/button";
import { withTranslation } from "react-i18next";
import { getFolder } from "@appserver/common/api/files";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { loopTreeFolders } from "../../../helpers/files-helpers";
import { inject, observer } from "mobx-react";

View File

@ -6,7 +6,7 @@ import Button from "@appserver/components/button";
import ModalDialog from "@appserver/components/modal-dialog";
import { withTranslation } from "react-i18next";
import { getProgress, emptyTrash } from "@appserver/common/api/files";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { TIMEOUT } from "../../../helpers/constants";
import { inject, observer } from "mobx-react";

View File

@ -18,7 +18,7 @@ import {
} from "@appserver/common/api/files";
import { checkIsAuthenticated } from "@appserver/common/api/user";
import { getUser } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import { setDocumentTitle } from "../../../helpers/utils";
import { changeTitle, setFavicon, isIPad } from "./utils";

View File

@ -15,7 +15,7 @@ import {
} from "@appserver/common/api/files";
import history from "@appserver/common/history";
import { FileAction, ShareAccessRights } from "@appserver/common/constants";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import FavoriteIcon from "../../../../../../../public/images/favorite.react.svg";
import FileActionsConvertEditDocIcon from "../../../../../../../public/images/file.actions.convert.edit.doc.react.svg";
import FileActionsLockedIcon from "../../../../../../../public/images/file.actions.locked.react.svg";

View File

@ -7,7 +7,7 @@ import Link from "@appserver/components/link";
import Text from "@appserver/components/text";
import { markAsRead } from "@appserver/common/api/files";
import { FileAction } from "@appserver/common/constants";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { getTitleWithoutExst } from "../../../../../helpers/files-helpers";
import { NewFilesPanel } from "../../../../panels";
import EditingWrapperComponent from "./EditingWrapperComponent";

View File

@ -31,7 +31,7 @@ import {
} from "@appserver/common/api/files";
import { FileAction } from "@appserver/common/constants";
import MediaViewer from "@appserver/common/components/MediaViewer";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import { TIMEOUT } from "../../../../../helpers/constants";
import { loopTreeFolders } from "../../../../../helpers/files-helpers";

View File

@ -2,7 +2,7 @@ import React from "react";
import copy from "copy-to-clipboard";
import styled, { css } from "styled-components";
import { withRouter } from "react-router";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import Headline from "@appserver/common/components/Headline";
import { FilterType, FileAction } from "@appserver/common/constants";

View File

@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { isMobile } from "react-device-detect";
import axios from "axios";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import PageLayout from "@appserver/common/components/PageLayout";
import { showLoader, hideLoader } from "@appserver/common/utils";
import FilesFilter from "@appserver/common/api/files/filter";

View File

@ -5,7 +5,7 @@ import Heading from "@appserver/components/heading";
import Aside from "@appserver/components/aside";
import IconButton from "@appserver/components/icon-button";
import { ShareAccessRights } from "@appserver/common/constants";
import PeopleSelector from "@appserver/common/components/PeopleSelector";
import PeopleSelector from "studio/PeopleSelector";
import { withTranslation } from "react-i18next";
import {
StyledAddUsersPanelPanel,

View File

@ -1,5 +1,5 @@
import React from "react";
import PeopleSelector from "@appserver/common/components/PeopleSelector";
import PeopleSelector from "studio/PeopleSelector";
import Aside from "@appserver/components/aside";
import Backdrop from "@appserver/components/backdrop";
import Heading from "@appserver/components/heading";

View File

@ -7,7 +7,7 @@ import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import { withTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import OwnerSelector from "./OwnerSelector";
import {
StyledAsidePanel,

View File

@ -8,7 +8,7 @@ import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import TextInput from "@appserver/components/text-input";
import Textarea from "@appserver/components/textarea";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { withTranslation } from "react-i18next";
import {
StyledEmbeddingPanel,

View File

@ -11,7 +11,7 @@ import RowContainer from "@appserver/components/row-container";
import Button from "@appserver/components/button";
import { withTranslation } from "react-i18next";
import { getNewFiles, markAsRead } from "@appserver/common/api/files";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { ReactSVG } from "react-svg";
import {
StyledAsidePanel,

View File

@ -2,7 +2,7 @@ import React from "react";
import { withRouter } from "react-router";
import ModalDialog from "@appserver/components/modal-dialog";
import { withTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { StyledAsidePanel } from "../StyledPanels";
import TreeFolders from "../../Article/Body/TreeFolders";
import { ThirdPartyMoveDialog } from "../../dialogs";

View File

@ -4,7 +4,7 @@ import ComboBox from "@appserver/components/combobox";
import Row from "@appserver/components/row";
import Text from "@appserver/components/text";
import DropDownItem from "@appserver/components/drop-down-item";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import copy from "copy-to-clipboard";
import LinkRow from "./linkRow";
import AccessComboBox from "./AccessComboBox";

View File

@ -10,7 +10,7 @@ import DropDownItem from "@appserver/components/drop-down-item";
import Textarea from "@appserver/components/textarea";
import { withRouter } from "react-router";
import { withTranslation, Trans } from "react-i18next";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { ShareAccessRights } from "@appserver/common/constants";
import {
StyledAsidePanel,

View File

@ -31,3 +31,144 @@ body.drag-cursor * {
6 6,
auto !important;
}
.ipl-progress-indicator.available {
opacity: 0;
}
.ipl-progress-indicator {
background-color: #f5f5f5;
width: 100%;
height: auto;
position: absolute;
opacity: 1;
pointer-events: none;
-webkit-transition: opacity cubic-bezier(0.4, 0, 0.2, 1) 436ms;
-moz-transition: opacity cubic-bezier(0.4, 0, 0.2, 1) 436ms;
transition: opacity cubic-bezier(0.4, 0, 0.2, 1) 436ms;
z-index: 9999;
top: 56px;
}
.insp-logo-frame {
display: -webkit-flex;
display: -moz-flex;
display: flex;
-webkit-flex-direction: column;
-moz-flex-direction: column;
flex-direction: column;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
-webkit-animation: fadein 436ms;
-moz-animation: fadein 436ms;
animation: fadein 436ms;
height: 98%;
}
.insp-logo-frame-img {
width: 112px;
height: 112px;
-webkit-align-self: center;
-moz-align-self: center;
align-self: center;
border-radius: 50%;
}
.ipl-progress-indicator-head {
background-color: #c6dafc;
height: 2px;
overflow: hidden;
position: relative;
}
.ipl-progress-indicator .first-indicator,
.ipl-progress-indicator .second-indicator {
background-color: red;
bottom: 0;
left: 0;
right: 0;
top: 0;
position: absolute;
-webkit-transform-origin: left center;
-moz-transform-origin: left center;
transform-origin: left center;
-webkit-transform: scaleX(0);
-moz-transform: scaleX(0);
transform: scaleX(0);
}
.ipl-progress-indicator .first-indicator {
-webkit-animation: first-indicator 2s linear infinite;
-moz-animation: first-indicator 2s linear infinite;
animation: first-indicator 2s linear infinite;
}
.ipl-progress-indicator .second-indicator {
-webkit-animation: second-indicator 2s linear infinite;
-moz-animation: second-indicator 2s linear infinite;
animation: second-indicator 2s linear infinite;
}
.ipl-progress-indicator .insp-logo {
animation: App-logo-spin infinite 20s linear;
border-radius: 50%;
-webkit-align-self: center;
-moz-align-self: center;
align-self: center;
}
@keyframes App-logo-spin {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-moz-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes first-indicator {
0% {
transform: translate(0) scaleX(0);
}
25% {
transform: translate(0) scaleX(0.5);
}
50% {
transform: translate(25%) scaleX(0.75);
}
75% {
transform: translate(100%) scaleX(0);
}
100% {
transform: translate(100%) scaleX(0);
}
}
@keyframes second-indicator {
0% {
transform: translate(0) scaleX(0);
}
60% {
transform: translate(0) scaleX(0);
}
80% {
transform: translate(0) scaleX(0.6);
}
100% {
transform: translate(100%) scaleX(0.1);
}
}

View File

@ -160,7 +160,7 @@ module.exports = (env, argv) => {
if (argv.mode === "production") {
config.mode = "production";
} else {
config.devtool = "source-map";
config.devtool = "cheap-module-source-map";
}
return config;

View File

@ -4,7 +4,7 @@ import { Switch } from "react-router";
import PeopleStore from "./store/PeopleStore";
import Home from "./components/pages/Home";
import Loader from "@appserver/components/loader";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import PrivateRoute from "@appserver/common/components/PrivateRoute";
import { updateTempContent } from "@appserver/common/utils";
const Profile = React.lazy(() => import("./components/pages/Profile"));

View File

@ -6,7 +6,7 @@ import MainButton from "@appserver/components/main-button";
import DropDownItem from "@appserver/components/drop-down-item";
import InviteDialog from "./../../dialogs/InviteDialog/index";
import { withTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import { inject, observer } from "mobx-react";
import config from "../../../../package.json";

View File

@ -8,7 +8,7 @@ import FieldContainer from "@appserver/components/field-container";
import { withTranslation } from "react-i18next";
import ModalDialogContainer from "../ModalDialogContainer";
import { sendInstructionsToChangeEmail } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
class ChangeEmailDialogComponent extends React.Component {
constructor(props) {

View File

@ -6,7 +6,7 @@ import Link from "@appserver/components/link";
import Text from "@appserver/components/text";
import { withTranslation, Trans } from "react-i18next";
import { sendInstructionsToChangePassword } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
class ChangePasswordDialogComponent extends React.Component {
constructor() {

View File

@ -4,7 +4,7 @@ import ModalDialog from "@appserver/components/modal-dialog";
import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import { withTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
class ChangePhoneDialogComponent extends React.Component {
constructor(props) {

View File

@ -12,7 +12,7 @@ import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-
import { FixedSizeList as List, areEqual } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import { withTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { EmployeeStatus } from "@appserver/common/constants";
import ModalDialogContainer from "../ModalDialogContainer";
import { inject, observer } from "mobx-react";

View File

@ -12,7 +12,7 @@ import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-
import { withTranslation } from "react-i18next";
import { FixedSizeList as List, areEqual } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { EmployeeType } from "@appserver/common/constants";
import ModalDialogContainer from "../ModalDialogContainer";

View File

@ -9,7 +9,7 @@ import history from "@appserver/common/history";
import { withTranslation, Trans } from "react-i18next";
import api from "@appserver/common/api";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import ModalDialogContainer from "../ModalDialogContainer";

View File

@ -9,7 +9,7 @@ import Text from "@appserver/components/text";
import { withTranslation } from "react-i18next";
import ModalDialogContainer from "../ModalDialogContainer";
import { sendInstructionsToDelete } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
class DeleteSelfProfileDialogComponent extends React.Component {
constructor(props) {

View File

@ -13,7 +13,7 @@ import { FixedSizeList as List, areEqual } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import { withTranslation } from "react-i18next";
import Filter from "@appserver/common/api/people/filter";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import ModalDialogContainer from "../ModalDialogContainer";
import { inject, observer } from "mobx-react";

View File

@ -13,7 +13,7 @@ import { FixedSizeList as List, areEqual } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import { withTranslation } from "react-i18next";
import { resendUserInvites } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import ModalDialogContainer from "../ModalDialogContainer";
import { inject, observer } from "mobx-react";

View File

@ -6,9 +6,9 @@ import SelectedItem from "@appserver/components/selected-item";
import TextInput from "@appserver/components/text-input";
import { tablet } from "@appserver/components/utils/device";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import PeopleSelector from "@appserver/common/components/PeopleSelector";
import PeopleSelector from "studio/PeopleSelector";
import { GUID_EMPTY } from "../../../../../helpers/constants";
import PropTypes from "prop-types";
import React from "react";

View File

@ -5,7 +5,7 @@ import UserContent from "./userContent";
import history from "@appserver/common/history";
import { inject, observer } from "mobx-react";
import { Trans, useTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import { EmployeeStatus } from "@appserver/common/constants";
import { resendUserInvites } from "@appserver/common/api/people"; //TODO: Move to store action

View File

@ -1,7 +1,7 @@
import React from "react";
import RowContainer from "@appserver/components/row-container";
import { Consumer } from "@appserver/components/utils/context";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import EmptyScreen from "./EmptyScreen";

View File

@ -9,7 +9,7 @@ import { tablet, desktop } from "@appserver/components/utils/device";
import { Consumer } from "@appserver/components/utils/context";
import Headline from "@appserver/common/components/Headline";
import toastr from "@appserver/common/components/Toast/toastr";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import { EmployeeType, EmployeeStatus } from "@appserver/common/constants";
import { withTranslation } from "react-i18next";

View File

@ -7,7 +7,7 @@ import ComboBox from "@appserver/components/combobox";
import HelpButton from "@appserver/components/help-button";
import styled from "styled-components";
import { resendUserInvites } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import { inject, observer } from "mobx-react";

View File

@ -3,7 +3,7 @@ import IconButton from "@appserver/components/icon-button";
import ContextMenuButton from "@appserver/components/context-menu-button";
import AvatarEditor from "@appserver/components/avatar-editor";
import Headline from "@appserver/common/components/Headline";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { withRouter } from "react-router";
import { withTranslation, Trans } from "react-i18next";
import styled from "styled-components";

View File

@ -1,7 +1,7 @@
import React from "react";
import PropTypes from "prop-types";
import PageLayout from "@appserver/common/components/PageLayout";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import Loaders from "@appserver/common/components/Loaders";
import {
ArticleHeaderContent,

View File

@ -10,7 +10,7 @@ import {
loadAvatar,
deleteAvatar,
} from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { isMobile } from "react-device-detect";
import { inject, observer } from "mobx-react";
import { toEmployeeWrapper } from "../../../../../helpers/people-helpers";

View File

@ -8,7 +8,7 @@ import { isTablet } from "@appserver/components/utils/device";
import {
/*createThumbnailsAvatar,*/ loadAvatar,
} from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { isMobile } from "react-device-detect";
import { inject, observer } from "mobx-react";
import { toEmployeeWrapper } from "../../../../../helpers/people-helpers";

View File

@ -27,7 +27,7 @@ import {
createThumbnailsAvatar,
loadAvatar,
} from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import { isMobile } from "react-device-detect";
import { inject, observer } from "mobx-react";
import {

View File

@ -0,0 +1,26 @@
import React from "react";
import CreateAvatarEditorPage from "./createAvatarEditorPage";
import AvatarEditorPage from "./avatarEditorPage";
import CreateUserForm from "./createUserForm";
import UpdateUserForm from "./updateUserForm";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
const SectionUserBody = ({ avatarEditorIsOpen, match }) => {
const { type } = match.params;
return type ? (
avatarEditorIsOpen ? (
<CreateAvatarEditorPage />
) : (
<CreateUserForm />
)
) : avatarEditorIsOpen ? (
<AvatarEditorPage />
) : (
<UpdateUserForm />
);
};
export default inject(({ peopleStore }) => ({
avatarEditorIsOpen: peopleStore.avatarEditorStore.visible,
}))(withRouter(observer(SectionUserBody)));

View File

@ -30,7 +30,7 @@ import {
loadAvatar,
deleteAvatar,
} from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import {
ChangeEmailDialog,
ChangePasswordDialog,

View File

@ -7,12 +7,13 @@ import {
ArticleMainButtonContent,
ArticleBodyContent,
} from "../../Article";
import SectionUserBody from "./Section/Body/index";
import {
SectionHeaderContent,
CreateUserForm,
UpdateUserForm,
AvatarEditorPage,
CreateAvatarEditorPage,
// CreateUserForm,
// UpdateUserForm,
// AvatarEditorPage,
// CreateAvatarEditorPage,
} from "./Section";
import { withTranslation } from "react-i18next";
@ -67,13 +68,7 @@ class ProfileAction extends React.Component {
console.log("ProfileAction render");
this.loaded = false;
const {
profile,
isVisitor,
match,
isAdmin,
avatarEditorIsOpen,
} = this.props;
const { profile, match } = this.props;
const { userId, type } = match.params;
if (type) {
@ -84,21 +79,17 @@ class ProfileAction extends React.Component {
return (
<PageLayout>
{!isVisitor && (
<PageLayout.ArticleHeader>
<ArticleHeaderContent />
</PageLayout.ArticleHeader>
)}
{!isVisitor && isAdmin && (
<PageLayout.ArticleMainButton>
<ArticleMainButtonContent />
</PageLayout.ArticleMainButton>
)}
{!isVisitor && (
<PageLayout.ArticleBody>
<ArticleBodyContent />
</PageLayout.ArticleBody>
)}
<PageLayout.ArticleHeader>
<ArticleHeaderContent />
</PageLayout.ArticleHeader>
<PageLayout.ArticleMainButton>
<ArticleMainButtonContent />
</PageLayout.ArticleMainButton>
<PageLayout.ArticleBody>
<ArticleBodyContent />
</PageLayout.ArticleBody>
<PageLayout.SectionHeader>
{this.loaded ? <SectionHeaderContent /> : <Loaders.SectionHeader />}
@ -106,17 +97,7 @@ class ProfileAction extends React.Component {
<PageLayout.SectionBody>
{this.loaded ? (
type ? (
avatarEditorIsOpen ? (
<CreateAvatarEditorPage />
) : (
<CreateUserForm />
)
) : avatarEditorIsOpen ? (
<AvatarEditorPage />
) : (
<UpdateUserForm />
)
<SectionUserBody />
) : (
<Loaders.ProfileView isEdit={false} />
)}
@ -127,19 +108,18 @@ class ProfileAction extends React.Component {
}
ProfileAction.propTypes = {
setDocumentTitle: PropTypes.func.isRequired,
isEdit: PropTypes.bool,
setIsEditingForm: PropTypes.func.isRequired,
fetchProfile: PropTypes.func.isRequired,
match: PropTypes.object.isRequired,
profile: PropTypes.object,
isAdmin: PropTypes.bool,
match: PropTypes.object.isRequired,
};
export default inject(({ auth, peopleStore }) => ({
setDocumentTitle: auth.setDocumentTitle,
isAdmin: auth.isAdmin,
isVisitor: auth.userStore.user.isVisitor,
isEdit: peopleStore.editingFormStore.isEdit,
setIsEditingForm: peopleStore.editingFormStore.setIsEditingForm,
fetchProfile: peopleStore.targetUserStore.getTargetUser,
profile: peopleStore.targetUserStore.targetUser,
avatarEditorIsOpen: peopleStore.avatarEditorStore.visible,
}))(withTranslation("ProfileAction")(withRouter(observer(ProfileAction))));

View File

@ -161,7 +161,7 @@ module.exports = (env, argv) => {
if (argv.mode === "production") {
config.mode = "production";
} else {
config.devtool = "source-map";
config.devtool = "cheap-module-source-map";
}
return config;

View File

@ -11,7 +11,7 @@ import ErrorBoundary from "@appserver/common/components/ErrorBoundary";
import Layout from "./components/Layout";
import ScrollToTop from "./components/Layout/ScrollToTop";
import history from "@appserver/common/history";
import toastr from "@appserver/common/components/Toast";
import toastr from "studio/toastr";
import RectangleLoader from "@appserver/common/components/Loaders/RectangleLoader";
import { updateTempContent } from "@appserver/common/utils";
import { Provider as MobxProvider } from "mobx-react";
@ -166,7 +166,6 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
}, [loadBaseInfo]);
useEffect(() => {
console.log("App render", isLoaded);
if (isLoaded) updateTempContent();
}, [isLoaded]);

Some files were not shown because too many files have changed in this diff Show More