mirror of https://github.com/argoproj/argo-ui.git
Compare commits
No commits in common. "master" and "v1.1.15" have entirely different histories.
|
@ -0,0 +1,29 @@
|
||||||
|
version: 2
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
docker:
|
||||||
|
- image: node:12.18.4
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||||
|
- run: yarn install --frozen-lockfile --ignore-optional --non-interactive
|
||||||
|
- save_cache:
|
||||||
|
key: yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||||
|
paths: [~/.cache/yarn, node_modules]
|
||||||
|
- run: yarn build
|
||||||
|
- run: yarn lint
|
||||||
|
- run: yarn tsc -p src/tsconfig.json
|
||||||
|
- run:
|
||||||
|
name: Install JUnit coverage reporter
|
||||||
|
command: yarn add --dev jest-junit
|
||||||
|
- run:
|
||||||
|
name: Run tests with JUnit as reporter
|
||||||
|
command: yarn test --ci --runInBand --reporters=default --reporters=jest-junit
|
||||||
|
environment:
|
||||||
|
JEST_JUNIT_OUTPUT_DIR: ./reports/junit/
|
||||||
|
- store_test_results:
|
||||||
|
path: ./reports/junit/
|
||||||
|
- store_artifacts:
|
||||||
|
path: ./reports/junit
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
Dockerfile
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:react/recommended"
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest",
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint", "react"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off"
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "./stories/*.tsx",
|
|
||||||
"rules": {
|
|
||||||
"react/display-name": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
# prod dependencies
|
|
||||||
- package-ecosystem: "npm"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
day: "saturday"
|
|
||||||
# ignore all non-security updates: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit
|
|
||||||
open-pull-requests-limit: 0
|
|
||||||
labels:
|
|
||||||
- type/dependencies
|
|
||||||
- javascript
|
|
||||||
commit-message:
|
|
||||||
prefix: chore(deps)
|
|
||||||
prefix-development: chore(deps-dev)
|
|
||||||
|
|
||||||
# build / CI dependencies
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
day: "saturday"
|
|
||||||
# ignore all non-security updates: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit
|
|
||||||
open-pull-requests-limit: 0
|
|
||||||
labels:
|
|
||||||
- type/dependencies
|
|
||||||
- github_actions
|
|
||||||
commit-message:
|
|
||||||
prefix: chore(deps)
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: Build Storybook Docs v2
|
name: Build Storybook Docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -7,9 +7,9 @@ jobs:
|
||||||
build-docs:
|
build-docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version: 12
|
||||||
- run: yarn install
|
- run: yarn --cwd v2 install
|
||||||
- run: yarn build-v2
|
- run: yarn --cwd v2 build-storybook-docs
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
name: Build v1
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- "master"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: ".nvmrc"
|
|
||||||
- run: yarn install
|
|
||||||
- run: yarn deduplicate
|
|
||||||
- run: yarn build
|
|
||||||
- run: yarn lint
|
|
||||||
- run: yarn tsc -p tsconfig.json
|
|
||||||
- run: yarn test --ci --runInBand
|
|
|
@ -1,28 +0,0 @@
|
||||||
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
|
|
||||||
name: Approve and enable auto-merge for dependabot
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
review:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
||||||
steps:
|
|
||||||
- name: Dependabot metadata
|
|
||||||
id: metadata
|
|
||||||
uses: dependabot/fetch-metadata@v1.6.0
|
|
||||||
with:
|
|
||||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
- name: Approve PR
|
|
||||||
run: gh pr review --approve "$PR_URL"
|
|
||||||
env:
|
|
||||||
PR_URL: ${{github.event.pull_request.html_url}}
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
- name: Enable auto-merge for Dependabot PRs
|
|
||||||
run: gh pr merge --auto --squash "$PR_URL"
|
|
||||||
env:
|
|
||||||
PR_URL: ${{github.event.pull_request.html_url}}
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
|
@ -1,18 +0,0 @@
|
||||||
name: Release NPM package v1
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- v1.*
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: ".nvmrc"
|
|
||||||
- run: npm publish
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: Release NPM package v2
|
name: Release NPM package
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
@ -11,12 +11,12 @@ jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.tag }}
|
ref: ${{ github.event.inputs.tag }}
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version: 12
|
||||||
- run: cd v2 && npm publish
|
- run: cd v2 && npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# https://github.com/actions/stale
|
|
||||||
name: Mark stale issues and pull requests
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 2 * * *' # once a day at 2am
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
permissions:
|
|
||||||
issues: write # for commenting on an issue and editing labels
|
|
||||||
pull-requests: write # for commenting on a PR and editing labels
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# timing
|
|
||||||
days-before-stale: 14 # 2 weeks of inactivity
|
|
||||||
days-before-close: 14 # 2 more weeks of inactivity
|
|
||||||
# labels to watch for, add, and remove
|
|
||||||
only-labels: 'problem/more information needed' # only mark issues/PRs as stale if they have this label
|
|
||||||
labels-to-remove-when-unstale: 'problem/more information needed' # remove label when unstale -- should be manually added back if information is insufficient
|
|
||||||
stale-issue-label: 'problem/stale'
|
|
||||||
stale-pr-label: 'problem/stale'
|
|
||||||
# automated messages to issue/PR authors
|
|
||||||
stale-issue-message: >
|
|
||||||
This issue has been automatically marked as stale because it has not had recent activity and needs more information.
|
|
||||||
It will be closed if no further activity occurs.
|
|
||||||
stale-pr-message: >
|
|
||||||
This PR has been automatically marked as stale because it has not had recent activity and needs further changes.
|
|
||||||
It will be closed if no further activity occurs.
|
|
||||||
close-issue-message: >
|
|
||||||
This issue has been closed due to inactivity and lack of information.
|
|
||||||
If you still encounter this issue, please add the requested information and re-open.
|
|
||||||
close-pr-message:
|
|
||||||
This PR has been closed due to inactivity and lack of changes.
|
|
||||||
If you would like to still work on this PR, please address the review comments and re-open.
|
|
|
@ -2,8 +2,6 @@ node_modules
|
||||||
dist
|
dist
|
||||||
bundle
|
bundle
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
|
||||||
*.log
|
|
||||||
coverage/
|
coverage/
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
docs/
|
docs/
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { configure } from '@storybook/react';
|
||||||
|
|
||||||
|
// automatically import all files ending in *.stories.js
|
||||||
|
const req = require.context('../stories', true, /.stories.tsx$/);
|
||||||
|
function loadStories() {
|
||||||
|
req.keys().forEach((filename) => req(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(loadStories, module);
|
|
@ -1,21 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
stories: ['../stories/*.stories.tsx'],
|
|
||||||
addons: ['@storybook/addon-essentials'],
|
|
||||||
typescript: {
|
|
||||||
check: false, // typecheck separately
|
|
||||||
reactDocgen: false, // substantially improves performance: https://github.com/storybookjs/storybook/issues/22164#issuecomment-1603627308
|
|
||||||
},
|
|
||||||
webpackFinal: async (config, {configType}) => {
|
|
||||||
config.devtool = false; // perf per: https://github.com/storybookjs/storybook/issues/19736#issuecomment-1478103817
|
|
||||||
config.module.rules.push({
|
|
||||||
test: /\.scss$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
include: path.resolve(__dirname, '../'),
|
|
||||||
sideEffects: true, // get side-effect styles to load per: https://github.com/storybookjs/storybook/issues/4690#issuecomment-435909433
|
|
||||||
loader: 'style-loader!raw-loader!sass-loader'
|
|
||||||
});
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
// import '../src/styles/main.scss'; -- this seems to not work and also makes the Storybook freeze for multiple minutes 😕
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
const path = require('path');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = ({config}) => {
|
||||||
|
config.module.rules = [{
|
||||||
|
test: /\.(ts|tsx)$/,
|
||||||
|
loader: `ts-loader?configFile=${path.resolve('./stories/tsconfig.json')}`
|
||||||
|
}, {
|
||||||
|
test: /\.scss$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'style-loader!raw-loader!sass-loader'
|
||||||
|
}, {
|
||||||
|
test: /\.css$/,
|
||||||
|
loader: 'style-loader!raw-loader'
|
||||||
|
}];
|
||||||
|
config.resolve = {
|
||||||
|
extensions: ['.ts', '.tsx', '.js', '.json']
|
||||||
|
};
|
||||||
|
config.plugins.push(new CopyWebpackPlugin([{
|
||||||
|
from: 'src/assets', to: 'assets'
|
||||||
|
}, {
|
||||||
|
from: 'node_modules/@fortawesome/fontawesome-free/webfonts', to: 'assets/fonts'
|
||||||
|
}]));
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
Use the storybook to test components:
|
||||||
|
|
||||||
|
~~~
|
||||||
|
yarn storybook
|
||||||
|
~~~
|
34
README.md
34
README.md
|
@ -1,35 +1,11 @@
|
||||||
# Argo UI Components
|
# Argo UI Components
|
||||||
|
|
||||||
<img src="https://github.com/argoproj/argo-ui/blob/master/src/assets/images/logo.png?raw=true" alt="Argo Image" height="200px">
|

|
||||||
|
|
||||||
Set of React components used by [Argo Workflows](https://github.com/argoproj/argo-workflows), [Argo CD](https://github.com/argoproj/argo-cd), and [Argo Rollouts](https://github.com/argoproj/argo-rollouts).
|
Set of React components used by https://github.com/argoproj/argo and https://github.com/argoproj/argo-cd
|
||||||
|
|
||||||
## Build & Run
|
## Build & Run
|
||||||
|
|
||||||
1. Install Toolset: [NodeJS](https://nodejs.org/en/download/) and [Yarn v1](https://classic.yarnpkg.com/en/docs)
|
1. Install Toolset: [NodeJS](https://nodejs.org/en/download/) and [Yarn](https://yarnpkg.com)
|
||||||
1. Install Dependencies: run `yarn install`
|
2. Install Dependencies: From your command line, navigate to the argo-ui directory and run `yarn install` to install dependencies.
|
||||||
1. Run: `yarn start` - starts the [Storybook v6](https://storybook.js.org/docs/6.5/get-started/install) dev server
|
3. Run: `yarn start` - starts https://storybook.js.org/ dev server
|
||||||
|
|
||||||
## Local Development
|
|
||||||
|
|
||||||
To test your changes locally against Argo CD or another Argo project, we recommend using [`yalc`](https://github.com/wclr/yalc).
|
|
||||||
|
|
||||||
First, install `yalc`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i -g yalc
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, in your local `argo-ui` directory, run
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yalc publish
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, in your local `argo-cd/ui` directory, run
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yalc add argo-ui
|
|
||||||
```
|
|
||||||
|
|
||||||
Your local changes to the `argo-ui` package will now be seen by your local `argo-cd`.
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ module.exports = {
|
||||||
setupFilesAfterEnv: ['<rootDir>src/setupTests.ts'],
|
setupFilesAfterEnv: ['<rootDir>src/setupTests.ts'],
|
||||||
globals: {
|
globals: {
|
||||||
'ts-jest': {
|
'ts-jest': {
|
||||||
tsconfig: '<rootDir>/tsconfig.json'
|
tsconfig: '<rootDir>/src/tsconfig.json'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
126
package.json
126
package.json
|
@ -3,96 +3,98 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"license": "Apache-2.0",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src"
|
||||||
"v2"
|
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .tsx .",
|
"build": "build-storybook -o ./dist/storybook",
|
||||||
"deduplicate": "yarn-deduplicate -s fewer yarn.lock",
|
"lint": "tslint -p ./src",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"utils:icons": "rm -f src/assets/fonts/* && node ./scripts/icons/generator.js",
|
"utils:icons": "rm -f src/assets/fonts/* && node ./scripts/icons/generator.js",
|
||||||
"start": "NODE_OPTIONS='--openssl-legacy-provider' start-storybook -p 6006",
|
"start": "start-storybook -p 6006"
|
||||||
"build": "NODE_OPTIONS='--openssl-legacy-provider' build-storybook -o ./dist/storybook",
|
|
||||||
"start-v2": "NODE_OPTIONS='--openssl-legacy-provider' start-storybook -c ./v2/.storybook -s ./v2/.storybook/images -p 6006",
|
|
||||||
"build-v2": "NODE_OPTIONS='--openssl-legacy-provider' build-storybook -o ./dist/storybook-v2 -c ./v2/.storybook"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
"@fortawesome/fontawesome-free": "^5.8.1",
|
||||||
"@tippy.js/react": "^3.1.1",
|
"@tippy.js/react": "^2.1.2",
|
||||||
"classnames": "^2.2.6",
|
"@types/react-autocomplete": "^1.8.5",
|
||||||
"core-js": "^3.32.1",
|
"@types/react-form": "^2.16.1",
|
||||||
|
"@types/react-helmet": "^6.1.0",
|
||||||
|
"classnames": "^2.2.5",
|
||||||
"foundation-sites": "^6.4.3",
|
"foundation-sites": "^6.4.3",
|
||||||
"history": "^4.10.1",
|
"history": "^4.7.2",
|
||||||
"prop-types": "^15.8.1",
|
"moment": "^2.20.1",
|
||||||
"react-autocomplete": "1.8.1",
|
"prop-types": "^15.6.0",
|
||||||
|
"react-autocomplete": "^1.8.1",
|
||||||
"react-form": "^2.16.0",
|
"react-form": "^2.16.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-toastify": "9.0.3",
|
"react-toastify": "^5.0.1",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^6.6.6",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.0.3",
|
||||||
"uuid": "^9.0.0",
|
"xterm": "2.4.0"
|
||||||
"xterm": "^4.19.0",
|
|
||||||
"xterm-addon-fit": "^0.5.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^16.8.5",
|
|
||||||
"react": "^16.9.3",
|
"react": "^16.9.3",
|
||||||
"react-dom": "^16.9.3"
|
"react-dom": "^16.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.21.3",
|
"@babel/core": "^7.11.6",
|
||||||
"@storybook/addon-actions": "6.5.0-beta.1",
|
"@dump247/storybook-state": "^1.5.0",
|
||||||
"@storybook/addon-controls": "6.5.0-beta.1",
|
"@storybook/addon-actions": "^6.0.22",
|
||||||
"@storybook/addon-essentials": "6.5.0-beta.1",
|
"@storybook/addon-links": "^6.0.22",
|
||||||
"@storybook/addon-links": "6.5.0-beta.1",
|
"@storybook/addons": "^6.0.22",
|
||||||
"@storybook/addons": "6.5.0-beta.1",
|
"@storybook/react": "^6.0.22",
|
||||||
"@storybook/react": "6.5.0-beta.1",
|
"@types/chai": "^4.1.2",
|
||||||
"@types/classnames": "^2.3.1",
|
"@types/classnames": "^2.2.3",
|
||||||
"@types/deep-equal": "^1.0.1",
|
"@types/deep-equal": "^1.0.1",
|
||||||
"@types/enzyme": "^3.10.12",
|
"@types/enzyme": "^3.10.8",
|
||||||
"@types/enzyme-adapter-react-16": "^1.0.6",
|
"@types/enzyme-adapter-react-16": "^1.0.6",
|
||||||
"@types/history": "^4.7.8",
|
"@types/history": "^4.7.8",
|
||||||
"@types/jest": "^26.0.15",
|
"@types/prop-types": "^15.5.2",
|
||||||
"@types/node": "^18.15.3",
|
"@types/react": "^16.9.3",
|
||||||
"@types/prop-types": "^15.7.5",
|
|
||||||
"@types/react": "^16.8.5",
|
|
||||||
"@types/react-autocomplete": "^1.8.4",
|
|
||||||
"@types/react-dom": "^16.9.3",
|
"@types/react-dom": "^16.9.3",
|
||||||
"@types/react-form": "^2.16.1",
|
"@types/react-router-dom": "^4.2.3",
|
||||||
"@types/react-helmet": "^6.1.6",
|
"@types/react-test-renderer": "^16.9.3",
|
||||||
"@types/react-router-dom": "^4.2.2",
|
"@types/storybook__addon-actions": "^3.0.2",
|
||||||
"@types/uuid": "^9.0.3",
|
"@types/storybook__addon-links": "^3.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
"@types/storybook__react": "^3.0.7",
|
||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@types/yamljs": "^0.2.30",
|
||||||
"babel-loader": "^8.2.5",
|
"chai": "^4.1.2",
|
||||||
"copy-webpack-plugin": "^4.3.1",
|
"copy-webpack-plugin": "^4.3.1",
|
||||||
"css-loader": "^3.6.0",
|
"copyfiles": "^1.2.0",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-adapter-react-16": "^1.15.7",
|
"enzyme-adapter-react-16": "^1.15.5",
|
||||||
"enzyme-to-json": "^3.6.2",
|
"enzyme-to-json": "^3.6.1",
|
||||||
"eslint": "^8.23.0",
|
"foreman": "^3.0.1",
|
||||||
"eslint-plugin-react": "^7.31.1",
|
"glob": "^7.1.2",
|
||||||
"glob": "^8.0.3",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.2",
|
||||||
"raw-loader": "^4.0.2",
|
"jscs": "^3.0.7",
|
||||||
|
"node-sass": "^4.12.0",
|
||||||
|
"nodemon": "^1.14.11",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
"react": "^16.9.3",
|
"react": "^16.9.3",
|
||||||
"react-dom": "^16.9.3",
|
"react-dom": "^16.9.3",
|
||||||
"sass": "^1.55.0",
|
"react-hot-loader": "^3.1.3",
|
||||||
"sass-loader": "^v10.1.0",
|
"react-test-renderer": "^16.9.3",
|
||||||
"storybook": "6.5.0-beta.1",
|
"sass-loader": "^6.0.6",
|
||||||
"style-loader": "^2.0.0",
|
"source-map-loader": "^0.2.3",
|
||||||
"ts-jest": "^26.5.6",
|
"style-loader": "^0.20.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-jest": "^26.4.3",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"ts-node": "^4.1.0",
|
||||||
|
"tslint": "^5.9.1",
|
||||||
|
"tslint-react": "^3.4.0",
|
||||||
"webfonts-generator": "^0.4.0",
|
"webfonts-generator": "^0.4.0",
|
||||||
"webpack": "^4.46.0",
|
"webpack": "^4.44.2",
|
||||||
"yarn-deduplicate": "^6.0.2"
|
"webpack-cli": "^3.3.12",
|
||||||
|
"webpack-dev-server": "^3.11.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "^16.8.5",
|
"@types/react": "16.9.3",
|
||||||
"@types/node": "14.11.2"
|
"@types/node": "14.11.2",
|
||||||
|
"@types/history": "4.7.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 190 KiB |
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactForm from 'react-form';
|
import * as ReactForm from 'react-form';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export const AutocompleteField = ReactForm.FormField((props: AutocompleteProps &
|
||||||
}}
|
}}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
className: props.className,
|
className: props.className,
|
||||||
style: {borderBottom: 'none', position: 'unset'},
|
style: {borderBottom: 'none'},
|
||||||
}}
|
}}
|
||||||
value={value}
|
value={value}
|
||||||
renderInput={(inputProps) => (
|
renderInput={(inputProps) => (
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
.autocomplete {
|
|
||||||
&__items__item {
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import {CSSProperties, ReactNode} from 'react';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import ReactAutocomplete from 'react-autocomplete';
|
import * as ReactAutocomplete from 'react-autocomplete';
|
||||||
|
|
||||||
require('./autocomplete.scss');
|
|
||||||
export interface AutocompleteApi {
|
export interface AutocompleteApi {
|
||||||
refresh(): any;
|
refresh(): any;
|
||||||
}
|
}
|
||||||
|
@ -25,8 +23,6 @@ export interface AutocompleteProps {
|
||||||
autoCompleteRef?: (api: AutocompleteApi) => any;
|
autoCompleteRef?: (api: AutocompleteApi) => any;
|
||||||
filterSuggestions?: boolean;
|
filterSuggestions?: boolean;
|
||||||
qeid?: string;
|
qeid?: string;
|
||||||
/** @default true */ // per https://github.com/reactjs/react-autocomplete/blob/41388f7d7760bf6cf38e7946e43d4fddd9c7c176/lib/Autocomplete.js#L188
|
|
||||||
autoHighlight?: ReactAutocomplete.Props['autoHighlight'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Autocomplete = (props: AutocompleteProps) => {
|
export const Autocomplete = (props: AutocompleteProps) => {
|
||||||
|
@ -59,7 +55,7 @@ export const Autocomplete = (props: AutocompleteProps) => {
|
||||||
wrapperProps.className = classNames('select', wrapperProps.className);
|
wrapperProps.className = classNames('select', wrapperProps.className);
|
||||||
return (
|
return (
|
||||||
<ReactAutocomplete
|
<ReactAutocomplete
|
||||||
autoHighlight={props.autoHighlight}
|
autoHighlight={true}
|
||||||
ref={(el: any) => {
|
ref={(el: any) => {
|
||||||
if (el) {
|
if (el) {
|
||||||
if (el.refs.input) {
|
if (el.refs.input) {
|
||||||
|
@ -109,16 +105,16 @@ export const Autocomplete = (props: AutocompleteProps) => {
|
||||||
shouldItemRender={(item: AutocompleteOption, val: string) => {
|
shouldItemRender={(item: AutocompleteOption, val: string) => {
|
||||||
return !props.filterSuggestions || item.label.toLowerCase().includes(val.toLowerCase());
|
return !props.filterSuggestions || item.label.toLowerCase().includes(val.toLowerCase());
|
||||||
}}
|
}}
|
||||||
renderMenu={function(menuItems: ReactNode[], _: string, style: CSSProperties) {
|
renderMenu={function(menuItems, _, style) {
|
||||||
if (menuItems.length === 0) {
|
if (menuItems.length === 0) {
|
||||||
return <div style={{display: 'none'}} />;
|
return <div style={{display: 'none'}} />;
|
||||||
}
|
}
|
||||||
return <div style={{...style, ...this.menuStyle, background: 'white', zIndex: 20, maxHeight: '20em'}}>{menuItems}</div>;
|
return <div style={{...style, ...this.menuStyle, background: 'white', zIndex: 10, maxHeight: '20em'}} children={menuItems} />;
|
||||||
}}
|
}}
|
||||||
getItemValue={(item: any) => item.label}
|
getItemValue={(item) => item.label}
|
||||||
items={items}
|
items={items}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
renderItem={(item: any, isSelected: boolean) => (
|
renderItem={(item, isSelected) => (
|
||||||
<div className={classNames('select__option', {selected: isSelected})} key={item.label}>
|
<div className={classNames('select__option', {selected: isSelected})} key={item.label}>
|
||||||
{(props.renderItem && props.renderItem(item)) || item.label}
|
{(props.renderItem && props.renderItem(item)) || item.label}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,7 +31,7 @@ interface LoaderState<TInput, TResult> {
|
||||||
inputChanged: boolean;
|
inputChanged: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataLoader<D = any, I = undefined> extends React.Component<LoaderProps<I, D> | LoaderPropsNoInput<D>, LoaderState<I, D>> {
|
export class DataLoader<D = {}, I = undefined> extends React.Component<LoaderProps<I, D> | LoaderPropsNoInput<D>, LoaderState<I, D>> {
|
||||||
public static contextTypes = {
|
public static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
apis: PropTypes.object,
|
apis: PropTypes.object,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
import { BehaviorSubject, fromEvent, merge, Subscription } from 'rxjs';
|
import { BehaviorSubject, fromEvent, merge, Subscription } from 'rxjs';
|
||||||
|
@ -9,7 +9,6 @@ export interface DropDownProps {
|
||||||
anchor: React.ComponentType;
|
anchor: React.ComponentType;
|
||||||
children: React.ReactNode | (() => React.ReactNode);
|
children: React.ReactNode | (() => React.ReactNode);
|
||||||
qeId?: string;
|
qeId?: string;
|
||||||
onOpenStateChange?: (open: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropDownState {
|
export interface DropDownState {
|
||||||
|
@ -59,12 +58,11 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UNSAFE_componentWillMount() {
|
public componentWillMount() {
|
||||||
this.subscriptions = [merge(
|
this.subscriptions = [merge(
|
||||||
dropDownOpened.pipe(filter((dropdown) => dropdown !== this)),
|
dropDownOpened.pipe(filter((dropdown) => dropdown !== this)),
|
||||||
fromEvent(document, 'click').pipe(filter((event: Event) => {
|
fromEvent(document, 'click').pipe(filter((event: Event) => {
|
||||||
const targetAttached = (event.target as Node).parentElement;
|
return this.content && this.state.opened && !this.content.contains(event.target as Node) && !this.el.contains(event.target as Node);
|
||||||
return targetAttached && this.content && this.state.opened && !this.content.contains(event.target as Node) && !this.el.contains(event.target as Node);
|
|
||||||
})),
|
})),
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.close();
|
this.close();
|
||||||
|
@ -82,9 +80,6 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
this.setState({ opened: false });
|
this.setState({ opened: false });
|
||||||
if (this.props.onOpenStateChange) {
|
|
||||||
this.props.onOpenStateChange(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private refreshState() {
|
private refreshState() {
|
||||||
|
@ -117,8 +112,6 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
||||||
const newState = this.refreshState();
|
const newState = this.refreshState();
|
||||||
newState.opened = true;
|
newState.opened = true;
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
if (this.props.onOpenStateChange) {
|
dropDownOpened.next(this);
|
||||||
this.props.onOpenStateChange(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
|
import * as moment from 'moment';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { formatDuration } from '../../v2';
|
export const Duration = (props: {durationMs: number, allowNewLines?: boolean}) => {
|
||||||
|
const momentTimeStart = moment.utc(0);
|
||||||
|
const momentTime = moment.utc(props.durationMs * 1000);
|
||||||
|
const duration = moment.duration(momentTime.diff(momentTimeStart));
|
||||||
|
let formattedTime = '';
|
||||||
|
|
||||||
/**
|
if (momentTime.diff(momentTimeStart, 'hours') === 0) {
|
||||||
* Output a string duration from a number of seconds
|
formattedTime = ('0' + duration.minutes()).slice(-2) + ':' + ('0' + duration.seconds()).slice(-2) + ' min';
|
||||||
*
|
} else {
|
||||||
* @param {number} props.durationS - The number of seconds.
|
if (momentTime.diff(momentTimeStart, 'days') > 0) {
|
||||||
*/
|
formattedTime += momentTime.diff(momentTimeStart, 'days') + ' days' + (props.allowNewLines ? '<br>' : ' ');
|
||||||
export function Duration(props: {durationS: number}) {
|
}
|
||||||
return <span>{formatDuration(props.durationS, 2)}</span>;
|
|
||||||
}
|
formattedTime += ('0' + duration.hours()).slice(-2) + ':' + ('0' + duration.minutes()).slice(-2) + ' hours';
|
||||||
|
}
|
||||||
|
return <span dangerouslySetInnerHTML={{__html: formattedTime}}/>;
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactForm from 'react-form';
|
import * as ReactForm from 'react-form';
|
||||||
|
|
||||||
import { Select as ArgoSelect, SelectOption, SelectProps } from '../select/select';
|
import { Select as ArgoSelect, SelectOption, SelectProps } from '../select/select';
|
||||||
|
|
||||||
import { v1 as uuid } from 'uuid';
|
const uuid = require('uuid/v1');
|
||||||
|
|
||||||
require('./form-field.scss');
|
require('./form-field.scss');
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ require('../styles/main.scss');
|
||||||
|
|
||||||
export { Utils } from './utils';
|
export { Utils } from './utils';
|
||||||
export { Layout } from './layout/layout';
|
export { Layout } from './layout/layout';
|
||||||
export { Page, PageContext, type PageContextProps } from './page/page';
|
export { Page, PageContext, PageContextProps } from './page/page';
|
||||||
export { MockupList } from './mockup-list/mockup-list';
|
export { MockupList } from './mockup-list/mockup-list';
|
||||||
export { DropDown } from './dropdown/dropdown';
|
export { DropDown } from './dropdown/dropdown';
|
||||||
export { DropDownMenu, type DropDownMenuProps, type MenuItem } from './dropdown-menu';
|
export { DropDownMenu, DropDownMenuProps, MenuItem } from './dropdown-menu';
|
||||||
export { Checkbox } from './checkbox';
|
export { Checkbox } from './checkbox';
|
||||||
export { type TopBarProps, type Toolbar, type TopBarFilter } from './top-bar/top-bar';
|
export { TopBarProps, Toolbar, TopBarFilter } from './top-bar/top-bar';
|
||||||
export { type Tab, Tabs } from './tabs/tabs';
|
export { Tab, Tabs } from './tabs/tabs';
|
||||||
export { Duration } from './duration';
|
export { Duration } from './duration';
|
||||||
export { SlidingPanel } from './sliding-panel/sliding-panel';
|
export { SlidingPanel } from './sliding-panel/sliding-panel';
|
||||||
export { LogsViewer } from './logs-viewer/logs-viewer';
|
export { LogsViewer } from './logs-viewer/logs-viewer';
|
||||||
|
@ -16,7 +16,7 @@ export * from './notifications/notifications';
|
||||||
export * from './notifications/notification-manager';
|
export * from './notifications/notification-manager';
|
||||||
export * from './popup/popup';
|
export * from './popup/popup';
|
||||||
export * from './popup/popup-manager';
|
export * from './popup/popup-manager';
|
||||||
export { Select, type SelectOption, type SelectProps } from './select/select';
|
export { Select, SelectOption, SelectProps } from './select/select';
|
||||||
export { HelpIcon } from './help-icon/help-icon';
|
export { HelpIcon } from './help-icon/help-icon';
|
||||||
export { Tooltip } from './tooltip/tooltip';
|
export { Tooltip } from './tooltip/tooltip';
|
||||||
export * from './ticker';
|
export * from './ticker';
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed('background-1');
|
|
||||||
}
|
|
||||||
&__loader {
|
&__loader {
|
||||||
@include themify($themes) {
|
background-color: rgba($argo-color-gray-7, 0.4);
|
||||||
background-color: themed('layout-loader-bg');
|
|
||||||
}
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {NavBar, NavBarStyle} from '../nav-bar/nav-bar';
|
import { NavBar } from '../nav-bar/nav-bar';
|
||||||
|
|
||||||
require('./layout.scss');
|
require('./layout.scss');
|
||||||
|
|
||||||
export interface LayoutProps {
|
export interface LayoutProps {
|
||||||
navItems: Array<{ path: string; iconClassName: string; title: string; }>;
|
navItems: Array<{ path: string; iconClassName: string; title: string; }>;
|
||||||
version?: () => React.ReactElement;
|
version?: () => React.ReactElement;
|
||||||
navBarStyle?: NavBarStyle;
|
|
||||||
theme?: string;
|
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Layout = (props: LayoutProps) => (
|
export const Layout = (props: LayoutProps) => (
|
||||||
<div className={props.theme ? 'theme-' + props.theme : 'theme-light'}>
|
<div className='layout'>
|
||||||
<div className='layout'>
|
<NavBar items={props.navItems} version={props.version}/>
|
||||||
<NavBar items={props.navItems} version={props.version} style={props.navBarStyle} />
|
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import 'node_modules/xterm/css/xterm';
|
@import 'node_modules/xterm/dist/xterm';
|
||||||
|
|
||||||
.logs-viewer {
|
.logs-viewer {
|
||||||
font: normal 13px/1.2 'Courier', sans-serif;
|
font: normal 13px/1.2 'Courier', sans-serif;
|
||||||
|
@ -11,3 +11,16 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.xterm-theme-ax {
|
||||||
|
background-color: transparent;
|
||||||
|
color: $argo-color-gray-7;
|
||||||
|
.xterm-viewport {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-cursor {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { Terminal } from 'xterm';
|
|
||||||
import { FitAddon } from 'xterm-addon-fit';
|
const Terminal = require('xterm');
|
||||||
|
Terminal.loadAddon('fit');
|
||||||
|
|
||||||
require('./logs-viewer.scss');
|
require('./logs-viewer.scss');
|
||||||
|
|
||||||
|
@ -16,43 +17,27 @@ export interface LogsViewerProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogsViewer extends React.Component<LogsViewerProps> {
|
export class LogsViewer extends React.Component<LogsViewerProps> {
|
||||||
private terminal: Terminal;
|
private terminal: any;
|
||||||
private fitAddon: FitAddon;
|
|
||||||
private subscription: Subscription | null = null;
|
private subscription: Subscription | null = null;
|
||||||
|
|
||||||
constructor(props: LogsViewerProps) {
|
constructor(props: LogsViewerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UNSAFE_componentWillReceiveProps(nextProps: LogsViewerProps) {
|
public componentWillReceiveProps(nextProps: LogsViewerProps) {
|
||||||
if (this.props.source.key !== nextProps.source.key) {
|
if (this.props.source.key !== nextProps.source.key) {
|
||||||
this.refresh(nextProps.source);
|
this.refresh(nextProps.source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public initTerminal(container: HTMLElement) {
|
public initTerminal(container: HTMLElement) {
|
||||||
this.fitAddon = new FitAddon();
|
|
||||||
this.terminal = new Terminal({
|
this.terminal = new Terminal({
|
||||||
scrollback: 99999,
|
scrollback: 99999,
|
||||||
allowTransparency: true,
|
theme: 'ax',
|
||||||
theme: {
|
|
||||||
background: 'transparent',
|
|
||||||
foreground: '#495763',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
this.terminal.loadAddon(this.fitAddon);
|
|
||||||
this.terminal.open(container);
|
|
||||||
this.fitAddon.fit();
|
|
||||||
// handle Ctrl+C for copying logs
|
|
||||||
this.terminal.attachCustomKeyEventHandler((ev) => {
|
|
||||||
if (ev.ctrlKey && ev.code === "KeyC" && ev.type === "keydown") {
|
|
||||||
const selection = this.terminal.getSelection();
|
|
||||||
if (!selection) return true;
|
|
||||||
|
|
||||||
navigator.clipboard?.writeText(selection);
|
this.terminal.open(container);
|
||||||
return false
|
this.terminal.fit();
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
@ -71,14 +56,12 @@ export class LogsViewer extends React.Component<LogsViewerProps> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public shouldComponentUpdate() {
|
public shouldComponentUpdate(prevProps: LogsViewerProps) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private refresh(source: LogsSource) {
|
private refresh(source: LogsSource) {
|
||||||
if (this.terminal) {
|
this.terminal.reset();
|
||||||
this.terminal.reset();
|
|
||||||
}
|
|
||||||
this.ensureUnsubscribed();
|
this.ensureUnsubscribed();
|
||||||
const onLoadComplete = () => {
|
const onLoadComplete = () => {
|
||||||
if (source.shouldRepeat()) {
|
if (source.shouldRepeat()) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
@ -10,11 +10,6 @@ require('./nav-bar.scss');
|
||||||
export interface NavBarProps {
|
export interface NavBarProps {
|
||||||
items: Array<{ path: string; iconClassName: string; title: string; }>;
|
items: Array<{ path: string; iconClassName: string; title: string; }>;
|
||||||
version?: () => React.ReactElement;
|
version?: () => React.ReactElement;
|
||||||
style?: NavBarStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NavBarStyle {
|
|
||||||
backgroundColor?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isActiveRoute(locationPath: string, path: string) {
|
export function isActiveRoute(locationPath: string, path: string) {
|
||||||
|
@ -23,13 +18,10 @@ export function isActiveRoute(locationPath: string, path: string) {
|
||||||
|
|
||||||
export const NavBar: React.FunctionComponent<NavBarProps> = (props: NavBarProps, context: AppContext) => {
|
export const NavBar: React.FunctionComponent<NavBarProps> = (props: NavBarProps, context: AppContext) => {
|
||||||
const locationPath = context.router.route.location.pathname;
|
const locationPath = context.router.route.location.pathname;
|
||||||
const navBarStyle = {
|
|
||||||
...(props.style?.backgroundColor && {background: `linear-gradient(to bottom, ${props.style?.backgroundColor}, #999`}),
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('nav-bar', {
|
<div className={classNames('nav-bar', {
|
||||||
'nav-bar--compact': (props.items || []).length >= 10,
|
'nav-bar--compact': (props.items || []).length >= 10,
|
||||||
})} style={navBarStyle}>
|
})}>
|
||||||
<div className='nav-bar__logo'>
|
<div className='nav-bar__logo'>
|
||||||
<img src='assets/images/logo.png' alt='Argo'/>
|
<img src='assets/images/logo.png' alt='Argo'/>
|
||||||
<div className='nav-bar__version'>{props.version && props.version()}</div>
|
<div className='nav-bar__version'>{props.version && props.version()}</div>
|
||||||
|
|
|
@ -37,7 +37,9 @@ export class NavigationManager implements NavigationApi {
|
||||||
path = `${path}?${urlQuery}`;
|
path = `${path}?${urlQuery}`;
|
||||||
}
|
}
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (options.event && (options.event.metaKey || options.event.ctrlKey || options.event.button === 1)) {
|
if (options.event && options.event.metaKey) {
|
||||||
|
window.open(path, '__target');
|
||||||
|
} else if (options.event && options.event.ctrlKey) {
|
||||||
window.open(path, '_blank');
|
window.open(path, '_blank');
|
||||||
} else {
|
} else {
|
||||||
if (options.replace) {
|
if (options.replace) {
|
||||||
|
|
|
@ -47,10 +47,6 @@ export class Notifications extends React.Component<NotificationsProps> {
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
sel.addRange(range);
|
sel.addRange(range);
|
||||||
}
|
}
|
||||||
}} style={{
|
|
||||||
// fit long words by wrapping them instead of overflowing past the width
|
|
||||||
overflowWrap: 'break-word',
|
|
||||||
maxWidth: '240px'
|
|
||||||
}}>
|
}}>
|
||||||
{next.content}
|
{next.content}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
@ -12,8 +12,6 @@ require('./page.scss');
|
||||||
interface PageProps extends React.Props<any> {
|
interface PageProps extends React.Props<any> {
|
||||||
title: string;
|
title: string;
|
||||||
toolbar?: Toolbar | Observable<Toolbar>;
|
toolbar?: Toolbar | Observable<Toolbar>;
|
||||||
topBarTitle?: string;
|
|
||||||
useTitleOnly?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageContextProps {
|
export interface PageContextProps {
|
||||||
|
@ -34,7 +32,7 @@ export const Page = (props: PageProps) => {
|
||||||
<PageContext.Consumer>
|
<PageContext.Consumer>
|
||||||
{(ctx) => {
|
{(ctx) => {
|
||||||
let titleParts = [ctx.title];
|
let titleParts = [ctx.title];
|
||||||
if (!props.useTitleOnly && toolbar && toolbar.breadcrumbs && toolbar.breadcrumbs.length > 0) {
|
if (toolbar && toolbar.breadcrumbs && toolbar.breadcrumbs.length > 0) {
|
||||||
titleParts = [toolbar.breadcrumbs.map((item) => item.title).reverse().join(' / ')].concat(titleParts);
|
titleParts = [toolbar.breadcrumbs.map((item) => item.title).reverse().join(' / ')].concat(titleParts);
|
||||||
} else if (props.title) {
|
} else if (props.title) {
|
||||||
titleParts = [props.title].concat(titleParts);
|
titleParts = [props.title].concat(titleParts);
|
||||||
|
@ -47,7 +45,7 @@ export const Page = (props: PageProps) => {
|
||||||
}}
|
}}
|
||||||
</PageContext.Consumer>
|
</PageContext.Consumer>
|
||||||
<div className='page__top-bar'>
|
<div className='page__top-bar'>
|
||||||
<TopBar title={props.topBarTitle ? props.topBarTitle : props.title} toolbar={toolbar}/>
|
<TopBar title={props.title} toolbar={toolbar}/>
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||||
import { Form, FormApi, FormValues, RenderReturn, ValidateValuesFunction } from 'react-form';
|
import { Form, FormApi, FormValues, RenderReturn, ValidateValuesFunction } from 'react-form';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
import { PopupProps } from './popup';
|
import { PopupProps } from './popup';
|
||||||
|
|
||||||
export interface PopupApi {
|
export interface PopupApi {
|
||||||
confirm(title: string, message: string | React.ComponentType): Promise<boolean>;
|
confirm(title: string, message: string | React.ComponentType): Promise<boolean>;
|
||||||
|
@ -14,7 +14,7 @@ export interface PopupApi {
|
||||||
},
|
},
|
||||||
customIcon?: {name: string, color: string},
|
customIcon?: {name: string, color: string},
|
||||||
titleColor?: string,
|
titleColor?: string,
|
||||||
defaultValues?: FormValues,
|
defaultValues?: {},
|
||||||
): Promise<FormValues | null>;
|
): Promise<FormValues | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ export class PopupManager implements PopupApi {
|
||||||
content,
|
content,
|
||||||
footer: (
|
footer: (
|
||||||
<div>
|
<div>
|
||||||
<button qe-id='argo-popup-ok-button' className='argo-button argo-button--base' onClick={() => closeAndResolve(true)}>OK</button>
|
<button qe-id='argo-popup-ok-button' className='argo-button argo-button--base' onClick={() => closeAndResolve(true)}>OK</button> <button
|
||||||
<button qe-id='argo-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(false)}>Cancel</button>
|
qe-id='argo-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(false)}>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@ -58,7 +58,7 @@ export class PopupManager implements PopupApi {
|
||||||
},
|
},
|
||||||
customIcon?: { name: string, color: string },
|
customIcon?: { name: string, color: string },
|
||||||
titleColor?: string,
|
titleColor?: string,
|
||||||
defaultValues?: FormValues,
|
defaultValues?: {},
|
||||||
): Promise<FormValues | null> {
|
): Promise<FormValues | null> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const closeAndResolve = (result: FormValues | null) => {
|
const closeAndResolve = (result: FormValues | null) => {
|
||||||
|
@ -98,8 +98,8 @@ export class PopupManager implements PopupApi {
|
||||||
),
|
),
|
||||||
footer: (
|
footer: (
|
||||||
<div>
|
<div>
|
||||||
<button qe-id='prompt-popup-ok-button' className='argo-button argo-button--base' onClick={(e) => formApi.submitForm(e)}>OK</button>
|
<button qe-id='prompt-popup-ok-button' className='argo-button argo-button--base' onClick={(e) => formApi.submitForm(e)}>OK</button> <button
|
||||||
<button qe-id='prompt-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(null)}>Cancel</button>
|
qe-id='prompt-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(null)}>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
$popup-border-radius: 8px;
|
$popup-border-radius: 8px;
|
||||||
|
|
||||||
|
@ -10,17 +9,12 @@ $popup-border-radius: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
// Just in case the theme class is not added
|
|
||||||
background-color: rgba(222, 222, 222, 0.62); /*dim the background*/
|
background-color: rgba(222, 222, 222, 0.62); /*dim the background*/
|
||||||
@include themify($themes) {
|
border: 1px solid $argo-color-gray-4;
|
||||||
background-color: themed('overlay');
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-container {
|
.popup-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
@include themify($themes) {
|
box-shadow: 3px 3px 20px #888888;
|
||||||
box-shadow: 3px 3px 20px themed('shadow');
|
|
||||||
}
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
@ -29,7 +23,6 @@ $popup-border-radius: 8px;
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
border-radius: $popup-border-radius;
|
border-radius: $popup-border-radius;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
|
@ -48,16 +41,12 @@ $popup-border-radius: 8px;
|
||||||
float: right;
|
float: right;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
top: 5px;
|
top: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__normal {
|
&__normal {
|
||||||
// Just in case the theme class is not added
|
|
||||||
background-color: $argo-color-gray-2;
|
background-color: $argo-color-gray-2;
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed('light-argo-gray-2');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__red {
|
&__red {
|
||||||
|
@ -71,11 +60,7 @@ $popup-border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
// Just in case the theme class is not added
|
|
||||||
background-color: $argo-color-gray-1;
|
background-color: $argo-color-gray-1;
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed('background-2');
|
|
||||||
}
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
|
@ -94,41 +79,28 @@ $popup-border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
// Just in case the theme class is not added
|
|
||||||
background-color: $argo-color-gray-1;
|
background-color: $argo-color-gray-1;
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed('background-2');
|
|
||||||
}
|
|
||||||
|
|
||||||
&__hasNoIcon {
|
&__hasNoIcon {
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4, p, ul {
|
h4, p {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
font-size: 15px;
|
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
// Just in case the theme class is not added
|
|
||||||
color: $argo-color-gray-6;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed('light-argo-gray-6');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
font-size: 15px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
|
font-size: 15px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
color: $argo-color-gray-6;
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
export interface BasePopupProps {
|
export interface BasePopupProps {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
.select {
|
.select {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -16,22 +15,18 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px 20px 8px 0;
|
padding: 8px 20px 8px 0;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
transition: border .2s;
|
transition: border .2s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@include themify($themes) {
|
|
||||||
border-bottom: 2px solid themed('border');;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__value-arrow {
|
&__value-arrow {
|
||||||
// right most, vertically centered
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 8px;
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__options {
|
&__options {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { fromEvent, Subscription } from 'rxjs';
|
import { fromEvent, Subscription } from 'rxjs';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
@import 'node_modules/foundation-sites/scss/util/util';
|
@import 'node_modules/foundation-sites/scss/util/util';
|
||||||
|
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
$sliding-panel-header-height: 50px;
|
$sliding-panel-header-height: 50px;
|
||||||
$sliding-panel-footer-height: 64px;
|
$sliding-panel-footer-height: 64px;
|
||||||
|
@ -51,10 +50,7 @@ $sliding-panel-middle-width: 600px;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
@include themify($themes) {
|
background-color: #fff;
|
||||||
background-color: themed('background-2');
|
|
||||||
color: themed('text-2');
|
|
||||||
}
|
|
||||||
transition: right .5s;
|
transition: right .5s;
|
||||||
|
|
||||||
@include breakpoint(medium down) {
|
@include breakpoint(medium down) {
|
||||||
|
@ -101,17 +97,13 @@ $sliding-panel-middle-width: 600px;
|
||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
line-height: $sliding-panel-header-height;
|
line-height: $sliding-panel-header-height;
|
||||||
color: $argo-color-gray-5;
|
color: $argo-color-gray-5;
|
||||||
@include themify($themes) {
|
background-color: $argo-color-gray-2;
|
||||||
background-color: themed('light-argo-gray-2');
|
border-bottom: 1px solid #c6cfd1;
|
||||||
border-bottom: 1px solid themed('border');
|
|
||||||
}
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .925em;
|
font-size: .925em;
|
||||||
|
|
||||||
.sliding-panel--off-canvas & {
|
.sliding-panel--off-canvas & {
|
||||||
@include themify($themes) {
|
background-color: $argo-color-gray-2;
|
||||||
background-color: themed('light-argo-gray-2');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
|
@ -128,9 +120,8 @@ $sliding-panel-middle-width: 600px;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@include themify($themes) {
|
background-color: $argo-color-gray-2;
|
||||||
background-color: themed('light-argo-gray-2');
|
|
||||||
}
|
|
||||||
.sliding-panel:not(.sliding-panel--no-padding) & {
|
.sliding-panel:not(.sliding-panel--no-padding) & {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Key, KeybindingContext, KeybindingProvider } from '../../../v2';
|
|
||||||
|
|
||||||
export interface SlidingPanelProps extends React.Props<any> {
|
export interface SlidingPanelProps extends React.Props<any> {
|
||||||
isShown?: boolean;
|
isShown?: boolean;
|
||||||
|
@ -16,76 +15,36 @@ export interface SlidingPanelProps extends React.Props<any> {
|
||||||
|
|
||||||
require('./sliding-panel.scss');
|
require('./sliding-panel.scss');
|
||||||
|
|
||||||
export const SlidingPanel = (props: SlidingPanelProps) => {
|
export const SlidingPanel = (props: SlidingPanelProps) => (
|
||||||
return (
|
<div className={classNames('sliding-panel', {
|
||||||
<KeybindingProvider>
|
'sliding-panel--has-header': !!props.header,
|
||||||
<RenderSlidingPanel {...props} />
|
'sliding-panel--has-footer': !!props.footer,
|
||||||
</KeybindingProvider>
|
'sliding-panel--is-narrow': props.isNarrow,
|
||||||
);
|
'sliding-panel--is-middle': props.isMiddle,
|
||||||
};
|
'sliding-panel--opened': props.isShown,
|
||||||
|
'sliding-panel--no-padding': props.hasNoPadding,
|
||||||
const RenderSlidingPanel = (props: SlidingPanelProps) => {
|
'sliding-panel--off-canvas': props.offCanvas,
|
||||||
|
})}>
|
||||||
const {useKeybinding} = React.useContext(KeybindingContext);
|
<div className='sliding-panel__wrapper'>
|
||||||
|
<button className='sliding-panel__close' aria-hidden='true' onClick={() => props.onClose && props.onClose()}>
|
||||||
const closeButtonRef = React.useRef(null);
|
<span>
|
||||||
const bodyDivRef = React.useRef(null);
|
<i className='argo-icon-close' aria-hidden='true'/>
|
||||||
const panelHeaderDivRef = React.useRef(null);
|
</span>
|
||||||
const panelFooterDivRef = React.useRef(null);
|
</button>
|
||||||
|
{props.header && (
|
||||||
React.useEffect(() => {
|
<div className={classNames('sliding-panel__header', {'sliding-panel__header--close-btn-right-padding': props.hasCloseButton})}>
|
||||||
if (closeButtonRef && closeButtonRef.current) {
|
{props.header}
|
||||||
closeButtonRef.current.focus();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useKeybinding({
|
|
||||||
keys: Key.ESCAPE,
|
|
||||||
action: () => {
|
|
||||||
if (props.isShown && props.onClose) {
|
|
||||||
props.onClose();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
combo: false,
|
|
||||||
target: [closeButtonRef, bodyDivRef, panelHeaderDivRef, panelFooterDivRef],
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames('sliding-panel', {
|
|
||||||
'sliding-panel--has-header': !!props.header,
|
|
||||||
'sliding-panel--has-footer': !!props.footer,
|
|
||||||
'sliding-panel--is-narrow': props.isNarrow,
|
|
||||||
'sliding-panel--is-middle': props.isMiddle,
|
|
||||||
'sliding-panel--opened': props.isShown,
|
|
||||||
'sliding-panel--no-padding': props.hasNoPadding,
|
|
||||||
'sliding-panel--off-canvas': props.offCanvas,
|
|
||||||
})}>
|
|
||||||
<div className='sliding-panel__wrapper'>
|
|
||||||
{props.isShown && (
|
|
||||||
<button autoFocus={true} className='sliding-panel__close' aria-hidden='true' onClick={() => props.onClose && props.onClose()} ref={closeButtonRef}>
|
|
||||||
<span>
|
|
||||||
<i className='argo-icon-close' aria-hidden='true'/>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{props.header && (
|
|
||||||
<div className={classNames('sliding-panel__header', {'sliding-panel__header--close-btn-right-padding': props.hasCloseButton})}
|
|
||||||
ref={panelHeaderDivRef} tabIndex={-1}>
|
|
||||||
{props.header}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className='sliding-panel__body' ref={bodyDivRef} tabIndex={-1}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
</div>
|
||||||
{props.footer && (
|
)}
|
||||||
<div className='sliding-panel__footer' ref={panelFooterDivRef} tabIndex={-1}>
|
<div className='sliding-panel__body'>
|
||||||
{props.footer}
|
{props.children}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='sliding-panel__outside' onClick={() => props.onClose && props.onClose()}/>
|
{props.footer && (
|
||||||
|
<div className='sliding-panel__footer'>
|
||||||
|
{props.footer}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
<div className='sliding-panel__outside' onClick={() => props.onClose && props.onClose()}/>
|
||||||
};
|
</div>
|
||||||
|
);
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
.fixed-width {
|
.fixed-width {
|
||||||
|
@ -13,9 +11,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
padding: 15px 20px 0;
|
padding: 15px 20px 0;
|
||||||
@include themify($themes) {
|
background-color: $white-color;
|
||||||
background-color: themed('background-2');
|
|
||||||
}
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
box-shadow: 0 1px 3px rgba(143,164,177,.3);
|
box-shadow: 0 1px 3px rgba(143,164,177,.3);
|
||||||
|
@ -28,9 +24,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 16px;
|
margin: 0 16px;
|
||||||
padding: 12px 36px;
|
padding: 12px 36px;
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-6;
|
||||||
color: themed('light-argo-gray-6');
|
|
||||||
}
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -57,17 +51,11 @@
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-bottom: 1px solid $argo-color-gray-4;
|
border-bottom: 1px solid $argo-color-gray-4;
|
||||||
|
|
||||||
.theme-dark & {
|
|
||||||
border-color: $argo-color-gray-7;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $argo-color-teal-5;
|
color: $argo-color-teal-5;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-8;
|
||||||
color: themed('text-2');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
|
|
||||||
export interface Tab {
|
export interface Tab {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -114,13 +114,7 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
const el = parentEl.querySelector<HTMLElement>('.active');
|
const el = parentEl.querySelector<HTMLElement>('.active');
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const newIndicatorPosition = this.getIndicatorPosition(parentEl, el);
|
this.setState({ indicatorPosition: this.getIndicatorPosition(parentEl, el) });
|
||||||
|
|
||||||
if (JSON.stringify(this.state.indicatorPosition) !== JSON.stringify(newIndicatorPosition)) {
|
|
||||||
this.setState({
|
|
||||||
indicatorPosition: this.getIndicatorPosition(parentEl, el),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
import * as moment from 'moment';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {interval, Subscription} from 'rxjs';
|
import {interval, Subscription} from 'rxjs';
|
||||||
|
|
||||||
export class Ticker extends React.Component<{intervalMs?: number, disabled?: boolean, children?: ((time: Date) => React.ReactNode)}, {time: Date}> {
|
export class Ticker extends React.Component<{intervalMs?: number, disabled?: boolean, children?: ((time: moment.Moment) => React.ReactNode)}, {time: moment.Moment}> {
|
||||||
|
|
||||||
private subscription: Subscription | null = null;
|
private subscription: Subscription | null = null;
|
||||||
|
|
||||||
constructor(props: {intervalMs?: number, children?: ((time: Date) => React.ReactNode)}) {
|
constructor(props: {intervalMs?: number, children?: ((time: moment.Moment) => React.ReactNode)}) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { time: new Date() };
|
this.state = { time: moment() };
|
||||||
this.ensureSubscribed();
|
this.ensureSubscribed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ export class Ticker extends React.Component<{intervalMs?: number, disabled?: boo
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
this.ensureUnsubscribed();
|
this.ensureUnsubscribed();
|
||||||
} else if (!this.subscription) {
|
} else if (!this.subscription) {
|
||||||
this.subscription = interval(this.props.intervalMs || 1000).subscribe(() => this.setState({ time: new Date() }));
|
this.subscription = interval(this.props.intervalMs || 1000).subscribe(() => this.setState({ time: moment() }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import Tippy, { TippyProps } from '@tippy.js/react';
|
import Tippy from '@tippy.js/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import 'tippy.js/dist/tippy.css';
|
|
||||||
import 'tippy.js/themes/light.css';
|
|
||||||
|
|
||||||
export const Tooltip = ( props: TippyProps ) => (
|
export const Tooltip = ( props: any ) => <Tippy animation='fade' arrow='true' {...props} />;
|
||||||
<Tippy animation='fade' appendTo={document.body} theme='light' interactive={true} {...props} />
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
@import '../../styles/config';
|
@import '../../styles/config';
|
||||||
@import 'node_modules/foundation-sites/scss/util/util';
|
@import 'node_modules/foundation-sites/scss/util/util';
|
||||||
@import '../../styles/theme';
|
|
||||||
|
|
||||||
.top-bar {
|
.top-bar {
|
||||||
line-height: $top-bar-height;
|
line-height: $top-bar-height;
|
||||||
@include themify($themes) {
|
background: $white-color;
|
||||||
background: themed('background-2');
|
|
||||||
}
|
|
||||||
transition: right .5s;
|
transition: right .5s;
|
||||||
border-bottom: 1px solid $argo-color-gray-2;
|
border-bottom: 1px solid $argo-color-gray-2;
|
||||||
|
|
||||||
.theme-dark & {
|
|
||||||
border-color: $argo-color-gray-7;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__left-side {
|
&__left-side {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -28,9 +21,7 @@
|
||||||
float: left;
|
float: left;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .925em;
|
font-size: .925em;
|
||||||
@include themify($themes) {
|
color: $argo-color-teal-7;
|
||||||
color: themed('light-argo-teal-7');
|
|
||||||
}
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +52,7 @@
|
||||||
height: $top-bar-height;
|
height: $top-bar-height;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-8;
|
||||||
color: themed('text-2');
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {default as classNames} from 'classnames';
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
@ -27,7 +27,7 @@ export interface ActionMenu {
|
||||||
|
|
||||||
export interface Toolbar {
|
export interface Toolbar {
|
||||||
filter?: TopBarFilter<any>;
|
filter?: TopBarFilter<any>;
|
||||||
breadcrumbs?: { title: string | React.ReactNode, path?: string; }[];
|
breadcrumbs?: { title: string, path?: string; }[];
|
||||||
tools?: React.ReactNode;
|
tools?: React.ReactNode;
|
||||||
actionMenu?: ActionMenu;
|
actionMenu?: ActionMenu;
|
||||||
}
|
}
|
||||||
|
@ -68,17 +68,17 @@ const renderFilter = (filter: TopBarFilter<any>) => (
|
||||||
</DropDown>
|
</DropDown>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBreadcrumbs = (breadcrumbs: { title: string | React.ReactNode, path?: string; }[]) => (
|
const renderBreadcrumbs = (breadcrumbs: { title: string, path?: string; }[]) => (
|
||||||
<div className='top-bar__breadcrumbs'>
|
<div className='top-bar__breadcrumbs'>
|
||||||
{(breadcrumbs || []).map((breadcrumb, i) => {
|
{(breadcrumbs || []).map((breadcrumb, i) => {
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
if (i === breadcrumbs.length - 1) {
|
if (i === breadcrumbs.length - 1) {
|
||||||
nodes.push(<span key={i} className='top-bar__breadcrumbs-last-item'>{breadcrumb.title}</span>);
|
nodes.push(<span key={breadcrumb.title} className='top-bar__breadcrumbs-last-item'>{breadcrumb.title}</span>);
|
||||||
} else {
|
} else {
|
||||||
nodes.push(<Link key={i} to={breadcrumb.path}> {breadcrumb.title} </Link>);
|
nodes.push(<Link key={breadcrumb.title} to={breadcrumb.path}> {breadcrumb.title} </Link>);
|
||||||
}
|
}
|
||||||
if (i < breadcrumbs.length - 1) {
|
if (i < breadcrumbs.length - 1) {
|
||||||
nodes.push(<span key={`${i}_sep`} className='top-bar__sep'/>);
|
nodes.push(<span key={`${breadcrumb.title}_sep`} className='top-bar__sep'/>);
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -5,6 +5,45 @@ export function isPromise<T>(obj: any): obj is PromiseLike<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Utils = {
|
export const Utils = {
|
||||||
|
getScrollParent(el: HTMLElement): HTMLElement {
|
||||||
|
const regex = /(auto|scroll)/;
|
||||||
|
while (el.parentNode) {
|
||||||
|
el = el.parentNode as HTMLElement;
|
||||||
|
const overflow = getComputedStyle(el, null).getPropertyValue('overflow') +
|
||||||
|
getComputedStyle(el, null).getPropertyValue('overflow-y') +
|
||||||
|
getComputedStyle(el, null).getPropertyValue('overflow-x');
|
||||||
|
if (regex.test(overflow)) {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return document.body;
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollTo(element: HTMLElement, to: number, duration = 1000) {
|
||||||
|
function easeInOutQuad(t: number, b: number, c: number, d: number) {
|
||||||
|
t /= d / 2;
|
||||||
|
if (t < 1) {
|
||||||
|
return c / 2 * t * t + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
t--;
|
||||||
|
return -c / 2 * ( t * ( t - 2 ) - 1) + b;
|
||||||
|
}
|
||||||
|
const start = element.scrollTop;
|
||||||
|
const change = to - start;
|
||||||
|
let currentTime = 0;
|
||||||
|
const increment = 20;
|
||||||
|
|
||||||
|
const animateScroll = () => {
|
||||||
|
currentTime += increment;
|
||||||
|
element.scrollTop = easeInOutQuad(currentTime, start, change, duration);
|
||||||
|
if (currentTime < duration) {
|
||||||
|
setTimeout(animateScroll, increment);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
animateScroll();
|
||||||
|
},
|
||||||
|
|
||||||
toObservable<T>(val: T | Observable<T> | Promise<T>): Observable<T> {
|
toObservable<T>(val: T | Observable<T> | Promise<T>): Observable<T> {
|
||||||
const observable = val as Observable<T>;
|
const observable = val as Observable<T>;
|
||||||
if (observable && observable.subscribe && observable.forEach) {
|
if (observable && observable.subscribe && observable.forEach) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import * as H from 'history';
|
import * as H from 'history';
|
||||||
import { match } from 'react-router';
|
import { match} from 'react-router';
|
||||||
|
import { NotificationsApi, PopupApi } from './components';
|
||||||
import { NotificationsApi } from './components/notifications/notification-manager';
|
|
||||||
import { PopupApi } from './components/popup/popup-manager';
|
|
||||||
|
|
||||||
export interface AppContext {
|
export interface AppContext {
|
||||||
router: {
|
router: {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {configure } from 'enzyme';
|
import {configure } from 'enzyme';
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import * as Adapter from 'enzyme-adapter-react-16';
|
||||||
|
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
|
|
|
@ -14,10 +14,10 @@ $argo-icon-fonts-root: '/' !default;
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "argo-icon";
|
font-family: "argo-icon";
|
||||||
src: url($argo-icon-fonts-root + "argo-icon.eot?73a3124f147facf3e90960528bdfae73?#iefix") format("embedded-opentype"),
|
src: url($argo-icon-fonts-root + "argo-icon.eot?af0f627a6b22b6f9f7b649206eee4263?#iefix") format("embedded-opentype"),
|
||||||
url($argo-icon-fonts-root + "argo-icon.woff?73a3124f147facf3e90960528bdfae73") format("woff"),
|
url($argo-icon-fonts-root + "argo-icon.woff?af0f627a6b22b6f9f7b649206eee4263") format("woff"),
|
||||||
url($argo-icon-fonts-root + "argo-icon.ttf?73a3124f147facf3e90960528bdfae73") format("truetype"),
|
url($argo-icon-fonts-root + "argo-icon.ttf?af0f627a6b22b6f9f7b649206eee4263") format("truetype"),
|
||||||
url($argo-icon-fonts-root + "argo-icon.svg?73a3124f147facf3e90960528bdfae73#argo-icon") format("svg");
|
url($argo-icon-fonts-root + "argo-icon.svg?af0f627a6b22b6f9f7b649206eee4263#argo-icon") format("svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
|
@ -300,117 +300,114 @@ i {
|
||||||
.argo-icon-notification:before {
|
.argo-icon-notification:before {
|
||||||
content: "\f14f";
|
content: "\f14f";
|
||||||
}
|
}
|
||||||
.argo-icon-oci:before {
|
.argo-icon-pencil:before {
|
||||||
content: "\f150";
|
content: "\f150";
|
||||||
}
|
}
|
||||||
.argo-icon-pencil:before {
|
.argo-icon-play-2:before {
|
||||||
content: "\f151";
|
content: "\f151";
|
||||||
}
|
}
|
||||||
.argo-icon-play-2:before {
|
.argo-icon-play:before {
|
||||||
content: "\f152";
|
content: "\f152";
|
||||||
}
|
}
|
||||||
.argo-icon-play:before {
|
.argo-icon-pod:before {
|
||||||
content: "\f153";
|
content: "\f153";
|
||||||
}
|
}
|
||||||
.argo-icon-pod:before {
|
.argo-icon-policies:before {
|
||||||
content: "\f154";
|
content: "\f154";
|
||||||
}
|
}
|
||||||
.argo-icon-policies:before {
|
.argo-icon-policy:before {
|
||||||
content: "\f155";
|
content: "\f155";
|
||||||
}
|
}
|
||||||
.argo-icon-policy:before {
|
.argo-icon-profile:before {
|
||||||
content: "\f156";
|
content: "\f156";
|
||||||
}
|
}
|
||||||
.argo-icon-profile:before {
|
.argo-icon-push:before {
|
||||||
content: "\f157";
|
content: "\f157";
|
||||||
}
|
}
|
||||||
.argo-icon-push:before {
|
.argo-icon-report-card:before {
|
||||||
content: "\f158";
|
content: "\f158";
|
||||||
}
|
}
|
||||||
.argo-icon-report-card:before {
|
.argo-icon-resubmit-failed:before {
|
||||||
content: "\f159";
|
content: "\f159";
|
||||||
}
|
}
|
||||||
.argo-icon-resubmit-failed:before {
|
.argo-icon-retry:before {
|
||||||
content: "\f15a";
|
content: "\f15a";
|
||||||
}
|
}
|
||||||
.argo-icon-retry:before {
|
.argo-icon-right-navigation-toolbar:before {
|
||||||
content: "\f15b";
|
content: "\f15b";
|
||||||
}
|
}
|
||||||
.argo-icon-right-navigation-toolbar:before {
|
.argo-icon-safe:before {
|
||||||
content: "\f15c";
|
content: "\f15c";
|
||||||
}
|
}
|
||||||
.argo-icon-safe:before {
|
.argo-icon-sales-channels:before {
|
||||||
content: "\f15d";
|
content: "\f15d";
|
||||||
}
|
}
|
||||||
.argo-icon-sales-channels:before {
|
.argo-icon-sample:before {
|
||||||
content: "\f15e";
|
content: "\f15e";
|
||||||
}
|
}
|
||||||
.argo-icon-sample:before {
|
.argo-icon-scale:before {
|
||||||
content: "\f15f";
|
content: "\f15f";
|
||||||
}
|
}
|
||||||
.argo-icon-scale:before {
|
.argo-icon-search:before {
|
||||||
content: "\f160";
|
content: "\f160";
|
||||||
}
|
}
|
||||||
.argo-icon-search:before {
|
.argo-icon-settings:before {
|
||||||
content: "\f161";
|
content: "\f161";
|
||||||
}
|
}
|
||||||
.argo-icon-settings:before {
|
.argo-icon-slack-02:before {
|
||||||
content: "\f162";
|
content: "\f162";
|
||||||
}
|
}
|
||||||
.argo-icon-slack-02:before {
|
.argo-icon-stop-property:before {
|
||||||
content: "\f163";
|
content: "\f163";
|
||||||
}
|
}
|
||||||
.argo-icon-stop-property:before {
|
.argo-icon-stop:before {
|
||||||
content: "\f164";
|
content: "\f164";
|
||||||
}
|
}
|
||||||
.argo-icon-stop:before {
|
.argo-icon-storageclass:before {
|
||||||
content: "\f165";
|
content: "\f165";
|
||||||
}
|
}
|
||||||
.argo-icon-storageclass:before {
|
.argo-icon-storageprovider:before {
|
||||||
content: "\f166";
|
content: "\f166";
|
||||||
}
|
}
|
||||||
.argo-icon-storageprovider:before {
|
.argo-icon-tag:before {
|
||||||
content: "\f167";
|
content: "\f167";
|
||||||
}
|
}
|
||||||
.argo-icon-tag:before {
|
.argo-icon-template:before {
|
||||||
content: "\f168";
|
content: "\f168";
|
||||||
}
|
}
|
||||||
.argo-icon-template:before {
|
.argo-icon-terminate:before {
|
||||||
content: "\f169";
|
content: "\f169";
|
||||||
}
|
}
|
||||||
.argo-icon-terminate:before {
|
.argo-icon-test:before {
|
||||||
content: "\f16a";
|
content: "\f16a";
|
||||||
}
|
}
|
||||||
.argo-icon-test:before {
|
.argo-icon-timeline:before {
|
||||||
content: "\f16b";
|
content: "\f16b";
|
||||||
}
|
}
|
||||||
.argo-icon-timeline:before {
|
.argo-icon-tools:before {
|
||||||
content: "\f16c";
|
content: "\f16c";
|
||||||
}
|
}
|
||||||
.argo-icon-tools:before {
|
.argo-icon-user-groups:before {
|
||||||
content: "\f16d";
|
content: "\f16d";
|
||||||
}
|
}
|
||||||
.argo-icon-user-groups:before {
|
.argo-icon-user-profile:before {
|
||||||
content: "\f16e";
|
content: "\f16e";
|
||||||
}
|
}
|
||||||
.argo-icon-user-profile:before {
|
.argo-icon-user:before {
|
||||||
content: "\f16f";
|
content: "\f16f";
|
||||||
}
|
}
|
||||||
.argo-icon-user:before {
|
.argo-icon-users:before {
|
||||||
content: "\f170";
|
content: "\f170";
|
||||||
}
|
}
|
||||||
.argo-icon-users:before {
|
.argo-icon-volume:before {
|
||||||
content: "\f171";
|
content: "\f171";
|
||||||
}
|
}
|
||||||
.argo-icon-volume:before {
|
.argo-icon-warning:before {
|
||||||
content: "\f172";
|
content: "\f172";
|
||||||
}
|
}
|
||||||
.argo-icon-warning:before {
|
.argo-icon-workflow:before {
|
||||||
content: "\f173";
|
content: "\f173";
|
||||||
}
|
}
|
||||||
.argo-icon-workflow:before {
|
.argo-icon-yaml:before {
|
||||||
content: "\f174";
|
content: "\f174";
|
||||||
}
|
}
|
||||||
.argo-icon-yaml:before {
|
|
||||||
content: "\f175";
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,9 +30,7 @@ $argo-color-teal-7: #006F8A;
|
||||||
$argo-color-teal-8: #004C67;
|
$argo-color-teal-8: #004C67;
|
||||||
|
|
||||||
$white-color: #ffffff;
|
$white-color: #ffffff;
|
||||||
$dark-theme-background-1: #100f0f;
|
|
||||||
$dark-theme-background-2: #303237;
|
|
||||||
$dark-theme-sliding-panel: #28292a;
|
|
||||||
// Status colors
|
// Status colors
|
||||||
$argo-failed-color: #E96D76;
|
$argo-failed-color: #E96D76;
|
||||||
$argo-failed-color-dark: #c04b4f;
|
$argo-failed-color-dark: #c04b4f;
|
||||||
|
@ -52,7 +50,6 @@ $argo-color-yellow: #FFD100;
|
||||||
$argo-running-color-dark: #378398;
|
$argo-running-color-dark: #378398;
|
||||||
$argo-running-color-light: #02C4D3;
|
$argo-running-color-light: #02C4D3;
|
||||||
$argo-running-color: #0DADEA;
|
$argo-running-color: #0DADEA;
|
||||||
$argo-suspended-color: #766F94;
|
|
||||||
|
|
||||||
$argo-waiting-color-dark: $argo-color-gray-6;
|
$argo-waiting-color-dark: $argo-color-gray-6;
|
||||||
$argo-waiting-color-light: $argo-color-gray-5;
|
$argo-waiting-color-light: $argo-color-gray-5;
|
||||||
|
|
|
@ -236,7 +236,8 @@
|
||||||
|
|
||||||
&--special {
|
&--special {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: $argo-color-teal-7;
|
background: linear-gradient(-58deg, #49505c 0%, #3f51b5 36%, #00c7d6 100%);
|
||||||
|
|
||||||
&:not(.disabled) {
|
&:not(.disabled) {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
@import '../theme';
|
|
||||||
|
|
||||||
.argo-container {
|
.argo-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $basePageWidth + 160;
|
width: $basePageWidth + 160;
|
||||||
|
@ -37,10 +35,8 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-6;
|
||||||
background-color: themed('background-2');
|
background-color: #fff;
|
||||||
color: themed('light-argo-gray-6');
|
|
||||||
}
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
@ -91,19 +87,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-row {
|
&-row {
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-8;
|
||||||
color: themed('text-2');
|
|
||||||
}
|
|
||||||
.columns {
|
.columns {
|
||||||
|
border-bottom: 1px solid $argo-color-gray-3;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
border-bottom: 1px solid themed('border');
|
|
||||||
}
|
|
||||||
|
|
||||||
&--narrower-height {
|
&--narrower-height {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 14px 0;
|
padding: 14px 0;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../config';
|
@import '../config';
|
||||||
@import '../theme';
|
|
||||||
|
|
||||||
.argo-form-row {
|
.argo-form-row {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -9,9 +8,7 @@
|
||||||
|
|
||||||
label {
|
label {
|
||||||
font-size: .9em;
|
font-size: .9em;
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-6;
|
||||||
color: themed('light-argo-gray-6');
|
|
||||||
}
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -72,11 +69,8 @@
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@include themify($themes) {
|
|
||||||
color: themed('text-2');
|
|
||||||
border-bottom: 2px solid themed('border');;
|
|
||||||
}
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
transition: border .2s;
|
transition: border .2s;
|
||||||
|
|
||||||
.error & {
|
.error & {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../config';
|
@import '../config';
|
||||||
@import '../theme';
|
|
||||||
|
|
||||||
.argo-table-list {
|
.argo-table-list {
|
||||||
&__head {
|
&__head {
|
||||||
|
@ -15,10 +14,8 @@
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
font-size: .8125em;
|
font-size: .8125em;
|
||||||
@include themify($themes) {
|
color: $argo-color-gray-6;
|
||||||
background: themed('background-2');
|
background-color: #fff;
|
||||||
color: themed('text-1');
|
|
||||||
}
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,43 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<svg data-name="Layer 1" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<path d="m254.28 303.41c-0.50074-2.8223-0.39948-103.6 0.0975-105.52h27.162v77.765c1.1717 0.0608 2.0912 0.14921 3.0108 0.15005q16.128 0.0147 32.256 7e-3h2.9073v27.596zm-96.707-0.04025v-105.17c1.617-0.52913 61.545-0.73586 65.462-0.20457v22.414c-0.87869 0.063-1.7857 0.18341-2.6928 0.18454q-16.009 0.0199-32.017 9e-3h-2.9677v17.434h33.347v23.192h-33.048c-0.55339 1.9851-0.70531 15.817-0.25622 19.646 0.84488 0.0568 1.75 0.16952 2.6553 0.17059q16.009 0.0186 32.017 8e-3h2.9694v22.321zm-59.941-105.49h27.263c0.55054 1.7525 0.65821 102.97 0.0947 105.52h-27.288c-0.15021-6.7034-0.04736-13.384-0.06622-20.061-0.01871-6.6226-0.0041-13.245-0.0041-20.04h-33.788v39.741c-2.0595 0.61518-25.334 0.67417-27.648 0.123v-105.28h27.538v37.189c1.9679 0.56844 30.923 0.67339 33.872 0.12942 0.0082-2.9771 0.02-6.0265 0.024-9.0758q0.0063-4.7433 9.4e-4 -9.4866 0-4.625 0-9.25c1e-5 -3.0784 0-6.1567 0-9.5178zm253.1-0.12062c2.7871 0 5.4704 0.18919 8.1149-0.0501 2.9951-0.271 5.139 0.8001 7.3235 2.8131 12.613 11.622 25.357 23.101 38.059 34.627 0.63855 0.57948 1.2904 1.1443 2.1101 1.8701 0.76445-0.65718 1.4812-1.2432 2.1648-1.8655q19.639-17.877 39.248-35.787a5.4477 5.4477 0 0 1 4.2036-1.6458c3.2189 0.13038 6.4467 0.0375 9.8412 0.0375v105.37c-1.7231 0.50368-24.876 0.60366-27.639 0.0611v-53.362l-0.53656-0.25427c-9.0108 8.2178-18.022 16.436-27.238 24.841-9.2258-8.3112-18.342-16.523-27.458-24.736l-0.52449 0.19192c-0.023 4.4536-8e-3 8.9083-0.0105 13.362q-4e-3 6.6394-5.8e-4 13.279v26.872h-27.428c-0.51411-1.773-0.75283-99.662-0.22996-105.62zm43.794 150.15a176.64 176.64 0 0 1-23.973 27.163c0.70941 0.59068 1.2859 1.0704 1.8621 1.5506a108.31 108.31 0 0 1 33.683 48.146 34.618 34.618 0 0 1 2.202 14.42 14.885 14.885 0 0 1-0.74786 3.6921 7.2076 7.2076 0 0 1-8.1579 5.0231 22.233 22.233 0 0 1-6.7628-2.0063 51.232 51.232 0 0 1-9.1815-5.8151 107.59 107.59 0 0 1-32.936-46.707c-0.18746-0.51334-0.39218-1.0204-0.72243-1.8769a194.65 194.65 0 0 1-25.012 14.008 181.67 181.67 0 0 1-26.687 9.7244 187.56 187.56 0 0 1-28.304 5.388c0.16807 0.84015 0.26446 1.5098 0.43745 2.1591a109.17 109.17 0 0 1 2.9708 36.443 80.804 80.804 0 0 1-4.4229 22.477 78.25 78.25 0 0 1-4.1648 8.7447 13.39 13.39 0 0 1-2.3386 2.9708c-3.9801 4.109-8.7322 4.144-12.612-0.0737a27.28 27.28 0 0 1-3.907-5.6178c-3.0768-5.7757-4.6604-12.056-5.791-18.46a116.86 116.86 0 0 1-1.3589-26.465 94.48 94.48 0 0 1 2.8847-19.185c0.14009-0.53269 0.268-1.0696 0.37134-1.6103 0.0263-0.13754-0.0634-0.2973-0.17067-0.73825a176.12 176.12 0 0 1-80.969-24.994c-0.40992 0.90921-0.76206 1.6747-1.1017 2.4458a110.48 110.48 0 0 1-30.901 41.42 38.161 38.161 0 0 1-12.047 6.9591 12.09 12.09 0 0 1-6.5152 0.70023 7.1186 7.1186 0 0 1-5.4033-4.4892c-1.4161-3.424-1.1653-6.985-0.68438-10.517a55.453 55.453 0 0 1 4.3077-14.25 112.5 112.5 0 0 1 26.511-37.763c0.459-0.43461 0.92981-0.857 1.3809-1.2995a3.7603 3.7603 0 0 0 0.36534-0.65529 178.9 178.9 0 0 1-28.469-31.317c0.98458-0.0802 1.6433-0.18 2.302-0.1806 10.514-0.01 21.029 0.0274 31.543-0.0436a4.7055 4.7055 0 0 1 3.7034 1.6262 146.95 146.95 0 0 0 39.403 28.885 139.95 139.95 0 0 0 49.704 14.774q70.68 6.8707 121.6-42.855a7.6457 7.6457 0 0 1 5.9926-2.4435c9.8014 0.12121 19.605 0.0499 29.408 0.0499h2.5335zm-258-226.78c-0.572-0.54252-1.1946-1.1264-1.81-1.7176-12.617-12.121-22.381-26.136-28.279-42.702-1.6507-4.6364-2.8969-9.3652-2.6759-14.359 0.021-0.4739 0.0196-0.94915 0.0523-1.4221 0.445-6.4459 4.7591-9.7032 11.058-8.1767a27.325 27.325 0 0 1 5.7315 2.1965c6.8902 3.4554 12.506 8.5636 17.671 14.166a112.53 112.53 0 0 1 21.722 33.43 8.2964 8.2964 0 0 0 0.38946 0.861c0.0712 0.12855 0.22232 0.21282 0.55927 0.51883a176.36 176.36 0 0 1 81.02-24.861c-0.17651-0.8761-0.28195-1.5457-0.44772-2.2a112.49 112.49 0 0 1-2.6529-36.956 84.075 84.075 0 0 1 4.4444-21.764 31.326 31.326 0 0 1 5.4765-10.171 15.687 15.687 0 0 1 3.1634-2.8215 7.026 7.026 0 0 1 8.0326-0.056 17.279 17.279 0 0 1 5.8402 6.7318 53.054 53.054 0 0 1 5.2622 14.677 112.5 112.5 0 0 1 2.1226 33.004 95.598 95.598 0 0 1-3.4905 19.911c7.1217 1.3119 14.21 2.3287 21.147 3.9771a186.38 186.38 0 0 1 20.441 6.0033 188.32 188.32 0 0 1 19.77 8.5693c6.3454 3.1643 12.386 6.9407 18.718 10.538 0.20571-0.433 0.50439-0.94982 0.706-1.5021a108.66 108.66 0 0 1 32.901-46.762 37.758 37.758 0 0 1 11.822-6.883 17.246 17.246 0 0 1 3.6783-0.84512c6.264-0.71729 8.8935 3.2244 9.3565 7.9318a29.944 29.944 0 0 1-0.77381 10.355 87.906 87.906 0 0 1-10.73 24.688c-6.7902 10.972-14.85 20.855-25.093 28.83-0.30234 0.2354-0.56784 0.51814-1.0799 0.99029a177.78 177.78 0 0 1 26.593 30.882 10.962 10.962 0 0 1-1.689 0.29762c-10.595 0.015-21.191-0.0183-31.786 0.0461a4.004 4.004 0 0 1-3.1725-1.69 147.88 147.88 0 0 0-88.178-46.548 143.36 143.36 0 0 0-30.28-1.1692 146.41 146.41 0 0 0-82.537 31.811 140.07 140.07 0 0 0-16.976 15.842 4.7284 4.7284 0 0 1-3.8633 1.7574c-10.121-0.0703-20.242-0.035-30.363-0.0349h-2.1521c0.618-2.408 6.8403-10.938 13.884-18.553 5.2525-5.6788 10.817-11.069 16.468-16.818z"/>
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 620 645.6" style="enable-background:new 0 0 620 645.6;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#277A9F;}
|
||||||
|
.st1{fill:none;stroke:#277A9F;stroke-width:30;}
|
||||||
|
</style>
|
||||||
|
<title>logo</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="Page-1">
|
||||||
|
<g id="logo_x40_2x" transform="translate(-152.000000, -162.000000)">
|
||||||
|
<g id="logo" transform="translate(149.000000, 162.000000)">
|
||||||
|
<g id="helm" transform="translate(22.377622, 0.000000)">
|
||||||
|
|
||||||
|
<g id="rungs-bottom" transform="translate(41.500000, 108.500000) scale(1, -1) translate(-41.500000, -108.500000) translate(0.000000, 91.000000)">
|
||||||
|
<path id="Oval-1" class="st0" d="M417.2-357.5c10.4,7.3,34.3-8.7,53.2-35.8s25.9-55,15.4-62.3c-10.4-7.3-34.3,8.7-53.2,35.8
|
||||||
|
C413.7-392.7,406.8-364.8,417.2-357.5z"/>
|
||||||
|
<path id="Oval-1-Copy" class="st0" d="M167.6-357.5c-10.4,7.3-34.3-8.7-53.2-35.8c-19-27.1-25.9-55-15.4-62.3
|
||||||
|
s34.3,8.7,53.2,35.8S178.1-364.8,167.6-357.5z"/>
|
||||||
|
<path id="Oval-1-Copy-4" class="st0" d="M292.3-399.9c-12.7,0-23.1-26.8-23.1-59.9s10.3-59.9,23.1-59.9s23.1,26.8,23.1,59.9
|
||||||
|
S305-399.9,292.3-399.9z"/>
|
||||||
|
</g>
|
||||||
|
<g id="rungs-top">
|
||||||
|
<path id="Oval-1_1_" class="st0" d="M417.2,162.5c10.4,7.3,34.3-8.7,53.2-35.8s25.9-55,15.4-62.3c-10.4-7.3-34.3,8.7-53.2,35.8
|
||||||
|
S406.8,155.2,417.2,162.5z"/>
|
||||||
|
<path id="Oval-1-Copy_1_" class="st0" d="M167.6,162.5c-10.4,7.3-34.3-8.7-53.2-35.8S88.5,71.8,99,64.4s34.3,8.7,53.2,35.8
|
||||||
|
S178.1,155.2,167.6,162.5z"/>
|
||||||
|
<path id="Oval-1-Copy-4_1_" class="st0" d="M292.3,120.1c-12.7,0-23.1-26.8-23.1-59.9s10.3-59.9,23.1-59.9s23.1,26.8,23.1,59.9
|
||||||
|
S305,120.1,292.3,120.1z"/>
|
||||||
|
</g>
|
||||||
|
<path id="Oval-1_2_" class="st1" d="M470.2,215.6C433,154.4,365.6,113.5,288.7,113.5c-74.9,0-140.7,38.7-178.4,97.2 M114.3,446
|
||||||
|
c38.3,55.1,102.1,91.1,174.4,91.1c72.4,0,136.3-36.2,174.6-91.4"/>
|
||||||
|
</g>
|
||||||
|
<path id="HELM" class="st0" d="M3.6,244.1H48v62.5h50.1v-62.5h44.4V412H98.1v-66.6H48V412H3.6V244.1z M179.8,244.1h108.5v37.2
|
||||||
|
h-64.1v26.3H279v37.2h-54.8v30h66.6V412H179.8V244.1z M323.4,244.1h44.4v130.7h63.5V412h-108V244.1z M460.3,244.1h46l26.3,69.7
|
||||||
|
l9.3,27.4h1l9.3-27.4l25.3-69.7h46V412h-41.3v-49.6c0-4.3,0.2-9,0.6-14.2c0.4-5.2,0.9-10.5,1.4-15.9c0.5-5.4,1.1-10.7,1.8-15.9
|
||||||
|
c0.7-5.2,1.3-9.8,1.8-13.9h-1l-13.9,39.3l-23.2,56.6h-16.5l-23.2-56.6l-13.4-39.3h-1c0.5,4.1,1.1,8.8,1.8,13.9
|
||||||
|
c0.7,5.2,1.3,10.5,1.8,15.9c0.5,5.4,1,10.7,1.4,15.9c0.4,5.2,0.6,9.9,0.6,14.2V412h-40.8V244.1z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2.7 KiB |
|
@ -1,22 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#808184;}
|
|
||||||
.st1{fill:#262261;}
|
|
||||||
</style>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<polygon class="st0" points="326.6,212.6 326.6,132.6 128.6,132.6 128.6,444.6 326.6,444.6 326.6,364.6 208.6,364.6 208.6,212.6
|
|
||||||
"/>
|
|
||||||
<g>
|
|
||||||
<rect x="366.5" y="132.6" class="st1" width="79.9" height="79.9"/>
|
|
||||||
<rect x="366.5" y="252.6" class="st1" width="79.9" height="192"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path class="st1" d="M8.5,9.5v558.2h558.2V9.5H8.5z M486.4,484.7H88.7V92.6h397.8V484.7z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 852 B |
|
@ -5,7 +5,6 @@
|
||||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
|
@import 'node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
|
||||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/solid';
|
@import 'node_modules/@fortawesome/fontawesome-free/scss/solid';
|
||||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/brands';
|
@import 'node_modules/@fortawesome/fontawesome-free/scss/brands';
|
||||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/regular';
|
|
||||||
|
|
||||||
@include foundation-global-styles;
|
@include foundation-global-styles;
|
||||||
@include foundation-flex-classes;
|
@include foundation-flex-classes;
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
@import './config.scss';
|
|
||||||
|
|
||||||
$themes: (
|
|
||||||
light: (
|
|
||||||
background-1: $argo-color-gray-3,
|
|
||||||
text-1: $argo-color-gray-7,
|
|
||||||
background-2: $white-color,
|
|
||||||
text-2: $argo-color-gray-8,
|
|
||||||
light-argo-gray-6: $argo-color-gray-6,
|
|
||||||
light-argo-gray-2: $argo-color-gray-2,
|
|
||||||
light-argo-teal-1: $argo-color-teal-1,
|
|
||||||
light-argo-teal-7: $argo-color-teal-7,
|
|
||||||
light-argo-teal-5: $argo-color-teal-5,
|
|
||||||
pod-cyan: lightcyan,
|
|
||||||
layout-loader-bg: rgba($argo-color-gray-7, 0.4),
|
|
||||||
overlay: rgba(222, 222, 222, 0.62),
|
|
||||||
shadow: $argo-color-gray-5,
|
|
||||||
border: $argo-color-gray-3
|
|
||||||
),
|
|
||||||
dark: (
|
|
||||||
background-1: $dark-theme-background-1,
|
|
||||||
text-1: $argo-color-gray-3,
|
|
||||||
background-2: $dark-theme-background-2,
|
|
||||||
text-2: $white-color,
|
|
||||||
light-argo-gray-6: $argo-color-gray-2,
|
|
||||||
light-argo-gray-2: $dark-theme-sliding-panel,
|
|
||||||
light-argo-teal-1: $argo-color-gray-6,
|
|
||||||
light-argo-teal-7: $argo-color-teal-5,
|
|
||||||
light-argo-teal-5: $argo-color-teal-4,
|
|
||||||
pod-cyan: $argo-color-teal-8,
|
|
||||||
layout-loader-bg: rgba($argo-color-gray-3, 0.4),
|
|
||||||
overlay: rgba(70, 70, 70, 0.62),
|
|
||||||
shadow: $dark-theme-background-1,
|
|
||||||
border: $argo-color-gray-7
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
@mixin themify($themes) {
|
|
||||||
@each $theme, $map in $themes {
|
|
||||||
.theme-#{$theme} & {
|
|
||||||
$theme-map: () !global;
|
|
||||||
@each $key, $submap in $map {
|
|
||||||
$value: map-get(map-get($themes, $theme), '#{$key}');
|
|
||||||
$theme-map: map-merge($theme-map, ($key: $value)) !global;
|
|
||||||
}
|
|
||||||
@content;
|
|
||||||
$theme-map: null !global;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@function themed($key) {
|
|
||||||
@return map-get($theme-map, $key);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./../bundle",
|
"outDir": "./../../bundle",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es6",
|
"target": "es5",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"esModuleInterop": true,
|
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"skipLibCheck": true,
|
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2017",
|
"es2017",
|
||||||
"dom"
|
"dom"
|
||||||
],
|
],
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"./node_modules/@types"
|
"../node_modules/@types"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["./**/*"],
|
"include": [
|
||||||
"exclude": ["node_modules", "./**/*.test.ts", "./**/*.test.tsx", "./**/*.stories.tsx"]
|
"./**/*"
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict;';
|
||||||
|
|
||||||
|
const config = require('../app/webpack.config');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
module.exports = Object.assign({}, config, {
|
||||||
|
entry: './src/index.ts',
|
||||||
|
output: {
|
||||||
|
filename: 'bundle.js',
|
||||||
|
path: __dirname + '/../../bundle',
|
||||||
|
library: 'argo-ui',
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
umdNamedDefine: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
SYSTEM_INFO: JSON.stringify({
|
||||||
|
version: process.env.ARGO_VERSION || 'latest',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Store, withState } from '@dump247/storybook-state';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { App } from './utils';
|
import { App } from './utils';
|
||||||
|
@ -8,18 +10,13 @@ function loadData(input: string): Promise<string> {
|
||||||
return new Promise((resolve) => window.setTimeout(() => resolve(`hello ${input}`), 50));
|
return new Promise((resolve) => window.setTimeout(() => resolve(`hello ${input}`), 50));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
storiesOf('Data Loader', module)
|
||||||
title: 'Data Loader',
|
.add('loading data asynchronously', withState({ input: 'world' })(({store}: { store: Store<any> }) => (
|
||||||
};
|
|
||||||
|
|
||||||
export const LoadingDataAsynchronously = () => {
|
|
||||||
const [input, setInput] = React.useState('world');
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{() => (
|
{() => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<input value={input} onChange={(e) => setInput(e.target.value)}/>
|
<input value={store.state.input} onChange={(e) => store.set({ input: e.target.value })}/>
|
||||||
<DataLoader input={input} load={loadData}>
|
<DataLoader input={store.state.input} load={(input) => loadData(input)}>
|
||||||
{(data) => (
|
{(data) => (
|
||||||
<div>
|
<div>
|
||||||
{data}
|
{data}
|
||||||
|
@ -29,6 +26,4 @@ export const LoadingDataAsynchronously = () => {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)));
|
||||||
};
|
|
||||||
LoadingDataAsynchronously.storyName = 'loading data asynchronously';
|
|
|
@ -1,33 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { DropDown } from '../src/components/dropdown/dropdown';
|
|
||||||
import { DropDownMenu } from '../src/components/dropdown-menu';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Dropdown',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => (<DropDown anchor={() => <a>Click me</a>}><p>Dropdown content here</p></DropDown>);
|
|
||||||
Default.storyName = 'default';
|
|
||||||
|
|
||||||
export const Menu = () => {
|
|
||||||
return (
|
|
||||||
<DropDown isMenu={true} anchor={() => <a>Click me</a>}>
|
|
||||||
<ul>
|
|
||||||
<li><a>menu item 1</a></li>
|
|
||||||
<li><a>menu item 2</a></li>
|
|
||||||
</ul>
|
|
||||||
</DropDown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Menu.storyName = 'menu';
|
|
||||||
|
|
||||||
export const MenuWrapper = () => {
|
|
||||||
return (
|
|
||||||
<DropDownMenu anchor={() => <a>Click me</a>} items={[{
|
|
||||||
title: 'menu item 1',
|
|
||||||
action: () => window.alert('Clicked!'),
|
|
||||||
}]} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
MenuWrapper.storyName = 'menu wrapper';
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { DropDown, DropDownMenu } from '../src/components';
|
||||||
|
|
||||||
|
storiesOf('Dropdown', module)
|
||||||
|
.add('default', () => <DropDown anchor={() => <a>Click me</a>}><p>Dropdown content here</p></DropDown>)
|
||||||
|
.add('menu', () => (
|
||||||
|
<DropDown isMenu={true} anchor={() => <a>Click me</a>}>
|
||||||
|
<ul>
|
||||||
|
<li><a>menu item 1</a></li>
|
||||||
|
<li><a>menu item 2</a></li>
|
||||||
|
</ul>
|
||||||
|
</DropDown>
|
||||||
|
)).add('menu wrapper', () => (
|
||||||
|
<DropDownMenu anchor={() => <a>Click me</a>} items={[{
|
||||||
|
title: 'menu item 1',
|
||||||
|
action: () => window.alert('Clicked!'),
|
||||||
|
}]} />
|
||||||
|
));
|
|
@ -1,14 +1,10 @@
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Form, Text } from 'react-form';
|
import { Form, Text } from 'react-form';
|
||||||
|
import { FormField, FormSelect } from '../src/components';
|
||||||
|
|
||||||
import { FormField, FormSelect } from '../src/components/form-field/form-field';
|
storiesOf('Forms', module)
|
||||||
|
.add('default', () => (
|
||||||
export default {
|
|
||||||
title: 'Forms',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => {
|
|
||||||
return (
|
|
||||||
<Form>
|
<Form>
|
||||||
{(api) => (
|
{(api) => (
|
||||||
<form style={{padding: '1em'}}>
|
<form style={{padding: '1em'}}>
|
||||||
|
@ -19,11 +15,9 @@ export const Default = () => {
|
||||||
<FormField label='Password' formApi={api} field='passwordField' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='passwordField' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Select' formApi={api} field='selectField' component={FormSelect} componentProps={{options: ['option1', 'option2']}} />
|
<FormField label='Select' formApi={api} field='selectField' component={FormSelect} componentProps={{options: ['option1', 'option2']}} />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
);
|
));
|
||||||
}
|
|
||||||
Default.storyName = 'default';
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import './data-loader';
|
||||||
|
import './dropdown';
|
||||||
|
import './forms';
|
||||||
|
import './notifications';
|
||||||
|
import './page';
|
||||||
|
import './popup';
|
||||||
|
import './select';
|
||||||
|
import './table';
|
||||||
|
import './tabs';
|
|
@ -1,22 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
import { LogsViewer } from '../src/components/logs-viewer/logs-viewer';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'LogsViewer',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => (
|
|
||||||
<div>
|
|
||||||
<LogsViewer source={{
|
|
||||||
key: 'test',
|
|
||||||
loadLogs: () => new Observable<string>((observer) => {
|
|
||||||
const interval = setInterval(() => observer.next('test\n'), 1000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}),
|
|
||||||
shouldRepeat: () => false,
|
|
||||||
}}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
Default.storyName = 'default';
|
|
|
@ -1,38 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { NotificationType } from '../src/components/notifications/notifications';
|
|
||||||
|
|
||||||
import { App } from './utils';
|
|
||||||
|
|
||||||
const messages = [
|
|
||||||
'Mist enveloped the ship three hours out from port. The recorded voice scratched in the speaker. Silver mist suffused the deck of the ship.',
|
|
||||||
'Then came the night of the first falling star. A red flare silhouetted the jagged edge of a wing. She stared through the window at the stars.',
|
|
||||||
'The spectacle before us was indeed sublime. A shining crescent far beneath the flying vessel. She stared through the window at the stars.',
|
|
||||||
];
|
|
||||||
|
|
||||||
function getMessage() {
|
|
||||||
return messages[Math.floor(Math.random() * messages.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Notifications',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
|
||||||
{(apis) => [
|
|
||||||
{type: NotificationType.Success, title: 'Success'},
|
|
||||||
{type: NotificationType.Warning, title: 'Warning'},
|
|
||||||
{type: NotificationType.Error, title: 'Error'},
|
|
||||||
].map((item) => (
|
|
||||||
<button key={item.type} className='argo-button argo-button--base' onClick={() =>
|
|
||||||
apis.notifications.show({type: item.type, content: <div>{getMessage()}</div>})
|
|
||||||
}>
|
|
||||||
{item.title}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</App>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Default.storyName = 'default';
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { NotificationType} from '../src/components';
|
||||||
|
|
||||||
|
import { App } from './utils';
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
'Mist enveloped the ship three hours out from port. The recorded voice scratched in the speaker. Silver mist suffused the deck of the ship.',
|
||||||
|
'Then came the night of the first falling star. A red flare silhouetted the jagged edge of a wing. She stared through the window at the stars.',
|
||||||
|
'The spectacle before us was indeed sublime. A shining crescent far beneath the flying vessel. She stared through the window at the stars.',
|
||||||
|
];
|
||||||
|
|
||||||
|
function getMessage() {
|
||||||
|
return messages[Math.floor(Math.random() * messages.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
storiesOf('Notifications', module)
|
||||||
|
.add('default', () => (
|
||||||
|
<App>
|
||||||
|
{(apis) => (
|
||||||
|
[
|
||||||
|
{type: NotificationType.Success, title: 'Success'},
|
||||||
|
{type: NotificationType.Warning, title: 'Warning'},
|
||||||
|
{type: NotificationType.Error, title: 'Error'},
|
||||||
|
].map((item) => (
|
||||||
|
<button key={item.type} className='argo-button argo-button--base'
|
||||||
|
onClick={() => apis.notifications.show({type: item.type, content: <div>{getMessage()}</div>})}>
|
||||||
|
{item.title}
|
||||||
|
</button>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</App>
|
||||||
|
));
|
|
@ -1,186 +0,0 @@
|
||||||
import { createBrowserHistory } from 'history';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { Route, Router } from 'react-router';
|
|
||||||
import { timer } from 'rxjs';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { Layout } from '../src/components/layout/layout';
|
|
||||||
import { Page } from '../src/components/page/page';
|
|
||||||
|
|
||||||
const navItems = [{ path: location.pathname, title: 'Sample', iconClassName: 'argo-icon-docs' }];
|
|
||||||
const breadcrumbs = [{
|
|
||||||
title: 'breadcrumb parent',
|
|
||||||
path: location.pathname,
|
|
||||||
}, {
|
|
||||||
title: 'this page breadcrumb',
|
|
||||||
}];
|
|
||||||
|
|
||||||
const actionMenu = {
|
|
||||||
items: [{
|
|
||||||
title: 'New Item 1',
|
|
||||||
iconClassName: 'fa fa-history',
|
|
||||||
action: () => {
|
|
||||||
// do nothing
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
title: 'New Item 2',
|
|
||||||
iconClassName: 'icon argo-icon-deploy',
|
|
||||||
action: () => {
|
|
||||||
// do nothing
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'New Item 3',
|
|
||||||
action: () => {
|
|
||||||
// do nothing
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'New Item 4',
|
|
||||||
iconClassName: 'fa fa-times-circle',
|
|
||||||
disabled: true,
|
|
||||||
action: () => {
|
|
||||||
// do nothing
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
const history = createBrowserHistory();
|
|
||||||
|
|
||||||
function ensureSelected(vals: string[], selected: string[]): string[] {
|
|
||||||
const res = new Set(selected);
|
|
||||||
vals.forEach((item) => res.add(item));
|
|
||||||
return Array.from(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Page',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => {
|
|
||||||
const [selectedFilter, setSelectedFilter] = React.useState<string[]>([]);
|
|
||||||
return (
|
|
||||||
<Router history={history}>
|
|
||||||
<Route path={location.pathname}>
|
|
||||||
<Layout navItems={navItems}>
|
|
||||||
<Page title='Hello world!' toolbar={{breadcrumbs, actionMenu, filter: {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
content: (changeSelection) => (
|
|
||||||
<React.Fragment>
|
|
||||||
Filter type one: <a onClick={() => changeSelection(ensureSelected(['1', '2'], selectedFilter))}>all</a>
|
|
||||||
</React.Fragment>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ label: 'filter 1', value: '1' },
|
|
||||||
{ label: 'filter 2', value: '2' },
|
|
||||||
{
|
|
||||||
content: (changeSelection) => (
|
|
||||||
<React.Fragment>
|
|
||||||
Filter type two: <a onClick={() => changeSelection(ensureSelected(['3', '4'], selectedFilter))}>all</a>
|
|
||||||
</React.Fragment>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ label: 'filter 3', value: '3' },
|
|
||||||
{ label: 'filter 4', value: '4' },
|
|
||||||
],
|
|
||||||
selectedValues: selectedFilter,
|
|
||||||
selectionChanged: setSelectedFilter,
|
|
||||||
}}}>
|
|
||||||
<div style={{ padding: '1em' }}>
|
|
||||||
<div className='white-box'>
|
|
||||||
Hello world!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
</Layout>
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Default.storyName = 'default';
|
|
||||||
|
|
||||||
export const DynamicToolbar = () => {
|
|
||||||
return (
|
|
||||||
<Router history={history}>
|
|
||||||
<Route path={location.pathname}>
|
|
||||||
<Layout navItems={navItems}>
|
|
||||||
<Page title='Hello world!' toolbar={timer(0, 1000).pipe(map(() => ({breadcrumbs: [
|
|
||||||
{ title: 'hello ' + new Date().toLocaleTimeString() },
|
|
||||||
]})))}>
|
|
||||||
<div style={{ padding: '1em' }}>
|
|
||||||
<div className='white-box'>Hello world!</div>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
</Layout>
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
DynamicToolbar.storyName = 'dynamic toolbar';
|
|
||||||
|
|
||||||
export const CompactNavBar = () => {
|
|
||||||
const manyNavItems = [];
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
manyNavItems.push({path: location.pathname + '/' + i, title: 'Sample', iconClassName: 'argo-icon-docs'});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
(
|
|
||||||
<Router history={history}>
|
|
||||||
<Route path={location.pathname}>
|
|
||||||
<Layout navItems={manyNavItems}>
|
|
||||||
<Page title='Hello world!'>
|
|
||||||
<div style={{ padding: '1em' }}>
|
|
||||||
<div className='white-box'>
|
|
||||||
Hello world!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
</Layout>
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
CompactNavBar.storyName = 'compact nav bar';
|
|
||||||
|
|
||||||
export const CustomTopBarTitle = () => {
|
|
||||||
return (
|
|
||||||
<Router history={history}>
|
|
||||||
<Route path={location.pathname}>
|
|
||||||
<Layout navItems={navItems}>
|
|
||||||
<Page title='helmet title' topBarTitle='Top Bar Title' toolbar={{breadcrumbs: [
|
|
||||||
{ title: 'Apps ', path: '/applications' },
|
|
||||||
{ title: 'app name' },
|
|
||||||
]}}>
|
|
||||||
<div style={{ padding: '1em' }}>
|
|
||||||
<div className='white-box'>
|
|
||||||
Test
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
</Layout>
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
CustomTopBarTitle.storyName = 'custom top bar title';
|
|
||||||
|
|
||||||
export const BackgroundColor = () => {
|
|
||||||
return (
|
|
||||||
<Router history={history}>
|
|
||||||
<Route path={location.pathname}>
|
|
||||||
<Layout navItems={navItems} navBarStyle={{ backgroundColor: 'red' }}>
|
|
||||||
<Page title='Hello world!'>
|
|
||||||
<div style={{ padding: '1em' }}>
|
|
||||||
<div className='white-box'>
|
|
||||||
Hello world!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Page>
|
|
||||||
</Layout>
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BackgroundColor.storyName = 'background color';
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
import { Store, withState } from '@dump247/storybook-state';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import createHistory from 'history/createBrowserHistory';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Route, Router } from 'react-router';
|
||||||
|
import { timer } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { Layout, Page } from '../src/components';
|
||||||
|
|
||||||
|
const navItems = [{ path: location.pathname, title: 'Sample', iconClassName: 'argo-icon-docs' }];
|
||||||
|
const breadcrumbs = [{
|
||||||
|
title: 'breadcrumb parent',
|
||||||
|
path: location.pathname,
|
||||||
|
}, {
|
||||||
|
title: 'this page breadcrumb',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const actionMenu = {
|
||||||
|
items: [{
|
||||||
|
title: 'New Item 1',
|
||||||
|
iconClassName: 'fa fa-history',
|
||||||
|
action: () => {
|
||||||
|
// do nothing
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
title: 'New Item 2',
|
||||||
|
iconClassName: 'icon argo-icon-deploy',
|
||||||
|
action: () => {
|
||||||
|
// do nothing
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'New Item 3',
|
||||||
|
action: () => {
|
||||||
|
// do nothing
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'New Item 4',
|
||||||
|
iconClassName: 'fa fa-times-circle',
|
||||||
|
disabled: true,
|
||||||
|
action: () => {
|
||||||
|
// do nothing
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
const history = createHistory();
|
||||||
|
|
||||||
|
function ensureSelected(vals: string[], selected: string[]): string[] {
|
||||||
|
const res = new Set(selected);
|
||||||
|
vals.forEach((item) => res.add(item));
|
||||||
|
return Array.from(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
storiesOf('Page', module)
|
||||||
|
.add('default', withState({ selectedFilter: [] })(({store}: { store: Store<any> }) => (
|
||||||
|
<Router history={history}>
|
||||||
|
<Route path={location.pathname}>
|
||||||
|
<Layout navItems={navItems}>
|
||||||
|
<Page title='Hello world!' toolbar={{ breadcrumbs, actionMenu, filter: {
|
||||||
|
items: [
|
||||||
|
{ content: (changeSelection) => (
|
||||||
|
<React.Fragment>
|
||||||
|
Filter type one: <a onClick={() => changeSelection(ensureSelected(['1', '2'], store.state.selectedFilter))}>all</a>
|
||||||
|
</React.Fragment>
|
||||||
|
)},
|
||||||
|
{label: 'filter 1', value: '1' },
|
||||||
|
{label: 'filter 2', value: '2' },
|
||||||
|
{ content: (changeSelection) => (
|
||||||
|
<React.Fragment>
|
||||||
|
Filter type two: <a onClick={() => changeSelection(ensureSelected(['3', '4'], store.state.selectedFilter))}>all</a>
|
||||||
|
</React.Fragment>
|
||||||
|
)},
|
||||||
|
{label: 'filter 3', value: '3' },
|
||||||
|
{label: 'filter 4', value: '4' },
|
||||||
|
],
|
||||||
|
selectedValues: store.state.selectedFilter,
|
||||||
|
selectionChanged: (vals) => {
|
||||||
|
store.set({ selectedFilter: vals });
|
||||||
|
},
|
||||||
|
}}}>
|
||||||
|
<div style={{padding: '1em'}}>
|
||||||
|
<div className='white-box'>
|
||||||
|
Hello world!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</Layout>
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
|
))).add('dynamic toolbar', () => (
|
||||||
|
<Router history={history}>
|
||||||
|
<Route path={location.pathname}>
|
||||||
|
<Layout navItems={navItems}>
|
||||||
|
<Page title='Hello world!' toolbar={timer(0, 1000).pipe(map(() => ({ breadcrumbs: [{title: 'hello ' + new Date().toLocaleTimeString()}] })))}>
|
||||||
|
<div style={{padding: '1em'}}>
|
||||||
|
<div className='white-box'>
|
||||||
|
Hello world!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</Layout>
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
|
)).add('compact nav bar', () => {
|
||||||
|
const manyNavItems = [];
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
manyNavItems.push({ path: location.pathname + '/' + i, title: 'Sample', iconClassName: 'argo-icon-docs' });
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Router history={history}>
|
||||||
|
<Route path={location.pathname}>
|
||||||
|
<Layout navItems={manyNavItems}>
|
||||||
|
<Page title='Hello world!'>
|
||||||
|
<div style={{padding: '1em'}}>
|
||||||
|
<div className='white-box'>
|
||||||
|
Hello world!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</Layout>
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,18 +1,15 @@
|
||||||
|
import { Store, withState } from '@dump247/storybook-state';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Checkbox as ReactCheckbox } from 'react-form';
|
import { Checkbox as ReactCheckbox} from 'react-form';
|
||||||
import { Text } from 'react-form';
|
import { Text } from 'react-form';
|
||||||
|
|
||||||
import { Checkbox } from '../src/components/checkbox';
|
import { Checkbox, FormField } from '../src/components';
|
||||||
import { FormField } from '../src/components/form-field/form-field';
|
|
||||||
import { App } from './utils';
|
import { App } from './utils';
|
||||||
|
|
||||||
export default {
|
storiesOf('Popup', module)
|
||||||
title: 'Popup',
|
.add('confirmation', () => (
|
||||||
};
|
|
||||||
|
|
||||||
export const Confirmation = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
|
@ -21,76 +18,57 @@ export const Confirmation = () => {
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('confirmation with custom form inside', withState({ checked: false })(({store}: { store: Store<any> }) => (
|
||||||
}
|
|
||||||
Confirmation.storyName = 'confirmation';
|
|
||||||
|
|
||||||
export const ConfirmationWithCustomFormInside = () => {
|
|
||||||
const [checked, setChecked] = React.useState(false);
|
|
||||||
return (
|
|
||||||
(
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<div>
|
<div>
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
const confirmed = await apis.popup.confirm('Do it!', () => (
|
const confirmed = await apis.popup.confirm('Do it!', () => (
|
||||||
<div>
|
<div>
|
||||||
Click checkbox and confirm <Checkbox checked={checked} onChange={setChecked} />
|
Click checkbox and confirm <Checkbox checked={store.state.checked} onChange={(val) => store.set({ checked: val })} />
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
action('Confirmed')(confirmed);
|
action('Confirmed')(confirmed);
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
<p>Checked?: {JSON.stringify(checked)}</p>
|
<p>Checked?: {JSON.stringify(store.state.checked)}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
)
|
),
|
||||||
);
|
)).add('prompt', () => (
|
||||||
}
|
|
||||||
ConfirmationWithCustomFormInside.storyName = 'confirmation with custom form inside';
|
|
||||||
|
|
||||||
export const Prompt = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
const values = await apis.popup.prompt('Enter name', (api) => (
|
const values = await apis.popup.prompt('Enter name', (api) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='First Name' formApi={api} field='firstName' component={Text} />
|
<FormField label='First Name' formApi={api} field='firstName' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Last Name' formApi={api} field='lastName' component={Text} />
|
<FormField label='Last Name' formApi={api} field='lastName' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
), { validate: (vals) => ({
|
||||||
validate: (vals) => ({
|
|
||||||
firstName: !vals.firstName && 'First Name is required',
|
firstName: !vals.firstName && 'First Name is required',
|
||||||
lastName: !vals.lastName && 'Last Name is required',
|
lastName: !vals.lastName && 'Last Name is required',
|
||||||
})
|
})});
|
||||||
});
|
|
||||||
|
|
||||||
action('Prompt values')(values);
|
action('Prompt values')(values);
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with custom submit', () => (
|
||||||
}
|
|
||||||
Prompt.storyName = 'prompt';
|
|
||||||
|
|
||||||
export const PromptWithCustomSubmit = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={() => {
|
<button className='argo-button argo-button--base' onClick={() => {
|
||||||
apis.popup.prompt('Username: test Password: test', (api) => (
|
apis.popup.prompt('Username: test Password: test', (api) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Username' formApi={api} field='username' component={Text} />
|
<FormField label='Username' formApi={api} field='username' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
), {
|
||||||
validate: (vals) => ({
|
validate: (vals) => ({
|
||||||
|
@ -109,23 +87,18 @@ export const PromptWithCustomSubmit = () => {
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with red title and icon, with custom submit', () => (
|
||||||
}
|
|
||||||
PromptWithCustomSubmit.storyName = 'prompt with custom submit';
|
|
||||||
|
|
||||||
export const PromptWithRedTitleAndIconWithCustomSubmit = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={() => {
|
<button className='argo-button argo-button--base' onClick={() => {
|
||||||
apis.popup.prompt('Username: test Password: test', (api) => (
|
apis.popup.prompt('Username: test Password: test', (api) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Username' formApi={api} field='username' component={Text} />
|
<FormField label='Username' formApi={api} field='username' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
), {
|
||||||
validate: (vals) => ({
|
validate: (vals) => ({
|
||||||
|
@ -146,30 +119,25 @@ export const PromptWithRedTitleAndIconWithCustomSubmit = () => {
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon', () => (
|
||||||
}
|
|
||||||
PromptWithRedTitleAndIconWithCustomSubmit.storyName = 'prompt with red title and icon, with custom submit';
|
|
||||||
|
|
||||||
export const PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={() => {
|
<button className='argo-button argo-button--base' onClick={() => {
|
||||||
apis.popup.prompt('Username: test Password: test', (api) => (
|
apis.popup.prompt('Username: test Password: test', (api) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Username' formApi={api} field='username' component={Text} />
|
<FormField label='Username' formApi={api} field='username' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Re-enter password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Re-enter password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
<h4>This is an h4 header</h4>
|
<h4>This is an h4 header</h4>
|
||||||
<p>This is a paragraph</p>
|
<p>This is a paragraph</p>
|
||||||
<h4>This is another h4 header</h4>
|
<h4>This is another h4 header</h4>
|
||||||
<p>This is a paragraph</p>
|
<p>This is a paragraph</p>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
), {
|
||||||
validate: (vals) => ({
|
validate: (vals) => ({
|
||||||
|
@ -190,23 +158,18 @@ export const PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCente
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with green clock icon and custom submit', () => (
|
||||||
}
|
|
||||||
PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon.storyName = 'prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon';
|
|
||||||
|
|
||||||
export const PromptWithGreenClockIconAndCustomSubmit = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={() => {
|
<button className='argo-button argo-button--base' onClick={() => {
|
||||||
apis.popup.prompt('Username: test Password: test', (api) => (
|
apis.popup.prompt('Username: test Password: test', (api) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Username' formApi={api} field='username' component={Text} />
|
<FormField label='Username' formApi={api} field='username' component={Text} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
), {
|
||||||
validate: (vals) => ({
|
validate: (vals) => ({
|
||||||
|
@ -222,16 +185,12 @@ export const PromptWithGreenClockIconAndCustomSubmit = () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ name: 'argo-icon-clock', color: 'success' });
|
{ name: 'argo-icon-clock', color: 'success'}
|
||||||
|
);
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with just headers and paragraphs', () => (
|
||||||
}
|
|
||||||
PromptWithGreenClockIconAndCustomSubmit.storyName = 'prompt with green clock icon and custom submit';
|
|
||||||
|
|
||||||
export const PromptWithJustHeadersAndParagraphs = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
|
@ -242,17 +201,13 @@ export const PromptWithJustHeadersAndParagraphs = () => {
|
||||||
<h4>This is another h4 header</h4>
|
<h4>This is another h4 header</h4>
|
||||||
<p>This is a paragraph</p>
|
<p>This is a paragraph</p>
|
||||||
</div>
|
</div>
|
||||||
));
|
)
|
||||||
|
);
|
||||||
action('Prompt values')(values);
|
action('Prompt values')(values);
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with only paragraphs. Additional top padding is optional for the first paragraph', () => (
|
||||||
}
|
|
||||||
PromptWithJustHeadersAndParagraphs.storyName = 'prompt with just headers and paragraphs';
|
|
||||||
|
|
||||||
export const PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
|
@ -261,17 +216,13 @@ export const PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstPa
|
||||||
<p style={{paddingTop: '20px'}}>This is a paragraph</p>
|
<p style={{paddingTop: '20px'}}>This is a paragraph</p>
|
||||||
<p>This is another paragraph</p>
|
<p>This is another paragraph</p>
|
||||||
</div>
|
</div>
|
||||||
));
|
)
|
||||||
|
);
|
||||||
action('Prompt values')(values);
|
action('Prompt values')(values);
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
)).add('prompt with React Checkbox that is checked by default; Username default set to admin', () => (
|
||||||
}
|
|
||||||
PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph.storyName = 'prompt with only paragraphs. Additional top padding is optional for the first paragraph';
|
|
||||||
|
|
||||||
export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAdmin = () => {
|
|
||||||
return (
|
|
||||||
<App>
|
<App>
|
||||||
{(apis) => (
|
{(apis) => (
|
||||||
<button className='argo-button argo-button--base' onClick={async () => {
|
<button className='argo-button argo-button--base' onClick={async () => {
|
||||||
|
@ -285,11 +236,11 @@ export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAd
|
||||||
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
<FormField label='Password' formApi={api} field='password' component={Text} componentProps={{type: 'password'}} />
|
||||||
</div>
|
</div>
|
||||||
<div className='argo-form-row'>
|
<div className='argo-form-row'>
|
||||||
<ReactCheckbox id='popup-react-checkbox' field='checkboxField' />{' '}
|
<ReactCheckbox id='popup-react-checkbox' field='checkboxField'/> <label htmlFor='popup-react-checkbox'>This is a React Checkbox</label>
|
||||||
<label htmlFor='popup-react-checkbox'>This is a React Checkbox</label>
|
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
), {
|
),
|
||||||
|
{
|
||||||
validate: (vals) => ({
|
validate: (vals) => ({
|
||||||
username: !vals.username && 'Username is required',
|
username: !vals.username && 'Username is required',
|
||||||
password: !vals.password && 'Password is required',
|
password: !vals.password && 'Password is required',
|
||||||
|
@ -311,6 +262,4 @@ export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAd
|
||||||
}}>Click me</button>
|
}}>Click me</button>
|
||||||
)}
|
)}
|
||||||
</App>
|
</App>
|
||||||
);
|
))
|
||||||
}
|
|
||||||
PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAdmin.storyName = 'prompt with React Checkbox that is checked by default; Username default set to admin';
|
|
|
@ -1,42 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { Select } from '../src/components/select/select';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Select',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = () => {
|
|
||||||
const [selected, setSelected] = React.useState('option1');
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h4>
|
|
||||||
Selected option value: {selected}
|
|
||||||
<button className='argo-button argo-button--base' onClick={() => setSelected('option2')} >Select option 2</button>
|
|
||||||
</h4>
|
|
||||||
<Select
|
|
||||||
value={selected}
|
|
||||||
placeholder='Select something'
|
|
||||||
options={['option1', { value: 'option2', title: 'Option 2' }]}
|
|
||||||
onChange={(option) => setSelected(option.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Default.storyName = 'default';
|
|
||||||
|
|
||||||
export const MultiSelect = () => {
|
|
||||||
const [selected, setSelected] = React.useState(['option1']);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Select
|
|
||||||
value={selected}
|
|
||||||
multiSelect={true}
|
|
||||||
placeholder='Select something'
|
|
||||||
options={['option1', { value: 'option2', title: 'Option 2' }]}
|
|
||||||
onMultiChange={(options) => setSelected(options.map((item) => item.value))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
MultiSelect.storyName = 'multi-select';
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Store, withState } from '@dump247/storybook-state';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Select } from '../src/components';
|
||||||
|
|
||||||
|
storiesOf('Select', module)
|
||||||
|
.add('default', withState({ selected: 'option1' })(({store}: { store: Store<any> }) => (
|
||||||
|
() => (
|
||||||
|
<div>
|
||||||
|
<h4>
|
||||||
|
Selected option value: {store.state.selected}
|
||||||
|
<button className='argo-button argo-button--base' onClick={() => store.set({ selected: 'option2' })} >Select option 2</button>
|
||||||
|
</h4>
|
||||||
|
<Select
|
||||||
|
value={store.state.selected}
|
||||||
|
placeholder='Select something'
|
||||||
|
options={['option1', { value: 'option2', title: 'Option 2' }]}
|
||||||
|
onChange={(option) => store.set({ selected: option.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))),
|
||||||
|
).add('multi-select', withState({ selected: 'option1' })(({store}: { store: Store<any> }) => (
|
||||||
|
() => (
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
value={store.state.selected}
|
||||||
|
multiSelect={true}
|
||||||
|
placeholder='Select something'
|
||||||
|
options={['option1', { value: 'option2', title: 'Option 2' }]}
|
||||||
|
onMultiChange={(options) => store.set({ selected: options.map((item) => item.value) })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))),
|
||||||
|
);
|
|
@ -1,11 +1,8 @@
|
||||||
import {default as classNames} from 'classnames';
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import * as classNames from 'classnames';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
export default {
|
class TableExample extends React.Component<{}, {selectedIndex: number}> {
|
||||||
title: 'Table',
|
|
||||||
};
|
|
||||||
|
|
||||||
class TableExample extends React.Component<any, {selectedIndex: number}> {
|
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -37,5 +34,5 @@ class TableExample extends React.Component<any, {selectedIndex: number}> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Default = () => <TableExample />;
|
storiesOf('Table', module)
|
||||||
Default.storyName = 'default';
|
.add('default', () => <TableExample/>);
|
|
@ -1,14 +1,10 @@
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { Tabs } from '../src/components';
|
||||||
|
|
||||||
import { Tabs } from '../src/components/tabs/tabs';
|
storiesOf('Tabs', module)
|
||||||
|
.add('basic tabs', () => (
|
||||||
export default {
|
<Tabs tabs={[{
|
||||||
title: 'Tabs',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BasicTabs = () => (
|
|
||||||
<Tabs
|
|
||||||
tabs={[{
|
|
||||||
title: 'Tab 1',
|
title: 'Tab 1',
|
||||||
content: <p>Tab 1 content</p>,
|
content: <p>Tab 1 content</p>,
|
||||||
key: 'tab1',
|
key: 'tab1',
|
||||||
|
@ -18,5 +14,4 @@ export const BasicTabs = () => (
|
||||||
key: 'tab2',
|
key: 'tab2',
|
||||||
badge: '5',
|
badge: '5',
|
||||||
}]}/>
|
}]}/>
|
||||||
);
|
));
|
||||||
BasicTabs.storyName = 'basic tabs';
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"declaration": false,
|
||||||
|
"lib": [
|
||||||
|
"es2017",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./**/*",
|
||||||
|
"../src/**/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,10 +1,7 @@
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Notifications } from '../src/components/notifications/notifications';
|
import { Notifications, NotificationsApi, NotificationsManager, Popup, PopupApi, PopupManager, PopupProps} from '../src/components';
|
||||||
import { NotificationsApi, NotificationsManager } from '../src/components/notifications/notification-manager';
|
|
||||||
import { Popup, PopupProps } from '../src/components/popup/popup';
|
|
||||||
import { PopupApi, PopupManager } from '../src/components/popup/popup-manager';
|
|
||||||
|
|
||||||
export class App extends React.Component<{ children: (apis: {
|
export class App extends React.Component<{ children: (apis: {
|
||||||
notifications: NotificationsApi,
|
notifications: NotificationsApi,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"tslint:recommended", "tslint-react"
|
||||||
|
],
|
||||||
|
"jsRules": {},
|
||||||
|
"rules": {
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"no-var-requires": false,
|
||||||
|
"interface-name": false,
|
||||||
|
"jsx-no-multiline-js": false,
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"jsx-alignment": false,
|
||||||
|
"max-line-length": [true, 180],
|
||||||
|
"jsx-no-lambda": false,
|
||||||
|
"array-type": false,
|
||||||
|
"max-classes-per-file": false
|
||||||
|
},
|
||||||
|
"rulesDirectory": []
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
|
@ -1,14 +1,13 @@
|
||||||
|
import {Key, useKeyListener} from 'react-keyhooks';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Key, useKeyListener} from '../../shared';
|
|
||||||
import {useClickOutside, useTimeout} from '../../utils/utils';
|
import {useClickOutside, useTimeout} from '../../utils/utils';
|
||||||
import {EffectDiv} from '../effect-div/effect-div';
|
import {EffectDiv} from '../effect-div/effect-div';
|
||||||
import {Tooltip} from '../tooltip/tooltip';
|
import {Tooltip} from '../tooltip/tooltip';
|
||||||
|
|
||||||
import {Theme} from '../theme-div/theme-div';
|
|
||||||
import './action-button.scss';
|
import './action-button.scss';
|
||||||
|
import {Theme} from '../theme-div/theme-div';
|
||||||
|
|
||||||
export interface ActionButtonProps {
|
export interface ActionButtonProps {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
||||||
action?: Function;
|
action?: Function;
|
||||||
label?: string;
|
label?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
&__items {
|
&__items {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
max-height: 12em;
|
max-height: 12em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -20,6 +20,10 @@
|
||||||
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.05);
|
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.05);
|
||||||
line-height: 0.5em;
|
line-height: 0.5em;
|
||||||
|
|
||||||
|
&--inverted {
|
||||||
|
bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
padding: 0.75em 0;
|
padding: 0.75em 0;
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
||||||
|
|
||||||
export const Primary = (args: any) => (
|
export const Primary = (args: any) => (
|
||||||
<div style={{width: '50%', paddingBottom: '6em'}}>
|
<div style={{width: '50%', paddingBottom: '6em'}}>
|
||||||
<Autocomplete {...args} items={['hello', 'world']} />
|
<Autocomplete items={['hello', 'world']} {...args} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
import {Key, KeybindingContext, KeybindingProvider, useNav} from 'react-keyhooks';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
|
||||||
import {Key, KeybindingContext, KeybindingProvider, useNav} from '../../shared';
|
|
||||||
import {Input, InputProps, SetInputFxn, useDebounce, useInput} from '../input/input';
|
import {Input, InputProps, SetInputFxn, useDebounce, useInput} from '../input/input';
|
||||||
import ThemeDiv from '../theme-div/theme-div';
|
import ThemeDiv from '../theme-div/theme-div';
|
||||||
|
|
||||||
|
@ -10,28 +9,23 @@ interface AutocompleteProps extends InputProps {
|
||||||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAutocomplete = (init: string): [string, SetInputFxn, AutocompleteProps] => {
|
export const useAutocomplete = (init: string, callback?: (val: string) => void): [string, SetInputFxn, AutocompleteProps] => {
|
||||||
const [state, setState, input] = useInput(init);
|
const [state, setState, Input] = useInput(init);
|
||||||
const autocomplete = input as AutocompleteProps;
|
const Autocomplete = Input as AutocompleteProps;
|
||||||
if (autocomplete.ref) {
|
if (Autocomplete.ref) {
|
||||||
autocomplete.inputref = input.ref;
|
Autocomplete.inputref = Input.ref;
|
||||||
delete autocomplete.ref;
|
delete Autocomplete.ref;
|
||||||
}
|
}
|
||||||
return [state, setState, autocomplete];
|
return [state, setState, Autocomplete];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Autocomplete = (
|
export const Autocomplete = (
|
||||||
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
||||||
items: string[];
|
items: string[];
|
||||||
abbreviations?: Map<string, string>;
|
|
||||||
inputStyle?: React.CSSProperties;
|
inputStyle?: React.CSSProperties;
|
||||||
onItemClick?: (item: string) => void;
|
onItemClick?: (item: string) => void;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||||
value: string;
|
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
className?: string;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
|
@ -44,15 +38,10 @@ export const Autocomplete = (
|
||||||
export const RenderAutocomplete = (
|
export const RenderAutocomplete = (
|
||||||
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
||||||
items: string[];
|
items: string[];
|
||||||
abbreviations?: Map<string, string>;
|
|
||||||
inputStyle?: React.CSSProperties;
|
inputStyle?: React.CSSProperties;
|
||||||
onItemClick?: (item: string) => void;
|
onItemClick?: (item: string) => void;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||||
value: string;
|
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
className?: string;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const [curItems, setCurItems] = React.useState(props.items || []);
|
const [curItems, setCurItems] = React.useState(props.items || []);
|
||||||
|
@ -60,12 +49,12 @@ export const RenderAutocomplete = (
|
||||||
const inputRef = props.inputref || nullInputRef;
|
const inputRef = props.inputref || nullInputRef;
|
||||||
const autocompleteRef = React.useRef(null);
|
const autocompleteRef = React.useRef(null);
|
||||||
const [showSuggestions, setShowSuggestions] = React.useState(false);
|
const [showSuggestions, setShowSuggestions] = React.useState(false);
|
||||||
const [pos, nav, reset] = useNav(props.items?.length);
|
const [pos, nav, reset] = useNav(props.items.length);
|
||||||
const menuRef = React.useRef(null);
|
const menuRef = React.useRef(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
function unfocus(e: any) {
|
function unfocus(e: any) {
|
||||||
if (autocompleteRef.current && !autocompleteRef.current.contains(e.target) && menuRef.current && !menuRef.current.contains(e.target)) {
|
if (autocompleteRef.current && !autocompleteRef.current.contains(e.target)) {
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -79,95 +68,58 @@ export const RenderAutocomplete = (
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const filtered = (props.items || []).filter((i) => {
|
const filtered = (props.items || []).filter((i) => {
|
||||||
if (i) {
|
return i.includes(debouncedVal);
|
||||||
return props.abbreviations !== undefined
|
|
||||||
? i.toLowerCase().includes(debouncedVal?.toLowerCase()) || props.abbreviations.get(i)?.includes(debouncedVal?.toLowerCase())
|
|
||||||
: i.toLowerCase().includes(debouncedVal?.toLowerCase());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
setCurItems(filtered.length > 0 ? filtered : props.items);
|
setCurItems(filtered.length > 0 ? filtered : props.items);
|
||||||
}, [debouncedVal, props.items]);
|
}, [debouncedVal, props.items]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (props.value !== null && props.value !== '') {
|
|
||||||
setShowSuggestions(true);
|
|
||||||
}
|
|
||||||
}, [props.value]);
|
|
||||||
|
|
||||||
const {useKeybinding} = React.useContext(KeybindingContext);
|
const {useKeybinding} = React.useContext(KeybindingContext);
|
||||||
|
useKeybinding(Key.TAB, (e) => {
|
||||||
const target = {
|
if (showSuggestions) {
|
||||||
combo: false,
|
if (pos === curItems.length - 1) {
|
||||||
target: inputRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
useKeybinding({
|
|
||||||
keys: Key.TAB,
|
|
||||||
action: () => {
|
|
||||||
if (showSuggestions) {
|
|
||||||
if (pos === curItems.length - 1) {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
nav(1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
...target,
|
|
||||||
});
|
|
||||||
|
|
||||||
useKeybinding({
|
|
||||||
keys: Key.ESCAPE,
|
|
||||||
action: () => {
|
|
||||||
if (showSuggestions) {
|
|
||||||
reset();
|
reset();
|
||||||
setShowSuggestions(false);
|
|
||||||
if (inputRef && inputRef.current) {
|
|
||||||
inputRef.current.blur();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
nav(1);
|
||||||
},
|
return true;
|
||||||
...target,
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
useKeybinding({
|
useKeybinding(Key.ESCAPE, (e) => {
|
||||||
keys: Key.ENTER,
|
if (showSuggestions) {
|
||||||
action: () => {
|
reset();
|
||||||
if (showSuggestions && props.onItemClick) {
|
setShowSuggestions(false);
|
||||||
props.onItemClick(curItems[pos]);
|
if (inputRef && inputRef.current) {
|
||||||
setShowSuggestions(false);
|
inputRef.current.blur();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
...target,
|
|
||||||
});
|
|
||||||
|
|
||||||
useKeybinding({
|
|
||||||
keys: Key.UP,
|
|
||||||
action: () => {
|
|
||||||
if (showSuggestions) {
|
|
||||||
nav(-1);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
...target,
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
useKeybinding({
|
useKeybinding(Key.ENTER, () => {
|
||||||
keys: Key.DOWN,
|
if (showSuggestions && props.onItemClick) {
|
||||||
action: () => {
|
props.onItemClick(curItems[pos]);
|
||||||
if (showSuggestions) {
|
setShowSuggestions(false);
|
||||||
nav(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
...target,
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
useKeybinding(Key.UP, () => {
|
||||||
|
if (showSuggestions) {
|
||||||
|
nav(-1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
useKeybinding(Key.DOWN, () => {
|
||||||
|
if (showSuggestions) {
|
||||||
|
nav(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = props.style;
|
const style = props.style;
|
||||||
|
@ -176,42 +128,37 @@ export const RenderAutocomplete = (
|
||||||
delete trimmedProps.inputStyle;
|
delete trimmedProps.inputStyle;
|
||||||
delete trimmedProps.onItemClick;
|
delete trimmedProps.onItemClick;
|
||||||
|
|
||||||
const [position, setPosition] = React.useState({top: 0, left: 0});
|
const [inverted, setInverted] = React.useState(false);
|
||||||
|
|
||||||
const checkDirection = () => {
|
const checkDirection = () => {
|
||||||
if (autocompleteRef && autocompleteRef.current && menuRef.current) {
|
if (autocompleteRef && autocompleteRef.current && menuRef.current && !(event.target === menuRef.current)) {
|
||||||
if (inputRef.current && menuRef.current) {
|
const node = inputRef.current;
|
||||||
const rect = inputRef.current.getBoundingClientRect();
|
if (node && menuRef.current) {
|
||||||
const menuHeight = menuRef.current.clientHeight;
|
const rect = node.getBoundingClientRect();
|
||||||
const offset = window.innerHeight - rect.bottom;
|
const computedStyle = window.getComputedStyle(node);
|
||||||
const inverted = offset < menuHeight;
|
const marginBottom = parseInt(computedStyle.marginBottom, 10) || 0;
|
||||||
|
let menuTop = rect.bottom + marginBottom;
|
||||||
const newPos = {
|
if (window.innerHeight - (menuTop + menuRef.current.offsetHeight) < 30) {
|
||||||
top: inverted ? rect.top - menuRef.current.clientHeight : rect.top + rect.height,
|
if (!inverted) {
|
||||||
left: rect.left,
|
setInverted(true);
|
||||||
};
|
}
|
||||||
if (position.left !== newPos.left || position.top !== newPos.top) {
|
} else {
|
||||||
setPosition(newPos);
|
if (inverted) {
|
||||||
|
setInverted(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
checkDirection();
|
|
||||||
document.addEventListener('scroll', checkDirection, true);
|
document.addEventListener('scroll', checkDirection, true);
|
||||||
document.addEventListener('resize', checkDirection, true);
|
document.addEventListener('resize', checkDirection, true);
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('scroll', checkDirection);
|
document.removeEventListener('scroll', checkDirection);
|
||||||
document.removeEventListener('resize', checkDirection);
|
document.removeEventListener('resize', checkDirection);
|
||||||
};
|
};
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (props.onChange) {
|
|
||||||
props.onChange(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='autocomplete' ref={autocompleteRef} style={style as any}>
|
<div className='autocomplete' ref={autocompleteRef} style={style as any}>
|
||||||
|
@ -220,28 +167,23 @@ export const RenderAutocomplete = (
|
||||||
style={props.inputStyle}
|
style={props.inputStyle}
|
||||||
innerref={inputRef}
|
innerref={inputRef}
|
||||||
className={(props.className || '') + ' autocomplete__input'}
|
className={(props.className || '') + ' autocomplete__input'}
|
||||||
onChange={onChange}
|
onChange={(e) => {
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setShowSuggestions(true);
|
|
||||||
checkDirection();
|
checkDirection();
|
||||||
|
setShowSuggestions(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<div ref={menuRef}>
|
||||||
{ReactDOM.createPortal(
|
<ThemeDiv className={`autocomplete__items ${inverted ? 'autocomplete__items--inverted' : ''}`} hidden={!showSuggestions || (props.items || []).length < 1}>
|
||||||
<ThemeDiv
|
|
||||||
className='autocomplete__items'
|
|
||||||
style={{
|
|
||||||
visibility: !showSuggestions || (props.items || []).length < 1 ? 'hidden' : 'visible',
|
|
||||||
overflow: !showSuggestions || (props.items || []).length < 1 ? 'hidden' : null,
|
|
||||||
top: position.top,
|
|
||||||
left: position.left,
|
|
||||||
}}
|
|
||||||
innerref={menuRef}>
|
|
||||||
{(curItems || []).map((i, n) => (
|
{(curItems || []).map((i, n) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onChange({target: {value: i}} as React.ChangeEvent<HTMLInputElement>);
|
props.onChange({target: {value: i}} as React.ChangeEvent<HTMLInputElement>);
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
if (props.onItemClick) {
|
if (props.onItemClick) {
|
||||||
props.onItemClick(i);
|
props.onItemClick(i);
|
||||||
|
@ -251,9 +193,8 @@ export const RenderAutocomplete = (
|
||||||
{i}
|
{i}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</ThemeDiv>,
|
</ThemeDiv>
|
||||||
document.body
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,4 +17,30 @@
|
||||||
background-color: $argo-color-teal-6;
|
background-color: $argo-color-teal-6;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
height: 2em;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: white;
|
||||||
|
transition: background-color 200ms ease;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-grow: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
background-color: $argo-color-teal-3;
|
||||||
|
color: $argo-color-teal-8;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,35 @@ export const Checkbox = (props: {value?: boolean; onChange?: (value: boolean) =>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface CheckboxOption {
|
||||||
|
label: string;
|
||||||
|
count?: number;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CheckboxRow = (props: {value: boolean; onChange?: (value: boolean) => void; option: CheckboxOption}) => {
|
||||||
|
const [value, setValue] = React.useState(props.value);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setValue(props.value);
|
||||||
|
}, [props.value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`checkbox__item ${value ? 'checkbox__item--selected' : ''}`} onClick={() => setValue(!value)}>
|
||||||
|
<Checkbox
|
||||||
|
onChange={(val) => {
|
||||||
|
setValue(val);
|
||||||
|
props.onChange(val);
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
style={{
|
||||||
|
marginRight: '8px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{props.option.icon && <div style={{marginRight: '5px'}}>{props.option.icon}</div>}
|
||||||
|
<div className='checkbox__item__label'>{props.option.label}</div>
|
||||||
|
<div style={{marginLeft: 'auto'}}>{props.option.count}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
@import '../../styles/colors.scss';
|
||||||
|
|
||||||
|
.code-editor {
|
||||||
|
height: 100%;
|
||||||
|
&--editor {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 0;
|
||||||
|
font-family: monospace;
|
||||||
|
appearance: none;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid $argo-color-gray-4;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import ThemeDiv from '../theme-div/theme-div';
|
||||||
|
import {Controlled as ReactCodeMirror} from 'react-codemirror2';
|
||||||
|
import {Editor as CMEditor, EditorConfiguration} from 'codemirror';
|
||||||
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import 'codemirror/mode/jsx/jsx';
|
||||||
|
import 'codemirror/theme/neo.css';
|
||||||
|
import './code-editor.scss';
|
||||||
|
|
||||||
|
export const Editor = (props: {setCode: (code: string) => void; init?: string}) => {
|
||||||
|
const [code, setCode] = React.useState(props.init || '');
|
||||||
|
const editorRef = React.useRef<CMEditor | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setCode(props.init);
|
||||||
|
}, [props.init]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeDiv className='code-editor'>
|
||||||
|
<ReactCodeMirror
|
||||||
|
className='code-editor--editor'
|
||||||
|
editorDidMount={(editorInstance) => {
|
||||||
|
editorRef.current = editorInstance;
|
||||||
|
}}
|
||||||
|
onBeforeChange={(editorInstance, data, newCode) => {
|
||||||
|
if (editorInstance.hasFocus()) {
|
||||||
|
setCode(newCode);
|
||||||
|
props.setCode(newCode);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={
|
||||||
|
{
|
||||||
|
mode: 'jsx',
|
||||||
|
autoCloseTags: true,
|
||||||
|
autoCloseBrackets: true,
|
||||||
|
theme: 'neo',
|
||||||
|
viewportMargin: 50,
|
||||||
|
lineNumbers: true,
|
||||||
|
extraKeys: {
|
||||||
|
Tab: (cm: any) => {
|
||||||
|
if (cm.somethingSelected()) {
|
||||||
|
cm.indentSelection('add');
|
||||||
|
} else {
|
||||||
|
const indent = cm.getOption('indentUnit') as number;
|
||||||
|
const spaces = Array(indent + 1).join(' ');
|
||||||
|
cm.replaceSelection(spaces);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as EditorConfiguration
|
||||||
|
}
|
||||||
|
value={code}
|
||||||
|
/>
|
||||||
|
</ThemeDiv>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Editor;
|
|
@ -11,13 +11,13 @@ interface EffectDivProps extends React.DetailedHTMLProps<React.HTMLAttributes<HT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EffectDiv is a component that attaches a background to a div, that can be animated with CSS transitions or otherwise.
|
* EffectDiv is a component that attaches a background to a div, that can be animated with CSS transitions or otherwise.
|
||||||
* It was designed to avoid text artifacts when scaling a div; an EffectDiv allows you to easily scale JUST its background, and not its contents.
|
It was designed to avoid text artifacts when scaling a div; an EffectDiv allows you to easily scale JUST its background, and not its contents.
|
||||||
*
|
|
||||||
* You can drop in replace a div with an EffectDiv, but to add a background effect, you need to:
|
You can drop in replace a div with an EffectDiv, but to add a background effect, you need to:
|
||||||
*
|
|
||||||
* - Remove background styles from the main div (including border and border-radius)
|
- Remove background styles from the main div (including border and border-radius)
|
||||||
* - Add the styles you removed to the `&__background` selector
|
- Add the styles you removed to the `&__background` selector
|
||||||
* - Add transitions to the `&__background` selector
|
- Add transitions to the `&__background` selector
|
||||||
*/
|
*/
|
||||||
export const EffectDiv = (props: EffectDivProps) => {
|
export const EffectDiv = (props: EffectDivProps) => {
|
||||||
const backgroundCl = appendSuffixToClasses(props.className, '__background');
|
const backgroundCl = appendSuffixToClasses(props.className, '__background');
|
||||||
|
|
|
@ -10,6 +10,8 @@ export * from './header/header';
|
||||||
export * from './info-item/info-item';
|
export * from './info-item/info-item';
|
||||||
export * from './input/input';
|
export * from './input/input';
|
||||||
export * from './menu/menu';
|
export * from './menu/menu';
|
||||||
|
export * from './pod/pod';
|
||||||
|
export * from './replica-set/replica-set';
|
||||||
export * from './row/row';
|
export * from './row/row';
|
||||||
export * from './text/text';
|
export * from './text/text';
|
||||||
export {ThemeDiv} from './theme-div/theme-div';
|
export {ThemeDiv} from './theme-div/theme-div';
|
||||||
|
|
|
@ -58,23 +58,17 @@
|
||||||
&--row {
|
&--row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0.5em 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
label {
|
label {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
.info-item {
|
.info-item {
|
||||||
margin: 0.25em 0;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
display: flex;
|
&:last-child {
|
||||||
min-width: 0;
|
margin-right: 0;
|
||||||
padding-left: 25px;
|
}
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,13 +43,12 @@ export const InfoItem = (props: InfoItemProps) => {
|
||||||
* Displays a right justified InfoItem (or multiple InfoItems) and a left justfied label
|
* Displays a right justified InfoItem (or multiple InfoItems) and a left justfied label
|
||||||
*/
|
*/
|
||||||
export const InfoItemRow = (props: {label: string | React.ReactNode; items?: InfoItemProps | InfoItemProps[]; lightweight?: boolean}) => {
|
export const InfoItemRow = (props: {label: string | React.ReactNode; items?: InfoItemProps | InfoItemProps[]; lightweight?: boolean}) => {
|
||||||
let {items} = props;
|
let {label, items} = props;
|
||||||
const {label} = props;
|
|
||||||
let itemComponents = null;
|
let itemComponents = null;
|
||||||
if (!Array.isArray(items)) {
|
if (!Array.isArray(items)) {
|
||||||
items = [items];
|
items = [items];
|
||||||
}
|
}
|
||||||
itemComponents = items?.map((c, i) => <InfoItem key={`${c} ${i}`} {...c} lightweight={c?.lightweight === undefined ? props.lightweight : c?.lightweight} />);
|
itemComponents = items.map((c, i) => <InfoItem key={`${c} ${i}`} {...c} lightweight={c.lightweight === undefined ? props.lightweight : c.lightweight} />);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='info-item--row'>
|
<div className='info-item--row'>
|
||||||
|
@ -58,7 +57,7 @@ export const InfoItemRow = (props: {label: string | React.ReactNode; items?: Inf
|
||||||
<label>{label}</label>
|
<label>{label}</label>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{props.items && <div className='info-item--row__container'>{itemComponents}</div>}
|
{props.items && <div style={{marginLeft: 'auto', display: 'flex', minWidth: 0, paddingLeft: '25px'}}>{itemComponents}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue