├── .browserslistrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
└── workflows
│ ├── deploy.yml
│ └── lint.yml
├── .gitignore
├── .scss-lint.yml
├── .travis.yml
├── .vscode
├── cSpell.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── config
├── env.js
├── jest
│ ├── cssTransform.js
│ ├── fileTransform.js
│ └── setup.js
├── paths.js
├── polyfills.js
├── webpack.config.es.js
└── webpack.config.lib.js
├── deploy.sh
├── jsconfig.json
├── package.json
├── scripts
├── build.js
└── start.js
├── src
├── components
│ ├── Accordion
│ │ ├── Collapse.js
│ │ ├── Context.js
│ │ ├── Toggle.js
│ │ └── index.js
│ ├── AskBox
│ │ └── index.js
│ ├── Avatar
│ │ └── index.js
│ ├── Badge
│ │ └── index.js
│ ├── Breadcrumb
│ │ ├── Item.js
│ │ └── index.js
│ ├── BubbleChat
│ │ ├── Context.js
│ │ ├── Image.js
│ │ └── index.js
│ ├── Button
│ │ ├── Group.js
│ │ └── index.js
│ ├── Calender
│ │ ├── Calendar.js
│ │ ├── DatePicker.js
│ │ ├── DateRangePicker.js
│ │ ├── TimePicker.js
│ │ ├── index.js
│ │ └── v2
│ │ │ └── DatePicker
│ │ │ ├── DateInput.jsx
│ │ │ ├── DateInput.spec.jsx
│ │ │ ├── DateInput
│ │ │ ├── DayInput.jsx
│ │ │ ├── DayInput.spec.jsx
│ │ │ ├── Input.jsx
│ │ │ ├── MonthInput.jsx
│ │ │ ├── MonthInput.spec.jsx
│ │ │ ├── MonthSelect.jsx
│ │ │ ├── MonthSelect.spec.jsx
│ │ │ ├── NativeInput.jsx
│ │ │ ├── NativeInput.spec.jsx
│ │ │ ├── YearInput.jsx
│ │ │ └── YearInput.spec.jsx
│ │ │ ├── DatePicker.jsx
│ │ │ ├── DatePicker.spec.jsx
│ │ │ ├── Divider.jsx
│ │ │ └── shared
│ │ │ ├── dateFormatter.js
│ │ │ ├── dates.js
│ │ │ ├── dates.spec.js
│ │ │ ├── propTypes.js
│ │ │ ├── utils.js
│ │ │ └── utils.spec.js
│ ├── Card
│ │ └── index.js
│ ├── Carousel
│ │ └── index.js
│ ├── ChatBox
│ │ └── index.js
│ ├── Collapse
│ │ └── index.js
│ ├── Composer
│ │ └── index.js
│ ├── Counter
│ │ └── index.js
│ ├── Dropdown
│ │ ├── Button.js
│ │ ├── Container.js
│ │ ├── Context.js
│ │ ├── Toggle.js
│ │ └── index.js
│ ├── EmptyState
│ │ └── index.js
│ ├── Fade
│ │ └── index.js
│ ├── FileAttachment
│ │ └── index.js
│ ├── Form
│ │ ├── Check.js
│ │ ├── Context.js
│ │ ├── Feedback.js
│ │ ├── File.js
│ │ ├── Group.js
│ │ ├── Input.js
│ │ ├── InputGroup.js
│ │ ├── Label.js
│ │ ├── Select.js
│ │ └── index.js
│ ├── Header
│ │ ├── Mobile.js
│ │ └── index.js
│ ├── Icon
│ │ └── index.js
│ ├── Loader
│ │ └── index.js
│ ├── Logo
│ │ └── index.js
│ ├── Media
│ │ └── index.js
│ ├── Message
│ │ ├── Context.js
│ │ ├── Title.js
│ │ └── index.js
│ ├── Modal
│ │ ├── Context.js
│ │ ├── Header.js
│ │ ├── Inside.js
│ │ └── index.js
│ ├── MultiSteps
│ │ ├── Item.js
│ │ └── index.js
│ ├── Overlay
│ │ ├── Trigger.js
│ │ └── index.js
│ ├── PageLayout
│ │ ├── Body.js
│ │ ├── Context.js
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ └── index.js
│ ├── Pagination
│ │ ├── Item.js
│ │ └── index.js
│ ├── ProblemInfo
│ │ └── index.js
│ ├── Progress
│ │ └── index.js
│ ├── Rating
│ │ └── index.js
│ ├── SafeAnchor
│ │ └── index.js
│ ├── SearchBox
│ │ └── index.js
│ ├── Separator
│ │ └── index.js
│ ├── SessionType
│ │ └── index.js
│ ├── SidebarMenu
│ │ ├── Context.js
│ │ ├── Item.js
│ │ ├── SubMenu.js
│ │ └── index.js
│ ├── Skeleton
│ │ └── index.js
│ ├── Slider
│ │ ├── Handle.js
│ │ ├── Range.js
│ │ ├── createSliderWithTooltip.js
│ │ └── index.js
│ ├── Tab
│ │ ├── Context.js
│ │ ├── Item.js
│ │ └── index.js
│ ├── Tag
│ │ └── index.js
│ ├── TagInput
│ │ └── index.js
│ ├── Toast
│ │ └── index.js
│ ├── Toggle
│ │ └── index.js
│ ├── Tooltip
│ │ └── index.js
│ ├── TopBanner
│ │ └── index.js
│ └── TopMenu
│ │ ├── Context.js
│ │ ├── Item.js
│ │ ├── SubMenu.js
│ │ └── index.js
├── constants
│ ├── common.js
│ ├── icons.js
│ └── messages.js
├── hooks
│ ├── useForkRef.js
│ ├── useIsFocusVisible.js
│ ├── usePopper.js
│ ├── useRootClose.js
│ └── useWaitForDOMRef.js
├── index.js
├── plugins
│ ├── AssetPlugin.js
│ ├── PluginArray.js
│ └── index.js
└── utils
│ ├── createBlock.js
│ ├── createChainedFunction.js
│ ├── setRef.js
│ └── triggerBrowserReflow.js
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | ie >= 8
3 | edge >= 15
4 | ie_mob >= 10
5 | ff >= 45
6 | chrome >= 45
7 | safari >= 7
8 | opera >= 23
9 | ios >= 7
10 | android >= 4
11 | bb >= 10
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = false
9 | insert_final_newline = false
10 |
11 | [**.json]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [**.js]
16 | indent_style = space
17 | indent_size = 2
18 |
19 | [**.scss]
20 | indent_style = space
21 | indent_size = 2
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /config
2 | /scripts
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "jquery": true,
7 | "jest": true
8 | },
9 | "extends": "airbnb",
10 | "rules": {
11 | "jsx-a11y/label-has-associated-control": 0,
12 | "space-before-function-paren": 0,
13 | "react/prefer-stateless-function": 0,
14 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
15 | "linebreak-style": 0,
16 | "global-require": 0,
17 | "eol-last": 0,
18 | "comma-dangle": ["error", "always-multiline"],
19 | "spaced-comment": 0,
20 | "react/require-default-props": 0,
21 | "react/forbid-prop-types": 0,
22 | "jsx-a11y/label-has-for": 0,
23 | "jsx-a11y/anchor-is-valid": 0,
24 | "jsx-a11y/href-no-hash": 0,
25 | "jsx-a11y/interactive-supports-focus": 0,
26 | "jsx-a11y/click-events-have-key-events": 0,
27 | "jsx-a11y/mouse-events-have-key-events": 0,
28 | "jsx-a11y/no-static-element-interactions": 0,
29 | "react/no-did-mount-set-state": 0,
30 | "max-len": 0,
31 | "react/jsx-no-bind": 0,
32 | "class-methods-use-this" :0,
33 | "react/prop-types": 0,
34 | "no-unused-expressions": 0,
35 | "radix": 0,
36 | "object-curly-newline": 0,
37 | "no-console": 0,
38 | "import/no-unresolved": 0,
39 | "import/no-named-as-default": 0,
40 | "import/no-extraneous-dependencies": 0,
41 | "import/extensions": 0,
42 | "prefer-destructuring": 0,
43 | "no-multi-assign": 0,
44 | "function-paren-newline": 0,
45 | "react/no-unescaped-entities": 0,
46 | "no-debugger": 0,
47 | "no-shadow": 0,
48 | "no-plusplus": 0,
49 | "camelcase": 0,
50 | "react/sort-comp": [
51 | 1,
52 | {
53 | "order": [
54 | "type-annotations",
55 | "static-methods",
56 | "lifecycle",
57 | "everything-else",
58 | "render"
59 | ]
60 | }
61 | ],
62 | "react/no-multi-comp": 0,
63 | "import/prefer-default-export": 0
64 | },
65 | "globals": {
66 | "zE": true,
67 | "log": true,
68 | "logToQueue": true,
69 | "jsdom": true
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Use Node.js 16
11 | uses: actions/setup-node@v1
12 | with:
13 | node-version: 16.x
14 | - uses: actions/checkout@v3
15 | with:
16 | submodules: recursive
17 | - run: deploy.sh
18 | if: "github.event.head_commit.message == 'chore: Deploy'"
19 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | env:
8 | CI: true
9 | FORCE_COLOR: 2
10 | NODE: 14.x
11 |
12 | jobs:
13 | lint:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Clone repository
18 | uses: actions/checkout@v2
19 |
20 | - name: Set up Node.js
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: "${{ env.NODE }}"
24 |
25 | - name: Set up yarn cache
26 | id: yarn-cache
27 | uses: actions/cache@v2
28 | with:
29 | path: '**/node_modules'
30 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
31 |
32 | - name: Install dependencies with yarn
33 | if: steps.yarn-cache.outputs.cache-hit != 'true'
34 | run: yarn install --frozen-lockfile
35 |
36 | - name: Lint
37 | run: yarn lint
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # parcel-bundler cache (https://parceljs.org/)
72 | .cache
73 |
74 | # Next.js build output
75 | .next
76 |
77 | # Nuxt.js build / generate output
78 | .nuxt
79 | dist
80 |
81 | # Gatsby files
82 | .cache/
83 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
84 | # https://nextjs.org/blog/next-9-1#public-directory-support
85 | # public
86 |
87 | # vuepress build output
88 | .vuepress/dist
89 |
90 | # Serverless directories
91 | .serverless/
92 |
93 | # FuseBox cache
94 | .fusebox/
95 |
96 | # DynamoDB Local files
97 | .dynamodb/
98 |
99 | # TernJS port file
100 | .tern-port
101 |
102 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
103 |
104 | # dependencies
105 | /node_modules
106 | /.pnp
107 | .pnp.js
108 |
109 | # testing
110 | /coverage
111 |
112 | # production
113 | /storybook-static
114 | /lib
115 | /es
116 | /dist
117 | # misc
118 | .DS_Store
119 | .env.local
120 | .env.development.local
121 | .env.test.local
122 | .env.production.local
123 |
124 | npm-debug.log*
125 | yarn-debug.log*
126 | yarn-error.log*
127 | package-lock.json
128 | package.json.backup
129 |
--------------------------------------------------------------------------------
/.scss-lint.yml:
--------------------------------------------------------------------------------
1 |
2 | scss_files: 'src/**/**.scss'
3 | plugin_directories: ['.scss-linters']
4 | plugin_gems: []
5 | severity: warning
6 | linters:
7 | BorderZero:
8 | enabled: true
9 | convention: zero
10 | NameFormat:
11 | enabled: true
12 | PrivateNamingConvention:
13 | enabled: true
14 | prefix: _
15 | SingleLinePerProperty:
16 | enabled: true
17 | allow_single_line_rule_sets: false
18 | StringQuotes:
19 | enabled: true
20 | style: double_quotes
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '10'
4 | script:
5 | - npm install -g yarn
6 | - yarn
7 | - yarn build
8 | cache:
9 | directories:
10 | - node_modules
11 | deploy:
12 | provider: s3
13 | access_key_id: ${S3_ACCESS_KEY}
14 | secret_access_key: ${S3_SECRET_ACCESS_KEY}
15 | bucket: ${S3_BUCKET}
16 | region: ${S3_REGION}
17 | skip_cleanup: true
18 | local_dir: dist
19 | upload-dir: dev/design/designsystem/${VERSION}
20 | on:
21 | branch: ${DEPLOY_BRANCH}
22 | notifications:
23 | # TODO: use var
24 | slack: gotitai:jP3x4ahOD7ygOkB2tegwGj0k
25 |
--------------------------------------------------------------------------------
/.vscode/cSpell.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1",
3 | "language": "en",
4 | "enabledLanguageIds": [
5 | "javascript",
6 | "javascriptreact",
7 | "typescript",
8 | "typescriptreact",
9 | "yml"
10 | ],
11 | "allowCompoundWords": false,
12 | "ignorePaths": [
13 | "node_modules",
14 | "**/node_modules",
15 | "**/node_modules/**",
16 | "node_modules/**",
17 | "*.scss"
18 | ],
19 | "words": [
20 | "astroturf",
21 | "Docgen",
22 | "Doclet",
23 | "Doclets",
24 | "frontmatter",
25 | "gotitinc",
26 | "lifecycles",
27 | "Menlo",
28 | "monospace",
29 | "noopener",
30 | "noreferrer",
31 | "tutoruniverse"
32 | ]
33 | }
34 | // this will ignore anything the node_modules directory
35 | // the same for this one
36 | // the same for this one
37 | // Doesn't currently work due to how the current working directory is determined.
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.tabSize": 2,
3 | "files.trimTrailingWhitespace": true,
4 | "files.insertFinalNewline": true,
5 | "files.trimFinalNewlines": true,
6 | "editor.wordWrap": "on",
7 | "emmet.includeLanguages": {
8 | "javascript": "javascriptreact"
9 | },
10 | "files.associations": {
11 | "*.mdx": "mdx"
12 | },
13 | "editor.codeActionsOnSave": {
14 | "source.fixAll.eslint": true
15 | },
16 | "cSpell.words": [
17 | "Docgen",
18 | "Doclet",
19 | "Doclets",
20 | "Menlo",
21 | "astroturf",
22 | "frontmatter",
23 | "gotitinc",
24 | "lifecycles",
25 | "monospace",
26 | "noopener",
27 | "noreferrer",
28 | "tutoruniverse"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Release 2.0.3 - January 18, 2022
2 | ### Fixed
3 | * Code base: use absolute imports, rearrange hooks and utils
4 | * TopMenu: TopMenu-itemAfter of TopMenu-subMenu has wrong position (need CSS changes)
5 | * Toast: Upgrade from `react-toastify@6` to version `7.0.4` to fix issue:
6 | - `pauseOnFocusLoss` is not applied to windows already unfocused. [Ref](https://github.com/fkhadra/react-toastify/issues/541)
7 | * BubbleChat: map text className from `variantTextClassNames` using variant (like `variantClassNames`)
8 | * Form/Select: add missing `is-disabled` className on disabled
9 | ### Added
10 | * Icons: `shapes`, `return`, `umbrella`, `game`, `tagCloud`, `one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight`, `nine`, `ten`
11 | * Message: add test-id `[data-testid="message-close"]`
12 | * Modal/Header: add test-id `[data-testid="modal-close-button"]`
13 | ### Updated
14 | * react-toastify to fixed version
15 | * Support React 17
16 |
17 | ## Release 2.0.2 - January 18, 2021
18 | ### Fixed
19 | * Button: modified variant `secondary`, fixed ActionBar
20 | * FileAttachment: modified prop `fileTypeLabel`
21 | * BubbleChat: fixed UI bug, removed console log, map style of `type=system` to `variant=primaryLight`, improve prop `options`
22 | * PaginationItem: removed console log
23 | * Toast: fixed warning
24 | ### Added
25 | * Breadcrumb: added prop `noHref`
26 | * Icon: added icon `fileImport`, `fileExport`, `bubbles`
27 | ### Updated
28 | * rc-slider, react-slick, react-split-pane, react-toastify to fixed version
29 |
30 | ## Release 2.0.1 - December 15, 2020
31 | ### Fixed
32 | * BubbleChat: pass `avatar` prop value to prop `name` of Avatar, instead of `src`
33 | * Composer: remove defaultProps `tooltipAttachButton` and `tooltipSendButton`
34 | * Button, Tag, Badge: add prop `textClassName` to custom text color
35 |
36 | ## Release 2.0.0 - December 6, 2020
37 | ### Added
38 | * Migrate all from Got It Design System 1.12.11
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Aha Design System - React
8 |
9 | Collection of React Components for building web applications.
10 |
11 | Explore Aha docs »
12 |
13 |
14 | Report bug
15 | ·
16 | Request feature
17 |
18 |
19 | ## Status
20 | [](https://github.com/gotitinc/aha-react/actions)
21 | 
22 | [](https://www.npmjs.com/package/@ahaui/react)
23 | [](https://david-dm.org/gotitinc/aha-react?type=peer)
24 | [](https://david-dm.org/gotitinc/aha-react?type=peer)
25 | [](https://david-dm.org/gotitinc/aha-react?type=dev)
26 |
27 | ## Quick start
28 |
29 | ### Installation
30 | You have to install both `@ahaui/react` and `@ahaui/css`
31 | ```sh
32 | # With npm
33 | npm install @ahaui/react @ahaui/css
34 |
35 | # Or with yarn
36 | yarn add @ahaui/react @ahaui/css
37 | ```
38 |
39 | ### Usage
40 | ```jsx
41 | import '@ahaui/css/dist/index.min.css';
42 | import React from 'react';
43 | import { Button } from '@ahaui/react';
44 |
45 | function Example() {
46 | const onButtonClick = () => {
47 | alert('Aha!');
48 | };
49 |
50 | return (
51 |
56 | );
57 | }
58 | ```
59 | **Aha!** Just simple as that!
60 |
61 | ## Customization
62 |
63 | ### Plugins
64 | You can customize specific Aha React Components via plugins!
65 |
66 | For now, to provide your custom assets to [Logo](./src/components/Logo/index.js), [Avatar](./src/components/Avatar/index.js), [EmptyState](./src/components/Logo/index.js), you can use [AssetPlugin](./src/plugins/AssetPlugin.js).
67 | ```jsx
68 | import { AssetPlugin, Plugins, Logo } from '@ahaui/react';
69 |
70 | const LogoAssetsPlugin = new AssetPlugin({
71 | prefix: 'logo',
72 | assets: {
73 | mylogo: require('./assets/images/logo/my-logo.svg').default,
74 | foobar: 'https://foo.bar/image.jpg',
75 | }
76 | });
77 |
78 | Plugins.loadPlugin(LogoAssetsPlugin);
79 |
80 | function Example() {
81 | return (
82 |
83 | );
84 | }
85 | ```
86 |
87 | Various types of plugin will be developed in the future, stay tune!
88 |
89 | Plugins could also be published standalone as a npm package (follow the template [here](https://github.com/gotitinc/aha-plugin-example)).
90 |
91 | ### Custom CSS
92 | Further instruction could be found in [here](https://github.com/gotitinc/aha-css#custom).
93 |
94 | ## Copyright and License
95 |
96 | Code and documentation copyright 2020 the [Got It, Inc.](https://www.got-it.ai) Code released under the [Apache-2.0 License](https://github.com/gotitinc/aha-react/blob/master/LICENSE).
97 |
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 |
7 | // Make sure that including paths.js after env.js will read .env variables.
8 | delete require.cache[require.resolve('./paths')];
9 |
10 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | const dotenvFiles = [
19 | `${paths.dotenv}.${process.env.NODE_ENV}`,
20 | paths.dotenv,
21 | ].filter(Boolean);
22 |
23 | // Load environment variables from .env* files. Suppress warnings using silent
24 | // if this file is missing. dotenv will never modify any environment variables
25 | // that have already been set.
26 | // https://github.com/motdotla/dotenv
27 | dotenvFiles.forEach(dotenvFile => {
28 | if (fs.existsSync(dotenvFile)) {
29 | require('dotenv').config({
30 | path: dotenvFile,
31 | });
32 | }
33 | });
34 |
35 | // We support resolving modules according to `NODE_PATH`.
36 | // This lets you use absolute paths in imports inside large monorepos:
37 | // https://github.com/facebookincubator/create-react-app/issues/253.
38 | // It works similar to `NODE_PATH` in Node itself:
39 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
40 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
41 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
42 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
43 | // We also resolve them to make sure all tools using them work consistently.
44 | const appDirectory = fs.realpathSync(process.cwd());
45 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
46 | .split(path.delimiter)
47 | .filter(folder => folder && !path.isAbsolute(folder))
48 | .map(folder => path.resolve(appDirectory, folder))
49 | .join(path.delimiter);
50 |
51 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
52 | // injected into the application via DefinePlugin in Webpack configuration.
53 | const REACT_APP = /^REACT_APP_/i;
54 |
55 | function getClientEnvironment(publicUrl) {
56 | const raw = Object.keys(process.env)
57 | .filter(key => REACT_APP.test(key))
58 | .reduce(
59 | (env, key) => {
60 | env[key] = process.env[key];
61 | return env;
62 | },
63 | {
64 | // Useful for determining whether we’re running in production mode.
65 | // Most importantly, it switches React into the correct mode.
66 | NODE_ENV: process.env.NODE_ENV || 'development',
67 | // Useful for resolving the correct path to static assets in `public`.
68 | // For example,
.
69 | // This should only be used as an escape hatch. Normally you would put
70 | // images into the `src` and `import` them in code to get their paths.
71 | PUBLIC_URL: publicUrl,
72 | }
73 | );
74 | // Stringify all values so we can feed into Webpack DefinePlugin
75 | const stringified = {
76 | 'process.env': Object.keys(raw).reduce((env, key) => {
77 | env[key] = JSON.stringify(raw[key]);
78 | return env;
79 | }, {}),
80 | };
81 |
82 | return { raw, stringified };
83 | }
84 |
85 | module.exports = getClientEnvironment;
86 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/config/jest/setup.js:
--------------------------------------------------------------------------------
1 | import { configure } from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 | import $ from 'jquery';
4 |
5 | // Setup enzyme
6 | configure({ adapter: new Adapter() });
7 |
8 | // Mocks
9 | window.LogzioLogger = function() {
10 | this.log = () => {};
11 | };
12 |
13 | global.log = jest.fn();
14 | global.logToQueue = jest.fn();
15 |
16 | window.scrollTo = jest.fn();
17 | window.open = jest.fn();
18 | window.alert = jest.fn();
19 |
20 | // fetch
21 | global.fetch = require('jest-fetch-mock');
22 |
23 |
24 | window.$ = window.jQuery = $;
25 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 |
6 | // Make sure any symlinks in the project folder are resolved:
7 | // https://github.com/facebookincubator/create-react-app/issues/637
8 | const appDirectory = fs.realpathSync(process.cwd());
9 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
10 |
11 | // config after eject: we're in ./config/
12 | module.exports = {
13 | dotenv: resolveApp(`.env`),
14 | appBuildES: resolveApp(`es`),
15 | appBuildLib: resolveApp(`lib`),
16 | appBuildDist: resolveApp(`dist`),
17 | appIndexJs: resolveApp('src/index.js'),
18 | appSCSS: resolveApp('scss'),
19 | appPackageJson: resolveApp('package.json'),
20 | appSrc: resolveApp('src'),
21 | yarnLockFile: resolveApp('yarn.lock'),
22 | testsSetup: resolveApp('src/setupTests.js'),
23 | appNodeModules: resolveApp('node_modules'),
24 | };
25 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof Promise === 'undefined') {
4 | // Rejection tracking prevents a common issue where React gets into an
5 | // inconsistent state due to an error, but it gets swallowed by a Promise,
6 | // and the user has no idea what causes React's erratic future behavior.
7 | require('promise/lib/rejection-tracking').enable();
8 | window.Promise = require('promise/lib/es6-extensions.js');
9 | }
10 |
11 | if (!String.prototype.startsWith) {
12 | String.prototype.startsWith = function(searchString, position) {
13 | position = position || 0;
14 | return this.indexOf(searchString, position) === position;
15 | };
16 | }
17 |
18 | if (!NodeList.prototype.forEach && Array.prototype.forEach) {
19 | NodeList.prototype.forEach = Array.prototype.forEach;
20 | }
21 |
22 | require('babel-polyfill');
23 |
24 | // fetch() polyfill for making API calls.
25 | require('isomorphic-fetch');
26 |
27 | // Object.assign() is commonly used with React.
28 | // It will use the native implementation if it's present and isn't buggy.
29 | Object.assign = require('object-assign');
30 |
31 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
32 | // We don't polyfill it in the browser--this is user's responsibility.
33 | if (process.env.NODE_ENV === 'test') {
34 | require('raf').polyfill(global);
35 | }
36 |
37 | (function (arr) {
38 | arr.forEach(function (item) {
39 | if (item.hasOwnProperty('remove')) {
40 | return;
41 | }
42 | Object.defineProperty(item, 'remove', {
43 | configurable: true,
44 | enumerable: true,
45 | writable: true,
46 | value: function remove() {
47 | if (this.parentNode !== null)
48 | this.parentNode.removeChild(this);
49 | }
50 | });
51 | });
52 | })([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
53 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | rm -rf es/images
2 | rm -rf lib/images
3 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
4 | npm publish
5 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs",
5 | "allowSyntheticDefaultImports": true,
6 | "baseUrl": "./src/"
7 | },
8 | "exclude": [
9 | "node_modules"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ahaui/react",
3 | "version": "2.0.3",
4 | "main": "lib/index.js",
5 | "module": "es/index.js",
6 | "files": [
7 | "es/*.{js,map}",
8 | "lib/*.{js,map}",
9 | "CHANGELOG.md"
10 | ],
11 | "scripts": {
12 | "build": "node scripts/build",
13 | "prepublishOnly": "yarn build",
14 | "start": "node scripts/start",
15 | "format": "eslint --ext js src --fix",
16 | "lint": "eslint --ext js src",
17 | "prepack": "clean-package -rm devDependencies scripts",
18 | "postpack": "clean-package restore"
19 | },
20 | "publishConfig": {
21 | "access": "public"
22 | },
23 | "dependencies": {
24 | "@restart/hooks": "0.3.25",
25 | "@wojtekmaj/predict-input-value": "1.0.0",
26 | "@wojtekmaj/react-daterange-picker": "2.5.0",
27 | "classnames": "2.2.6",
28 | "dom-helpers": "5.1.0",
29 | "popper.js": "1.15.0",
30 | "prop-types": "15.7.2",
31 | "prop-types-extra": "1.1.0",
32 | "rc-slider": "8.7.1",
33 | "react-date-picker": "7.10.0",
34 | "react-slick": "0.27.13",
35 | "react-split-pane": "0.1.92",
36 | "react-tagsinput": "3.19.0",
37 | "react-textarea-autosize": "7.1.2",
38 | "react-time-picker": "3.9.0",
39 | "react-toastify": "7.0.4",
40 | "react-transition-group": "4.0.0",
41 | "uncontrollable": "7.1.1",
42 | "warning": "4.0.3"
43 | },
44 | "devDependencies": {
45 | "@babel/core": "7.6.4",
46 | "@babel/plugin-syntax-class-properties": "7.12.1",
47 | "@babel/plugin-syntax-dynamic-import": "7.8.3",
48 | "@babel/plugin-syntax-optional-chaining": "7.8.3",
49 | "autoprefixer": "9.6.1",
50 | "babel-eslint": "10.1.0",
51 | "babel-loader": "8.1.0",
52 | "babel-plugin-module-resolver": "4.1.0",
53 | "babel-preset-react-app": "9.1.2",
54 | "case-sensitive-paths-webpack-plugin": "2.2.0",
55 | "clean-package": "1.0.1",
56 | "dotenv": "8.2.0",
57 | "eslint": "5.12.0",
58 | "eslint-config-airbnb": "17.1.0",
59 | "eslint-config-react-app": "3.0.6",
60 | "eslint-loader": "2.1.1",
61 | "eslint-plugin-flowtype": "3.2.0",
62 | "eslint-plugin-import": "2.14.0",
63 | "eslint-plugin-jsx-a11y": "6.1.2",
64 | "eslint-plugin-react": "7.12.3",
65 | "fs-extra": "7.0.1",
66 | "husky": ">=4",
67 | "lint-staged": "10.5.2",
68 | "react": "16.9.0",
69 | "react-dev-utils": "9.0.4",
70 | "react-dom": "16.9.0",
71 | "webpack": "4.40.2",
72 | "webpack-node-externals": "1.7.2"
73 | },
74 | "peerDependencies": {
75 | "@ahaui/css": "^2.0.4",
76 | "react": "^16.9.0 || 17.x",
77 | "react-dom": "^16.9.0 || 17.x"
78 | },
79 | "lint-staged": {
80 | "*.js": "eslint --cache --fix"
81 | },
82 | "prettier": {
83 | "singleQuote": true,
84 | "trailingComma": "all"
85 | },
86 | "author": "Got It, Inc.",
87 | "homepage": "https://github.com/gotitinc/aha-react#readme",
88 | "license": "Apache-2.0",
89 | "repository": {
90 | "type": "git",
91 | "url": "git+https://github.com/gotitinc/aha-react.git"
92 | },
93 | "description": "Aha Design System - React",
94 | "directories": {
95 | "lib": "lib"
96 | },
97 | "husky": {
98 | "hooks": {
99 | "pre-commit": "lint-staged"
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/scripts/start.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'development';
5 | process.env.NODE_ENV = 'development';
6 |
7 | // Makes the script crash on unhandled rejections instead of silently
8 | // ignoring them. In the future, promise rejections that are not handled will
9 | // terminate the Node.js process with a non-zero exit code.
10 | process.on('unhandledRejection', err => {
11 | throw err;
12 | });
13 |
14 | // Ensure environment variables are read.
15 | require('../config/env');
16 |
17 |
18 | const chalk = require('chalk');
19 | const fs = require('fs-extra');
20 | const webpack = require('webpack');
21 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
22 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
23 | const paths = require('../config/paths');
24 | const esConfig = require('../config/webpack.config.es');
25 | const libConfig = require('../config/webpack.config.lib');
26 |
27 | const measureFileSizesBeforeBuild =
28 | FileSizeReporter.measureFileSizesBeforeBuild;
29 |
30 | // Warn and crash if required files are missing
31 | if (!checkRequiredFiles([paths.appIndexJs])) {
32 | process.exit(1);
33 | }
34 |
35 | // First, read the current file sizes in build directory.
36 | // This lets us display how much they changed later.
37 | measureFileSizesBeforeBuild(paths.appBuildES)
38 | .then(previousFileSizes => {
39 | // Remove all content but keep the directory so that
40 | // if you're in it, you don't end up in Trash
41 | fs.emptyDirSync(paths.appBuildES);
42 | // Start the webpack build
43 | return build(previousFileSizes, esConfig, 'es');
44 | });
45 |
46 | measureFileSizesBeforeBuild(paths.appBuildLib)
47 | .then(previousFileSizes => {
48 | // Remove all content but keep the directory so that
49 | // if you're in it, you don't end up in Trash
50 | fs.emptyDirSync(paths.appBuildLib);
51 | // Start the webpack build
52 | return build(previousFileSizes, libConfig, 'lib');
53 | });
54 |
55 | // Create the production build and print the deployment instructions.
56 | function build(previousFileSizes, config, moduleName) {
57 | let compiler = webpack(config);
58 |
59 | compiler.watch({
60 | aggregateTimeout: 300, // wait so long for more changes
61 | poll: false // use polling instead of native watchers
62 | }, (err, stats) => {
63 | if (err) {
64 | console.error(err);
65 | }
66 |
67 | console.log(`============================== ${chalk.green(moduleName)} ==============================`);
68 |
69 | console.log(stats.toString({
70 | chunks: false, // Makes the build much quieter
71 | colors: true
72 | }));
73 |
74 | console.log('');
75 | console.log('');
76 | });
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/Accordion/Collapse.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import CollapseBase from 'components/Collapse';
4 | import AccordionContext from './Context';
5 |
6 | const propTypes = {
7 | /**
8 | * A key that corresponds to the toggler that triggers this collapse's expand or collapse.
9 | */
10 | eventKey: PropTypes.string.isRequired,
11 | /** Children prop should only contain a single child, and is enforced as such */
12 | children: PropTypes.element.isRequired,
13 | };
14 |
15 | const Collapse = React.forwardRef(
16 | ({ children, eventKey, ...props }, ref) => {
17 | const contextEventKey = useContext(AccordionContext);
18 | return (
19 |
25 | {React.Children.only(children)}
26 |
27 | );
28 | },
29 | );
30 | Collapse.displayName = 'AccordionCollapse';
31 | Collapse.defaultProps = {};
32 | Collapse.propTypes = propTypes;
33 | export default Collapse;
34 |
--------------------------------------------------------------------------------
/src/components/Accordion/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const SelectableContext = React.createContext(null);
4 | const AccordionContext = React.createContext(null);
5 |
6 | export default AccordionContext;
7 |
--------------------------------------------------------------------------------
/src/components/Accordion/Toggle.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, cloneElement } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import AccordionContext, { SelectableContext } from './Context';
5 |
6 | const propTypes = {
7 | /**
8 | * A key that corresponds to the collapse component that gets triggered
9 | * when this has been clicked.
10 | */
11 | eventKey: PropTypes.string.isRequired,
12 |
13 | /** A callback function for when this component is clicked */
14 | onClick: PropTypes.func,
15 |
16 | /** Children prop should only contain a single child, and is enforced as such */
17 | children: PropTypes.element.isRequired,
18 | };
19 | export function useAccordionToggle(eventKey, onClick) {
20 | const contextEventKey = useContext(AccordionContext);
21 | const onSelect = useContext(SelectableContext);
22 | return (e) => {
23 | const eventKeyPassed = eventKey === contextEventKey ? null : eventKey;
24 | onSelect(eventKeyPassed, e);
25 | if (onClick) onClick(e);
26 | };
27 | }
28 |
29 | const AccordionToggle = React.forwardRef(({ className, eventKey, onClick, children, disabled, ...props }, ref) => {
30 | const onAccordionClick = useAccordionToggle(eventKey, onClick);
31 | return cloneElement(children, {
32 | className: classNames(
33 | 'Accordion-toggle',
34 | disabled ? ' u-pointerEventsNone u-cursorNotAllow' : 'u-cursorPointer',
35 | className && className
36 | ),
37 | onClick: !disabled ? onAccordionClick : null,
38 | ...props,
39 | ref,
40 | children,
41 | });
42 | });
43 | AccordionToggle.propTypes = propTypes;
44 | AccordionToggle.defaultProps = {};
45 | AccordionToggle.displayName = 'AccordionToggle';
46 | export default AccordionToggle;
47 |
--------------------------------------------------------------------------------
/src/components/Accordion/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { useUncontrolled } from 'uncontrollable';
5 | import AccordionContext, { SelectableContext } from './Context';
6 | import Toggle from './Toggle';
7 | import Collapse from './Collapse';
8 |
9 | const propTypes = {
10 | /**
11 | * The current active key that corresponds to the currently expanded card
12 | *
13 | * @controllable onSelect
14 | */
15 | activeKey: PropTypes.string,
16 |
17 | };
18 |
19 | const defaultProps = {
20 |
21 | };
22 |
23 | const Accordion = React.forwardRef(({ className, as: Component = 'div', ...props }, ref) => {
24 | const {
25 | activeKey,
26 | onSelect,
27 | ...controlledProps
28 | } = useUncontrolled(props, {
29 | activeKey: 'onSelect',
30 | });
31 |
32 | return (
33 |
34 |
35 |
43 |
44 |
45 | );
46 | });
47 |
48 | Accordion.displayName = 'Accordion';
49 | Accordion.defaultProps = defaultProps;
50 | Accordion.propTypes = propTypes;
51 | Accordion.Toggle = Toggle;
52 | Accordion.Collapse = Collapse;
53 | export default Accordion;
54 |
--------------------------------------------------------------------------------
/src/components/AskBox/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import createBlock from 'utils/createBlock';
4 |
5 | const propTypes = {
6 |
7 | };
8 | const defaultProps = {
9 |
10 | };
11 |
12 | const AskBox = React.forwardRef(({ className, ...props }, ref) => (
13 |
21 | ));
22 |
23 | const Title = createBlock('AskBox-title u-text400 lg:u-text500');
24 | const Header = createBlock('AskBox-header u-paddingHorizontalSmall lg:u-paddingHorizontalLarge u-paddingTopSmall lg:u-paddingTopMedium u-paddingBottomSmall');
25 | const Body = createBlock('AskBox-body u-paddingHorizontalSmall lg:u-paddingHorizontalLarge');
26 | const Footer = createBlock('AskBox-footer u-paddingHorizontalSmall lg:u-paddingHorizontalLarge u-paddingBottomSmall lg:u-paddingBottomMedium');
27 | const Note = createBlock('AskBox-note u-textCenter u-text200 u-textGray u-paddingTopSmall');
28 |
29 | AskBox.Title = Title;
30 | AskBox.Header = Header;
31 | AskBox.Body = Body;
32 | AskBox.Footer = Footer;
33 | AskBox.Note = Note;
34 | AskBox.displayName = 'AskBox';
35 | AskBox.defaultProps = defaultProps;
36 | AskBox.propTypes = propTypes;
37 |
38 | export default AskBox;
39 |
--------------------------------------------------------------------------------
/src/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Plugins from 'plugins';
5 | import { PluginType } from 'constants/common';
6 |
7 | const propTypes = {
8 | /** The Avatar visual name, should be provide via an AssetPlugin with prefix "avatar" */
9 | name: PropTypes.string,
10 | /** Avatar size variants */
11 | size: PropTypes.oneOf([
12 | 'extraSmall',
13 | 'small',
14 | 'medium',
15 | 'large',
16 | 'extraLarge',
17 | 'extraLargePlus',
18 | 'huge',
19 | ]),
20 | /** Providing a `src` will render an `
` element */
21 | src: PropTypes.string,
22 | /** Providing a `text` will render an `` element */
23 | text: PropTypes.string,
24 | /** Providing a alt if `src` exits */
25 | alt: PropTypes.string,
26 | /** Set the width of the avatar */
27 | width: PropTypes.number,
28 | /** Set the height of the avatar */
29 | height: PropTypes.number,
30 | /**
31 | * You can use a custom element type for this component.
32 | * @default div
33 | * */
34 | as: PropTypes.elementType,
35 | };
36 |
37 | const defaultProps = {
38 | size: 'medium',
39 | alt: 'Avatar',
40 | };
41 |
42 | const Avatar = React.forwardRef(({ className, size, name, src, alt, height, width, text, as: Component = 'div', ...props }, ref) => {
43 | let nameOri = name;
44 | let srcOri = src;
45 | let textOri = text;
46 | if (srcOri) {
47 | nameOri = false;
48 | textOri = false;
49 | } else if (textOri) {
50 | nameOri = false;
51 | } else if (nameOri) {
52 | srcOri = Plugins
53 | .getPlugins(PluginType.ASSET)
54 | .traverseCall('getAsset', 'avatar', nameOri)
55 | .find(asset => !!asset);
56 | }
57 | const heightStyle = {
58 | width: width && width,
59 | height: height && height,
60 | };
61 | const mergeProps = {
62 | ref,
63 | style: { ...props.style, ...heightStyle },
64 | ...props,
65 | };
66 | return (
67 |
77 | {srcOri && (
78 |
79 | )}
80 | {textOri && (
81 | {textOri}
82 | )}
83 |
84 | );
85 | });
86 |
87 |
88 | Avatar.displayName = 'Avatar';
89 | Avatar.defaultProps = defaultProps;
90 | Avatar.propTypes = propTypes;
91 | export default Avatar;
92 |
--------------------------------------------------------------------------------
/src/components/Badge/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** The Badge visual variant */
7 | variant: PropTypes.oneOf([
8 | 'default',
9 | 'white',
10 | 'black',
11 | 'primary',
12 | 'primary_subtle',
13 | 'warning',
14 | 'warning_subtle',
15 | 'positive',
16 | 'positive_subtle',
17 | 'information',
18 | 'information_subtle',
19 | 'negative',
20 | 'negative_subtle',
21 | ]),
22 |
23 | /** Fixed className for text color, just available for variant: `primary`, `primary_subtle` */
24 | textClassName: PropTypes.oneOfType([
25 | PropTypes.string,
26 | PropTypes.arrayOf(PropTypes.string),
27 | ]),
28 | /**
29 | * You can use a custom element type for this component.
30 | * @default span
31 | */
32 | as: PropTypes.elementType,
33 | };
34 | const defaultProps = {
35 | variant: 'default',
36 | };
37 |
38 |
39 | const variantsTextClassName = {
40 | primary: 'u-textWhite hover:u-textWhite',
41 | primary_subtle: 'u-textPrimary hover:u-textPrimary',
42 | };
43 |
44 | const variantsClassName = {
45 | default: 'u-textGray hover:u-textGray u-backgroundUltraLight',
46 | white: 'u-texDark hover:u-texDark u-backgroundWhite',
47 | black: 'u-textWhite hover:u-textWhite u-backgroundBlack',
48 | primary: 'u-backgroundPrimary',
49 | primary_subtle: 'u-backgroundPrimaryLighter',
50 | information: 'u-textWhite hover:u-textWhite u-backgroundInformation',
51 | information_subtle: 'u-textInformation hover:u-textInformation u-backgroundInformationLighter',
52 | warning: 'u-textDark hover:u-textDark u-backgroundWarning',
53 | warning_subtle: 'u-textDark hover:u-textDark u-backgroundWarningLighter',
54 | positive: 'u-textWhite hover:u-textWhite u-backgroundPositive',
55 | positive_subtle: 'u-textPositive hover:u-textPositive u-backgroundPositiveLighter',
56 | negative: 'u-textWhite hover:u-textWhite u-backgroundNegative',
57 | negative_subtle: 'u-textNegative hover:u-textNegative u-backgroundNegativeLighter',
58 | };
59 |
60 | const Badge = React.forwardRef(({ className, textClassName, variant, as: Component = 'span', ...props }, ref) => (
61 |
72 | ));
73 |
74 | Badge.displayName = 'Badge';
75 | Badge.defaultProps = defaultProps;
76 | Badge.propTypes = propTypes;
77 | export default Badge;
78 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb/Item.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import SafeAnchor from 'components/SafeAnchor';
5 |
6 | const propTypes = {
7 |
8 | /**
9 | * `href` attribute for the inner `a` element
10 | * @default #
11 | */
12 | href: PropTypes.string,
13 | /**
14 | * Non-render the SafeAnchor
15 | */
16 | noHref: PropTypes.bool,
17 | /**
18 | * `title` attribute for the inner `a` element
19 | */
20 | title: PropTypes.node,
21 | /**
22 | * `target` attribute for the inner `a` element
23 | */
24 | target: PropTypes.string,
25 |
26 | };
27 | const defaultProps = {
28 |
29 | };
30 |
31 | const Item = React.forwardRef(({ className, children, noHref, position, schema, isLast, ...props }, ref) => {
32 | const Component = isLast || noHref ? 'span' : SafeAnchor;
33 | return (
34 |
41 | {schema ? (
42 |
43 |
51 | {children}
52 |
53 |
54 |
55 | ) : (
56 |
63 | {children}
64 |
65 | )}
66 |
67 | );
68 | });
69 |
70 | Item.displayName = 'BreadcrumbItem';
71 | Item.defaultProps = defaultProps;
72 | Item.propTypes = propTypes;
73 | export default Item;
74 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Item from './Item';
5 |
6 | const propTypes = {
7 | /** Enable Structured Data `https://schema.org/BreadcrumbList` */
8 | schema: PropTypes.bool,
9 | };
10 | const defaultProps = {
11 | schema: false,
12 | };
13 |
14 | const Breadcrumb = React.forwardRef(({ className, schema, children, ...props }, ref) => {
15 | let schemasList;
16 | let schemasItem;
17 | if (schema) {
18 | schemasList = {
19 | itemScope: true,
20 | itemType: 'http://schema.org/BreadcrumbList',
21 | };
22 | schemasItem = {
23 | itemScope: true,
24 | itemProp: 'itemListElement',
25 | itemType: 'http://schema.org/ListItem',
26 | };
27 | }
28 | const numChildren = React.Children.count(children);
29 | const modifiedChildren = React.Children.map(children, (child, index) => {
30 | if (!child) {
31 | return null;
32 | }
33 | return React.cloneElement(
34 | child, ({
35 | ...schemasItem,
36 | schema,
37 | isLast: index === numChildren - 1,
38 | position: index + 1,
39 | })
40 | );
41 | });
42 | return (
43 |
52 | {modifiedChildren}
53 |
54 | );
55 | });
56 |
57 | Breadcrumb.displayName = 'Breadcrumb';
58 | Breadcrumb.defaultProps = defaultProps;
59 | Breadcrumb.propTypes = propTypes;
60 | Breadcrumb.Item = Item;
61 | export default Breadcrumb;
62 |
--------------------------------------------------------------------------------
/src/components/BubbleChat/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext();
4 |
5 | export default Context;
6 |
--------------------------------------------------------------------------------
/src/components/BubbleChat/Image.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import Context from './Context';
4 |
5 |
6 | const BubbleChatImage = React.forwardRef(({ className, ...props }, ref) => {
7 | const { type } = useContext(Context);
8 | return (
9 |
18 |
![]()
19 |
20 | );
21 | });
22 |
23 | BubbleChatImage.displayName = 'BubbleChat.Image';
24 | BubbleChatImage.defaultProps = {};
25 | BubbleChatImage.propTypes = {};
26 | export default BubbleChatImage;
27 |
--------------------------------------------------------------------------------
/src/components/Button/Group.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Context from 'components/Form/Context';
5 |
6 | const propTypes = {
7 | /**
8 | * Sets the size for all Buttons in the group.
9 | * @default 'medium'
10 | * @type {('small'|'medium'|'large')}
11 | * */
12 | sizeControl: PropTypes.string,
13 | /** Sets the disabled state for all Buttons in the group. */
14 | disabledControl: PropTypes.bool,
15 | };
16 |
17 | const defaultProps = {
18 | disabledControl: false,
19 | };
20 |
21 | const Group = React.forwardRef(({ className, sizeControl, disabledControl, as: Component = 'div', ...props }, ref) => {
22 | const context = useMemo(() => ({ sizeControl, disabledControl }), [sizeControl, disabledControl]);
23 | return (
24 |
25 |
33 |
34 | );
35 | }
36 | );
37 | Group.displayName = 'ButtonGroup';
38 | Group.propTypes = propTypes;
39 | Group.defaultProps = defaultProps;
40 | export default Group;
41 |
--------------------------------------------------------------------------------
/src/components/Calender/Calendar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import CalendarBase from 'react-calendar/dist/entry.nostyle';
4 |
5 | const Calendar = React.forwardRef(({ className, ...props }, ref) => (
6 |
14 | ));
15 |
16 | Calendar.displayName = 'Calendar';
17 | Calendar.defaultProps = {};
18 | Calendar.propTypes = {};
19 |
20 | export default Calendar;
21 |
--------------------------------------------------------------------------------
/src/components/Calender/DatePicker.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import DatePickerBase from 'react-date-picker/dist/entry.nostyle';
5 | import Context from 'components/Form/Context';
6 | import Icon from 'components/Icon';
7 | import DatePickerBaseV2 from './v2/DatePicker/DatePicker';
8 |
9 | const DatePicker = React.forwardRef(({ className, noClearIcon, size, version, calendarClassName, ...props }, ref) => {
10 | const { sizeControl } = useContext(Context);
11 | const sizeOri = size || sizeControl;
12 | const BaseClass = (version === 2) ? DatePickerBaseV2 : DatePickerBase;
13 | return (
14 |
}
24 | calendarIcon={
}
25 | calendarClassName={classNames(
26 | 'Calendar u-marginTopExtraSmall',
27 | calendarClassName && calendarClassName,
28 | )}
29 | />
30 | );
31 | });
32 |
33 | DatePicker.displayName = 'DatePicker';
34 | DatePicker.defaultProps = {
35 | noClearIcon: false,
36 | version: 1,
37 | };
38 | DatePicker.propTypes = {
39 | /**
40 | * Remove clear Icon
41 | */
42 | noClearIcon: PropTypes.bool,
43 | /**
44 | * DatePicker size variants
45 | *
46 | * Uses sizeControl from `
` if not explicitly specified.
47 | * @default 'medium'
48 | * */
49 | size: PropTypes.oneOf(['small', 'medium', 'large']),
50 | /**
51 | * DatePicker version:
52 | * - 1: default version, import from 'react-date-picker'
53 | * - 2: customized version
54 | *
55 | * @default 1
56 | * */
57 | version: PropTypes.oneOf([1, 2]),
58 | /**
59 | * Custom className for pop-up Calendar
60 | * */
61 | calendarClassName: PropTypes.string,
62 | };
63 |
64 | export default DatePicker;
65 |
--------------------------------------------------------------------------------
/src/components/Calender/DateRangePicker.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import DateRangePickerBase from '@wojtekmaj/react-daterange-picker/dist/entry.nostyle';
5 | import Icon from 'components/Icon';
6 | import Context from 'components/Form/Context';
7 |
8 | const DateRangePicker = React.forwardRef(({ className, noClearIcon, size, ...props }, ref) => {
9 | const { sizeControl } = useContext(Context);
10 | const sizeOri = size || sizeControl;
11 | return (
12 | }
22 | calendarIcon={}
23 | calendarClassName={classNames(
24 | 'Calendar u-marginTopExtraSmall'
25 | )}
26 | />
27 | );
28 | });
29 |
30 | DateRangePicker.displayName = 'DateRangePicker';
31 | DateRangePicker.defaultProps = {
32 | noClearIcon: false,
33 | };
34 | DateRangePicker.propTypes = {
35 | /**
36 | * Remove clear Icon
37 | */
38 | noClearIcon: PropTypes.bool,
39 | /**
40 | * DateRangePicker size variants
41 | *
42 | * Uses sizeControl from `` if not explicitly specified.
43 | * @default 'medium'
44 | * */
45 | size: PropTypes.oneOf(['small', 'medium', 'large']),
46 | };
47 |
48 | export default DateRangePicker;
49 |
--------------------------------------------------------------------------------
/src/components/Calender/TimePicker.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import TimePickerBase from 'react-time-picker/dist/entry.nostyle';
5 | import Icon from 'components/Icon';
6 | import Context from 'components/Form/Context';
7 |
8 | const TimePicker = React.forwardRef(({ className, noClearIcon, size, ...props }, ref) => {
9 | const { sizeControl } = useContext(Context);
10 | const sizeOri = size || sizeControl; return (
11 | }
21 | clockIcon={}
22 | calendarClassName={classNames(
23 | 'Calendar u-marginTopExtraSmall'
24 | )}
25 | />
26 | );
27 | });
28 |
29 | TimePicker.displayName = 'TimePicker';
30 | TimePicker.defaultProps = {
31 | noClearIcon: false,
32 | };
33 | TimePicker.propTypes = {
34 | /**
35 | * Remove clear Icon
36 | */
37 | noClearIcon: PropTypes.bool,
38 | /**
39 | * TimePicker size variants
40 | *
41 | * Uses sizeControl from `` if not explicitly specified.
42 | * @default 'medium'
43 | * */
44 | size: PropTypes.oneOf(['small', 'medium', 'large']),
45 | };
46 |
47 | export default TimePicker;
48 |
--------------------------------------------------------------------------------
/src/components/Calender/index.js:
--------------------------------------------------------------------------------
1 | export { default as Calendar } from './Calendar';
2 | export { default as DateRangePicker } from './DateRangePicker';
3 | export { default as DatePicker } from './DatePicker';
4 | export { default as TimePicker } from './TimePicker';
5 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/DayInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {
4 | getYear,
5 | getMonthHuman,
6 | getDate,
7 | getDaysInMonth,
8 | } from '@wojtekmaj/date-utils';
9 |
10 | import Input from './Input';
11 |
12 | import { isMaxDate, isMinDate } from '../shared/propTypes';
13 | import { safeMin, safeMax } from '../shared/utils';
14 |
15 | export default function DayInput({
16 | maxDate,
17 | minDate,
18 | month,
19 | year,
20 | ...otherProps
21 | }) {
22 | const currentMonthMaxDays = (() => {
23 | if (!month) {
24 | return 31;
25 | }
26 |
27 | return getDaysInMonth(new Date(year, month - 1, 1));
28 | })();
29 |
30 | function isSameMonth(date) {
31 | return date && year === getYear(date) && month === getMonthHuman(date);
32 | }
33 |
34 | const maxDay = safeMin(currentMonthMaxDays, isSameMonth(maxDate) && getDate(maxDate));
35 | const minDay = safeMax(1, isSameMonth(minDate) && getDate(minDate));
36 |
37 | return (
38 |
44 | );
45 | }
46 |
47 | const isNumberOrString = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
48 |
49 | DayInput.propTypes = {
50 | ariaLabel: PropTypes.string,
51 | className: PropTypes.string.isRequired,
52 | disabled: PropTypes.bool,
53 | itemRef: PropTypes.func,
54 | maxDate: isMaxDate,
55 | minDate: isMinDate,
56 | month: isNumberOrString,
57 | onChange: PropTypes.func,
58 | onKeyDown: PropTypes.func,
59 | onKeyUp: PropTypes.func,
60 | placeholder: PropTypes.string,
61 | required: PropTypes.bool,
62 | showLeadingZeros: PropTypes.bool,
63 | value: isNumberOrString,
64 | year: isNumberOrString,
65 | };
66 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/Input.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import mergeClassNames from 'merge-class-names';
4 | import updateInputWidth, { getFontShorthand } from 'update-input-width';
5 | import predictInputValue from '@wojtekmaj/predict-input-value';
6 |
7 | /* eslint-disable jsx-a11y/no-autofocus */
8 |
9 | function onFocus(event) {
10 | const { target } = event;
11 |
12 | requestAnimationFrame(() => target.select());
13 | }
14 |
15 | function updateInputWidthOnFontLoad(element) {
16 | if (!document.fonts) {
17 | return;
18 | }
19 |
20 | const font = getFontShorthand(element);
21 |
22 | if (!font) {
23 | return;
24 | }
25 |
26 | const isFontLoaded = document.fonts.check(font);
27 |
28 | if (isFontLoaded) {
29 | return;
30 | }
31 |
32 | function onLoadingDone() {
33 | updateInputWidth(element);
34 | }
35 |
36 | document.fonts.addEventListener('loadingdone', onLoadingDone);
37 | }
38 |
39 | function makeOnKeyPress(max) {
40 | return function onKeyPress(event) {
41 | const { key } = event;
42 |
43 | const isNumberKey = !isNaN(parseInt(key, 10));
44 | const nextValue = predictInputValue(event);
45 |
46 | if (isNumberKey && (nextValue <= max)) {
47 | return;
48 | }
49 |
50 | event.preventDefault();
51 | };
52 | }
53 |
54 | export default function Input({
55 | ariaLabel,
56 | autoFocus,
57 | className,
58 | disabled,
59 | itemRef,
60 | max,
61 | min,
62 | name,
63 | nameForClass,
64 | onChange,
65 | onKeyDown,
66 | onKeyUp,
67 | placeholder = '--',
68 | required,
69 | showLeadingZeros,
70 | step,
71 | value,
72 | }) {
73 | const hasLeadingZero = showLeadingZeros && value !== null && value < 10;
74 |
75 | return [
76 | (hasLeadingZero && 0),
77 | {
98 | updateInputWidth(event.target);
99 |
100 | if (onKeyUp) {
101 | onKeyUp(event);
102 | }
103 | }}
104 | placeholder={placeholder}
105 | ref={(ref) => {
106 | if (ref) {
107 | updateInputWidth(ref);
108 | updateInputWidthOnFontLoad(ref);
109 | }
110 |
111 | if (itemRef) {
112 | itemRef(ref, name);
113 | }
114 | }}
115 | required={required}
116 | step={step}
117 | type="text"
118 | value={value !== null ? value : ''}
119 | />,
120 | ];
121 | }
122 |
123 | Input.propTypes = {
124 | ariaLabel: PropTypes.string,
125 | className: PropTypes.string.isRequired,
126 | disabled: PropTypes.bool,
127 | itemRef: PropTypes.func,
128 | max: PropTypes.number,
129 | min: PropTypes.number,
130 | onChange: PropTypes.func,
131 | onKeyDown: PropTypes.func,
132 | onKeyUp: PropTypes.func,
133 | required: PropTypes.bool,
134 | showLeadingZeros: PropTypes.bool,
135 | step: PropTypes.number,
136 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
137 | };
138 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/MonthInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { getYear, getMonthHuman } from '@wojtekmaj/date-utils';
4 |
5 | import Input from './Input';
6 |
7 | import { isMaxDate, isMinDate } from '../shared/propTypes';
8 | import { safeMin, safeMax } from '../shared/utils';
9 |
10 | export default function MonthInput({
11 | maxDate,
12 | minDate,
13 | year,
14 | ...otherProps
15 | }) {
16 | function isSameYear(date) {
17 | return date && year === getYear(date);
18 | }
19 |
20 | const maxMonth = safeMin(12, isSameYear(maxDate) && getMonthHuman(maxDate));
21 | const minMonth = safeMax(1, isSameYear(minDate) && getMonthHuman(minDate));
22 |
23 | return (
24 |
30 | );
31 | }
32 |
33 | const isNumberOrString = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
34 |
35 | MonthInput.propTypes = {
36 | ariaLabel: PropTypes.string,
37 | className: PropTypes.string.isRequired,
38 | disabled: PropTypes.bool,
39 | itemRef: PropTypes.func,
40 | maxDate: isMaxDate,
41 | minDate: isMinDate,
42 | onChange: PropTypes.func,
43 | onKeyDown: PropTypes.func,
44 | onKeyUp: PropTypes.func,
45 | placeholder: PropTypes.string,
46 | required: PropTypes.bool,
47 | showLeadingZeros: PropTypes.bool,
48 | value: isNumberOrString,
49 | year: isNumberOrString,
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/MonthSelect.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import mergeClassNames from 'merge-class-names';
4 | import { getYear, getMonthHuman } from '@wojtekmaj/date-utils';
5 |
6 | import { formatMonth, formatShortMonth } from '../shared/dateFormatter';
7 | import { isMaxDate, isMinDate } from '../shared/propTypes';
8 | import { safeMin, safeMax } from '../shared/utils';
9 |
10 | export default function MonthSelect({
11 | ariaLabel,
12 | className,
13 | itemRef,
14 | locale,
15 | maxDate,
16 | minDate,
17 | placeholder = '--',
18 | short,
19 | value,
20 | year,
21 | ...otherProps
22 | }) {
23 | function isSameYear(date) {
24 | return date && year === getYear(date);
25 | }
26 |
27 | const maxMonth = safeMin(12, isSameYear(maxDate) && getMonthHuman(maxDate));
28 | const minMonth = safeMax(1, isSameYear(minDate) && getMonthHuman(minDate));
29 | const dates = [...Array(12)].map((el, index) => new Date(2019, index, 1));
30 | const name = 'month';
31 | const formatter = short ? formatShortMonth : formatMonth;
32 |
33 | return (
34 |
69 | );
70 | }
71 |
72 | const isNumberOrString = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
73 |
74 | MonthSelect.propTypes = {
75 | ariaLabel: PropTypes.string,
76 | className: PropTypes.string.isRequired,
77 | disabled: PropTypes.bool,
78 | itemRef: PropTypes.func,
79 | locale: PropTypes.string,
80 | maxDate: isMaxDate,
81 | minDate: isMinDate,
82 | onChange: PropTypes.func,
83 | onKeyDown: PropTypes.func,
84 | onKeyUp: PropTypes.func,
85 | placeholder: PropTypes.string,
86 | required: PropTypes.bool,
87 | short: PropTypes.bool,
88 | value: isNumberOrString,
89 | year: isNumberOrString,
90 | };
91 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/NativeInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {
4 | getYear,
5 | getISOLocalDate,
6 | getISOLocalMonth,
7 | } from '@wojtekmaj/date-utils';
8 |
9 | import { isMaxDate, isMinDate, isValueType } from '../shared/propTypes';
10 |
11 | export default function NativeInput({
12 | ariaLabel,
13 | disabled,
14 | maxDate,
15 | minDate,
16 | name,
17 | onChange,
18 | required,
19 | value,
20 | valueType,
21 | }) {
22 | const nativeInputType = (() => {
23 | switch (valueType) {
24 | case 'decade':
25 | case 'year':
26 | return 'number';
27 | case 'month':
28 | return 'month';
29 | case 'day':
30 | return 'date';
31 | default:
32 | throw new Error('Invalid valueType.');
33 | }
34 | })();
35 |
36 | const nativeValueParser = (() => {
37 | switch (valueType) {
38 | case 'century':
39 | case 'decade':
40 | case 'year':
41 | return getYear;
42 | case 'month':
43 | return getISOLocalMonth;
44 | case 'day':
45 | return getISOLocalDate;
46 | default:
47 | throw new Error('Invalid valueType.');
48 | }
49 | })();
50 |
51 | function stopPropagation(event) {
52 | event.stopPropagation();
53 | }
54 |
55 | return (
56 |
74 | );
75 | }
76 |
77 | NativeInput.propTypes = {
78 | ariaLabel: PropTypes.string,
79 | disabled: PropTypes.bool,
80 | maxDate: isMaxDate,
81 | minDate: isMinDate,
82 | name: PropTypes.string,
83 | onChange: PropTypes.func,
84 | required: PropTypes.bool,
85 | value: PropTypes.oneOfType([
86 | PropTypes.string,
87 | PropTypes.instanceOf(Date),
88 | ]),
89 | valueType: isValueType,
90 | };
91 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/DateInput/YearInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { getYear } from '@wojtekmaj/date-utils';
4 |
5 | import Input from './Input';
6 |
7 | import { isMaxDate, isMinDate, isValueType } from '../shared/propTypes';
8 | import { safeMax, safeMin } from '../shared/utils';
9 |
10 | export default function YearInput({
11 | maxDate,
12 | minDate,
13 | placeholder = '----',
14 | valueType,
15 | ...otherProps
16 | }) {
17 | const maxYear = safeMin(275760, maxDate && getYear(maxDate));
18 | const minYear = safeMax(1, minDate && getYear(minDate));
19 |
20 | const yearStep = (() => {
21 | if (valueType === 'century') {
22 | return 10;
23 | }
24 |
25 | return 1;
26 | })();
27 |
28 | return (
29 |
37 | );
38 | }
39 |
40 | const isNumberOrString = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
41 |
42 | YearInput.propTypes = {
43 | ariaLabel: PropTypes.string,
44 | className: PropTypes.string.isRequired,
45 | disabled: PropTypes.bool,
46 | itemRef: PropTypes.func,
47 | maxDate: isMaxDate,
48 | minDate: isMinDate,
49 | onChange: PropTypes.func,
50 | onKeyDown: PropTypes.func,
51 | onKeyUp: PropTypes.func,
52 | placeholder: PropTypes.string,
53 | required: PropTypes.bool,
54 | value: isNumberOrString,
55 | valueType: isValueType,
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/Divider.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default function Divider({ children }) {
5 | return (
6 |
7 | {children}
8 |
9 | );
10 | }
11 |
12 | Divider.propTypes = {
13 | children: PropTypes.node,
14 | };
15 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/dateFormatter.js:
--------------------------------------------------------------------------------
1 | import getUserLocale from 'get-user-locale';
2 |
3 | export function getFormatter(options) {
4 | return (locale, date) => date.toLocaleString(locale || getUserLocale(), options);
5 | }
6 |
7 | /**
8 | * Changes the hour in a Date to ensure right date formatting even if DST is messed up.
9 | * Workaround for bug in WebKit and Firefox with historical dates.
10 | * For more details, see:
11 | * https://bugs.chromium.org/p/chromium/issues/detail?id=750465
12 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1385643
13 | *
14 | * @param {Date} date Date.
15 | */
16 | function toSafeHour(date) {
17 | const safeDate = new Date(date);
18 | return new Date(safeDate.setHours(12));
19 | }
20 |
21 | function getSafeFormatter(options) {
22 | return (locale, date) => getFormatter(options)(locale, toSafeHour(date));
23 | }
24 |
25 | const formatMonthOptions = { month: 'long' };
26 | const formatShortMonthOptions = { month: 'short' };
27 |
28 | export const formatMonth = getSafeFormatter(formatMonthOptions);
29 | export const formatShortMonth = getSafeFormatter(formatShortMonthOptions);
30 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/dates.js:
--------------------------------------------------------------------------------
1 | import {
2 | getCenturyStart,
3 | getCenturyEnd,
4 |
5 | getDecadeStart,
6 | getDecadeEnd,
7 |
8 | getYearStart,
9 | getYearEnd,
10 |
11 | getMonthStart,
12 | getMonthEnd,
13 |
14 | getDayStart,
15 | getDayEnd,
16 | } from '@wojtekmaj/date-utils';
17 |
18 | /**
19 | * Returns the beginning of a given range.
20 | *
21 | * @param {String} rangeType Range type (e.g. 'day')
22 | * @param {Date} date Date.
23 | */
24 | export function getBegin(rangeType, date) {
25 | switch (rangeType) {
26 | case 'century': return getCenturyStart(date);
27 | case 'decade': return getDecadeStart(date);
28 | case 'year': return getYearStart(date);
29 | case 'month': return getMonthStart(date);
30 | case 'day': return getDayStart(date);
31 | default: throw new Error(`Invalid rangeType: ${rangeType}`);
32 | }
33 | }
34 |
35 | /**
36 | * Returns the end of a given range.
37 | *
38 | * @param {String} rangeType Range type (e.g. 'day')
39 | * @param {Date} date Date.
40 | */
41 | export function getEnd(rangeType, date) {
42 | switch (rangeType) {
43 | case 'century': return getCenturyEnd(date);
44 | case 'decade': return getDecadeEnd(date);
45 | case 'year': return getYearEnd(date);
46 | case 'month': return getMonthEnd(date);
47 | case 'day': return getDayEnd(date);
48 | default: throw new Error(`Invalid rangeType: ${rangeType}`);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/dates.spec.js:
--------------------------------------------------------------------------------
1 | import {
2 | getBegin,
3 | getEnd,
4 | } from './dates';
5 |
6 | describe('getBegin', () => {
7 | it('returns proper beginning of the century', () => {
8 | const date = new Date(2017, 0, 1);
9 | const beginOfCenturyDate = new Date(2001, 0, 1);
10 |
11 | const beginOfCentury = getBegin('century', date);
12 |
13 | expect(beginOfCentury).toEqual(beginOfCenturyDate);
14 | });
15 |
16 | it('returns proper beginning of the decade', () => {
17 | const date = new Date(2017, 0, 1);
18 | const beginOfDecadeDate = new Date(2011, 0, 1);
19 |
20 | const beginOfDecade = getBegin('decade', date);
21 |
22 | expect(beginOfDecade).toEqual(beginOfDecadeDate);
23 | });
24 |
25 | it('returns proper beginning of the year', () => {
26 | const date = new Date(2017, 0, 1);
27 | const beginOfYearDate = new Date(2017, 0, 1);
28 |
29 | const beginOfYear = getBegin('year', date);
30 |
31 | expect(beginOfYear).toEqual(beginOfYearDate);
32 | });
33 |
34 | it('returns proper beginning of the month', () => {
35 | const date = new Date(2017, 0, 1);
36 | const beginOfMonthDate = new Date(2017, 0, 1);
37 |
38 | const monthRange = getBegin('month', date);
39 |
40 | expect(monthRange).toEqual(beginOfMonthDate);
41 | });
42 |
43 | it('returns proper beginning of the day', () => {
44 | const date = new Date(2017, 0, 1);
45 | const beginOfDayDate = new Date(2017, 0, 1);
46 |
47 | const beginOfDay = getBegin('day', date);
48 |
49 | expect(beginOfDay).toEqual(beginOfDayDate);
50 | });
51 |
52 | it('throws an error when given unrecognized range type', () => {
53 | const date = new Date(2017, 0, 1);
54 |
55 | expect(() => getBegin('hamster', date)).toThrow();
56 | });
57 | });
58 |
59 | describe('getEnd', () => {
60 | it('returns proper end of the century', () => {
61 | const date = new Date(2017, 0, 1);
62 | const endOfCenturyDate = new Date(2100, 11, 31, 23, 59, 59, 999);
63 |
64 | const endOfCentury = getEnd('century', date);
65 |
66 | expect(endOfCentury).toEqual(endOfCenturyDate);
67 | });
68 |
69 | it('returns proper end of the decade', () => {
70 | const date = new Date(2017, 0, 1);
71 | const endOfDecadeDate = new Date(2020, 11, 31, 23, 59, 59, 999);
72 |
73 | const endOfDecade = getEnd('decade', date);
74 |
75 | expect(endOfDecade).toEqual(endOfDecadeDate);
76 | });
77 |
78 | it('returns proper end of the year', () => {
79 | const date = new Date(2017, 0, 1);
80 | const endOfYearDate = new Date(2017, 11, 31, 23, 59, 59, 999);
81 |
82 | const endOfYear = getEnd('year', date);
83 |
84 | expect(endOfYear).toEqual(endOfYearDate);
85 | });
86 |
87 | it('returns proper end of the month', () => {
88 | const date = new Date(2017, 0, 1);
89 | const endOfMonthDate = new Date(2017, 0, 31, 23, 59, 59, 999);
90 |
91 | const monthRange = getEnd('month', date);
92 |
93 | expect(monthRange).toEqual(endOfMonthDate);
94 | });
95 |
96 | it('returns proper end of the day', () => {
97 | const date = new Date(2017, 0, 1);
98 | const endOfDayDate = new Date(2017, 0, 1, 23, 59, 59, 999);
99 |
100 | const endOfDay = getEnd('day', date);
101 |
102 | expect(endOfDay).toEqual(endOfDayDate);
103 | });
104 |
105 | it('throws an error when given unrecognized range type', () => {
106 | const date = new Date(2017, 0, 1);
107 |
108 | expect(() => getEnd('hamster', date)).toThrow();
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | const allViews = ['century', 'decade', 'year', 'month'];
4 | const allValueTypes = [...allViews.slice(1), 'day'];
5 |
6 | export const isValueType = PropTypes.oneOf(allValueTypes);
7 |
8 | export const isMinDate = (props, propName, componentName) => {
9 | const { [propName]: minDate } = props;
10 |
11 | if (!minDate) {
12 | return null;
13 | }
14 |
15 | if (!(minDate instanceof Date)) {
16 | return new Error(`Invalid prop \`${propName}\` of type \`${typeof minDate}\` supplied to \`${componentName}\`, expected instance of \`Date\`.`);
17 | }
18 |
19 | const { maxDate } = props;
20 |
21 | if (maxDate && minDate > maxDate) {
22 | return new Error(`Invalid prop \`${propName}\` of type \`${typeof minDate}\` supplied to \`${componentName}\`, minDate cannot be larger than maxDate.`);
23 | }
24 |
25 | return null;
26 | };
27 |
28 | export const isMaxDate = (props, propName, componentName) => {
29 | const { [propName]: maxDate } = props;
30 |
31 | if (!maxDate) {
32 | return null;
33 | }
34 |
35 | if (!(maxDate instanceof Date)) {
36 | return new Error(`Invalid prop \`${propName}\` of type \`${typeof maxDate}\` supplied to \`${componentName}\`, expected instance of \`Date\`.`);
37 | }
38 |
39 | const { minDate } = props;
40 |
41 | if (minDate && maxDate < minDate) {
42 | return new Error(`Invalid prop \`${propName}\` of type \`${typeof maxDate}\` supplied to \`${componentName}\`, maxDate cannot be smaller than minDate.`);
43 | }
44 |
45 | return null;
46 | };
47 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Calls a function, if it's defined, with specified arguments
3 | * @param {Function} fn
4 | * @param {Object} args
5 | */
6 | export function callIfDefined(fn, ...args) {
7 | if (fn && typeof fn === 'function') {
8 | fn(...args);
9 | }
10 | }
11 |
12 | function isValidNumber(num) {
13 | return num !== null && num !== false && !Number.isNaN(Number(num));
14 | }
15 |
16 | export function safeMin(...args) {
17 | return Math.min(...args.filter(isValidNumber));
18 | }
19 |
20 | export function safeMax(...args) {
21 | return Math.max(...args.filter(isValidNumber));
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Calender/v2/DatePicker/shared/utils.spec.js:
--------------------------------------------------------------------------------
1 | import {
2 | callIfDefined,
3 | safeMin,
4 | safeMax,
5 | } from './utils';
6 |
7 | describe('callIfDefined', () => {
8 | it('calls given function if defined', () => {
9 | const fn = jest.fn();
10 | const arg1 = 'hi';
11 | const arg2 = 'hello';
12 |
13 | callIfDefined(fn, arg1, arg2);
14 |
15 | expect(fn).toHaveBeenCalledWith(arg1, arg2);
16 | });
17 | });
18 |
19 | describe('safeMin', () => {
20 | it('returns Infinity given no values', () => {
21 | const result = safeMin();
22 |
23 | expect(result).toBe(Infinity);
24 | });
25 |
26 | it('returns the smallest value given valid numbers', () => {
27 | const result = safeMin(3, 4, 5);
28 |
29 | expect(result).toBe(3);
30 | });
31 |
32 | it('returns the smallest value given valid numbers with zero', () => {
33 | const result = safeMin(0, 1, 2);
34 |
35 | expect(result).toBe(0);
36 | });
37 |
38 | it('returns the smallest value given valid number and null', () => {
39 | const result = safeMin(1, 2, null);
40 |
41 | expect(result).toBe(1);
42 | });
43 |
44 | it('returns the smallest value given valid number and undefined', () => {
45 | const result = safeMin(1, 2, undefined);
46 |
47 | expect(result).toBe(1);
48 | });
49 |
50 | it('returns the smallest value given valid numbers as strings', () => {
51 | const result = safeMin('1', '2');
52 |
53 | expect(result).toBe(1);
54 | });
55 | });
56 |
57 | describe('safeMax', () => {
58 | it('returns -Infinity given no values', () => {
59 | const result = safeMax();
60 |
61 | expect(result).toBe(-Infinity);
62 | });
63 |
64 | it('returns the largest value given valid numbers', () => {
65 | const result = safeMax(3, 4, 5);
66 |
67 | expect(result).toBe(5);
68 | });
69 |
70 | it('returns the largest value given valid numbers with zero', () => {
71 | const result = safeMax(-2, -1, 0);
72 |
73 | expect(result).toBe(0);
74 | });
75 |
76 | it('returns the largest value given valid number and null', () => {
77 | const result = safeMax(3, 4, null);
78 |
79 | expect(result).toBe(4);
80 | });
81 |
82 | it('returns the largest value given valid number and undefined', () => {
83 | const result = safeMax(3, 4, undefined);
84 |
85 | expect(result).toBe(4);
86 | });
87 |
88 | it('returns the largest value given valid numbers as strings', () => {
89 | const result = safeMax('3', '4');
90 |
91 | expect(result).toBe(4);
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/src/components/Card/index.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const Context = React.createContext();
6 | const propTypes = {
7 | /**
8 | * When this prop is set, it creates a Card with a Card.Body inside
9 | * passing the children directly to it
10 | */
11 | body: PropTypes.bool,
12 | /**
13 | * Card size variants
14 | * */
15 | size: PropTypes.oneOf(['small', 'medium', 'large']),
16 | };
17 |
18 | const defaultProps = {
19 | body: false,
20 | size: 'medium',
21 | };
22 |
23 | const sizesClassName = {
24 | small: {
25 | content: 'u-paddingExtraSmall',
26 | title: 'u-text300',
27 | },
28 | medium: {
29 | content: 'u-paddingSmall',
30 | title: 'u-text400',
31 | },
32 | large: {
33 | content: 'u-paddingMedium',
34 | title: 'u-text600',
35 | },
36 | };
37 |
38 | const Card = React.forwardRef(({ className, body, size, children, as: Component = 'div', ...props }, ref) => {
39 | const context = useMemo(() => ({ size }), [size]);
40 | return (
41 |
42 |
51 | {body ? {children} : children}
52 |
53 |
54 | );
55 | });
56 |
57 | const CardBody = React.forwardRef(({ className, as: Component = 'div', ...props }, ref) => {
58 | const { size } = useContext(Context);
59 | return (
60 |
69 | );
70 | });
71 | const CardHeader = React.forwardRef(({ className, as: Component = 'div', ...props }, ref) => {
72 | const { size } = useContext(Context);
73 | return (
74 |
83 | );
84 | });
85 | const CardTitle = React.forwardRef(({ className, as: Component = 'div', ...props }, ref) => {
86 | const { size } = useContext(Context);
87 | return (
88 |
97 | );
98 | });
99 |
100 |
101 | Card.displayName = 'Card';
102 | Card.propTypes = propTypes;
103 | Card.defaultProps = defaultProps;
104 | Card.Header = CardHeader;
105 | Card.Title = CardTitle;
106 | Card.Body = CardBody;
107 | export default Card;
108 |
--------------------------------------------------------------------------------
/src/components/Carousel/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import SlickBase from 'react-slick';
5 | import createBlock from 'utils/createBlock';
6 |
7 | const propTypes = {
8 | /**
9 | * The indicator inside content
10 | */
11 | dotInside: PropTypes.bool,
12 | /** [Define settings](https://react-slick.neostack.com/docs/api) */
13 | settings: PropTypes.object,
14 | };
15 |
16 | const defaultProps = {
17 | dotInside: false,
18 | settings: {
19 | infinite: true,
20 | dots: true,
21 | },
22 | };
23 |
24 | const Carousel = React.forwardRef(({ className, dotInside, settings, ...props }, ref) => (
25 |
35 | ));
36 |
37 | const Item = createBlock('Carousel-item u-lineHeightReset');
38 |
39 | Carousel.displayName = 'Carousel';
40 | Carousel.defaultProps = defaultProps;
41 | Carousel.propTypes = propTypes;
42 | Carousel.Item = Item;
43 | export default Carousel;
44 |
--------------------------------------------------------------------------------
/src/components/ChatBox/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import createBlock from 'utils/createBlock';
4 |
5 | const propTypes = {
6 |
7 | };
8 | const defaultProps = {
9 |
10 | };
11 |
12 | const ChatBox = React.forwardRef(({ className, ...props }, ref) => (
13 |
22 | ));
23 |
24 | const List = React.forwardRef(({ className, children, innerClassName, ...props }, ref) => (
25 |
33 |
39 | {children}
40 |
41 |
42 | ));
43 |
44 | const Context = createBlock('ChatBox-context u-positionRelative');
45 | const Notice = React.forwardRef(({ className, children, ...props }, ref) => (
46 |
54 |
55 | {children}
56 |
57 |
58 | ));
59 |
60 | const Info = createBlock('ChatBox-info');
61 | const Attachment = createBlock('ChatBox-attachment u-paddingExtraSmall u-borderTop');
62 | ChatBox.List = List;
63 | ChatBox.Attachment = Attachment;
64 | ChatBox.Info = Info;
65 | ChatBox.Context = Context;
66 | ChatBox.Notice = Notice;
67 | ChatBox.displayName = 'ChatBox';
68 | ChatBox.defaultProps = defaultProps;
69 | ChatBox.propTypes = propTypes;
70 |
71 | export default ChatBox;
72 |
--------------------------------------------------------------------------------
/src/components/Counter/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Icon from 'components/Icon';
5 |
6 | const propTypes = {
7 | /** The Counter visual variant */
8 | variant: PropTypes.oneOf([
9 | 'primary',
10 | 'secondary',
11 | 'accent',
12 | 'information',
13 | 'warning',
14 | 'positive',
15 | 'negative',
16 | 'white',
17 | ]),
18 | /** Custom label */
19 | label: PropTypes.oneOfType([
20 | PropTypes.string,
21 | PropTypes.func,
22 | ]),
23 | /** Custom number */
24 | number: PropTypes.oneOfType([
25 | PropTypes.string,
26 | PropTypes.func,
27 | ]),
28 | /** The icon to display. The name can get from Component `Icon` */
29 | iconLeft: PropTypes.oneOfType([
30 | PropTypes.string,
31 | PropTypes.func,
32 | ]),
33 | };
34 |
35 | const defaultProps = {
36 | iconLeft: 'time',
37 | variant: 'secondary',
38 | };
39 |
40 | const variantsClassName = {
41 | primary: {
42 | text: 'u-textPrimary',
43 | icon: 'u-textPrimary',
44 | },
45 | secondary: {
46 | text: 'u-textDark',
47 | icon: 'u-textGray',
48 | },
49 | accent: {
50 | text: 'u-textAccent',
51 | icon: 'u-textAccent',
52 | },
53 | warning: {
54 | text: 'u-textWarning',
55 | icon: 'u-textWarning',
56 | },
57 | information: {
58 | text: 'u-textInformation',
59 | icon: 'u-textInformation',
60 | },
61 | positive: {
62 | text: 'u-textPositive',
63 | icon: 'u-textPositive',
64 | },
65 | negative: {
66 | text: 'u-textNegative',
67 | icon: 'u-textNegative',
68 | },
69 | white: {
70 | text: 'u-textWhite',
71 | icon: 'u-textWhite',
72 | },
73 | };
74 |
75 | const Counter = React.forwardRef(({ className, children, iconLeft, label, number, variant, as: Component = 'div', ...props }, ref) => (
76 |
85 | {iconLeft && (
86 |
87 | {typeof (iconLeft) === 'function'
88 | ? iconLeft()
89 | : (
90 |
97 | )
98 | }
99 |
100 | )}
101 |
102 | {label && (
103 | typeof (label) === 'function'
104 | ? label()
105 | : (
106 |
111 | {label}
112 |
113 | )
114 |
115 | )}
116 | {number && (
117 |
118 | {typeof (number) === 'function'
119 | ? number()
120 | : (
121 |
126 | {number}
127 |
128 | )
129 | }
130 |
131 | )}
132 | {children}
133 |
134 | ));
135 |
136 |
137 | Counter.displayName = 'Counter';
138 | Counter.defaultProps = defaultProps;
139 | Counter.propTypes = propTypes;
140 | export default Counter;
141 |
--------------------------------------------------------------------------------
/src/components/Dropdown/Button.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import useMergedRefs from '@restart/hooks/useMergedRefs';
4 | import PropTypes from 'prop-types';
5 | import Button from 'components/Button';
6 | import Icon from 'components/Icon';
7 | import DropdownContext from './Context';
8 | import { useToggle } from './Toggle';
9 |
10 | const propTypes = {
11 | /** You can use a custom element type for this component. */
12 | as: PropTypes.elementType,
13 | /**
14 | * Define size of caret icon
15 | * @default 'extraSmall'
16 | * */
17 | caret: PropTypes.string,
18 | };
19 | const defaultProps = {
20 | as: Button,
21 | };
22 | const DropButton = React.forwardRef(({ className, children, caret, as: Component, ...props }, ref) => {
23 | const [toggleProps, { toggle }] = useToggle();
24 | const { drop } = useContext(DropdownContext);
25 | toggleProps.ref = useMergedRefs(toggleProps.ref, ref);
26 |
27 | return (
28 |
38 | {children}
39 | {caret && (
40 |
44 |
51 |
52 | )}
53 |
54 | );
55 | });
56 |
57 | DropButton.displayName = 'DropdownButton';
58 | DropButton.propTypes = propTypes;
59 | DropButton.defaultProps = defaultProps;
60 | export default DropButton;
61 |
--------------------------------------------------------------------------------
/src/components/Dropdown/Container.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { elementType } from 'prop-types-extra';
5 | import usePopper from 'hooks/usePopper';
6 | import useRootClose from 'hooks/useRootClose';
7 | import DropdownContext from './Context';
8 |
9 |
10 | const propTypes = {
11 | /**
12 | * You can use a custom element type for this component.
13 | * @default div
14 | * */
15 | as: PropTypes.elementType,
16 | /**
17 | * A set of popper options and props passed directly to react-popper's Popper component.
18 | */
19 | popperConfig: PropTypes.object,
20 | /** A `react-transition-group` Transition component used to animate the Message on dismissal. */
21 | transition: elementType,
22 | /** Custom style */
23 | additionalStyles: PropTypes.object,
24 | };
25 |
26 | const defaultProps = {
27 | };
28 |
29 | const Container = React.forwardRef(({ additionalStyles, ...props }, ref) => {
30 | const {
31 | className,
32 | flip,
33 | rootCloseEvent,
34 | children,
35 | popperConfig = {},
36 | as: Component = 'div',
37 | shouldUsePopper = true,
38 | transition: Transition,
39 | } = props;
40 | const context = useContext(DropdownContext);
41 |
42 | const show = context.show == null ? props.show : context.show;
43 | const alignRight = context.alignRight == null ? props.alignRight : context.alignRight;
44 | const handleClose = (e) => {
45 | if (!context.toggle) return;
46 | context.toggle(false, e);
47 | };
48 | const { drop, setContainer, containerElement, toggleElement } = context;
49 |
50 | let placement = alignRight ? 'bottom-end' : 'bottom-start';
51 | if (drop === 'up') placement = alignRight ? 'top-end' : 'top-start';
52 | else if (drop === 'right') placement = alignRight ? 'right-end' : 'right-start';
53 | else if (drop === 'left') placement = alignRight ? 'left-end' : 'left-start';
54 | const popper = usePopper(toggleElement, containerElement, {
55 | placement,
56 | enabled: !!(shouldUsePopper && show),
57 | eventsEnabled: !!show,
58 | modifiers: {
59 | flip: { enabled: !!flip },
60 | ...popperConfig.modifiers,
61 | },
62 | });
63 | const containerProps = {
64 | ref: setContainer,
65 | style: { ...popper.styles, ...additionalStyles },
66 | 'aria-labelledby': toggleElement && toggleElement.id,
67 | ...props,
68 | };
69 | useRootClose(context.containerElement, handleClose, {
70 | clickTrigger: rootCloseEvent,
71 | disabled: !(show),
72 | });
73 | const container = (
74 |
83 | {children}
84 |
85 | );
86 | if (!Transition) return show ? container : null;
87 | return (
88 |
89 | {container}
90 |
91 | );
92 | });
93 | Container.displayName = 'DropdownContainer';
94 | Container.defaultProps = defaultProps;
95 | Container.propTypes = propTypes;
96 | export default Container;
97 |
--------------------------------------------------------------------------------
/src/components/Dropdown/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const DropdownContext = React.createContext({
4 | containerRef() {},
5 | toggleRef() {},
6 | onToggle() {},
7 | show: null,
8 | drop: null,
9 | });
10 |
11 | export default DropdownContext;
12 |
--------------------------------------------------------------------------------
/src/components/Dropdown/Toggle.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, cloneElement } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 |
5 | import DropdownContext from './Context';
6 |
7 | const propTypes = {
8 | /** Set a custom element for this component */
9 | as: PropTypes.elementType,
10 |
11 | /** Children prop should only contain a single child, and is enforced as such */
12 | children: PropTypes.element.isRequired,
13 | };
14 | export function useToggle() {
15 | const { show, toggle, setToggle } = useContext(DropdownContext);
16 | return [
17 | {
18 | ref: setToggle,
19 | 'aria-haspopup': true,
20 | 'aria-expanded': !!show,
21 | },
22 | { show, toggle },
23 | ];
24 | }
25 |
26 | const Toggle = React.forwardRef(({ className, children, disabled, ...props }, ref) => {
27 | const [toggleProps, { toggle }] = useToggle();
28 |
29 | return cloneElement(children, {
30 | className: classNames(
31 | 'Dropdown-toggle',
32 | disabled ? ' u-pointerEventsNone u-cursorNotAllow' : 'u-cursorPointer',
33 | className && className
34 | ),
35 | onClick: !disabled ? toggle : null,
36 | ref,
37 | ...props,
38 | ...toggleProps,
39 | children,
40 | });
41 | });
42 |
43 | Toggle.displayName = 'DropdownToggle';
44 | Toggle.defaultProps = {};
45 | Toggle.propTypes = propTypes;
46 | export default Toggle;
47 |
--------------------------------------------------------------------------------
/src/components/Dropdown/index.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useRef, useCallback } from 'react';
2 | import classNames from 'classnames';
3 | import { useUncontrolled } from 'uncontrollable';
4 | import useCallbackRef from '@restart/hooks/useCallbackRef';
5 | import useForceUpdate from '@restart/hooks/useForceUpdate';
6 | import PropTypes from 'prop-types';
7 | import createBlock from 'utils/createBlock';
8 | import DropButton from './Button';
9 | import Toggle from './Toggle';
10 | import Container from './Container';
11 | import DropdownContext from './Context';
12 |
13 |
14 | const propTypes = {
15 | /**
16 | * You can use a custom element type for this component.
17 | * @default div
18 | * */
19 | as: PropTypes.elementType,
20 | /**
21 | * Determines the direction and location of the Menu in relation to it's Toggle.
22 | * @default 'down'
23 | * */
24 | drop: PropTypes.oneOf([
25 | 'up',
26 | 'down',
27 | 'left',
28 | 'right',
29 | ]),
30 | /**
31 | * Allow Dropdown to flip in case of an overlapping on the reference element. For more information refer to
32 | * Popper.js's flip [docs](https://popper.js.org/popper-documentation.html#modifiers..flip.enabled).
33 | * @default true
34 | */
35 | flip: PropTypes.bool,
36 | /**
37 | * Whether or not the Dropdown is visible.
38 | * @controllable onToggle
39 | * */
40 | show: PropTypes.bool,
41 | /**
42 | * Align the menu to the right side of the Dropdown toggle
43 | * @default false
44 | * */
45 | alignRight: PropTypes.bool,
46 | /**
47 | * A callback fired when the Dropdown wishes to change visibility. Called with the requested
48 | * `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
49 | *
50 | * ```js
51 | * function(
52 | * isOpen: boolean,
53 | * event: SyntheticEvent,
54 | * metadata: {
55 | * source: 'select' | 'click' | 'rootClose' | 'keydown'
56 | * }
57 | * ): void
58 | * ```
59 | *
60 | * @controllable show
61 | */
62 | onToggle: PropTypes.func,
63 | };
64 | const defaultProps = {
65 |
66 | };
67 | const Dropdown = React.forwardRef((uncontrolledProps, ref) => {
68 | const {
69 | drop,
70 | show,
71 | className,
72 | alignRight,
73 | onToggle,
74 | as: Component = 'div',
75 | ...props
76 | } = useUncontrolled(uncontrolledProps, { show: 'onToggle' });
77 | const forceUpdate = useForceUpdate();
78 | const [toggleElement, setToggle] = useCallbackRef();
79 | const containerRef = useRef();
80 | const containerElement = containerRef.current;
81 | const setContainer = useCallback(
82 | (ref) => {
83 | containerRef.current = ref;
84 | // ensure that a menu set triggers an update for consumers
85 | forceUpdate();
86 | },
87 | [forceUpdate],
88 | );
89 |
90 | const toggle = useCallback(
91 | (event) => {
92 | onToggle(!show, event);
93 | },
94 | [onToggle, show],
95 | );
96 | const context = useMemo(
97 | () => ({
98 | toggle,
99 | drop,
100 | show,
101 | alignRight,
102 | containerElement,
103 | toggleElement,
104 | setContainer,
105 | setToggle,
106 | }),
107 | [
108 | toggle,
109 | drop,
110 | show,
111 | alignRight,
112 | containerElement,
113 | toggleElement,
114 | setContainer,
115 | setToggle,
116 | ],
117 | );
118 | return (
119 |
120 |
129 |
130 | );
131 | });
132 | const Item = createBlock('Dropdown-item u-flex u-paddingHorizontalSmall u-paddingVerticalTiny hover:u-backgroundLightest');
133 | Dropdown.Item = Item;
134 | Dropdown.Container = Container;
135 | Dropdown.Button = DropButton;
136 | Dropdown.Toggle = Toggle;
137 | Dropdown.propTypes = propTypes;
138 | Dropdown.defaultProps = defaultProps;
139 | Dropdown.displayName = 'Dropdown';
140 | export default Dropdown;
141 |
--------------------------------------------------------------------------------
/src/components/EmptyState/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import createBlock from '../../utils/createBlock';
5 | import { PluginType } from '../../constants/common';
6 | import Plugins from '../../plugins';
7 |
8 | const propTypes = {
9 | /** The EmptyState visual name, should be provide via an AssetPlugin with prefix "emptyState" */
10 | name: PropTypes.string,
11 | /** Providing a `src` will render an `
` element */
12 | src: PropTypes.string,
13 | /** Providing a alt if `src` exits */
14 | alt: PropTypes.string,
15 | /** Set the width of the logo */
16 | width: PropTypes.number,
17 | /** Set the height of the logo */
18 | height: PropTypes.number,
19 | };
20 | const defaultProps = {
21 | width: 240,
22 | alt: 'EmptyState',
23 | };
24 | const EmptyState = React.forwardRef(({ className, children, name, src, fileType, alt, height, width, as: Component = 'div', ...props }, ref) => {
25 | let nameOri = name;
26 | let srcOri = src;
27 | if (srcOri) {
28 | nameOri = false;
29 | } else if (nameOri) {
30 | srcOri = Plugins
31 | .getPlugins(PluginType.ASSET)
32 | .traverseCall('getAsset', 'emptyState', nameOri)
33 | .find(asset => !!asset);
34 | }
35 | return (
36 |
45 | {srcOri && (
46 |
47 | )}
48 | {children}
49 |
50 | );
51 | });
52 |
53 | const Heading = createBlock('EmptyState-heading u-marginTopSmall u-text600 u-fontMedium u-textLight');
54 | const Description = createBlock('EmptyState-description u-marginBottomSmall u-textLight');
55 |
56 | EmptyState.Heading = Heading;
57 | EmptyState.Description = Description;
58 | EmptyState.displayName = 'EmptyState';
59 | EmptyState.defaultProps = defaultProps;
60 | EmptyState.propTypes = propTypes;
61 | export default EmptyState;
62 |
--------------------------------------------------------------------------------
/src/components/Fade/index.js:
--------------------------------------------------------------------------------
1 | //fork react-bootstrap/src/Fade.js
2 | import classNames from 'classnames';
3 | import React, { useCallback } from 'react';
4 | import PropTypes from 'prop-types';
5 | import Transition, {
6 | ENTERED,
7 | ENTERING,
8 | } from 'react-transition-group/Transition';
9 | import triggerBrowserReflow from 'utils/triggerBrowserReflow';
10 |
11 | const propTypes = {
12 | /**
13 | * Show the component; triggers the fade in or fade out animation
14 | */
15 | in: PropTypes.bool,
16 |
17 | /**
18 | * Wait until the first "enter" transition to mount the component (add it to the DOM)
19 | */
20 | mountOnEnter: PropTypes.bool,
21 |
22 | /**
23 | * Unmount the component (remove it from the DOM) when it is faded out
24 | */
25 | unmountOnExit: PropTypes.bool,
26 |
27 | /**
28 | * Run the fade in animation when the component mounts, if it is initially
29 | * shown
30 | */
31 | appear: PropTypes.bool,
32 |
33 | /**
34 | * Duration of the fade animation in milliseconds, to ensure that finishing
35 | * callbacks are fired even if the original browser transition end events are
36 | * canceled
37 | */
38 | timeout: PropTypes.number,
39 |
40 | /**
41 | * Callback fired before the component fades in
42 | */
43 | onEnter: PropTypes.func,
44 | /**
45 | * Callback fired after the component starts to fade in
46 | */
47 | onEntering: PropTypes.func,
48 | /**
49 | * Callback fired after the has component faded in
50 | */
51 | onEntered: PropTypes.func,
52 | /**
53 | * Callback fired before the component fades out
54 | */
55 | onExit: PropTypes.func,
56 | /**
57 | * Callback fired after the component starts to fade out
58 | */
59 | onExiting: PropTypes.func,
60 | /**
61 | * Callback fired after the component has faded out
62 | */
63 | onExited: PropTypes.func,
64 | };
65 |
66 | const defaultProps = {
67 | in: false,
68 | timeout: 300,
69 | mountOnEnter: false,
70 | unmountOnExit: false,
71 | appear: false,
72 | };
73 |
74 | const fadeStyles = {
75 | [ENTERING]: 'Show js-entering',
76 | [ENTERED]: 'Show js-entered',
77 | };
78 |
79 | const Fade = React.forwardRef(({ className, children, ...props }, ref) => {
80 | const handleEnter = useCallback(
81 | (node) => {
82 | triggerBrowserReflow(node);
83 | if (props.onEnter) props.onEnter(node);
84 | },
85 | [props],
86 | );
87 |
88 | return (
89 |
95 | {(status, innerProps) => React.cloneElement(children, {
96 | ...innerProps,
97 | className: classNames(
98 | 'Fade',
99 | className,
100 | children.props.className,
101 | fadeStyles[status],
102 | ),
103 | })
104 | }
105 |
106 | );
107 | });
108 |
109 | Fade.propTypes = propTypes;
110 | Fade.defaultProps = defaultProps;
111 | Fade.displayName = 'Fade';
112 |
113 | export default Fade;
114 |
--------------------------------------------------------------------------------
/src/components/Form/Check.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import warning from 'warning';
5 | import Context from './Context';
6 |
7 | const propTypes = {
8 | /**
9 | * The type of checkable.
10 | * @type {('radio' | 'checkbox' | 'checkbox_button')}
11 | */
12 | type: PropTypes.oneOf(['radio', 'checkbox', 'checkbox_button']).isRequired,
13 | /** A HTML id attribute, necessary for proper form accessibility. */
14 | id: PropTypes.string.isRequired,
15 | /** Custom label */
16 | label: PropTypes.oneOfType([
17 | PropTypes.string,
18 | PropTypes.func,
19 | ]),
20 | /**
21 | * Render inline ``
22 | * @default false
23 | * */
24 | inline: PropTypes.bool,
25 | /**
26 | * Add "valid" validation styles to the control
27 | * @default false
28 | * */
29 | isValid: PropTypes.bool,
30 | /**
31 | * Add "invalid" validation styles to the control and accompanying label
32 | * @default false
33 | * */
34 | isInvalid: PropTypes.bool,
35 | /**
36 | * The underlying HTML element to use when rendering the Form.Check.
37 | * @default input
38 | */
39 | /**
40 | * Input size variants
41 | * @default 'medium'
42 | * */
43 | sizeInput: PropTypes.oneOf([
44 | 'small',
45 | 'medium',
46 | 'large',
47 | ]),
48 | as: PropTypes.elementType,
49 | };
50 |
51 | const defaultProps = {
52 | // eslint-disable-next-line react/default-props-match-prop-types
53 | type: 'checkbox',
54 | };
55 |
56 | const Check = React.forwardRef(({ className, sizeInput, type, id, label, inline, isValid, isInvalid, disabled, as: Component = 'div', ...props }, ref) => {
57 | const { controlId, disabledControl, sizeControl } = useContext(Context);
58 |
59 | warning(
60 | controlId == null || !id,
61 | '`controlId` is ignored on `` when `id` is specified.',
62 | );
63 | const disabledOri = disabled || disabledControl;
64 | const sizeInputSet = sizeInput || sizeControl;
65 | const idSet = id || controlId;
66 | return (
67 |
82 |
90 |
103 |
104 | );
105 | });
106 |
107 | Check.displayName = 'FormCheck';
108 | Check.defaultProps = defaultProps;
109 | Check.propTypes = propTypes;
110 | export default Check;
111 |
--------------------------------------------------------------------------------
/src/components/Form/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext(
4 | {
5 | controlId: undefined,
6 | sizeControl: undefined,
7 | requiredControl: undefined,
8 | disabledControl: undefined,
9 | });
10 |
11 | export default Context;
12 |
--------------------------------------------------------------------------------
/src/components/Form/Feedback.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** Specify whether the feedback is for valid or invalid fields */
7 | type: PropTypes.oneOf(['valid', 'invalid']),
8 | /**
9 | * Set Form.Feedback visible
10 | */
11 | visible: PropTypes.bool,
12 | /**
13 | * You can use a custom element type for this component.
14 | * @default div
15 | * */
16 | as: PropTypes.elementType,
17 | };
18 |
19 | const defaultProps = {
20 | type: 'valid',
21 | visible: false,
22 | };
23 |
24 | const Feedback = React.forwardRef(({ className, type, visible, as: Component = 'div', ...props }, ref) => (
25 |
35 | ));
36 | Feedback.displayName = 'FormFeedback';
37 | Feedback.propTypes = propTypes;
38 | Feedback.defaultProps = defaultProps;
39 |
40 | export default Feedback;
41 |
--------------------------------------------------------------------------------
/src/components/Form/File.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import warning from 'warning';
5 | import Context from './Context';
6 |
7 | const propTypes = {
8 | /**
9 | * The underlying HTML element to use when rendering the Form.File.
10 | * @default div
11 | * */
12 | as: PropTypes.elementType,
13 | /** Uses controlId from `` if not explicitly specified. */
14 | id: PropTypes.string,
15 | /**
16 | * Uses sizeControl from `` if not explicitly specified.
17 | * @default 'medium'
18 | * */
19 | sizeInput: PropTypes.oneOf([
20 | 'small',
21 | 'medium',
22 | 'large',
23 | ]),
24 | /** File name */
25 | fileName: PropTypes.string,
26 | /** Custom browse button text */
27 | browseText: PropTypes.string,
28 | /**
29 | * Add "invalid" validation styles to the control and accompanying label
30 | * @default false
31 | * */
32 | isValid: PropTypes.bool,
33 | /**
34 | * Add "valid" validation styles to the control
35 | * @default false
36 | * */
37 | isInvalid: PropTypes.bool,
38 | /** Remove border all state */
39 | isBorderNone: PropTypes.bool,
40 | /** Reset background to transparent*/
41 | isBackgroundReset: PropTypes.bool,
42 | /** The underlying HTML element to use when rendering the Form.Input. */
43 | placeholder: PropTypes.string,
44 | };
45 |
46 | const defaultProps = {
47 | browseText: 'Browse',
48 | placeholder: '',
49 | };
50 |
51 | const File = React.forwardRef(({ className, sizeInput, id, fileName, placeholder, browseText, isValid, isInvalid, isBorderNone, isBackgroundReset, as: Component = 'div', ...props }, ref) => {
52 | const { sizeControl, controlId } = useContext(Context);
53 | const sizeInputSet = sizeInput || sizeControl;
54 | const idSet = id || controlId;
55 |
56 | warning(
57 | controlId == null || !id,
58 | '`controlId` is ignored on `` when `id` is specified.',
59 | );
60 | return (
61 |
76 |
83 |
96 |
97 |
98 | );
99 | });
100 |
101 | File.displayName = 'FormFile';
102 | File.defaultProps = defaultProps;
103 | File.propTypes = propTypes;
104 | export default File;
105 |
--------------------------------------------------------------------------------
/src/components/Form/Group.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Context from './Context';
5 |
6 | const propTypes = {
7 | /** Sets id on `` and htmlFor on ``. */
8 | controlId: PropTypes.string,
9 | /**
10 | * Sets sizeInput on `` and sizeLabel on ``.
11 | * @default 'medium'
12 | * @type {('small'|'medium'|'large')}
13 | * */
14 | sizeControl: PropTypes.string,
15 | /**
16 | * Sets required on ``.
17 | * @default false
18 | * */
19 | requiredControl: PropTypes.bool,
20 | /**
21 | * You can use a custom element type for this component.
22 | * @default div
23 | * */
24 | as: PropTypes.elementType,
25 | };
26 |
27 | const defaultProps = {
28 | };
29 |
30 | const Group = React.forwardRef(({ className, controlId, sizeControl, disabledControl, requiredControl, as: Component = 'div', ...props }, ref) => {
31 | const context = useMemo(() => ({ controlId, sizeControl, requiredControl, disabledControl }), [controlId, sizeControl, requiredControl, disabledControl]);
32 | return (
33 |
34 |
43 |
44 | );
45 | }
46 | );
47 | Group.displayName = 'FormGroup';
48 | Group.propTypes = propTypes;
49 | Group.defaultProps = defaultProps;
50 | export default Group;
51 |
--------------------------------------------------------------------------------
/src/components/Form/Input.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import warning from 'warning';
5 | import Context from './Context';
6 |
7 | const propTypes = {
8 | /** The HTML input type, which is only relevant if as is 'input' (the default). */
9 | type: PropTypes.string,
10 | /**
11 | * The `value` attribute of underlying input
12 | *
13 | * @controllable onChange
14 | * */
15 | value: PropTypes.oneOfType([
16 | PropTypes.number,
17 | PropTypes.string,
18 | ]),
19 | /** Uses controlId from `` if not explicitly specified. */
20 | id: PropTypes.string,
21 | /**
22 | * Make the control disabled
23 | * @default false
24 | * */
25 | disabled: PropTypes.bool,
26 | /**
27 | * Uses requiredControl from `` if not explicitly specified.
28 | * @default false
29 | * */
30 | required: PropTypes.bool,
31 | /**
32 | * Make the control readonly
33 | * @default false
34 | * */
35 | readOnly: PropTypes.bool,
36 | /**
37 | * Input size variants
38 | * @default 'medium'
39 | * */
40 | sizeInput: PropTypes.oneOf([
41 | 'small',
42 | 'medium',
43 | 'large',
44 | ]),
45 | /**
46 | * Add "valid" validation styles to the control
47 | * @default false
48 | * */
49 | isValid: PropTypes.bool,
50 | /**
51 | * Add "invalid" validation styles to the control and accompanying label
52 | * @default false
53 | * */
54 | isInvalid: PropTypes.bool,
55 | /** Remove border all state */
56 | isBorderNone: PropTypes.bool,
57 |
58 | /** Reset background to transparent*/
59 | isBackgroundReset: PropTypes.bool,
60 | /**
61 | * The underlying HTML element to use when rendering the FormControl.
62 | *
63 | * @type {('input'|'textarea')}
64 | * @default input
65 | */
66 | as: PropTypes.elementType,
67 | };
68 |
69 | const defaultProps = {
70 | };
71 | const Input = React.forwardRef(({ className, sizeInput, required, id, type, disabled, isValid, isInvalid, isBorderNone, isBackgroundReset, as: Component = 'input', ...props }, ref) => {
72 | const { controlId, sizeControl, requiredControl, disabledControl } = useContext(Context);
73 | warning(
74 | controlId == null || !id,
75 | '`controlId` is ignored on `` when `id` is specified.',
76 | );
77 | const sizeInputSet = sizeInput || sizeControl;
78 | const requiredSet = required || requiredControl;
79 | const disabledOri = disabled || disabledControl;
80 | return (
81 |
100 | );
101 | });
102 | Input.displayName = 'FormInput';
103 | Input.propTypes = propTypes;
104 | Input.defaultProps = defaultProps;
105 | export default Input;
106 |
--------------------------------------------------------------------------------
/src/components/Form/InputGroup.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import createBlock from 'utils/createBlock';
5 | import Context from './Context';
6 |
7 | const propTypes = {
8 | /**
9 | * You can use a custom element type for this component.
10 | * @default div
11 | * */
12 | as: PropTypes.elementType,
13 | };
14 |
15 | const defaultProps = {
16 | };
17 |
18 | const InputGroup = React.forwardRef(({ className, as: Component = 'div', ...props }, ref) => {
19 | const { sizeControl } = useContext(Context);
20 | return (
21 |
31 | );
32 | });
33 |
34 | const Append = createBlock('FormInputGroup-append u-flex');
35 | const Prepend = createBlock('FormInputGroup-prepend u-flex ');
36 | const Text = createBlock('FormInputGroup-text u-flex u-alignItemsCenter u-textGray u-textCenter u-backgroundLightest u-textNoWrap');
37 |
38 | InputGroup.Text = Text;
39 | InputGroup.Append = Append;
40 | InputGroup.Prepend = Prepend;
41 | InputGroup.displayName = 'FormInputGroup';
42 | InputGroup.defaultProps = defaultProps;
43 | InputGroup.propTypes = propTypes;
44 | export default InputGroup;
45 |
--------------------------------------------------------------------------------
/src/components/Form/Label.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import warning from 'warning';
5 | import Context from './Context';
6 |
7 | const propTypes = {
8 | /**
9 | * Uses sizeControl from `` if not explicitly specified.
10 | * @default 'medium'
11 | * */
12 | sizeLabel: PropTypes.oneOf([
13 | 'small',
14 | 'medium',
15 | 'large',
16 | ]),
17 | /**
18 | * Uses requiredId from `` if not explicitly specified.
19 | * @default false
20 | * */
21 | required: PropTypes.bool,
22 | /** Uses controlId from `` if not explicitly specified. */
23 | htmlFor: PropTypes.string,
24 | };
25 |
26 | const defaultProps = {
27 | };
28 |
29 | const labelSizes = {
30 | small: 'u-text200',
31 | medium: '',
32 | large: '',
33 | };
34 | const Label = React.forwardRef(({ className, sizeLabel, required, htmlFor, as: Component = 'label', ...props }, ref) => {
35 | const { controlId, sizeControl, requiredControl } = useContext(Context);
36 |
37 | warning(
38 | controlId == null || !htmlFor,
39 | '`controlId` is ignored on `` when `htmlFor` is specified.',
40 | );
41 | const htmlForSet = htmlFor || controlId;
42 | const requiredSet = required || requiredControl;
43 | const sizeLabelSet = sizeLabel || sizeControl;
44 | return (
45 |
58 | );
59 | });
60 | Label.displayName = 'FormLabel';
61 | Label.propTypes = propTypes;
62 | Label.defaultProps = defaultProps;
63 |
64 | export default Label;
65 |
--------------------------------------------------------------------------------
/src/components/Form/Select.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import warning from 'warning';
5 | import Icon from 'components/Icon';
6 | import Context from './Context';
7 |
8 | const propTypes = {
9 | /**
10 | * The `value` attribute of underlying input
11 | *
12 | * @controllable onChange
13 | * */
14 | value: PropTypes.oneOfType([
15 | PropTypes.number,
16 | PropTypes.string,
17 | ]),
18 | /** Uses controlId from `` if not explicitly specified. */
19 | id: PropTypes.string,
20 | /**
21 | * Make the control disabled
22 | * @default false
23 | * */
24 | disabled: PropTypes.bool,
25 | /**
26 | * Uses requiredControl from `` if not explicitly specified.
27 | * @default false
28 | * */
29 | required: PropTypes.bool,
30 | /**
31 | * Input size variants
32 | * @default 'medium'
33 | * */
34 | sizeInput: PropTypes.oneOf([
35 | 'small',
36 | 'medium',
37 | 'large',
38 | ]),
39 | /**
40 | * Add "valid" validation styles to the control
41 | * @default false
42 | * */
43 | isValid: PropTypes.bool,
44 | /**
45 | * Add "invalid" validation styles to the control and accompanying label
46 | * @default false
47 | * */
48 | isInvalid: PropTypes.bool,
49 | /** Remove border all state */
50 | isBorderNone: PropTypes.bool,
51 | /** Reset background to transparent*/
52 | isBackgroundReset: PropTypes.bool,
53 | };
54 |
55 | const defaultProps = {
56 | };
57 | const Select = React.forwardRef(({ className, sizeInput, required, multiple, id, type, disabled, isValid, isInvalid, isBorderNone, isBackgroundReset, as: Component = 'div', ...props }, ref) => {
58 | const { controlId, sizeControl, requiredControl, disabledControl } = useContext(Context);
59 | warning(
60 | controlId == null || !id,
61 | '`controlId` is ignored on `` when `id` is specified.',
62 | );
63 | const sizeInputSet = sizeInput || sizeControl;
64 | const requiredSet = required || requiredControl;
65 | const disabledOri = disabled || disabledControl;
66 | const [isFocus, setFocus] = useState(false);
67 | return (
68 |
84 |
114 | );
115 | });
116 | Select.displayName = 'FormSelect';
117 | Select.propTypes = propTypes;
118 | Select.defaultProps = defaultProps;
119 | export default Select;
120 |
--------------------------------------------------------------------------------
/src/components/Form/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Check from './Check';
3 | import File from './File';
4 | import Input from './Input';
5 | import Label from './Label';
6 | import Feedback from './Feedback';
7 | import Group from './Group';
8 | import InputGroup from './InputGroup';
9 | import Select from './Select';
10 |
11 |
12 | const Form = React.forwardRef((props, ref) => (
13 |
17 | ));
18 | Form.Group = Group;
19 | Form.Check = Check;
20 | Form.File = File;
21 | Form.Input = Input;
22 | Form.InputGroup = InputGroup;
23 | Form.Label = Label;
24 | Form.Feedback = Feedback;
25 | Form.displayName = 'Form';
26 | Form.Select = Select;
27 | export default Form;
28 |
--------------------------------------------------------------------------------
/src/components/Header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import createBlock from 'utils/createBlock';
5 |
6 | const propTypes = {
7 | /** Whether or not the Header is visible. */
8 | show: PropTypes.bool,
9 | /** Custom className for Inner */
10 | innerClassName: PropTypes.string,
11 | };
12 | const defaultProps = {
13 | show: true,
14 | };
15 |
16 | const Header = React.forwardRef(({ className, innerClassName, fullWidth, show, children, ...props }, ref) => {
17 | if (!show) return null;
18 | return (
19 |
28 |
33 |
38 | {children}
39 |
40 |
41 |
42 | );
43 | });
44 |
45 | const Brand = createBlock('Header-brand u-lineHeightReset u-fontSizeNone u-flexShrink0 u-marginRightSmall lg:u-marginRightMedium xl:u-marginRightLarge');
46 | const Main = createBlock('Header-main u-flexGrow1 u-flex u-alignItemsCenter');
47 | const Left = createBlock('Header-left u-flex');
48 | const Right = createBlock('Header-right u-flex u-alignItemsCenter u-marginLeftAuto');
49 | const AbsoluteCenter = createBlock('Header-center u-flex u-positionAbsolute u-positionCenter');
50 |
51 | Header.Left = Left;
52 | Header.AbsoluteCenter = AbsoluteCenter;
53 | Header.Right = Right;
54 | Header.Brand = Brand;
55 | Header.Main = Main;
56 | Header.displayName = 'Header';
57 | Header.defaultProps = defaultProps;
58 | Header.propTypes = propTypes;
59 |
60 | export default Header;
61 |
--------------------------------------------------------------------------------
/src/components/Loader/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /**
7 | * Loader size variants
8 | * @default 'medium'
9 | * */
10 | size: PropTypes.oneOf(['small', 'medium', 'large']),
11 | /** Set the duration of the animation skeleton */
12 | duration: PropTypes.number,
13 | };
14 |
15 | const defaultProps = {
16 | duration: 2000,
17 | };
18 |
19 | const sizes = {
20 | small: 16,
21 | medium: 32,
22 | large: 64,
23 | };
24 |
25 | const Loader = React.forwardRef(({ className, size, duration, ...props }, ref) => {
26 | const styles = {
27 | width: sizes[size] || sizes.medium,
28 | height: sizes[size] || sizes.medium,
29 | animationDuration: `${duration}ms`,
30 | };
31 | return (
32 |
43 |
44 | );
45 | });
46 |
47 | Loader.displayName = 'Loader';
48 | Loader.defaultProps = defaultProps;
49 | Loader.propTypes = propTypes;
50 | export default Loader;
51 |
--------------------------------------------------------------------------------
/src/components/Logo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import { PluginType } from 'constants/common';
5 | import Plugins from 'plugins';
6 |
7 | const propTypes = {
8 | /** The Logo visual name, should be provide via an AssetPlugin with prefix "logo" */
9 | name: PropTypes.string,
10 | /** Providing a `src` will render an `
` element */
11 | src: PropTypes.string,
12 | /** Providing a alt if `src` exits */
13 | alt: PropTypes.string,
14 | /** Set the width of the logo */
15 | width: PropTypes.number,
16 | /** Set the height of the logo */
17 | height: PropTypes.number,
18 | };
19 | const defaultProps = {
20 | alt: 'Logo',
21 | };
22 | const Logo = React.forwardRef(({ className, name, src, alt, height, width, as: Component = 'div', ...props }, ref) => {
23 | let nameOri = name;
24 | let srcOri = src;
25 | if (srcOri) {
26 | nameOri = false;
27 | } else if (nameOri) {
28 | srcOri = Plugins
29 | .getPlugins(PluginType.ASSET)
30 | .traverseCall('getAsset', 'logo', nameOri)
31 | .find(asset => !!asset);
32 | }
33 | return (
34 |
43 | {srcOri && (
44 |
45 | )}
46 |
47 | );
48 | });
49 |
50 |
51 | Logo.displayName = 'Logo';
52 | Logo.defaultProps = defaultProps;
53 | Logo.propTypes = propTypes;
54 | export default Logo;
55 |
--------------------------------------------------------------------------------
/src/components/Media/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** Set the aspect ration of the embed */
7 | aspectRatio: PropTypes.oneOf([
8 | 'square',
9 | 'classic',
10 | 'wide',
11 | 'cinema',
12 | ]),
13 | /** You can use a custom element type for this component. */
14 | as: PropTypes.elementType,
15 | };
16 | const defaultProps = {
17 | aspectRatio: 'wide',
18 | as: 'embed',
19 | };
20 | const aspectRatios = {
21 | square: 'Media--1by1',
22 | classic: 'Media--4by3',
23 | wide: 'Media--16by9',
24 | cinema: 'Media--21by9',
25 | };
26 |
27 | const Media = React.forwardRef(({ className, aspectRatio, width, height, style, as: Component = 'embed', ...props }, ref) => {
28 | const mergeStyle = {
29 | ...props.style,
30 | width: width || undefined,
31 | height: height || undefined,
32 | };
33 | return (
34 |
43 |
50 |
51 | );
52 | });
53 |
54 | Media.displayName = 'Media';
55 | Media.defaultProps = defaultProps;
56 | Media.propTypes = propTypes;
57 | export default Media;
58 |
--------------------------------------------------------------------------------
/src/components/Message/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext({ variant: undefined, type: undefined });
4 | export default Context;
5 |
--------------------------------------------------------------------------------
/src/components/Message/Title.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import { messagesVariants } from 'constants/messages';
4 | import Context from './Context';
5 |
6 | const propTypes = {
7 | };
8 |
9 | const defaultProps = {
10 | };
11 |
12 | export const Title = React.forwardRef(({ className, children, ...props }, ref) => {
13 | const { variant, type } = useContext(Context);
14 | const variantOri = messagesVariants.find(item => item.type === type && item.id === variant);
15 | return (
16 |
26 | {children}
27 |
28 | );
29 | });
30 |
31 | Title.displayName = 'MessagesTitle';
32 | Title.propTypes = propTypes;
33 | Title.defaultProps = defaultProps;
34 | export default Title;
35 |
--------------------------------------------------------------------------------
/src/components/Modal/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ModalContext = React.createContext({ onHide() {} });
4 |
5 | export default ModalContext;
6 |
--------------------------------------------------------------------------------
/src/components/Modal/Header.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import useEventCallback from '@restart/hooks/useEventCallback';
5 | import Icon from 'components/Icon';
6 | import ModalContext from './Context';
7 |
8 |
9 | const propTypes = {
10 | /** Specify whether the Component should contain a close button */
11 | closeButton: PropTypes.bool,
12 | /** A Callback fired when the close button is clicked. If used directly inside a Modal component, the onHide will automatically be propagated up to the parent Modal onHide. */
13 | onHide: PropTypes.func,
14 | };
15 |
16 | const defaultProps = {
17 | closeButton: false,
18 | };
19 |
20 | const Header = React.forwardRef(({ className, children, closeButton, onHide, ...props }, ref) => {
21 | const context = useContext(ModalContext);
22 | const [closeHover, setCloseHover] = useState(false);
23 | const handleClick = useEventCallback(() => {
24 | if (context) context.onHide();
25 | if (onHide) onHide();
26 | });
27 |
28 | return (
29 |
34 | {children}
35 | {closeButton && (
36 |
51 | )}
52 |
53 | );
54 | });
55 |
56 | Header.displayName = 'ModalHeader';
57 | Header.defaultProps = defaultProps;
58 | Header.propTypes = propTypes;
59 | export default Header;
60 |
--------------------------------------------------------------------------------
/src/components/Modal/Inside.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 |
4 | const Inside = React.forwardRef(({ children, ...props }, ref) => (
5 |
15 | ));
16 | Inside.defaultProps = {};
17 | Inside.displayName = 'ModalInside';
18 | Inside.propTypes = {};
19 |
20 | export default Inside;
21 |
--------------------------------------------------------------------------------
/src/components/MultiSteps/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Item from './Item';
5 |
6 | const propTypes = {
7 | /**
8 | * Defines the current active step index.
9 | * @controllable onChange
10 | * */
11 | current: PropTypes.number,
12 | /** Custom current label */
13 | currentLabel: PropTypes.string,
14 | /**
15 | * Callback fired when the current active step changes.
16 | * @controllable current
17 | * */
18 | onChange: PropTypes.func,
19 | /** Define direction of the MultiSteps */
20 | direction: PropTypes.oneOf([
21 | 'horizontal',
22 | 'vertical',
23 | ]),
24 | /**
25 | * The MultiSteps visual variant
26 | */
27 | variant: PropTypes.oneOf([
28 | 'primary',
29 | 'accent',
30 | 'positive',
31 | 'warning',
32 | 'negative',
33 | 'white',
34 | ]),
35 | };
36 |
37 | const defaultProps = {
38 | direction: 'horizontal',
39 | variant: 'primary',
40 | };
41 |
42 | const MultiSteps = React.forwardRef(({ className, current, currentLabel, children, variant, direction, onChange, as: Component = 'div', ...props }, ref) => {
43 | const numChildren = React.Children.count(children);
44 | const modifiedChildren = React.Children.map(children, (child, index) => {
45 | if (!child) {
46 | return null;
47 | }
48 | return React.cloneElement(
49 | child, ({
50 | className: direction !== 'vertical' && `u-size${numChildren / numChildren}of${numChildren}`,
51 | style: direction === 'vertical' ? { height: `calc(100% * 1/${numChildren})` } : null,
52 | isLast: index === numChildren - 1,
53 | isCompleted: index < current,
54 | isActive: index === current,
55 | step: index + 1,
56 | onClick: () => onChange(index),
57 | currentLabel,
58 | direction,
59 | variant,
60 | })
61 | );
62 | });
63 | return (
64 |
74 | {modifiedChildren}
75 |
76 | );
77 | });
78 |
79 | MultiSteps.displayName = 'MultiSteps';
80 | MultiSteps.defaultProps = defaultProps;
81 | MultiSteps.propTypes = propTypes;
82 | MultiSteps.Item = Item;
83 | export default MultiSteps;
84 |
--------------------------------------------------------------------------------
/src/components/PageLayout/Body.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import Context from './Context';
4 |
5 |
6 | export const PageLayoutBody = React.forwardRef(({ className, children, ...props }, ref) => {
7 | const { bodyProps } = useContext(Context);
8 | const mergeProps = {
9 | ref,
10 | ...bodyProps,
11 | ...props,
12 | };
13 | return (
14 |
23 | {children}
24 |
25 | );
26 | });
27 |
28 | PageLayoutBody.displayName = 'PageLayout.Body';
29 | PageLayoutBody.propTypes = {};
30 | PageLayoutBody.defaultProps = {};
31 | export default PageLayoutBody;
32 |
--------------------------------------------------------------------------------
/src/components/PageLayout/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext();
4 | export default Context;
5 |
--------------------------------------------------------------------------------
/src/components/PageLayout/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import Context from './Context';
4 |
5 |
6 | export const PageLayoutFooter = React.forwardRef(({ className, children, ...props }, ref) => {
7 | const { footerProps } = useContext(Context);
8 | const mergeProps = {
9 | ref,
10 | ...footerProps,
11 | ...props,
12 | };
13 | return (
14 |
22 |
23 | {children}
24 |
25 | );
26 | });
27 |
28 | PageLayoutFooter.displayName = 'PageLayout.Footer';
29 | PageLayoutFooter.propTypes = {};
30 | PageLayoutFooter.defaultProps = {};
31 | export default PageLayoutFooter;
32 |
--------------------------------------------------------------------------------
/src/components/PageLayout/Header.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import Context from './Context';
4 |
5 |
6 | export const PageLayoutHeader = React.forwardRef(({ className, children, ...props }, ref) => {
7 | const { headerProps } = useContext(Context);
8 | const mergeProps = {
9 | ref,
10 | ...headerProps,
11 | ...props,
12 | };
13 | return (
14 |
22 | {children}
23 |
24 | );
25 | });
26 |
27 | PageLayoutHeader.displayName = 'PageLayout.Header';
28 | PageLayoutHeader.propTypes = {};
29 | PageLayoutHeader.defaultProps = {};
30 | export default PageLayoutHeader;
31 |
--------------------------------------------------------------------------------
/src/components/PageLayout/index.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import Context from './Context';
5 | import PageLayoutHeader from './Header';
6 | import PageLayoutBody from './Body';
7 | import PageLayoutFooter from './Footer';
8 |
9 | const propTypes = {
10 | /**
11 | * Header props
12 | */
13 | headerProps: PropTypes.object,
14 | /**
15 | * Body props
16 | */
17 | bodyProps: PropTypes.object,
18 | /**
19 | * Footer props
20 | */
21 | footerProps: PropTypes.object,
22 | };
23 |
24 | const defaultProps = {
25 | headerProps: {},
26 | bodyProps: {},
27 | footerProps: {},
28 | };
29 |
30 | const PageLayout = React.forwardRef(({ children, className, headerProps, bodyProps, footerProps, ...props }, ref) => {
31 | const context = useMemo(() => ({
32 | headerProps,
33 | bodyProps,
34 | footerProps,
35 | }), [
36 | headerProps,
37 | bodyProps,
38 | footerProps]);
39 | return (
40 |
41 |
50 | {children}
51 |
52 |
53 | );
54 | });
55 |
56 | PageLayout.displayName = 'PageLayout';
57 | PageLayout.defaultProps = defaultProps;
58 | PageLayout.propTypes = propTypes;
59 | PageLayout.Header = PageLayoutHeader;
60 | PageLayout.Body = PageLayoutBody;
61 | PageLayout.Footer = PageLayoutFooter;
62 | export default PageLayout;
63 |
--------------------------------------------------------------------------------
/src/components/Pagination/Item.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Button from 'components/Button';
5 | import Icon from 'components/Icon';
6 | import SafeAnchor from 'components/SafeAnchor';
7 |
8 | const propTypes = {
9 | /** Styles PageItem as active, and renders a `` instead of an ``. */
10 | active: PropTypes.bool,
11 | /** Disables the PageItem */
12 | disabled: PropTypes.bool,
13 | };
14 |
15 | const defaultProps = {
16 | active: false,
17 | disabled: false,
18 | };
19 |
20 | export const PageItem = React.forwardRef(({ className, active, safeItem, disabled, children, displayName, ...props }, ref) => {
21 | let variant = active ? 'primary' : 'secondary';
22 | if (safeItem) {
23 | variant = 'default';
24 | }
25 | const Component = active || disabled || safeItem ? 'span' : SafeAnchor;
26 | return (
27 |
35 |
36 |
45 |
46 |
47 | );
48 | });
49 |
50 | PageItem.displayName = 'PaginationItem';
51 | PageItem.propTypes = propTypes;
52 | PageItem.defaultProps = defaultProps;
53 |
54 | function createButton(name, defaultValue, safeItem, className) {
55 | return class extends React.Component {
56 | static displayName = name;
57 |
58 | render() {
59 | const { children, ...props } = this.props;
60 | delete props.active;
61 | return (
62 |
63 | {children || defaultValue}
64 |
65 | );
66 | }
67 | };
68 | }
69 | export const Prev = createButton('Prev', , false, 'Pagination-prev');
70 | export const Ellipsis = createButton('Ellipsis', , true);
71 | export const Next = createButton('Next', , false, 'Pagination-next');
72 |
--------------------------------------------------------------------------------
/src/components/Pagination/index.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Context from 'components/Form/Context';
5 | import { Prev, Next, Ellipsis, PageItem } from './Item';
6 |
7 | const propTypes = {
8 | /** PageItem size variants */
9 | sizeControl: PropTypes.oneOf(['small', 'medium', 'large']),
10 | };
11 |
12 |
13 | const defaultProps = {
14 | sizeControl: 'small',
15 | };
16 |
17 | const Pagination = React.forwardRef(({ className, sizeControl, as: Component = 'ul', ...props }, ref) => {
18 | const context = useMemo(() => ({ sizeControl }), [sizeControl]);
19 | return (
20 |
21 |
30 |
31 | );
32 | }
33 | );
34 |
35 | Pagination.Prev = Prev;
36 | Pagination.Next = Next;
37 | Pagination.Ellipsis = Ellipsis;
38 | Pagination.Item = PageItem;
39 | Pagination.displayName = 'Pagination';
40 | Pagination.propTypes = propTypes;
41 | Pagination.defaultProps = defaultProps;
42 | export default Pagination;
43 |
--------------------------------------------------------------------------------
/src/components/Progress/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import createBlock from 'utils/createBlock';
5 |
6 | const propTypes = {
7 | /** Sets the background class of the progress bar. */
8 | variant: PropTypes.oneOf([
9 | 'primary',
10 | 'accent',
11 | 'warning',
12 | 'positive',
13 | 'negative',
14 | ]),
15 | /** Current value of progress */
16 | now: PropTypes.number,
17 | /** Set the height of the progress bar */
18 | height: PropTypes.number,
19 | /** Show label that represents visual percentage. EG. 60% */
20 | label: PropTypes.node,
21 | /** Custom `class` for label */
22 | labelClassName: PropTypes.string,
23 | /** Change to style border */
24 | border: PropTypes.bool,
25 | /** Uses a gradient to create a striped effect. */
26 | striped: PropTypes.bool,
27 | /** Animate's the stripes from right to left */
28 | animated: PropTypes.bool,
29 | };
30 | const defaultProps = {
31 | variant: 'primary',
32 | now: 100,
33 | height: 8,
34 | label: false,
35 | labelClassName: 'u-text100 u-fontMedium',
36 | animated: false,
37 | striped: false,
38 | border: false,
39 | };
40 |
41 | const variantsClassName = {
42 | backgroundColor: {
43 | primary: 'u-backgroundPrimary',
44 | accent: 'u-backgroundAccent',
45 | warning: 'u-backgroundWarning',
46 | positive: 'u-backgroundPositive',
47 | negative: 'u-backgroundNegative',
48 | },
49 | textColor: {
50 | primary: 'u-textWhite',
51 | accent: 'u-textWhite',
52 | warning: 'u-textDark',
53 | positive: 'u-textWhite',
54 | negative: 'u-textWhite',
55 | },
56 | borderColor: {
57 | primary: 'u-borderPrimary',
58 | accent: 'u-borderAccent',
59 | warning: 'u-borderWarning',
60 | positive: 'u-borderPositive',
61 | negative: 'u-borderNegative',
62 | },
63 | };
64 |
65 | const Progress = React.forwardRef(({ className, labelClassName, border, striped, animated, now, height, label, variant, as: Component = 'div', ...props }, ref) => (
66 |
79 |
87 | {label && (
88 |
94 | {label}
95 |
96 | )}
97 |
98 |
99 | ));
100 |
101 | const Group = createBlock('ProgressGroup u-flex u-flexNoWrap');
102 | Progress.displayName = 'Progress';
103 | Progress.defaultProps = defaultProps;
104 | Progress.propTypes = propTypes;
105 | Progress.Group = Group;
106 | export default Progress;
107 |
--------------------------------------------------------------------------------
/src/components/SafeAnchor/index.js:
--------------------------------------------------------------------------------
1 | //fork react-bootstrap/src/SafeAnchor.js
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import useMergedRefs from '@restart/hooks/useMergedRefs';
5 | import createChainedFunction from 'utils/createChainedFunction';
6 |
7 | const propTypes = {
8 | href: PropTypes.string,
9 | onClick: PropTypes.func,
10 | onKeyDown: PropTypes.func,
11 | disabled: PropTypes.bool,
12 | role: PropTypes.string,
13 | tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
14 |
15 | /**
16 | * this is sort of silly but needed for Button
17 | */
18 | as: PropTypes.element,
19 |
20 | /** @private */
21 | innerRef: PropTypes.any,
22 | };
23 |
24 | function isTrivialHref(href) {
25 | return !href || href.trim() === '#';
26 | }
27 |
28 | /**
29 | * There are situations due to browser quirks or Bootstrap CSS where
30 | * an anchor tag is needed, when semantically a button tag is the
31 | * better choice. SafeAnchor ensures that when an anchor is used like a
32 | * button its accessible. It also emulates input `disabled` behavior for
33 | * links, which is usually desirable for Buttons, NavItems, DropdownItems, etc.
34 | */
35 |
36 | const SafeAnchor = React.forwardRef((props, ref) => {
37 | const handleClick = (event) => {
38 | const { disabled, href, onClick } = props;
39 | if (disabled || isTrivialHref(href)) {
40 | event.preventDefault();
41 | }
42 |
43 | if (disabled) {
44 | event.stopPropagation();
45 | return;
46 | }
47 |
48 | if (onClick) {
49 | onClick(event);
50 | }
51 | };
52 | const handleKeyDown = (event) => {
53 | if (event.key === ' ') {
54 | event.preventDefault();
55 | handleClick(event);
56 | }
57 | };
58 |
59 | let mergeRefs;
60 | let propsHref;
61 | let propsTabIndex;
62 | const {
63 | as: Component = 'a',
64 | disabled,
65 | onKeyDown,
66 | innerRef } = props;
67 | if (isTrivialHref(props.href)) {
68 | propsHref = {
69 | role: props.role || 'button',
70 | href: props.href || '#',
71 | };
72 | }
73 |
74 | if (disabled) {
75 | propsTabIndex = {
76 | tabIndex: -1,
77 | 'aria-disabled': true,
78 | };
79 | }
80 | if (innerRef) mergeRefs = useMergedRefs(ref, innerRef);
81 | return (
82 |
90 | );
91 | });
92 |
93 | SafeAnchor.propTypes = propTypes;
94 | SafeAnchor.displayName = 'SafeAnchor';
95 |
96 | export default SafeAnchor;
97 |
--------------------------------------------------------------------------------
/src/components/SearchBox/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Form from 'components/Form';
5 | import Icon from 'components/Icon';
6 | import Button from 'components/Button';
7 |
8 |
9 | const propTypes = {
10 | /** Callback fired when the Search Button is clicking */
11 | onClickButton: PropTypes.func,
12 | /** The icon to display. The name can get from Component Icon */
13 | buttonIcon: PropTypes.string,
14 | /**
15 | * Sets sizeInput on `Input` and size on `Button`.
16 | * @default 'medium'
17 | * @type {('small'|'medium'|'large')}
18 | * */
19 | sizeControl: PropTypes.string,
20 | /** Custom text button */
21 | buttonText: PropTypes.string,
22 | };
23 |
24 | const defaultProps = {
25 | buttonIcon: 'search',
26 | };
27 |
28 | const SearchBox = React.forwardRef(({ className, sizeControl, onClickButton, buttonIcon, buttonText, ...props }, ref) => (
29 |
30 |
37 |
38 |
43 |
44 |
52 |
53 |
54 |
55 |
56 | ));
57 |
58 | SearchBox.displayName = 'SearchBox';
59 | SearchBox.defaultProps = defaultProps;
60 | SearchBox.propTypes = propTypes;
61 | export default SearchBox;
62 |
--------------------------------------------------------------------------------
/src/components/Separator/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** Custom label */
7 | label: PropTypes.string,
8 | /**
9 | * The `Separator` visual variant
10 | */
11 | variant: PropTypes.oneOf([
12 | 'light',
13 | 'lighter',
14 | 'primary',
15 | 'positive',
16 | 'negative',
17 | 'gray',
18 | ]),
19 | lineType: PropTypes.oneOf([
20 | 'solid',
21 | 'dashed',
22 | ]),
23 | };
24 |
25 | const defaultProps = {
26 | variant: 'light',
27 | lineType: 'solid',
28 | };
29 |
30 | const variantsClassName = {
31 | light: {
32 | label: 'u-textLight hover:u-textLight',
33 | line: 'u-borderLight',
34 | },
35 | lighter: {
36 | label: 'u-textLight hover:u-textLight',
37 | line: '',
38 | },
39 | primary: {
40 | label: 'u-textPrimary hover:u-textPrimary',
41 | line: 'u-borderPrimary',
42 | },
43 | positive: {
44 | label: 'u-textPositive hover:u-textPositive',
45 | line: 'u-borderPositive',
46 | },
47 | negative: {
48 | label: 'u-textNegative hover:u-textNegative',
49 | line: 'u-borderNegative',
50 | },
51 | gray: {
52 | label: 'u-textGray hover:u-textGray',
53 | line: 'u-borderGray',
54 | },
55 | };
56 |
57 | const Separator = React.forwardRef(({ className, label, lineType, variant, as: Component = 'div', ...props }, ref) => {
58 | const lineClass = classNames(
59 | 'Separator-line u-border u-sizeFill',
60 | 'u-border u-borderRightNone u-borderBottomNone u-borderLeftNone',
61 | variant && variantsClassName[variant].line,
62 | lineType === 'dashed' && 'u-borderDashed'
63 | );
64 | return (
65 |
74 |
75 | {label && (
76 |
77 |
83 | {label}
84 |
85 |
86 |
87 | )}
88 |
89 | );
90 | });
91 |
92 | Separator.displayName = 'Separator';
93 | Separator.defaultProps = defaultProps;
94 | Separator.propTypes = propTypes;
95 | export default Separator;
96 |
--------------------------------------------------------------------------------
/src/components/SessionType/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Icon from 'components/Icon';
5 |
6 | const propTypes = {
7 | /** Custom label */
8 | label: PropTypes.oneOfType([
9 | PropTypes.string,
10 | PropTypes.func,
11 | ]),
12 | /** Custom label of left side */
13 | leftLabel: PropTypes.string,
14 | /**
15 | * The `SessionType` visual variant
16 | */
17 | variant: PropTypes.oneOf([
18 | 'default',
19 | 'positive',
20 | 'warning',
21 | 'accent',
22 | ]),
23 | };
24 |
25 | const defaultProps = {
26 | variant: 'default',
27 | leftLabel: 'Open guide',
28 | };
29 | const variantsClassName = {
30 | default: 'u-textWhite hover:u-textWhite u-backgroundLight hover:u-backgroundSemiGray',
31 | positive: 'u-textWhite hover:u-textWhite u-backgroundPositive hover:u-backgroundPositiveDark',
32 | warning: 'u-textDark hover:u-textDark u-backgroundWarning hover:u-backgroundWarningDark',
33 | accent: 'u-textWhite hover:u-textWhite u-backgroundAccent hover:u-backgroundAccentDark',
34 | };
35 | const SessionType = React.forwardRef(({ className, label, leftLabel, variant, as: Component = 'div', ...props }, ref) => (
36 |
46 |
47 |
48 | {label && (
49 |
50 | {typeof (label) === 'function'
51 | ? label()
52 | : label
53 | }
54 |
55 | )}
56 |
57 |
58 | {leftLabel}
59 |
60 |
61 |
62 | ));
63 |
64 | SessionType.displayName = 'SessionType';
65 | SessionType.defaultProps = defaultProps;
66 | SessionType.propTypes = propTypes;
67 | export default SessionType;
68 |
--------------------------------------------------------------------------------
/src/components/SidebarMenu/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SidebarContext = React.createContext({ });
4 |
5 | export default SidebarContext;
6 |
--------------------------------------------------------------------------------
/src/components/SidebarMenu/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import createBlock from 'utils/createBlock';
5 | import Item from './Item';
6 | import SubMenu from './SubMenu';
7 | import SidebarContext from './Context';
8 |
9 | const propTypes = {
10 | /**
11 | * Set's the size of all SidebarMenu.Item & SidebarMenu.SubMenu
12 | * */
13 | size: PropTypes.oneOf([
14 | 'small',
15 | 'medium',
16 | ]),
17 | /** Set current menu item*/
18 | current: PropTypes.string,
19 | /** Callback fired when the menu item is clicked. */
20 | onSelect: PropTypes.func,
21 | };
22 |
23 | const defaultProps = {
24 | size: 'medium',
25 | };
26 | const SidebarMenu = React.forwardRef(({ className, children, current, onSelect, size, ...props }, ref) => {
27 | const modifiedChildren = React.Children.map(children, (child, index) => {
28 | if (!child) {
29 | return null;
30 | }
31 | const path = child.props.eventKey || index;
32 | return React.cloneElement(
33 | child, ({
34 | level: 1,
35 | path: path.toString(),
36 | })
37 | );
38 | });
39 | return (
40 |
47 |
54 |
61 | {modifiedChildren}
62 |
63 |
64 |
65 | );
66 | });
67 |
68 | const Divider = createBlock('SidebarMenu-divider u-borderTop u-marginVerticalExtraSmall');
69 | const Header = createBlock('SidebarMenu-header u-textLight u-text200 u-fontMedium u-paddingHorizontalSmall u-marginVerticalTiny md:u-marginVerticalExtraSmall lg:u-marginVerticalSmall');
70 |
71 | SidebarMenu.Item = Item;
72 | SidebarMenu.SubMenu = SubMenu;
73 | SidebarMenu.Divider = Divider;
74 | SidebarMenu.Header = Header;
75 | SidebarMenu.defaultProps = defaultProps;
76 | SidebarMenu.displayName = 'SidebarMenu';
77 | SidebarMenu.propTypes = propTypes;
78 | export default SidebarMenu;
79 |
--------------------------------------------------------------------------------
/src/components/Skeleton/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 |
5 | const propTypes = {
6 | /** The Skeleton visual variant */
7 | variant: PropTypes.oneOf([
8 | 'text',
9 | 'circle',
10 | ]),
11 | /** Set the width of the skeleton */
12 | width: PropTypes.oneOfType([
13 | PropTypes.string,
14 | PropTypes.number,
15 | ]),
16 | /** Set the height of the skeleton */
17 | height: PropTypes.oneOfType([
18 | PropTypes.string,
19 | PropTypes.number,
20 | ]),
21 | /** Set the duration of the animation skeleton */
22 | duration: PropTypes.number,
23 | };
24 |
25 | const variantClass = {
26 | text: '',
27 | circle: 'u-roundedCircle',
28 | };
29 | const defaultProps = {
30 | height: 16,
31 | variant: 'text',
32 | duration: 2000,
33 | };
34 |
35 |
36 | const Skeleton = React.forwardRef(({ variant, width, height, duration, as: Component = 'div', ...props }, ref) => {
37 | const heightStyle = {
38 | width: width && width,
39 | height: height && height,
40 | animationDuration: `${duration}ms`,
41 | };
42 | const mergeProps = {
43 | ref,
44 | style: { ...props.style, ...heightStyle },
45 | ...props,
46 | };
47 | return (
48 |
56 | );
57 | });
58 |
59 | Skeleton.displayName = 'Skeleton';
60 | Skeleton.propTypes = propTypes;
61 | Skeleton.defaultProps = defaultProps;
62 | export default Skeleton;
63 |
--------------------------------------------------------------------------------
/src/components/Slider/Handle.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Handle as HandleBase } from 'rc-slider';
3 |
4 | const Handle = React.forwardRef((props, ref) => (
5 |
9 | ));
10 | Handle.displayName = 'Slider.Handle';
11 | Handle.propTypes = {};
12 | Handle.defaultProps = {};
13 | export default Handle;
14 |
--------------------------------------------------------------------------------
/src/components/Slider/Range.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import { Range as RangeBase } from 'rc-slider';
5 |
6 | const propTypes = {
7 | variant: PropTypes.oneOf([
8 | 'primary',
9 | 'accent',
10 | 'warning',
11 | 'positive',
12 | 'negative',
13 | ]),
14 | };
15 |
16 | const Range = React.forwardRef(({ variant, tipFormatter, tipProps, ...props }, ref) => (
17 |
26 | ));
27 |
28 | Range.displayName = 'Slider.Range';
29 | Range.propTypes = propTypes;
30 | Range.defaultProps = {};
31 | export default Range;
32 |
--------------------------------------------------------------------------------
/src/components/Slider/createSliderWithTooltip.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Tooltip from 'rc-tooltip';
4 | import Handle from './Handle';
5 |
6 | export default function createSliderWithTooltip(Component) {
7 | return class ComponentWrapper extends React.Component {
8 | static propTypes = {
9 | tipFormatter: PropTypes.func,
10 | handleStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),
11 | tipProps: PropTypes.object,
12 | };
13 |
14 | static defaultProps = {
15 | tipFormatter(value) { return value; },
16 | handleStyle: [{}],
17 | tipProps: {},
18 | };
19 |
20 | state = {
21 | visibles: {},
22 | };
23 |
24 | handleTooltipVisibleChange = (index, visible) => {
25 | this.setState(prevState => ({
26 | visibles: {
27 | ...prevState.visibles,
28 | [index]: visible,
29 | },
30 | }));
31 | }
32 |
33 | handleWithTooltip = ({ value, dragging, index, disabled, ...restProps }) => {
34 | const {
35 | tipFormatter,
36 | tipProps,
37 | handleStyle,
38 | } = this.props;
39 |
40 | const {
41 | prefixCls = 'Slider-tooltip',
42 | overlay = tipFormatter(value),
43 | placement = 'top',
44 | visible = false,
45 | ...restTooltipProps
46 | } = tipProps;
47 |
48 | let handleStyleWithIndex;
49 | if (Array.isArray(handleStyle)) {
50 | handleStyleWithIndex = handleStyle[index] || handleStyle[0];
51 | } else {
52 | handleStyleWithIndex = handleStyle;
53 | }
54 | const { visibles } = this.state;
55 | return (
56 |
64 |
65 | this.handleTooltipVisibleChange(index, true)}
72 | onMouseLeave={() => this.handleTooltipVisibleChange(index, false)}
73 | />
74 |
75 | );
76 | }
77 |
78 | render() {
79 | return ;
80 | }
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/Slider/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import SliderBase from 'rc-slider';
5 | import Range from './Range';
6 | import Handle from './Handle';
7 | import createSliderWithTooltip from './createSliderWithTooltip';
8 |
9 | const propTypes = {
10 | /**
11 | * The Slider visual variant
12 | * @default 'primary'
13 | */
14 | variant: PropTypes.oneOf([
15 | 'primary',
16 | 'accent',
17 | 'warning',
18 | 'positive',
19 | 'negative',
20 | ]),
21 | /** @private */
22 | vertical: PropTypes.bool,
23 | };
24 |
25 | const Slider = React.forwardRef(({ vertical, variant, ...props }, ref) => (
26 |
37 | ));
38 |
39 | Slider.displayName = 'Slider';
40 | Slider.propTypes = propTypes;
41 | Slider.defaultProps = {};
42 | Slider.Handle = Handle;
43 | Slider.Range = Range;
44 | Slider.createSliderWithTooltip = createSliderWithTooltip;
45 | export default Slider;
46 |
--------------------------------------------------------------------------------
/src/components/Tab/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const TabContext = React.createContext({ });
4 |
5 | export default TabContext;
6 |
--------------------------------------------------------------------------------
/src/components/Tab/Item.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import TabContext from './Context';
5 |
6 | const propTypes = {
7 | /** A key that associates the Tab with it's controlling Tab.Item.*/
8 | eventKey: PropTypes.string,
9 | /**
10 | * Manually set the visual state of the Tab.Item to disabled
11 | * @default false
12 | * */
13 | disabled: PropTypes.bool,
14 |
15 | };
16 | const defaultProps = {
17 | };
18 |
19 | const Item = React.forwardRef(({ className, disabled, eventKey, index, fullWidth, direction, children, path, visual, as: Component = 'div', ...props }, ref) => {
20 | let active;
21 |
22 | const context = useContext(TabContext);
23 | if (path === context.current) {
24 | active = true;
25 | }
26 | const onClick = (e) => {
27 | e.stopPropagation();
28 | e.preventDefault();
29 | context.onSelect(path);
30 | };
31 | return (
32 | 0 && !fullWidth && (direction !== 'vertical') && (visual !== 'filled')) && 'u-marginLeftSmall',
37 | fullWidth && 'u-flexGrow1',
38 | (index > 0 && visual === 'filled' && (direction !== 'vertical')) && 'u-borderLeft',
39 | (index > 0 && visual === 'filled' && (direction === 'vertical')) && 'u-borderTop',
40 | active && 'is-active',
41 | disabled ? 'is-disabled u-cursorNotAllow' : 'u-cursorPointer',
42 | className && className
43 | )}
44 | >
45 | {active && (
46 |
53 | )}
54 |
68 | {children}
69 |
70 |
71 |
72 | );
73 | });
74 |
75 | Item.defaultProps = defaultProps;
76 | Item.displayName = 'TabItem';
77 | Item.propTypes = propTypes;
78 | export default Item;
79 |
--------------------------------------------------------------------------------
/src/components/Tab/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 |
5 | import TabContext from './Context';
6 | import Item from './Item';
7 |
8 | const propTypes = {
9 | /**
10 | * Set current tab item
11 | * @controllable onSelect
12 | * */
13 | current: PropTypes.string,
14 | /** Callback fired when the tab item is clicked. */
15 | onSelect: PropTypes.func,
16 | /** Set Tabs to full width */
17 | fullWidth: PropTypes.bool,
18 | /** Define direction of the Tabs */
19 | direction: PropTypes.oneOf([
20 | 'horizontal',
21 | 'vertical',
22 | ]),
23 | /** Define visual of the Tabs */
24 | visual: PropTypes.oneOf([
25 | 'default',
26 | 'filled',
27 | ]),
28 | };
29 |
30 | const defaultProps = {
31 | direction: 'horizontal',
32 | visual: 'default',
33 | };
34 | const Tab = React.forwardRef(({ className, children, current, fullWidth, onSelect, direction, visual, ...props }, ref) => {
35 | const modifiedChildren = React.Children.map(children, (child, index) => {
36 | if (!child) {
37 | return null;
38 | }
39 | const path = child.props.eventKey || index;
40 | return React.cloneElement(
41 | child, ({
42 | index,
43 | fullWidth,
44 | direction,
45 | visual,
46 | path: path.toString(),
47 | })
48 | );
49 | });
50 | return (
51 |
57 |
69 | {modifiedChildren}
70 | {direction === 'vertical' && (
71 |
72 | )}
73 |
74 |
75 | );
76 | });
77 |
78 | Tab.Item = Item;
79 | Tab.defaultProps = defaultProps;
80 | Tab.displayName = 'Tab';
81 | Tab.propTypes = propTypes;
82 | export default Tab;
83 |
--------------------------------------------------------------------------------
/src/components/Tag/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** The visual style of the tag */
7 | variant: PropTypes.oneOf([
8 | 'black',
9 | 'white',
10 | 'primary',
11 | 'primary_subtle',
12 | 'accent',
13 | 'accent_subtle',
14 | 'warning',
15 | 'warning_subtle',
16 | 'positive',
17 | 'positive_subtle',
18 | 'information',
19 | 'information_subtle',
20 | 'negative',
21 | 'negative_subtle',
22 | ]),
23 | /** Fixed className for text color, just available for variant: `primary`, `primary_subtle`, `accent`, `accent_subtle` */
24 | textClassName: PropTypes.oneOfType([
25 | PropTypes.string,
26 | PropTypes.arrayOf(PropTypes.string),
27 | ]),
28 | /**
29 | * You can use a custom element type for this component.
30 | * @default span
31 | * */
32 | as: PropTypes.elementType,
33 | };
34 | const defaultProps = {
35 | variant: 'primary',
36 | textClassName: false,
37 | };
38 |
39 |
40 | const variantsTextClassName = {
41 | primary: 'u-textWhite hover:u-textWhite',
42 | primary_subtle: 'u-textPrimary hover:u-textPrimary',
43 | accent: 'u-textWhite hover:u-textWhite ',
44 | accent_subtle: 'u-textDark hover:u-textDark ',
45 | };
46 |
47 | const variantsClassName = {
48 | black: 'u-textWhite hover:u-textWhite u-backgroundDarker',
49 | white: 'u-textGray hover:u-textGray u-backgroundWhite',
50 | primary: 'u-textWhite hover:u-textWhite u-backgroundPrimary',
51 | primary_subtle: 'u-textDark hover:u-textDark u-backgroundPrimaryLighter',
52 | information: 'u-textWhite hover:u-textWhite u-backgroundInformation',
53 | information_subtle: 'u-textInformation hover:u-textInformation u-backgroundInformationLighter',
54 | accent: 'u-backgroundAccent',
55 | accent_subtle: 'u-backgroundAccentLighter',
56 | warning: 'u-textDark hover:u-textDark u-backgroundWarning',
57 | warning_subtle: 'u-textDark hover:u-textDark u-backgroundWarningLighter',
58 | positive: 'u-textWhite hover:u-textWhite u-backgroundPositive',
59 | positive_subtle: 'u-textDark hover:u-textDark u-backgroundPositiveLighter',
60 | negative: 'u-textWhite hover:u-textWhite u-backgroundNegative',
61 | negative_subtle: 'u-textDark hover:u-textDark u-backgroundNegativeLighter',
62 | };
63 |
64 | const Tag = React.forwardRef(({ className, textClassName, variant, as: Component = 'span', ...props }, ref) => (
65 |
76 | ));
77 |
78 | Tag.displayName = 'Tag';
79 | Tag.defaultProps = defaultProps;
80 | Tag.propTypes = propTypes;
81 | Tag.variantsClassName = variantsClassName;
82 |
83 | export default Tag;
84 |
--------------------------------------------------------------------------------
/src/components/TagInput/index.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import TagsInput from 'react-tagsinput';
5 | import Tag from 'components/Tag';
6 | import Context from 'components/Form/Context';
7 |
8 |
9 | const propTypes = {
10 | /** The visual style of the tag */
11 | variant: PropTypes.oneOf([
12 | 'black',
13 | 'white',
14 | 'primary',
15 | 'primary_subtle',
16 | 'warning',
17 | 'warning_subtle',
18 | 'positive',
19 | 'positive_subtle',
20 | 'negative',
21 | 'negative_subtle',
22 | ]),
23 | /**
24 | * TagInput size variants
25 | *
26 | * Uses sizeControl from `` if not explicitly specified.
27 | * @default 'medium'
28 | * */
29 | size: PropTypes.oneOf(['small', 'medium', 'large']),
30 | };
31 |
32 | const defaultProps = {
33 | variant: 'primary_subtle',
34 | };
35 |
36 | const TagInput = React.forwardRef(({ className, variant, value, size, onChange, tagProps = {}, inputProps = {}, renderInput, ...props }, ref) => {
37 | const { sizeControl } = useContext(Context);
38 | const sizeOri = size || sizeControl;
39 |
40 | const tagInputProps = {
41 | ...props,
42 | focusedClassName: 'is-focus',
43 | tagProps: {
44 | ...tagProps,
45 | className: classNames(
46 | 'Tag u-flexInline u-alignItemsCenter u-textCenter u-textNoWrap u-roundedMedium hover:u-textDecorationNone u-marginRightTiny',
47 | (sizeOri === 'small') ? 'Tag--small u-text100' : 'u-text200',
48 | tagProps.className && tagProps.className,
49 | variant && Tag.variantsClassName[variant],
50 | ),
51 | classNameRemove: classNames(
52 | 'Tag-close u-marginLeftTiny u-cursorPointer hover:u-textDecorationNone',
53 | tagProps.classNameRemove && tagProps.classNameRemove,
54 | ),
55 | },
56 | inputProps: {
57 | ...inputProps,
58 | className: classNames(
59 | 'u-backgroundTransparent u-borderNone',
60 | (sizeOri === 'small') ? 'u-text100' : 'u-text200',
61 | inputProps.className && inputProps.className
62 | ),
63 | },
64 | };
65 | return (
66 |
82 | );
83 | });
84 |
85 |
86 | TagInput.displayName = 'TagInput';
87 | TagInput.defaultProps = defaultProps;
88 | TagInput.propTypes = propTypes;
89 |
90 | export default TagInput;
91 |
--------------------------------------------------------------------------------
/src/components/Toast/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ToastContainer as ToastContainerBase, toast as toastBase } from 'react-toastify';
4 | import Icon from 'components/Icon';
5 |
6 | const propTypes = {
7 | /**
8 | * One of top-right, top-center, top-left, bottom-right, bottom-center, bottom-left
9 | */
10 | position: PropTypes.oneOf([
11 | 'top-left',
12 | 'top-right',
13 | 'top-center',
14 | 'bottom-left',
15 | 'bottom-right',
16 | 'bottom-center',
17 | ]),
18 | /**
19 | * Delay in ms to close the toast. If set to false, the notification needs to be closed manually
20 | */
21 | autoDismiss: PropTypes.oneOfType([
22 | PropTypes.bool,
23 | PropTypes.number,
24 | ]),
25 | /** Renders a properly aligned dismiss button */
26 | dismissible: PropTypes.bool,
27 | /**
28 | * Display or not the progress bar below the toast(remaining time)
29 | */
30 | hideProgressBar: PropTypes.bool,
31 | };
32 |
33 | const defaultProps = {
34 | position: 'top-right',
35 | autoDismiss: 5000,
36 | dismissible: true,
37 | hideProgressBar: false,
38 | };
39 |
40 | const ToastContainer = React.forwardRef(({ position, dismissible, autoDismiss, ...props }, ref) => (
41 | : false}
51 | />
52 | ));
53 | export const toast = toastBase;
54 | ToastContainer.displayName = 'ToastContainer';
55 | ToastContainer.defaultProps = defaultProps;
56 | ToastContainer.propTypes = propTypes;
57 | export default ToastContainer;
58 |
--------------------------------------------------------------------------------
/src/components/Toggle/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | const propTypes = {
6 | /** Set the visual state of the Toggle to active */
7 | checked: PropTypes.bool,
8 | /** Disables the disabled, preventing mouse events, even if the underlying component is an `` element */
9 | disabled: PropTypes.bool,
10 | /** Disable the label */
11 | nonLabel: PropTypes.bool,
12 | /** Custom label for the on-state */
13 | textLabelOn: PropTypes.string,
14 | /** Custom label for the off-state */
15 | textLabelOff: PropTypes.string,
16 | };
17 |
18 | const defaultProps = {
19 | checked: false,
20 | disabled: false,
21 | nonLabel: false,
22 | textLabelOn: 'On',
23 | textLabelOff: 'Off',
24 | };
25 |
26 | const Toggle = React.forwardRef(({ className, checked, disabled, nonLabel, textLabelOn, textLabelOff, as: Component = 'button', ...props }, ref) => (
27 |
35 |
46 |
51 |
52 | {!nonLabel && (
53 |
58 | {checked ? textLabelOn : textLabelOff}
59 |
60 | )}
61 |
62 | ));
63 | Toggle.displayName = 'Toggle';
64 | Toggle.defaultProps = defaultProps;
65 | Toggle.propTypes = propTypes;
66 | export default Toggle;
67 |
--------------------------------------------------------------------------------
/src/components/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import isRequiredForA11y from 'prop-types-extra/lib/isRequiredForA11y';
5 |
6 | const propTypes = {
7 | /**
8 | * An html id attribute, necessary for accessibility
9 | * @type {string|number}
10 | * @required
11 | */
12 | id: isRequiredForA11y(
13 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
14 | ),
15 | /**
16 | * Remove arrow
17 | */
18 | noArrow: PropTypes.bool,
19 | /**
20 | * Sets the direction the Tooltip is positioned towards.
21 | *
22 | * This is generally provided by the `Overlay` component positioning the tooltip
23 | */
24 | placement: PropTypes.oneOf([
25 | 'auto-start',
26 | 'auto',
27 | 'auto-end',
28 | 'top-start',
29 | 'top',
30 | 'top-end',
31 | 'right-start',
32 | 'right',
33 | 'right-end',
34 | 'bottom-end',
35 | 'bottom',
36 | 'bottom-start',
37 | 'left-end',
38 | 'left',
39 | 'left-start',
40 | ]),
41 | /**
42 | * An Overlay injected set of props for positioning the tooltip arrow.
43 | *
44 | * This is generally provided by the `Overlay` component positioning the tooltip
45 | * @type {{ ref: ReactRef, style: Object }}
46 | */
47 | arrowProps: PropTypes.shape({
48 | ref: PropTypes.any,
49 | style: PropTypes.object,
50 | }),
51 | /** The Tooltip visual variant */
52 | variant: PropTypes.oneOf([
53 | 'white',
54 | 'black',
55 | ]),
56 | /** @private */
57 | scheduleUpdate: PropTypes.func,
58 |
59 | /** @private */
60 | outOfBoundaries: PropTypes.any,
61 | };
62 | const defaultProps = {
63 | placement: 'right',
64 | noArrow: false,
65 | variant: 'black',
66 | };
67 | const variantsClassName = {
68 | black: 'u-textWhite u-backgroundBlack',
69 | white: 'u-textDark u-backgroundWhite u-border',
70 | };
71 | const Tooltip = React.forwardRef(({
72 | className,
73 | show,
74 | variant,
75 | children,
76 | noArrow,
77 | arrowProps,
78 | styleTooltip,
79 | placement,
80 | scheduleUpdate: _,
81 | outOfBoundaries: _1,
82 | ...props
83 | }, ref) => (
84 |
99 | {!noArrow && (
100 |
107 | )}
108 |
115 | {children}
116 |
117 |
118 | ));
119 |
120 | Tooltip.displayName = 'Tooltip';
121 | Tooltip.defaultProps = defaultProps;
122 | Tooltip.propTypes = propTypes;
123 | export default Tooltip;
124 |
--------------------------------------------------------------------------------
/src/components/TopBanner/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import { useUncontrolled } from 'uncontrollable';
5 | import useEventCallback from '@restart/hooks/useEventCallback';
6 | import { elementType } from 'prop-types-extra';
7 | import Icon from 'components/Icon';
8 | import Fade from 'components/Fade';
9 |
10 | const propTypes = {
11 | /** Sets image shape as background. */
12 | bgImage: PropTypes.node,
13 | /**
14 | * Renders a properly aligned dismiss button, as well as
15 | * adding extra horizontal padding to the TopBanner.
16 | * @default false
17 | */
18 | dismissible: PropTypes.bool,
19 | /**
20 | * Controls the visual state of the TopBanner.
21 | * @controllable onClose
22 | * */
23 | show: PropTypes.bool,
24 | /** Callback fired when TopBanner is closed. */
25 | onClose: PropTypes.func,
26 | /** A `react-transition-group` Transition component used to animate the TopBanner on dismissal. */
27 | transition: elementType,
28 | };
29 |
30 | const defaultProps = {
31 | show: true,
32 | transition: Fade,
33 | };
34 | const controllables = {
35 | show: 'onClose',
36 | };
37 |
38 | const TopBanner = React.forwardRef((uncontrolledProps, ref) => {
39 | const {
40 | className,
41 | bgImage,
42 | children,
43 | dismissible,
44 | onClose,
45 | show,
46 | transition: Transition,
47 | ...props } = useUncontrolled(uncontrolledProps, controllables);
48 | const [dismissButtonHover, setDismissButtonHover] = useState(false);
49 | const handleClose = useEventCallback((e) => {
50 | onClose(false, e);
51 | });
52 | return (
53 |
62 | {bgImage && (
63 |
64 |

65 |
66 | )}
67 |
68 | {children}
69 |
70 | {dismissible && (
71 |
setDismissButtonHover(dismissButtonHover => !dismissButtonHover)}
74 | onMouseLeave={() => setDismissButtonHover(dismissButtonHover => !dismissButtonHover)}
75 | onClick={handleClose}
76 | >
77 |
84 |
85 | )}
86 |
87 | );
88 | });
89 |
90 | TopBanner.displayName = 'TopBanner';
91 | TopBanner.defaultProps = defaultProps;
92 | TopBanner.propTypes = propTypes;
93 | export default TopBanner;
94 |
--------------------------------------------------------------------------------
/src/components/TopMenu/Context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext({ });
4 |
5 | export default Context;
6 |
--------------------------------------------------------------------------------
/src/components/TopMenu/Item.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import SafeAnchor from 'components/SafeAnchor';
5 | import Badge from 'components/Badge';
6 | import TopMenuContext from './Context';
7 |
8 | const propTypes = {
9 | /** A key that associates the TopMenu with it's controlling TopMenu.Item.*/
10 | eventKey: PropTypes.string,
11 | /**
12 | * Manually set the visual state of the TopMenu.Item to disabled
13 | * @default false
14 | * */
15 | disabled: PropTypes.bool,
16 | /** The badge to display. The structure can get from Component Badge */
17 | badge: PropTypes.oneOfType([
18 | PropTypes.string,
19 | PropTypes.func,
20 | ]),
21 | };
22 | const defaultProps = {
23 | };
24 |
25 | const Item = React.forwardRef(({ className, disabled, eventKey, children, badge, isSubItem, level, index, path, ...props }, ref) => {
26 | let active;
27 |
28 | const context = useContext(TopMenuContext);
29 |
30 | if (path === context.current) {
31 | active = true;
32 | }
33 |
34 | const Component = active || disabled ? 'span' : SafeAnchor;
35 |
36 | const onClick = (e) => {
37 | e.stopPropagation();
38 | e.preventDefault();
39 | context.onSelect(path);
40 | };
41 | return (
42 | 0 && !isSubItem) && 'u-marginLeftLarge',
47 | active && 'is-active',
48 | isSubItem ? 'u-flex hover:u-backgroundLightest u-paddingHorizontalSmall' : 'u-flexInline u-alignItemsCenter',
49 | disabled ? 'is-disabled u-cursorNotAllow u-pointerEventsNone' : 'u-cursorPointer',
50 | )}
51 | >
52 | {(active && !isSubItem) && (
53 | <>
54 |
59 |
64 | >
65 | )}
66 |
75 |
76 | {children}
77 |
78 |
79 | {badge && (
80 |
81 | {typeof (badge) === 'function'
82 | ? badge()
83 | : {badge}
84 | }
85 |
86 | )}
87 |
88 |
89 | );
90 | });
91 |
92 | Item.defaultProps = defaultProps;
93 | Item.displayName = 'TopMenuItem';
94 | Item.propTypes = propTypes;
95 | export default Item;
96 |
--------------------------------------------------------------------------------
/src/components/TopMenu/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import Item from './Item';
5 | import SubMenu from './SubMenu';
6 | import TopMenuContext from './Context';
7 |
8 | const propTypes = {
9 | /**
10 | * Set current menu item
11 | * @controllable onSelect
12 | * */
13 | current: PropTypes.string,
14 | /** Callback fired when the menu item is clicked. */
15 | onSelect: PropTypes.func,
16 | };
17 |
18 | const TopMenu = React.forwardRef(({ className, children, current, onSelect, ...props }, ref) => {
19 | const modifiedChildren = React.Children.map(children, (child, index) => {
20 | if (!child) {
21 | return null;
22 | }
23 | const path = child.props.eventKey || index;
24 | return React.cloneElement(
25 | child, ({
26 | level: 1,
27 | index,
28 | path: path.toString(),
29 | })
30 | );
31 | });
32 | return (
33 |
39 |
46 |
53 | {modifiedChildren}
54 |
55 |
56 |
57 | );
58 | });
59 |
60 | TopMenu.Item = Item;
61 | TopMenu.SubMenu = SubMenu;
62 | TopMenu.defaultProps = {};
63 | TopMenu.displayName = 'TopMenu';
64 | TopMenu.propTypes = propTypes;
65 | export default TopMenu;
66 |
--------------------------------------------------------------------------------
/src/constants/common.js:
--------------------------------------------------------------------------------
1 | export const PluginType = {
2 | ASSET: 'asset',
3 | };
--------------------------------------------------------------------------------
/src/constants/messages.js:
--------------------------------------------------------------------------------
1 | export const messagesVariants = [
2 | {
3 | id: 'information',
4 | type: 'form',
5 | className: 'u-borderInformation u-backgroundInformationLighter',
6 | textClassName: 'u-textDark',
7 | },
8 | {
9 | id: 'positive',
10 | type: 'form',
11 | className: 'u-borderPositive u-backgroundPositiveLighter',
12 | textClassName: 'u-textDark',
13 | },
14 | {
15 | id: 'warning',
16 | type: 'form',
17 | className: 'u-borderWarning u-backgroundWarningLighter',
18 | textClassName: 'u-textDark',
19 | },
20 | {
21 | id: 'negative',
22 | type: 'form',
23 | className: 'u-borderNegative u-backgroundNegativeLighter',
24 | textClassName: 'u-textDark',
25 | },
26 | {
27 | id: 'information',
28 | type: 'system',
29 | className: 'u-borderInformation u-backgroundInformation',
30 | textClassName: 'u-textWhite',
31 | },
32 | {
33 | id: 'positive',
34 | type: 'system',
35 | className: 'u-borderPositive u-backgroundPositive',
36 | textClassName: 'u-textWhite',
37 | },
38 | {
39 | id: 'warning',
40 | type: 'system',
41 | className: 'u-borderWarning u-backgroundWarning',
42 | textClassName: 'u-textDark',
43 | textHeadingClassName: 'u-textDark',
44 | },
45 | {
46 | id: 'negative',
47 | type: 'system',
48 | className: 'u-borderNegative u-backgroundNegative',
49 | textClassName: 'u-textWhite',
50 | },
51 | ];
52 |
--------------------------------------------------------------------------------
/src/hooks/useForkRef.js:
--------------------------------------------------------------------------------
1 | //fork react-overlays/src/useForkRef.js
2 | import React from 'react';
3 | import setRef from '../utils/setRef';
4 |
5 | export default function useForkRef(refA, refB) {
6 | /**
7 | * This will create a new function if the ref props change and are defined.
8 | * This means react will call the old forkRef with `null` and the new forkRef
9 | * with the ref. Cleanup naturally emerges from this behavior
10 | */
11 | return React.useMemo(() => {
12 | if (refA == null && refB == null) {
13 | return null;
14 | }
15 | return (refValue) => {
16 | setRef(refA, refValue);
17 | setRef(refB, refValue);
18 | };
19 | }, [refA, refB]);
20 | }
21 |
--------------------------------------------------------------------------------
/src/hooks/usePopper.js:
--------------------------------------------------------------------------------
1 | //fork react-overlays/src/usePopper.js
2 | import PopperJS from 'popper.js';
3 | import { useCallback, useEffect, useRef, useState } from 'react';
4 |
5 | const initialPopperStyles = {
6 | position: 'absolute',
7 | top: '0',
8 | left: '0',
9 | opacity: '0',
10 | pointerEvents: 'none',
11 | };
12 | const initialArrowStyles = {};
13 |
14 | /**
15 | * Position an element relative some reference element using Popper.js
16 | *
17 | * @param {HTMLElement} referenceElement The element
18 | * @param {HTMLElement} popperElement
19 | * @param {Object} options
20 | * @param {Object} options.modifiers Popper.js modifiers
21 | * @param {Boolean} options.enabled toggle the popper functionality on/off
22 | * @param {String} options.placement The popper element placement relative to the reference element
23 | * @param {Boolean} options.positionFixed use fixed positioning
24 | * @param {Boolean} options.eventsEnabled have Popper listen on window resize events to reposition the element
25 | */
26 |
27 | export default function usePopper(
28 | referenceElement,
29 | popperElement,
30 | {
31 | enabled = true,
32 | placement = 'bottom',
33 | positionFixed = false,
34 | eventsEnabled = true,
35 | modifiers = {},
36 | } = {},
37 | ) {
38 | const popperInstanceRef = useRef();
39 |
40 | const hasArrow = !!(modifiers.arrow && modifiers.arrow.element);
41 |
42 | const scheduleUpdate = useCallback(() => {
43 | if (popperInstanceRef.current) {
44 | popperInstanceRef.current.scheduleUpdate();
45 | }
46 | }, []);
47 |
48 | const [state, setState] = useState({
49 | placement,
50 | scheduleUpdate,
51 | outOfBoundaries: false,
52 | styles: initialPopperStyles,
53 | arrowStyles: initialArrowStyles,
54 | });
55 |
56 | // A placement difference in state means popper determined a new placement
57 | // apart from the props value. By the time the popper element is rendered with
58 | // the new position Popper has already measured it, if the place change triggers
59 | // a size change it will result in a misaligned popper. So we schedule an update to be sure.
60 | useEffect(() => {
61 | scheduleUpdate();
62 | }, [state.placement, scheduleUpdate]);
63 |
64 | /** Toggle Events */
65 | useEffect(() => {
66 | if (popperInstanceRef.current) {
67 | // eslint-disable-next-line no-unused-expressions
68 | eventsEnabled
69 | ? popperInstanceRef.current.enableEventListeners()
70 | : popperInstanceRef.current.disableEventListeners();
71 | }
72 | }, [eventsEnabled]);
73 |
74 | useEffect(() => {
75 | if (!enabled || referenceElement == null || popperElement == null) {
76 | return undefined;
77 | }
78 |
79 | const arrow = modifiers.arrow && {
80 | ...modifiers.arrow,
81 | element: modifiers.arrow.element,
82 | };
83 |
84 | popperInstanceRef.current = new PopperJS(referenceElement, popperElement, {
85 | placement,
86 | positionFixed,
87 | modifiers: {
88 | ...modifiers,
89 | arrow,
90 | applyStyle: { enabled: false },
91 | updateStateModifier: {
92 | enabled: true,
93 | order: 900,
94 | fn(data) {
95 | setState({
96 | scheduleUpdate,
97 | styles: {
98 | position: data.offsets.popper.position,
99 | ...data.styles,
100 | },
101 | arrowStyles: data.arrowStyles,
102 | outOfBoundaries: data.hide,
103 | placement: data.placement,
104 | });
105 | },
106 | },
107 | },
108 | });
109 |
110 | return () => {
111 | if (popperInstanceRef.current !== null) {
112 | popperInstanceRef.current.destroy();
113 | popperInstanceRef.current = null;
114 | }
115 | };
116 | // intentionally NOT re-running on new modifiers
117 | // eslint-disable-next-line react-hooks/exhaustive-deps
118 | }, [
119 | enabled,
120 | placement,
121 | positionFixed,
122 | referenceElement,
123 | popperElement,
124 | hasArrow,
125 | ]);
126 |
127 | return state;
128 | }
129 |
--------------------------------------------------------------------------------
/src/hooks/useRootClose.js:
--------------------------------------------------------------------------------
1 | //fork react-overlays/src/useRootClose.js
2 | import contains from 'dom-helpers/contains';
3 | import listen from 'dom-helpers/listen';
4 | import { useCallback, useEffect, useRef } from 'react';
5 |
6 | import useEventCallback from '@restart/hooks/useEventCallback';
7 | import warning from 'warning';
8 |
9 | const escapeKeyCode = 27;
10 | const noop = () => {};
11 |
12 | function isLeftClickEvent(event) {
13 | return event.button === 0;
14 | }
15 |
16 | function isModifiedEvent(event) {
17 | return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
18 | }
19 |
20 | /**
21 | * The `useRootClose` hook registers your callback on the document
22 | * when rendered. Powers the `` component. This is used achieve modal
23 | * style behavior where your callback is triggered when the user tries to
24 | * interact with the rest of the document or hits the `esc` key.
25 | *
26 | * @param {Ref|HTMLElement} ref The element boundary
27 | * @param {function} onRootClose
28 | * @param {object} options
29 | * @param {boolean} options.disabled
30 | * @param {string} options.clickTrigger The DOM event name (click, mousedown, etc) to attach listeners on
31 | */
32 | function useRootClose(
33 | ref,
34 | onRootClose,
35 | { disabled, clickTrigger = 'click' } = {},
36 | ) {
37 | const preventMouseRootCloseRef = useRef(false);
38 | const onClose = onRootClose || noop;
39 |
40 | const handleMouseCapture = useCallback(
41 | (e) => {
42 | const currentTarget = ref && ('current' in ref ? ref.current : ref);
43 | warning(
44 | !!currentTarget,
45 | 'RootClose captured a close event but does not have a ref to compare it to. '
46 | + 'useRootClose(), should be passed a ref that resolves to a DOM node',
47 | );
48 |
49 | preventMouseRootCloseRef.current = !currentTarget
50 | || isModifiedEvent(e)
51 | || !isLeftClickEvent(e)
52 | || contains(currentTarget, e.target);
53 | },
54 | [ref],
55 | );
56 |
57 | const handleMouse = useEventCallback((e) => {
58 | if (!preventMouseRootCloseRef.current) {
59 | onClose(e);
60 | }
61 | });
62 |
63 | const handleKeyUp = useEventCallback((e) => {
64 | if (e.keyCode === escapeKeyCode) {
65 | onClose(e);
66 | }
67 | });
68 |
69 | useEffect(() => {
70 | if (disabled || ref == null) return undefined;
71 |
72 | // Use capture for this listener so it fires before React's listener, to
73 | // avoid false positives in the contains() check below if the target DOM
74 | // element is removed in the React mouse callback.
75 | const removeMouseCaptureListener = listen(
76 | document,
77 | clickTrigger,
78 | handleMouseCapture,
79 | true,
80 | );
81 |
82 | const removeMouseListener = listen(document, clickTrigger, handleMouse);
83 | const removeKeyupListener = listen(document, 'keyup', handleKeyUp);
84 |
85 | let mobileSafariHackListeners = [];
86 | if ('ontouchstart' in document.documentElement) {
87 | mobileSafariHackListeners = [].slice
88 | .call(document.body.children)
89 | .map(el => listen(el, 'mousemove', noop));
90 | }
91 |
92 | return () => {
93 | removeMouseCaptureListener();
94 | removeMouseListener();
95 | removeKeyupListener();
96 | mobileSafariHackListeners.forEach(remove => remove());
97 | };
98 | }, [
99 | ref,
100 | disabled,
101 | clickTrigger,
102 | handleMouseCapture,
103 | handleMouse,
104 | handleKeyUp,
105 | ]);
106 | }
107 |
108 | export default useRootClose;
109 |
--------------------------------------------------------------------------------
/src/hooks/useWaitForDOMRef.js:
--------------------------------------------------------------------------------
1 | //fork react-overlays/src/utils/useWaitForDOMRef.js
2 | import ownerDocument from 'dom-helpers/ownerDocument';
3 | import { useState, useEffect } from 'react';
4 |
5 | const resolveRef = (ref) => {
6 | if (typeof document === 'undefined') return undefined;
7 | if (ref == null) return ownerDocument().body;
8 | // eslint-disable-next-line no-param-reassign
9 | if (typeof ref === 'function') ref = ref();
10 |
11 | // eslint-disable-next-line no-param-reassign
12 | if (ref && ref.current) ref = ref.current;
13 | if (ref && ref.nodeType) return ref;
14 |
15 | return null;
16 | };
17 |
18 | export default function useWaitForDOMRef(ref, onResolved) {
19 | const [resolvedRef, setRef] = useState(() => resolveRef(ref));
20 |
21 | if (!resolvedRef) {
22 | const earlyRef = resolveRef(ref);
23 | if (earlyRef) setRef(earlyRef);
24 | }
25 |
26 | useEffect(() => {
27 | if (onResolved && resolvedRef) {
28 | onResolved(resolvedRef);
29 | }
30 | }, [onResolved, resolvedRef]);
31 |
32 | useEffect(() => {
33 | const nextRef = resolveRef(ref);
34 | if (nextRef !== resolvedRef) {
35 | setRef(nextRef);
36 | }
37 | }, [ref, resolvedRef]);
38 |
39 | return resolvedRef;
40 | }
41 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as Accordion } from './components/Accordion';
2 | export { useAccordionToggle } from './components/Accordion/Toggle';
3 | export { default as AskBox } from './components/AskBox';
4 | export { default as Avatar } from './components/Avatar';
5 | export { default as Badge } from './components/Badge';
6 | export { default as Breadcrumb } from './components/Breadcrumb';
7 | export { default as BubbleChat } from './components/BubbleChat';
8 | export { default as Button } from './components/Button';
9 | export { default as Carousel } from './components/Carousel';
10 | export { default as ChatBox } from './components/ChatBox';
11 | export { default as Collapse } from './components/Collapse';
12 | export { default as Composer } from './components/Composer';
13 | export { default as Counter } from './components/Counter';
14 | export { default as Card } from './components/Card';
15 | export { Calendar, DatePicker, DateRangePicker, TimePicker } from './components/Calender';
16 | export { default as Dropdown } from './components/Dropdown';
17 | export { useToggle } from './components/Dropdown/Toggle';
18 | export { default as EmptyState } from './components/EmptyState';
19 | export { default as FileAttachment } from './components/FileAttachment';
20 | export { default as Form } from './components/Form';
21 | export { default as Header } from './components/Header';
22 | export { default as HeaderMobile } from './components/Header/Mobile';
23 | export { default as Icon } from './components/Icon';
24 | export { default as PageLayout } from './components/PageLayout';
25 | export { default as Loader } from './components/Loader';
26 | export { default as Logo } from './components/Logo';
27 | export { default as Media } from './components/Media';
28 | export { default as Message } from './components/Message';
29 | export { default as Modal } from './components/Modal';
30 | export { default as MultiSteps } from './components/MultiSteps';
31 | export { default as Overlay } from './components/Overlay';
32 | export { default as Pagination } from './components/Pagination';
33 | export { default as ProblemInfo } from './components/ProblemInfo';
34 | export { default as Progress } from './components/Progress';
35 | export { default as Rating } from './components/Rating';
36 | export { default as SearchBox } from './components/SearchBox';
37 | export { default as Separator } from './components/Separator';
38 | export { default as SessionType } from './components/SessionType';
39 | export { default as SidebarMenu } from './components/SidebarMenu';
40 | export { default as Skeleton } from './components/Skeleton';
41 | export { default as Slider } from './components/Slider';
42 | export { default as Tab } from './components/Tab';
43 | export { default as Tag } from './components/Tag';
44 | export { default as TagInput } from './components/TagInput';
45 | export { toast, default as ToastContainer } from './components/Toast';
46 | export { default as Toggle } from './components/Toggle';
47 | export { default as Tooltip } from './components/Tooltip';
48 | export { default as TopBanner } from './components/TopBanner';
49 | export { default as TopMenu } from './components/TopMenu';
50 | export { default as SafeAnchor } from './components/SafeAnchor';
51 | export { default as Fade } from './components/Fade';
52 | export { default as createBlock } from './utils/createBlock';
53 | export { default as useRootClose } from './hooks/useRootClose';
54 | export { default as Plugins } from './plugins';
55 | export { default as AssetPlugin } from './plugins/AssetPlugin';
56 |
--------------------------------------------------------------------------------
/src/plugins/AssetPlugin.js:
--------------------------------------------------------------------------------
1 | import { PluginType } from 'constants/common';
2 |
3 | class AssetPlugin {
4 | constructor({
5 | prefix,
6 | assets,
7 | }) {
8 | this.type = PluginType.ASSET;
9 | this.validateAssets(assets);
10 | this.prefix = prefix;
11 | this.assets = assets;
12 | }
13 |
14 | validateAssets(assets) {
15 | if (!assets) {
16 | throw new Error('Invalid plugin: missing "assets".');
17 | }
18 | if (typeof assets !== 'object') {
19 | throw new Error('Invalid plugin: assets must be an object of key-value pairs: key is the asset name and value is asset url.');
20 | }
21 | }
22 |
23 | getAsset(...args) {
24 | if (args.length === 0) return undefined;
25 | if (args.length === 1) {
26 | const assetName = args[0];
27 | return this.assets[assetName];
28 | }
29 | if (args.length === 2) {
30 | const prefix = args[0];
31 | const assetName = args[1];
32 | if (prefix !== this.prefix) return undefined;
33 | return this.assets[assetName];
34 | }
35 | return undefined;
36 | }
37 | }
38 |
39 | export default AssetPlugin;
40 |
--------------------------------------------------------------------------------
/src/plugins/PluginArray.js:
--------------------------------------------------------------------------------
1 | class PluginArray extends Array {
2 | traverseCall(methodName, ...param) {
3 | const results = [];
4 | this.forEach((plugin) => {
5 | if (typeof plugin[methodName] !== 'function') {
6 | throw new Error(`Invalid plugin: One plugin does not have method with name "${methodName}".`);
7 | }
8 | const result = plugin[methodName](...param);
9 | results.push(result);
10 | });
11 | return results;
12 | }
13 | }
14 |
15 | export default PluginArray;
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | import { PluginType } from 'constants/common';
2 | import AssetPlugin from './AssetPlugin';
3 | import PluginArray from './PluginArray';
4 |
5 | class Plugins {
6 | constructor() {
7 | this.plugins = new PluginArray();
8 | }
9 |
10 | validatePlugin(plugin) {
11 | if (!plugin) {
12 | throw new Error('Invalid plugin: Can not read plugin.');
13 | }
14 | if (!plugin.type) {
15 | throw new Error('Invalid plugin: missing "type".');
16 | }
17 | switch (plugin.type) {
18 | case PluginType.ASSET: {
19 | if (!(plugin instanceof AssetPlugin)) {
20 | throw new Error(`Invalid plugin: plugin with type "${PluginType.ASSET}" must be constructed from class AssetPlugin.`);
21 | }
22 | break;
23 | }
24 | default:
25 | break;
26 | }
27 | }
28 |
29 | loadPlugin(plugin) {
30 | this.validatePlugin(plugin);
31 | this.plugins.push(plugin);
32 | }
33 |
34 | getPlugins(type = undefined) {
35 | if (!type) {
36 | return this.plugins;
37 | }
38 | return this.plugins.filter(plugin => plugin.type === type);
39 | }
40 | }
41 |
42 | export default new Plugins();
43 |
--------------------------------------------------------------------------------
/src/utils/createBlock.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import camelize from 'dom-helpers/camelize';
4 |
5 | const pascalCase = str => str[0].toUpperCase() + camelize(str).slice(1);
6 |
7 | function createBlock(
8 | prefix,
9 | { displayName = pascalCase(prefix), Component = 'div', defaultProps } = {},
10 | ) {
11 | const Block = React.forwardRef(
12 | ({ className, as: Tag = Component, ...props }, ref) => (
13 |
18 | ),
19 | );
20 | Block.defaultProps = defaultProps;
21 | Block.displayName = displayName;
22 | return Block;
23 | }
24 | export default createBlock;
25 |
--------------------------------------------------------------------------------
/src/utils/createChainedFunction.js:
--------------------------------------------------------------------------------
1 |
2 | function createChainedFunction(...funcs) {
3 | return funcs
4 | .filter(f => f != null)
5 | .reduce((acc, f) => {
6 | if (typeof f !== 'function') {
7 | throw new Error(
8 | 'Invalid Argument Type, must only provide functions, undefined, or null.',
9 | );
10 | }
11 |
12 | if (acc === null) return f;
13 |
14 | return function chainedFunction(...args) {
15 | acc.apply(this, args);
16 | f.apply(this, args);
17 | };
18 | }, null);
19 | }
20 | export default createChainedFunction;
21 |
--------------------------------------------------------------------------------
/src/utils/setRef.js:
--------------------------------------------------------------------------------
1 | // TODO: Make it private only in v5
2 | export default function setRef(ref, value) {
3 | if (typeof ref === 'function') {
4 | ref(value);
5 | } else if (ref) {
6 | // eslint-disable-next-line no-param-reassign
7 | ref.current = value;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/triggerBrowserReflow.js:
--------------------------------------------------------------------------------
1 | // reading a dimension prop will cause the browser to recalculate,
2 | // which will let our animations work
3 | export default function triggerBrowserReflow(node) {
4 | node.offsetHeight; // eslint-disable-line no-unused-expressions
5 | }
6 |
--------------------------------------------------------------------------------