├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── notify-new-issue.yml │ ├── notify-reviewers.yml │ ├── publish-canary.yml │ ├── publish-next.yml │ ├── publish.yml │ └── pull-request.yml ├── .gitignore ├── .storybook ├── .babelrc ├── assets │ ├── __logo.svg │ ├── _logo.svg │ ├── arrow-down.svg │ ├── browsers-support.svg │ ├── building-blocks-illustration.svg │ ├── deprecated.svg │ ├── experimental.svg │ ├── logo.svg │ ├── screen-rotation-icon.svg │ └── tablet-telephone.svg ├── components │ ├── DocContainer.jsx │ └── SidebarLabelWrapper │ │ ├── SidebarLabelWrapper.js │ │ └── index.css ├── decorators.js ├── main.js ├── manager-head.html ├── manager.js ├── preview.js ├── public │ └── favicon.ico ├── softConstructThem.js ├── storybookReset.scss └── style.css ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── configs ├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .lintstagedrc.json ├── .prettierignore ├── .prettierrc ├── .releaserc.json ├── .stylelintrc.json ├── changelog.config.js ├── commitlint.config.js ├── jest.config.js ├── rollup.config.js └── tsconfig.json ├── index.js ├── package-lock.json ├── package.json ├── scripts ├── analyzeBundleSize.js ├── build.js ├── copyFiles.js ├── createComponent.js ├── findComponentUsage.js ├── postPublish.js ├── testReport.js └── utils.js ├── src ├── assets │ ├── media │ │ ├── empty-state │ │ │ ├── greyscale │ │ │ │ ├── data.svg │ │ │ │ ├── image.svg │ │ │ │ ├── message.svg │ │ │ │ └── search.svg │ │ │ ├── transparent │ │ │ │ ├── data.svg │ │ │ │ ├── image.svg │ │ │ │ ├── message.svg │ │ │ │ └── search.svg │ │ │ ├── with-circles │ │ │ │ ├── data.svg │ │ │ │ ├── image.svg │ │ │ │ └── search.svg │ │ │ └── without-circles │ │ │ │ ├── data.svg │ │ │ │ ├── image.svg │ │ │ │ ├── message.svg │ │ │ │ └── search.svg │ │ └── export │ │ │ ├── csv.svg │ │ │ └── xls.svg │ └── styles │ │ ├── checkboxRadioSwitcher.scss │ │ ├── globalStyling.scss │ │ ├── mixinsAndFunctions.scss │ │ ├── rangeAndSlider.scss │ │ ├── rtl.scss │ │ ├── sharedStyles.scss │ │ └── variables.scss ├── configs.js ├── hooks │ ├── index.ts │ ├── useBodyScroll.js │ ├── useClick.js │ ├── useClickOutside.js │ ├── useDebounce.js │ ├── useDeviceType.js │ ├── useDidMount.js │ ├── useEllipsisDetection │ │ ├── index.ts │ │ ├── useEllipsisDetection.mdx │ │ ├── useEllipsisDetection.test.tsx │ │ └── useEllipsisDetection.ts │ ├── useForceUpdate.js │ ├── useHover.js │ ├── useImgDownload.js │ ├── useKeyDown.js │ ├── useMount.js │ ├── useMutationObserver.js │ ├── usePrevious.js │ ├── useThrottle.js │ ├── useToggle.js │ ├── useUpdatableRef.js │ ├── useUpdate.js │ ├── useWidth.js │ └── useWindowSize.js ├── index.mobile.ts ├── index.ts ├── lib │ ├── atoms │ │ ├── Avatar │ │ │ ├── Avatar.scss │ │ │ ├── Avatar.stories.tsx │ │ │ ├── Avatar.test.tsx │ │ │ ├── Avatar.tsx │ │ │ └── index.tsx │ │ ├── Badge │ │ │ ├── Badge.scss │ │ │ ├── Badge.stories.tsx │ │ │ ├── Badge.test.tsx │ │ │ ├── Badge.tsx │ │ │ └── index.tsx │ │ ├── BusyLoader │ │ │ ├── BusyLoader.scss │ │ │ ├── BusyLoader.stories.tsx │ │ │ ├── BusyLoader.test.tsx │ │ │ ├── BusyLoader.tsx │ │ │ ├── BusyLoaderHolderHOC.tsx │ │ │ └── index.tsx │ │ ├── Button │ │ │ ├── Button.scss │ │ │ ├── Button.stories.tsx │ │ │ ├── Button.test.tsx │ │ │ ├── Button.tsx │ │ │ └── index.tsx │ │ ├── Divider │ │ │ ├── Divider.scss │ │ │ ├── Divider.stories.tsx │ │ │ ├── Divider.test.tsx │ │ │ ├── Divider.tsx │ │ │ └── index.tsx │ │ ├── Empty │ │ │ ├── Empty.scss │ │ │ ├── Empty.stories.tsx │ │ │ ├── Empty.test.tsx │ │ │ ├── Empty.tsx │ │ │ ├── index.tsx │ │ │ └── utils.ts │ │ ├── Icon │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── Image │ │ │ ├── Image.scss │ │ │ ├── Image.stories.tsx │ │ │ ├── Image.test.tsx │ │ │ ├── Image.tsx │ │ │ └── index.tsx │ │ ├── ImagePreview │ │ │ ├── ImagePreview.scss │ │ │ ├── ImagePreview.stories.tsx │ │ │ ├── ImagePreview.test.tsx │ │ │ ├── ImagePreview.tsx │ │ │ ├── ImagePreviewHOC.tsx │ │ │ ├── Magnifier.tsx │ │ │ └── index.tsx │ │ ├── KeyValue │ │ │ ├── KeyValue.scss │ │ │ ├── KeyValue.stories.tsx │ │ │ ├── KeyValue.test.tsx │ │ │ ├── KeyValue.tsx │ │ │ └── index.tsx │ │ ├── Label │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── LinkButton │ │ │ ├── LinkButton.scss │ │ │ ├── LinkButton.stories.tsx │ │ │ ├── LinkButton.test.tsx │ │ │ ├── LinkButton.tsx │ │ │ └── index.tsx │ │ ├── ModuleTitle │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Option │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Paper │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Popover │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── PopoverV2 │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Portal │ │ │ └── index.js │ │ ├── QRCode │ │ │ └── index.js │ │ ├── Radio │ │ │ ├── index.js │ │ │ └── index.mobile.js │ │ ├── Rating │ │ │ ├── DefaultSvg.tsx │ │ │ ├── Rating.scss │ │ │ ├── Rating.stories.tsx │ │ │ ├── Rating.test.tsx │ │ │ ├── Rating.tsx │ │ │ └── index.tsx │ │ ├── Scrollbar │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── SkeletonLoader │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Switcher │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── TextLink │ │ │ └── index.js │ │ ├── Time │ │ │ ├── index.js │ │ │ └── index.scss │ │ └── Title │ │ │ ├── index.js │ │ │ └── index.scss │ ├── molecules │ │ ├── AdvancedSearch │ │ │ ├── Content.js │ │ │ ├── FilterList.js │ │ │ ├── ListElementWithCheckbox.js │ │ │ ├── SearchResult.js │ │ │ ├── SearchResultRow.js │ │ │ ├── SkeletonSet.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Alert │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── Breadcrumb │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── item.js │ │ ├── Card │ │ │ ├── Col.js │ │ │ ├── Menu.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Charts │ │ │ ├── AreaChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── BarChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ColumnChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ColumnRangeChart │ │ │ │ ├── index.js │ │ │ │ └── style.scss │ │ │ ├── DalColumnChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── DonutChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── FunnelChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── HeatMapChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Legend │ │ │ │ ├── index.js │ │ │ │ └── styles.scss │ │ │ ├── LineChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── MapChart │ │ │ │ ├── IconButton.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── PieChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ScatterChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── StackedBarChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── StackedColumnChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── TreeMapChart │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Checkbox │ │ │ ├── index.js │ │ │ └── index.mobile.js │ │ ├── Collapse │ │ │ ├── Collapse.js │ │ │ ├── Panel.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils.js │ │ ├── ColorPicker │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── ComboBox │ │ │ ├── TagWrapper │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Copy │ │ │ ├── Copy.scss │ │ │ ├── Copy.stories.tsx │ │ │ ├── Copy.test.tsx │ │ │ ├── Copy.tsx │ │ │ └── index.tsx │ │ ├── Counter │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── DatePickerInput │ │ │ ├── DateInput.js │ │ │ ├── DateRangeInput.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── ExtendedInput │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── Grid │ │ │ ├── col.js │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ ├── index.scss │ │ │ ├── row.js │ │ │ └── utils.js │ │ ├── Holder │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── InteractiveWidget │ │ │ ├── InteractiveWidget.scss │ │ │ ├── InteractiveWidget.stories.tsx │ │ │ ├── InteractiveWidget.test.tsx │ │ │ ├── InteractiveWidget.tsx │ │ │ ├── InteractiveWidgetIcon.tsx │ │ │ └── index.tsx │ │ ├── Menu │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ ├── item.js │ │ │ └── utils.js │ │ ├── MobileNavigation │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── MobilePopup │ │ │ ├── Menu.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Modal │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── NavigationMenu │ │ │ ├── Content.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils.js │ │ ├── Notification │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── Overlay │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Pagination │ │ │ ├── index.js │ │ │ ├── index.mobile.js │ │ │ └── index.scss │ │ ├── Products │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Profile │ │ │ ├── Languages │ │ │ │ ├── flags │ │ │ │ │ ├── de.svg │ │ │ │ │ ├── en.svg │ │ │ │ │ ├── es.svg │ │ │ │ │ ├── flagsIcons.js │ │ │ │ │ ├── fr.svg │ │ │ │ │ ├── hi.svg │ │ │ │ │ ├── ko.svg │ │ │ │ │ ├── pt.svg │ │ │ │ │ ├── ro.svg │ │ │ │ │ ├── ru.svg │ │ │ │ │ ├── tr.svg │ │ │ │ │ └── zh.svg │ │ │ │ └── languagesDataModel.js │ │ │ ├── Partners.js │ │ │ ├── ProfileModule.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Progress │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── RadioGroup │ │ │ ├── index.js │ │ │ └── index.mobile.js │ │ ├── Range │ │ │ └── index.js │ │ ├── Search │ │ │ └── index.js │ │ ├── Section │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Slider │ │ │ └── index.js │ │ ├── Status │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Steps │ │ │ ├── DetailedView.js │ │ │ ├── Step.js │ │ │ ├── Steps.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── SuggestionList │ │ │ ├── detectPosition.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Tabs │ │ │ ├── Tab.js │ │ │ ├── Tabs.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Tag │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Textarea │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── TimePicker │ │ │ ├── Popover │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Timeline │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ ├── item.js │ │ │ └── item.scss │ │ ├── Tooltip │ │ │ ├── Tooltip.scss │ │ │ ├── Tooltip.stories.tsx │ │ │ ├── Tooltip.test.tsx │ │ │ ├── Tooltip.tsx │ │ │ └── index.tsx │ │ ├── Uploader │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ ├── uploadUtils.js │ │ │ ├── uploadView │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ └── uploadedItem │ │ │ │ ├── Preview │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ ├── ValidatableElements │ │ │ ├── Elements │ │ │ │ ├── ValidatableCheckbox.js │ │ │ │ ├── ValidatableDatePicker.js │ │ │ │ ├── ValidatableDropdown.js │ │ │ │ ├── ValidatableMultiSelectDropdown.js │ │ │ │ ├── ValidatableNumberInput.js │ │ │ │ ├── ValidatableRadio.js │ │ │ │ ├── ValidatableSwitcher.js │ │ │ │ ├── ValidatableTextInput.js │ │ │ │ ├── ValidatableTimeInput.js │ │ │ │ ├── ValidatableUploader.js │ │ │ │ └── index.mobile.js │ │ │ └── index.js │ │ └── Widget │ │ │ ├── index.js │ │ │ └── index.scss │ ├── organisms │ │ ├── ActionableList │ │ │ ├── Row.js │ │ │ ├── RowContent.js │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── CardList │ │ │ ├── DefaultCardList │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── WrappedCardList │ │ │ │ ├── PaperWrapper │ │ │ │ │ └── index.js │ │ │ │ ├── WithTitle │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── CheckboxGroup │ │ │ ├── index.js │ │ │ └── index.mobile.js │ │ ├── CheckboxGroupWithSearch │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── DateFilter │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── DatePicker │ │ │ ├── Calendar │ │ │ │ ├── Body.js │ │ │ │ ├── Footer.js │ │ │ │ ├── Header.js │ │ │ │ └── index.js │ │ │ ├── Context │ │ │ │ ├── configs.js │ │ │ │ └── index.js │ │ │ ├── DatePicker.js │ │ │ ├── Days │ │ │ │ ├── Day.js │ │ │ │ ├── Days.js │ │ │ │ └── index.js │ │ │ ├── MonthPicker.js │ │ │ ├── Months │ │ │ │ └── index.js │ │ │ ├── RangeOptions │ │ │ │ ├── RangeOptions.js │ │ │ │ ├── index.js │ │ │ │ └── options.js │ │ │ ├── RangePicker.js │ │ │ ├── TimeInput │ │ │ │ └── index.js │ │ │ ├── WeekPicker.js │ │ │ ├── Years │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils │ │ │ │ ├── addTime.js │ │ │ │ ├── disableUtils.js │ │ │ │ ├── getCalendarDays.js │ │ │ │ ├── getCalendarMonths.js │ │ │ │ ├── getCalendarYears.js │ │ │ │ ├── getRange.js │ │ │ │ └── index.js │ │ ├── Drawer │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Dropdown │ │ │ ├── MultipleSelect.js │ │ │ ├── SingleSelect.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Editor │ │ │ ├── emojis.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── toolbarConfig.js │ │ ├── Form │ │ │ ├── FormableHOC.js │ │ │ ├── Formables │ │ │ │ ├── FormContainer.js │ │ │ │ ├── FormableCheckbox.js │ │ │ │ ├── FormableDatePicker.js │ │ │ │ ├── FormableDropdown.js │ │ │ │ ├── FormableEditor.js │ │ │ │ ├── FormableMultiSelectDropdown.js │ │ │ │ ├── FormableNumberInput.js │ │ │ │ ├── FormableRadio.js │ │ │ │ ├── FormableSwitcher.js │ │ │ │ ├── FormableTextInput.js │ │ │ │ └── FormableUploader.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils │ │ │ │ └── context │ │ │ │ └── index.js │ │ ├── Overspread │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── RichEditor │ │ │ ├── RichEditor.js │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── SearchWithDropdown │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── Table │ │ │ ├── Footer │ │ │ │ ├── index.js │ │ │ │ ├── index.scss │ │ │ │ └── item.js │ │ │ ├── Header │ │ │ │ ├── index.js │ │ │ │ ├── index.scss │ │ │ │ ├── item.js │ │ │ │ └── resize.js │ │ │ ├── Row │ │ │ │ ├── Nested │ │ │ │ │ ├── body.js │ │ │ │ │ ├── header.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── actionBar.js │ │ │ │ ├── actionsWrapper.js │ │ │ │ ├── col.js │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils │ │ │ │ ├── arrayReorder.js │ │ │ │ ├── getColumnInfo.js │ │ │ │ ├── getOffsetValues.js │ │ │ │ ├── hasStickyElemens.js │ │ │ │ ├── index.js │ │ │ │ ├── itemTypes.js │ │ │ │ ├── observeElementResize.js │ │ │ │ ├── sortHandler.js │ │ │ │ ├── stickyInfo.js │ │ │ │ └── useSortConfigs.js │ │ ├── TableCompositions │ │ │ ├── Combo │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Export │ │ │ │ ├── Item.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Header │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Pagination │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Title │ │ │ │ └── index.js │ │ │ ├── Wrapped │ │ │ │ ├── container.js │ │ │ │ ├── header.js │ │ │ │ ├── index.js │ │ │ │ ├── pagination.js │ │ │ │ └── title.js │ │ │ ├── index.js │ │ │ └── utils │ │ │ │ ├── Export │ │ │ │ └── index.js │ │ │ │ ├── PaginationSelector │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ │ ├── PaperWrapper │ │ │ │ └── index.js │ │ │ │ ├── WithHeader │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ │ ├── WithTitle │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ ├── Toaster │ │ │ ├── index.js │ │ │ └── index.scss │ │ └── TransferList │ │ │ ├── Container │ │ │ ├── Buttons.js │ │ │ ├── Item.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ │ ├── Element │ │ │ ├── index.js │ │ │ └── index.scss │ │ │ ├── constants.js │ │ │ ├── index.js │ │ │ ├── index.scss │ │ │ └── utils.js │ └── providers │ │ └── GeneUIProvider │ │ └── index.js ├── types │ └── index.ts ├── utils │ ├── callAfterDelay.js │ ├── checkTimeValidation.js │ ├── configs │ │ └── tableConfigs.js │ ├── copyToClipboard.js │ ├── dateFormatChecker.js │ ├── dateValidation.js │ ├── debounce.js │ ├── guid.js │ ├── index.js │ ├── indexof.js │ ├── localization.js │ ├── logger.js │ └── timeValidation.js └── wrappers │ ├── dayjsWithPlugins.js │ └── index.js ├── stories ├── assets │ └── storybook.globals.js ├── atoms │ ├── Icon │ │ ├── Icon.stories.jsx │ │ └── Icon.stories.scss │ ├── Label │ │ └── LabelValue.stories.jsx │ ├── ModuleTitle │ │ ├── ModuleTitle.stories.jsx │ │ └── data.js │ ├── Option │ │ └── Option.stories.jsx │ ├── Paper │ │ ├── Paper.stories.jsx │ │ └── index.scss │ ├── Popover │ │ └── Popover.stories.jsx │ ├── QRCode │ │ └── QRCode.stories.jsx │ ├── Radio │ │ └── Radio.stories.jsx │ ├── Scrollbar │ │ └── Scrollbar.stories.jsx │ ├── Skeleton │ │ ├── Skeleton.stories.jsx │ │ └── index.scss │ ├── Switcher │ │ └── Switcher.stories.jsx │ ├── TextLink │ │ └── TextLink.stories.jsx │ ├── Time │ │ └── Time.stories.jsx │ └── Title │ │ └── Title.stories.jsx ├── changelog.mdx ├── charts │ ├── AreaChart.stories.jsx │ ├── BarChart.stories.jsx │ ├── ColumnChart.stories.jsx │ ├── ColumnRange.stories.jsx │ ├── DalColumnChart.stories.jsx │ ├── DonutChart.stories.jsx │ ├── FunnelChart.stories.jsx │ ├── HeatMapChart.stories.jsx │ ├── LineChart.stories.jsx │ ├── Map │ │ ├── data.js │ │ ├── regionData.js │ │ └── woldMapData.js │ ├── MapChart.stories.jsx │ ├── PieChart.stories.jsx │ ├── ScatterChart.stories.jsx │ ├── StackedBarChart.stories.jsx │ ├── StackedColumnChart.stories.jsx │ ├── TreeMapChart.stories.jsx │ ├── data.js │ └── treeMapData.js ├── gettingStarted.mdx ├── index.stories.js ├── introduction.mdx ├── molecules │ ├── AdvancedSearch │ │ ├── AdvancedSearch.stories.jsx │ │ └── data.js │ ├── Alert │ │ └── Alert.stories.jsx │ ├── Breadcrumb │ │ ├── Breadcrumb.stories.jsx │ │ └── data.js │ ├── Card │ │ └── Card.stories.jsx │ ├── Checkbox │ │ └── Checkbox.stories.jsx │ ├── Collapse │ │ └── Collapse.stories.jsx │ ├── ColorPicker │ │ └── ColorPicker.stories.jsx │ ├── ComboBox │ │ ├── ComboBox.stories.jsx │ │ └── data.js │ ├── Counter │ │ └── Counter.stories.jsx │ ├── DatePickerInput │ │ └── DatePicker.stories.jsx │ ├── ExtendedInput │ │ └── ExtendedInput.stories.jsx │ ├── FileUpload │ │ └── FileUpload.stories.jsx │ ├── Grid │ │ ├── Grid.stories.jsx │ │ └── index.scss │ ├── Holder │ │ ├── Holder.stories.jsx │ │ └── index.scss │ ├── ImageUpload │ │ └── ImageUpload.stories.jsx │ ├── Menu │ │ ├── Menu.stories.jsx │ │ └── data.js │ ├── MobileNavigation │ │ ├── MobileNavigation.stories.jsx │ │ └── data.js │ ├── MobilePopup │ │ ├── MobilePopup.stories.jsx │ │ └── data.js │ ├── Modal │ │ └── Modal.stories.jsx │ ├── NavigationMenu │ │ ├── NavigationMenu.stories.jsx │ │ └── data.js │ ├── Notification │ │ └── Notification.stories.jsx │ ├── Overlay │ │ └── Overlay.stories.jsx │ ├── Pagination │ │ └── Pagination.stories.jsx │ ├── Products │ │ ├── Products.stories.jsx │ │ └── data.js │ ├── Profile │ │ ├── Profile.stories.jsx │ │ ├── br.svg │ │ └── data.js │ ├── Progress │ │ └── Progress.stories.jsx │ ├── RadioGroup │ │ ├── RadioGroup.stories.jsx │ │ └── data.js │ ├── Range │ │ └── Range.stories.jsx │ ├── Search │ │ └── Search.stories.jsx │ ├── Section │ │ └── Section.stories.jsx │ ├── Slider │ │ └── Slider.stories.jsx │ ├── Status │ │ └── Status.stories.jsx │ ├── Steps │ │ └── Steps.stories.jsx │ ├── Tabs │ │ ├── Tabs.stories.jsx │ │ └── index.scss │ ├── Tag │ │ └── Tag.stories.jsx │ ├── Textarea │ │ ├── TextArea.stories.jsx │ │ └── data.js │ ├── TimePicker │ │ └── TimePicker.stories.jsx │ ├── Timeline │ │ └── Timeline.stories.jsx │ ├── ValidatableElements │ │ └── ValidatableElements.stories.jsx │ └── Widget │ │ └── Widget.stories.jsx ├── organisms │ ├── ActionableList │ │ ├── ActionableList.stories.jsx │ │ └── data.js │ ├── CardList │ │ ├── CardList.stories.jsx │ │ └── data.js │ ├── CheckboxGroup │ │ └── CheckboxGroup.stories.jsx │ ├── CheckboxGroupWithSearch │ │ └── CheckboxGroupWithSearch.stories.jsx │ ├── DateFilter │ │ ├── DataFilter.stories.jsx │ │ └── style.scss │ ├── DatePicker │ │ └── DatePicker.stories.jsx │ ├── Drawer │ │ ├── Drawer.stories.jsx │ │ └── data.js │ ├── Dropdown │ │ ├── Dropdown.stories.jsx │ │ └── data.js │ ├── Editor │ │ └── Editor.stories.jsx │ ├── Form │ │ ├── Form.stories.jsx │ │ └── style.scss │ ├── Overspread │ │ └── Overspread.stories.jsx │ ├── RichEditor │ │ ├── RichEditor.stories.jsx │ │ └── store.js │ ├── SearchWithDropdown │ │ └── SearchWithDropdown.stories.jsx │ ├── Table │ │ ├── Table.stories.jsx │ │ ├── example.js │ │ └── example2.js │ ├── Toaster │ │ └── Toaster.stories.jsx │ └── TransferList │ │ └── TransferList.stories.jsx └── utils │ ├── ChartAnimation.js │ ├── assets │ └── prism-custom-theme.scss │ ├── index.js │ ├── knobs.js │ └── propTables │ ├── ButtonProp.js │ ├── CardProp.js │ ├── CheckboxProp.js │ ├── ComboTableProp.js │ ├── DateMonthPickerProp.js │ ├── DatePickerProp.js │ ├── DateRangePickerProp.js │ ├── DateWeekPickerProp.js │ ├── ExtendedInputProp.js │ ├── HeaderTableProp.js │ ├── MenuProp.js │ ├── PaperProp.js │ ├── PopoverProp.js │ ├── RadioProp.js │ ├── ScrollbarProp.js │ ├── StepsProp.js │ ├── SwitcherProp.js │ ├── TitleTableProp.js │ ├── ToasterProp.js │ ├── index.js │ └── validateElements │ ├── ValidableDatePickerProp.js │ ├── ValidatableCheckbox.js │ ├── ValidatableNumberinput.js │ ├── ValidatableRadio.js │ └── ValidatableTextInputProp.js └── tests ├── __mocks__ └── svg.js ├── helpers.ts └── setup.ts /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file is define code owners of the repository, more details about the GitHub feature here https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 2 | 3 | * @hamikhambardzumyan @GaroGabrielyan @AregSoft @NairaMisakyan @NarekArshakyan 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 💡 2 | description: >- 3 | Use this template to propose new features. 4 | title: '[Feat]: ' 5 | labels: 6 | - phase / exploring 7 | - type / feature 8 | body: 9 | - type: input 10 | id: component 11 | attributes: 12 | label: Component 13 | description: Write the component name issue is related with. 14 | - type: textarea 15 | id: problem 16 | attributes: 17 | label: Is your feature request related to a problem? Please describe. 18 | description: >- 19 | A clear and concise description of the problem. E.g. I'm always 20 | frustrated when [...] 21 | - type: textarea 22 | id: describe 23 | attributes: 24 | label: Describe the solution you'd like 25 | description: A clear and concise description of what you want to happen. 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: alternatives 30 | attributes: 31 | label: Describe alternatives you've considered 32 | description: Any alternative solutions or features you've considered. 33 | - type: dropdown 34 | id: help 35 | attributes: 36 | label: Are you able to assist to bring the feature to reality? 37 | options: 38 | - 'no' 39 | - 'yes' 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: context 44 | attributes: 45 | label: Additional context 46 | description: Add any other context, screenshots, or already existing solutions about the feature request here. 47 | -------------------------------------------------------------------------------- /.github/workflows/notify-new-issue.yml: -------------------------------------------------------------------------------- 1 | name: Notify New Issue Opened 2 | on: 3 | issues: 4 | types: 5 | - opened 6 | jobs: 7 | send_notification: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: 🔔 Send Notification 11 | env: 12 | INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT: ${{ secrets.INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT }} 13 | run: | 14 | ISSUE_TITLE="${{ github.event.issue.title }}" 15 | ISSUE_LINK=${{ github.event.issue.html_url }} 16 | ISSUE_AUTHOR=${{ github.event.issue.user.login }} 17 | 18 | MESSAGE="@channel\n**New Issue Opened**\nAuthor: $ISSUE_AUTHOR\nTitle: $ISSUE_TITLE\nLink: $ISSUE_LINK" 19 | 20 | curl -X POST -H "Content-Type: application/json" -d "{\"text\": \"$MESSAGE\", \"username\": \"GitHub\", \"icon_url\": \"https://github.githubassets.com/favicons/favicon.png\"}" $INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT 21 | -------------------------------------------------------------------------------- /.github/workflows/notify-reviewers.yml: -------------------------------------------------------------------------------- 1 | name: Notify Reviewers 2 | on: 3 | pull_request: 4 | branches: ["release/*"] 5 | jobs: 6 | send_notification: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Send Notification 10 | env: 11 | INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT: ${{ secrets.INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT }} 12 | run: | 13 | PR_ID=${{ github.event.pull_request.number }} 14 | PR_URL=${{ github.event.pull_request.html_url }} 15 | PR_AUTHOR=${{ github.event.pull_request.user.login }} 16 | REVIEWERS="${{ join(github.event.pull_request.requested_reviewers.*.login, ', ') }}" 17 | 18 | MESSAGE="@channel\nPull Request: [#$PR_ID]($PR_URL) by @$PR_AUTHOR\nRequested Reviewers: $REVIEWERS" 19 | 20 | curl -X POST -H "Content-Type: application/json" -d "{\"text\": \"$MESSAGE\", \"username\": \"GitHub\", \"icon_url\": \"https://github.githubassets.com/favicons/favicon.png\"}" $INTERNAL_NOTIFICATION_CHANNEL_API_ENDPOINT 21 | 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to NPM 2 | on: 3 | pull_request: 4 | types: 5 | - closed 6 | branches: 7 | - main 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Setup Node 15 | uses: actions/setup-node@v2 16 | with: 17 | node-version: '16.x' 18 | registry-url: 'https://registry.npmjs.org' 19 | - name: 📥 Install dependencies 20 | run: npm install 21 | - name: 🔧 Build 22 | run: npm run build 23 | - name: 📦 Publish package on NPM 24 | run: cd dist && npm publish --access public 25 | env: 26 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_ACCESS_TOKEN }} 27 | - name: 🚀 Run bump up commit 28 | run: | 29 | git config --global user.name "Hamik Hambardzumyan" 30 | git config --global user.email "hamik.hambardzumyan@softconstruct.com" 31 | npm run bump-up-commit 32 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request CI 2 | on: 3 | pull_request: 4 | branches: ["release/*", "main"] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | - name: Setup Node 12 | uses: actions/setup-node@v2 13 | with: 14 | node-version: '16.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - name: 📥 Install dependencies 17 | run: npm install 18 | - name: 🔧 Pure build 19 | run: npm run build -- --pure 20 | - name: 🧪 Run tests 21 | run: npm run test 22 | - name: 🧑‍💻 Linting source code 23 | run: npm run lint-styles && npm run lint-scripts 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # builds 7 | build 8 | dist 9 | stats 10 | .rpt2_cache 11 | styleguide 12 | storybook-static 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | jest-coverage 25 | 26 | # Logs 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | #IDE metadata 32 | *.iml 33 | *.ipr 34 | *.idea 35 | *.iws 36 | *.map 37 | *.orig 38 | *.project 39 | *.settings/* 40 | -------------------------------------------------------------------------------- /.storybook/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../configs/.babelrc", 3 | "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"] 4 | } -------------------------------------------------------------------------------- /.storybook/assets/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.storybook/assets/deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.storybook/assets/experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.storybook/assets/screen-rotation-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.storybook/components/DocContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { DocsContainer as BaseContainer } from '@storybook/blocks'; 3 | import { useDarkMode } from 'storybook-dark-mode'; 4 | import { themes } from '@storybook/theming'; 5 | 6 | export const DocsContainer = ({ children, context }) => { 7 | let contextTitle = context?.primaryStory?.title; 8 | 9 | const title = useMemo(() => { 10 | if (contextTitle) { 11 | return contextTitle[contextTitle.length - 2] === '-' 12 | ? contextTitle.replace(contextTitle.slice(-2), '') 13 | : contextTitle; 14 | } 15 | }, [contextTitle]); 16 | 17 | if (contextTitle) context.primaryStory.title = title; 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /.storybook/components/SidebarLabelWrapper/SidebarLabelWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import experimental from '../../assets/experimental.svg'; 3 | import deprecated from '../../assets/deprecated.svg'; 4 | import './index.css'; 5 | 6 | const cmpStages = { 7 | '-e': { 8 | src: experimental, 9 | alt: 'The component is experimental' 10 | }, 11 | '-d': { 12 | src: deprecated, 13 | alt: 'The component is deprecated' 14 | } 15 | }; 16 | 17 | const SidebarLabelWrapper = ({ item }) => { 18 | const stage = useMemo(() => (item.id[item.id.length - 2] === '-' ? item.name.slice(-2) : null), []); 19 | const name = useMemo(() => (stage ? item.name.replace(stage, '') : item.name), []); 20 | 21 | return ( 22 |
23 | {name} 24 | {stage && ( 25 | {cmpStages[stage].alt} 31 | )} 32 |
33 | ); 34 | }; 35 | 36 | export default SidebarLabelWrapper; 37 | -------------------------------------------------------------------------------- /.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { addons } from '@storybook/addons'; 3 | import { themes } from '@storybook/theming'; 4 | import SidebarLabelWrapper from './components/SidebarLabelWrapper/SidebarLabelWrapper'; 5 | 6 | addons.setConfig({ 7 | isFullscreen: false, 8 | showNav: true, 9 | showPanel: 'right', 10 | panelPosition: 'right', 11 | enableShortcuts: true, 12 | showToolbar: true, 13 | theme: themes.dark, 14 | selectedPanel: undefined, 15 | initialActive: 'sidebar', 16 | sidebar: { 17 | showRoots: true, 18 | collapsedRoots: ['other'], 19 | renderLabel: (item) => { 20 | return ; 21 | } 22 | }, 23 | toolbar: { 24 | title: { hidden: false }, 25 | zoom: { hidden: false }, 26 | eject: { hidden: false }, 27 | copy: { hidden: false }, 28 | fullscreen: { hidden: false } 29 | }, 30 | previewTabs: { 31 | canvas: 'Playground' 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /.storybook/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softconstruct/gene-ui-components/ab23d640aa07d2bc6c328037647402a515d6165f/.storybook/public/favicon.ico -------------------------------------------------------------------------------- /.storybook/softConstructThem.js: -------------------------------------------------------------------------------- 1 | import logo from './assets/logo.svg'; 2 | const colorPrimary = `#E90789`; 3 | 4 | //for all theming adjustable features visit -> https://storybook.js.org/docs/react/configure/theming 5 | const baseConfig = { 6 | // Typography 7 | fontBase: '"Open Sans", sans-serif', 8 | fontCode: 'monospace', 9 | 10 | brandTitle: 'Gene UI by SoftConstruct', 11 | barSelectedColor: colorPrimary, 12 | colorSecondary: colorPrimary, 13 | brandImage: logo 14 | }; 15 | 16 | export const softConstructThem = { 17 | dark: { ...baseConfig }, 18 | light: { ...baseConfig } 19 | }; 20 | -------------------------------------------------------------------------------- /.storybook/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | padding: 4px; 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.configPath": "./configs/.prettierrc", 3 | "prettier.ignorePath": "./configs/.prettierignore", 4 | "editor.formatOnSave": true, 5 | 6 | "stylelint.enable": true, 7 | "stylelint.configFile": "./configs/.stylelintrc.json", 8 | "stylelint.validate": ["css", "scss"], 9 | 10 | "eslint.enable": true, 11 | "eslint.options": { 12 | "configFile": "./configs/.eslintrc.json", 13 | "ignorePatterns": [ 14 | ".git/", 15 | ".github/", 16 | "node_modules/", 17 | "dist/", 18 | "stats/", 19 | "coverage/", 20 | "docs/" 21 | ] 22 | }, 23 | 24 | "cSpell.words": [ 25 | "geneui" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 SoftConstruct 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /configs/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-export-default-from", 4 | "@babel/plugin-proposal-optional-chaining", 5 | "@babel/plugin-proposal-class-properties", 6 | "@babel/plugin-transform-react-jsx", 7 | "@babel/plugin-syntax-jsx", 8 | "@babel/plugin-proposal-nullish-coalescing-operator" 9 | ], 10 | "presets": ["@babel/preset-env", "@babel/preset-react"] 11 | } 12 | -------------------------------------------------------------------------------- /configs/.eslintignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | node_modules 4 | 5 | # Ignore artifacts: 6 | dist 7 | stats 8 | 9 | coverage 10 | docs -------------------------------------------------------------------------------- /configs/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*": ["prettier --config ./configs/.prettierrc --ignore-path ./configs/.prettierignore --write"], 3 | "*.js": ["eslint --config ./configs/.eslintrc.json --ignore-path ./configs/.eslintignore --fix"], 4 | "*.scss": ["stylelint **/*.{css,scss,sass} --config ./configs/.stylelintrc.json --fix"] 5 | } 6 | -------------------------------------------------------------------------------- /configs/.prettierignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | 4 | node_modules 5 | 6 | # Ignore artifacts: 7 | dist 8 | stats 9 | 10 | coverage 11 | docs 12 | 13 | **/.prettierignore 14 | 15 | **/*.svg 16 | 17 | **/*.mdx 18 | **/*.md 19 | 20 | **/*.ico 21 | -------------------------------------------------------------------------------- /configs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "proseWrap": "always", 5 | "tabWidth": 4, 6 | "useTabs": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "semi": true 10 | } 11 | -------------------------------------------------------------------------------- /configs/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["main"], 3 | "plugins": [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | "preset": "angular", 8 | "releaseRules": [ 9 | { "type": "fix", "release": "patch" }, 10 | { "type": "feat", "release": "minor" } 11 | ] 12 | } 13 | ], 14 | "@semantic-release/release-notes-generator", 15 | [ 16 | "@semantic-release/changelog", 17 | { 18 | "changelogFile": "CHANGELOG.md", 19 | "changelogTitle": "# Changelog" 20 | } 21 | ], 22 | [ 23 | "@semantic-release/npm", 24 | { 25 | "npmPublish": false 26 | } 27 | ] 28 | ], 29 | "repositoryUrl": "https://github.com/softconstruct/gene-ui-components.git" 30 | } 31 | -------------------------------------------------------------------------------- /configs/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard-scss", "stylelint-config-prettier"], 3 | "rules": { 4 | "no-empty-source": null, 5 | "no-descending-specificity": null, 6 | "font-family-no-missing-generic-family-keyword": null, 7 | "declaration-block-no-shorthand-property-overrides": null, 8 | "declaration-block-no-redundant-longhand-properties": null, 9 | "custom-property-pattern": null, 10 | "keyframes-name-pattern": null, 11 | "no-duplicate-selectors": null, 12 | "declaration-block-no-duplicate-properties": null, 13 | "keyframe-block-no-duplicate-selectors": null, 14 | "selector-pseudo-class-no-unknown": null, 15 | "selector-pseudo-element-no-unknown": null, 16 | "selector-class-pattern": null, 17 | "media-feature-name-no-unknown": null, 18 | "max-line-length": null, 19 | "scss/dollar-variable-pattern": null, 20 | "scss/no-global-function-names": null, 21 | "scss/at-mixin-pattern": null, 22 | "scss/at-if-no-null": null, 23 | "scss/double-slash-comment-whitespace-inside": null, 24 | "scss/operator-no-unspaced": null, 25 | "scss/operator-no-newline-after": null, 26 | "color-function-notation": "legacy", 27 | "value-no-vendor-prefix": null 28 | }, 29 | "ignoreFiles": ["../coverage/**/*.css", "../storybook-static/**/*.css"] 30 | } 31 | -------------------------------------------------------------------------------- /configs/commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | /* 4 | * Any rules defined here will override rules from @commitlint/config-conventional 5 | */ 6 | rules: { 7 | 'scope-case': [0, 'always', ['pascal-case', 'lower-case']] 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /configs/jest.config.js: -------------------------------------------------------------------------------- 1 | // jest.config.js 2 | module.exports = { 3 | preset: 'ts-jest', 4 | transform: { 5 | '^.+\\.tsx?$': [ 6 | 'ts-jest', 7 | { 8 | tsconfig: './configs/tsconfig.json' 9 | } 10 | ], 11 | '^.+\\.js$': [ 12 | 'babel-jest', 13 | { 14 | configFile: './configs/.babelrc' 15 | } 16 | ] 17 | }, 18 | testEnvironment: 'jsdom', 19 | moduleNameMapper: { 20 | '\\.(css|scss)$': 'identity-obj-proxy', 21 | '.svg': '/tests/__mocks__/svg.js', 22 | '^src': '/src', 23 | '^utils$': '/src/utils/', 24 | '^wrappers$': '/src/wrappers/', 25 | '^configs$': '/src/configs.js', 26 | '^hooks$': '/src/hooks/', 27 | '^components$': '/src/index.ts' 28 | }, 29 | setupFilesAfterEnv: ['/tests/setup.ts'], 30 | testMatch: ['**/?(*.)+(test).tsx'], // TODO add .ts also for helpers 31 | collectCoverageFrom: [ 32 | 'src/**/*.tsx', 33 | 'src/hooks/**/*.ts', 34 | '!src/hooks/index.ts', 35 | '!src/**/*.d.ts', 36 | '!src/**/*.stories.tsx' 37 | ], // TODO add .ts also for helpers 38 | coveragePathIgnorePatterns: ['/node_modules/', '/dist/'], 39 | transformIgnorePatterns: ['/node_modules/(?!react-dnd|dnd-core|@react-dnd)'], 40 | modulePathIgnorePatterns: ['node_modules', 'jest-test-results.json'] 41 | }; 42 | -------------------------------------------------------------------------------- /configs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "../src", 4 | "paths": { 5 | "src": ["./"], 6 | "components": ["./index.ts"], 7 | "utils": ["./utils/index.js"], 8 | "wrappers": ["./wrappers/index.js"], 9 | "configs": ["./configs.js"], 10 | "hooks": ["./hooks/index.js"] 11 | }, 12 | "outDir": "../dist", 13 | "target": "es6", 14 | "module": "esnext", 15 | "declaration": true, 16 | "emitDeclarationOnly": true, 17 | "moduleResolution": "node", 18 | "jsx": "react", 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "strict": true, 23 | "noImplicitAny": false // TODO remove after typescript migration 24 | }, 25 | "include": ["../src/**/*.ts", "../src/**/index.tsx"] 26 | } 27 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | process.env.REACT_APP_CORE_PLATFORM === 'mobile' ? require('./src/index.mobile') : require('./src/index'); 3 | -------------------------------------------------------------------------------- /scripts/copyFiles.js: -------------------------------------------------------------------------------- 1 | import { copyStaticFilesToDist } from './utils'; 2 | 3 | copyStaticFilesToDist(); 4 | -------------------------------------------------------------------------------- /scripts/postPublish.js: -------------------------------------------------------------------------------- 1 | import { execCommand } from './utils'; 2 | import pgk from '../package.json'; 3 | 4 | const filesToCommit = 'package.json package-lock.json CHANGELOG.md'; 5 | const commitMessage = `Bump up library version to ${pgk.version}`; 6 | const tagName = `v${pgk.version}`; 7 | const defaultBranch = 'main'; 8 | 9 | // First, remove the current milestone tag as the semantic-release 10 | // plugin does not have an API to disable the push of the generated tag 11 | execCommand(`git push --delete origin ${tagName}`); 12 | 13 | execCommand( 14 | `git add ${filesToCommit} && git commit --no-verify -m '${commitMessage}' && git push origin ${defaultBranch}` 15 | ); 16 | execCommand(`git tag ${tagName} && git push origin ${tagName}`); 17 | -------------------------------------------------------------------------------- /src/assets/media/empty-state/transparent/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/styles/rtl.scss: -------------------------------------------------------------------------------- 1 | @mixin rtl($type, $v1, $v2) { 2 | #{$type}: #{$v1}; 3 | 4 | html[dir='rtl'] & { 5 | #{$type}: #{$v2}; 6 | } 7 | } 8 | 9 | @mixin rtls($type, $v, $side, $suffix: '') { 10 | @if $suffix != '' { 11 | $suffix: '-' + $suffix; 12 | } 13 | 14 | html:not([dir='rtl']) & { 15 | #{$type + '-' + $side + $suffix}: #{$v}; 16 | } 17 | 18 | html[dir='rtl'] & { 19 | @if $side == left { 20 | $type: $type + '-right'; 21 | } @else if $side == right { 22 | $type: $type + '-left'; 23 | } 24 | 25 | #{$type + $suffix}: #{$v}; 26 | } 27 | } 28 | @mixin rtlp($type1, $type2, $v) { 29 | html:not([dir='rtl']) & { 30 | #{$type1}: #{$v}; 31 | } 32 | 33 | html[dir='rtl'] & { 34 | #{$type2}: #{$v}; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/styles/variables.scss: -------------------------------------------------------------------------------- 1 | @import 'rtl'; 2 | @import 'sharedStyles'; 3 | @import 'mixinsAndFunctions'; 4 | 5 | $c: ( 6 | hero: #1473e6, 7 | confirm: #2bc784, 8 | warning: #e6af57, 9 | danger: #e64e48, 10 | note: #fdc625, 11 | message: #8bd779, 12 | background: #fff, 13 | foreground: #3d3d5f 14 | ); 15 | 16 | @each $name, $color in $c { 17 | $c: map-merge($c, (#{$name + '-hover'}: darken($color, 5))); 18 | $c: map-merge($c, (#{$name + '-sc'}: set-color($color))); 19 | $c: map-merge($c, (#{$name + '-rgb'}: #{red($color), green($color), blue($color)})); 20 | $c: map-merge( 21 | $c, 22 | (#{$name + '-sc-rgb'}: #{red(set-color($color)), green(set-color($color)), blue(set-color($color))}) 23 | ); 24 | } 25 | 26 | $f: var(--font-family); 27 | $zIndex: ( 28 | 'loader': 100, 29 | 'header': 200, 30 | 'holder': 200, 31 | 'navigation': 200, 32 | 'modal': 300, 33 | 'overlay': 300, 34 | 'overspread': 300, 35 | 'popover': 400, 36 | 'tooltip': 500, 37 | 'toaster': 500, 38 | 'preview': 500, 39 | 'tableSticky': 10, 40 | 'tableStickyOver': 11, 41 | 'tableStickyOverPlus': 12 42 | ); 43 | $tableTransition: 300ms; 44 | $scrollbar: ( 45 | 'size': var(--scrollbar-size, 16px), 46 | 'thumbSize': var(--scrollbar-thumbSize, 8px), 47 | 'gutter': var(--scrollbar-gutter, 10px), 48 | 'space': var(--scrollbar-space, 4px), 49 | 'thumbBackground': sc(b, 0.05), 50 | 'thumbActiveBackground': sc(b, 0.1) 51 | ); 52 | $popoverClassName: '.popover-positioner'; 53 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { default as useWidth } from './useWidth'; 2 | export { default as useMount } from './useMount'; 3 | export { default as useClick } from './useClick'; 4 | export { default as useToggle } from './useToggle'; 5 | export { default as useUpdate } from './useUpdate'; 6 | export { default as usePrevious } from './usePrevious'; 7 | export { default as useKeyDown } from './useKeyDown'; 8 | export { default as useForceUpdate } from './useForceUpdate'; 9 | export { default as useDeviceType } from './useDeviceType'; 10 | export { default as useWindowSize } from './useWindowSize'; 11 | export { default as useClickOutside } from './useClickOutside'; 12 | export { default as useDebounce } from './useDebounce'; 13 | export { default as useThrottle } from './useThrottle'; 14 | export { default as useMutationObserver } from './useMutationObserver'; 15 | export { default as useUpdatableRef } from './useUpdatableRef'; 16 | export { default as useDidMount } from './useDidMount'; 17 | export { default as useBodyScroll } from './useBodyScroll'; 18 | export { default as useImgDownload } from './useImgDownload'; 19 | export { default as useEllipsisDetection } from './useEllipsisDetection'; 20 | export { default as useHover } from './useHover'; 21 | -------------------------------------------------------------------------------- /src/hooks/useBodyScroll.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | 3 | function useBodyScroll() { 4 | const lock = useCallback(() => { 5 | document.body.style.overflow = 'hidden'; 6 | }, []); 7 | 8 | const unlock = useCallback(() => { 9 | document.body.style.overflow = 'auto'; 10 | }, []); 11 | 12 | return { 13 | lock, 14 | unlock 15 | }; 16 | } 17 | 18 | export default useBodyScroll; 19 | -------------------------------------------------------------------------------- /src/hooks/useClick.js: -------------------------------------------------------------------------------- 1 | import { useRef, useCallback, useEffect } from 'react'; 2 | 3 | function useClick(callback) { 4 | const ref = useRef(); 5 | 6 | const handleClick = useCallback( 7 | (e) => { 8 | if (ref.current && ref.current.contains(e.target)) { 9 | callback && callback(e); 10 | } 11 | }, 12 | [ref.current, callback] 13 | ); 14 | 15 | useEffect(() => { 16 | document.addEventListener('mousedown', handleClick); 17 | return () => document.removeEventListener('mousedown', handleClick); 18 | }, [handleClick]); 19 | 20 | const setRef = (node) => (ref.current = node); 21 | 22 | return setRef; 23 | } 24 | 25 | export default useClick; 26 | -------------------------------------------------------------------------------- /src/hooks/useClickOutside.js: -------------------------------------------------------------------------------- 1 | import { useRef, useCallback, useEffect } from 'react'; 2 | 3 | function useClickOutside(callback, relativeElements) { 4 | const ref = useRef(); 5 | 6 | const handleClickOutside = useCallback( 7 | (e) => { 8 | const { target } = e; 9 | const isNotRelativeTarget = 10 | Array.isArray(relativeElements) && relativeElements.length 11 | ? relativeElements?.find((relativeRef) => !relativeRef.current?.contains(target)) 12 | : true; 13 | 14 | if (ref.current && !ref.current?.contains(target) && isNotRelativeTarget) { 15 | callback && callback(e); 16 | } 17 | }, 18 | [ref.current, callback, relativeElements] 19 | ); 20 | 21 | useEffect(() => { 22 | document.addEventListener('mousedown', handleClickOutside); 23 | return () => document.removeEventListener('mousedown', handleClickOutside); 24 | }, [handleClickOutside]); 25 | 26 | return (node) => (ref.current = node); 27 | } 28 | 29 | export default useClickOutside; 30 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | const useDebounce = (value, delay) => { 4 | if (value === undefined) { 5 | let timeoutId; 6 | 7 | const debounceCallback = (value, delay) => { 8 | clearTimeout(timeoutId); 9 | timeoutId = setTimeout(value, delay); 10 | }; 11 | 12 | const clearDebounce = () => clearTimeout(timeoutId); 13 | 14 | return { debounceCallback, clearDebounce }; 15 | } 16 | 17 | const [debouncedValue, setDebouncedValue] = useState(value); 18 | 19 | useEffect(() => { 20 | const handler = setTimeout(() => { 21 | setDebouncedValue(value); 22 | }, delay); 23 | 24 | return () => { 25 | clearTimeout(handler); 26 | }; 27 | }, [delay, value]); 28 | 29 | return debouncedValue; 30 | }; 31 | 32 | export default useDebounce; 33 | -------------------------------------------------------------------------------- /src/hooks/useDeviceType.js: -------------------------------------------------------------------------------- 1 | import { clientConfigs, mobileScreenSize } from 'configs'; 2 | import useWindowSize from './useWindowSize'; 3 | 4 | function useDeviceType(screenType) { 5 | const { width } = useWindowSize(); 6 | 7 | const type = 8 | screenType || 9 | (clientConfigs.isMobile 10 | ? clientConfigs.isMobile() 11 | ? 'mobile' 12 | : 'desktop' 13 | : width < mobileScreenSize 14 | ? 'mobile' 15 | : 'desktop'); 16 | 17 | return { type, isMobile: type === 'mobile' }; 18 | } 19 | 20 | export default useDeviceType; 21 | -------------------------------------------------------------------------------- /src/hooks/useDidMount.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | const useDidMount = (callback = () => {}, deps = []) => { 4 | const _isFirstUpdate = useRef(true); 5 | 6 | useEffect(() => { 7 | if (!_isFirstUpdate.current) { 8 | callback(); 9 | } 10 | _isFirstUpdate.current = false; 11 | }, deps); 12 | }; 13 | 14 | export default useDidMount; 15 | -------------------------------------------------------------------------------- /src/hooks/useEllipsisDetection/index.ts: -------------------------------------------------------------------------------- 1 | export { default as default } from './useEllipsisDetection'; 2 | -------------------------------------------------------------------------------- /src/hooks/useEllipsisDetection/useEllipsisDetection.test.tsx: -------------------------------------------------------------------------------- 1 | import React, { RefObject } from 'react'; 2 | import { mount } from 'enzyme'; 3 | import useEllipsisDetection from './useEllipsisDetection'; 4 | 5 | describe('useEllipsisDetection', () => { 6 | it('should return false if text is not truncated', () => { 7 | const ref = { 8 | current: { scrollWidth: 100, clientWidth: 200, scrollHeight: 50, clientHeight: 100 } 9 | } as RefObject; 10 | 11 | const Component = () => { 12 | const isTruncated = useEllipsisDetection(ref); 13 | return
{isTruncated ? 'Truncated' : 'Not Truncated'}
; 14 | }; 15 | 16 | const wrapper = mount(); 17 | expect(wrapper.text()).toBe('Not Truncated'); 18 | }); 19 | 20 | it('should return true if text is truncated', () => { 21 | const ref = { 22 | current: { scrollWidth: 300, clientWidth: 200, scrollHeight: 150, clientHeight: 100 } 23 | } as RefObject; 24 | 25 | const Component = () => { 26 | const isTruncated = useEllipsisDetection(ref); 27 | return
{isTruncated ? 'Truncated' : 'Not Truncated'}
; 28 | }; 29 | 30 | const wrapper = mount(); 31 | expect(wrapper.text()).toBe('Truncated'); 32 | }); 33 | 34 | // TODO: need to add resize test case 35 | }); 36 | -------------------------------------------------------------------------------- /src/hooks/useEllipsisDetection/useEllipsisDetection.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, RefObject } from 'react'; 2 | import useDebounce from '../useDebounce'; 3 | 4 | const EQUAL_HEIGHT_DIFF = 3; 5 | 6 | interface IUseEllipsisDetection { 7 | (ref: RefObject, externalDependencies?: any[]): boolean; 8 | } 9 | 10 | const useEllipsisDetection: IUseEllipsisDetection = (ref, externalDependencies = []) => { 11 | const [isTruncated, setIsTruncated] = useState(false); 12 | 13 | const { debounceCallback, clearDebounce } = useDebounce(); 14 | 15 | const handleResize = () => { 16 | if (!ref.current) return; 17 | const { scrollWidth, clientWidth, scrollHeight, clientHeight } = ref.current; 18 | setIsTruncated(scrollWidth > clientWidth || scrollHeight > clientHeight + EQUAL_HEIGHT_DIFF); 19 | }; 20 | 21 | useEffect(() => handleResize(), []); 22 | 23 | useEffect(() => { 24 | const debounce = () => debounceCallback(handleResize, 100); 25 | window.addEventListener('resize', debounce); 26 | 27 | return () => { 28 | clearDebounce(); 29 | window.removeEventListener('resize', debounce); 30 | }; 31 | }, [ 32 | ref, 33 | ref?.current?.scrollWidth, 34 | ref?.current?.clientWidth, 35 | ref?.current?.scrollHeight, 36 | ref?.current?.clientHeight, 37 | ...externalDependencies 38 | ]); 39 | 40 | return isTruncated; 41 | }; 42 | 43 | export default useEllipsisDetection; 44 | -------------------------------------------------------------------------------- /src/hooks/useForceUpdate.js: -------------------------------------------------------------------------------- 1 | import { useReducer } from 'react'; 2 | 3 | const useForceUpdate = () => { 4 | const [ignored, forceUpdate] = useReducer((x) => x + 1, 0); 5 | return forceUpdate; 6 | }; 7 | 8 | export default useForceUpdate; 9 | -------------------------------------------------------------------------------- /src/hooks/useHover.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | const useHover = (ref) => { 4 | const [isHovered, setIsHovered] = useState(false); 5 | 6 | const handleMouseEnter = () => setIsHovered(true); 7 | const handleMouseLeave = () => setIsHovered(false); 8 | 9 | useEffect(() => { 10 | const node = ref.current; 11 | 12 | if (!node) return; 13 | 14 | node.addEventListener('mouseenter', handleMouseEnter); 15 | node.addEventListener('mouseleave', handleMouseLeave); 16 | return () => { 17 | node.removeEventListener('mouseenter', handleMouseEnter); 18 | node.removeEventListener('mouseleave', handleMouseLeave); 19 | }; 20 | }, [ref]); 21 | 22 | return isHovered; 23 | }; 24 | 25 | export default useHover; 26 | -------------------------------------------------------------------------------- /src/hooks/useImgDownload.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | 3 | const useImgDownload = () => 4 | useCallback((url, name, customHeaders = {}) => { 5 | fetch(url, { headers: { ...customHeaders } }) 6 | .then((response) => response.blob()) 7 | .then((blob) => { 8 | const blobURL = URL.createObjectURL(blob); 9 | const a = document.createElement('a'); 10 | a.href = blobURL; 11 | a.download = name && name.length ? name : 'download'; 12 | a.click(); 13 | }) 14 | .catch((error) => console.log(error)); 15 | }, []); 16 | 17 | export default useImgDownload; 18 | -------------------------------------------------------------------------------- /src/hooks/useKeyDown.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect } from 'react'; 2 | 3 | const useKeyDown = (callback, deps, ref, keys = []) => { 4 | const handleKeyDown = useCallback( 5 | (e) => { 6 | if (!keys.length || keys.includes(e.key)) { 7 | callback(e); 8 | } 9 | }, 10 | [...keys, ...deps] 11 | ); 12 | 13 | useEffect(() => { 14 | if (ref && ref.current) { 15 | ref.current.addEventListener('keydown', handleKeyDown); 16 | return () => ref?.current?.removeEventListener('keydown', handleKeyDown); 17 | } 18 | }, [handleKeyDown, ref.current]); 19 | }; 20 | 21 | export default useKeyDown; 22 | -------------------------------------------------------------------------------- /src/hooks/useMount.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | import { noop } from 'utils'; 4 | 5 | const useMount = (onMount = noop) => useEffect(onMount, []); 6 | 7 | export default useMount; 8 | -------------------------------------------------------------------------------- /src/hooks/useMutationObserver.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | const defaultOptions = { attributes: true, childList: true, subtree: true }; 4 | 5 | const useMutationObserver = (ref, callback, options = defaultOptions) => { 6 | useEffect(() => { 7 | if (ref.current) { 8 | const observer = new MutationObserver(callback); 9 | 10 | observer.observe(ref.current, options); 11 | return () => { 12 | observer.disconnect(); 13 | }; 14 | } 15 | }, [callback, options]); 16 | }; 17 | 18 | export default useMutationObserver; 19 | -------------------------------------------------------------------------------- /src/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useRef, useMemo } from 'react'; 2 | 3 | const usePrevious = (callback, deps) => { 4 | const previous = useRef(); 5 | 6 | const value = useMemo(() => callback(previous.current), deps); 7 | previous.current = value; 8 | 9 | return value; 10 | }; 11 | 12 | export default usePrevious; 13 | -------------------------------------------------------------------------------- /src/hooks/useThrottle.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useRef } from 'react'; 2 | 3 | const useThrottle = (callback, delay) => { 4 | const ref = useRef(null); 5 | 6 | return useCallback( 7 | (...args) => { 8 | clearTimeout(ref.current); 9 | 10 | ref.current = setTimeout(() => { 11 | callback(...args); 12 | }, delay); 13 | }, 14 | [callback, delay] 15 | ); 16 | }; 17 | 18 | export default useThrottle; 19 | -------------------------------------------------------------------------------- /src/hooks/useToggle.js: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from 'react'; 2 | 3 | function useToggle(defaultValue) { 4 | const [state, setState] = useState(!!defaultValue); 5 | 6 | const toggle = useCallback((value) => { 7 | setState((prev) => (typeof value === 'undefined' ? !prev : !!value)); 8 | }, []); 9 | 10 | return [state, toggle]; 11 | } 12 | 13 | export default useToggle; 14 | -------------------------------------------------------------------------------- /src/hooks/useUpdatableRef.js: -------------------------------------------------------------------------------- 1 | import { useRef, useCallback } from 'react'; 2 | 3 | import useForceUpdate from './useForceUpdate'; 4 | 5 | const useUpdatableRef = (initial) => { 6 | const ref = useRef(initial); 7 | const forceUpdate = useForceUpdate(); 8 | 9 | const update = useCallback((value) => { 10 | if (ref.current !== value) forceUpdate(); 11 | ref.current = value; 12 | }, []); 13 | 14 | return [ref, update]; 15 | }; 16 | 17 | export default useUpdatableRef; 18 | -------------------------------------------------------------------------------- /src/hooks/useUpdate.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | function useUpdate(callback, dependencies) { 4 | const initial = useRef(true); 5 | useEffect( 6 | initial.current 7 | ? () => { 8 | initial.current = false; 9 | } 10 | : callback, 11 | dependencies 12 | ); 13 | } 14 | 15 | export default useUpdate; 16 | -------------------------------------------------------------------------------- /src/hooks/useWidth.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import useWindowSize from './useWindowSize'; 3 | 4 | function useWidth(ref) { 5 | const [width, setWidth] = useState(null); 6 | 7 | const { width: windowWidth } = useWindowSize(); 8 | 9 | useEffect(() => { 10 | setWidth(ref.current.clientWidth); 11 | }, [ref.current, windowWidth]); 12 | 13 | return width; 14 | } 15 | 16 | export default useWidth; 17 | -------------------------------------------------------------------------------- /src/hooks/useWindowSize.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import useDebounce from './useDebounce'; 3 | 4 | function useWindowSize() { 5 | const w = window; 6 | const { innerHeight, innerWidth } = w; 7 | const [width, setWindowWidth] = useState(innerWidth); 8 | const [height, setWindowHeight] = useState(innerHeight); 9 | const { debounceCallback, clearDebounce } = useDebounce(); 10 | 11 | const handleResize = () => { 12 | const { innerHeight, innerWidth } = w; 13 | setWindowWidth(innerWidth); 14 | setWindowHeight(innerHeight); 15 | }; 16 | 17 | const debounce = () => debounceCallback(handleResize, 100); 18 | 19 | useEffect(() => { 20 | w.addEventListener('resize', debounce); 21 | w.addEventListener('orientationChange', debounce); 22 | 23 | return () => { 24 | w.removeEventListener('resize', debounce); 25 | w.removeEventListener('orientationChange', debounce); 26 | clearDebounce(); 27 | }; 28 | }, []); 29 | 30 | return { 31 | width, 32 | height 33 | }; 34 | } 35 | 36 | export default useWindowSize; 37 | -------------------------------------------------------------------------------- /src/index.mobile.ts: -------------------------------------------------------------------------------- 1 | // Atoms 2 | export * from './lib/atoms/Button/index.mobile'; 3 | export * from './lib/atoms/Divider/index.mobile'; 4 | export * from './lib/atoms/Icon/index.mobile'; 5 | export * from './lib/atoms/Label/index.mobile'; 6 | export * from './lib/atoms/Radio/index.mobile'; 7 | export * from './lib/atoms/Switcher/index.mobile'; 8 | 9 | // Molecules 10 | export * from './lib/molecules/Alert/index.mobile'; 11 | export * from './lib/molecules/Checkbox/index.mobile'; 12 | export * from './lib/molecules/ExtendedInput/index.mobile'; 13 | export * from './lib/molecules/Grid/index.mobile'; 14 | export * from './lib/molecules/Notification/index.mobile'; 15 | export * from './lib/molecules/Pagination/index.mobile'; 16 | export * from './lib/molecules/RadioGroup/index.mobile'; 17 | export * from './lib/molecules/ValidatableElements/Elements/index.mobile'; 18 | export * from './lib/molecules/Counter/index.mobile'; 19 | 20 | // Organisms 21 | export * from './lib/organisms/CheckboxGroup/index.mobile'; 22 | -------------------------------------------------------------------------------- /src/lib/atoms/Avatar/Avatar.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Avatar from './index'; 4 | 5 | describe('Avatar Component', () => { 6 | it('renders without crashing', () => { 7 | const wrapper = shallow(); 8 | expect(wrapper.exists()).toBeTruthy(); 9 | }); 10 | 11 | it('renders with an image when src prop is provided', () => { 12 | const wrapper = shallow(); 13 | expect(wrapper.find('.user-avatar-c').prop('style')).toHaveProperty('backgroundImage', 'url(test-image.jpg)'); 14 | }); 15 | 16 | it('renders with an icon when icon prop is provided', () => { 17 | const wrapper = shallow(); 18 | expect(wrapper.find('.user-avatar-c').hasClass('icon-test')).toBeTruthy(); 19 | }); 20 | 21 | it('renders with initials when children prop is provided', () => { 22 | const wrapper = shallow(); 23 | expect(wrapper.text()).toBe('JD'); // Assuming getInitials returns 'JD' 24 | }); 25 | 26 | it('calls onClick prop when clicked', () => { 27 | const onClickMock = jest.fn(); 28 | const wrapper = shallow(); 29 | wrapper.simulate('click'); 30 | expect(onClickMock).toHaveBeenCalled(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/lib/atoms/Avatar/index.tsx: -------------------------------------------------------------------------------- 1 | export { IAvatarProps, default as default } from './Avatar'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Badge/index.tsx: -------------------------------------------------------------------------------- 1 | export { IBadgeProps, default as default } from './Badge'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/BusyLoader/BusyLoaderHolderHOC.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | interface IBusyLoaderHolderHOC { 5 | children?: ReactNode; 6 | loadingText?: string; 7 | className?: string; 8 | } 9 | 10 | const BusyLoaderHolderHOC: FC = ({ children, loadingText, className }) => { 11 | return ( 12 |
13 | {children} 14 | {loadingText &&
{loadingText}
} 15 |
16 | ); 17 | }; 18 | 19 | export default BusyLoaderHolderHOC; 20 | -------------------------------------------------------------------------------- /src/lib/atoms/BusyLoader/index.tsx: -------------------------------------------------------------------------------- 1 | export { IBusyLoaderProps, default as default } from './BusyLoader'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Button/index.tsx: -------------------------------------------------------------------------------- 1 | export { IButtonProps, default as default } from './Button'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Divider/Divider.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .divider { 4 | width: rem(1); 5 | height: rem(26); 6 | background: sc(b, 0.05); 7 | 8 | &:not(&-withNoSpace) { 9 | margin: 0 rem(15); 10 | 11 | &.type-horizontal { 12 | margin: rem(15) 0; 13 | } 14 | } 15 | 16 | &.type-horizontal { 17 | width: rem(26); 18 | height: rem(1); 19 | 20 | &.full-width { 21 | width: 100%; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/atoms/Divider/Divider.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { args, propCategory } from '../../../../stories/assets/storybook.globals'; 4 | 5 | // Components 6 | import DividerComponent, { IDividerProps } from './index'; 7 | 8 | const meta: Meta = { 9 | title: 'Atoms/Divider', 10 | component: DividerComponent, 11 | argTypes: { 12 | type: args({ control: 'select', ...propCategory.appearance }), 13 | size: args({ control: 'text', ...propCategory.appearance }), 14 | withSpace: args({ control: 'boolean', ...propCategory.appearance }), 15 | className: args({ control: false, ...propCategory.others }) 16 | }, 17 | args: { 18 | size: '50px', 19 | withSpace: true 20 | } 21 | }; 22 | 23 | export default meta; 24 | 25 | const Template: FC = ({ ...args }) => ; 26 | 27 | export const Default = Template.bind({}); 28 | Default.args = { 29 | type: 'horizontal' 30 | }; 31 | 32 | export const Vertical = Template.bind({}); 33 | Vertical.args = { 34 | type: 'vertical' 35 | }; 36 | -------------------------------------------------------------------------------- /src/lib/atoms/Divider/index.tsx: -------------------------------------------------------------------------------- 1 | export { IDividerProps, default as default } from './Divider'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Empty/Empty.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .empty-state-holder { 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | text-align: center; 9 | 10 | &.absolute { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -50%); 15 | } 16 | } 17 | 18 | .empty-state-image { 19 | img { 20 | height: rem(166); 21 | max-width: inherit; 22 | } 23 | 24 | .s-small > & { 25 | img { 26 | height: rem(66); 27 | } 28 | } 29 | 30 | .s-medium > & { 31 | img { 32 | height: rem(120); 33 | } 34 | } 35 | } 36 | 37 | .empty-state-title { 38 | font: 600 rem(16) / rem(20) $f; 39 | 40 | &:not(:last-child) { 41 | margin: 0 0 rem(10); 42 | } 43 | 44 | .s-medium > & { 45 | font-size: rem(14); 46 | } 47 | 48 | .s-small > & { 49 | font-size: rem(12); 50 | } 51 | } 52 | 53 | .empty-state-subtitle { 54 | font: rem(14) / 1.36 $f; 55 | max-width: rem(238); 56 | 57 | .s-small > & { 58 | font-size: rem(10); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/lib/atoms/Empty/Empty.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | 4 | // Helpers 5 | import { args, propCategory } from '../../../../stories/assets/storybook.globals'; 6 | 7 | // Components 8 | import Empty, { IEmptyProps } from './index'; 9 | 10 | const meta: Meta = { 11 | title: 'Atoms/Empty', 12 | component: Empty, 13 | argTypes: { 14 | appearance: args({ control: 'select', ...propCategory.appearance }), 15 | type: args({ control: 'select', ...propCategory.appearance }), 16 | size: args({ control: 'select', ...propCategory.appearance }), 17 | title: args({ control: 'text', ...propCategory.content }), 18 | subTitle: args({ control: 'text', ...propCategory.content }), 19 | className: args({ control: false, ...propCategory.others }), 20 | withImage: args({ control: 'boolean', ...propCategory.functionality }) 21 | }, 22 | args: { 23 | appearance: 'with-circles', 24 | size: 'big', 25 | type: 'data', 26 | withImage: true, 27 | title: 'No Data to Display', 28 | subTitle: 'Subtitle text can bes some descriptive information.' 29 | } 30 | }; 31 | 32 | export default meta; 33 | 34 | const Template: FC = (args) => ; 35 | 36 | export const Default = Template.bind({}); 37 | 38 | export const WithNoImage = Template.bind({}); 39 | 40 | WithNoImage.args = { 41 | type: 'message', 42 | withImage: false 43 | } as IEmptyProps; 44 | -------------------------------------------------------------------------------- /src/lib/atoms/Empty/index.tsx: -------------------------------------------------------------------------------- 1 | export { IEmptyProps, default as default } from './Empty'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Icon/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Image/index.tsx: -------------------------------------------------------------------------------- 1 | export { IImageProps, default as default } from './Image'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/ImagePreview/ImagePreviewHOC.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | // Components 4 | import Portal from '../Portal'; 5 | import ImagePreview, { IImagePreviewProps } from './ImagePreview'; 6 | 7 | const ImagePreviewHOC: FC = ({ withModal, ...restProps }) => { 8 | return withModal ? ( 9 | // @ts-ignore 10 | 11 | 12 | 13 | ) : ( 14 | 15 | ); 16 | }; 17 | 18 | export { IImagePreviewProps, ImagePreviewHOC as default }; 19 | -------------------------------------------------------------------------------- /src/lib/atoms/ImagePreview/index.tsx: -------------------------------------------------------------------------------- 1 | export { IImagePreviewProps, default as default } from './ImagePreviewHOC'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/KeyValue/KeyValue.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Meta } from '@storybook/react'; 3 | import { args, propCategory } from '../../../../stories/assets/storybook.globals'; 4 | 5 | // Components 6 | import KeyValueComponent from '.'; 7 | 8 | // Types 9 | import { IKeyValueProps } from '.'; 10 | 11 | const appearance = ['horizontal', 'vertical'] as const; 12 | 13 | const meta: Meta = { 14 | title: 'Atoms/KeyValue', 15 | component: KeyValueComponent, 16 | argTypes: { 17 | label: args({ control: 'text', ...propCategory.content }), 18 | value: args({ control: 'text', ...propCategory.content }), 19 | className: args({ control: false, ...propCategory.others }), 20 | appearance: args({ 21 | control: 'select', 22 | defaultValue: appearance[0], 23 | options: appearance, 24 | ...propCategory.appearance 25 | }), 26 | icon: args({ control: 'text', ...propCategory.content }) 27 | }, 28 | args: { 29 | label: 'Some label', 30 | value: 'Some value', 31 | icon: 'bc-icon-info', 32 | appearance: appearance[0] 33 | } 34 | }; 35 | 36 | export default meta; 37 | 38 | const Template: FC = ({ ...args }) => ; 39 | 40 | export const Default = Template.bind({}); 41 | 42 | export const Vertical = Template.bind({}); 43 | Vertical.args = { 44 | appearance: appearance[1] 45 | }; 46 | -------------------------------------------------------------------------------- /src/lib/atoms/KeyValue/index.tsx: -------------------------------------------------------------------------------- 1 | export { IKeyValueProps, default as default } from './KeyValue'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Label/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | // Styles 6 | import './index.scss'; 7 | 8 | const labels = { 9 | headingExtraLarge: 'h1', 10 | headingLarge: 'h1', 11 | headingBig: 'h2', 12 | heading: 'h3', 13 | bodyBig: 'h4', 14 | body: 'h5', 15 | bodySmall: 'h6', 16 | content: 'p' 17 | }; 18 | 19 | function Label({ children, className, size, font, ...restProps }) { 20 | return React.createElement( 21 | labels[size], 22 | { 23 | className: classnames('label', className, font, { 24 | large: size === 'headingExtraLarge' 25 | }), 26 | ...restProps 27 | }, 28 | children 29 | ); 30 | } 31 | 32 | Label.propTypes = { 33 | /** 34 | * Any valid React node. 35 | */ 36 | children: PropTypes.string.isRequired, 37 | /** 38 | * Label sizing 39 | */ 40 | size: PropTypes.oneOf([ 41 | 'headingExtraLarge', 42 | 'headingLarge', 43 | 'headingBig', 44 | 'heading', 45 | 'bodyBig', 46 | 'body', 47 | 'bodySmall', 48 | 'content' 49 | ]), 50 | /** 51 | * Label font to be used 52 | */ 53 | font: PropTypes.oneOf(['regular', 'semiBold', 'bold']), 54 | /** 55 | * Addional className 56 | */ 57 | className: PropTypes.string 58 | }; 59 | 60 | Label.defaultProps = { 61 | size: 'content', 62 | font: 'regular' 63 | }; 64 | 65 | export default Label; 66 | -------------------------------------------------------------------------------- /src/lib/atoms/Label/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Label/index.scss: -------------------------------------------------------------------------------- 1 | .label { 2 | display: inline-block; 3 | vertical-align: top; 4 | max-width: 100%; 5 | white-space: nowrap; 6 | text-overflow: ellipsis; 7 | overflow: hidden; 8 | font-weight: normal; 9 | 10 | &.bold { 11 | font-weight: bolder; 12 | } 13 | 14 | &.semiBold { 15 | font-weight: bold; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/atoms/LinkButton/LinkButton.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .linkButton { 4 | cursor: pointer; 5 | display: flex; 6 | align-items: center; 7 | max-width: fit-content; 8 | color: var(--hero); 9 | padding: 0 rem(8); 10 | @include focus-visible; 11 | 12 | &__icon { 13 | display: inline-block; 14 | } 15 | 16 | &-disable { 17 | opacity: 0.3; 18 | pointer-events: none; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/atoms/LinkButton/LinkButton.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import LinkButton from './index'; 3 | import { args, category } from '../../../../stories/assets/storybook.globals'; 4 | 5 | const meta = { 6 | component: LinkButton, 7 | title: 'Atoms/LinkButton', 8 | argTypes: { 9 | href: args({ control: 'text', category: category.content }), 10 | onClick: args({ control: false, category: category.action }), 11 | className: args({ control: false, category: category.others }), 12 | children: args({ control: 'text', category: category.content }), 13 | ariaLabel: args({ control: 'text', category: category.others }), 14 | onMouseDown: args({ control: false, category: category.action }), 15 | iconAfter: args({ control: 'text', category: category.content }), 16 | iconBefore: args({ control: 'text', category: category.content }), 17 | isDisabled: args({ control: 'boolean', category: category.states }) 18 | } 19 | } satisfies Meta; 20 | 21 | export default meta; 22 | 23 | type Story = StoryObj; 24 | 25 | export const Default: Story = {}; 26 | 27 | export const WithIcon: Story = { 28 | args: { 29 | iconAfter: 'bc-icon-arrow-right' 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/lib/atoms/LinkButton/index.tsx: -------------------------------------------------------------------------------- 1 | export { ILinkButtonProps, default as default } from './LinkButton'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/QRCode/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { QRCodeSVG } from 'qrcode.react'; 4 | 5 | const QRConfig = { 6 | levels: { 7 | L: 'L', 8 | M: 'M', 9 | Q: 'Q', 10 | H: 'H' 11 | } 12 | }; 13 | 14 | function QRCode({ value, level, maxSize }) { 15 | return ( 16 | 24 | ); 25 | } 26 | 27 | QRCode.propTypes = { 28 | /** 29 | * If you'd like to apply styles to the single container div that your popover content is rendered within via stylesheets 30 | */ 31 | value: PropTypes.string.isRequired, 32 | /** 33 | * Specifies the maximum size of QRCode component in pixels. 34 | * The component behaves adaptively and listens to the parent wrapper. 35 | */ 36 | maxSize: PropTypes.number, 37 | /** 38 | * Level L (Low) 7% of data bytes can be restored. 39 | * Level M (Medium) 15% of data bytes can be restored. 40 | * Level Q (Quartile)[76] 25% of data bytes can be restored. 41 | * Level H (High) 30% of data bytes can be restored. 42 | */ 43 | level: PropTypes.oneOf(Object.keys(QRConfig.levels)) 44 | }; 45 | 46 | QRCode.defaultProps = { 47 | maxSize: 400, 48 | level: QRConfig.levels.M 49 | }; 50 | 51 | export default QRCode; 52 | -------------------------------------------------------------------------------- /src/lib/atoms/Radio/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/Rating/DefaultSvg.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | function SvgSquareMajor(props) { 4 | return React.createElement( 5 | 'svg', 6 | { 7 | xmlns: 'http://www.w3.org/2000/svg', 8 | width: 24, 9 | height: 24, 10 | viewBox: '0 0 24 24', 11 | strokeLinecap: 'round', 12 | strokeLinejoin: 'round' 13 | }, 14 | props, 15 | React.createElement('rect', { 16 | width: 18, 17 | height: 18, 18 | x: 3, 19 | y: 3, 20 | rx: 2 21 | }) 22 | ); 23 | } 24 | 25 | export default SvgSquareMajor; 26 | -------------------------------------------------------------------------------- /src/lib/atoms/Rating/index.tsx: -------------------------------------------------------------------------------- 1 | export { IRatingProps, default as default } from './Rating'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/SkeletonLoader/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .skeleton-holder { 4 | max-width: 100%; 5 | display: block; 6 | 7 | .react-loading-skeleton { 8 | background-color: var(--background-hover); 9 | background-image: linear-gradient(90deg, var(--background-hover), var(--background), var(--background-hover)); 10 | } 11 | 12 | span { 13 | display: block; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/atoms/Switcher/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/atoms/TextLink/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | function TextLink({ children, ...props }) { 5 | return {children}; 6 | } 7 | 8 | TextLink.propTypes = { 9 | /** 10 | * Any valid React node 11 | */ 12 | children: PropTypes.string.isRequired, 13 | /** 14 | * Additional className 15 | */ 16 | className: PropTypes.string 17 | }; 18 | 19 | export default TextLink; 20 | -------------------------------------------------------------------------------- /src/lib/atoms/Time/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .time-holder { 4 | display: flex; 5 | align-items: center; 6 | font-weight: 600; 7 | white-space: nowrap; 8 | line-height: rem(32); 9 | 10 | > * + * { 11 | @include rtl(margin, 0 0 0 rem(3), 0 rem(3) 0 0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/atoms/Title/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .title-holder { 4 | display: flex; 5 | width: 100%; 6 | align-items: center; 7 | font: 600 rem(14) / rem(18) $f; 8 | min-height: rem(32); 9 | 10 | &.c-hero { 11 | .title-text { 12 | color: c(hero); 13 | } 14 | } 15 | 16 | > li { 17 | flex-shrink: 0; 18 | display: flex; 19 | align-items: center; 20 | 21 | &.line { 22 | flex: auto; 23 | 24 | i { 25 | display: block; 26 | width: 100%; 27 | height: rem(1); 28 | background: sc(b, 0.1); 29 | } 30 | } 31 | 32 | ~ li { 33 | @include rtl(margin, 0 0 0 rem(10), 0 rem(10) 0 0); 34 | } 35 | } 36 | 37 | .divider { 38 | margin: 0 rem(10); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/lib/molecules/AdvancedSearch/SkeletonSet.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkeletonLoader from '../../atoms/SkeletonLoader'; 3 | 4 | function SkeletonSet({ count, searchResult }) { 5 | return searchResult ? ( 6 |
    7 | {Array(count || 1) 8 | .fill('') 9 | .map((_, i) => ( 10 |
  • 11 | 12 |
  • 13 | ))} 14 |
15 | ) : ( 16 |
    17 | {Array(count || 1) 18 | .fill('') 19 | .map((_, i) => ( 20 |
  • 21 | 22 | 23 | 29 |
  • 30 | ))} 31 |
32 | ); 33 | } 34 | 35 | SkeletonSet.defaultProps = {}; 36 | 37 | SkeletonSet.propTypes = {}; 38 | 39 | export default SkeletonSet; 40 | -------------------------------------------------------------------------------- /src/lib/molecules/Alert/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Breadcrumb/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .bread-crumbs-holder { 4 | display: flex; 5 | align-items: center; 6 | justify-content: flex-start; 7 | width: 100%; 8 | font-weight: 600; 9 | min-height: rem(36); 10 | 11 | > * { 12 | + *:not(button) { 13 | @include rtl(margin, 0 0 0 rem(10), 0 rem(10) 0 0); 14 | } 15 | } 16 | 17 | > button { 18 | margin: 0 rem(4); 19 | 20 | + .icon { 21 | @include rtl(margin, 0, 0); 22 | } 23 | } 24 | } 25 | 26 | .breadcrumbs-link { 27 | color: inherit; 28 | cursor: pointer; 29 | @include hover-active { 30 | color: c(hero); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/molecules/Breadcrumb/item.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | function BreadcrumbItem({ data, active, onClick }) { 5 | return ( 6 | 9 | ); 10 | } 11 | 12 | export default BreadcrumbItem; 13 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/BarChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .bar-chart { 4 | min-height: rem(480); 5 | background-color: var(--background); 6 | } 7 | 8 | .bar-chart-proxy-content { 9 | min-height: rem(480); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/ColumnChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .chart-overflow-holder.column-chart { 4 | .highcharts-series-0 { 5 | .highcharts-point { 6 | y: 0; 7 | height: 100%; 8 | } 9 | } 10 | 11 | min-height: rem(480); 12 | background-color: var(--background); 13 | } 14 | 15 | .column-chart-proxy-content { 16 | min-height: rem(480); 17 | 18 | > .icon { 19 | margin: auto; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/ColumnRangeChart/style.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .columnRangeChart { 4 | &__formatter { 5 | color: rgba(0, 0, 0, 80%); 6 | font-weight: 700; 7 | font-size: 10px; 8 | } 9 | } 10 | 11 | .column-range-chart { 12 | min-height: 440px; 13 | background-color: var(--background); 14 | } 15 | 16 | .column-range-proxy-content { 17 | min-height: 440px; 18 | 19 | > .icon { 20 | margin: auto; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/DalColumnChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .highcharts-drillup-button { 4 | rect { 5 | display: none; 6 | } 7 | 8 | &.highcharts-button { 9 | text { 10 | font-size: rem(14) !important; 11 | font-weight: 600 !important; 12 | fill: #3c4043 !important; 13 | } 14 | 15 | &:hover { 16 | text { 17 | fill: c(hero) !important; 18 | } 19 | } 20 | } 21 | } 22 | 23 | .dal-chart { 24 | min-height: rem(550); 25 | background-color: var(--background); 26 | } 27 | 28 | .dal-chart-proxy-content { 29 | min-height: rem(550); 30 | 31 | > .icon { 32 | margin: auto; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/DonutChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .charts-donut-chart { 4 | overflow: hidden; 5 | min-height: rem(550); 6 | background-color: var(--background); 7 | 8 | &.direction-row { 9 | .chart-wrapper { 10 | width: calc(100% - 30rem); 11 | } 12 | } 13 | 14 | .highcharts-title { 15 | white-space: normal !important; 16 | } 17 | 18 | .highcharts-container { 19 | width: 100% !important; 20 | } 21 | 22 | .highcharts-label-box { 23 | fill: none !important; 24 | } 25 | 26 | .highcharts-root { 27 | max-width: 100%; 28 | margin: 0 auto; 29 | } 30 | 31 | .hs-tooltip { 32 | background: rgba(0, 0, 0, 70%); 33 | padding: 10px; 34 | border-radius: 2px; 35 | } 36 | } 37 | 38 | .donut-chart-proxy-content { 39 | min-height: rem(550); 40 | 41 | > .icon { 42 | margin: auto; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/FunnelChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .funnel-chart { 4 | min-height: rem(480); 5 | background-color: var(--background); 6 | } 7 | 8 | .funnel-chart-proxy-content { 9 | min-height: rem(480); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/HeatMapChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .high-chart { 4 | min-height: rem(700); 5 | background-color: var(--background); 6 | } 7 | 8 | .highchart-proxy-content { 9 | min-height: rem(700); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/LineChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .line-chart { 4 | min-height: rem(480); 5 | background-color: var(--background); 6 | } 7 | 8 | .line-chart-proxy-content { 9 | min-height: rem(480); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/MapChart/IconButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Components 5 | import Icon from '../../../atoms/Icon'; 6 | 7 | function MapChartIconButton({ disabled, name, onClick }) { 8 | return ( 9 | 17 | ); 18 | } 19 | 20 | export default MapChartIconButton; 21 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/PieChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .charts-pie-chart { 4 | overflow: hidden; 5 | min-height: rem(480); 6 | background-color: var(--background); 7 | 8 | &.vertical-legend { 9 | width: 100%; 10 | display: flex; 11 | align-items: center; 12 | } 13 | } 14 | 15 | .pie-chart-proxy-content { 16 | min-height: rem(480); 17 | 18 | > .icon { 19 | margin: auto; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/ScatterChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .scatter-chart-holder { 4 | height: rem(1000); 5 | min-height: rem(480); 6 | background-color: var(--background); 7 | 8 | .chart-overflow-header { 9 | display: flex; 10 | align-items: center; 11 | justify-content: space-between; 12 | margin: 10px 0; 13 | padding: 0 10px 0 50px; 14 | height: 40px; 15 | 16 | .chart-label { 17 | display: flex; 18 | align-items: center; 19 | 20 | .label { 21 | max-width: 200px; 22 | color: #3c4043; 23 | } 24 | 25 | .static-title-holder { 26 | margin-left: 10px; 27 | 28 | .tag-c { 29 | color: white; 30 | background: rgba(20, 115, 230, 50%); 31 | } 32 | } 33 | } 34 | } 35 | 36 | .highcharts-selection-marker { 37 | stroke-width: 0.5; 38 | stroke: c(hero); 39 | stroke-linecap: round; 40 | stroke-dasharray: 1, 3; 41 | } 42 | 43 | .highcharts-button { 44 | path, 45 | rect { 46 | fill: c(hero) !important; 47 | 48 | &:hover { 49 | fill: c(hero-hover) !important; 50 | } 51 | } 52 | } 53 | } 54 | 55 | .scatter-chart-proxy-content { 56 | min-height: rem(480); 57 | 58 | > .icon { 59 | margin: auto; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/StackedBarChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .stacked-chart { 4 | min-height: rem(550); 5 | background-color: var(--background); 6 | } 7 | 8 | .stacked-chart-proxy-content { 9 | min-height: rem(550); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/StackedColumnChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .stacked-column-chart { 4 | min-height: rem(480); 5 | background-color: var(--background); 6 | } 7 | 8 | .stacked-column-chart-proxy-content { 9 | min-height: rem(480); 10 | 11 | > .icon { 12 | margin: auto; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/TreeMapChart/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .whiteDrillDown { 4 | .highcharts-drillup-button { 5 | &.highcharts-button:not(:hover) { 6 | text { 7 | fill: white !important; 8 | } 9 | } 10 | } 11 | } 12 | 13 | .tree-map-chart { 14 | min-height: rem(480); 15 | background-color: var(--background); 16 | } 17 | 18 | .tree-map-chart-proxy-content { 19 | min-height: rem(480); 20 | 21 | > .icon { 22 | margin: auto; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/molecules/Charts/index.js: -------------------------------------------------------------------------------- 1 | import './index.scss'; 2 | 3 | export { default as LineChart } from './LineChart'; 4 | export { default as FunnelChart } from './FunnelChart'; 5 | export { default as MapChart } from './MapChart'; 6 | export { default as TreeMapChart } from './TreeMapChart'; 7 | export { default as BarChart } from './BarChart'; 8 | export { default as ColumnChart } from './ColumnChart'; 9 | export { default as DalColumnChart } from './DalColumnChart'; 10 | export { default as StackedBarChart } from './StackedBarChart'; 11 | export { default as StackedColumnChart } from './StackedColumnChart'; 12 | export { default as AreaChart } from './AreaChart'; 13 | export { default as PieChart } from './PieChart'; 14 | export { default as DonutChart } from './DonutChart'; 15 | export { default as ScatterChart } from './ScatterChart'; 16 | export { default as HeatMapChart } from './HeatMapChart'; 17 | export { default as ColumnRangeChart } from './ColumnRangeChart'; 18 | -------------------------------------------------------------------------------- /src/lib/molecules/Checkbox/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Collapse/index.js: -------------------------------------------------------------------------------- 1 | import Collapse from './Collapse'; 2 | import Panel from './Panel'; 3 | 4 | export { Collapse, Panel }; 5 | -------------------------------------------------------------------------------- /src/lib/molecules/Collapse/utils.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | // Local components 4 | import Panel from './Panel'; 5 | 6 | const deepCheck = (children) => { 7 | Array.from(children).every((child) => { 8 | Array.isArray(child) && deepCheck(child); 9 | 10 | return PropTypes.shape({ 11 | type: PropTypes.instanceOf(Panel) 12 | }); 13 | }); 14 | }; 15 | 16 | export default deepCheck; 17 | -------------------------------------------------------------------------------- /src/lib/molecules/ComboBox/TagWrapper/index.scss: -------------------------------------------------------------------------------- 1 | .tag-wrapper { 2 | max-width: 100%; 3 | display: flex; 4 | align-items: center; 5 | 6 | &.flex-basis { 7 | flex-basis: 20px; 8 | align-self: center; 9 | } 10 | 11 | .input { 12 | margin: 1px 0; 13 | width: 100%; 14 | color: var(--background-sc); 15 | -webkit-box-flex: 1; 16 | flex: 1 1; 17 | border: none; 18 | outline: none; 19 | padding: 0; 20 | background: transparent; 21 | caret-color: var(--background-sc); 22 | } 23 | 24 | span { 25 | align-self: flex-start; 26 | height: 0; 27 | visibility: hidden; 28 | white-space: pre; 29 | display: flex; 30 | background: #fff; 31 | color: black; 32 | -webkit-box-flex: 1; 33 | flex: 1 1; 34 | border: none; 35 | outline: none; 36 | padding: 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/molecules/ComboBox/config.js: -------------------------------------------------------------------------------- 1 | export const actionTypes = { 2 | add: 'add', 3 | edit: 'edit', 4 | delete: 'delete' 5 | }; 6 | 7 | export const keyDownKeys = { 8 | space: ' ', 9 | enter: 'Enter', 10 | escape: 'Escape', 11 | arrowDown: 'ArrowDown', 12 | arrowUp: 'ArrowUp', 13 | arrowLeft: 'ArrowLeft', 14 | arrowRight: 'ArrowRight', 15 | backspace: 'Backspace', 16 | delete: 'Delete', 17 | tab: 'Tab' 18 | }; 19 | 20 | export const SPACE_HEIGHT = 8; 21 | -------------------------------------------------------------------------------- /src/lib/molecules/Copy/Copy.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .copy { 4 | cursor: pointer; 5 | max-width: fit-content; 6 | max-height: fit-content; 7 | 8 | &-controlVisibility { 9 | visibility: hidden; 10 | } 11 | 12 | &-pointerNon { 13 | cursor: initial; 14 | } 15 | 16 | &-isVisible { 17 | visibility: visible; 18 | } 19 | 20 | &__showOnHover { 21 | visibility: hidden; 22 | 23 | &-show { 24 | visibility: visible; 25 | } 26 | } 27 | 28 | &__icon { 29 | max-width: fit-content; 30 | max-height: fit-content; 31 | 32 | &-big { 33 | font-size: rem(32); 34 | } 35 | 36 | &-small { 37 | font-size: rem(14); 38 | } 39 | } 40 | 41 | &:hover { 42 | visibility: visible; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/molecules/Copy/Copy.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, ShallowWrapper } from 'enzyme'; 3 | import Copy, { ICopyProps } from './Copy'; 4 | 5 | describe('Copy Component', () => { 6 | const defaultProps: ICopyProps = { 7 | size: 'medium', 8 | showOnHover: false, 9 | copyTooltipText: 'Copy', 10 | copiedTooltipText: 'Copied!' 11 | }; 12 | 13 | const setup = (props?: ICopyProps): ShallowWrapper => { 14 | const finalProps = { ...defaultProps, ...props }; 15 | return shallow(); 16 | }; 17 | 18 | const wrapper = setup(); 19 | it('renders without crashing', () => { 20 | expect(wrapper.exists()).toBeTruthy(); 21 | }); 22 | 23 | it('renders the copy icon', () => { 24 | expect(wrapper.find('.copy__icon').exists()).toBeTruthy(); 25 | }); 26 | 27 | it('renders the correct tooltip text', () => { 28 | expect(wrapper.find('Tooltip').prop('title')).toBe(defaultProps.copyTooltipText); 29 | }); 30 | 31 | it('renders the copy icon with displayOnHover', () => { 32 | const wrapper = setup({ showOnHover: true }); 33 | expect(wrapper.find('.copy__showOnHover').exists()).toBeTruthy(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/lib/molecules/Copy/index.tsx: -------------------------------------------------------------------------------- 1 | export { ICopyProps, default as default } from './Copy'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Counter/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/DatePickerInput/index.js: -------------------------------------------------------------------------------- 1 | // Styles 2 | import './index.scss'; 3 | 4 | // Local components 5 | import DatePickerInput from './DateInput'; 6 | import DateRangeInput from './DateRangeInput'; 7 | 8 | DatePickerInput.WithRange = DateRangeInput; 9 | 10 | export default DatePickerInput; 11 | -------------------------------------------------------------------------------- /src/lib/molecules/ExtendedInput/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Grid/index.js: -------------------------------------------------------------------------------- 1 | export { default as Row } from './row'; 2 | export { default as Col } from './col'; 3 | -------------------------------------------------------------------------------- /src/lib/molecules/Grid/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Grid/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .grid-holder { 4 | width: 100%; 5 | } 6 | 7 | .grid-items-group { 8 | width: 100%; 9 | display: flex; 10 | flex-wrap: wrap; 11 | } 12 | 13 | .grid-child { 14 | display: flex; 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/molecules/Grid/utils.js: -------------------------------------------------------------------------------- 1 | /* Recognizing screen size by configuration. 2 | * Now we have 5 supported screen sizes. 3 | */ 4 | 5 | const getPartsByWindowSize = (size, options) => { 6 | let screenKey = 'xxl'; 7 | let screenSize = options[screenKey]; 8 | 9 | for (const key in options) { 10 | const value = options[key]; 11 | // find value closest to our window size 12 | if (size <= value && value < screenSize) { 13 | screenKey = key; 14 | screenSize = value; 15 | } 16 | } 17 | return screenKey; 18 | }; 19 | 20 | export { getPartsByWindowSize }; 21 | -------------------------------------------------------------------------------- /src/lib/molecules/InteractiveWidget/InteractiveWidgetIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties, FC, PropsWithChildren } from 'react'; 2 | import classNames from 'classnames'; 3 | 4 | //Components 5 | import Icon from '../../atoms/Icon'; 6 | 7 | interface IInteractiveWidgetIconProps extends PropsWithChildren { 8 | appearance?: 'default' | 'compact'; 9 | iconColor?: string; 10 | iconBackground?: boolean; 11 | } 12 | 13 | const InteractiveWidgetIcon: FC = ({ 14 | children, 15 | iconColor, 16 | appearance, 17 | iconBackground 18 | }) => { 19 | return ( 20 |
24 | {iconBackground &&
} 25 | {typeof children === 'string' ? ( 26 | // @ts-ignore 27 | 28 | ) : ( 29 | children 30 | )} 31 |
32 | ); 33 | }; 34 | 35 | export default InteractiveWidgetIcon; 36 | -------------------------------------------------------------------------------- /src/lib/molecules/InteractiveWidget/index.tsx: -------------------------------------------------------------------------------- 1 | export { IInteractiveWidgetProps, default as default } from './InteractiveWidget'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Menu/utils.js: -------------------------------------------------------------------------------- 1 | export const findDeep = (data, indexStack, index = 0) => 2 | index < indexStack.length ? findDeep(data[indexStack[index]].children, indexStack, index + 1) : data; 3 | -------------------------------------------------------------------------------- /src/lib/molecules/NavigationMenu/utils.js: -------------------------------------------------------------------------------- 1 | const getTitle = (id, data) => { 2 | if (data) { 3 | const matchedDatum = data.find((datum) => datum.id === id); 4 | 5 | return matchedDatum && matchedDatum.title 6 | ? matchedDatum 7 | : data.map((datum) => getTitle(id, datum.data)).filter(Boolean)[0]; 8 | } 9 | }; 10 | 11 | export const getTitlesArray = (ids, data) => ids.map((id) => getTitle(id, data)).filter(Boolean); 12 | 13 | export const navigationOptionsToMenu = (navigationOptions, optionId) => 14 | navigationOptions.map(({ data, ...rest }) => ({ 15 | ...rest, 16 | active: optionId?.toString() === rest.id.toString(), 17 | ...(data ? { children: navigationOptionsToMenu(data, optionId) } : {}) 18 | })); 19 | 20 | export const indexStackFromItems = (stack, items, selectedItemId) => { 21 | if (!items?.length) { 22 | return []; 23 | } 24 | 25 | for (const itemIndex in items) { 26 | const item = items[itemIndex]; 27 | if (item.id === selectedItemId) { 28 | return stack; 29 | } 30 | 31 | if (item.children) { 32 | const currentIndexStack = indexStackFromItems([...stack, Number(itemIndex)], item.children, selectedItemId); 33 | if (currentIndexStack.length) { 34 | return currentIndexStack; 35 | } 36 | } 37 | } 38 | return []; 39 | }; 40 | -------------------------------------------------------------------------------- /src/lib/molecules/Notification/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Pagination/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Pagination/index.scss: -------------------------------------------------------------------------------- 1 | .pagination-holder { 2 | display: flex; 3 | align-items: center; 4 | 5 | .icon { 6 | cursor: pointer; 7 | } 8 | 9 | .defaultCursor { 10 | cursor: default; 11 | } 12 | 13 | .input-element { 14 | text-align: center; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/molecules/Products/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .products-switcher-holder { 4 | display: flex; 5 | flex-wrap: wrap; 6 | width: 100%; 7 | padding: rem(5) rem(20); 8 | justify-content: flex-start; 9 | text-align: center; 10 | 11 | .divider { 12 | margin-left: auto; 13 | margin-right: auto; 14 | } 15 | } 16 | 17 | .product-item { 18 | display: flex; 19 | flex-wrap: wrap; 20 | justify-content: center; 21 | text-align: center; 22 | border: rem(1) solid transparent; 23 | padding: rem(10) rem(5); 24 | width: calc(100% / 3); 25 | border-radius: rem(10); 26 | font: 600 rem(12) / 1.42 $f; 27 | cursor: pointer; 28 | margin: rem(5) 0; 29 | 30 | .icon { 31 | display: flex; 32 | justify-content: center; 33 | align-items: center; 34 | width: rem(50); 35 | height: rem(50); 36 | margin: 0 0 rem(5); 37 | background: sc(b, 0.05); 38 | border-radius: 100%; 39 | transition: 200ms color, 200ms background; 40 | } 41 | 42 | p { 43 | width: 100%; 44 | } 45 | @include hover-active { 46 | .icon { 47 | color: sc(hero); 48 | background: c(hero); 49 | } 50 | } 51 | 52 | &.active { 53 | pointer-events: none; 54 | } 55 | @include hover { 56 | border-color: sc(b, 0.11); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/de.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/en.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/flagsIcons.js: -------------------------------------------------------------------------------- 1 | import en from './en.svg'; 2 | import de from './de.svg'; 3 | import es from './es.svg'; 4 | import fr from './fr.svg'; 5 | import hi from './hi.svg'; 6 | import ko from './ko.svg'; 7 | import pt from './pt.svg'; 8 | import ro from './ro.svg'; 9 | import ru from './ru.svg'; 10 | import tr from './tr.svg'; 11 | import zh from './zh.svg'; 12 | 13 | const flagSVGs = { en, de, es, fr, hi, ko, pt, ro, ru, tr, zh }; 14 | 15 | export default flagSVGs; 16 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/fr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/hi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/ro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/ru.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/tr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/flags/zh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/Languages/languagesDataModel.js: -------------------------------------------------------------------------------- 1 | const languagesDataModel = { 2 | en: { 3 | id: 'en', 4 | title: 'English' 5 | }, 6 | ru: { 7 | id: 'ru', 8 | title: 'Russian' 9 | }, 10 | es: { 11 | id: 'es', 12 | title: 'Spanish' 13 | }, 14 | tr: { 15 | id: 'tr', 16 | title: 'Turkish' 17 | }, 18 | zh: { 19 | id: 'zh', 20 | title: 'Chinese' 21 | }, 22 | ko: { 23 | id: 'ko', 24 | title: 'Korean' 25 | }, 26 | de: { 27 | id: 'de', 28 | title: 'German' 29 | }, 30 | fr: { 31 | id: 'fr', 32 | title: 'France' 33 | }, 34 | ro: { 35 | id: 'ro', 36 | title: 'Romanian' 37 | }, 38 | pt: { 39 | id: 'pt', 40 | title: 'Portuguese' 41 | }, 42 | hi: { 43 | id: 'hi', 44 | title: 'Hindi' 45 | } 46 | }; 47 | 48 | export default languagesDataModel; 49 | -------------------------------------------------------------------------------- /src/lib/molecules/Profile/ProfileModule.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Components 4 | import Avatar from '../../atoms/Avatar'; 5 | import Divider from '../../atoms/Divider'; 6 | 7 | // Styles 8 | import './index.scss'; 9 | 10 | function ProfileModule({ username, email, avatarProps }) { 11 | return ( 12 | <> 13 |
    14 |
  • 15 | 16 |
  • 17 |
  • 18 |
    {username}
    19 |
    {email}
    20 |
  • 21 |
22 | 23 | 24 | ); 25 | } 26 | 27 | export default ProfileModule; 28 | -------------------------------------------------------------------------------- /src/lib/molecules/RadioGroup/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Section/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .section-holder { 4 | width: 100%; 5 | background: c(b); 6 | border: rem(1) solid sc(b, 0.1); 7 | border-radius: rem(20); 8 | padding: 0 rem(20) rem(20); 9 | 10 | & + & { 11 | margin: rem(13) 0 0; 12 | } 13 | } 14 | 15 | .section-head { 16 | display: flex; 17 | align-items: center; 18 | width: 100%; 19 | height: rem(54); 20 | } 21 | 22 | .section-body { 23 | display: flex; 24 | flex-direction: column; 25 | align-items: stretch; 26 | } 27 | 28 | .section-sub-group { 29 | width: 100%; 30 | 31 | &:not(:last-child) { 32 | margin-bottom: rem(14); 33 | } 34 | 35 | &:not(:first-child) { 36 | margin-top: rem(7); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/molecules/Steps/index.js: -------------------------------------------------------------------------------- 1 | export { default as Step } from './Step'; 2 | export { default as Steps } from './Steps'; 3 | -------------------------------------------------------------------------------- /src/lib/molecules/SuggestionList/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .suggestion-list { 4 | overflow: hidden; 5 | position: fixed; 6 | background: c(b); 7 | border-radius: 1rem; 8 | box-shadow: 0 0.2rem 0.4rem 0 rgba(0, 0, 0, 5%), 0 0 0 1px rgba(#fff, 0.08); 9 | border: 1px solid rgba(0, 0, 0, 5%); 10 | z-index: 400; 11 | width: 200px; 12 | 13 | .suggestion-rows { 14 | width: 100%; 15 | font: 600 rem(14) / rem(18) $f; 16 | 17 | ul { 18 | width: 100%; 19 | 20 | li { 21 | width: 100%; 22 | display: flex; 23 | padding-left: 16px; 24 | align-items: center; 25 | height: rem(40); 26 | cursor: pointer; 27 | transition: 400ms background, 400ms color; 28 | 29 | &.hover { 30 | background: sc(b, 0.05); 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/lib/molecules/Tabs/index.js: -------------------------------------------------------------------------------- 1 | export { default as Tab } from './Tab'; 2 | export { default as Tabs } from './Tabs'; 3 | -------------------------------------------------------------------------------- /src/lib/molecules/Textarea/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Timeline/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | // Helpers 6 | import { childrenOf } from 'utils'; 7 | 8 | // Local components 9 | import TimelineItem, { timelineColors, timelineAppearances } from './item'; 10 | 11 | // Styles 12 | import './index.scss'; 13 | 14 | function Timeline(props) { 15 | const { children, className, ...restProps } = props; 16 | 17 | return ( 18 |
19 | {children} 20 |
21 | ); 22 | } 23 | 24 | Timeline.propTypes = { 25 | /** 26 | * External/Additional className 27 | */ 28 | className: PropTypes.string, 29 | /** 30 | * Any valid React node. 31 | */ 32 | children: childrenOf([TimelineItem]) 33 | }; 34 | 35 | export { Timeline, TimelineItem, timelineColors, timelineAppearances }; 36 | -------------------------------------------------------------------------------- /src/lib/molecules/Timeline/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .timeline-holder { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/molecules/Tooltip/Tooltip.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .tooltip { 4 | &-c-p { 5 | pointer-events: none; 6 | z-index: 10; 7 | 8 | .s-small & { 9 | padding: rem(15); 10 | } 11 | } 12 | 13 | &-wrapper { 14 | max-width: inherit; 15 | } 16 | 17 | &-content { 18 | border-radius: rem(4); 19 | padding: rem(6) rem(10); 20 | font: 600 rem(12) / rem(16) $f; 21 | background: sc(b, 0.6); 22 | color: c(b, 0.7); 23 | max-width: rem(200); 24 | word-break: break-word; 25 | 26 | > * + * { 27 | margin: rem(2) 0 0; 28 | } 29 | 30 | .s-small & { 31 | padding: rem(6) rem(8); 32 | font: 600 rem(10) / rem(14) $f; 33 | } 34 | } 35 | 36 | &-title { 37 | color: c(b); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/molecules/Tooltip/index.tsx: -------------------------------------------------------------------------------- 1 | export { ITooltipProps, default as default } from './Tooltip'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/Uploader/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .uploader-holder { 4 | width: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: flex-start; 8 | 9 | .grid-child { 10 | position: relative; 11 | min-height: rem(28); 12 | } 13 | } 14 | 15 | .uploader-header { 16 | width: 100%; 17 | display: flex; 18 | flex-direction: column; 19 | align-items: flex-start; 20 | 21 | &:not(.only-child) { 22 | margin: 0 0 rem(14); 23 | } 24 | } 25 | 26 | .uploader-footer { 27 | width: 100%; 28 | display: flex; 29 | flex-direction: column; 30 | align-items: flex-start; 31 | 32 | .ua-cloud > & { 33 | align-items: center; 34 | } 35 | 36 | .ua-input > & { 37 | padding: 0 rem(20); 38 | } 39 | 40 | .uploader-footer-button { 41 | margin: rem(18) 0 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/molecules/Uploader/uploadedItem/Preview/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .media-preview-holder { 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | background-color: rgba(0, 0, 0, 80%); 10 | color: white; 11 | display: flex; 12 | @include zIndex(preview); 13 | 14 | .bc-icon-close { 15 | position: absolute; 16 | top: rem(22); 17 | right: rem(22); 18 | cursor: pointer; 19 | } 20 | } 21 | 22 | .media-preview { 23 | margin: auto; 24 | display: flex; 25 | flex-direction: column; 26 | position: relative; 27 | padding: rem(30) 0 0; 28 | 29 | .mp-title { 30 | font: 600 rem(14) / rem(18) $f; 31 | position: absolute; 32 | top: 0; 33 | left: 0; 34 | width: 100%; 35 | } 36 | } 37 | 38 | .mp-details { 39 | margin: rem(5) 0 0; 40 | font: 600 rem(14) / rem(20) $f; 41 | 42 | > li { 43 | margin: rem(10) 0 0; 44 | } 45 | 46 | span { 47 | opacity: 0.7; 48 | } 49 | } 50 | 51 | .mp-element { 52 | width: auto; 53 | height: auto; 54 | max-width: 100%; 55 | max-height: calc(100vh - #{rem(200)}); 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/molecules/ValidatableElements/Elements/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from '../index'; 2 | -------------------------------------------------------------------------------- /src/lib/molecules/ValidatableElements/index.js: -------------------------------------------------------------------------------- 1 | export { default as ValidatableDropdown } from './Elements/ValidatableDropdown'; 2 | export { default as MultiSelectDropdownField } from './Elements/ValidatableMultiSelectDropdown'; 3 | export { default as ValidatableEditor } from '../../organisms/Editor'; 4 | 5 | export { default as ValidatableUploader } from './Elements/ValidatableUploader'; 6 | export { default as ValidatableRadio } from './Elements/ValidatableRadio'; 7 | export { default as ValidatableCheckbox } from './Elements/ValidatableCheckbox'; 8 | export { default as ValidatableSwitcher } from './Elements/ValidatableSwitcher'; 9 | 10 | export { default as ValidatableNumberInput } from './Elements/ValidatableNumberInput'; 11 | export { default as ValidatableTextInput } from './Elements/ValidatableTextInput'; 12 | export { default as ValidatableDatePicker } from './Elements/ValidatableDatePicker'; 13 | -------------------------------------------------------------------------------- /src/lib/organisms/ActionableList/config.js: -------------------------------------------------------------------------------- 1 | // @TODO think about internalization give possibilities to 2 | // pass tooltip texts via props refactor code duplications 3 | export const searchMethods = { 4 | like: { 5 | value: 'like', 6 | tooltipText: 7 | 'Your typed text will be applied to the last level of the data and will use the "like search" algorithm.' 8 | }, 9 | startsWith: { 10 | value: 'startsWith', 11 | tooltipText: 12 | 'Your typed text will be applied to the last level of the data and will use the "starts with search" algorithm.' 13 | }, 14 | endsWith: { 15 | value: 'endsWith', 16 | tooltipText: 17 | 'Your typed text will be applied to the last level of the data and will use the "ends with search" algorithm.' 18 | } 19 | }; 20 | 21 | export const searchFunctions = { 22 | [searchMethods.like.value]: (label, searchKey) => `${label}`.toLowerCase().includes(searchKey.toLowerCase()), 23 | [searchMethods.startsWith.value]: (label, searchKey) => label.toLowerCase().startsWith(searchKey.toLowerCase()), 24 | [searchMethods.endsWith.value]: (label, searchKey) => label.toLowerCase().endsWith(searchKey.toLowerCase()) 25 | }; 26 | -------------------------------------------------------------------------------- /src/lib/organisms/CardList/WrappedCardList/PaperWrapper/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Components 5 | import Paper from '../../../../atoms/Paper'; 6 | 7 | function PaperWrapper({ paperDirection, cornerRadius, className, children, shadow, ...restProps }) { 8 | return ( 9 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | PaperWrapper.propTypes = { 22 | className: PropTypes.string, 23 | children: PropTypes.node.isRequired, 24 | cornerRadius: PropTypes.string, 25 | shadow: PropTypes.bool, 26 | ...Paper.propTypes 27 | }; 28 | 29 | PaperWrapper.defaultProps = { 30 | shadow: false, 31 | cornerRadius: 'full-radius', 32 | paperDirection: 'column' 33 | }; 34 | 35 | export default PaperWrapper; 36 | -------------------------------------------------------------------------------- /src/lib/organisms/CardList/WrappedCardList/WithTitle/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Components 5 | import ModuleTitle from '../../../../atoms/ModuleTitle'; 6 | 7 | function WithTitle({ name, actions, children, ...restProps }) { 8 | return ( 9 | <> 10 | 11 | {actions} 12 | 13 | {children} 14 | 15 | ); 16 | } 17 | 18 | WithTitle.propTypes = { 19 | name: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, 20 | actions: PropTypes.node, 21 | children: PropTypes.node 22 | }; 23 | 24 | export default WithTitle; 25 | -------------------------------------------------------------------------------- /src/lib/organisms/CardList/index.js: -------------------------------------------------------------------------------- 1 | export { default as WrappedCardList } from './WrappedCardList'; 2 | export { default as CardList } from './DefaultCardList'; 3 | -------------------------------------------------------------------------------- /src/lib/organisms/CheckboxGroup/index.mobile.js: -------------------------------------------------------------------------------- 1 | export * from './index'; 2 | -------------------------------------------------------------------------------- /src/lib/organisms/CheckboxGroupWithSearch/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .checkbox-group-with-search-holder { 4 | width: 100%; 5 | background: c(b); 6 | } 7 | 8 | .empty-data-holder { 9 | width: 100%; 10 | height: rem(190); 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | } 15 | 16 | .c-g-w-s-content { 17 | display: flex; 18 | flex-direction: column; 19 | width: 100%; 20 | background: sc(b, 0.01); 21 | 22 | > li { 23 | width: 100%; 24 | display: flex; 25 | } 26 | } 27 | 28 | .c-g-w-s-head { 29 | padding: rem(15); 30 | border-bottom: rem(1) solid sc(b, 0.1); 31 | } 32 | 33 | .c-g-w-s-footer { 34 | padding: rem(15) 0; 35 | display: flex; 36 | justify-content: center; 37 | border-top: rem(1) solid sc(b, 0.1); 38 | 39 | > * { 40 | + * { 41 | margin-inline-start: rem(10); 42 | } 43 | } 44 | } 45 | 46 | .c-g-w-s-body-content { 47 | width: 100%; 48 | padding: rem(15) 0; 49 | max-height: rem((15 * 2) + (40 * 7)); 50 | } 51 | 52 | .grouped-checkbox-with-search { 53 | flex-direction: column; 54 | 55 | > div { 56 | padding: 1rem !important; 57 | } 58 | } 59 | 60 | .checkbox-group-with-search-holder { 61 | .crs-holder.active { 62 | @media (hover: hover) { 63 | background: sc(b, 0.05); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib/organisms/DateFilter/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .date-filters-holder { 4 | display: flex; 5 | align-items: center; 6 | width: 100%; 7 | justify-content: flex-end; 8 | 9 | > *:not(:last-child) { 10 | flex-shrink: 0; 11 | } 12 | } 13 | 14 | .date-filter-input { 15 | margin-inline-start: rem(10); 16 | 17 | .input-structure { 18 | @include rtlp(padding-right, padding-left, 0); 19 | } 20 | 21 | .icon { 22 | width: rem(36); 23 | height: rem(36); 24 | border-radius: var(--input-element-border-radius, 100%); 25 | @include rtl(margin, 0 0 0 rem(5), 0 rem(5) 0 0); 26 | } 27 | 28 | &.active { 29 | .icon { 30 | background: c(hero); 31 | color: sc(hero); 32 | } 33 | 34 | .input-element-back { 35 | background: transparent; 36 | border-color: c(hero); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/Context/configs.js: -------------------------------------------------------------------------------- 1 | const configs = { 2 | weekdays: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], 3 | months: [ 4 | 'January', 5 | 'February', 6 | 'March', 7 | 'April', 8 | 'May', 9 | 'June', 10 | 'July', 11 | 'August', 12 | 'September', 13 | 'October', 14 | 'November', 15 | 'December' 16 | ], 17 | weekdaysSlice: 1, 18 | monthsSlice: 3, 19 | weekStart: 1, 20 | buttons: { 21 | today: 'Today', 22 | apply: 'Apply', 23 | thisMonth: 'This Month', 24 | thisWeek: 'This Week' 25 | }, 26 | actions: { 27 | today: 'Today', 28 | yesterday: 'Yesterday', 29 | last7Days: 'Last 7 days', 30 | last30Days: 'Last 30 days', 31 | thisMonth: 'This Month', 32 | custom: 'Custom' 33 | } 34 | }; 35 | 36 | export default configs; 37 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/Context/index.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState, useCallback, useContext } from 'react'; 2 | import defaultConfigs from './configs'; 3 | 4 | const Context = createContext([defaultConfigs]); 5 | 6 | function Provider({ children }) { 7 | const [state, setState] = useState(defaultConfigs); 8 | 9 | const setter = useCallback((next) => { 10 | setState({ ...defaultConfigs, ...next }); 11 | }, []); 12 | 13 | return {children}; 14 | } 15 | 16 | const useDatePickerContext = () => { 17 | const [configs, setConfigs] = useContext(Context); 18 | return [configs, setConfigs]; 19 | }; 20 | 21 | export { Provider, useDatePickerContext }; 22 | export default Context; 23 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/Days/index.js: -------------------------------------------------------------------------------- 1 | import Days from './Days'; 2 | 3 | export default Days; 4 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/RangeOptions/index.js: -------------------------------------------------------------------------------- 1 | export RangeOptions from './RangeOptions'; 2 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/RangeOptions/options.js: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | // Helpers 4 | import { addTime } from '../utils'; 5 | 6 | const getDefaultOptions = ([startTime, endTime], actionsTextes = {}) => [ 7 | { 8 | name: actionsTextes.today, 9 | start: addTime(dayjs().startOf('d'), startTime), 10 | end: addTime(dayjs().add(1, 'd').startOf('d'), endTime) 11 | }, 12 | { 13 | name: actionsTextes.yesterday, 14 | start: addTime(dayjs().subtract(1, 'd').startOf('d'), startTime), 15 | end: addTime(dayjs().startOf('d'), endTime) 16 | }, 17 | { 18 | name: actionsTextes.last7Days, 19 | start: addTime(dayjs().subtract(6, 'd').startOf('d'), startTime), 20 | end: addTime(dayjs().endOf('d'), endTime) 21 | }, 22 | { 23 | name: actionsTextes.last30Days, 24 | start: addTime(dayjs().subtract(29, 'd').startOf('d'), startTime), 25 | end: addTime(dayjs().startOf('d'), endTime) 26 | }, 27 | { 28 | name: actionsTextes.thisMonth, 29 | start: addTime(dayjs().startOf('M').startOf('d'), startTime), 30 | end: addTime(dayjs().endOf('M').startOf('d'), endTime) 31 | } 32 | ]; 33 | 34 | const getOptionIndex = (rangeStart, rangeEnd, options) => 35 | options.findIndex( 36 | ({ start, end }) => rangeStart && rangeEnd && start.isSame(rangeStart, 'd') && end.isSame(rangeEnd, 'd') 37 | ); 38 | 39 | export { getDefaultOptions, getOptionIndex }; 40 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/index.js: -------------------------------------------------------------------------------- 1 | // Styles 2 | import './index.scss'; 3 | 4 | // Local components 5 | import DatePicker from './DatePicker'; 6 | import WeekPicker from './WeekPicker'; 7 | import MonthPicker from './MonthPicker'; 8 | import RangePicker from './RangePicker'; 9 | import Context, { Provider, useDatePickerContext } from './Context'; 10 | 11 | DatePicker.WeekPicker = WeekPicker; 12 | DatePicker.MonthPicker = MonthPicker; 13 | DatePicker.RangePicker = RangePicker; 14 | DatePicker.Context = Context; 15 | DatePicker.Provider = Provider; 16 | DatePicker.useDatePickerContext = useDatePickerContext; 17 | 18 | export default DatePicker; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/addTime.js: -------------------------------------------------------------------------------- 1 | const addTime = (date, [hour, minute, second]) => date.hour(hour).minute(minute).second(second); 2 | 3 | export default addTime; 4 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/disableUtils.js: -------------------------------------------------------------------------------- 1 | const nextMonthAvailable = (date, max) => !max || date.add(1, 'M').isBefore(max, 'M'); 2 | const prevMonthAvailable = (date, min) => !min || date.subtract(1, 'M').isAfter(min, 'M'); 3 | 4 | export { nextMonthAvailable, prevMonthAvailable }; 5 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/getCalendarDays.js: -------------------------------------------------------------------------------- 1 | const DAYS_TO_SHOW = 6 * 7; 2 | 3 | function getCalendarDays(startOfMonth) { 4 | const firstWeekDay = startOfMonth.day(); 5 | // Because of using Monday as week start 6 | const startDiff = firstWeekDay === 0 ? 6 : firstWeekDay - 1; 7 | const first = startOfMonth.subtract(startDiff, 'days'); 8 | 9 | const days = Array(DAYS_TO_SHOW) 10 | .fill() 11 | .map((_, i) => first.add(i, 'd')); 12 | 13 | return days; 14 | } 15 | 16 | export default getCalendarDays; 17 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/getCalendarMonths.js: -------------------------------------------------------------------------------- 1 | function getCalendarMonths(startOfYear) { 2 | const end = startOfYear.endOf('y'); 3 | 4 | let pointer = startOfYear; 5 | const months = []; 6 | 7 | while (pointer.isBefore(end)) { 8 | months.push(pointer); 9 | pointer = pointer.add(1, 'M'); 10 | } 11 | 12 | return months; 13 | } 14 | 15 | export default getCalendarMonths; 16 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/getCalendarYears.js: -------------------------------------------------------------------------------- 1 | const getCalendarYears = (startYear) => 2 | Array(12) 3 | .fill(startYear - 1) 4 | .map((item, i) => item + i); 5 | 6 | const getStartOfDecade = (year) => Math.floor(year / 10) * 10; 7 | 8 | const nextDecade = (date) => date.set('y', getStartOfDecade(date.year()) + 10).startOf('y'); 9 | const prevDecade = (date) => date.set('y', getStartOfDecade(date.year()) - 10).startOf('y'); 10 | 11 | export { getCalendarYears, getStartOfDecade, nextDecade, prevDecade }; 12 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/getRange.js: -------------------------------------------------------------------------------- 1 | function getRange(rangeStart, rangeEnd, selected) { 2 | return !rangeStart || rangeEnd 3 | ? [selected, null] 4 | : selected.isBefore(rangeStart) 5 | ? [selected, rangeStart] 6 | : [rangeStart, selected]; 7 | } 8 | 9 | export default getRange; 10 | -------------------------------------------------------------------------------- /src/lib/organisms/DatePicker/utils/index.js: -------------------------------------------------------------------------------- 1 | export getRange from './getRange'; 2 | export addTime from './addTime'; 3 | export getCalendarDays from './getCalendarDays'; 4 | export getCalendarMonths from './getCalendarMonths'; 5 | export * from './getCalendarYears'; 6 | export * from './disableUtils'; 7 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableCheckbox.js: -------------------------------------------------------------------------------- 1 | import CheckboxField from '../FormableHOC'; 2 | import ValidatableCheckbox from '../../../molecules/ValidatableElements/Elements/ValidatableCheckbox'; 3 | 4 | export default CheckboxField(ValidatableCheckbox); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableDatePicker.js: -------------------------------------------------------------------------------- 1 | import DatePicker from '../FormableHOC'; 2 | import ValidatableDatePicker from '../../../molecules/ValidatableElements/Elements/ValidatableDatePicker'; 3 | 4 | export default DatePicker(ValidatableDatePicker); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableDropdown.js: -------------------------------------------------------------------------------- 1 | import DropdownField from '../FormableHOC'; 2 | import ValidatableDropdown from '../../../molecules/ValidatableElements/Elements/ValidatableDropdown'; 3 | 4 | export default DropdownField(ValidatableDropdown); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableEditor.js: -------------------------------------------------------------------------------- 1 | import Editor from '../FormableHOC'; 2 | import ValidatableEditor from '../../Editor'; 3 | 4 | export default Editor(ValidatableEditor); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableMultiSelectDropdown.js: -------------------------------------------------------------------------------- 1 | import FormableHOC from '../FormableHOC'; 2 | import MultiSelectDropdownField from '../../../molecules/ValidatableElements/Elements/ValidatableMultiSelectDropdown'; 3 | 4 | export default FormableHOC(MultiSelectDropdownField); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableNumberInput.js: -------------------------------------------------------------------------------- 1 | import NumberInput from '../FormableHOC'; 2 | import ValidatableNumberInput from '../../../molecules/ValidatableElements/Elements/ValidatableNumberInput'; 3 | 4 | export default NumberInput(ValidatableNumberInput); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableRadio.js: -------------------------------------------------------------------------------- 1 | import Radio from '../FormableHOC'; 2 | import ValidatableRadio from '../../../molecules/ValidatableElements/Elements/ValidatableRadio'; 3 | 4 | export default Radio(ValidatableRadio); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableSwitcher.js: -------------------------------------------------------------------------------- 1 | import SwitcherElement from '../FormableHOC'; 2 | import ValidatableSwitcher from '../../../molecules/ValidatableElements/Elements/ValidatableSwitcher'; 3 | 4 | export default SwitcherElement(ValidatableSwitcher); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableTextInput.js: -------------------------------------------------------------------------------- 1 | import TextInput from '../FormableHOC'; 2 | import ValidatableTextInput from '../../../molecules/ValidatableElements/Elements/ValidatableTextInput'; 3 | 4 | export default TextInput(ValidatableTextInput); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/Formables/FormableUploader.js: -------------------------------------------------------------------------------- 1 | import UploaderField from '../FormableHOC'; 2 | import ValidatableUploader from '../../../molecules/ValidatableElements/Elements/ValidatableUploader'; 3 | 4 | export default UploaderField(ValidatableUploader); 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import FormContainer from './Formables/FormContainer'; 5 | 6 | export FormableMultiSelectDropdown from './Formables/FormableMultiSelectDropdown'; 7 | export FormableNumberInput from './Formables/FormableNumberInput'; 8 | export FormableDatePicker from './Formables/FormableDatePicker'; 9 | export FormableTextInput from './Formables/FormableTextInput'; 10 | export FormableDropdown from './Formables/FormableDropdown'; 11 | export FormableCheckbox from './Formables/FormableCheckbox'; 12 | export FormableUploader from './Formables/FormableUploader'; 13 | export FormableSwitcher from './Formables/FormableSwitcher'; 14 | export FormableEditor from './Formables/FormableEditor'; 15 | export FormableRadio from './Formables/FormableRadio'; 16 | export FormableHOC from './FormableHOC'; 17 | 18 | export default FormContainer; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/Form/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .form-buttons { 4 | margin: rem(20) 0 0; 5 | 6 | > * { 7 | + * { 8 | margin-inline-start: rem(10); 9 | } 10 | } 11 | } 12 | 13 | form { 14 | width: 100%; 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/organisms/RichEditor/index.js: -------------------------------------------------------------------------------- 1 | import RichEditor from './RichEditor'; 2 | 3 | export default RichEditor; 4 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Local components 5 | import FooterItem from './item'; 6 | import ActionBar from '../Row/actionBar'; 7 | 8 | // Styles 9 | import './index.scss'; 10 | 11 | function Footer({ values, orders, withRightCorner, withLeftCorners, stickyLeftExist, ...rest }) { 12 | return ( 13 |
14 | {withLeftCorners.map( 15 | (item, index) => 16 | item && ( 17 |
23 | ) 24 | )} 25 | {orders.map(({ id }, index) => ( 26 | 27 | ))} 28 | {withRightCorner && } 29 |
30 | ); 31 | } 32 | 33 | Footer.defaultProps = { 34 | values: {}, 35 | withLeftCorners: [] 36 | }; 37 | 38 | export default Footer; 39 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Footer/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .ta-footer { 4 | .ellipsis-text { 5 | user-select: all; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/Nested/body.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import isEqual from 'react-fast-compare'; 3 | 4 | const Body = ({ rows, columns, isEditActive }) => 5 | rows.length && 6 | rows.map((row, index) => 7 | row.render ? ( 8 | row.render(row, index, columns.dataKey) 9 | ) : ( 10 |
11 | {columns.map((column, index) => ( 12 |
13 | {typeof row.data[column.dataKey] === 'function' 14 | ? row.data[column.dataKey](isEditActive, index, column.dataKey) 15 | : row.data[column.dataKey]} 16 |
17 | ))} 18 |
19 | ) 20 | ); 21 | 22 | export default memo(Body, isEqual); 23 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/Nested/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Header({ columns }) { 4 | return ( 5 |
    6 | {columns.map((column, index) => { 7 | const { render, text } = column; 8 | const children = render ? render(column, index) : text; 9 | 10 | return ( 11 |
    12 |
    {children}
    13 |
    14 | ); 15 | })} 16 |
17 | ); 18 | } 19 | 20 | export default Header; 21 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/Nested/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Local components 4 | import Header from './header'; 5 | import Body from './body'; 6 | 7 | // Styles 8 | import './index.scss'; 9 | 10 | function Nested({ rows, columns, isEditActive, ...restProps }) { 11 | return ( 12 |
13 |
14 | 15 |
16 | ); 17 | } 18 | 19 | Nested.defaultProps = { 20 | columns: [], 21 | rows: [] 22 | }; 23 | 24 | export default Nested; 25 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/Nested/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .nested-table { 4 | width: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | border-radius: rem(4); 8 | border: solid rem(1) sc(b, 0.1); 9 | font: 600 rem(14) / rem(20) $f; 10 | overflow: hidden; 11 | } 12 | 13 | .nt-row { 14 | width: 100%; 15 | display: flex; 16 | 17 | & ~ & { 18 | border-top: solid rem(1) sc(b, 0.1); 19 | } 20 | } 21 | 22 | .nt-head { 23 | background: sc(b, 0.04); 24 | font-weight: 700; 25 | } 26 | 27 | .nt-cell { 28 | padding: rem(11) rem(20); 29 | position: relative; 30 | width: 100%; 31 | display: flex; 32 | overflow: hidden; 33 | word-break: break-word; 34 | 35 | & ~ & { 36 | &::before { 37 | content: ''; 38 | display: block; 39 | position: absolute; 40 | top: rem(10); 41 | bottom: rem(10); 42 | background: c(b); 43 | border-right: rem(1) solid sc(b, 0.15); 44 | @include rtlp(left, right, 0); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/actionBar.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import isEqual from 'react-fast-compare'; 3 | import classnames from 'classnames'; 4 | 5 | // Local components 6 | import ActionsWrapper from './actionsWrapper'; 7 | 8 | function ActionBar({ children, lastColRef, stickyRightExist }) { 9 | return ( 10 |
16 | {children && {children}} 17 |
18 | ); 19 | } 20 | 21 | export default memo(ActionBar, isEqual); 22 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/Row/actionsWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isFragment } from 'react-is'; 3 | 4 | // Components 5 | import Button from '../../../atoms/Button'; 6 | 7 | function ActionsWrapper({ children }) { 8 | return isFragment(children) || Array.isArray(children) ? ( 9 | <> 10 |
11 |
12 |
14 |
15 |
16 |
{children}
17 |
18 | 19 | ) : ( 20 |
21 |
{children}
22 |
23 | ); 24 | } 25 | 26 | export default ActionsWrapper; 27 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/arrayReorder.js: -------------------------------------------------------------------------------- 1 | const arrayReorder = (array, from, to) => { 2 | const arrayCopy = [...array]; 3 | const [removed] = arrayCopy.splice(from, 1); 4 | 5 | arrayCopy.splice(to, 0, removed); 6 | return arrayCopy; 7 | }; 8 | 9 | export default arrayReorder; 10 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/getOffsetValues.js: -------------------------------------------------------------------------------- 1 | export const getOffsetValuesAndSubscriptions = ( 2 | state, 3 | index, 4 | updatedState, 5 | offsetLeft = 0, 6 | offSetRight = 0, 7 | subscribe, 8 | container, 9 | eventName 10 | ) => { 11 | const offset = Object.keys(updatedState) 12 | .map(Number) 13 | .reduce( 14 | (offsetCalc, item) => { 15 | if (index > item) { 16 | offsetCalc.left += updatedState[item].target.clientWidth; 17 | } else if (index < item) { 18 | offsetCalc.right += updatedState[item].target.clientWidth; 19 | } 20 | return offsetCalc; 21 | }, 22 | { 23 | left: offsetLeft, 24 | right: offSetRight 25 | } 26 | ); 27 | 28 | const subscriptions = subscribe( 29 | container, 30 | { 31 | left: updatedState[index].left, 32 | right: updatedState[index].right 33 | }, 34 | eventName, 35 | { index, offset } 36 | ); 37 | 38 | return { 39 | ...state, 40 | [index]: { 41 | ...updatedState[index], 42 | offset, 43 | subscriptions 44 | } 45 | }; 46 | }; 47 | 48 | export default getOffsetValuesAndSubscriptions; 49 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/hasStickyElemens.js: -------------------------------------------------------------------------------- 1 | const hasStickyElements = (columnsObject) => { 2 | const keys = Object.keys(columnsObject); 3 | const stickyLeftExist = keys.some((column) => columnsObject[column].isStickyLeft); 4 | const stickyRightExist = keys.some((column) => columnsObject[column].isStickyRight); 5 | 6 | return { 7 | stickyLeftExist, 8 | stickyRightExist 9 | }; 10 | }; 11 | 12 | export default hasStickyElements; 13 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as observeStickyInfo } from './stickyInfo'; 2 | export { default as useGetColsInfo } from './getColumnInfo'; 3 | export { default as arrayReorder } from './arrayReorder'; 4 | export { default as itemTypes } from './itemTypes'; 5 | export { default as getOffsetValuesAndSubscriptions } from './getOffsetValues'; 6 | export { default as hasStickyElements } from './hasStickyElemens'; 7 | export { default as observeElementResize } from './observeElementResize'; 8 | export { default as sortHandler } from './sortHandler'; 9 | export { default as useSortConfigs } from './useSortConfigs'; 10 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/itemTypes.js: -------------------------------------------------------------------------------- 1 | const itemTypes = { 2 | th: 'TH', 3 | tr: 'TR' 4 | }; 5 | 6 | export default itemTypes; 7 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/observeElementResize.js: -------------------------------------------------------------------------------- 1 | import ResizeObserver from 'resize-observer-polyfill'; 2 | 3 | const observeElementResize = (element, callback) => { 4 | const resizeObserver = new ResizeObserver(callback); 5 | 6 | if (element) { 7 | resizeObserver.observe(element); 8 | return { 9 | unobserve: () => resizeObserver.unobserve(element) 10 | }; 11 | } 12 | }; 13 | 14 | export default observeElementResize; 15 | -------------------------------------------------------------------------------- /src/lib/organisms/Table/utils/sortHandler.js: -------------------------------------------------------------------------------- 1 | const sortHandler = (nextSortObject, setSortObject, onSortChange, sortableColumns, currentSortState) => { 2 | const { id, column, dataKey, index } = nextSortObject; 3 | const isSortable = typeof column.sortable === 'boolean' ? column.sortable : sortableColumns; 4 | const { id: currId, type: currType } = currentSortState; 5 | 6 | const type = id !== currId || !currType ? 'asc' : currType === 'asc' ? 'desc' : null; 7 | 8 | if (isSortable) { 9 | setSortObject({ 10 | id, 11 | type, 12 | index 13 | }); 14 | onSortChange(isSortable, id, index, dataKey, column, type); 15 | } 16 | }; 17 | 18 | export default sortHandler; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Combo/index.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softconstruct/gene-ui-components/ab23d640aa07d2bc6c328037647402a515d6165f/src/lib/organisms/TableCompositions/Combo/index.scss -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Export/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .export-holder { 4 | display: flex; 5 | align-items: center; 6 | height: rem(28); 7 | padding-inline-end: rem(3); 8 | color: c(hero); 9 | user-select: none; 10 | transition: 400ms opacity; 11 | 12 | &.disabled { 13 | opacity: 0.5; 14 | pointer-events: none; 15 | } 16 | 17 | p { 18 | font-weight: 600; 19 | } 20 | 21 | .export-icon-holder { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | width: rem(24); 26 | height: rem(24); 27 | margin: 0 rem(5); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Helpers 5 | import { WithHeader } from '../utils'; 6 | 7 | // Components 8 | import Table from '../../Table'; 9 | 10 | // Styles 11 | import './index.scss'; 12 | 13 | function TableHeader({ 14 | columns, 15 | withSearch, 16 | actions, 17 | handleSearch, 18 | checkAllText, 19 | leftHeaderActions, 20 | hideSearchDropdown, 21 | ...tableProps 22 | }) { 23 | return ( 24 | 33 | 34 | 35 | ); 36 | } 37 | 38 | TableHeader.propTypes = { 39 | /** 40 | * Define is search bar will shown or no 41 | */ 42 | withSearch: PropTypes.bool, 43 | /** 44 | * Any valid react node 45 | */ 46 | headerActions: PropTypes.node 47 | }; 48 | 49 | export default TableHeader; 50 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Header/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .table-actions { 4 | padding: rem(10); 5 | display: flex; 6 | align-items: center; 7 | 8 | > li { 9 | padding: rem(10); 10 | } 11 | 12 | .table-left-actions { 13 | flex: auto; 14 | } 15 | 16 | .table-right-actions { 17 | flex-shrink: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Pagination/index.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softconstruct/gene-ui-components/ab23d640aa07d2bc6c328037647402a515d6165f/src/lib/organisms/TableCompositions/Pagination/index.scss -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Title/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Helpers 5 | import { WithTitle } from '../utils'; 6 | 7 | // Components 8 | import Table from '../../Table'; 9 | 10 | function TableTitle({ name, titleActions, ...tableProps }) { 11 | return ( 12 | 13 |
14 | 15 | ); 16 | } 17 | 18 | TableTitle.propTypes = { 19 | /** 20 | * Any valid react node 21 | */ 22 | titleActions: PropTypes.node, 23 | /** 24 | * Value for title 25 | */ 26 | name: PropTypes.string.isRequired, 27 | /** 28 | * Callback for refresh icon click 29 | */ 30 | onRefreshClick: PropTypes.func 31 | }; 32 | 33 | export default TableTitle; 34 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Wrapped/container.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | // Helpers 6 | import { PaperWrapper, WithTitle } from '../utils'; 7 | 8 | // Local components 9 | import Header from '../Header'; 10 | 11 | function WrapperTableContainer({ name, titleActions, headerActions, className, paperProps, ...headerProps }) { 12 | return ( 13 | 19 | 20 |
21 | 22 | 23 | ); 24 | } 25 | 26 | WrapperTableContainer.propTypes = { 27 | name: PropTypes.string.isRequired, 28 | titleActions: PropTypes.node, 29 | headerActions: PropTypes.node, 30 | ...Header.propTypes 31 | }; 32 | 33 | export default WrapperTableContainer; 34 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Wrapped/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Helpers 5 | import { PaperWrapper } from '../utils'; 6 | 7 | // Local components 8 | import Header from '../Header'; 9 | 10 | function WrapperTableWithHeader({ paperProps, ...props }) { 11 | return ( 12 | 13 |
14 | 15 | ); 16 | } 17 | 18 | export default WrapperTableWithHeader; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Wrapped/index.js: -------------------------------------------------------------------------------- 1 | export { default as TableHeader } from './header'; 2 | export { default as TableTitle } from './title'; 3 | export { default as TablePagination } from './pagination'; 4 | export { default as TableContainer } from './container'; 5 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Wrapped/pagination.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Helpers 5 | import { PaperWrapper } from '../utils'; 6 | 7 | // Local components 8 | import Pagination from '../Pagination'; 9 | 10 | function WrapperTablePagination({ paperProps, ...props }) { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default WrapperTablePagination; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/Wrapped/title.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Helpers 5 | import { PaperWrapper } from '../utils'; 6 | 7 | // Local components 8 | import Title from '../Title'; 9 | 10 | function WrapperTableTitle({ paperProps, ...props }) { 11 | return ( 12 | 13 | 14 | </PaperWrapper> 15 | ); 16 | } 17 | 18 | export default WrapperTableTitle; 19 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/index.js: -------------------------------------------------------------------------------- 1 | export { TableHeader, TableTitle, TablePagination, TableContainer } from './Wrapped'; 2 | export { WithTitle, WithHeader, PaperWrapper, PaginationSelector } from './utils'; 3 | export { default as ComboTable } from './Combo'; 4 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/utils/PaginationSelector/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .pagination-drop { 4 | display: flex; 5 | align-items: center; 6 | 7 | > { 8 | * + * { 9 | @include rtlp(margin-left, margin-right, rem(20)); 10 | } 11 | 12 | p { 13 | font-weight: 600; 14 | } 15 | } 16 | } 17 | 18 | .ta-pagination-holder { 19 | width: 100%; 20 | padding: rem(10); 21 | display: flex; 22 | align-items: center; 23 | flex-shrink: 0; 24 | border-top: rem(1) solid sc(b, 0.1); 25 | flex-wrap: wrap; 26 | justify-content: flex-end; 27 | 28 | > li { 29 | padding: rem(10); 30 | 31 | &:first-child { 32 | flex: auto; 33 | } 34 | 35 | &:last-child { 36 | flex-shrink: 0; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/utils/PaperWrapper/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | // Components 6 | import Paper from '../../../../atoms/Paper'; 7 | 8 | function PaperWrapper({ className, shadow, cornerRadius, paperDirection, children, ...restProps }) { 9 | return ( 10 | <Paper 11 | shadow={shadow} 12 | paperDirection={paperDirection} 13 | cornerRadius={cornerRadius} 14 | className={classnames(className)} 15 | {...restProps} 16 | > 17 | {children} 18 | </Paper> 19 | ); 20 | } 21 | 22 | PaperWrapper.propTypes = { 23 | className: PropTypes.string, 24 | children: PropTypes.node.isRequired, 25 | cornerRadius: PropTypes.string, 26 | shadow: PropTypes.bool 27 | }; 28 | 29 | PaperWrapper.defaultProps = { 30 | shadow: true, 31 | cornerRadius: 'full-radius', 32 | paperDirection: 'column' 33 | }; 34 | 35 | export default PaperWrapper; 36 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/utils/WithHeader/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .paper-actions { 4 | display: flex; 5 | align-items: center; 6 | flex-shrink: 0; 7 | 8 | > li { 9 | padding: rem(20); 10 | display: flex; 11 | align-items: center; 12 | } 13 | 14 | .paper-left-actions { 15 | flex: auto; 16 | 17 | > { 18 | .input-holder, 19 | .dropdown-holder { 20 | max-width: rem(255); 21 | } 22 | } 23 | 24 | > * { 25 | + * { 26 | @include rtlp(margin-left, margin-right, rem(20)); 27 | } 28 | } 29 | } 30 | 31 | .paper-right-actions { 32 | flex-shrink: 0; 33 | 34 | > *:not(.btn) + *:not(.btn) { 35 | @include rtlp(margin-left, margin-right, rem(16)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/utils/WithTitle/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Components 5 | import ModuleTitle from '../../../../atoms/ModuleTitle'; 6 | 7 | function WithTitle({ name, actions, children, ...restProps }) { 8 | return ( 9 | <> 10 | {(!!name || !!actions) && ( 11 | <ModuleTitle title={name} cornerRadius="position-radius" {...restProps}> 12 | {actions} 13 | </ModuleTitle> 14 | )} 15 | {children} 16 | </> 17 | ); 18 | } 19 | 20 | WithTitle.propTypes = { 21 | name: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, 22 | actions: PropTypes.node, 23 | children: PropTypes.node 24 | }; 25 | 26 | export default WithTitle; 27 | -------------------------------------------------------------------------------- /src/lib/organisms/TableCompositions/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as WithTitle } from './WithTitle'; 2 | export { default as WithHeader } from './WithHeader'; 3 | export { default as PaperWrapper } from './PaperWrapper'; 4 | export { default as PaginationSelector } from './PaginationSelector'; 5 | -------------------------------------------------------------------------------- /src/lib/organisms/Toaster/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .toaster-holder { 4 | position: fixed; 5 | padding: rem(15); 6 | max-width: rem(380); 7 | width: 100%; 8 | @include zIndex(toaster); 9 | 10 | > * + * { 11 | margin: rem(20) 0 0; 12 | } 13 | 14 | &.left-top { 15 | left: 0; 16 | top: var(--header-height, 0); 17 | } 18 | 19 | &.left-bottom { 20 | left: 0; 21 | bottom: 0; 22 | } 23 | 24 | &.right-top { 25 | right: 0; 26 | top: var(--header-height, 0); 27 | } 28 | 29 | &.right-bottom { 30 | right: 0; 31 | bottom: 0; 32 | } 33 | 34 | &.top, 35 | &.bottom { 36 | left: 50%; 37 | transform: translate3d(-50%, 0, 0); 38 | } 39 | 40 | &.top { 41 | top: var(--header-height, 0); 42 | } 43 | 44 | &.center { 45 | top: 50%; 46 | left: 50%; 47 | transform: translate3d(-50%, -50%, 0); 48 | } 49 | 50 | &.bottom { 51 | bottom: 0; 52 | } 53 | @include mobile { 54 | width: 100%; 55 | transform: none; 56 | 57 | &:not(.top, .bottom) { 58 | padding: 0; 59 | } 60 | 61 | &.center:not(.top, .bottom) { 62 | padding: 0 rem(15); 63 | } 64 | } 65 | 66 | &:empty { 67 | pointer-events: none; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/lib/organisms/TransferList/Container/Buttons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Components 4 | import Button from '../../../atoms/Button'; 5 | import Tooltip from '../../../molecules/Tooltip'; 6 | 7 | function TransferListButtons({ onTransmit, send, receive, readOnly }) { 8 | const handleReceive = () => onTransmit(send.id, receive.id); 9 | const handleSend = () => onTransmit(receive.id, send.id); 10 | 11 | return ( 12 | <div className="bc-tl-buttons"> 13 | <Tooltip text={send.title}> 14 | <Button 15 | onClick={handleSend} 16 | disabled={readOnly || send.disabled} 17 | icon="bc-icon-arrow-right" 18 | appearance="clean" 19 | /> 20 | </Tooltip> 21 | <Tooltip text={receive.title}> 22 | <Button 23 | onClick={handleReceive} 24 | disabled={readOnly || receive.disabled} 25 | icon="bc-icon-arrow-left" 26 | appearance="clean" 27 | /> 28 | </Tooltip> 29 | </div> 30 | ); 31 | } 32 | 33 | export default TransferListButtons; 34 | -------------------------------------------------------------------------------- /src/lib/organisms/TransferList/Element/index.js: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | // Components 5 | import Icon from '../../../atoms/Icon'; 6 | import Checkbox from '../../../molecules/Checkbox'; 7 | 8 | // Styles 9 | import './index.scss'; 10 | 11 | const TransferElement = forwardRef( 12 | ({ disabled, hovered, dragged, checked, label, onClick, readOnly, minimalistic, indeterminate }, ref) => ( 13 | <div 14 | ref={ref} 15 | onClick={onClick} 16 | className={classnames('bc-transfer-element', 'crs-external-hover', 'crs-external-active', { 17 | minimalistic, 18 | disabled, 19 | hovered, 20 | dragged, 21 | readOnly 22 | })} 23 | > 24 | <Checkbox indeterminate={indeterminate} checked={checked} label={label} /> 25 | {!readOnly && !minimalistic && <Icon type="bc-icon-drag" />} 26 | </div> 27 | ) 28 | ); 29 | 30 | export default TransferElement; 31 | -------------------------------------------------------------------------------- /src/lib/organisms/TransferList/constants.js: -------------------------------------------------------------------------------- 1 | export const DEFAULT_TRANSLATE_MESSAGE = { 2 | empty: 'No data', 3 | dropHere: 'Drop Element Here', 4 | multiSelectText: 'Elements', 5 | moveTo: 'Move to' 6 | }; 7 | 8 | export const DEFAULT_DATA_ERROR_MESSAGE = 9 | 'TransferList: Be sure to send the defaultDate according to the documentation standard'; 10 | -------------------------------------------------------------------------------- /src/lib/organisms/TransferList/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .bc-transfer-list { 4 | --transfer-container-border-color: #{sc(b, 0.1)}; 5 | --transfer-container-content-height: #{rem(334)}; 6 | 7 | width: 100%; 8 | display: flex; 9 | background: c(b); 10 | grid-template-columns: 1fr auto 1fr; 11 | box-shadow: 0 0 0 rem(1) var(--transfer-container-border-color); 12 | user-select: none; 13 | 14 | .scroll-holder { 15 | .scroll-content { 16 | z-index: inherit; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from 'react'; 2 | 3 | export interface IHTMLDivElementAttributes extends Omit<HTMLAttributes<HTMLDivElement>, 'onKeyDown'> {} 4 | -------------------------------------------------------------------------------- /src/utils/callAfterDelay.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | 3 | const callAfterDelay = (callBack, time) => { 4 | const timerId = setTimeout(() => { 5 | clearTimeout(timerId); 6 | ReactDOM.unstable_batchedUpdates(() => { 7 | callBack(); 8 | }); 9 | }, time); 10 | }; 11 | 12 | export default callAfterDelay; 13 | -------------------------------------------------------------------------------- /src/utils/checkTimeValidation.js: -------------------------------------------------------------------------------- 1 | const meridiemValues = ['am', 'pm']; 2 | 3 | export const checkTimeValidation = ({ hour, minute, second, meridiem }) => { 4 | const isHourValid = !hour.format || (meridiem ? hour.value < 12 : hour.value < 24); 5 | 6 | const isMinuteValid = !minute.format || (minute.value >= 0 && minute.value < 60); 7 | 8 | const isSecondValid = !second.format || (second.value >= 0 && second.value < 60); 9 | 10 | const isMeridiemValid = 11 | !meridiem || !meridiem.format || !meridiem.value || meridiemValues.includes(meridiem.value.toLowerCase()); 12 | 13 | return isHourValid && isMinuteValid && isSecondValid && isMeridiemValid; 14 | }; 15 | -------------------------------------------------------------------------------- /src/utils/configs/tableConfigs.js: -------------------------------------------------------------------------------- 1 | export const searchConfigs = { 2 | types: ['text', 'select'], 3 | typeEnum: { 4 | text: 'text', 5 | select: 'select' 6 | } 7 | }; 8 | 9 | export const resizeConfigs = { 10 | typeEnum: { 11 | resize: 'resize', 12 | autoSize: 'autoSize' 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/utils/copyToClipboard.js: -------------------------------------------------------------------------------- 1 | const copyToClipboard = (str) => { 2 | const el = document.createElement('textarea'); 3 | el.value = str; 4 | document.body.appendChild(el); 5 | el.select(); 6 | document.execCommand('copy'); 7 | document.body.removeChild(el); 8 | }; 9 | 10 | export default copyToClipboard; 11 | -------------------------------------------------------------------------------- /src/utils/dateFormatChecker.js: -------------------------------------------------------------------------------- 1 | const nonLettersRegex = /[\W_]+/g; 2 | 3 | const dateFormats = ['YY', 'YYYY', 'M', 'MM', 'MMM', 'MMMM', 'D', 'DD']; 4 | const timeFormats = ['H', 'HH', 'h', 'hh', 'm', 'mm', 's', 'ss']; 5 | 6 | const getFormatSeparator = (format) => format[format.search(nonLettersRegex)]; 7 | 8 | export const checkFormat = (format) => { 9 | const [dateFormatOnly, timeFormatOnly] = format.split(' '); 10 | 11 | const dateFormatSeparator = getFormatSeparator(dateFormatOnly); 12 | const timeFormatSeparator = timeFormatOnly && getFormatSeparator(timeFormatOnly); 13 | const isDateFormatValid = dateFormatOnly.split(dateFormatSeparator).every((part) => dateFormats.includes(part)); 14 | const isTimeFormatValid = 15 | !timeFormatOnly || timeFormatOnly.split(timeFormatSeparator).every((part) => timeFormats.includes(part)); 16 | 17 | return isDateFormatValid && isTimeFormatValid; 18 | }; 19 | -------------------------------------------------------------------------------- /src/utils/debounce.js: -------------------------------------------------------------------------------- 1 | export default function debounce(callback, wait, immediate) { 2 | let timeout; 3 | 4 | return function func() { 5 | const context = this; 6 | const args = arguments; 7 | 8 | const later = function () { 9 | timeout = null; 10 | if (!immediate) callback.apply(context, args); 11 | }; 12 | 13 | const callNow = immediate && !timeout; 14 | 15 | clearTimeout(timeout); 16 | 17 | timeout = setTimeout(later, wait); 18 | 19 | if (callNow) callback.apply(context, args); 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/guid.js: -------------------------------------------------------------------------------- 1 | /** UUID generator 2 | * Function provides "unique" id. 3 | * Must be used with React iterable components. 4 | */ 5 | 6 | const hash = () => 7 | Math.floor((1 + Math.random()) * 0x10000) 8 | .toString(16) 9 | .substring(1); 10 | 11 | /** @function guid 12 | * @description 13 | * The function generates uuid. It can be used as `key`-s needed for 14 | * React iterated components 15 | */ 16 | 17 | const guid = () => `${hash() + hash()}-${hash()}-${hash()}-${hash()}-${hash()}${hash()}${hash()}`; 18 | 19 | export default guid; 20 | -------------------------------------------------------------------------------- /src/utils/indexof.js: -------------------------------------------------------------------------------- 1 | // this file existance is 2 | // related with rc-slider package issue 3 | 4 | export default function (arr, obj) { 5 | if (arr.indexOf) return arr.indexOf(obj); 6 | for (let i = 0; i < arr.length; ++i) { 7 | if (arr[i] === obj) return i; 8 | } 9 | return -1; 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/logger.js: -------------------------------------------------------------------------------- 1 | class Logger { 2 | success(message) { 3 | console.log(`%c${message}`, 'color: green'); 4 | } 5 | 6 | warning(message) { 7 | console.warn(message); 8 | } 9 | 10 | error(message) { 11 | console.error(message); 12 | } 13 | 14 | deprecate(message) { 15 | console.warn(`DEPRECATION WARNING(gene-ui): ${message}`); 16 | } 17 | } 18 | 19 | export default new Logger(); 20 | -------------------------------------------------------------------------------- /src/wrappers/dayjsWithPlugins.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Setup dayjs plugin. 3 | */ 4 | import dayjs from 'dayjs'; 5 | import customParseFormat from 'dayjs/plugin/customParseFormat'; 6 | import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; 7 | import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; 8 | import isLeapYear from 'dayjs/plugin/isLeapYear'; 9 | import isBetween from 'dayjs/plugin/isBetween'; 10 | 11 | dayjs.extend(customParseFormat); 12 | dayjs.extend(isSameOrBefore); 13 | dayjs.extend(isSameOrAfter); 14 | dayjs.extend(isLeapYear); 15 | dayjs.extend(isBetween); 16 | 17 | export const dayjsWithPlugins = dayjs; 18 | -------------------------------------------------------------------------------- /src/wrappers/index.js: -------------------------------------------------------------------------------- 1 | export * from './dayjsWithPlugins'; 2 | -------------------------------------------------------------------------------- /stories/atoms/Icon/Icon.stories.scss: -------------------------------------------------------------------------------- 1 | #docs-root { 2 | .iconsSearch { 3 | display: none; 4 | } 5 | } 6 | 7 | .iconsSearch { 8 | position: fixed; 9 | z-index: 10; 10 | top: 0; 11 | left: 0; 12 | background: rgba(var(--background-rgb), 1); 13 | width: 100%; 14 | padding: 10px 15px; 15 | } 16 | 17 | .iconsWrapper { 18 | display: flex; 19 | flex-wrap: wrap; 20 | margin-top: 50px; 21 | } 22 | 23 | .iconButton { 24 | width: calc(25% - 10px); 25 | display: flex; 26 | align-items: center; 27 | padding: 10px; 28 | margin: 5px; 29 | cursor: pointer; 30 | border-radius: var(--button-external-border-radius, 0.4rem); 31 | background: rgba(var(--background-rgb), 0.8); 32 | border-left: 3px solid transparent; 33 | 34 | &:hover { 35 | border-left: 3px solid #e90789; 36 | } 37 | 38 | &_name { 39 | max-width: 100%; 40 | } 41 | 42 | &_svg { 43 | margin-right: 5px; 44 | } 45 | } 46 | 47 | .noData_empty { 48 | position: relative; 49 | left: 50%; 50 | transform: translateX(-50%); 51 | } 52 | 53 | .icons_loader { 54 | margin-top: 80px; 55 | } 56 | 57 | .icon_loader { 58 | align-items: flex-start; 59 | } 60 | -------------------------------------------------------------------------------- /stories/atoms/Label/LabelValue.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import LabelComponent from 'src/lib/atoms/Label/index'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Atoms/Label', 8 | component: LabelComponent, 9 | argTypes: { 10 | font: args({ control: 'select', category: category.appearance }), 11 | children: args({ control: 'text', category: category.content }), 12 | size: args({ control: 'select', category: category.appearance }), 13 | className: args({ control: false, category: category.others }) 14 | } 15 | }; 16 | 17 | const Template = ({ children, ...args }) => <LabelComponent {...args}>{children}</LabelComponent>; 18 | 19 | export const Label = Template.bind({}); 20 | Label.args = { 21 | size: 'content', 22 | font: 'semiBold', 23 | children: 'Some Label' 24 | }; 25 | -------------------------------------------------------------------------------- /stories/atoms/ModuleTitle/data.js: -------------------------------------------------------------------------------- 1 | export const data = [ 2 | { 3 | title: 'Title 1', 4 | slug: 'title1' 5 | }, 6 | { 7 | title: 'Title 2', 8 | slug: 'title2' 9 | }, 10 | { 11 | title: 'Title 3', 12 | slug: 'title3' 13 | }, 14 | { 15 | title: 'Title 4', 16 | slug: 'title4' 17 | }, 18 | { 19 | title: 'Title 5', 20 | slug: 'title5' 21 | }, 22 | { 23 | title: 'Title 6', 24 | slug: 'title6' 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /stories/atoms/Paper/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .fake-paper-placeholder { 4 | margin: rem(10); 5 | border-radius: rem(4); 6 | border: rem(1) solid sc(b, 0.1); 7 | font: 600 rem(14) / rem(46) $f; 8 | padding: 0 rem(36); 9 | } 10 | -------------------------------------------------------------------------------- /stories/atoms/QRCode/QRCode.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import QRCodeComponent from 'src/lib/atoms/QRCode'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | const QRConfig = { 7 | level: ['L', 'M', 'Q', 'H'] 8 | }; 9 | export default { 10 | title: 'Atoms/QRCode', 11 | component: QRCodeComponent, 12 | darkMode: { 13 | stylePreview: true 14 | }, 15 | // src, icon, size, color, shape, onClick, children 16 | argTypes: { 17 | value: args({ control: 'text', category: category.content }), 18 | maxSize: args({ control: 'number', category: category.appearance }), 19 | level: args({ control: 'select', category: category.appearance }) 20 | }, 21 | args: { 22 | value: 'https://en.wikipedia.org/wiki/QR_code', 23 | maxSize: 400, 24 | level: QRConfig.level[0] 25 | } 26 | }; 27 | 28 | export let QRCode = ({ ...args }) => <QRCodeComponent {...args} />; 29 | -------------------------------------------------------------------------------- /stories/atoms/Skeleton/index.scss: -------------------------------------------------------------------------------- 1 | .skeleton-story-wrapper { 2 | background: var(--background); 3 | padding: 20px; 4 | } 5 | -------------------------------------------------------------------------------- /stories/atoms/TextLink/TextLink.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TextLinkComponent from 'src/lib/atoms/TextLink/index'; 4 | import { args, category, componentStage } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Atoms/TextLink-d', 8 | component: TextLinkComponent, 9 | argTypes: { 10 | className: args({ control: false, category: category.others }), 11 | children: args({ control: 'text', category: category.content }) 12 | }, 13 | args: { 14 | componentStage: { 15 | type: componentStage.deprecated 16 | } 17 | } 18 | }; 19 | 20 | const Template = ({ children, ...args }) => <TextLinkComponent {...args}>{children}</TextLinkComponent>; 21 | 22 | export const TextLink = Template.bind({}); 23 | TextLink.args = { 24 | children: 'Some link' 25 | }; 26 | -------------------------------------------------------------------------------- /stories/atoms/Time/Time.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TimeComponent from 'src/lib/atoms/Time/index'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Atoms/Time', 8 | component: TimeComponent, 9 | argTypes: { 10 | showIcon: args({ control: 'boolean', category: category.content }), 11 | format: args({ control: 'text', category: category.functionality }), 12 | startDate: args({ control: false, category: category.functionality }), 13 | showSeconds: args({ control: 'boolean', category: category.functionality }) 14 | }, 15 | args: { 16 | showIcon: true 17 | } 18 | }; 19 | 20 | const Template = ({ ...args }) => <TimeComponent {...args} />; 21 | 22 | export const DateAndTime = Template.bind({}); 23 | DateAndTime.args = { 24 | format: 'DD/MM/YYYY HH:mm:ss', 25 | showSeconds: true 26 | }; 27 | DateAndTime.argTypes = { 28 | showSeconds: args({ 29 | table: { 30 | disable: true 31 | } 32 | }) 33 | }; 34 | export const TimeWithFormat = Template.bind({}); 35 | TimeWithFormat.args = { 36 | showSeconds: false 37 | }; 38 | -------------------------------------------------------------------------------- /stories/atoms/Title/Title.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TitleComponent from 'src/lib/atoms/Title/index'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | import { titleConfig } from 'configs'; 7 | 8 | export default { 9 | title: 'Atoms/Title', 10 | component: TitleComponent, 11 | argTypes: { 12 | text: args({ control: 'text', category: category.content }), 13 | icon: args({ control: 'text', category: category.content }), 14 | actions: args({ control: 'text', category: category.content }), 15 | color: args({ 16 | control: 'select', 17 | defaultValue: titleConfig.color[0], 18 | options: titleConfig.color, 19 | category: category.appearance 20 | }), 21 | withLine: args({ control: 'boolean', category: category.appearance }), 22 | className: args({ control: false, category: category.others }) 23 | }, 24 | args: { 25 | actions: 'Text' 26 | } 27 | }; 28 | 29 | const Template = ({ text, ...args }) => <TitleComponent text={text} {...args}></TitleComponent>; 30 | 31 | export const Title = Template.bind({}); 32 | Title.args = { 33 | text: 'Some Text', 34 | icon: 'bc-icon-align-center', 35 | color: titleConfig.color[0], 36 | withLine: true 37 | }; 38 | -------------------------------------------------------------------------------- /stories/changelog.mdx: -------------------------------------------------------------------------------- 1 | import { Markdown , Meta} from '@storybook/blocks'; 2 | import ChangelogMD from '../CHANGELOG.md'; 3 | 4 | <Meta title="Changelog" /> 5 | 6 | <div style={{display: "flex", "justify-content": "center"}}> 7 | <Markdown>{ChangelogMD}</Markdown> 8 | </div> 9 | -------------------------------------------------------------------------------- /stories/gettingStarted.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | <Meta title="Getting started"/> 4 | 5 | <div style={{ paddingInlineStart:'20px' }}> 6 | # Getting started with <b style={{color: '#E90789'}}>Gene UI</b> components package 7 | 8 | <br /> 9 | <br /> 10 | <br /> 11 | ## Package installation 12 | <p> 13 | Go to your project directory and run one of the following commands in your terminal 14 | </p> 15 | <div style={{ width: '70%' }}> 16 | <Source code={`npm install @geneui/components --save`} language="bash" /> 17 | </div> 18 | If your favorite package manager is `Yarn` 19 | <div style={{ width: '70%' }}> 20 | <Source code={`yarn add @geneui/components`} language="bash" /> 21 | </div> 22 | 23 | <br /> 24 | ## Usage 25 | 26 | <p> 27 | At first you need to import the provider in your main component e.g. in the `App.js` file. 28 | </p> 29 | <div style={{ width: '70%' }}> 30 | <Source code={`import GeneUIProvider from '@geneui/components/GeneUIProvider';`} language="js" /> 31 | </div> 32 | Then you need to wrap your main component with provider 33 | <div style={{ width: '70%' }}> 34 | <Source 35 | code={`<GeneUIProvider> 36 | <App /> 37 | </GeneUIProvider>`} 38 | language="jsx" 39 | /> 40 | </div> 41 | <p> 42 | Now you can start use any components available in the package. Happy coding. 43 | </p> 44 | </div> 45 | -------------------------------------------------------------------------------- /stories/index.stories.js: -------------------------------------------------------------------------------- 1 | import './atoms'; 2 | import './molecules'; 3 | import './organisms'; 4 | -------------------------------------------------------------------------------- /stories/molecules/Breadcrumb/Breadcrumb.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BreadcrumbComponent from 'src/lib/molecules/Breadcrumb'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | import { data } from './data'; 6 | 7 | export default { 8 | title: 'Molecules/Breadcrumb', 9 | component: BreadcrumbComponent, 10 | argTypes: { 11 | data: args({ control: 'object', category: category.content }), 12 | className: args({ control: false, category: category.others }), 13 | onClick: args({ action: 'onClick', category: category.action }), 14 | separator: args({ control: 'text', category: category.content }), 15 | collapsed: args({ control: 'boolean', category: category.states }) 16 | }, 17 | args: { 18 | data: data, 19 | separator: '>', 20 | collapsed: true 21 | } 22 | }; 23 | 24 | export const Breadcrumb = ({ ...args }) => <BreadcrumbComponent {...args} />; 25 | -------------------------------------------------------------------------------- /stories/molecules/Breadcrumb/data.js: -------------------------------------------------------------------------------- 1 | export const data = [ 2 | { 3 | title: 'Title 1', 4 | slug: 'title1' 5 | }, 6 | { 7 | title: 'Title 2', 8 | slug: 'title2' 9 | }, 10 | { 11 | title: 'Title 3', 12 | slug: 'title3' 13 | }, 14 | { 15 | title: 'Title 4', 16 | slug: 'title4' 17 | }, 18 | { 19 | title: 'Title 5', 20 | slug: 'title5' 21 | }, 22 | { 23 | title: 'Title 6', 24 | slug: 'title6' 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /stories/molecules/ComboBox/data.js: -------------------------------------------------------------------------------- 1 | import { faker } from '@faker-js/faker'; 2 | 3 | export const emails = []; 4 | 5 | export function createEmails() { 6 | return { 7 | label: faker.internet.email() 8 | }; 9 | } 10 | 11 | Array.from({ length: 5 }).forEach(() => { 12 | emails.push(createEmails()); 13 | }); 14 | 15 | export default emails; 16 | -------------------------------------------------------------------------------- /stories/molecules/Grid/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .grid-item { 4 | text-align: center; 5 | font: rem(10) / rem(14) $f; 6 | text-transform: uppercase; 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | height: rem(100); 11 | border-radius: rem(20); 12 | color: c(hero); 13 | background: c(hero, 0.24); 14 | overflow: hidden; 15 | width: 100%; 16 | } 17 | -------------------------------------------------------------------------------- /stories/molecules/Holder/index.scss: -------------------------------------------------------------------------------- 1 | .holder-left { 2 | height: 500px; 3 | } 4 | -------------------------------------------------------------------------------- /stories/molecules/Menu/Menu.stories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import MenuComponent from 'src/lib/molecules/Menu'; 4 | import { data } from './data'; 5 | import { screenTypes } from '../../../src/configs'; 6 | import { args, category } from '../../assets/storybook.globals'; 7 | 8 | export default { 9 | title: 'Molecules/Menu', 10 | component: MenuComponent, 11 | argTypes: { 12 | onNext: args({ control: false, category: category.action }), 13 | onSelect: args({ control: false, category: category.action }), 14 | data: args({ control: 'object', category: category.content }), 15 | onBack: args({ control: false, action: 'onBack', category: category.action }), 16 | scrollToActiveElement: args({ control: 'boolean', category: category.functionality }), 17 | screenType: args({ control: 'select', options: screenTypes, category: category.appearance }) 18 | }, 19 | args: { 20 | data, 21 | scrollToActiveElement: true, 22 | screenType: args({ control: 'select', options: screenTypes, category: category.appearance }), 23 | initialIndexStack: [] 24 | } 25 | }; 26 | 27 | export const Menu = ({ ...args }) => { 28 | return <MenuComponent {...args} />; 29 | }; 30 | -------------------------------------------------------------------------------- /stories/molecules/MobileNavigation/MobileNavigation.stories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import MobileNavigationComponent from 'src/lib/molecules/MobileNavigation'; 4 | 5 | import { args, category } from '../../assets/storybook.globals'; 6 | import { list } from './data'; 7 | 8 | export default { 9 | title: 'Molecules/MobileNavigation', 10 | component: MobileNavigationComponent, 11 | argTypes: { 12 | list: args({ control: 'object', category: category.content }), 13 | onChange: args({ control: false, category: category.action }), 14 | className: args({ control: false, category: category.others }), 15 | activeSlug: args({ control: 'text', category: category.content }) 16 | }, 17 | args: { 18 | activeSlug: list[1].slug, 19 | list: list 20 | } 21 | }; 22 | 23 | export let MobileNavigation = ({ ...args }) => { 24 | const [selectedSlug, setSelectedSlug] = useState(args.activeSlug); 25 | return ( 26 | <MobileNavigationComponent 27 | {...args} 28 | onChange={(e) => { 29 | setSelectedSlug(e.slug); 30 | }} 31 | activeSlug={selectedSlug} 32 | /> 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /stories/molecules/MobileNavigation/data.js: -------------------------------------------------------------------------------- 1 | export const list = [ 2 | { 3 | title: 'Filters', 4 | slug: 'filters', 5 | icon: 'bc-icon-apps', 6 | disabled: true 7 | }, 8 | { 9 | title: 'Products', 10 | slug: 'products', 11 | icon: 'bc-icon-apps', 12 | badge: { 13 | count: 30, 14 | maxCount: 20, 15 | position: 'right' 16 | } 17 | }, 18 | { 19 | title: 'Profile', 20 | slug: 'profile', 21 | icon: 'bc-icon-apps' 22 | } 23 | ]; 24 | -------------------------------------------------------------------------------- /stories/molecules/MobilePopup/MobilePopup.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MobilePopupComponent from 'src/lib/molecules/MobilePopup'; 4 | import { leftAction, rightAction } from './data'; 5 | import { args, category } from '../../assets/storybook.globals'; 6 | 7 | export default { 8 | title: 'Molecules/MobilePopup', 9 | component: MobilePopupComponent, 10 | argTypes: { 11 | title: args({ control: 'text', category: category.content }), 12 | children: args({ control: 'text', category: category.content }), 13 | className: args({ control: false, category: category.content }), 14 | isOpened: args({ control: 'boolean', category: category.states }), 15 | portalContainer: args({ control: false, category: category.content }), 16 | rightAction: args({ control: 'object', category: category.functionality }), 17 | leftAction: args({ control: 'object', action: 'leftAction', category: category.functionality }), 18 | leftActionClick: args({ control: false, action: 'leftActionClick', category: category.action }), 19 | onBackdropClick: args({ control: false, action: 'onBackdropClick', category: category.action }), 20 | rightActionClick: args({ control: false, action: 'rightActionClick', category: category.action }) 21 | }, 22 | args: { 23 | leftAction, 24 | rightAction, 25 | title: 'Title', 26 | isOpened: true 27 | } 28 | }; 29 | 30 | export const MobilePopup = ({ ...args }) => <MobilePopupComponent {...args} />; 31 | -------------------------------------------------------------------------------- /stories/molecules/MobilePopup/data.js: -------------------------------------------------------------------------------- 1 | export const rightAction = { 2 | iconType: 'bc-icon-more-horizontal', 3 | menuOptions: [ 4 | { 5 | title: 'Menu 1', 6 | icon: 'bc-icon-report', 7 | color: 'hero' 8 | }, 9 | { 10 | title: 'Menu 2', 11 | icon: 'bc-icon-average-coefficient', 12 | color: 'hero' 13 | } 14 | ] 15 | }; 16 | 17 | export const leftAction = { 18 | iconType: 'bc-icon-arrow-back', 19 | text: 'Back' 20 | }; 21 | -------------------------------------------------------------------------------- /stories/molecules/Pagination/Pagination.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import PaginationComponent from 'src/lib/molecules/Pagination'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Molecules/Pagination', 8 | component: PaginationComponent, 9 | argTypes: { 10 | onChange: args({ control: false, category: category.action }), 11 | count: args({ control: 'number', category: category.states }), 12 | errorText: args({ control: 'text', category: category.content }), 13 | selected: args({ control: 'number', category: category.states }), 14 | autoFocus: args({ control: 'boolean', category: category.states }), 15 | defaultSelected: args({ control: 'number', category: category.states }), 16 | supportedKeyCodes: args({ control: 'number', category: category.states }), 17 | nextIconTooltipText: args({ control: 'text', category: category.content }), 18 | previousIconTooltipText: args({ control: 'text', category: category.content }) 19 | }, 20 | args: { 21 | count: 50, 22 | autoFocus: false, 23 | defaultSelected: 1 24 | } 25 | }; 26 | 27 | export const Pagination = ({ ...args }) => <PaginationComponent {...args} />; 28 | -------------------------------------------------------------------------------- /stories/molecules/Products/Products.stories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import ProductsComponent from 'src/lib/molecules/Products'; 4 | 5 | const screenTypes = ['desktop', 'mobile']; 6 | import { list, favoritesList } from './data'; 7 | import { args, category } from '../../assets/storybook.globals'; 8 | 9 | export default { 10 | title: 'Molecules/Products', 11 | component: ProductsComponent, 12 | argTypes: { 13 | onClick: args({ control: false, category: category.action }), 14 | list: args({ control: 'array', category: category.content }), 15 | className: args({ control: false, category: category.others }), 16 | activeSlug: args({ control: false, category: category.states }), 17 | favorites: args({ control: 'object', category: category.content }), 18 | screenType: args({ control: false, options: screenTypes, category: category.states }) 19 | }, 20 | args: { 21 | list, 22 | favorites: favoritesList 23 | } 24 | }; 25 | 26 | export const Products = ({ ...args }) => { 27 | const [activeSlug, setActiveSlug] = useState(list[0].slug); 28 | 29 | const onChange = (item) => { 30 | setActiveSlug(item.slug); 31 | }; 32 | return <ProductsComponent activeSlug={activeSlug} onChange={onChange} {...args} />; 33 | }; 34 | -------------------------------------------------------------------------------- /stories/molecules/Products/data.js: -------------------------------------------------------------------------------- 1 | export const list = Array(12) 2 | .fill({}) 3 | .map((_, i) => ({ 4 | title: `Product ${i}`, 5 | slug: `product-${i}`, 6 | icon: 'bc-icon-apps' 7 | })); 8 | 9 | export const favoritesList = Array(3) 10 | .fill({}) 11 | .map((_, i) => ({ 12 | title: `Product ${i}`, 13 | slug: `product-${i}`, 14 | icon: 'bc-icon-apps' 15 | })); 16 | -------------------------------------------------------------------------------- /stories/molecules/RadioGroup/RadioGroup.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RadioGroupComponent from 'src/lib/molecules/RadioGroup'; 3 | import { optionsWithDisabled } from './data'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | const type = ['default', 'tab']; 7 | 8 | export default { 9 | title: 'Molecules/RadioGroup', 10 | component: RadioGroupComponent, 11 | argTypes: { 12 | name: args({ control: 'text', category: category.content }), 13 | value: args({ control: 'object', category: category.content }), 14 | options: args({ control: 'object', category: category.content }), 15 | required: args({ control: 'boolean', category: category.states }), 16 | disabled: args({ control: 'boolean', category: category.states }), 17 | descriptionKey: args({ control: 'text', category: category.content }), 18 | defaultValue: args({ control: 'number', category: category.content }), 19 | type: args({ control: 'select', options: type, category: category.appearance }), 20 | onChange: args({ control: 'object', action: 'onChange', category: category.action }) 21 | }, 22 | args: { 23 | type: type[0], 24 | defaultValue: 1, 25 | disabled: false, 26 | descriptionKey: 'info', 27 | options: optionsWithDisabled 28 | } 29 | }; 30 | 31 | export const RadioGroup = ({ ...args }) => <RadioGroupComponent {...args} />; 32 | -------------------------------------------------------------------------------- /stories/molecules/RadioGroup/data.js: -------------------------------------------------------------------------------- 1 | export const optionsWithDisabled = [ 2 | { label: 'Option 1', value: 1, info: 'description1' }, 3 | { label: 'Option 2', value: 2, info: 'description2' }, 4 | { label: 'Option 3', value: 3, disabled: true, info: 'description3' } 5 | ]; 6 | -------------------------------------------------------------------------------- /stories/molecules/Search/Search.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SearchComponent from 'src/lib/molecules/Search'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Molecules/Search', 8 | component: SearchComponent, 9 | argTypes: { 10 | onChange: args({ control: false, category: category.action }), 11 | className: args({ control: false, category: category.others }), 12 | canClear: args({ control: 'boolean', category: category.states }), 13 | placeholder: args({ control: 'text', category: category.content }), 14 | defaultValue: args({ control: 'text', category: category.content }) 15 | }, 16 | args: { 17 | canClear: false, 18 | defaultValue: 'search', 19 | placeholder: 'Some placeholder' 20 | } 21 | }; 22 | 23 | export const Search = ({ ...args }) => <SearchComponent {...args} />; 24 | -------------------------------------------------------------------------------- /stories/molecules/Status/Status.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import StatusComponent, { statusIconTypes } from 'src/lib/molecules/Status'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | export default { 7 | title: 'Molecules/Status', 8 | component: StatusComponent, 9 | argTypes: { 10 | title: args({ control: 'text', category: category.content }), 11 | label: args({ control: 'text', category: category.content }), 12 | onClick: args({ control: 'text', category: category.action }), 13 | className: args({ control: false, category: category.others }), 14 | color: args({ control: 'color', category: category.appearance }), 15 | hoverTitle: args({ control: 'text', category: category.content }), 16 | tooltipText: args({ control: 'text', category: category.content }), 17 | iconType: args({ control: 'select', options: statusIconTypes, category: category.appearance }) 18 | }, 19 | args: { 20 | label: '', 21 | color: '', 22 | title: 'In Progress', 23 | hoverTitle: 'Hover Title', 24 | tooltipText: 'Some tooltip', 25 | iconType: statusIconTypes[0] 26 | } 27 | }; 28 | 29 | export const Status = ({ ...args }) => <StatusComponent {...args} />; 30 | -------------------------------------------------------------------------------- /stories/molecules/Tabs/index.scss: -------------------------------------------------------------------------------- 1 | @import 'src/assets/styles/variables'; 2 | 3 | .tabs-content-holder { 4 | min-height: rem(250); 5 | } 6 | -------------------------------------------------------------------------------- /stories/molecules/Textarea/data.js: -------------------------------------------------------------------------------- 1 | export const suggestionData = [ 2 | { 3 | key: '@', 4 | data: [ 5 | { 6 | label: 'test1', 7 | value: 'test1' 8 | }, 9 | { 10 | label: 'test2', 11 | value: 'test2' 12 | }, 13 | { 14 | label: 'test3', 15 | value: 'test3' 16 | } 17 | ] 18 | }, 19 | { 20 | key: ':', 21 | data: [ 22 | { 23 | label: 'test1', 24 | value: 'test1' 25 | } 26 | ] 27 | } 28 | ]; 29 | -------------------------------------------------------------------------------- /stories/molecules/Timeline/Timeline.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | Timeline as TimelineComponent, 5 | TimelineItem, 6 | timelineColors, 7 | timelineAppearances 8 | } from 'src/lib/molecules/Timeline/index'; 9 | import { args, category } from '../../assets/storybook.globals'; 10 | 11 | export default { 12 | title: 'Molecules/Timeline', 13 | component: TimelineComponent, 14 | argTypes: { 15 | title: args({ control: 'text', category: category.content }), 16 | className: args({ control: false, category: category.others }), 17 | children: args({ control: 'text', category: category.content }), 18 | description: args({ control: 'text', category: category.content }), 19 | isLoading: args({ control: 'boolean', category: category.states }), 20 | color: args({ control: 'select', options: timelineColors, category: category.appearance }), 21 | appearance: args({ control: 'select', options: timelineAppearances, category: category.appearance }) 22 | }, 23 | args: { 24 | isLoading: false, 25 | title: 'Some title', 26 | color: timelineColors[0], 27 | description: 'Some description', 28 | appearance: timelineAppearances[0] 29 | } 30 | }; 31 | 32 | export const Timeline = ({ ...args }) => { 33 | return ( 34 | <TimelineComponent> 35 | <TimelineItem {...args} /> 36 | <TimelineItem {...args} /> 37 | </TimelineComponent> 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /stories/organisms/DateFilter/style.scss: -------------------------------------------------------------------------------- 1 | .dateFilterWrapper { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /stories/organisms/Drawer/data.js: -------------------------------------------------------------------------------- 1 | export const menuData = [ 2 | { 3 | icon: 'bc-icon-crm-standart-report', 4 | id: 'item-1', 5 | title: 'Menu Item 1' 6 | }, 7 | { 8 | id: 'item-2', 9 | icon: 'bc-icon-test-48', 10 | title: 'Menu Item 2', 11 | disabled: true 12 | }, 13 | { 14 | id: 'item-3', 15 | icon: 'bc-icon-sent-outline', 16 | title: 'Menu Item 3' 17 | }, 18 | { 19 | id: 'item-4', 20 | icon: 'bc-icon-list-border', 21 | title: 'Menu Item 4', 22 | nested: [ 23 | { 24 | id: 'section-1', 25 | title: 'Section 1' 26 | }, 27 | { 28 | id: 'section-2', 29 | title: 'Section 2' 30 | } 31 | ] 32 | }, 33 | { 34 | id: 'item-5', 35 | icon: 'bc-icon-pages', 36 | title: 'Menu Item 5', 37 | nested: [ 38 | { 39 | id: 'section-3', 40 | title: 'Section 3' 41 | }, 42 | { 43 | id: 'section-4', 44 | title: 'Section 4' 45 | } 46 | ] 47 | } 48 | ]; 49 | -------------------------------------------------------------------------------- /stories/organisms/Form/style.scss: -------------------------------------------------------------------------------- 1 | .sb_form { 2 | background: var(--background); 3 | padding: 10px; 4 | } 5 | -------------------------------------------------------------------------------- /stories/organisms/SearchWithDropdown/SearchWithDropdown.stories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SearchWithDropdownComponent from 'src/lib/organisms/SearchWithDropdown'; 4 | import { args, category } from '../../assets/storybook.globals'; 5 | 6 | const data = [ 7 | { label: 'Option 1', value: 1 }, 8 | { label: 'Option 2', value: 2 }, 9 | { label: 'Option 3', value: 3 } 10 | ]; 11 | 12 | export default { 13 | title: 'Organisms/SearchWithDropdown', 14 | component: SearchWithDropdownComponent, 15 | argTypes: { 16 | onChange: args({ control: false, category: category.action }), 17 | dataKey: args({ control: 'text', category: category.others }), 18 | searchProps: args({ control: 'object', category: category.others }), 19 | dropdownProps: args({ control: 'object', category: category.others }), 20 | flexibility: args({ control: 'select', category: category.appearance }), 21 | hideDropdown: args({ control: 'boolean', category: category.functionality }) 22 | }, 23 | args: { 24 | hideDropdown: false 25 | } 26 | }; 27 | 28 | export const SearchWithDropdown = ({ ...args }) => ( 29 | <SearchWithDropdownComponent 30 | {...args} 31 | dropdownProps={{ 32 | data, 33 | checkAllText: 'All', 34 | isMultiSelect: true 35 | }} 36 | /> 37 | ); 38 | 39 | SearchWithDropdown.args = {}; 40 | -------------------------------------------------------------------------------- /stories/utils/ChartAnimation.js: -------------------------------------------------------------------------------- 1 | const easeOutBounce = (pos) => { 2 | if (pos < 1 / 2.75) { 3 | return 7.5625 * pos * pos; 4 | } 5 | if (pos < 2 / 2.75) { 6 | return 7.5625 * (pos -= 1.5 / 2.75) * pos + 0.75; 7 | } 8 | if (pos < 2.5 / 2.75) { 9 | return 7.5625 * (pos -= 2.25 / 2.75) * pos + 0.9375; 10 | } 11 | return 7.5625 * (pos -= 2.625 / 2.75) * pos + 0.984375; 12 | }; 13 | 14 | Math.easeOutBounce = easeOutBounce; 15 | -------------------------------------------------------------------------------- /stories/utils/index.js: -------------------------------------------------------------------------------- 1 | export ChartAnimation from './ChartAnimation'; 2 | export * from './propTables'; 3 | -------------------------------------------------------------------------------- /stories/utils/knobs.js: -------------------------------------------------------------------------------- 1 | import { date } from '@storybook/addon-knobs'; 2 | 3 | function dateKnob(name, defaultValue) { 4 | const stringTimestamp = date(name, defaultValue); 5 | return new Date(stringTimestamp); 6 | } 7 | 8 | export { dateKnob }; 9 | -------------------------------------------------------------------------------- /stories/utils/propTables/DateMonthPickerProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import dayjs from 'dayjs'; 4 | 5 | const DateMonthPickerProp = (props) => <div></div>; 6 | 7 | DateMonthPickerProp.name = 'DateMonthPicker'; 8 | 9 | DateMonthPickerProp.propTypes = { 10 | /** 11 | * Fires event when user changes datepicker value 12 | * ([startDate: date endDate: date]) => void 13 | */ 14 | onChange: PropTypes.func, 15 | /** 16 | * Initial value for start date 17 | */ 18 | rangeStartDefault: PropTypes.oneOfType([ 19 | PropTypes.instanceOf(dayjs), 20 | PropTypes.instanceOf(Date), 21 | PropTypes.string, 22 | PropTypes.number 23 | ]), 24 | /** 25 | * Initial value for end date 26 | */ 27 | rangeEndDefault: PropTypes.oneOfType([ 28 | PropTypes.instanceOf(dayjs), 29 | PropTypes.instanceOf(Date), 30 | PropTypes.string, 31 | PropTypes.number 32 | ]), 33 | /** 34 | * Additional classname which will apply to datepicker holder ul element 35 | */ 36 | className: PropTypes.string, 37 | /** 38 | * Custom text for this month button inside footer. 39 | * Default value is `This Month` 40 | */ 41 | thisMonthText: PropTypes.string 42 | }; 43 | 44 | export default DateMonthPickerProp; 45 | -------------------------------------------------------------------------------- /stories/utils/propTables/DatePickerProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import dayjs from 'dayjs'; 4 | 5 | const DatePickerProp = (props) => <div></div>; 6 | 7 | DatePickerProp.propTypes = { 8 | /** 9 | * Initial datepicker value. 10 | */ 11 | defaultValue: PropTypes.oneOfType([ 12 | PropTypes.instanceOf(dayjs), 13 | PropTypes.instanceOf(Date), 14 | PropTypes.string, 15 | PropTypes.number 16 | ]), 17 | /** 18 | * Initial value for datepicker preview 19 | */ 20 | defaultPreview: PropTypes.oneOfType([ 21 | PropTypes.instanceOf(dayjs), 22 | PropTypes.instanceOf(Date), 23 | PropTypes.string, 24 | PropTypes.number 25 | ]), 26 | /** 27 | * Max specifies the maximum value allowed for datepicker 28 | */ 29 | max: PropTypes.instanceOf(dayjs), 30 | /** 31 | * Min specifies the minimum value allowed for datepicker 32 | */ 33 | min: PropTypes.instanceOf(dayjs), 34 | /** 35 | * Fires event when user changes date[icker value 36 | * (date: Date) => void 37 | */ 38 | onChange: PropTypes.func, 39 | /** 40 | * Additional classname which will apply to datepicker holder ul element 41 | */ 42 | className: PropTypes.string, 43 | /** 44 | * Custom text for today button inside footer. 45 | * Default value is `Today` 46 | */ 47 | todayText: PropTypes.string 48 | }; 49 | 50 | export default DatePickerProp; 51 | -------------------------------------------------------------------------------- /stories/utils/propTables/DateWeekPickerProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import dayjs from 'dayjs'; 4 | 5 | const DateWeekPickerProp = (props) => <div></div>; 6 | 7 | DateWeekPickerProp.propTypes = { 8 | /** 9 | * Fires event when user changes datepicker value 10 | * ([startDate: Date, endDate: Date]) => void 11 | */ 12 | onChange: PropTypes.func, 13 | /** 14 | * Initial value for start date 15 | */ 16 | rangeStartDefault: PropTypes.oneOfType([ 17 | PropTypes.instanceOf(dayjs), 18 | PropTypes.instanceOf(Date), 19 | PropTypes.string, 20 | PropTypes.number 21 | ]), 22 | /** 23 | * Initial value for end date 24 | */ 25 | rangeEndDefault: PropTypes.oneOfType([ 26 | PropTypes.instanceOf(dayjs), 27 | PropTypes.instanceOf(Date), 28 | PropTypes.string, 29 | PropTypes.number 30 | ]), 31 | /** 32 | * Additional classname which will apply to datepicker holder ul element 33 | */ 34 | className: PropTypes.string, 35 | /** 36 | * Custom text for this week button inside footer. 37 | * Default value is `This Week` 38 | */ 39 | thisWeekText: PropTypes.string 40 | }; 41 | 42 | export default DateWeekPickerProp; 43 | -------------------------------------------------------------------------------- /stories/utils/propTables/HeaderTableProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const HeaderTableProp = (props) => <div></div>; 5 | 6 | HeaderTableProp.propTypes = { 7 | /** 8 | * Define is search bar will shown or no 9 | */ 10 | withSearch: PropTypes.bool, 11 | /** 12 | * Any valid react node 13 | */ 14 | headerActions: PropTypes.node, 15 | /** 16 | * All props from Table component 17 | */ 18 | '': PropTypes.any 19 | }; 20 | 21 | export default HeaderTableProp; 22 | -------------------------------------------------------------------------------- /stories/utils/propTables/MenuProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const MenuProp = (props) => <div></div>; 5 | 6 | MenuProp.counter = 0; 7 | 8 | MenuProp.propTypes = { 9 | /** 10 | * Menu items data 11 | */ 12 | data: PropTypes.arrayOf( 13 | PropTypes.shape({ 14 | /** 15 | * Title for menu item 16 | */ 17 | title: PropTypes.string, 18 | /** 19 | * Divider between menu items 20 | */ 21 | divider: PropTypes.shape({ 22 | /** 23 | * Controls width of divider 24 | */ 25 | hasFullWidth: PropTypes.bool 26 | }), 27 | /** 28 | * Fires event when user click on one of the menu items; 29 | * (event: Event, item: Object) => void 30 | */ 31 | onClick: PropTypes.func, 32 | /** 33 | * Text value inside menu item 34 | */ 35 | description: PropTypes.string, 36 | /** 37 | * Children elements data for menu item 38 | */ 39 | children: PropTypes.any 40 | }) 41 | ), 42 | /** 43 | * Fires event when user click on header elements; 44 | * (event: Event, item: Object) => void 45 | */ 46 | onBack: PropTypes.func 47 | }; 48 | 49 | export default MenuProp; 50 | -------------------------------------------------------------------------------- /stories/utils/propTables/ScrollbarProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const CustomScrollbarProp = (props) => <div></div>; 5 | 6 | CustomScrollbarProp.propTypes = { 7 | /** 8 | * Any valid React node 9 | */ 10 | children: PropTypes.node.isRequired, 11 | /** 12 | * Enable auto-height mode. When true container grows with content. 13 | */ 14 | autoHeight: PropTypes.bool, 15 | /** 16 | * Set a minimum height for auto-height mode. 17 | */ 18 | autoHeightMin: PropTypes.number, 19 | /** 20 | * Set a maximum height for auto-height mode 21 | */ 22 | autoHeightMax: PropTypes.number 23 | }; 24 | 25 | export default CustomScrollbarProp; 26 | -------------------------------------------------------------------------------- /stories/utils/propTables/TitleTableProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const TitleTableProp = (props) => <div></div>; 5 | 6 | TitleTableProp.propTypes = { 7 | /** 8 | * Any valid react node 9 | */ 10 | titleActions: PropTypes.node, 11 | /** 12 | * Value for title 13 | */ 14 | name: PropTypes.string.isRequired, 15 | /** 16 | * Callback for refresh icon click 17 | */ 18 | onRefreshClick: PropTypes.func, 19 | /** 20 | * All props from Table component 21 | */ 22 | '': PropTypes.any 23 | }; 24 | 25 | export default TitleTableProp; 26 | -------------------------------------------------------------------------------- /stories/utils/propTables/ToasterProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ToasterProp = (props) => <div></div>; 5 | 6 | ToasterProp.propTypes = { 7 | defaultDuration: PropTypes.number, 8 | toasterPosition: PropTypes.string, 9 | notificationPosition: PropTypes.string 10 | }; 11 | 12 | ToasterProp.defaultProps = { 13 | defaultDuration: 4000, 14 | toasterPosition: 'right-top', 15 | notificationPosition: 'right-top' 16 | }; 17 | 18 | export default ToasterProp; 19 | -------------------------------------------------------------------------------- /stories/utils/propTables/index.js: -------------------------------------------------------------------------------- 1 | export PaperProp from './PaperProp'; 2 | export CardProp from './CardProp'; 3 | export ButtonProp from './ButtonProp'; 4 | export RadioProp from './RadioProp'; 5 | export ScrollbarProp from './ScrollbarProp'; 6 | export SwitcherProp from './SwitcherProp'; 7 | export PopoverProp from './PopoverProp'; 8 | export ExtendedInputProp from './ExtendedInputProp'; 9 | export CheckboxProp from './CheckboxProp'; 10 | export DatePickerProp from './DatePickerProp'; 11 | export DateMonthPickerProp from './DateMonthPickerProp'; 12 | export DateRangePickerProp from './DateRangePickerProp'; 13 | export DateWeekPickerProp from './DateWeekPickerProp'; 14 | export ComboTableProp from './ComboTableProp'; 15 | export TitleTableProp from './TitleTableProp'; 16 | export HeaderTableProp from './HeaderTableProp'; 17 | export { StepProp, StepsProp } from './StepsProp'; 18 | export MenuProp from './MenuProp'; 19 | export ToasterProp from './ToasterProp'; 20 | export ValidatableNumberInputProp from './validateElements/ValidatableNumberinput'; 21 | export ValidatableRadioProp from './validateElements/ValidatableRadio'; 22 | export ValidatableCheckboxProp from './validateElements/ValidatableCheckbox'; 23 | export ValidatableDatePickerProp from './validateElements/ValidableDatePickerProp'; 24 | export ValidatableTextInputProp from './validateElements/ValidatableTextInputProp'; 25 | -------------------------------------------------------------------------------- /stories/utils/propTables/validateElements/ValidatableCheckbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { noop } from 'utils'; 5 | 6 | const ValidatableCheckboxProp = (props) => <div></div>; 7 | 8 | ValidatableCheckboxProp.propTypes = { 9 | /** 10 | * Value for checkbox 11 | */ 12 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), 13 | /** 14 | * Callback fires when checkbox changes 15 | */ 16 | onChange: PropTypes.func, 17 | /** 18 | * Define is field required or no. 19 | */ 20 | required: PropTypes.bool, 21 | /** 22 | * Callback fires when field validation state changes 23 | */ 24 | isFieldValid: PropTypes.func, 25 | /** 26 | * Additional validation state 27 | */ 28 | isValid: PropTypes.bool, 29 | /** 30 | * Allow validation without onBlur, validate field when mount 31 | */ 32 | forceAllowValidation: PropTypes.bool 33 | }; 34 | 35 | ValidatableCheckboxProp.defaultProps = { 36 | isValid: true, 37 | isFieldValid: noop, 38 | onChange: noop 39 | }; 40 | 41 | export default ValidatableCheckboxProp; 42 | -------------------------------------------------------------------------------- /stories/utils/propTables/validateElements/ValidatableNumberinput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { noop } from 'utils'; 5 | 6 | const ValidatableNumberInputProp = (props) => <div></div>; 7 | 8 | ValidatableNumberInputProp.propTypes = { 9 | /** 10 | * Maximum value 11 | */ 12 | min: PropTypes.number, 13 | /** 14 | * Minimum value 15 | */ 16 | max: PropTypes.number, 17 | /** 18 | * Value for number field 19 | */ 20 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 21 | /** 22 | * Callback fires when field changes 23 | */ 24 | onChange: PropTypes.func, 25 | /** 26 | * Callback fires when field validation state changes 27 | */ 28 | isFieldValid: PropTypes.func, 29 | /** 30 | * Additional validation state 31 | */ 32 | isValid: PropTypes.bool, 33 | /** 34 | * Allow validation without onBlur, validate field when mount 35 | */ 36 | forceAllowValidation: PropTypes.bool 37 | }; 38 | 39 | ValidatableNumberInputProp.defaultProps = { 40 | isValid: true, 41 | isFieldValid: noop 42 | }; 43 | 44 | export default ValidatableNumberInputProp; 45 | -------------------------------------------------------------------------------- /stories/utils/propTables/validateElements/ValidatableRadio.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { noop } from 'utils'; 5 | 6 | const ValidatableRadioProp = (props) => <div></div>; 7 | 8 | ValidatableRadioProp.propTypes = { 9 | /** 10 | * Value for radio field 11 | */ 12 | value: PropTypes.string, 13 | /** 14 | * Callback fires when radio field changes 15 | */ 16 | onChange: PropTypes.func, 17 | /** 18 | * Define is field required or no. 19 | */ 20 | required: PropTypes.bool, 21 | /** 22 | * Callback fires when field validation state changes 23 | */ 24 | isFieldValid: PropTypes.func, 25 | /** 26 | * Additional validation state 27 | */ 28 | isValid: PropTypes.bool, 29 | /** 30 | * Allow validation without onBlur, validate field when mount 31 | */ 32 | forceAllowValidation: PropTypes.bool 33 | }; 34 | 35 | ValidatableRadioProp.defaultProps = { 36 | isValid: true, 37 | isFieldValid: noop, 38 | onChange: noop 39 | }; 40 | 41 | export default ValidatableRadioProp; 42 | -------------------------------------------------------------------------------- /stories/utils/propTables/validateElements/ValidatableTextInputProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { noop } from 'utils'; 5 | 6 | const ValidatableTextInputProp = (props) => <div></div>; 7 | 8 | ValidatableTextInputProp.propTypes = { 9 | /** 10 | * Value for text input 11 | */ 12 | value: PropTypes.string, 13 | /** 14 | * Callback fires when field changes 15 | */ 16 | onChange: PropTypes.func, 17 | /** 18 | * Define is field required or no. 19 | */ 20 | required: PropTypes.bool, 21 | /** 22 | * Is field accept only email or no 23 | */ 24 | isEmail: PropTypes.bool, 25 | /** 26 | * Maximum length of value 27 | */ 28 | maxLength: PropTypes.number, 29 | /** 30 | * Minimum length of value 31 | */ 32 | minLength: PropTypes.number, 33 | /** 34 | * Callback fires when field validation state changes 35 | */ 36 | isFieldValid: PropTypes.func, 37 | /** 38 | * Additional validation state 39 | */ 40 | isValid: PropTypes.bool, 41 | /** 42 | * Allow validation without onBlur, validate field when mount 43 | */ 44 | forceAllowValidation: PropTypes.bool 45 | }; 46 | 47 | ValidatableTextInputProp.defaultProps = { 48 | isValid: true, 49 | isFieldValid: noop 50 | }; 51 | 52 | export default ValidatableTextInputProp; 53 | -------------------------------------------------------------------------------- /tests/__mocks__/svg.js: -------------------------------------------------------------------------------- 1 | export default 'SvgrURL'; 2 | export const ReactComponent = 'div'; 3 | -------------------------------------------------------------------------------- /tests/helpers.ts: -------------------------------------------------------------------------------- 1 | // The module for testing helper functions 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /tests/setup.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | --------------------------------------------------------------------------------