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:
|
||||
push:
|
||||
|
@ -7,9 +7,9 @@ jobs:
|
|||
build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
- run: yarn install
|
||||
- run: yarn build-v2
|
||||
node-version: 12
|
||||
- run: yarn --cwd v2 install
|
||||
- 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:
|
||||
workflow_dispatch:
|
||||
|
@ -11,12 +11,12 @@ jobs:
|
|||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
node-version: 12
|
||||
- run: cd v2 && npm publish
|
||||
env:
|
||||
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
|
||||
bundle
|
||||
.vscode
|
||||
.idea
|
||||
*.log
|
||||
coverage/
|
||||
.DS_STORE
|
||||
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
|
||||
|
||||
<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
|
||||
|
||||
1. Install Toolset: [NodeJS](https://nodejs.org/en/download/) and [Yarn v1](https://classic.yarnpkg.com/en/docs)
|
||||
1. Install Dependencies: run `yarn install`
|
||||
1. Run: `yarn start` - starts the [Storybook v6](https://storybook.js.org/docs/6.5/get-started/install) 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`.
|
||||
1. Install Toolset: [NodeJS](https://nodejs.org/en/download/) and [Yarn](https://yarnpkg.com)
|
||||
2. Install Dependencies: From your command line, navigate to the argo-ui directory and run `yarn install` to install dependencies.
|
||||
3. Run: `yarn start` - starts https://storybook.js.org/ dev server
|
||||
|
|
|
@ -11,7 +11,7 @@ module.exports = {
|
|||
setupFilesAfterEnv: ['<rootDir>src/setupTests.ts'],
|
||||
globals: {
|
||||
'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",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"src",
|
||||
"v2"
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .tsx .",
|
||||
"deduplicate": "yarn-deduplicate -s fewer yarn.lock",
|
||||
"build": "build-storybook -o ./dist/storybook",
|
||||
"lint": "tslint -p ./src",
|
||||
"test": "jest",
|
||||
"utils:icons": "rm -f src/assets/fonts/* && node ./scripts/icons/generator.js",
|
||||
"start": "NODE_OPTIONS='--openssl-legacy-provider' 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"
|
||||
"start": "start-storybook -p 6006"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@tippy.js/react": "^3.1.1",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^3.32.1",
|
||||
"@fortawesome/fontawesome-free": "^5.8.1",
|
||||
"@tippy.js/react": "^2.1.2",
|
||||
"@types/react-autocomplete": "^1.8.5",
|
||||
"@types/react-form": "^2.16.1",
|
||||
"@types/react-helmet": "^6.1.0",
|
||||
"classnames": "^2.2.5",
|
||||
"foundation-sites": "^6.4.3",
|
||||
"history": "^4.10.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-autocomplete": "1.8.1",
|
||||
"history": "^4.7.2",
|
||||
"moment": "^2.20.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-autocomplete": "^1.8.1",
|
||||
"react-form": "^2.16.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-toastify": "9.0.3",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^9.0.0",
|
||||
"xterm": "^4.19.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
"react-toastify": "^5.0.1",
|
||||
"rxjs": "^6.6.6",
|
||||
"typescript": "^4.0.3",
|
||||
"xterm": "2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.5",
|
||||
"react": "^16.9.3",
|
||||
"react-dom": "^16.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@storybook/addon-actions": "6.5.0-beta.1",
|
||||
"@storybook/addon-controls": "6.5.0-beta.1",
|
||||
"@storybook/addon-essentials": "6.5.0-beta.1",
|
||||
"@storybook/addon-links": "6.5.0-beta.1",
|
||||
"@storybook/addons": "6.5.0-beta.1",
|
||||
"@storybook/react": "6.5.0-beta.1",
|
||||
"@types/classnames": "^2.3.1",
|
||||
"@babel/core": "^7.11.6",
|
||||
"@dump247/storybook-state": "^1.5.0",
|
||||
"@storybook/addon-actions": "^6.0.22",
|
||||
"@storybook/addon-links": "^6.0.22",
|
||||
"@storybook/addons": "^6.0.22",
|
||||
"@storybook/react": "^6.0.22",
|
||||
"@types/chai": "^4.1.2",
|
||||
"@types/classnames": "^2.2.3",
|
||||
"@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/history": "^4.7.8",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^18.15.3",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"@types/react": "^16.8.5",
|
||||
"@types/react-autocomplete": "^1.8.4",
|
||||
"@types/prop-types": "^15.5.2",
|
||||
"@types/react": "^16.9.3",
|
||||
"@types/react-dom": "^16.9.3",
|
||||
"@types/react-form": "^2.16.1",
|
||||
"@types/react-helmet": "^6.1.6",
|
||||
"@types/react-router-dom": "^4.2.2",
|
||||
"@types/uuid": "^9.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"babel-loader": "^8.2.5",
|
||||
"@types/react-router-dom": "^4.2.3",
|
||||
"@types/react-test-renderer": "^16.9.3",
|
||||
"@types/storybook__addon-actions": "^3.0.2",
|
||||
"@types/storybook__addon-links": "^3.3.0",
|
||||
"@types/storybook__react": "^3.0.7",
|
||||
"@types/yamljs": "^0.2.30",
|
||||
"chai": "^4.1.2",
|
||||
"copy-webpack-plugin": "^4.3.1",
|
||||
"css-loader": "^3.6.0",
|
||||
"copyfiles": "^1.2.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.7",
|
||||
"enzyme-to-json": "^3.6.2",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-plugin-react": "^7.31.1",
|
||||
"glob": "^8.0.3",
|
||||
"enzyme-adapter-react-16": "^1.15.5",
|
||||
"enzyme-to-json": "^3.6.1",
|
||||
"foreman": "^3.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"jest": "^26.6.2",
|
||||
"jscs": "^3.0.7",
|
||||
"node-sass": "^4.12.0",
|
||||
"nodemon": "^1.14.11",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "^16.9.3",
|
||||
"react-dom": "^16.9.3",
|
||||
"sass": "^1.55.0",
|
||||
"sass-loader": "^v10.1.0",
|
||||
"storybook": "6.5.0-beta.1",
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-jest": "^26.5.6",
|
||||
"ts-node": "^10.9.1",
|
||||
"react-hot-loader": "^3.1.3",
|
||||
"react-test-renderer": "^16.9.3",
|
||||
"sass-loader": "^6.0.6",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"style-loader": "^0.20.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",
|
||||
"webpack": "^4.46.0",
|
||||
"yarn-deduplicate": "^6.0.2"
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "^16.8.5",
|
||||
"@types/node": "14.11.2"
|
||||
"@types/react": "16.9.3",
|
||||
"@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 ReactForm from 'react-form';
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const AutocompleteField = ReactForm.FormField((props: AutocompleteProps &
|
|||
}}
|
||||
inputProps={{
|
||||
className: props.className,
|
||||
style: {borderBottom: 'none', position: 'unset'},
|
||||
style: {borderBottom: 'none'},
|
||||
}}
|
||||
value={value}
|
||||
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 {CSSProperties, ReactNode} from 'react';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import ReactAutocomplete from 'react-autocomplete';
|
||||
import * as ReactAutocomplete from 'react-autocomplete';
|
||||
|
||||
require('./autocomplete.scss');
|
||||
export interface AutocompleteApi {
|
||||
refresh(): any;
|
||||
}
|
||||
|
@ -25,8 +23,6 @@ export interface AutocompleteProps {
|
|||
autoCompleteRef?: (api: AutocompleteApi) => any;
|
||||
filterSuggestions?: boolean;
|
||||
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) => {
|
||||
|
@ -59,7 +55,7 @@ export const Autocomplete = (props: AutocompleteProps) => {
|
|||
wrapperProps.className = classNames('select', wrapperProps.className);
|
||||
return (
|
||||
<ReactAutocomplete
|
||||
autoHighlight={props.autoHighlight}
|
||||
autoHighlight={true}
|
||||
ref={(el: any) => {
|
||||
if (el) {
|
||||
if (el.refs.input) {
|
||||
|
@ -109,16 +105,16 @@ export const Autocomplete = (props: AutocompleteProps) => {
|
|||
shouldItemRender={(item: AutocompleteOption, val: string) => {
|
||||
return !props.filterSuggestions || item.label.toLowerCase().includes(val.toLowerCase());
|
||||
}}
|
||||
renderMenu={function(menuItems: ReactNode[], _: string, style: CSSProperties) {
|
||||
renderMenu={function(menuItems, _, style) {
|
||||
if (menuItems.length === 0) {
|
||||
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}
|
||||
value={props.value}
|
||||
renderItem={(item: any, isSelected: boolean) => (
|
||||
renderItem={(item, isSelected) => (
|
||||
<div className={classNames('select__option', {selected: isSelected})} key={item.label}>
|
||||
{(props.renderItem && props.renderItem(item)) || item.label}
|
||||
</div>
|
||||
|
|
|
@ -31,7 +31,7 @@ interface LoaderState<TInput, TResult> {
|
|||
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 = {
|
||||
router: 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 ReactDOM from 'react-dom';
|
||||
import { BehaviorSubject, fromEvent, merge, Subscription } from 'rxjs';
|
||||
|
@ -9,7 +9,6 @@ export interface DropDownProps {
|
|||
anchor: React.ComponentType;
|
||||
children: React.ReactNode | (() => React.ReactNode);
|
||||
qeId?: string;
|
||||
onOpenStateChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export interface DropDownState {
|
||||
|
@ -59,12 +58,11 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
|||
);
|
||||
}
|
||||
|
||||
public UNSAFE_componentWillMount() {
|
||||
public componentWillMount() {
|
||||
this.subscriptions = [merge(
|
||||
dropDownOpened.pipe(filter((dropdown) => dropdown !== this)),
|
||||
fromEvent(document, 'click').pipe(filter((event: Event) => {
|
||||
const targetAttached = (event.target as Node).parentElement;
|
||||
return targetAttached && this.content && this.state.opened && !this.content.contains(event.target as Node) && !this.el.contains(event.target as Node);
|
||||
return this.content && this.state.opened && !this.content.contains(event.target as Node) && !this.el.contains(event.target as Node);
|
||||
})),
|
||||
).subscribe(() => {
|
||||
this.close();
|
||||
|
@ -82,9 +80,6 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
|||
|
||||
public close() {
|
||||
this.setState({ opened: false });
|
||||
if (this.props.onOpenStateChange) {
|
||||
this.props.onOpenStateChange(false);
|
||||
}
|
||||
}
|
||||
|
||||
private refreshState() {
|
||||
|
@ -117,8 +112,6 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
|
|||
const newState = this.refreshState();
|
||||
newState.opened = true;
|
||||
this.setState(newState);
|
||||
if (this.props.onOpenStateChange) {
|
||||
this.props.onOpenStateChange(true);
|
||||
}
|
||||
dropDownOpened.next(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import * as moment from 'moment';
|
||||
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 = '';
|
||||
|
||||
/**
|
||||
* Output a string duration from a number of seconds
|
||||
*
|
||||
* @param {number} props.durationS - The number of seconds.
|
||||
*/
|
||||
export function Duration(props: {durationS: number}) {
|
||||
return <span>{formatDuration(props.durationS, 2)}</span>;
|
||||
if (momentTime.diff(momentTimeStart, 'hours') === 0) {
|
||||
formattedTime = ('0' + duration.minutes()).slice(-2) + ':' + ('0' + duration.seconds()).slice(-2) + ' min';
|
||||
} else {
|
||||
if (momentTime.diff(momentTimeStart, 'days') > 0) {
|
||||
formattedTime += momentTime.diff(momentTimeStart, 'days') + ' days' + (props.allowNewLines ? '<br>' : ' ');
|
||||
}
|
||||
|
||||
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 ReactForm from 'react-form';
|
||||
|
||||
import { Select as ArgoSelect, SelectOption, SelectProps } from '../select/select';
|
||||
|
||||
import { v1 as uuid } from 'uuid';
|
||||
const uuid = require('uuid/v1');
|
||||
|
||||
require('./form-field.scss');
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ require('../styles/main.scss');
|
|||
|
||||
export { Utils } from './utils';
|
||||
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 { 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 { type TopBarProps, type Toolbar, type TopBarFilter } from './top-bar/top-bar';
|
||||
export { type Tab, Tabs } from './tabs/tabs';
|
||||
export { TopBarProps, Toolbar, TopBarFilter } from './top-bar/top-bar';
|
||||
export { Tab, Tabs } from './tabs/tabs';
|
||||
export { Duration } from './duration';
|
||||
export { SlidingPanel } from './sliding-panel/sliding-panel';
|
||||
export { LogsViewer } from './logs-viewer/logs-viewer';
|
||||
|
@ -16,7 +16,7 @@ export * from './notifications/notifications';
|
|||
export * from './notifications/notification-manager';
|
||||
export * from './popup/popup';
|
||||
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 { Tooltip } from './tooltip/tooltip';
|
||||
export * from './ticker';
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
@import '../../styles/config';
|
||||
@import '../../styles/theme';
|
||||
|
||||
.layout {
|
||||
overflow: hidden;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-1');
|
||||
}
|
||||
&__loader {
|
||||
@include themify($themes) {
|
||||
background-color: themed('layout-loader-bg');
|
||||
}
|
||||
background-color: rgba($argo-color-gray-7, 0.4);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
import * as React from 'react';
|
||||
import {NavBar, NavBarStyle} from '../nav-bar/nav-bar';
|
||||
import { NavBar } from '../nav-bar/nav-bar';
|
||||
|
||||
require('./layout.scss');
|
||||
|
||||
export interface LayoutProps {
|
||||
navItems: Array<{ path: string; iconClassName: string; title: string; }>;
|
||||
version?: () => React.ReactElement;
|
||||
navBarStyle?: NavBarStyle;
|
||||
theme?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Layout = (props: LayoutProps) => (
|
||||
<div className={props.theme ? 'theme-' + props.theme : 'theme-light'}>
|
||||
<div className='layout'>
|
||||
<NavBar items={props.navItems} version={props.version} style={props.navBarStyle} />
|
||||
<NavBar items={props.navItems} version={props.version}/>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import '../../styles/config';
|
||||
@import 'node_modules/xterm/css/xterm';
|
||||
@import 'node_modules/xterm/dist/xterm';
|
||||
|
||||
.logs-viewer {
|
||||
font: normal 13px/1.2 'Courier', sans-serif;
|
||||
|
@ -11,3 +11,16 @@
|
|||
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 { 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');
|
||||
|
||||
|
@ -16,43 +17,27 @@ export interface LogsViewerProps {
|
|||
}
|
||||
|
||||
export class LogsViewer extends React.Component<LogsViewerProps> {
|
||||
private terminal: Terminal;
|
||||
private fitAddon: FitAddon;
|
||||
private terminal: any;
|
||||
private subscription: Subscription | null = null;
|
||||
|
||||
constructor(props: LogsViewerProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public UNSAFE_componentWillReceiveProps(nextProps: LogsViewerProps) {
|
||||
public componentWillReceiveProps(nextProps: LogsViewerProps) {
|
||||
if (this.props.source.key !== nextProps.source.key) {
|
||||
this.refresh(nextProps.source);
|
||||
}
|
||||
}
|
||||
|
||||
public initTerminal(container: HTMLElement) {
|
||||
this.fitAddon = new FitAddon();
|
||||
this.terminal = new Terminal({
|
||||
scrollback: 99999,
|
||||
allowTransparency: true,
|
||||
theme: {
|
||||
background: 'transparent',
|
||||
foreground: '#495763',
|
||||
},
|
||||
theme: 'ax',
|
||||
});
|
||||
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);
|
||||
return false
|
||||
}
|
||||
});
|
||||
this.terminal.open(container);
|
||||
this.terminal.fit();
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
|
@ -71,14 +56,12 @@ export class LogsViewer extends React.Component<LogsViewerProps> {
|
|||
);
|
||||
}
|
||||
|
||||
public shouldComponentUpdate() {
|
||||
public shouldComponentUpdate(prevProps: LogsViewerProps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private refresh(source: LogsSource) {
|
||||
if (this.terminal) {
|
||||
this.terminal.reset();
|
||||
}
|
||||
this.ensureUnsubscribed();
|
||||
const onLoadComplete = () => {
|
||||
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 React from 'react';
|
||||
|
||||
|
@ -10,11 +10,6 @@ require('./nav-bar.scss');
|
|||
export interface NavBarProps {
|
||||
items: Array<{ path: string; iconClassName: string; title: string; }>;
|
||||
version?: () => React.ReactElement;
|
||||
style?: NavBarStyle;
|
||||
}
|
||||
|
||||
export interface NavBarStyle {
|
||||
backgroundColor?: 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) => {
|
||||
const locationPath = context.router.route.location.pathname;
|
||||
const navBarStyle = {
|
||||
...(props.style?.backgroundColor && {background: `linear-gradient(to bottom, ${props.style?.backgroundColor}, #999`}),
|
||||
};
|
||||
return (
|
||||
<div className={classNames('nav-bar', {
|
||||
'nav-bar--compact': (props.items || []).length >= 10,
|
||||
})} style={navBarStyle}>
|
||||
})}>
|
||||
<div className='nav-bar__logo'>
|
||||
<img src='assets/images/logo.png' alt='Argo'/>
|
||||
<div className='nav-bar__version'>{props.version && props.version()}</div>
|
||||
|
|
|
@ -37,7 +37,9 @@ export class NavigationManager implements NavigationApi {
|
|||
path = `${path}?${urlQuery}`;
|
||||
}
|
||||
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');
|
||||
} else {
|
||||
if (options.replace) {
|
||||
|
|
|
@ -47,10 +47,6 @@ export class Notifications extends React.Component<NotificationsProps> {
|
|||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}} style={{
|
||||
// fit long words by wrapping them instead of overflowing past the width
|
||||
overflowWrap: 'break-word',
|
||||
maxWidth: '240px'
|
||||
}}>
|
||||
{next.content}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Observable } from 'rxjs';
|
||||
|
@ -12,8 +12,6 @@ require('./page.scss');
|
|||
interface PageProps extends React.Props<any> {
|
||||
title: string;
|
||||
toolbar?: Toolbar | Observable<Toolbar>;
|
||||
topBarTitle?: string;
|
||||
useTitleOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface PageContextProps {
|
||||
|
@ -34,7 +32,7 @@ export const Page = (props: PageProps) => {
|
|||
<PageContext.Consumer>
|
||||
{(ctx) => {
|
||||
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);
|
||||
} else if (props.title) {
|
||||
titleParts = [props.title].concat(titleParts);
|
||||
|
@ -47,7 +45,7 @@ export const Page = (props: PageProps) => {
|
|||
}}
|
||||
</PageContext.Consumer>
|
||||
<div className='page__top-bar'>
|
||||
<TopBar title={props.topBarTitle ? props.topBarTitle : props.title} toolbar={toolbar}/>
|
||||
<TopBar title={props.title} toolbar={toolbar}/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
|
|
@ -14,7 +14,7 @@ export interface PopupApi {
|
|||
},
|
||||
customIcon?: {name: string, color: string},
|
||||
titleColor?: string,
|
||||
defaultValues?: FormValues,
|
||||
defaultValues?: {},
|
||||
): Promise<FormValues | null>;
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,8 @@ export class PopupManager implements PopupApi {
|
|||
content,
|
||||
footer: (
|
||||
<div>
|
||||
<button qe-id='argo-popup-ok-button' className='argo-button argo-button--base' onClick={() => closeAndResolve(true)}>OK</button>
|
||||
<button qe-id='argo-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(false)}>Cancel</button>
|
||||
<button qe-id='argo-popup-ok-button' className='argo-button argo-button--base' onClick={() => closeAndResolve(true)}>OK</button> <button
|
||||
qe-id='argo-popup-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(false)}>Cancel</button>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ export class PopupManager implements PopupApi {
|
|||
},
|
||||
customIcon?: { name: string, color: string },
|
||||
titleColor?: string,
|
||||
defaultValues?: FormValues,
|
||||
defaultValues?: {},
|
||||
): Promise<FormValues | null> {
|
||||
return new Promise((resolve) => {
|
||||
const closeAndResolve = (result: FormValues | null) => {
|
||||
|
@ -98,8 +98,8 @@ export class PopupManager implements PopupApi {
|
|||
),
|
||||
footer: (
|
||||
<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-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(null)}>Cancel</button>
|
||||
<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-cancel-button' className='argo-button argo-button--base-o' onClick={() => closeAndResolve(null)}>Cancel</button>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@import '../../styles/config';
|
||||
@import '../../styles/theme';
|
||||
|
||||
$popup-border-radius: 8px;
|
||||
|
||||
|
@ -10,17 +9,12 @@ $popup-border-radius: 8px;
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 200;
|
||||
// Just in case the theme class is not added
|
||||
background-color: rgba(222, 222, 222, 0.62); /*dim the background*/
|
||||
@include themify($themes) {
|
||||
background-color: themed('overlay');
|
||||
}
|
||||
border: 1px solid $argo-color-gray-4;
|
||||
|
||||
.popup-container {
|
||||
position: relative;
|
||||
@include themify($themes) {
|
||||
box-shadow: 3px 3px 20px themed('shadow');
|
||||
}
|
||||
box-shadow: 3px 3px 20px #888888;
|
||||
margin: 0 auto;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
|
@ -29,7 +23,6 @@ $popup-border-radius: 8px;
|
|||
max-height: 600px;
|
||||
z-index: 20;
|
||||
border-radius: $popup-border-radius;
|
||||
overflow: auto;
|
||||
|
||||
&__header {
|
||||
line-height: 60px;
|
||||
|
@ -48,16 +41,12 @@ $popup-border-radius: 8px;
|
|||
float: right;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 5px;
|
||||
top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__normal {
|
||||
// Just in case the theme class is not added
|
||||
background-color: $argo-color-gray-2;
|
||||
@include themify($themes) {
|
||||
background-color: themed('light-argo-gray-2');
|
||||
}
|
||||
}
|
||||
|
||||
&__red {
|
||||
|
@ -71,11 +60,7 @@ $popup-border-radius: 8px;
|
|||
}
|
||||
|
||||
&__footer {
|
||||
// Just in case the theme class is not added
|
||||
background-color: $argo-color-gray-1;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-2');
|
||||
}
|
||||
bottom: 0;
|
||||
padding-top: 20px;
|
||||
padding-left: 30px;
|
||||
|
@ -94,41 +79,28 @@ $popup-border-radius: 8px;
|
|||
}
|
||||
|
||||
&__body {
|
||||
// Just in case the theme class is not added
|
||||
background-color: $argo-color-gray-1;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-2');
|
||||
}
|
||||
|
||||
&__hasNoIcon {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
h4, p, ul {
|
||||
h4, p {
|
||||
margin-right: 10px;
|
||||
font-size: 15px;
|
||||
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 {
|
||||
margin-top: 40px;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 10px;
|
||||
color: $argo-color-gray-6;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface BasePopupProps {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@import '../../styles/config';
|
||||
@import '../../styles/theme';
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
|
@ -16,22 +15,18 @@
|
|||
position: relative;
|
||||
padding: 8px 20px 8px 0;
|
||||
font-size: 15px;
|
||||
border-bottom: 2px solid #ccc;
|
||||
transition: border .2s;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@include themify($themes) {
|
||||
border-bottom: 2px solid themed('border');;
|
||||
}
|
||||
}
|
||||
|
||||
&__value-arrow {
|
||||
// right most, vertically centered
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
top: 8px;
|
||||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&__options {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { fromEvent, Subscription } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
@import 'node_modules/foundation-sites/scss/util/util';
|
||||
|
||||
@import '../../styles/config';
|
||||
@import '../../styles/theme';
|
||||
|
||||
$sliding-panel-header-height: 50px;
|
||||
$sliding-panel-footer-height: 64px;
|
||||
|
@ -51,10 +50,7 @@ $sliding-panel-middle-width: 600px;
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
width: 80%;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-2');
|
||||
color: themed('text-2');
|
||||
}
|
||||
background-color: #fff;
|
||||
transition: right .5s;
|
||||
|
||||
@include breakpoint(medium down) {
|
||||
|
@ -101,17 +97,13 @@ $sliding-panel-middle-width: 600px;
|
|||
padding: 0 30px;
|
||||
line-height: $sliding-panel-header-height;
|
||||
color: $argo-color-gray-5;
|
||||
@include themify($themes) {
|
||||
background-color: themed('light-argo-gray-2');
|
||||
border-bottom: 1px solid themed('border');
|
||||
}
|
||||
background-color: $argo-color-gray-2;
|
||||
border-bottom: 1px solid #c6cfd1;
|
||||
font-weight: 500;
|
||||
font-size: .925em;
|
||||
|
||||
.sliding-panel--off-canvas & {
|
||||
@include themify($themes) {
|
||||
background-color: themed('light-argo-gray-2');
|
||||
}
|
||||
background-color: $argo-color-gray-2;
|
||||
}
|
||||
|
||||
strong {
|
||||
|
@ -128,9 +120,8 @@ $sliding-panel-middle-width: 600px;
|
|||
position: relative;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
@include themify($themes) {
|
||||
background-color: themed('light-argo-gray-2');
|
||||
}
|
||||
background-color: $argo-color-gray-2;
|
||||
|
||||
.sliding-panel:not(.sliding-panel--no-padding) & {
|
||||
padding: 30px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { Key, KeybindingContext, KeybindingProvider } from '../../../v2';
|
||||
|
||||
export interface SlidingPanelProps extends React.Props<any> {
|
||||
isShown?: boolean;
|
||||
|
@ -16,43 +15,7 @@ export interface SlidingPanelProps extends React.Props<any> {
|
|||
|
||||
require('./sliding-panel.scss');
|
||||
|
||||
export const SlidingPanel = (props: SlidingPanelProps) => {
|
||||
return (
|
||||
<KeybindingProvider>
|
||||
<RenderSlidingPanel {...props} />
|
||||
</KeybindingProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderSlidingPanel = (props: SlidingPanelProps) => {
|
||||
|
||||
const {useKeybinding} = React.useContext(KeybindingContext);
|
||||
|
||||
const closeButtonRef = React.useRef(null);
|
||||
const bodyDivRef = React.useRef(null);
|
||||
const panelHeaderDivRef = React.useRef(null);
|
||||
const panelFooterDivRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (closeButtonRef && closeButtonRef.current) {
|
||||
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 (
|
||||
export const SlidingPanel = (props: SlidingPanelProps) => (
|
||||
<div className={classNames('sliding-panel', {
|
||||
'sliding-panel--has-header': !!props.header,
|
||||
'sliding-panel--has-footer': !!props.footer,
|
||||
|
@ -63,24 +26,21 @@ const RenderSlidingPanel = (props: SlidingPanelProps) => {
|
|||
'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}>
|
||||
<button className='sliding-panel__close' aria-hidden='true' onClick={() => props.onClose && props.onClose()}>
|
||||
<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}>
|
||||
<div className={classNames('sliding-panel__header', {'sliding-panel__header--close-btn-right-padding': props.hasCloseButton})}>
|
||||
{props.header}
|
||||
</div>
|
||||
)}
|
||||
<div className='sliding-panel__body' ref={bodyDivRef} tabIndex={-1}>
|
||||
<div className='sliding-panel__body'>
|
||||
{props.children}
|
||||
</div>
|
||||
{props.footer && (
|
||||
<div className='sliding-panel__footer' ref={panelFooterDivRef} tabIndex={-1}>
|
||||
<div className='sliding-panel__footer'>
|
||||
{props.footer}
|
||||
</div>
|
||||
)}
|
||||
|
@ -88,4 +48,3 @@ const RenderSlidingPanel = (props: SlidingPanelProps) => {
|
|||
<div className='sliding-panel__outside' onClick={() => props.onClose && props.onClose()}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
@import '../../styles/config';
|
||||
@import '../../styles/theme';
|
||||
|
||||
|
||||
.tabs {
|
||||
.fixed-width {
|
||||
|
@ -13,9 +11,7 @@
|
|||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 15px 20px 0;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-2');
|
||||
}
|
||||
background-color: $white-color;
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
box-shadow: 0 1px 3px rgba(143,164,177,.3);
|
||||
|
@ -28,9 +24,7 @@
|
|||
display: inline-block;
|
||||
margin: 0 16px;
|
||||
padding: 12px 36px;
|
||||
@include themify($themes) {
|
||||
color: themed('light-argo-gray-6');
|
||||
}
|
||||
color: $argo-color-gray-6;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
|
@ -57,17 +51,11 @@
|
|||
box-shadow: none;
|
||||
border-bottom: 1px solid $argo-color-gray-4;
|
||||
|
||||
.theme-dark & {
|
||||
border-color: $argo-color-gray-7;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $argo-color-teal-5;
|
||||
|
||||
&.active {
|
||||
@include themify($themes) {
|
||||
color: themed('text-2');
|
||||
}
|
||||
color: $argo-color-gray-8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
|
||||
export interface Tab {
|
||||
title: string;
|
||||
|
@ -114,13 +114,7 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|||
const el = parentEl.querySelector<HTMLElement>('.active');
|
||||
|
||||
if (el) {
|
||||
const newIndicatorPosition = this.getIndicatorPosition(parentEl, el);
|
||||
|
||||
if (JSON.stringify(this.state.indicatorPosition) !== JSON.stringify(newIndicatorPosition)) {
|
||||
this.setState({
|
||||
indicatorPosition: this.getIndicatorPosition(parentEl, el),
|
||||
});
|
||||
}
|
||||
this.setState({ indicatorPosition: this.getIndicatorPosition(parentEl, el) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import * as moment from 'moment';
|
||||
import * as React from 'react';
|
||||
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;
|
||||
|
||||
constructor(props: {intervalMs?: number, children?: ((time: Date) => React.ReactNode)}) {
|
||||
constructor(props: {intervalMs?: number, children?: ((time: moment.Moment) => React.ReactNode)}) {
|
||||
super(props);
|
||||
this.state = { time: new Date() };
|
||||
this.state = { time: moment() };
|
||||
this.ensureSubscribed();
|
||||
}
|
||||
|
||||
|
@ -27,7 +28,7 @@ export class Ticker extends React.Component<{intervalMs?: number, disabled?: boo
|
|||
if (this.props.disabled) {
|
||||
this.ensureUnsubscribed();
|
||||
} 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 'tippy.js/dist/tippy.css';
|
||||
import 'tippy.js/themes/light.css';
|
||||
|
||||
export const Tooltip = ( props: TippyProps ) => (
|
||||
<Tippy animation='fade' appendTo={document.body} theme='light' interactive={true} {...props} />
|
||||
);
|
||||
export const Tooltip = ( props: any ) => <Tippy animation='fade' arrow='true' {...props} />;
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
@import '../../styles/config';
|
||||
@import 'node_modules/foundation-sites/scss/util/util';
|
||||
@import '../../styles/theme';
|
||||
|
||||
.top-bar {
|
||||
line-height: $top-bar-height;
|
||||
@include themify($themes) {
|
||||
background: themed('background-2');
|
||||
}
|
||||
background: $white-color;
|
||||
transition: right .5s;
|
||||
border-bottom: 1px solid $argo-color-gray-2;
|
||||
|
||||
.theme-dark & {
|
||||
border-color: $argo-color-gray-7;
|
||||
}
|
||||
|
||||
&__left-side {
|
||||
padding-left: 20px;
|
||||
white-space: nowrap;
|
||||
|
@ -28,9 +21,7 @@
|
|||
float: left;
|
||||
font-weight: 500;
|
||||
font-size: .925em;
|
||||
@include themify($themes) {
|
||||
color: themed('light-argo-teal-7');
|
||||
}
|
||||
color: $argo-color-teal-7;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
@ -61,9 +52,7 @@
|
|||
height: $top-bar-height;
|
||||
font-weight: 500;
|
||||
font-size: .8em;
|
||||
@include themify($themes) {
|
||||
color: themed('text-2');
|
||||
}
|
||||
color: $argo-color-gray-8;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {default as classNames} from 'classnames';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -27,7 +27,7 @@ export interface ActionMenu {
|
|||
|
||||
export interface Toolbar {
|
||||
filter?: TopBarFilter<any>;
|
||||
breadcrumbs?: { title: string | React.ReactNode, path?: string; }[];
|
||||
breadcrumbs?: { title: string, path?: string; }[];
|
||||
tools?: React.ReactNode;
|
||||
actionMenu?: ActionMenu;
|
||||
}
|
||||
|
@ -68,17 +68,17 @@ const renderFilter = (filter: TopBarFilter<any>) => (
|
|||
</DropDown>
|
||||
);
|
||||
|
||||
const renderBreadcrumbs = (breadcrumbs: { title: string | React.ReactNode, path?: string; }[]) => (
|
||||
const renderBreadcrumbs = (breadcrumbs: { title: string, path?: string; }[]) => (
|
||||
<div className='top-bar__breadcrumbs'>
|
||||
{(breadcrumbs || []).map((breadcrumb, i) => {
|
||||
const nodes = [];
|
||||
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 {
|
||||
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) {
|
||||
nodes.push(<span key={`${i}_sep`} className='top-bar__sep'/>);
|
||||
nodes.push(<span key={`${breadcrumb.title}_sep`} className='top-bar__sep'/>);
|
||||
}
|
||||
return nodes;
|
||||
})}
|
||||
|
|
|
@ -5,6 +5,45 @@ export function isPromise<T>(obj: any): obj is PromiseLike<T> {
|
|||
}
|
||||
|
||||
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> {
|
||||
const observable = val as Observable<T>;
|
||||
if (observable && observable.subscribe && observable.forEach) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import * as H from 'history';
|
||||
import { match} from 'react-router';
|
||||
|
||||
import { NotificationsApi } from './components/notifications/notification-manager';
|
||||
import { PopupApi } from './components/popup/popup-manager';
|
||||
import { NotificationsApi, PopupApi } from './components';
|
||||
|
||||
export interface AppContext {
|
||||
router: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {configure } from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
import * as Adapter from 'enzyme-adapter-react-16';
|
||||
|
||||
configure({ adapter: new Adapter() });
|
||||
|
|
|
@ -14,10 +14,10 @@ $argo-icon-fonts-root: '/' !default;
|
|||
|
||||
@font-face {
|
||||
font-family: "argo-icon";
|
||||
src: url($argo-icon-fonts-root + "argo-icon.eot?73a3124f147facf3e90960528bdfae73?#iefix") format("embedded-opentype"),
|
||||
url($argo-icon-fonts-root + "argo-icon.woff?73a3124f147facf3e90960528bdfae73") format("woff"),
|
||||
url($argo-icon-fonts-root + "argo-icon.ttf?73a3124f147facf3e90960528bdfae73") format("truetype"),
|
||||
url($argo-icon-fonts-root + "argo-icon.svg?73a3124f147facf3e90960528bdfae73#argo-icon") format("svg");
|
||||
src: url($argo-icon-fonts-root + "argo-icon.eot?af0f627a6b22b6f9f7b649206eee4263?#iefix") format("embedded-opentype"),
|
||||
url($argo-icon-fonts-root + "argo-icon.woff?af0f627a6b22b6f9f7b649206eee4263") format("woff"),
|
||||
url($argo-icon-fonts-root + "argo-icon.ttf?af0f627a6b22b6f9f7b649206eee4263") format("truetype"),
|
||||
url($argo-icon-fonts-root + "argo-icon.svg?af0f627a6b22b6f9f7b649206eee4263#argo-icon") format("svg");
|
||||
}
|
||||
|
||||
i {
|
||||
|
@ -300,117 +300,114 @@ i {
|
|||
.argo-icon-notification:before {
|
||||
content: "\f14f";
|
||||
}
|
||||
.argo-icon-oci:before {
|
||||
.argo-icon-pencil:before {
|
||||
content: "\f150";
|
||||
}
|
||||
.argo-icon-pencil:before {
|
||||
.argo-icon-play-2:before {
|
||||
content: "\f151";
|
||||
}
|
||||
.argo-icon-play-2:before {
|
||||
.argo-icon-play:before {
|
||||
content: "\f152";
|
||||
}
|
||||
.argo-icon-play:before {
|
||||
.argo-icon-pod:before {
|
||||
content: "\f153";
|
||||
}
|
||||
.argo-icon-pod:before {
|
||||
.argo-icon-policies:before {
|
||||
content: "\f154";
|
||||
}
|
||||
.argo-icon-policies:before {
|
||||
.argo-icon-policy:before {
|
||||
content: "\f155";
|
||||
}
|
||||
.argo-icon-policy:before {
|
||||
.argo-icon-profile:before {
|
||||
content: "\f156";
|
||||
}
|
||||
.argo-icon-profile:before {
|
||||
.argo-icon-push:before {
|
||||
content: "\f157";
|
||||
}
|
||||
.argo-icon-push:before {
|
||||
.argo-icon-report-card:before {
|
||||
content: "\f158";
|
||||
}
|
||||
.argo-icon-report-card:before {
|
||||
.argo-icon-resubmit-failed:before {
|
||||
content: "\f159";
|
||||
}
|
||||
.argo-icon-resubmit-failed:before {
|
||||
.argo-icon-retry:before {
|
||||
content: "\f15a";
|
||||
}
|
||||
.argo-icon-retry:before {
|
||||
.argo-icon-right-navigation-toolbar:before {
|
||||
content: "\f15b";
|
||||
}
|
||||
.argo-icon-right-navigation-toolbar:before {
|
||||
.argo-icon-safe:before {
|
||||
content: "\f15c";
|
||||
}
|
||||
.argo-icon-safe:before {
|
||||
.argo-icon-sales-channels:before {
|
||||
content: "\f15d";
|
||||
}
|
||||
.argo-icon-sales-channels:before {
|
||||
.argo-icon-sample:before {
|
||||
content: "\f15e";
|
||||
}
|
||||
.argo-icon-sample:before {
|
||||
.argo-icon-scale:before {
|
||||
content: "\f15f";
|
||||
}
|
||||
.argo-icon-scale:before {
|
||||
.argo-icon-search:before {
|
||||
content: "\f160";
|
||||
}
|
||||
.argo-icon-search:before {
|
||||
.argo-icon-settings:before {
|
||||
content: "\f161";
|
||||
}
|
||||
.argo-icon-settings:before {
|
||||
.argo-icon-slack-02:before {
|
||||
content: "\f162";
|
||||
}
|
||||
.argo-icon-slack-02:before {
|
||||
.argo-icon-stop-property:before {
|
||||
content: "\f163";
|
||||
}
|
||||
.argo-icon-stop-property:before {
|
||||
.argo-icon-stop:before {
|
||||
content: "\f164";
|
||||
}
|
||||
.argo-icon-stop:before {
|
||||
.argo-icon-storageclass:before {
|
||||
content: "\f165";
|
||||
}
|
||||
.argo-icon-storageclass:before {
|
||||
.argo-icon-storageprovider:before {
|
||||
content: "\f166";
|
||||
}
|
||||
.argo-icon-storageprovider:before {
|
||||
.argo-icon-tag:before {
|
||||
content: "\f167";
|
||||
}
|
||||
.argo-icon-tag:before {
|
||||
.argo-icon-template:before {
|
||||
content: "\f168";
|
||||
}
|
||||
.argo-icon-template:before {
|
||||
.argo-icon-terminate:before {
|
||||
content: "\f169";
|
||||
}
|
||||
.argo-icon-terminate:before {
|
||||
.argo-icon-test:before {
|
||||
content: "\f16a";
|
||||
}
|
||||
.argo-icon-test:before {
|
||||
.argo-icon-timeline:before {
|
||||
content: "\f16b";
|
||||
}
|
||||
.argo-icon-timeline:before {
|
||||
.argo-icon-tools:before {
|
||||
content: "\f16c";
|
||||
}
|
||||
.argo-icon-tools:before {
|
||||
.argo-icon-user-groups:before {
|
||||
content: "\f16d";
|
||||
}
|
||||
.argo-icon-user-groups:before {
|
||||
.argo-icon-user-profile:before {
|
||||
content: "\f16e";
|
||||
}
|
||||
.argo-icon-user-profile:before {
|
||||
.argo-icon-user:before {
|
||||
content: "\f16f";
|
||||
}
|
||||
.argo-icon-user:before {
|
||||
.argo-icon-users:before {
|
||||
content: "\f170";
|
||||
}
|
||||
.argo-icon-users:before {
|
||||
.argo-icon-volume:before {
|
||||
content: "\f171";
|
||||
}
|
||||
.argo-icon-volume:before {
|
||||
.argo-icon-warning:before {
|
||||
content: "\f172";
|
||||
}
|
||||
.argo-icon-warning:before {
|
||||
.argo-icon-workflow:before {
|
||||
content: "\f173";
|
||||
}
|
||||
.argo-icon-workflow:before {
|
||||
.argo-icon-yaml:before {
|
||||
content: "\f174";
|
||||
}
|
||||
.argo-icon-yaml:before {
|
||||
content: "\f175";
|
||||
}
|
||||
|
|
|
@ -30,9 +30,7 @@ $argo-color-teal-7: #006F8A;
|
|||
$argo-color-teal-8: #004C67;
|
||||
|
||||
$white-color: #ffffff;
|
||||
$dark-theme-background-1: #100f0f;
|
||||
$dark-theme-background-2: #303237;
|
||||
$dark-theme-sliding-panel: #28292a;
|
||||
|
||||
// Status colors
|
||||
$argo-failed-color: #E96D76;
|
||||
$argo-failed-color-dark: #c04b4f;
|
||||
|
@ -52,7 +50,6 @@ $argo-color-yellow: #FFD100;
|
|||
$argo-running-color-dark: #378398;
|
||||
$argo-running-color-light: #02C4D3;
|
||||
$argo-running-color: #0DADEA;
|
||||
$argo-suspended-color: #766F94;
|
||||
|
||||
$argo-waiting-color-dark: $argo-color-gray-6;
|
||||
$argo-waiting-color-light: $argo-color-gray-5;
|
||||
|
|
|
@ -236,7 +236,8 @@
|
|||
|
||||
&--special {
|
||||
color: #fff;
|
||||
background: $argo-color-teal-7;
|
||||
background: linear-gradient(-58deg, #49505c 0%, #3f51b5 36%, #00c7d6 100%);
|
||||
|
||||
&:not(.disabled) {
|
||||
&:hover {
|
||||
color: #fff;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
@import '../theme';
|
||||
|
||||
.argo-container {
|
||||
position: relative;
|
||||
width: $basePageWidth + 160;
|
||||
|
@ -37,10 +35,8 @@
|
|||
position: relative;
|
||||
padding: 30px;
|
||||
font-size: 0.8125rem;
|
||||
@include themify($themes) {
|
||||
background-color: themed('background-2');
|
||||
color: themed('light-argo-gray-6');
|
||||
}
|
||||
color: $argo-color-gray-6;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
@ -91,19 +87,15 @@
|
|||
}
|
||||
|
||||
&-row {
|
||||
@include themify($themes) {
|
||||
color: themed('text-2');
|
||||
}
|
||||
color: $argo-color-gray-8;
|
||||
|
||||
.columns {
|
||||
border-bottom: 1px solid $argo-color-gray-3;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
line-height: 50px;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
@include themify($themes) {
|
||||
border-bottom: 1px solid themed('border');
|
||||
}
|
||||
|
||||
&--narrower-height {
|
||||
line-height: 20px;
|
||||
padding: 14px 0;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@import '../config';
|
||||
@import '../theme';
|
||||
|
||||
.argo-form-row {
|
||||
position: relative;
|
||||
|
@ -9,9 +8,7 @@
|
|||
|
||||
label {
|
||||
font-size: .9em;
|
||||
@include themify($themes) {
|
||||
color: themed('light-argo-gray-6');
|
||||
}
|
||||
color: $argo-color-gray-6;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -72,11 +69,8 @@
|
|||
padding: 8px 0;
|
||||
font-size: 15px;
|
||||
background-color: transparent;
|
||||
@include themify($themes) {
|
||||
color: themed('text-2');
|
||||
border-bottom: 2px solid themed('border');;
|
||||
}
|
||||
border: 0;
|
||||
border-bottom: 2px solid #ccc;
|
||||
transition: border .2s;
|
||||
|
||||
.error & {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@import '../config';
|
||||
@import '../theme';
|
||||
|
||||
.argo-table-list {
|
||||
&__head {
|
||||
|
@ -15,10 +14,8 @@
|
|||
margin: 8px 0;
|
||||
line-height: 60px;
|
||||
font-size: .8125em;
|
||||
@include themify($themes) {
|
||||
background: themed('background-2');
|
||||
color: themed('text-1');
|
||||
}
|
||||
color: $argo-color-gray-6;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
|
|
@ -1,4 +1,43 @@
|
|||
<?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">
|
||||
<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"/>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.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 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>
|
||||
|
|
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/solid';
|
||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/brands';
|
||||
@import 'node_modules/@fortawesome/fontawesome-free/scss/regular';
|
||||
|
||||
@include foundation-global-styles;
|
||||
@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": {
|
||||
"outDir": "./../bundle",
|
||||
"outDir": "./../../bundle",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"noUnusedLocals": true,
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
"../node_modules/@types"
|
||||
]
|
||||
},
|
||||
"include": ["./**/*"],
|
||||
"exclude": ["node_modules", "./**/*.test.ts", "./**/*.test.tsx", "./**/*.stories.tsx"]
|
||||
"include": [
|
||||
"./**/*"
|
||||
]
|
||||
}
|
|
@ -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 { App } from './utils';
|
||||
|
@ -8,18 +10,13 @@ function loadData(input: string): Promise<string> {
|
|||
return new Promise((resolve) => window.setTimeout(() => resolve(`hello ${input}`), 50));
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Data Loader',
|
||||
};
|
||||
|
||||
export const LoadingDataAsynchronously = () => {
|
||||
const [input, setInput] = React.useState('world');
|
||||
return (
|
||||
storiesOf('Data Loader', module)
|
||||
.add('loading data asynchronously', withState({ input: 'world' })(({store}: { store: Store<any> }) => (
|
||||
<App>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
<input value={input} onChange={(e) => setInput(e.target.value)}/>
|
||||
<DataLoader input={input} load={loadData}>
|
||||
<input value={store.state.input} onChange={(e) => store.set({ input: e.target.value })}/>
|
||||
<DataLoader input={store.state.input} load={(input) => loadData(input)}>
|
||||
{(data) => (
|
||||
<div>
|
||||
{data}
|
||||
|
@ -29,6 +26,4 @@ export const LoadingDataAsynchronously = () => {
|
|||
</React.Fragment>
|
||||
)}
|
||||
</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 { Form, Text } from 'react-form';
|
||||
import { FormField, FormSelect } from '../src/components';
|
||||
|
||||
import { FormField, FormSelect } from '../src/components/form-field/form-field';
|
||||
|
||||
export default {
|
||||
title: 'Forms',
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
storiesOf('Forms', module)
|
||||
.add('default', () => (
|
||||
<Form>
|
||||
{(api) => (
|
||||
<form style={{padding: '1em'}}>
|
||||
|
@ -24,6 +20,4 @@ export const Default = () => {
|
|||
</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 { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import { Checkbox as ReactCheckbox} from 'react-form';
|
||||
import { Text } from 'react-form';
|
||||
|
||||
import { Checkbox } from '../src/components/checkbox';
|
||||
import { FormField } from '../src/components/form-field/form-field';
|
||||
import { Checkbox, FormField } from '../src/components';
|
||||
import { App } from './utils';
|
||||
|
||||
export default {
|
||||
title: 'Popup',
|
||||
};
|
||||
|
||||
export const Confirmation = () => {
|
||||
return (
|
||||
storiesOf('Popup', module)
|
||||
.add('confirmation', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={async () => {
|
||||
|
@ -21,36 +18,24 @@ export const Confirmation = () => {
|
|||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
Confirmation.storyName = 'confirmation';
|
||||
|
||||
export const ConfirmationWithCustomFormInside = () => {
|
||||
const [checked, setChecked] = React.useState(false);
|
||||
return (
|
||||
(
|
||||
)).add('confirmation with custom form inside', withState({ checked: false })(({store}: { store: Store<any> }) => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<div>
|
||||
<button className='argo-button argo-button--base' onClick={async () => {
|
||||
const confirmed = await apis.popup.confirm('Do it!', () => (
|
||||
<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>
|
||||
));
|
||||
action('Confirmed')(confirmed);
|
||||
}}>Click me</button>
|
||||
<p>Checked?: {JSON.stringify(checked)}</p>
|
||||
<p>Checked?: {JSON.stringify(store.state.checked)}</p>
|
||||
</div>
|
||||
)}
|
||||
</App>
|
||||
)
|
||||
);
|
||||
}
|
||||
ConfirmationWithCustomFormInside.storyName = 'confirmation with custom form inside';
|
||||
|
||||
export const Prompt = () => {
|
||||
return (
|
||||
),
|
||||
)).add('prompt', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={async () => {
|
||||
|
@ -63,23 +48,16 @@ export const Prompt = () => {
|
|||
<FormField label='Last Name' formApi={api} field='lastName' component={Text} />
|
||||
</div>
|
||||
</React.Fragment>
|
||||
), {
|
||||
validate: (vals) => ({
|
||||
), { validate: (vals) => ({
|
||||
firstName: !vals.firstName && 'First Name is required',
|
||||
lastName: !vals.lastName && 'Last Name is required',
|
||||
})
|
||||
});
|
||||
})});
|
||||
|
||||
action('Prompt values')(values);
|
||||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
Prompt.storyName = 'prompt';
|
||||
|
||||
export const PromptWithCustomSubmit = () => {
|
||||
return (
|
||||
)).add('prompt with custom submit', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={() => {
|
||||
|
@ -109,12 +87,7 @@ export const PromptWithCustomSubmit = () => {
|
|||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithCustomSubmit.storyName = 'prompt with custom submit';
|
||||
|
||||
export const PromptWithRedTitleAndIconWithCustomSubmit = () => {
|
||||
return (
|
||||
)).add('prompt with red title and icon, with custom submit', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={() => {
|
||||
|
@ -146,12 +119,7 @@ export const PromptWithRedTitleAndIconWithCustomSubmit = () => {
|
|||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithRedTitleAndIconWithCustomSubmit.storyName = 'prompt with red title and icon, with custom submit';
|
||||
|
||||
export const PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon = () => {
|
||||
return (
|
||||
)).add('prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={() => {
|
||||
|
@ -190,12 +158,7 @@ export const PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCente
|
|||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithYellowTitleAndIconThreeFieldsAndCustomSubmitVerticalCenterLayoutOfIcon.storyName = 'prompt with yellow title and icon, three fields and custom submit. Vertical center layout of icon';
|
||||
|
||||
export const PromptWithGreenClockIconAndCustomSubmit = () => {
|
||||
return (
|
||||
)).add('prompt with green clock icon and custom submit', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={() => {
|
||||
|
@ -222,16 +185,12 @@ export const PromptWithGreenClockIconAndCustomSubmit = () => {
|
|||
}
|
||||
},
|
||||
},
|
||||
{ name: 'argo-icon-clock', color: 'success' });
|
||||
{ name: 'argo-icon-clock', color: 'success'}
|
||||
);
|
||||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithGreenClockIconAndCustomSubmit.storyName = 'prompt with green clock icon and custom submit';
|
||||
|
||||
export const PromptWithJustHeadersAndParagraphs = () => {
|
||||
return (
|
||||
)).add('prompt with just headers and paragraphs', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<button className='argo-button argo-button--base' onClick={async () => {
|
||||
|
@ -242,17 +201,13 @@ export const PromptWithJustHeadersAndParagraphs = () => {
|
|||
<h4>This is another h4 header</h4>
|
||||
<p>This is a paragraph</p>
|
||||
</div>
|
||||
));
|
||||
)
|
||||
);
|
||||
action('Prompt values')(values);
|
||||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithJustHeadersAndParagraphs.storyName = 'prompt with just headers and paragraphs';
|
||||
|
||||
export const PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph = () => {
|
||||
return (
|
||||
)).add('prompt with only paragraphs. Additional top padding is optional for the first paragraph', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<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>This is another paragraph</p>
|
||||
</div>
|
||||
));
|
||||
)
|
||||
);
|
||||
action('Prompt values')(values);
|
||||
}}>Click me</button>
|
||||
)}
|
||||
</App>
|
||||
);
|
||||
}
|
||||
PromptWithOnlyParagraphsAdditionalTopPaddingIsOptionalForTheFirstParagraph.storyName = 'prompt with only paragraphs. Additional top padding is optional for the first paragraph';
|
||||
|
||||
export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAdmin = () => {
|
||||
return (
|
||||
)).add('prompt with React Checkbox that is checked by default; Username default set to admin', () => (
|
||||
<App>
|
||||
{(apis) => (
|
||||
<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'}} />
|
||||
</div>
|
||||
<div className='argo-form-row'>
|
||||
<ReactCheckbox id='popup-react-checkbox' field='checkboxField' />{' '}
|
||||
<label htmlFor='popup-react-checkbox'>This is a React Checkbox</label>
|
||||
<ReactCheckbox id='popup-react-checkbox' field='checkboxField'/> <label htmlFor='popup-react-checkbox'>This is a React Checkbox</label>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
), {
|
||||
),
|
||||
{
|
||||
validate: (vals) => ({
|
||||
username: !vals.username && 'Username is required',
|
||||
password: !vals.password && 'Password is required',
|
||||
|
@ -311,6 +262,4 @@ export const PromptWithReactCheckboxThatIsCheckedByDefaultUsernameDefaultSetToAd
|
|||
}}>Click me</button>
|
||||
)}
|
||||
</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';
|
||||
|
||||
export default {
|
||||
title: 'Table',
|
||||
};
|
||||
|
||||
class TableExample extends React.Component<any, {selectedIndex: number}> {
|
||||
class TableExample extends React.Component<{}, {selectedIndex: number}> {
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
@ -37,5 +34,5 @@ class TableExample extends React.Component<any, {selectedIndex: number}> {
|
|||
}
|
||||
}
|
||||
|
||||
export const Default = () => <TableExample />;
|
||||
Default.storyName = 'default';
|
||||
storiesOf('Table', module)
|
||||
.add('default', () => <TableExample/>);
|
|
@ -1,14 +1,10 @@
|
|||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import { Tabs } from '../src/components';
|
||||
|
||||
import { Tabs } from '../src/components/tabs/tabs';
|
||||
|
||||
export default {
|
||||
title: 'Tabs',
|
||||
};
|
||||
|
||||
export const BasicTabs = () => (
|
||||
<Tabs
|
||||
tabs={[{
|
||||
storiesOf('Tabs', module)
|
||||
.add('basic tabs', () => (
|
||||
<Tabs tabs={[{
|
||||
title: 'Tab 1',
|
||||
content: <p>Tab 1 content</p>,
|
||||
key: 'tab1',
|
||||
|
@ -18,5 +14,4 @@ export const BasicTabs = () => (
|
|||
key: 'tab2',
|
||||
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 React from 'react';
|
||||
|
||||
import { Notifications } from '../src/components/notifications/notifications';
|
||||
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';
|
||||
import { Notifications, NotificationsApi, NotificationsManager, Popup, PopupApi, PopupManager, PopupProps} from '../src/components';
|
||||
|
||||
export class App extends React.Component<{ children: (apis: {
|
||||
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 {Key, useKeyListener} from '../../shared';
|
||||
import {useClickOutside, useTimeout} from '../../utils/utils';
|
||||
import {EffectDiv} from '../effect-div/effect-div';
|
||||
import {Tooltip} from '../tooltip/tooltip';
|
||||
|
||||
import {Theme} from '../theme-div/theme-div';
|
||||
import './action-button.scss';
|
||||
import {Theme} from '../theme-div/theme-div';
|
||||
|
||||
export interface ActionButtonProps {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
action?: Function;
|
||||
label?: string;
|
||||
icon?: string;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
&__items {
|
||||
z-index: 3;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
max-height: 12em;
|
||||
overflow-y: auto;
|
||||
|
@ -20,6 +20,10 @@
|
|||
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.05);
|
||||
line-height: 0.5em;
|
||||
|
||||
&--inverted {
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
z-index: 2;
|
||||
padding: 0.75em 0;
|
||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
|||
|
||||
export const Primary = (args: any) => (
|
||||
<div style={{width: '50%', paddingBottom: '6em'}}>
|
||||
<Autocomplete {...args} items={['hello', 'world']} />
|
||||
<Autocomplete items={['hello', 'world']} {...args} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {Key, KeybindingContext, KeybindingProvider, useNav} from 'react-keyhooks';
|
||||
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 ThemeDiv from '../theme-div/theme-div';
|
||||
|
||||
|
@ -10,28 +9,23 @@ interface AutocompleteProps extends InputProps {
|
|||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export const useAutocomplete = (init: string): [string, SetInputFxn, AutocompleteProps] => {
|
||||
const [state, setState, input] = useInput(init);
|
||||
const autocomplete = input as AutocompleteProps;
|
||||
if (autocomplete.ref) {
|
||||
autocomplete.inputref = input.ref;
|
||||
delete autocomplete.ref;
|
||||
export const useAutocomplete = (init: string, callback?: (val: string) => void): [string, SetInputFxn, AutocompleteProps] => {
|
||||
const [state, setState, Input] = useInput(init);
|
||||
const Autocomplete = Input as AutocompleteProps;
|
||||
if (Autocomplete.ref) {
|
||||
Autocomplete.inputref = Input.ref;
|
||||
delete Autocomplete.ref;
|
||||
}
|
||||
return [state, setState, autocomplete];
|
||||
return [state, setState, Autocomplete];
|
||||
};
|
||||
|
||||
export const Autocomplete = (
|
||||
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
items: string[];
|
||||
abbreviations?: Map<string, string>;
|
||||
inputStyle?: React.CSSProperties;
|
||||
onItemClick?: (item: string) => void;
|
||||
icon?: string;
|
||||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||
value: string;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
) => {
|
||||
return (
|
||||
|
@ -44,15 +38,10 @@ export const Autocomplete = (
|
|||
export const RenderAutocomplete = (
|
||||
props: React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
items: string[];
|
||||
abbreviations?: Map<string, string>;
|
||||
inputStyle?: React.CSSProperties;
|
||||
onItemClick?: (item: string) => void;
|
||||
icon?: string;
|
||||
inputref?: React.MutableRefObject<HTMLInputElement>;
|
||||
value: string;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
) => {
|
||||
const [curItems, setCurItems] = React.useState(props.items || []);
|
||||
|
@ -60,12 +49,12 @@ export const RenderAutocomplete = (
|
|||
const inputRef = props.inputref || nullInputRef;
|
||||
const autocompleteRef = React.useRef(null);
|
||||
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);
|
||||
|
||||
React.useEffect(() => {
|
||||
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);
|
||||
reset();
|
||||
}
|
||||
|
@ -79,32 +68,13 @@ export const RenderAutocomplete = (
|
|||
|
||||
React.useEffect(() => {
|
||||
const filtered = (props.items || []).filter((i) => {
|
||||
if (i) {
|
||||
return props.abbreviations !== undefined
|
||||
? i.toLowerCase().includes(debouncedVal?.toLowerCase()) || props.abbreviations.get(i)?.includes(debouncedVal?.toLowerCase())
|
||||
: i.toLowerCase().includes(debouncedVal?.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
return i.includes(debouncedVal);
|
||||
});
|
||||
setCurItems(filtered.length > 0 ? filtered : props.items);
|
||||
}, [debouncedVal, props.items]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.value !== null && props.value !== '') {
|
||||
setShowSuggestions(true);
|
||||
}
|
||||
}, [props.value]);
|
||||
|
||||
const {useKeybinding} = React.useContext(KeybindingContext);
|
||||
|
||||
const target = {
|
||||
combo: false,
|
||||
target: inputRef,
|
||||
};
|
||||
|
||||
useKeybinding({
|
||||
keys: Key.TAB,
|
||||
action: () => {
|
||||
useKeybinding(Key.TAB, (e) => {
|
||||
if (showSuggestions) {
|
||||
if (pos === curItems.length - 1) {
|
||||
reset();
|
||||
|
@ -113,13 +83,9 @@ export const RenderAutocomplete = (
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
...target,
|
||||
});
|
||||
|
||||
useKeybinding({
|
||||
keys: Key.ESCAPE,
|
||||
action: () => {
|
||||
useKeybinding(Key.ESCAPE, (e) => {
|
||||
if (showSuggestions) {
|
||||
reset();
|
||||
setShowSuggestions(false);
|
||||
|
@ -129,45 +95,31 @@ export const RenderAutocomplete = (
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
...target,
|
||||
});
|
||||
|
||||
useKeybinding({
|
||||
keys: Key.ENTER,
|
||||
action: () => {
|
||||
useKeybinding(Key.ENTER, () => {
|
||||
if (showSuggestions && props.onItemClick) {
|
||||
props.onItemClick(curItems[pos]);
|
||||
setShowSuggestions(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
...target,
|
||||
});
|
||||
|
||||
useKeybinding({
|
||||
keys: Key.UP,
|
||||
action: () => {
|
||||
useKeybinding(Key.UP, () => {
|
||||
if (showSuggestions) {
|
||||
nav(-1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
...target,
|
||||
});
|
||||
|
||||
useKeybinding({
|
||||
keys: Key.DOWN,
|
||||
action: () => {
|
||||
useKeybinding(Key.DOWN, () => {
|
||||
if (showSuggestions) {
|
||||
nav(1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
...target,
|
||||
});
|
||||
|
||||
const style = props.style;
|
||||
|
@ -176,42 +128,37 @@ export const RenderAutocomplete = (
|
|||
delete trimmedProps.inputStyle;
|
||||
delete trimmedProps.onItemClick;
|
||||
|
||||
const [position, setPosition] = React.useState({top: 0, left: 0});
|
||||
const [inverted, setInverted] = React.useState(false);
|
||||
|
||||
const checkDirection = () => {
|
||||
if (autocompleteRef && autocompleteRef.current && menuRef.current) {
|
||||
if (inputRef.current && menuRef.current) {
|
||||
const rect = inputRef.current.getBoundingClientRect();
|
||||
const menuHeight = menuRef.current.clientHeight;
|
||||
const offset = window.innerHeight - rect.bottom;
|
||||
const inverted = offset < menuHeight;
|
||||
|
||||
const newPos = {
|
||||
top: inverted ? rect.top - menuRef.current.clientHeight : rect.top + rect.height,
|
||||
left: rect.left,
|
||||
};
|
||||
if (position.left !== newPos.left || position.top !== newPos.top) {
|
||||
setPosition(newPos);
|
||||
if (autocompleteRef && autocompleteRef.current && menuRef.current && !(event.target === menuRef.current)) {
|
||||
const node = inputRef.current;
|
||||
if (node && menuRef.current) {
|
||||
const rect = node.getBoundingClientRect();
|
||||
const computedStyle = window.getComputedStyle(node);
|
||||
const marginBottom = parseInt(computedStyle.marginBottom, 10) || 0;
|
||||
let menuTop = rect.bottom + marginBottom;
|
||||
if (window.innerHeight - (menuTop + menuRef.current.offsetHeight) < 30) {
|
||||
if (!inverted) {
|
||||
setInverted(true);
|
||||
}
|
||||
} else {
|
||||
if (inverted) {
|
||||
setInverted(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
checkDirection();
|
||||
document.addEventListener('scroll', checkDirection, true);
|
||||
document.addEventListener('resize', checkDirection, true);
|
||||
return () => {
|
||||
document.removeEventListener('scroll', checkDirection);
|
||||
document.removeEventListener('resize', checkDirection);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (props.onChange) {
|
||||
props.onChange(e);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='autocomplete' ref={autocompleteRef} style={style as any}>
|
||||
|
@ -220,28 +167,23 @@ export const RenderAutocomplete = (
|
|||
style={props.inputStyle}
|
||||
innerref={inputRef}
|
||||
className={(props.className || '') + ' autocomplete__input'}
|
||||
onChange={onChange}
|
||||
onChange={(e) => {
|
||||
if (props.onChange) {
|
||||
props.onChange(e);
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
setShowSuggestions(true);
|
||||
checkDirection();
|
||||
setShowSuggestions(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{ReactDOM.createPortal(
|
||||
<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}>
|
||||
<div ref={menuRef}>
|
||||
<ThemeDiv className={`autocomplete__items ${inverted ? 'autocomplete__items--inverted' : ''}`} hidden={!showSuggestions || (props.items || []).length < 1}>
|
||||
{(curItems || []).map((i, n) => (
|
||||
<div
|
||||
key={i}
|
||||
onClick={() => {
|
||||
onChange({target: {value: i}} as React.ChangeEvent<HTMLInputElement>);
|
||||
props.onChange({target: {value: i}} as React.ChangeEvent<HTMLInputElement>);
|
||||
setShowSuggestions(false);
|
||||
if (props.onItemClick) {
|
||||
props.onItemClick(i);
|
||||
|
@ -251,9 +193,8 @@ export const RenderAutocomplete = (
|
|||
{i}
|
||||
</div>
|
||||
))}
|
||||
</ThemeDiv>,
|
||||
document.body
|
||||
)}
|
||||
</ThemeDiv>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,4 +17,30 @@
|
|||
background-color: $argo-color-teal-6;
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
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.
|
||||
* 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:
|
||||
*
|
||||
* - Remove background styles from the main div (including border and border-radius)
|
||||
* - Add the styles you removed to the `&__background` selector
|
||||
* - Add transitions to the `&__background` selector
|
||||
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:
|
||||
|
||||
- Remove background styles from the main div (including border and border-radius)
|
||||
- Add the styles you removed to the `&__background` selector
|
||||
- Add transitions to the `&__background` selector
|
||||
*/
|
||||
export const EffectDiv = (props: EffectDivProps) => {
|
||||
const backgroundCl = appendSuffixToClasses(props.className, '__background');
|
||||
|
|
|
@ -10,6 +10,8 @@ export * from './header/header';
|
|||
export * from './info-item/info-item';
|
||||
export * from './input/input';
|
||||
export * from './menu/menu';
|
||||
export * from './pod/pod';
|
||||
export * from './replica-set/replica-set';
|
||||
export * from './row/row';
|
||||
export * from './text/text';
|
||||
export {ThemeDiv} from './theme-div/theme-div';
|
||||
|
|
|
@ -58,23 +58,17 @@
|
|||
&--row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5em 0;
|
||||
flex-grow: 1;
|
||||
label {
|
||||
margin-right: auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.info-item {
|
||||
margin: 0.25em 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&__container {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
padding-left: 25px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,12 @@ export const InfoItem = (props: InfoItemProps) => {
|
|||
* 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}) => {
|
||||
let {items} = props;
|
||||
const {label} = props;
|
||||
let {label, items} = props;
|
||||
let itemComponents = null;
|
||||
if (!Array.isArray(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 (
|
||||
<div className='info-item--row'>
|
||||
|
@ -58,7 +57,7 @@ export const InfoItemRow = (props: {label: string | React.ReactNode; items?: Inf
|
|||
<label>{label}</label>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue