mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Merge branch 'develop' into feature/canvas-grid-resizer
This commit is contained in:
commit
eccd8a7cf2
282 changed files with 9077 additions and 6207 deletions
|
|
@ -1,2 +0,0 @@
|
|||
frontend/node_modules/**
|
||||
# **/*.js
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve"
|
||||
}
|
||||
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
0.7.2
|
||||
0.7.3
|
||||
11
.vscode/extension.json
vendored
Normal file
11
.vscode/extension.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"CoenraadS.bracket-pair-colorizer",
|
||||
"mgmcdermott.vscode-language-babel",
|
||||
"formulahendry.auto-rename-tag",
|
||||
"xabikos.javascriptsnippets",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"[javascript, typescript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
},
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact"
|
||||
],
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
}
|
||||
19
README.md
19
README.md
|
|
@ -6,14 +6,21 @@
|
|||
|
||||
ToolJet is an **open-source no-code framework** to build and deploy internal tools quickly without much effort from the engineering teams. You can connect to your data sources such as databases ( like PostgreSQL, MongoDB, Elasticsearch, etc ), API endpoints ( ToolJet supports importing OpenAPI spec & OAuth2 authorization) and external services ( like Stripe, Slack, Google Sheets, Airtable ) and use our pre-built UI widgets to build internal tools.
|
||||
|
||||

|
||||

|
||||
[](https://github.com/ToolJet/ToolJet/issues)
|
||||
[](https://github.com/ToolJet/ToolJet/stargazers)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://github.com/ToolJet/ToolJet)
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<kbd>
|
||||
<img src="https://user-images.githubusercontent.com/7828962/130593222-d1268766-b776-4952-bd36-9caf9810d851.gif" />
|
||||
<img src="https://user-images.githubusercontent.com/7828962/134216201-b2c65c48-547a-4e79-946c-60b7be54b70c.png" />
|
||||
</kbd>
|
||||
</p>
|
||||
|
||||
|
|
@ -29,7 +36,6 @@ ToolJet is an **open-source no-code framework** to build and deploy internal too
|
|||
- Write JS code almost anywhere in the builder
|
||||
- Query editors for all supported data sources
|
||||
- Transform query results using JS code
|
||||
- Import endpoints from OpenAPI specs
|
||||
- All the credentials are securely encrypted using `aes-256-gcm`.
|
||||
- ToolJet acts only as a proxy and doesn't store any data.
|
||||
- Support for OAuth
|
||||
|
|
@ -44,8 +50,6 @@ You can deploy ToolJet on Heroku for free using the one-click-deployment button
|
|||
<a href="https://heroku.com/deploy?template=https://github.com/tooljet/tooljet/tree/main"><img src="https://www.herokucdn.com/deploy/button.svg" /></a>
|
||||
</P>
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
[Building a Github contributor leaderboard using ToolJet](https://blog.tooljet.io/building-a-github-contributor-leaderboard-using-tooljet/)<br>
|
||||
|
|
@ -65,5 +69,10 @@ We use the git-flow branching model. The base branch is develop. If you are look
|
|||
Read our contributing guide (CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to ToolJet. <br>
|
||||
[Contributing Guide](https://docs.tooljet.io/docs/contributing-guide/setup/docker)
|
||||
|
||||
## Contributors
|
||||
<a href="https://github.com/tooljet/tooljet/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=tooljet/tooljet" />
|
||||
</a>
|
||||
|
||||
## Licence
|
||||
ToolJet © 2021, ToolJet Inc - Released under the GNU General Public License v3.0.
|
||||
ToolJet © 2021, ToolJet Solutions Inc - Released under the GNU General Public License v3.0.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ ENV PATH /app/node_modules/.bin:$PATH
|
|||
# Fix for heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=2048"
|
||||
|
||||
|
||||
# install app dependencies
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true
|
||||
},
|
||||
|
||||
parser: 'babel-eslint',
|
||||
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
},
|
||||
ecmaVersion: 12,
|
||||
sourceType: 'module'
|
||||
},
|
||||
extends: [
|
||||
'plugin:react/recommended',
|
||||
'prettier',
|
||||
'airbnb-base/legacy'
|
||||
],
|
||||
plugins: ['html', 'react', 'prettier', 'babel'],
|
||||
rules: {
|
||||
"react/prop-types": 0,
|
||||
"no-underscore-dangle": ["error", { "allow": ["_self"] }],
|
||||
"max-len": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-use-before-define": ["error", { "variables": false, "functions": false }],
|
||||
"no-nested-ternary": 0,
|
||||
"no-loop-func": 0,
|
||||
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect"
|
||||
}
|
||||
}
|
||||
};
|
||||
56
frontend/.eslintrc.json
Normal file
56
frontend/.eslintrc.json
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"amd": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:cypress/recommended"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve"
|
||||
}
|
||||
],
|
||||
"react/prop-types": 0,
|
||||
"react/display-name": "off",
|
||||
"no-unused-vars": [2, { "args": "after-used", "argsIgnorePattern": "reject" }],
|
||||
"react/no-deprecated": 0,
|
||||
"no-prototype-builtins": 0
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
},
|
||||
"import/resolver": "webpack"
|
||||
},
|
||||
"globals": {
|
||||
"fetch": true,
|
||||
"process": true,
|
||||
"module": true,
|
||||
"__dirname": true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
if (config.testingType === 'component') {
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server')
|
||||
const { startDevServer } = require('@cypress/webpack-dev-server');
|
||||
|
||||
// Your project's Webpack configuration
|
||||
const webpackConfig = require('../../webpack.config.js')
|
||||
const webpackConfig = require('../../webpack.config.js');
|
||||
|
||||
on('dev-server:start', (options) =>
|
||||
startDevServer({ options, webpackConfig })
|
||||
)
|
||||
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }));
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
|
|||
2314
frontend/package-lock.json
generated
2314
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,6 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"babel-plugin-import": "^1.13.3",
|
||||
"@babel/core": "^7.4.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/preset-env": "^7.4.3",
|
||||
|
|
@ -18,6 +17,7 @@
|
|||
"array-move": "^3.0.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-console-source": "^2.0.5",
|
||||
"babel-plugin-import": "^1.13.3",
|
||||
"bootstrap": "^4.6.0",
|
||||
"dompurify": "^2.2.7",
|
||||
"draft-js": "^0.11.7",
|
||||
|
|
@ -32,7 +32,6 @@
|
|||
"papaparse": "^5.3.0",
|
||||
"plotly.js-basic-dist-min": "^1.58.4",
|
||||
"query-string": "^6.13.6",
|
||||
"re-resizable": "^6.9.0",
|
||||
"react": "^16.14.0",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-color": "^2.19.3",
|
||||
|
|
@ -48,7 +47,6 @@
|
|||
"react-loading-skeleton": "^2.2.0",
|
||||
"react-plotly.js": "^2.5.1",
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-resizable": "^1.11.1",
|
||||
"react-rnd": "^10.3.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "3.4.3",
|
||||
|
|
@ -71,13 +69,24 @@
|
|||
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"cypress": "^7.4.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.1",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-react": "^7.25.2",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"path": "^0.12.7",
|
||||
"prettier": "^2.3.2",
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --open --port 8082 --host 0.0.0.0",
|
||||
"build": "webpack -p && cp -a ./assets/. ./build/assets/",
|
||||
"lint": "eslint . '**/*.{js,jsx}'",
|
||||
"format": "eslint . --fix '**/*.{js,jsx}'",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import 'react-toastify/dist/ReactToastify.css';
|
|||
import { ManageOrgUsers } from '@/ManageOrgUsers';
|
||||
import { SettingsPage } from '../SettingsPage/SettingsPage';
|
||||
import { OnboardingModal } from '@/Onboarding/OnboardingModal';
|
||||
import {ForgotPassword} from '@/ForgotPassword'
|
||||
import { ForgotPassword } from '@/ForgotPassword';
|
||||
import { ResetPassword } from '@/ResetPassword';
|
||||
import { lt } from 'semver';
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ class App extends React.Component {
|
|||
currentUser: null,
|
||||
fetchedMetadata: false,
|
||||
onboarded: true,
|
||||
darkMode: localStorage.getItem('darkMode') === 'true'
|
||||
darkMode: localStorage.getItem('darkMode') === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -40,56 +40,107 @@ class App extends React.Component {
|
|||
logout = () => {
|
||||
authenticationService.logout();
|
||||
history.push('/login');
|
||||
}
|
||||
};
|
||||
|
||||
switchDarkMode = (newMode) => {
|
||||
this.setState({ darkMode: newMode });
|
||||
localStorage.setItem('darkMode', newMode);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { currentUser, fetchedMetadata, updateAvailable, onboarded, darkMode } = this.state;
|
||||
|
||||
if(currentUser && fetchedMetadata === false) {
|
||||
if (currentUser && fetchedMetadata === false) {
|
||||
tooljetService.fetchMetaData().then((data) => {
|
||||
this.setState({ fetchedMetadata: true, onboarded: data.onboarded });
|
||||
|
||||
if(lt(data.installed_version, data.latest_version) && data.version_ignored === false) {
|
||||
if (lt(data.installed_version, data.latest_version) && data.version_ignored === false) {
|
||||
this.setState({ updateAvailable: true });
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Router history={history}>
|
||||
<div className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`}>
|
||||
{updateAvailable && <div className="alert alert-info alert-dismissible" role="alert">
|
||||
<h3 className="mb-1">Update available</h3>
|
||||
<p>A new version of ToolJet has been released.</p>
|
||||
<div className="btn-list">
|
||||
<a href="https://docs.tooljet.io/docs/setup/updating" target="_blank" className="btn btn-info">Read release notes & update</a>
|
||||
<a onClick={() => { tooljetService.skipVersion(); this.setState({ updateAvailable: false }); }} className="btn">Skip this version</a>
|
||||
{updateAvailable && (
|
||||
<div className="alert alert-info alert-dismissible" role="alert">
|
||||
<h3 className="mb-1">Update available</h3>
|
||||
<p>A new version of ToolJet has been released.</p>
|
||||
<div className="btn-list">
|
||||
<a
|
||||
href="https://docs.tooljet.io/docs/setup/updating"
|
||||
target="_blank"
|
||||
className="btn btn-info"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read release notes & update
|
||||
</a>
|
||||
<a
|
||||
onClick={() => {
|
||||
tooljetService.skipVersion();
|
||||
this.setState({ updateAvailable: false });
|
||||
}}
|
||||
className="btn"
|
||||
>
|
||||
Skip this version
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
)}
|
||||
|
||||
{!onboarded &&
|
||||
<OnboardingModal />
|
||||
}
|
||||
{!onboarded && <OnboardingModal />}
|
||||
|
||||
<ToastContainer />
|
||||
|
||||
<PrivateRoute exact path="/" component={HomePage} switchDarkMode={this.switchDarkMode} darkMode={darkMode}/>
|
||||
<Route path="/login" component={LoginPage}/>
|
||||
<PrivateRoute exact path="/" component={HomePage} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/signup" component={SignupPage} />
|
||||
<Route path = "/forgot-password" component ={ForgotPassword} />
|
||||
<Route path = "/reset-password" component ={ResetPassword} />
|
||||
<Route path="/forgot-password" component={ForgotPassword} />
|
||||
<Route path="/reset-password" component={ResetPassword} />
|
||||
<Route path="/invitations/:token" component={InvitationPage} />
|
||||
<PrivateRoute exact path="/apps/:id" component={Editor} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<PrivateRoute exact path="/applications/:id/versions/:versionId" component={Viewer} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<PrivateRoute exact path="/applications/:slug" component={Viewer} switchDarkMode={this.switchDarkMode} darkMode={darkMode}/>
|
||||
<PrivateRoute exact path="/oauth2/authorize" component={Authorize} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<PrivateRoute exact path="/users" component={ManageOrgUsers} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<PrivateRoute exact path="/settings" component={SettingsPage} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/apps/:id"
|
||||
component={Editor}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:id/versions/:versionId"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:slug"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/oauth2/authorize"
|
||||
component={Authorize}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/users"
|
||||
component={ManageOrgUsers}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/settings"
|
||||
component={SettingsPage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,51 +2,39 @@ export const ActionTypes = [
|
|||
{
|
||||
name: 'Show Alert',
|
||||
id: 'show-alert',
|
||||
options: [
|
||||
{ name: 'message', type: 'text', default: 'Message !' }
|
||||
]
|
||||
options: [{ name: 'message', type: 'text', default: 'Message !' }],
|
||||
},
|
||||
{
|
||||
name: 'Run Query',
|
||||
id: 'run-query',
|
||||
options: [
|
||||
{ queryId: '' }
|
||||
]
|
||||
options: [{ queryId: '' }],
|
||||
},
|
||||
{
|
||||
name: 'Open Webpage',
|
||||
id: 'open-webpage',
|
||||
options: [
|
||||
{ name: 'url', type: 'text', default: 'https://example.com' }
|
||||
]
|
||||
options: [{ name: 'url', type: 'text', default: 'https://example.com' }],
|
||||
},
|
||||
{
|
||||
name: 'Go to app',
|
||||
id: 'go-to-app',
|
||||
options: [
|
||||
{ name: 'app', type: 'text', default: '' },
|
||||
{ name: 'queryParams', type: 'code', default: '[]' }
|
||||
]
|
||||
{ name: 'queryParams', type: 'code', default: '[]' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Show Modal',
|
||||
id: 'show-modal',
|
||||
options: [
|
||||
{ name: 'modal', type: 'text', default: '' }
|
||||
]
|
||||
options: [{ name: 'modal', type: 'text', default: '' }],
|
||||
},
|
||||
{
|
||||
name: 'Close Modal',
|
||||
id: 'close-modal',
|
||||
options: [
|
||||
{ name: 'modal', type: 'text', default: '' }
|
||||
]
|
||||
options: [{ name: 'modal', type: 'text', default: '' }],
|
||||
},
|
||||
{
|
||||
name: 'Copy to clipboard',
|
||||
id: 'copy-to-clipboard',
|
||||
options: [
|
||||
{ name: 'copy-to-clipboard', type: 'text', default: '' }
|
||||
]
|
||||
}
|
||||
options: [{ name: 'copy-to-clipboard', type: 'text', default: '' }],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import { Modal } from './Components/Modal';
|
|||
import { Chart } from './Components/Chart';
|
||||
import { Map } from './Components/Map/Map';
|
||||
import { QrScanner } from './Components/QrScanner/QrScanner';
|
||||
import { ToggleSwitch } from './Components/Toggle'
|
||||
import { RadioButton } from './Components/RadioButton'
|
||||
import { StarRating } from './Components/StarRating'
|
||||
import { ToggleSwitch } from './Components/Toggle';
|
||||
import { RadioButton } from './Components/RadioButton';
|
||||
import { StarRating } from './Components/StarRating';
|
||||
import { renderTooltip } from '../_helpers/appUtils';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import '@/_styles/custom.scss';
|
||||
|
|
@ -45,12 +45,11 @@ const AllComponents = {
|
|||
QrScanner,
|
||||
ToggleSwitch,
|
||||
RadioButton,
|
||||
StarRating
|
||||
StarRating,
|
||||
};
|
||||
|
||||
export const Box = function Box({
|
||||
id,
|
||||
mode,
|
||||
width,
|
||||
height,
|
||||
yellow,
|
||||
|
|
@ -66,7 +65,7 @@ export const Box = function Box({
|
|||
changeCanDrag,
|
||||
containerProps,
|
||||
darkMode,
|
||||
removeComponent
|
||||
removeComponent,
|
||||
}) {
|
||||
const backgroundColor = yellow ? 'yellow' : '';
|
||||
|
||||
|
|
@ -86,49 +85,49 @@ export const Box = function Box({
|
|||
<OverlayTrigger
|
||||
placement="top"
|
||||
delay={{ show: 500, hide: 0 }}
|
||||
trigger={!inCanvas? ['hover', 'focus']: null}
|
||||
overlay={(props) => renderTooltip({props, text: `${component.description}`})}
|
||||
trigger={!inCanvas ? ['hover', 'focus'] : null}
|
||||
overlay={(props) => renderTooltip({ props, text: `${component.description}` })}
|
||||
>
|
||||
<div style={{ ...styles, backgroundColor }} role={preview ? 'BoxPreview' : 'Box'}>
|
||||
{inCanvas ? (
|
||||
<ComponentToRender
|
||||
onComponentClick={onComponentClick}
|
||||
onComponentOptionChanged={onComponentOptionChanged}
|
||||
currentState={currentState}
|
||||
onEvent={onEvent}
|
||||
id={id}
|
||||
paramUpdated={paramUpdated}
|
||||
width={width}
|
||||
changeCanDrag={changeCanDrag}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
height={height}
|
||||
component={component}
|
||||
containerProps={containerProps}
|
||||
darkMode={darkMode}
|
||||
removeComponent={removeComponent}
|
||||
></ComponentToRender>
|
||||
) : (
|
||||
<div className="m-1" style={{ height: '100%' }}>
|
||||
<div
|
||||
className="component-image-holder p-2 d-flex flex-column justify-content-center"
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<center>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
backgroundSize: 'contain',
|
||||
backgroundImage: `url(/assets/images/icons/widgets/${component.name.toLowerCase()}.svg)`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
></div>
|
||||
</center>
|
||||
<span className="component-title">{component.displayName}</span>
|
||||
<div style={{ ...styles, backgroundColor }} role={preview ? 'BoxPreview' : 'Box'}>
|
||||
{inCanvas ? (
|
||||
<ComponentToRender
|
||||
onComponentClick={onComponentClick}
|
||||
onComponentOptionChanged={onComponentOptionChanged}
|
||||
currentState={currentState}
|
||||
onEvent={onEvent}
|
||||
id={id}
|
||||
paramUpdated={paramUpdated}
|
||||
width={width}
|
||||
changeCanDrag={changeCanDrag}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
height={height}
|
||||
component={component}
|
||||
containerProps={containerProps}
|
||||
darkMode={darkMode}
|
||||
removeComponent={removeComponent}
|
||||
></ComponentToRender>
|
||||
) : (
|
||||
<div className="m-1" style={{ height: '100%' }}>
|
||||
<div
|
||||
className="component-image-holder p-2 d-flex flex-column justify-content-center"
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<center>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
backgroundSize: 'contain',
|
||||
backgroundImage: `url(/assets/images/icons/widgets/${component.name.toLowerCase()}.svg)`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
></div>
|
||||
</center>
|
||||
<span className="component-title">{component.displayName}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const BoxDragPreview = memo(function BoxDragPreview({ item, currentLayout
|
|||
[tickTock]
|
||||
);
|
||||
|
||||
const layouts = item.layouts;
|
||||
const layouts = item.layouts;
|
||||
let { width, height } = layouts ? item.layouts[currentLayout] : {};
|
||||
|
||||
if (item.id === undefined) {
|
||||
|
|
@ -21,9 +21,14 @@ export const BoxDragPreview = memo(function BoxDragPreview({ item, currentLayout
|
|||
|
||||
return (
|
||||
<div style={{ height, width, border: 'solid 1px rgb(70, 165, 253)' }}>
|
||||
<div style={{
|
||||
background: '#438fd7', opacity: '0.7', height: '100%', width: '100%'
|
||||
}}></div>
|
||||
<div
|
||||
style={{
|
||||
background: '#438fd7',
|
||||
opacity: '0.7',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ import React, { useState } from 'react';
|
|||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import 'codemirror/theme/duotone-light.css';
|
||||
import { componentTypes } from '../Components/components';
|
||||
import { DataSourceTypes } from '../DataSourceManager/DataSourceTypes';
|
||||
import { DataSourceTypes } from '../DataSourceManager/SourceComponents';
|
||||
import { debounce } from 'lodash';
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
export function CodeBuilder({
|
||||
initialValue, onChange, components, dataQueries
|
||||
}) {
|
||||
export function CodeBuilder({ initialValue, onChange, components, dataQueries }) {
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const [currentValue, setCurrentValue] = useState(initialValue);
|
||||
|
|
@ -130,7 +128,7 @@ export function CodeBuilder({
|
|||
mode: 'javascript',
|
||||
lineWrapping: true,
|
||||
scrollbarStyle: null,
|
||||
lineNumbers: false
|
||||
lineNumbers: false,
|
||||
}}
|
||||
/>
|
||||
{showDropdown && (
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@ export function CodeHinter({
|
|||
});
|
||||
useEffect(() => {
|
||||
setRealState(currentState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentState.components]);
|
||||
|
||||
let suggestions = useMemo(() => {
|
||||
return getSuggestionKeys(realState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [realState.components, realState.queries]);
|
||||
|
||||
function valueChanged(editor, onChange, suggestions, ignoreBraces) {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
import * as React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
import { CodeHinter } from './CodeHinter'
|
||||
import * as React from 'react';
|
||||
import { mount } from '@cypress/react';
|
||||
import { CodeHinter } from './CodeHinter';
|
||||
|
||||
it('Codehinter', () => {
|
||||
mount(<CodeHinter
|
||||
currentState={{
|
||||
queries: {
|
||||
postgres: {
|
||||
data: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
mount(
|
||||
<CodeHinter
|
||||
currentState={{
|
||||
queries: {
|
||||
postgres: {
|
||||
data: [],
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
globals: {},
|
||||
}}
|
||||
initialValue={''}
|
||||
theme="duotone-light"
|
||||
mode="javascript"
|
||||
lineNumbers={true}
|
||||
className="query-hinter"
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
);
|
||||
|
||||
},
|
||||
globals: {
|
||||
|
||||
}
|
||||
}}
|
||||
initialValue={''}
|
||||
theme="duotone-light"
|
||||
mode="javascript"
|
||||
lineNumbers={true}
|
||||
className="query-hinter"
|
||||
onChange={(value) => {}}
|
||||
/>)
|
||||
|
||||
cy.get('.code-hinter')
|
||||
.click()
|
||||
.type('{{')
|
||||
.contains('{{}}') // autocomplete for dynamic variables
|
||||
})
|
||||
|
||||
cy.get('.code-hinter').click().type('{{').contains('{{}}'); // autocomplete for dynamic variables
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ export function getSuggestionKeys(currentState) {
|
|||
_.keys(currentState).forEach((key) => {
|
||||
_.keys(currentState[key]).forEach((key2) => {
|
||||
_.keys(currentState[key][key2]).forEach((key3) => {
|
||||
suggestions.push(`${key}.${key2}.${key3}`)
|
||||
})
|
||||
})
|
||||
suggestions.push(`${key}.${key2}.${key3}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
export function generateHints(word, suggestions) {
|
||||
|
||||
if(word === '') {
|
||||
if (word === '') {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
|
|
@ -38,25 +37,25 @@ export function computeCurrentWord(editor, _cursorPosition, ignoreBraces = false
|
|||
|
||||
export function makeOverlay(style) {
|
||||
return {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
token: function (stream, state) {
|
||||
var ch;
|
||||
if (stream.match("{{")) {
|
||||
if (stream.match('{{')) {
|
||||
while ((ch = stream.next()) != null)
|
||||
if (ch == "}" && stream.next() == "}") {
|
||||
stream.eat("}");
|
||||
if (ch == '}' && stream.next() == '}') {
|
||||
stream.eat('}');
|
||||
return style;
|
||||
}
|
||||
}
|
||||
while (stream.next() != null && !stream.match("{{", false)) { }
|
||||
// eslint-disable-next-line no-empty
|
||||
while (stream.next() != null && !stream.match('{{', false)) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function onBeforeChange(editor, change, ignoreBraces = false) {
|
||||
|
||||
if(!ignoreBraces) {
|
||||
|
||||
if (!ignoreBraces) {
|
||||
const cursor = editor.getCursor();
|
||||
const line = cursor.line;
|
||||
const ch = cursor.ch;
|
||||
|
|
@ -64,33 +63,30 @@ export function onBeforeChange(editor, change, ignoreBraces = false) {
|
|||
const isLastCharacterBrace = value.slice(ch - 1, value.length) === '{';
|
||||
|
||||
if (isLastCharacterBrace && change.origin === '+input' && change.text[0] === '{') {
|
||||
change.text[0] = '{}}'
|
||||
change.text[0] = '{}}';
|
||||
// editor.setCursor({ line: 0, ch: ch })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
export function canShowHint(editor, ignoreBraces = false) {
|
||||
|
||||
if(!editor.hasFocus()) return false;
|
||||
if (!editor.hasFocus()) return false;
|
||||
|
||||
const cursor = editor.getCursor();
|
||||
const line = cursor.line;
|
||||
const ch = cursor.ch;
|
||||
const value = editor.getLine(line);
|
||||
|
||||
if(ignoreBraces && value.length > 0) return true;
|
||||
if (ignoreBraces && value.length > 0) return true;
|
||||
|
||||
return value.slice(ch, ch + 2) === '}}';
|
||||
}
|
||||
|
||||
export function handleChange(editor, onChange, suggestions, ignoreBraces = false) {
|
||||
|
||||
let state = editor.state.matchHighlighter;
|
||||
editor.addOverlay(state.overlay = makeOverlay(state.options.style));
|
||||
editor.addOverlay((state.overlay = makeOverlay(state.options.style)));
|
||||
|
||||
const cursor = editor.getCursor();
|
||||
const currentWord = computeCurrentWord(editor, cursor.ch, ignoreBraces);
|
||||
|
|
@ -103,11 +99,11 @@ export function handleChange(editor, onChange, suggestions, ignoreBraces = false
|
|||
return {
|
||||
from: { line: cursor.line, ch: cursor.ch - currentWord.length },
|
||||
to: cursor,
|
||||
list: hints
|
||||
}
|
||||
}
|
||||
list: hints,
|
||||
};
|
||||
},
|
||||
};
|
||||
if (canShowHint(editor, ignoreBraces)) {
|
||||
editor.showHint(options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
var tinycolor = require("tinycolor2");
|
||||
var tinycolor = require('tinycolor2');
|
||||
|
||||
export const Button = function Button({
|
||||
id, width, height, component, onComponentClick, currentState
|
||||
}) {
|
||||
export const Button = function Button({ id, width, height, component, onComponentClick, currentState }) {
|
||||
console.log('currentState', currentState);
|
||||
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
|
|
@ -15,6 +13,7 @@ export const Button = function Button({
|
|||
const newState = resolveReferences(loadingStateProperty.value, currentState, false);
|
||||
setLoadingState(newState);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentState]);
|
||||
|
||||
const text = component.definition.properties.text.value;
|
||||
|
|
@ -23,12 +22,15 @@ export const Button = function Button({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor,
|
||||
|
|
@ -36,7 +38,7 @@ export const Button = function Button({
|
|||
width,
|
||||
height,
|
||||
display: parsedWidgetVisibility ? '' : 'none',
|
||||
'--tblr-btn-color-darker': tinycolor(backgroundColor).darken(8).toString()
|
||||
'--tblr-btn-color-darker': tinycolor(backgroundColor).darken(8).toString(),
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -44,7 +46,10 @@ export const Button = function Button({
|
|||
disabled={parsedDisabledState}
|
||||
className={`jet-button btn btn-primary p-1 ${loadingState === true ? ' btn-loading' : ''}`}
|
||||
style={computedStyles}
|
||||
onClick={(event) => {event.stopPropagation(); onComponentClick(id, component)}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,9 @@ import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
|||
// Use plotly basic bundle
|
||||
import Plotly from 'plotly.js-basic-dist-min';
|
||||
import createPlotlyComponent from 'react-plotly.js/factory';
|
||||
const Plot = createPlotlyComponent(Plotly)
|
||||
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
export const Chart = function Chart({
|
||||
id, width, height, component, onComponentClick, currentState, darkMode
|
||||
}) {
|
||||
const Plot = createPlotlyComponent(Plotly);
|
||||
|
||||
export const Chart = function Chart({ id, width, height, component, onComponentClick, currentState, darkMode }) {
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
const [chartData, setChartData] = useState([]);
|
||||
|
||||
|
|
@ -19,28 +14,31 @@ export const Chart = function Chart({
|
|||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const loadingStateProperty = component.definition.properties.loadingState;
|
||||
if (loadingStateProperty && currentState) {
|
||||
const newState = resolveReferences(loadingStateProperty.value, currentState, false);
|
||||
setLoadingState(newState);
|
||||
setLoadingState(newState);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentState]);
|
||||
|
||||
const computedStyles = {
|
||||
width,
|
||||
height,
|
||||
display: parsedWidgetVisibility ? '' : 'none',
|
||||
background: darkMode ? '#1f2936' : 'white'
|
||||
background: darkMode ? '#1f2936' : 'white',
|
||||
};
|
||||
|
||||
|
||||
// darkMode ? '#1f2936' : 'white'
|
||||
const dataProperty = component.definition.properties.data;
|
||||
const dataString = dataProperty ? dataProperty.value : [];
|
||||
|
|
@ -66,73 +64,84 @@ export const Chart = function Chart({
|
|||
title: {
|
||||
text: title,
|
||||
font: {
|
||||
color: fontColor
|
||||
}
|
||||
color: fontColor,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
text: title,
|
||||
font: {
|
||||
color: fontColor
|
||||
}
|
||||
color: fontColor,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
showgrid: showGridLines,
|
||||
showline: true,
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
yaxis: {
|
||||
showgrid: showGridLines,
|
||||
showline: true,
|
||||
color: fontColor
|
||||
}
|
||||
}
|
||||
showgrid: showGridLines,
|
||||
showline: true,
|
||||
color: fontColor,
|
||||
},
|
||||
};
|
||||
|
||||
const data = resolveReferences(dataString, currentState, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
let rawData = data || [];
|
||||
if(typeof rawData === 'string') {
|
||||
if (typeof rawData === 'string') {
|
||||
try {
|
||||
rawData = JSON.parse(dataString);
|
||||
} catch (err) { rawData = []; }
|
||||
} catch (err) {
|
||||
rawData = [];
|
||||
}
|
||||
}
|
||||
|
||||
if(!Array.isArray(rawData)) { rawData = []; }
|
||||
if (!Array.isArray(rawData)) {
|
||||
rawData = [];
|
||||
}
|
||||
|
||||
let newData = [];
|
||||
|
||||
if(chartType === 'pie') {
|
||||
newData = [{
|
||||
type: chartType,
|
||||
values: rawData.map((item) => item["value"]),
|
||||
labels: rawData.map((item) => item["label"]),
|
||||
}];
|
||||
if (chartType === 'pie') {
|
||||
newData = [
|
||||
{
|
||||
type: chartType,
|
||||
values: rawData.map((item) => item['value']),
|
||||
labels: rawData.map((item) => item['label']),
|
||||
},
|
||||
];
|
||||
} else {
|
||||
newData = [{
|
||||
type: chartType || 'line',
|
||||
x: rawData.map((item) => item["x"]),
|
||||
y: rawData.map((item) => item["y"]),
|
||||
marker: { color: markerColor }
|
||||
}];
|
||||
newData = [
|
||||
{
|
||||
type: chartType || 'line',
|
||||
x: rawData.map((item) => item['x']),
|
||||
y: rawData.map((item) => item['y']),
|
||||
marker: { color: markerColor },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
setChartData(newData);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, chartType]);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
style={computedStyles}
|
||||
onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
{loadingState === true ?
|
||||
{loadingState === true ? (
|
||||
<div style={{ width: '100%' }} className="p-2">
|
||||
<center>
|
||||
<div className="spinner-border mt-5" role="status"></div>
|
||||
</center>
|
||||
</div>
|
||||
:
|
||||
) : (
|
||||
<Plot
|
||||
data={chartData}
|
||||
layout={layout}
|
||||
|
|
@ -141,7 +150,7 @@ export const Chart = function Chart({
|
|||
// staticPlot: true
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,22 +9,24 @@ export const Checkbox = function Checkbox({
|
|||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged,
|
||||
onEvent
|
||||
onEvent,
|
||||
}) {
|
||||
|
||||
const label = component.definition.properties.label.value;
|
||||
const textColorProperty = component.definition.styles.textColor;
|
||||
const textColor = textColorProperty ? textColorProperty.value : '#000';
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
function toggleValue(e) {
|
||||
const checked = e.target.checked;
|
||||
|
|
@ -37,7 +39,15 @@ export const Checkbox = function Checkbox({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="row" style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="row"
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<label className="my-auto mx-2 form-check form-check-inline">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
@ -46,7 +56,9 @@ export const Checkbox = function Checkbox({
|
|||
toggleValue(e);
|
||||
}}
|
||||
/>
|
||||
<span className="form-check-label" style={{color: textColor}}>{label}</span>
|
||||
<span className="form-check-label" style={{ color: textColor }}>
|
||||
{label}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,43 +10,42 @@ export const Container = function Container({
|
|||
containerProps,
|
||||
width,
|
||||
currentState,
|
||||
removeComponent
|
||||
removeComponent,
|
||||
}) {
|
||||
|
||||
const backgroundColor = component.definition.styles.backgroundColor.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor,
|
||||
width,
|
||||
height,
|
||||
display: parsedWidgetVisibility ? 'flex' : 'none'
|
||||
display: parsedWidgetVisibility ? 'flex' : 'none',
|
||||
};
|
||||
|
||||
const parentRef = useRef(null);
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} className="jet-container" ref={parentRef} onClick={() => containerProps.onComponentClick(id, component)} style={computedStyles}>
|
||||
<SubContainer
|
||||
parent={id}
|
||||
{...containerProps}
|
||||
parentRef={parentRef}
|
||||
removeComponent={removeComponent}
|
||||
/>
|
||||
<SubCustomDragLayer
|
||||
parent={id}
|
||||
parentRef={parentRef}
|
||||
currentLayout={containerProps.currentLayout}
|
||||
/>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="jet-container"
|
||||
ref={parentRef}
|
||||
onClick={() => containerProps.onComponentClick(id, component)}
|
||||
style={computedStyles}
|
||||
>
|
||||
<SubContainer parent={id} {...containerProps} parentRef={parentRef} removeComponent={removeComponent} />
|
||||
<SubCustomDragLayer parent={id} parentRef={parentRef} currentLayout={containerProps.currentLayout} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Datetime from 'react-datetime';
|
||||
import 'react-datetime/css/react-datetime.css';
|
||||
import { resolveReferences, resolveWidgetFieldValue, validateWidget } from '@/_helpers/utils';
|
||||
|
|
@ -10,7 +10,7 @@ export const Datepicker = function Datepicker({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
console.log('currentState', currentState);
|
||||
|
||||
|
|
@ -19,55 +19,89 @@ export const Datepicker = function Datepicker({
|
|||
const enableDateProp = component.definition.properties.enableDate;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
const defaultValue = component.definition.properties?.defaultValue?.value ?? '';
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const enableTime = resolveReferences(enableTimeProp.value, currentState, false);
|
||||
|
||||
let enableDate = true;
|
||||
if (enableDateProp) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
enableDate = resolveReferences(enableDateProp.value, currentState, true);
|
||||
}
|
||||
|
||||
let dateFormat = formatProp
|
||||
|
||||
let dateFormat = formatProp;
|
||||
try {
|
||||
dateFormat = resolveReferences(formatProp, currentState);
|
||||
} catch (err) { console.log(err); }
|
||||
|
||||
function onDateChange(event) {
|
||||
onComponentOptionChanged(component, 'value', event.format(dateFormat.value));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const value = currentState?.components[component?.name]?.value;
|
||||
function onDateChange(event) {
|
||||
const value = event._isAMomentObject ? event.format(dateFormat.value) : event;
|
||||
setDateText(value);
|
||||
onComponentOptionChanged(component, 'value', value);
|
||||
}
|
||||
|
||||
let value = defaultValue;
|
||||
if (value && currentState) value = resolveReferences(value, currentState, '');
|
||||
|
||||
const [dateText, setDateText] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
setDateText(value);
|
||||
onComponentOptionChanged(component, 'value', value);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
const validationData = validateWidget({
|
||||
validationObject: component.definition.validation,
|
||||
widgetValue: value,
|
||||
currentState
|
||||
})
|
||||
currentState,
|
||||
});
|
||||
|
||||
const { isValid, validationError } = validationData;
|
||||
|
||||
const currentValidState = currentState?.components[component?.name]?.isValid;
|
||||
|
||||
if(currentValidState !== isValid) {
|
||||
if (currentValidState !== isValid) {
|
||||
onComponentOptionChanged(component, 'isValid', isValid);
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} style={{ width, height, display:parsedWidgetVisibility ? '' : 'none'}} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<Datetime
|
||||
onChange={onDateChange}
|
||||
timeFormat={enableTime}
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<Datetime
|
||||
onChange={onDateChange}
|
||||
timeFormat={enableTime}
|
||||
closeOnSelect={true}
|
||||
dateFormat={dateFormat.value}
|
||||
dateFormat={dateFormat.value}
|
||||
value={dateText}
|
||||
renderInput={(props) => {
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
value={dateText}
|
||||
className={`form-control ${!isValid ? 'is-invalid' : ''} validation-without-icon`}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className={`invalid-feedback ${isValid ? '' : 'd-flex'}`}>{validationError}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const DaterangePicker = function DaterangePicker({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
console.log('currentState', currentState);
|
||||
|
||||
|
|
@ -21,18 +21,20 @@ export const DaterangePicker = function DaterangePicker({
|
|||
const formatProp = component.definition.properties.format;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
const [focusedInput, setFocusedInput] = useState(null);
|
||||
const [startDate, setStartDate] = useState(startDateProp ? startDateProp.value : null);
|
||||
const [endDate, setEndDate] = useState(endDateProp ? endDateProp.value : null);
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
function onDateChange(dates) {
|
||||
const start = dates.startDate;
|
||||
|
|
@ -55,7 +57,13 @@ export const DaterangePicker = function DaterangePicker({
|
|||
}
|
||||
|
||||
return (
|
||||
<div style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<div
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<DateRangePicker
|
||||
disabled={parsedDisabledState}
|
||||
startDate={startDate}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable react/no-string-refs */
|
||||
import React from 'react';
|
||||
import { Editor, EditorState, RichUtils, getDefaultKeyBinding } from 'draft-js';
|
||||
import 'draft-js/dist/Draft.css';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const DropDown = function DropDown({
|
|||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged,
|
||||
onEvent
|
||||
onEvent,
|
||||
}) {
|
||||
console.log('currentState', currentState);
|
||||
|
||||
|
|
@ -20,25 +20,32 @@ export const DropDown = function DropDown({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedValues = values;
|
||||
|
||||
try {
|
||||
parsedValues = resolveReferences(values, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let parsedDisplayValues = displayValues;
|
||||
|
||||
try {
|
||||
parsedDisplayValues = resolveReferences(displayValues, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let selectOptions = [];
|
||||
|
||||
|
|
@ -46,14 +53,15 @@ export const DropDown = function DropDown({
|
|||
selectOptions = [
|
||||
...parsedValues.map((value, index) => {
|
||||
return { name: parsedDisplayValues[index], value: value };
|
||||
})
|
||||
}),
|
||||
];
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const currentValueProperty = component.definition.properties.value;
|
||||
const value = currentValueProperty ? currentValueProperty.value : '';
|
||||
const [currentValue, setCurrentValue] = useState('');
|
||||
|
||||
|
||||
let newValue = value;
|
||||
if (currentValueProperty && currentState) {
|
||||
|
|
@ -63,14 +71,14 @@ export const DropDown = function DropDown({
|
|||
const validationData = validateWidget({
|
||||
validationObject: component.definition.validation,
|
||||
widgetValue: currentValue,
|
||||
currentState
|
||||
})
|
||||
currentState,
|
||||
});
|
||||
|
||||
const { isValid, validationError } = validationData;
|
||||
|
||||
const currentValidState = currentState?.components[component?.name]?.isValid;
|
||||
|
||||
if(currentValidState !== isValid) {
|
||||
if (currentValidState !== isValid) {
|
||||
onComponentOptionChanged(component, 'isValid', isValid);
|
||||
}
|
||||
|
||||
|
|
@ -79,13 +87,23 @@ export const DropDown = function DropDown({
|
|||
}, [newValue]);
|
||||
|
||||
useEffect(() => {
|
||||
onComponentOptionChanged(component, 'value', currentValue).then(() => onEvent('onSelect', { component }) );
|
||||
onComponentOptionChanged(component, 'value', currentValue).then(() => onEvent('onSelect', { component }));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentValue]);
|
||||
|
||||
return (
|
||||
<div className="dropdown-widget row g-0" style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<div
|
||||
className="dropdown-widget row g-0"
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<div className="col-auto">
|
||||
<label style={{marginRight: label !== '' ? '1rem' : '0.001rem'}} className="form-label py-1">{label}</label>
|
||||
<label style={{ marginRight: label !== '' ? '1rem' : '0.001rem' }} className="form-label py-1">
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
<div className="col px-0">
|
||||
<SelectSearch
|
||||
|
|
|
|||
|
|
@ -2,33 +2,39 @@ import React from 'react';
|
|||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import LazyLoad from 'react-lazyload';
|
||||
|
||||
export const Image = function Image({
|
||||
id, width, height, component, onComponentClick, currentState
|
||||
}) {
|
||||
export const Image = function Image({ id, width, height, component, onComponentClick, currentState }) {
|
||||
const source = component.definition.properties.source.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let data = resolveReferences(source, currentState, null);
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
if (data === '') data = null;
|
||||
|
||||
function Placeholder() {
|
||||
return (
|
||||
<div className="skeleton-image" style={{ objectFit: 'contain', width, height }}></div>
|
||||
);
|
||||
return <div className="skeleton-image" style={{ objectFit: 'contain', width, height }}></div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} style={{display:parsedWidgetVisibility ? '' : 'none'}} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<LazyLoad width={width} height={height} placeholder={<Placeholder/>} debounce={500}>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
style={{ display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<LazyLoad width={width} height={height} placeholder={<Placeholder />} debounce={500}>
|
||||
<img style={{ objectFit: 'contain' }} src={data} width={width} height={height} />
|
||||
</LazyLoad>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { GoogleMap, LoadScript } from '@react-google-maps/api';
|
||||
import { Marker } from '@react-google-maps/api';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { GoogleMap, LoadScript, Marker, Autocomplete } from '@react-google-maps/api';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import { Autocomplete } from '@react-google-maps/api';
|
||||
import { darkModeStyles } from './styles';
|
||||
|
||||
export const Map = function Map({
|
||||
|
|
@ -88,6 +86,7 @@ export const Map = function Map({
|
|||
]).then(() => onEvent('onBoundsChange', { component }));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const onLoad = useCallback(function onLoad(mapInstance) {
|
||||
setGmap(mapInstance);
|
||||
onComponentOptionsChanged(component, [['center', mapInstance.center?.toJSON()]]);
|
||||
|
|
@ -151,7 +150,7 @@ export const Map = function Map({
|
|||
{Array.isArray(markers) && (
|
||||
<>
|
||||
{markers.map((marker, index) => (
|
||||
<Marker position={marker} label={marker.label} onClick={(e) => handleMarkerClick(index)} />
|
||||
<Marker key={index} position={marker} label={marker.label} onClick={() => handleMarkerClick(index)} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,137 +1,137 @@
|
|||
export const darkModeStyles = [
|
||||
{
|
||||
"featureType": "all",
|
||||
"elementType": "labels.text.fill",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#ffffff"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "all",
|
||||
"elementType": "labels.text.stroke",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#000000"
|
||||
},
|
||||
{
|
||||
"lightness": 13
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "administrative",
|
||||
"elementType": "geometry.fill",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "administrative",
|
||||
"elementType": "geometry.stroke",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#144b53"
|
||||
},
|
||||
{
|
||||
"lightness": 14
|
||||
},
|
||||
{
|
||||
"weight": 1.4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "landscape",
|
||||
"elementType": "all",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#08304b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "poi",
|
||||
"elementType": "geometry",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#0c4152"
|
||||
},
|
||||
{
|
||||
"lightness": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "road.highway",
|
||||
"elementType": "geometry.fill",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "road.highway",
|
||||
"elementType": "geometry.stroke",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#0b434f"
|
||||
},
|
||||
{
|
||||
"lightness": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "road.arterial",
|
||||
"elementType": "geometry.fill",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "road.arterial",
|
||||
"elementType": "geometry.stroke",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#0b3d51"
|
||||
},
|
||||
{
|
||||
"lightness": 16
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "road.local",
|
||||
"elementType": "geometry",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "transit",
|
||||
"elementType": "all",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#146474"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"featureType": "water",
|
||||
"elementType": "all",
|
||||
"stylers": [
|
||||
{
|
||||
"color": "#021019"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
{
|
||||
featureType: 'all',
|
||||
elementType: 'labels.text.fill',
|
||||
stylers: [
|
||||
{
|
||||
color: '#ffffff',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'all',
|
||||
elementType: 'labels.text.stroke',
|
||||
stylers: [
|
||||
{
|
||||
color: '#000000',
|
||||
},
|
||||
{
|
||||
lightness: 13,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'administrative',
|
||||
elementType: 'geometry.fill',
|
||||
stylers: [
|
||||
{
|
||||
color: '#000000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'administrative',
|
||||
elementType: 'geometry.stroke',
|
||||
stylers: [
|
||||
{
|
||||
color: '#144b53',
|
||||
},
|
||||
{
|
||||
lightness: 14,
|
||||
},
|
||||
{
|
||||
weight: 1.4,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'landscape',
|
||||
elementType: 'all',
|
||||
stylers: [
|
||||
{
|
||||
color: '#08304b',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'poi',
|
||||
elementType: 'geometry',
|
||||
stylers: [
|
||||
{
|
||||
color: '#0c4152',
|
||||
},
|
||||
{
|
||||
lightness: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'road.highway',
|
||||
elementType: 'geometry.fill',
|
||||
stylers: [
|
||||
{
|
||||
color: '#000000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'road.highway',
|
||||
elementType: 'geometry.stroke',
|
||||
stylers: [
|
||||
{
|
||||
color: '#0b434f',
|
||||
},
|
||||
{
|
||||
lightness: 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'road.arterial',
|
||||
elementType: 'geometry.fill',
|
||||
stylers: [
|
||||
{
|
||||
color: '#000000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'road.arterial',
|
||||
elementType: 'geometry.stroke',
|
||||
stylers: [
|
||||
{
|
||||
color: '#0b3d51',
|
||||
},
|
||||
{
|
||||
lightness: 16,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'road.local',
|
||||
elementType: 'geometry',
|
||||
stylers: [
|
||||
{
|
||||
color: '#000000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'transit',
|
||||
elementType: 'all',
|
||||
stylers: [
|
||||
{
|
||||
color: '#146474',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
featureType: 'water',
|
||||
elementType: 'all',
|
||||
stylers: [
|
||||
{
|
||||
color: '#021019',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -4,16 +4,9 @@ import Button from 'react-bootstrap/Button';
|
|||
import { SubCustomDragLayer } from '../SubCustomDragLayer';
|
||||
import { SubContainer } from '../SubContainer';
|
||||
import { ConfigHandle } from '../ConfigHandle';
|
||||
import { resolveWidgetFieldValue, resolveReferences } from '../../_helpers/utils';
|
||||
import { resolveWidgetFieldValue } from '../../_helpers/utils';
|
||||
|
||||
export const Modal = function Modal({
|
||||
id,
|
||||
component,
|
||||
height,
|
||||
mode,
|
||||
containerProps,
|
||||
currentState
|
||||
}) {
|
||||
export const Modal = function Modal({ id, component, height, containerProps, currentState }) {
|
||||
const [show, showModal] = useState(false);
|
||||
const parentRef = useRef(null);
|
||||
|
||||
|
|
@ -25,12 +18,14 @@ export const Modal = function Modal({
|
|||
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
useEffect(() => {
|
||||
const componentState = containerProps.currentState.components[component.name];
|
||||
const canShowModel = componentState ? componentState.show : false;
|
||||
showModal(canShowModel);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [containerProps.currentState.components[component.name]]);
|
||||
|
||||
function hideModal() {
|
||||
|
|
@ -51,17 +46,11 @@ export const Modal = function Modal({
|
|||
animation={false}
|
||||
onEscapeKeyDown={() => showModal(false)}
|
||||
>
|
||||
{containerProps.mode === 'edit' &&
|
||||
<ConfigHandle
|
||||
id={id}
|
||||
component={component}
|
||||
configHandleClicked={containerProps.onComponentClick}
|
||||
/>
|
||||
}
|
||||
{containerProps.mode === 'edit' && (
|
||||
<ConfigHandle id={id} component={component} configHandleClicked={containerProps.onComponentClick} />
|
||||
)}
|
||||
<BootstrapModal.Header>
|
||||
<BootstrapModal.Title>
|
||||
{title}
|
||||
</BootstrapModal.Title>
|
||||
<BootstrapModal.Title>{title}</BootstrapModal.Title>
|
||||
<div>
|
||||
<Button variant="light" size="sm" onClick={hideModal}>
|
||||
x
|
||||
|
|
@ -70,17 +59,13 @@ export const Modal = function Modal({
|
|||
</BootstrapModal.Header>
|
||||
|
||||
<BootstrapModal.Body style={{ height }} ref={parentRef}>
|
||||
<SubContainer
|
||||
parent={id}
|
||||
{...containerProps}
|
||||
parentRef={parentRef}
|
||||
/>
|
||||
<SubCustomDragLayer
|
||||
snapToGrid={true}
|
||||
parentRef={parentRef}
|
||||
parent={id}
|
||||
currentLayout={containerProps.currentLayout}
|
||||
/>
|
||||
<SubContainer parent={id} {...containerProps} parentRef={parentRef} />
|
||||
<SubCustomDragLayer
|
||||
snapToGrid={true}
|
||||
parentRef={parentRef}
|
||||
parent={id}
|
||||
currentLayout={containerProps.currentLayout}
|
||||
/>
|
||||
</BootstrapModal.Body>
|
||||
</BootstrapModal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export const Multiselect = function Multiselect({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
console.log('currentState', currentState);
|
||||
|
||||
|
|
@ -19,7 +19,8 @@ export const Multiselect = function Multiselect({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
const parsedValues = JSON.parse(values);
|
||||
const parsedDisplayValues = JSON.parse(displayValues);
|
||||
|
|
@ -27,7 +28,7 @@ export const Multiselect = function Multiselect({
|
|||
const selectOptions = [
|
||||
...parsedValues.map((value, index) => {
|
||||
return { name: parsedDisplayValues[index], value: value };
|
||||
})
|
||||
}),
|
||||
];
|
||||
|
||||
const currentValueProperty = component.definition.properties.values;
|
||||
|
|
@ -40,19 +41,30 @@ export const Multiselect = function Multiselect({
|
|||
}
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentValue(newValue);
|
||||
}, [newValue]);
|
||||
|
||||
return (
|
||||
<div className="multiselect-widget row g-0" style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<div
|
||||
className="multiselect-widget row g-0"
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<div className="col-auto">
|
||||
<label style={{marginRight: '1rem'}} className="form-label py-1">{label}</label>
|
||||
<label style={{ marginRight: '1rem' }} className="form-label py-1">
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
<div className="col px-0">
|
||||
<SelectSearch
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ export const NumberInput = function NumberInput({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
|
||||
const value = component.definition.properties.value ? component.definition.properties.value.value : '';
|
||||
const [number, setNumber] = useState(value);
|
||||
|
||||
|
|
@ -23,24 +22,31 @@ export const NumberInput = function NumberInput({
|
|||
useEffect(() => {
|
||||
setNumber(parseInt(newNumber));
|
||||
onComponentOptionChanged(component, 'value', parseInt(newNumber));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newNumber]);
|
||||
|
||||
const placeholder = component.definition.properties.placeholder.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
disabled={parsedDisabledState}
|
||||
onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setNumber(parseInt(e.target.value));
|
||||
onComponentOptionChanged(component, 'value', parseInt(e.target.value));
|
||||
|
|
@ -48,7 +54,7 @@ export const NumberInput = function NumberInput({
|
|||
type="number"
|
||||
className="form-control"
|
||||
placeholder={placeholder}
|
||||
style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }}
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
value={number}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,34 +1,41 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export default function ErrorModal() {
|
||||
|
||||
const [show, setShow] = React.useState(true)
|
||||
const [show, setShow] = React.useState(true);
|
||||
|
||||
const close = () => {
|
||||
setShow(false)
|
||||
}
|
||||
|
||||
return(
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
show ?
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">QR Scanner is not working</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onClick={close}></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Please make sure a camera is available on your device. Try closing your browser and opening it again, if it doesn't work, please contact support.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-bs-dismiss="modal" onClick={close}>Close</button>
|
||||
</div>
|
||||
{show ? (
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">QR Scanner is not working</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={close}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
Please make sure a camera is available on your device. Try closing your browser and opening it again, if
|
||||
it doesn't work, please contact support.
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn" data-bs-dismiss="modal" onClick={close}>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
''
|
||||
}
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ import QrReader from 'react-qr-reader';
|
|||
import ErrorModal from './ErrorModal';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
|
||||
export const QrScanner = function QrScanner({
|
||||
component, onEvent, onComponentOptionChanged, currentState
|
||||
}) {
|
||||
|
||||
export const QrScanner = function QrScanner({ component, onEvent, onComponentOptionChanged, currentState }) {
|
||||
const handleError = async (errorMessage) => {
|
||||
console.log(errorMessage);
|
||||
setErrorOccured(true);
|
||||
|
|
@ -16,7 +13,7 @@ export const QrScanner = function QrScanner({
|
|||
if (data != null) {
|
||||
onEvent('onDetect', { component, data: data });
|
||||
onComponentOptionChanged(component, 'lastDetectedValue', data);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let [errorOccured, setErrorOccured] = useState(false);
|
||||
|
|
@ -24,25 +21,20 @@ export const QrScanner = function QrScanner({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} style={{display:parsedWidgetVisibility ? '' : 'none'}}>
|
||||
{
|
||||
errorOccured ?
|
||||
<ErrorModal />
|
||||
:
|
||||
<QrReader
|
||||
onError={handleError}
|
||||
onScan={handleScan}
|
||||
/>
|
||||
}
|
||||
<div data-disabled={parsedDisabledState} style={{ display: parsedWidgetVisibility ? '' : 'none' }}>
|
||||
{errorOccured ? <ErrorModal /> : <QrReader onError={handleError} onScan={handleScan} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
|
||||
|
||||
export const RadioButton = function RadioButton({
|
||||
id,
|
||||
width,
|
||||
|
|
@ -10,9 +9,8 @@ export const RadioButton = function RadioButton({
|
|||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged,
|
||||
onEvent
|
||||
onEvent,
|
||||
}) {
|
||||
|
||||
const label = component.definition.properties.label.value;
|
||||
const textColorProperty = component.definition.styles.textColor;
|
||||
const textColor = textColorProperty ? textColorProperty.value : '#000';
|
||||
|
|
@ -23,25 +21,32 @@ export const RadioButton = function RadioButton({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedValues = values;
|
||||
|
||||
try {
|
||||
parsedValues = resolveReferences(values, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let parsedDisplayValues = displayValues;
|
||||
|
||||
try {
|
||||
parsedDisplayValues = resolveReferences(displayValues, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let parsedDefaultValue = defaultValue;
|
||||
|
||||
try {
|
||||
parsedDefaultValue = resolveReferences(defaultValue, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let selectOptions = [];
|
||||
|
||||
|
|
@ -49,33 +54,54 @@ export const RadioButton = function RadioButton({
|
|||
selectOptions = [
|
||||
...parsedValues.map((value, index) => {
|
||||
return { name: parsedDisplayValues[index], value: value };
|
||||
})
|
||||
}),
|
||||
];
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
function onSelect(event) {
|
||||
const selection = event.target.value
|
||||
const selection = event.target.value;
|
||||
onComponentOptionChanged(component, 'value', selection);
|
||||
if (selection) {
|
||||
onEvent('onSelectionChange', { component });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} className="row" style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<span className="form-check-label form-check-label col-auto py-1" style={{color: textColor}}>{label}</span>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="row"
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<span className="form-check-label form-check-label col-auto py-1" style={{ color: textColor }}>
|
||||
{label}
|
||||
</span>
|
||||
<div className="col py-1" onChange={(e) => onSelect(e)}>
|
||||
{selectOptions.map((option, index) => (
|
||||
<label key={index} class="form-check form-check-inline">
|
||||
<input class="form-check-input" defaultChecked={parsedDefaultValue === option.value} type="radio" value={option.value} name="radio-options" />
|
||||
<span className="form-check-label" style={{color: textColor}}>{option.name}</span>
|
||||
<label key={index} className="form-check form-check-inline">
|
||||
<input
|
||||
className="form-check-input"
|
||||
defaultChecked={parsedDefaultValue === option.value}
|
||||
type="radio"
|
||||
value={option.value}
|
||||
name="radio-options"
|
||||
/>
|
||||
<span className="form-check-label" style={{ color: textColor }}>
|
||||
{option.name}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Editor, EditorState } from "draft-js";
|
||||
import "draft-js/dist/Draft.css";
|
||||
import 'draft-js/dist/Draft.css';
|
||||
import { DraftEditor } from './DraftEditor';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
|
||||
|
|
@ -11,33 +10,37 @@ export const RichTextEditor = function RichTextEditor({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
|
||||
const placeholder = component.definition.properties.placeholder.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
function handleChange(html) {
|
||||
onComponentOptionChanged(component, 'value', html);
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} style={{ width: `${width}px`, height: `${height}px`, display:parsedWidgetVisibility ? '' : 'none' }} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<DraftEditor
|
||||
handleChange={handleChange}
|
||||
height={height}
|
||||
width={width}
|
||||
placeholder={placeholder}
|
||||
></DraftEditor>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
style={{ width: `${width}px`, height: `${height}px`, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
<DraftEditor handleChange={handleChange} height={height} width={width} placeholder={placeholder}></DraftEditor>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,47 +1,44 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
export default ({ fill = '#ffb400'}) => {
|
||||
export default ({ fill = '#ffb400' }) => {
|
||||
return (
|
||||
<svg height="20" width="20" fill={fill} version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 630 630" xmlSpace="preserve">
|
||||
<path d="M610.089,233.999c-4.591-14.132-16.807-24.432-31.512-26.567l-164.157-23.856L341.006,34.827
|
||||
<svg
|
||||
height="20"
|
||||
width="20"
|
||||
fill={fill}
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 630 630"
|
||||
xmlSpace="preserve"
|
||||
>
|
||||
<path
|
||||
d="M610.089,233.999c-4.591-14.132-16.807-24.432-31.512-26.567l-164.157-23.856L341.006,34.827
|
||||
c-6.577-13.325-20.147-21.76-35.005-21.76c-14.86,0-28.43,8.435-35.005,21.76l-73.416,148.749L33.424,207.432
|
||||
c-14.705,2.136-26.921,12.436-31.512,26.567s-0.762,29.645,9.879,40.017l118.786,115.787l-28.043,163.492
|
||||
c-2.513,14.646,3.507,29.446,15.529,38.18c12.021,8.734,27.958,9.885,41.112,2.972l146.825-77.192l146.825,77.192
|
||||
c5.713,3.004,11.949,4.485,18.162,4.485c8.092,0,16.149-2.515,22.95-7.455c12.021-8.734,18.041-23.536,15.529-38.18
|
||||
l-28.041-163.494l118.784-115.789C610.851,263.643,614.68,248.131,610.089,233.999z M412.235,348.222
|
||||
c-9.202,8.967-13.399,21.889-11.227,34.552l18.139,105.762l-94.979-49.934c-3.162-1.664-6.498-2.847-9.909-3.584V157.04
|
||||
l39.232,79.493c5.686,11.522,16.676,19.508,29.391,21.354l106.192,15.431L412.235,348.222z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
l39.232,79.493c5.686,11.522,16.676,19.508,29.391,21.354l106.192,15.431L412.235,348.222z"
|
||||
/>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,44 +1,41 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
export default ({ fill = '#ffb400'}) => {
|
||||
export default ({ fill = '#ffb400' }) => {
|
||||
return (
|
||||
<svg height="20" width="20" fill={fill} version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 500 500" xmlSpace="preserve">
|
||||
<path d="M471.563,173.778l-145.5-20.8l-64.4-132c-8-15.4-30-12.2-35.3,0l-64.4,132l-145.6,20.8c-16.4,1-21.6,20.9-10.4,33.2
|
||||
<svg
|
||||
height="20"
|
||||
width="20"
|
||||
fill={fill}
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 500 500"
|
||||
xmlSpace="preserve"
|
||||
>
|
||||
<path
|
||||
d="M471.563,173.778l-145.5-20.8l-64.4-132c-8-15.4-30-12.2-35.3,0l-64.4,132l-145.6,20.8c-16.4,1-21.6,20.9-10.4,33.2
|
||||
l105,102.9l-25,144.5c-2.9,17.8,16.7,27.8,28.1,20.8l129.9-68.6l129.9,67.6c13.6,7,29.8-2.8,28.1-19.7l-25-144.6l105-102.9
|
||||
C494.663,193.478,485.563,175.478,471.563,173.778z M342.663,288.078c-4.2,5.2-6.2,11.4-5.2,17.7l19.7,116.4l-103.9-55.1
|
||||
c-6.7-2.8-13-2.8-18.7,0l-103.9,55.1l19.7-116.4c1-7.3-1-13.5-5.2-17.7l-84.1-82.1l116.4-16.6c6.2-1,11.4-4.2,14.6-10.4l52-105
|
||||
l52,105c3.1,5.2,8.3,9.4,14.6,10.4l116.2,16.6L342.663,288.078z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
l52,105c3.1,5.2,8.3,9.4,14.6,10.4l116.2,16.6L342.663,288.078z"
|
||||
/>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,44 +1,41 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
export default ({ fill = '#ffb400'}) => {
|
||||
export default ({ fill = '#ffb400' }) => {
|
||||
return (
|
||||
<svg version="1.1" fill={fill} height="20" width="20" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 60 60" xmlSpace="preserve">
|
||||
<path d="M55.818,21.578c-0.118-0.362-0.431-0.626-0.808-0.681L36.92,18.268L28.83,1.876c-0.168-0.342-0.516-0.558-0.896-0.558
|
||||
<svg
|
||||
version="1.1"
|
||||
fill={fill}
|
||||
height="20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 60 60"
|
||||
xmlSpace="preserve"
|
||||
>
|
||||
<path
|
||||
d="M55.818,21.578c-0.118-0.362-0.431-0.626-0.808-0.681L36.92,18.268L28.83,1.876c-0.168-0.342-0.516-0.558-0.896-0.558
|
||||
s-0.729,0.216-0.896,0.558l-8.091,16.393l-18.09,2.629c-0.377,0.055-0.689,0.318-0.808,0.681c-0.117,0.361-0.02,0.759,0.253,1.024
|
||||
l13.091,12.76l-3.091,18.018c-0.064,0.375,0.09,0.754,0.397,0.978c0.309,0.226,0.718,0.255,1.053,0.076l16.182-8.506l16.18,8.506
|
||||
c0.146,0.077,0.307,0.115,0.466,0.115c0.207,0,0.413-0.064,0.588-0.191c0.308-0.224,0.462-0.603,0.397-0.978l-3.09-18.017
|
||||
l13.091-12.761C55.838,22.336,55.936,21.939,55.818,21.578z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
l13.091-12.761C55.838,22.336,55.936,21.939,55.818,21.578z"
|
||||
/>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
<g></g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,13 +23,16 @@ export const StarRating = function StarRating({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const tooltips = component.definition.properties.tooltips.value ?? [];
|
||||
const _tooltips = resolveReferences(tooltips, currentState, []) ?? [];
|
||||
|
|
@ -54,12 +57,14 @@ export const StarRating = function StarRating({
|
|||
React.useEffect(() => {
|
||||
setRatingIndex(defaultSelected - 1);
|
||||
onComponentOptionChanged(component, 'value', defaultSelected);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultSelected]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
onComponentOptionChanged(component, 'value', defaultSelected);
|
||||
}, 1000)
|
||||
}, 1000);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
function handleClick(idx) {
|
||||
|
|
@ -84,7 +89,15 @@ export const StarRating = function StarRating({
|
|||
};
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} className="star-rating" onClick={event => {event.stopPropagation(); onComponentClick(id, component)}} style={{display:parsedWidgetVisibility ? '' : 'none'}}>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="star-rating"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
style={{ display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
>
|
||||
{/* TODO: Add label color defination property instead of hardcoded color*/}
|
||||
<span className="label form-check-label form-check-label col-auto" style={{ color: '#000' }}>
|
||||
{label}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const Star = ({
|
|||
React.useEffect(() => {
|
||||
setIcon(isHalfStar ? halfStar : star);
|
||||
setOutlineIcon(isHalfStar ? halfStar : starOutline);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [color]);
|
||||
|
||||
const ref = React.useRef(null);
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
import React from 'react';
|
||||
import SelectSearch from 'react-select-search';
|
||||
|
||||
export const CustomSelect = ({ options, value, multiple, disabled, onChange }) => {
|
||||
|
||||
function renderValue(valueProps) {
|
||||
if(valueProps) {
|
||||
return valueProps.value.split(', ').map((value) => <span {...valueProps} className="badge bg-blue-lt p-2 mx-1">{value}</span>);
|
||||
}
|
||||
export const CustomSelect = ({ options, value, multiple, onChange }) => {
|
||||
function renderValue(valueProps) {
|
||||
if (valueProps) {
|
||||
return valueProps.value.split(', ').map((value, index) => (
|
||||
<span key={index} {...valueProps} className="badge bg-blue-lt p-2 mx-1">
|
||||
{value}
|
||||
</span>
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="custom-select">
|
||||
<SelectSearch
|
||||
options={options}
|
||||
printOptions="on-focus"
|
||||
value={value}
|
||||
renderValue={renderValue}
|
||||
search={false}
|
||||
onChange={onChange}
|
||||
multiple={multiple}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div className="custom-select">
|
||||
<SelectSearch
|
||||
options={options}
|
||||
printOptions="on-focus"
|
||||
value={value}
|
||||
renderValue={renderValue}
|
||||
search={false}
|
||||
onChange={onChange}
|
||||
multiple={multiple}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,80 +2,72 @@ import React from 'react';
|
|||
import Datetime from 'react-datetime';
|
||||
import 'react-datetime/css/react-datetime.css';
|
||||
import '@/_styles/custom.scss';
|
||||
import moment from 'moment'
|
||||
import moment from 'moment';
|
||||
|
||||
export const Datepicker = function Datepicker({value, onChange, readOnly, isTimeChecked, dateFormat}) {
|
||||
|
||||
const [date, setDate] = React.useState(value)
|
||||
export const Datepicker = function Datepicker({ value, onChange, readOnly, isTimeChecked, dateFormat }) {
|
||||
const [date, setDate] = React.useState(value);
|
||||
|
||||
const dateChange = (e) => {
|
||||
if(isTimeChecked) {
|
||||
setDate(e.format(`${dateFormat} LT`))
|
||||
} else {
|
||||
setDate(e.format(dateFormat))
|
||||
}
|
||||
const dateChange = (e) => {
|
||||
if (isTimeChecked) {
|
||||
setDate(e.format(`${dateFormat} LT`));
|
||||
} else {
|
||||
setDate(e.format(dateFormat));
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isTimeChecked) {
|
||||
setDate(moment(value, 'DD-MM-YYYY').format(dateFormat));
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const _date = isTimeChecked ? moment(value, `${dateFormat} LT`) : moment(value, dateFormat)
|
||||
if (isTimeChecked) {
|
||||
setDate(moment(value, 'DD-MM-YYYY LT').format(`${dateFormat} LT`));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTimeChecked, readOnly, dateFormat]);
|
||||
|
||||
if(!isTimeChecked) {
|
||||
setDate(moment(value, 'DD-MM-YYYY').format(dateFormat))
|
||||
}
|
||||
let inputProps = {
|
||||
disabled: !readOnly,
|
||||
};
|
||||
|
||||
if(isTimeChecked) {
|
||||
setDate(moment(value, 'DD-MM-YYYY LT' ).format(`${dateFormat} LT`))
|
||||
}
|
||||
|
||||
},[isTimeChecked, readOnly, dateFormat])
|
||||
const [isDatepickerOpen, setIsDatepickerOpen] = React.useState(false);
|
||||
|
||||
const onDatepickerClose = () => {
|
||||
onChange(date);
|
||||
setIsDatepickerOpen((prev) => !prev);
|
||||
};
|
||||
|
||||
let inputProps = {
|
||||
disabled: !readOnly,
|
||||
React.useEffect(() => {
|
||||
const myElement = document.querySelector('.cell-type-datepicker');
|
||||
|
||||
myElement.parentNode.style.position = 'absolute';
|
||||
myElement.style.position = 'relative';
|
||||
myElement.style.marginTop = '2px';
|
||||
myElement.style.left = '50%';
|
||||
myElement.style.width = '250px';
|
||||
myElement.style.transform = 'translate(-50%, -25%)';
|
||||
|
||||
return () => {
|
||||
myElement.parentNode.style.position = '';
|
||||
myElement.style.position = '';
|
||||
myElement.style.marginTop = '';
|
||||
myElement.style.left = '';
|
||||
myElement.style.width = '';
|
||||
myElement.style.transform = '';
|
||||
};
|
||||
}, [isDatepickerOpen]);
|
||||
|
||||
const [isDatepickerOpen, setIsDatepickerOpen] = React.useState(false)
|
||||
|
||||
const onDatepickerClose = () => {
|
||||
onChange(date)
|
||||
setIsDatepickerOpen((prev) => !prev)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const myElement = document.querySelector('.cell-type-datepicker')
|
||||
|
||||
myElement.parentNode.style.position = 'absolute'
|
||||
myElement.style.position = 'relative'
|
||||
myElement.style.marginTop = '2px'
|
||||
myElement.style.left = '50%'
|
||||
myElement.style.width = '250px'
|
||||
myElement.style.transform = 'translate(-50%, -25%)'
|
||||
|
||||
|
||||
return () => {
|
||||
myElement.parentNode.style.position = ''
|
||||
myElement.style.position = ''
|
||||
myElement.style.marginTop = ''
|
||||
myElement.style.left = ''
|
||||
myElement.style.width = ''
|
||||
myElement.style.transform = ''
|
||||
}
|
||||
|
||||
},[isDatepickerOpen])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Datetime
|
||||
inputProps={inputProps}
|
||||
timeFormat={isTimeChecked}
|
||||
className='cell-type-datepicker'
|
||||
dateFormat={dateFormat}
|
||||
value={date}
|
||||
onChange={dateChange}
|
||||
onClose={onDatepickerClose}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Datetime
|
||||
inputProps={inputProps}
|
||||
timeFormat={isTimeChecked}
|
||||
className="cell-type-datepicker"
|
||||
dateFormat={dateFormat}
|
||||
value={date}
|
||||
onChange={dateChange}
|
||||
onClose={onDatepickerClose}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const Pagination = function Pagination({
|
|||
autoCanNextPage,
|
||||
autoPageCount,
|
||||
autoPageOptions,
|
||||
lastActivePageIndex
|
||||
lastActivePageIndex,
|
||||
}) {
|
||||
const [pageIndex, setPageIndex] = useState(lastActivePageIndex ?? 1);
|
||||
const [pageCount, setPageCount] = useState(autoPageCount);
|
||||
|
|
@ -17,13 +17,12 @@ export const Pagination = function Pagination({
|
|||
}, [autoPageCount]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if(serverSide && lastActivePageIndex > 0) {
|
||||
setPageCount(lastActivePageIndex)
|
||||
} else if(serverSide || lastActivePageIndex === 0) {
|
||||
setPageIndex(1)
|
||||
if (serverSide && lastActivePageIndex > 0) {
|
||||
setPageCount(lastActivePageIndex);
|
||||
} else if (serverSide || lastActivePageIndex === 0) {
|
||||
setPageIndex(1);
|
||||
}
|
||||
}, [serverSide, lastActivePageIndex ])
|
||||
}, [serverSide, lastActivePageIndex]);
|
||||
|
||||
function gotoPage(page) {
|
||||
setPageIndex(page);
|
||||
|
|
@ -42,48 +41,35 @@ export const Pagination = function Pagination({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="pagination">
|
||||
{!serverSide
|
||||
&& <button className="btn btn-sm btn-light mx-2" onClick={() => gotoPage(1)}>
|
||||
{'<<'}
|
||||
</button>
|
||||
}
|
||||
<button
|
||||
className="btn btn-light btn-sm"
|
||||
onClick={() => goToPreviousPage()}
|
||||
disabled={pageIndex === 1}
|
||||
>
|
||||
{'<'}
|
||||
</button>{' '}
|
||||
<small className="p-1 mx-2">
|
||||
|
||||
{serverSide &&
|
||||
<strong>
|
||||
{pageIndex}
|
||||
</strong>
|
||||
}
|
||||
{!serverSide &&
|
||||
<strong>
|
||||
{pageIndex} of {autoPageOptions.length}
|
||||
</strong>
|
||||
}
|
||||
</small>
|
||||
<button
|
||||
className="btn btn-light btn-sm"
|
||||
onClick={() => goToNextPage()}
|
||||
disabled={!autoCanNextPage && !serverSide}
|
||||
>
|
||||
{'>'}
|
||||
</button>{' '}
|
||||
|
||||
{!serverSide
|
||||
&& <button
|
||||
className="btn btn-light btn-sm mx-2"
|
||||
onClick={() => gotoPage(pageCount)}
|
||||
>
|
||||
{'>>'}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div className="pagination">
|
||||
{!serverSide && (
|
||||
<button className="btn btn-sm btn-light mx-2" onClick={() => gotoPage(1)}>
|
||||
{'<<'}
|
||||
</button>
|
||||
)}
|
||||
<button className="btn btn-light btn-sm" onClick={() => goToPreviousPage()} disabled={pageIndex === 1}>
|
||||
{'<'}
|
||||
</button>{' '}
|
||||
<small className="p-1 mx-2">
|
||||
{serverSide && <strong>{pageIndex}</strong>}
|
||||
{!serverSide && (
|
||||
<strong>
|
||||
{pageIndex} of {autoPageOptions.length}
|
||||
</strong>
|
||||
)}
|
||||
</small>
|
||||
<button
|
||||
className="btn btn-light btn-sm"
|
||||
onClick={() => goToNextPage()}
|
||||
disabled={!autoCanNextPage && !serverSide}
|
||||
>
|
||||
{'>'}
|
||||
</button>{' '}
|
||||
{!serverSide && (
|
||||
<button className="btn btn-light btn-sm mx-2" onClick={() => gotoPage(pageCount)}>
|
||||
{'>>'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export const Radio = ({ options, value, onChange, readOnly }) => {
|
||||
|
||||
value = value === undefined ? [] : value;
|
||||
options = Array.isArray(options) ? options : [];
|
||||
|
||||
return (
|
||||
<div className="radio row">
|
||||
<div>
|
||||
{options.map((option) =>
|
||||
<label class="form-check form-check-inline" onClick={() => { if(!readOnly) onChange(option.value); } }>
|
||||
<input class="form-check-input" type="radio" checked={option.value === value} disabled={readOnly && (option.value !== value)}/>
|
||||
<span class ="form-check-label">{option.name}</span>
|
||||
{options.map((option, index) => (
|
||||
<label
|
||||
key={index}
|
||||
className="form-check form-check-inline"
|
||||
onClick={() => {
|
||||
if (!readOnly) onChange(option.value);
|
||||
}}
|
||||
>
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
checked={option.value === value}
|
||||
disabled={readOnly && option.value !== value}
|
||||
/>
|
||||
<span className="form-check-label">{option.name}</span>
|
||||
</label>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
import {
|
||||
useTable,
|
||||
|
|
@ -7,7 +9,7 @@ import {
|
|||
useAsyncDebounce,
|
||||
usePagination,
|
||||
useBlockLayout,
|
||||
useResizeColumns
|
||||
useResizeColumns,
|
||||
} from 'react-table';
|
||||
import { resolveReferences, resolveWidgetFieldValue, validateWidget } from '@/_helpers/utils';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
|
|
@ -17,7 +19,7 @@ import { Pagination } from './Pagination';
|
|||
import { CustomSelect } from './CustomSelect';
|
||||
import { Tags } from './Tags';
|
||||
import { Radio } from './Radio';
|
||||
import { Toggle } from './Toggle'
|
||||
import { Toggle } from './Toggle';
|
||||
import { Datepicker } from './Datepicker';
|
||||
|
||||
var _ = require('lodash');
|
||||
|
|
@ -34,7 +36,7 @@ export function Table({
|
|||
changeCanDrag,
|
||||
onComponentOptionChanged,
|
||||
onComponentOptionsChanged,
|
||||
darkMode
|
||||
darkMode,
|
||||
}) {
|
||||
const color = component.definition.styles.textColor.value;
|
||||
const actions = component.definition.properties.actions || { value: [] };
|
||||
|
|
@ -57,7 +59,8 @@ export function Table({
|
|||
const showBulkUpdateActions = resolveWidgetFieldValue(showBulkUpdateActionsProperty, currentState) ?? true; // default is true for backward compatibility
|
||||
|
||||
const clientSidePaginationProperty = component.definition.properties.clientSidePagination?.value;
|
||||
const clientSidePagination = resolveWidgetFieldValue(clientSidePaginationProperty, currentState) ?? !serverSidePagination; // default is true for backward compatibility
|
||||
const clientSidePagination =
|
||||
resolveWidgetFieldValue(clientSidePaginationProperty, currentState) ?? !serverSidePagination; // default is true for backward compatibility
|
||||
|
||||
const tableTypeProperty = component.definition.styles.tableType;
|
||||
let tableType = tableTypeProperty ? tableTypeProperty.value : 'table-bordered';
|
||||
|
|
@ -66,12 +69,15 @@ export function Table({
|
|||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
|
||||
|
|
@ -111,7 +117,7 @@ export function Table({
|
|||
const newFilters = filters;
|
||||
newFilters[index].value = {
|
||||
...newFilters[index].value,
|
||||
operation: value
|
||||
operation: value,
|
||||
};
|
||||
setFilters(newFilters);
|
||||
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
||||
|
|
@ -121,7 +127,7 @@ export function Table({
|
|||
const newFilters = filters;
|
||||
newFilters[index].value = {
|
||||
...newFilters[index].value,
|
||||
value: value
|
||||
value: value,
|
||||
};
|
||||
setFilters(newFilters);
|
||||
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
||||
|
|
@ -146,7 +152,7 @@ export function Table({
|
|||
const defaultColumn = React.useMemo(
|
||||
() => ({
|
||||
minWidth: 60,
|
||||
width: 268
|
||||
width: 268,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
|
@ -163,26 +169,24 @@ export function Table({
|
|||
let newChangeset = {
|
||||
...changeSet,
|
||||
[index]: {
|
||||
...obj
|
||||
}
|
||||
...obj,
|
||||
},
|
||||
};
|
||||
|
||||
obj = _.set(rowData, key, value);
|
||||
|
||||
let newDataUpdates = {
|
||||
...dataUpdates,
|
||||
[index]: { ...obj }
|
||||
...dataUpdates,
|
||||
[index]: { ...obj },
|
||||
};
|
||||
|
||||
onComponentOptionsChanged(component, [
|
||||
['dataUpdates', newDataUpdates],
|
||||
['changeSet', newChangeset]
|
||||
['changeSet', newChangeset],
|
||||
]);
|
||||
}
|
||||
|
||||
function getExportFileBlob({
|
||||
columns, data
|
||||
}) {
|
||||
function getExportFileBlob({ columns, data }) {
|
||||
const headerNames = columns.map((col) => col.exportValue);
|
||||
const csvString = Papa.unparse({ fields: headerNames, data });
|
||||
return new Blob([csvString], { type: 'text/csv' });
|
||||
|
|
@ -190,14 +194,14 @@ export function Table({
|
|||
|
||||
function onPageIndexChanged(page) {
|
||||
onComponentOptionChanged(component, 'pageIndex', page).then(() => {
|
||||
onEvent('onPageChanged', { component, data: {} });
|
||||
onEvent('onPageChanged', { component, data: {} });
|
||||
});
|
||||
}
|
||||
|
||||
function handleChangesSaved() {
|
||||
Object.keys(changeSet).forEach((key) => {
|
||||
tableData[key] = {
|
||||
..._.merge(tableData[key], changeSet[key])
|
||||
..._.merge(tableData[key], changeSet[key]),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -217,7 +221,9 @@ export function Table({
|
|||
}
|
||||
|
||||
if (filterValue.operation === 'matches') {
|
||||
return rows.filter((row) => row.values[columnIds[0]].toString().toLowerCase().includes(filterValue.value.toLowerCase()));
|
||||
return rows.filter((row) =>
|
||||
row.values[columnIds[0]].toString().toLowerCase().includes(filterValue.value.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (filterValue.operation === 'gt') {
|
||||
|
|
@ -260,7 +266,13 @@ export function Table({
|
|||
const columnType = column.columnType;
|
||||
|
||||
const columnOptions = {};
|
||||
if (columnType === 'dropdown' || columnType === 'multiselect' || columnType === 'badge' || columnType === 'badges' || columnType === 'radio') {
|
||||
if (
|
||||
columnType === 'dropdown' ||
|
||||
columnType === 'multiselect' ||
|
||||
columnType === 'badge' ||
|
||||
columnType === 'badges' ||
|
||||
columnType === 'radio'
|
||||
) {
|
||||
const values = resolveReferences(column.values, currentState) || [];
|
||||
const labels = resolveReferences(column.labels, currentState, []) || [];
|
||||
|
||||
|
|
@ -271,8 +283,8 @@ export function Table({
|
|||
}
|
||||
}
|
||||
if (columnType === 'datepicker') {
|
||||
column.isTimeChecked = column.isTimeChecked ? column.isTimeChecked : false
|
||||
column.dateFormat = column.dateFormat ? column.dateFormat : 'DD/MM/YYYY'
|
||||
column.isTimeChecked = column.isTimeChecked ? column.isTimeChecked : false;
|
||||
column.dateFormat = column.dateFormat ? column.dateFormat : 'DD/MM/YYYY';
|
||||
}
|
||||
|
||||
const width = columnSize || defaultColumn.width;
|
||||
|
|
@ -290,34 +302,33 @@ export function Table({
|
|||
const cellValue = rowChangeSet ? rowChangeSet[column.name] || cell.value : cell.value;
|
||||
|
||||
if (columnType === 'string' || columnType === undefined || columnType === 'default') {
|
||||
|
||||
const textColor = resolveReferences(column.textColor, currentState, { cellValue });
|
||||
|
||||
const cellStyles = {
|
||||
color: textColor === undefined ? darkMode === true ? '#fff' : 'black' : textColor
|
||||
}
|
||||
color: textColor === undefined ? (darkMode === true ? '#fff' : 'black') : textColor,
|
||||
};
|
||||
|
||||
if (column.isEditable) {
|
||||
const validationData = validateWidget({
|
||||
validationObject: {
|
||||
regex: {
|
||||
value: column.regex
|
||||
value: column.regex,
|
||||
},
|
||||
minLength: {
|
||||
value: column.minLength
|
||||
value: column.minLength,
|
||||
},
|
||||
maxLength: {
|
||||
value: column.maxLength
|
||||
value: column.maxLength,
|
||||
},
|
||||
customRule: {
|
||||
value: column.customRule
|
||||
}
|
||||
value: column.customRule,
|
||||
},
|
||||
},
|
||||
widgetValue: cellValue,
|
||||
currentState,
|
||||
customResolveObjects: { cellValue }
|
||||
})
|
||||
|
||||
customResolveObjects: { cellValue },
|
||||
});
|
||||
|
||||
const { isValid, validationError } = validationData;
|
||||
|
||||
return (
|
||||
|
|
@ -327,48 +338,60 @@ export function Table({
|
|||
style={cellStyles}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if(e.target.defaultValue !== e.target.value) {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
|
||||
if (e.target.defaultValue !== e.target.value) {
|
||||
handleCellValueChange(
|
||||
cell.row.index,
|
||||
column.key || column.name,
|
||||
e.target.value,
|
||||
cell.row.original
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
if(e.target.defaultValue !== e.target.value) {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
|
||||
if (e.target.defaultValue !== e.target.value) {
|
||||
handleCellValueChange(
|
||||
cell.row.index,
|
||||
column.key || column.name,
|
||||
e.target.value,
|
||||
cell.row.original
|
||||
);
|
||||
}
|
||||
}}
|
||||
className={`form-control-plaintext form-control-plaintext-sm ${!isValid ? 'is-invalid' : ''}`}
|
||||
defaultValue={cellValue}
|
||||
/>
|
||||
<div class="invalid-feedback">{validationError}</div>
|
||||
<div className="invalid-feedback">{validationError}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <span style={cellStyles}>{cellValue}</span>;
|
||||
} if (columnType === 'text') {
|
||||
return <textarea
|
||||
rows="1"
|
||||
}
|
||||
if (columnType === 'text') {
|
||||
return (
|
||||
<textarea
|
||||
rows="1"
|
||||
className="form-control-plaintext text-container text-muted"
|
||||
readOnly={!column.isEditable}
|
||||
style={{maxWidth: width, minWidth: width - 10}}
|
||||
style={{ maxWidth: width, minWidth: width - 10 }}
|
||||
onBlur={(e) => {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
|
||||
}}
|
||||
defaultValue={cellValue}
|
||||
>
|
||||
</textarea>;
|
||||
} if (columnType === 'dropdown') {
|
||||
|
||||
></textarea>
|
||||
);
|
||||
}
|
||||
if (columnType === 'dropdown') {
|
||||
const validationData = validateWidget({
|
||||
validationObject: {
|
||||
customRule: {
|
||||
value: column.customRule
|
||||
}
|
||||
value: column.customRule,
|
||||
},
|
||||
},
|
||||
widgetValue: cellValue,
|
||||
currentState,
|
||||
customResolveObjects: { cellValue }
|
||||
})
|
||||
customResolveObjects: { cellValue },
|
||||
});
|
||||
|
||||
const { isValid, validationError } = validationData;
|
||||
|
||||
|
|
@ -387,7 +410,8 @@ export function Table({
|
|||
<div className={`invalid-feedback ${isValid ? '' : 'd-flex'}`}>{validationError}</div>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'multiselect') {
|
||||
}
|
||||
if (columnType === 'multiselect') {
|
||||
return (
|
||||
<div>
|
||||
<SelectSearch
|
||||
|
|
@ -403,7 +427,8 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'badge') {
|
||||
}
|
||||
if (columnType === 'badge') {
|
||||
return (
|
||||
<div>
|
||||
<CustomSelect
|
||||
|
|
@ -415,7 +440,8 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'badges') {
|
||||
}
|
||||
if (columnType === 'badges') {
|
||||
return (
|
||||
<div>
|
||||
<CustomSelect
|
||||
|
|
@ -428,10 +454,12 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'tags') {
|
||||
}
|
||||
if (columnType === 'tags') {
|
||||
return (
|
||||
<div>
|
||||
<Tags
|
||||
readOnly={!column.isEditable}
|
||||
value={cellValue}
|
||||
onChange={(value) => {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
||||
|
|
@ -439,7 +467,8 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'radio') {
|
||||
}
|
||||
if (columnType === 'radio') {
|
||||
return (
|
||||
<div>
|
||||
<Radio
|
||||
|
|
@ -449,10 +478,11 @@ export function Table({
|
|||
onChange={(value) => {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'toggle') {
|
||||
}
|
||||
if (columnType === 'toggle') {
|
||||
return (
|
||||
<div>
|
||||
<Toggle
|
||||
|
|
@ -465,7 +495,8 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} if (columnType === 'datepicker') {
|
||||
}
|
||||
if (columnType === 'datepicker') {
|
||||
return (
|
||||
<div>
|
||||
<Datepicker
|
||||
|
|
@ -481,7 +512,7 @@ export function Table({
|
|||
);
|
||||
}
|
||||
return cellValue || '';
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -494,18 +525,19 @@ export function Table({
|
|||
|
||||
tableData = tableData || [];
|
||||
|
||||
const leftActions = () => actions.value.filter(action => action.position === 'left')
|
||||
const rightActions = () => actions.value.filter(action => [undefined, 'right'].includes(action.position))
|
||||
const leftActions = () => actions.value.filter((action) => action.position === 'left');
|
||||
const rightActions = () => actions.value.filter((action) => [undefined, 'right'].includes(action.position));
|
||||
|
||||
const leftActionsCellData = leftActions().length > 0
|
||||
? [
|
||||
{
|
||||
id: 'leftActions',
|
||||
Header: 'Actions',
|
||||
accessor: 'edit',
|
||||
width: columnSizes.leftActions || defaultColumn.width,
|
||||
Cell: (cell) => {
|
||||
return leftActions().map((action) => (
|
||||
const leftActionsCellData =
|
||||
leftActions().length > 0
|
||||
? [
|
||||
{
|
||||
id: 'leftActions',
|
||||
Header: 'Actions',
|
||||
accessor: 'edit',
|
||||
width: columnSizes.leftActions || defaultColumn.width,
|
||||
Cell: (cell) => {
|
||||
return leftActions().map((action) => (
|
||||
<button
|
||||
key={action.name}
|
||||
className="btn btn-sm m-1 btn-light"
|
||||
|
|
@ -517,21 +549,22 @@ export function Table({
|
|||
>
|
||||
{action.buttonText}
|
||||
</button>
|
||||
));
|
||||
}
|
||||
}
|
||||
]
|
||||
: [];
|
||||
));
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const rightActionsCellData = rightActions().length > 0
|
||||
? [
|
||||
{
|
||||
id: 'rightActions',
|
||||
Header: 'Actions',
|
||||
accessor: 'edit',
|
||||
width: columnSizes.rightActions || defaultColumn.width,
|
||||
Cell: (cell) => {
|
||||
return rightActions().map((action) => (
|
||||
const rightActionsCellData =
|
||||
rightActions().length > 0
|
||||
? [
|
||||
{
|
||||
id: 'rightActions',
|
||||
Header: 'Actions',
|
||||
accessor: 'edit',
|
||||
width: columnSizes.rightActions || defaultColumn.width,
|
||||
Cell: (cell) => {
|
||||
return rightActions().map((action) => (
|
||||
<button
|
||||
key={action.name}
|
||||
className="btn btn-sm m-1 btn-light"
|
||||
|
|
@ -543,23 +576,23 @@ export function Table({
|
|||
>
|
||||
{action.buttonText}
|
||||
</button>
|
||||
));
|
||||
}
|
||||
}
|
||||
]
|
||||
: [];
|
||||
));
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
|
||||
const optionsData = columnData.map(column => column.columnOptions?.selectOptions);
|
||||
const optionsData = columnData.map((column) => column.columnOptions?.selectOptions);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [...leftActionsCellData, ...columnData, ...rightActionsCellData],
|
||||
[JSON.stringify(columnData),
|
||||
[
|
||||
JSON.stringify(columnData),
|
||||
leftActionsCellData.length,
|
||||
rightActionsCellData.length,
|
||||
componentState.changeSet,
|
||||
JSON.stringify(optionsData),
|
||||
JSON.stringify(component.definition.properties.columns)
|
||||
JSON.stringify(component.definition.properties.columns),
|
||||
] // Hack: need to fix
|
||||
);
|
||||
|
||||
|
|
@ -567,7 +600,7 @@ export function Table({
|
|||
|
||||
const computedStyles = {
|
||||
color,
|
||||
width: `${width}px`
|
||||
width: `${width}px`,
|
||||
};
|
||||
|
||||
const {
|
||||
|
|
@ -590,16 +623,17 @@ export function Table({
|
|||
preGlobalFilteredRows,
|
||||
setGlobalFilter,
|
||||
state: { pageIndex, pageSize },
|
||||
exportData
|
||||
exportData,
|
||||
} = useTable(
|
||||
{
|
||||
autoResetPage: false,
|
||||
columns,
|
||||
data,
|
||||
defaultColumn,
|
||||
initialState: { pageIndex: 0, pageSize: -1},
|
||||
pageCount: -1,
|
||||
manualPagination: false,
|
||||
getExportFileBlob
|
||||
initialState: { pageIndex: 0, pageSize: -1 },
|
||||
pageCount: -1,
|
||||
manualPagination: false,
|
||||
getExportFileBlob,
|
||||
},
|
||||
useFilters,
|
||||
useGlobalFilter,
|
||||
|
|
@ -610,32 +644,28 @@ export function Table({
|
|||
useExportData
|
||||
);
|
||||
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if(serverSidePagination || !clientSidePagination) {
|
||||
setPageSize(-1)
|
||||
}
|
||||
if(!serverSidePagination && clientSidePagination) {
|
||||
setPageSize(10)
|
||||
if (serverSidePagination || !clientSidePagination) {
|
||||
setPageSize(-1);
|
||||
}
|
||||
|
||||
},[clientSidePagination, serverSidePagination])
|
||||
if (!serverSidePagination && clientSidePagination) {
|
||||
setPageSize(10);
|
||||
}
|
||||
}, [clientSidePagination, serverSidePagination]);
|
||||
|
||||
useEffect(() => {
|
||||
const pageData = page.map(row => row.original);
|
||||
const currentData = rows.map(row => row.original);;
|
||||
const pageData = page.map((row) => row.original);
|
||||
const currentData = rows.map((row) => row.original);
|
||||
onComponentOptionsChanged(component, [
|
||||
['currentPageData', pageData],
|
||||
['currentData', currentData]
|
||||
['currentData', currentData],
|
||||
]);
|
||||
}, [tableData.length, componentState.changeSet]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!state.columnResizing.isResizingColumn) {
|
||||
changeCanDrag(true);
|
||||
paramUpdated(id, 'columnSizes', { ...columnSizes, ...state.columnResizing.columnWidths});
|
||||
paramUpdated(id, 'columnSizes', { ...columnSizes, ...state.columnResizing.columnWidths });
|
||||
} else {
|
||||
changeCanDrag(false);
|
||||
}
|
||||
|
|
@ -649,16 +679,15 @@ export function Table({
|
|||
}, 200);
|
||||
|
||||
const handleSearchTextChange = (text) => {
|
||||
|
||||
setValue(text);
|
||||
onChange(text);
|
||||
|
||||
onComponentOptionChanged(component, 'searchText', text).then(() => {
|
||||
if(serverSideSearch === true ) {
|
||||
if (serverSideSearch === true) {
|
||||
onEvent('onSearch', { component, data: {} });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ms-2 d-inline-block">
|
||||
|
|
@ -667,18 +696,16 @@ export function Table({
|
|||
className="global-search-field"
|
||||
defaultValue={value || ''}
|
||||
onBlur={(e) => {
|
||||
handleSearchTextChange(e.target.value)
|
||||
handleSearchTextChange(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if(e.key === 'Enter') {
|
||||
handleSearchTextChange(e.target.value)
|
||||
if (e.key === 'Enter') {
|
||||
handleSearchTextChange(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}}
|
||||
placeholder={`${count} records`}
|
||||
style={{
|
||||
border: '0'
|
||||
border: '0',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -689,26 +716,32 @@ export function Table({
|
|||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="card jet-table"
|
||||
style={{ width: `${width}px`, height: `${height}px`, display:parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}
|
||||
style={{ width: `${width}px`, height: `${height}px`, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
{/* Show top bar unless search box is disabled and server pagination is enabled */}
|
||||
{displaySearchBox &&
|
||||
{displaySearchBox && (
|
||||
<div className="card-body border-bottom py-3 jet-data-table-header">
|
||||
<div className="d-flex">
|
||||
{displaySearchBox && <div className="ms-auto text-muted">
|
||||
<GlobalFilter />
|
||||
</div>}
|
||||
{displaySearchBox && (
|
||||
<div className="ms-auto text-muted">
|
||||
<GlobalFilter />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
<div className="table-responsive jet-data-table">
|
||||
<table {...getTableProps()} className={`table table-vcenter table-nowrap ${tableType}`} style={computedStyles}>
|
||||
<thead>
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()} tabIndex="0" className="tr">
|
||||
{headerGroup.headers.map((column) => (
|
||||
{headerGroups.map((headerGroup, index) => (
|
||||
<tr key={index} {...headerGroup.getHeaderGroupProps()} tabIndex="0" className="tr">
|
||||
{headerGroup.headers.map((column, index) => (
|
||||
<th
|
||||
key={index}
|
||||
{...column.getHeaderProps(column.getSortByToggleProps())}
|
||||
className={column.isSorted ? (column.isSortedDesc ? 'sort-desc th' : 'sort-asc th') : 'th'}
|
||||
>
|
||||
|
|
@ -724,17 +757,20 @@ export function Table({
|
|||
))}
|
||||
</thead>
|
||||
|
||||
{!loadingState && page.length === 0 &&
|
||||
<center className="w-100"><div className="py-5"> no data </div></center>
|
||||
}
|
||||
{!loadingState && page.length === 0 && (
|
||||
<center className="w-100">
|
||||
<div className="py-5"> no data </div>
|
||||
</center>
|
||||
)}
|
||||
|
||||
{!loadingState && (
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{console.log('page', page)}
|
||||
{page.map((row) => {
|
||||
{page.map((row, index) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
className="table-row"
|
||||
{...row.getRowProps()}
|
||||
onClick={(e) => {
|
||||
|
|
@ -742,20 +778,26 @@ export function Table({
|
|||
onEvent('onRowClicked', { component, data: row.original });
|
||||
}}
|
||||
>
|
||||
{row.cells.map((cell) => {
|
||||
{row.cells.map((cell, index) => {
|
||||
let cellProps = cell.getCellProps();
|
||||
if (componentState.changeSet) {
|
||||
if (componentState.changeSet[cell.row.index]) {
|
||||
const currentColumn = columnData.find((column) => column.id === cell.column.id);
|
||||
|
||||
const currentColumn = columnData.find(column => column.id === cell.column.id);
|
||||
|
||||
if (_.get(componentState.changeSet[cell.row.index], currentColumn?.accessor, undefined) !== undefined) {
|
||||
if (
|
||||
_.get(componentState.changeSet[cell.row.index], currentColumn?.accessor, undefined) !==
|
||||
undefined
|
||||
) {
|
||||
console.log('componentState.changeSet', componentState.changeSet);
|
||||
cellProps.style.backgroundColor = '#ffffde';
|
||||
}
|
||||
}
|
||||
}
|
||||
return <td {...cellProps}>{cell.render('Cell')}</td>;
|
||||
return (
|
||||
<td key={index} {...cellProps}>
|
||||
{cell.render('Cell')}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
|
|
@ -771,30 +813,35 @@ export function Table({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
{(clientSidePagination || serverSidePagination || Object.keys(componentState.changeSet || {}).length > 0 || showFilterButton || showDownloadButton) &&
|
||||
{(clientSidePagination ||
|
||||
serverSidePagination ||
|
||||
Object.keys(componentState.changeSet || {}).length > 0 ||
|
||||
showFilterButton ||
|
||||
showDownloadButton) && (
|
||||
<div className="card-footer d-flex align-items-center jet-table-footer">
|
||||
<div className="table-footer row">
|
||||
<div className="col">
|
||||
{(clientSidePagination || serverSidePagination) &&
|
||||
{(clientSidePagination || serverSidePagination) && (
|
||||
<Pagination
|
||||
lastActivePageIndex={currentState.components[component.name]?.pageIndex ?? 1 }
|
||||
serverSide={serverSidePagination}
|
||||
autoGotoPage={gotoPage}
|
||||
autoCanNextPage={canNextPage}
|
||||
autoPageCount={pageCount}
|
||||
autoPageOptions={pageOptions}
|
||||
onPageIndexChanged={onPageIndexChanged}
|
||||
lastActivePageIndex={currentState.components[component.name]?.pageIndex ?? 1}
|
||||
serverSide={serverSidePagination}
|
||||
autoGotoPage={gotoPage}
|
||||
autoCanNextPage={canNextPage}
|
||||
autoPageCount={pageCount}
|
||||
autoPageOptions={pageOptions}
|
||||
onPageIndexChanged={onPageIndexChanged}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(showBulkUpdateActions && Object.keys(componentState.changeSet || {}).length > 0) && (
|
||||
{showBulkUpdateActions && Object.keys(componentState.changeSet || {}).length > 0 && (
|
||||
<div className="col">
|
||||
<button
|
||||
className={`btn btn-primary btn-sm ${componentState.isSavingChanges ? 'btn-loading' : ''}`}
|
||||
onClick={() => onEvent('onBulkUpdate', { component }).then(() => {
|
||||
handleChangesSaved();
|
||||
})
|
||||
onClick={() =>
|
||||
onEvent('onBulkUpdate', { component }).then(() => {
|
||||
handleChangesSaved();
|
||||
})
|
||||
}
|
||||
>
|
||||
Save Changes
|
||||
|
|
@ -806,15 +853,15 @@ export function Table({
|
|||
)}
|
||||
|
||||
<div className="col-auto">
|
||||
{showFilterButton &&
|
||||
{showFilterButton && (
|
||||
<span data-tip="Filter data" className="btn btn-light btn-sm p-1 mx-2" onClick={() => showFilters()}>
|
||||
<img src="/assets/images/icons/filter.svg" width="13" height="13" />
|
||||
{filters.length > 0 &&
|
||||
<a className="badge bg-azure" style={{width: '4px', height: '4px', marginTop: '5px'}}></a>
|
||||
}
|
||||
{filters.length > 0 && (
|
||||
<a className="badge bg-azure" style={{ width: '4px', height: '4px', marginTop: '5px' }}></a>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
{showDownloadButton &&
|
||||
)}
|
||||
{showDownloadButton && (
|
||||
<span
|
||||
data-tip="Download as CSV"
|
||||
className="btn btn-light btn-sm p-1"
|
||||
|
|
@ -822,11 +869,11 @@ export function Table({
|
|||
>
|
||||
<img src="/assets/images/icons/download.svg" width="13" height="13" />
|
||||
</span>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
{isFiltersVisible && (
|
||||
<div className="table-filters card">
|
||||
<div className="card-header row">
|
||||
|
|
@ -868,7 +915,7 @@ export function Table({
|
|||
{ name: 'greater than', value: 'gt' },
|
||||
{ name: 'less than', value: 'lt' },
|
||||
{ name: 'greater than or equals', value: 'gte' },
|
||||
{ name: 'less than or equals', value: 'lte' }
|
||||
{ name: 'less than or equals', value: 'lte' },
|
||||
]}
|
||||
value={filter.value.operation}
|
||||
search={true}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,67 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
export const Tags = ({ value, onChange }) => {
|
||||
export const Tags = ({ value, onChange, readOnly }) => {
|
||||
value = value || [];
|
||||
|
||||
value = value || [];
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const [ showForm, setShowForm ] = useState(false);
|
||||
|
||||
function addTag(text) {
|
||||
if(text !== '') {
|
||||
value.push(text);
|
||||
onChange(value);
|
||||
} else {
|
||||
setShowForm(false);
|
||||
}
|
||||
function addTag(text) {
|
||||
if (text !== '') {
|
||||
value.push(text);
|
||||
onChange(value);
|
||||
} else {
|
||||
setShowForm(false);
|
||||
}
|
||||
}
|
||||
|
||||
function removeTag(text) {
|
||||
const newValue = value.filter(tag => tag !== text);
|
||||
onChange(newValue);
|
||||
setShowForm(false);
|
||||
}
|
||||
function removeTag(text) {
|
||||
const newValue = value.filter((tag) => tag !== text);
|
||||
onChange(newValue);
|
||||
setShowForm(false);
|
||||
}
|
||||
|
||||
function handleFormKeyDown(e) {
|
||||
if(e.key === 'Enter') {
|
||||
addTag(e.target.value)
|
||||
}
|
||||
}
|
||||
|
||||
function renderTag(text) {
|
||||
return <span className="col-auto badge bg-blue-lt p-2 mx-1 tag mb-2">
|
||||
{text}
|
||||
<span className="badge badge-pill bg-red-lt remove-tag-button" onClick={() => removeTag(text)}>
|
||||
x
|
||||
</span>
|
||||
</span>;
|
||||
function handleFormKeyDown(e) {
|
||||
if (e.key === 'Enter') {
|
||||
addTag(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
function renderTag(text) {
|
||||
return (
|
||||
<div className="tags row">
|
||||
{value.map((item) => {
|
||||
return renderTag(item)
|
||||
})}
|
||||
|
||||
{!showForm &&
|
||||
<span className="col-auto badge bg-green-lt mx-1 add-tag-button" onClick={() => setShowForm(true)}>{'+'}</span>
|
||||
}
|
||||
|
||||
{showForm &&
|
||||
<span className="col-auto badge bg-green-lt mx-1">
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
className="form-control-plaintext"
|
||||
onBlur={(e) => addTag(e.target.value)}
|
||||
onKeyDown={handleFormKeyDown}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<span className="col-auto badge bg-blue-lt p-2 mx-1 tag mb-2">
|
||||
{text}
|
||||
{!readOnly && (
|
||||
<span className="badge badge-pill bg-red-lt remove-tag-button" onClick={() => removeTag(text)}>
|
||||
x
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tags row">
|
||||
{value.map((item) => {
|
||||
return renderTag(item);
|
||||
})}
|
||||
|
||||
{!showForm && !readOnly && (
|
||||
<span className="col-auto badge bg-green-lt mx-1 add-tag-button" onClick={() => setShowForm(true)}>
|
||||
{'+'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{showForm && (
|
||||
<span className="col-auto badge bg-green-lt mx-1">
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
className="form-control-plaintext"
|
||||
onBlur={(e) => addTag(e.target.value)}
|
||||
onKeyDown={handleFormKeyDown}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
export const Toggle = ({readOnly, value, onChange, activeColor, options }) => {
|
||||
const [on, setOn] = useState(() => value)
|
||||
export const Toggle = ({ readOnly, value, onChange, activeColor }) => {
|
||||
const [on, setOn] = useState(() => value);
|
||||
|
||||
const toggle = () => {
|
||||
setOn((prev) => !prev)
|
||||
onChange(!on)
|
||||
}
|
||||
setOn((prev) => !prev);
|
||||
onChange(!on);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="radio row g-0">
|
||||
<label className="form-check form-switch form-check-inline">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={on}
|
||||
style={ on ? { backgroundColor: activeColor} : {}}
|
||||
onClick={() => {if(!readOnly) toggle()}}
|
||||
/>
|
||||
</label>
|
||||
<label className="form-check form-switch form-check-inline">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={on}
|
||||
style={on ? { backgroundColor: activeColor } : {}}
|
||||
onClick={() => {
|
||||
if (!readOnly) toggle();
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import DOMPurify from 'dompurify';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
export const Text = function Text({
|
||||
id, width, height, component, onComponentClick, currentState
|
||||
}) {
|
||||
export const Text = function Text({ id, width, height, component, onComponentClick, currentState }) {
|
||||
const text = component.definition.properties.text.value;
|
||||
const color = component.definition.styles.textColor.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
|
||||
|
|
@ -21,6 +19,7 @@ export const Text = function Text({
|
|||
const newState = resolveReferences(loadingStateProperty.value, currentState, false);
|
||||
setLoadingState(newState);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentState]);
|
||||
|
||||
let data = text;
|
||||
|
|
@ -37,21 +36,31 @@ export const Text = function Text({
|
|||
}
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
const computedStyles = {
|
||||
color,
|
||||
width,
|
||||
height,
|
||||
display: parsedWidgetVisibility ? 'flex' : 'none',
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-disabled={parsedDisabledState} className="text-widget" style={computedStyles} onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}>
|
||||
<div
|
||||
data-disabled={parsedDisabledState}
|
||||
className="text-widget"
|
||||
style={computedStyles}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
>
|
||||
{!loadingState && <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(data) }} />}
|
||||
{loadingState === true && (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ export const TextArea = function TextArea({
|
|||
component,
|
||||
onComponentClick,
|
||||
currentState,
|
||||
onComponentOptionChanged
|
||||
onComponentOptionChanged,
|
||||
}) {
|
||||
|
||||
const value = component.definition.properties.value ? component.definition.properties.value.value : '';
|
||||
const [text, setText] = useState(value);
|
||||
|
||||
|
|
@ -23,24 +22,31 @@ export const TextArea = function TextArea({
|
|||
useEffect(() => {
|
||||
setText(newText);
|
||||
onComponentOptionChanged(component, 'value', newText);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newText]);
|
||||
|
||||
|
||||
const placeholder = component.definition.properties.placeholder.value;
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
|
||||
let parsedWidgetVisibility = widgetVisibility;
|
||||
|
||||
|
||||
try {
|
||||
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
||||
} catch (err) { console.log(err); }
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
return (
|
||||
<textarea
|
||||
disabled={parsedDisabledState}
|
||||
onClick={event => {event.stopPropagation(); onComponentClick(id, component)}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setText(e.target.value);
|
||||
onComponentOptionChanged(component, 'value', e.target.value);
|
||||
|
|
@ -48,7 +54,7 @@ export const TextArea = function TextArea({
|
|||
type="text"
|
||||
className="form-control"
|
||||
placeholder={placeholder}
|
||||
style={{ width, height, display:parsedWidgetVisibility ? '' : 'none' }}
|
||||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
value={text}
|
||||
></textarea>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const TextInput = function TextInput({
|
|||
useEffect(() => {
|
||||
setText(newText);
|
||||
onComponentOptionChanged(component, 'value', newText);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newText]);
|
||||
|
||||
const validationData = validateWidget({
|
||||
|
|
|
|||
|
|
@ -35,15 +35,19 @@ export const componentTypes = [
|
|||
},
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Text Color' },
|
||||
tableType: { type: 'select', displayName: 'Table type', options: [
|
||||
{ name: 'Bordered', value: '' },
|
||||
{ name: 'Borderless', value: 'table-borderless' },
|
||||
{ name: 'Classic', value: 'table-classic' },
|
||||
{ name: 'Striped', value: 'table-striped' },
|
||||
{ name: 'Striped & bordered', value: 'table-striped table-bordered' }
|
||||
] },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
tableType: {
|
||||
type: 'select',
|
||||
displayName: 'Table type',
|
||||
options: [
|
||||
{ name: 'Bordered', value: '' },
|
||||
{ name: 'Borderless', value: 'table-borderless' },
|
||||
{ name: 'Classic', value: 'table-classic' },
|
||||
{ name: 'Striped', value: 'table-striped' },
|
||||
{ name: 'Striped & bordered', value: 'table-striped table-bordered' },
|
||||
],
|
||||
},
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
selectedRow: {},
|
||||
|
|
@ -82,8 +86,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -110,8 +114,8 @@ export const componentTypes = [
|
|||
styles: {
|
||||
backgroundColor: { type: 'color', displayName: 'Background color' },
|
||||
textColor: { type: 'color', displayName: 'Text color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -128,8 +132,8 @@ export const componentTypes = [
|
|||
styles: {
|
||||
backgroundColor: { value: '#3c92dc' },
|
||||
textColor: { value: '#fff' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -164,8 +168,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
show: null,
|
||||
|
|
@ -183,16 +187,16 @@ export const componentTypes = [
|
|||
type: { value: `line` },
|
||||
data: {
|
||||
value: `[
|
||||
{ "x": 100, "y": "Jan"},
|
||||
{ "x": 80, "y": "Feb"},
|
||||
{ "x": 40, "y": "Mar"}
|
||||
{ "x": "Jan", "y": 100},
|
||||
{ "x": "Feb", "y": 80},
|
||||
{ "x": "Mar", "y": 40}
|
||||
]`,
|
||||
},
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -223,7 +227,7 @@ export const componentTypes = [
|
|||
},
|
||||
events: {},
|
||||
styles: {
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
show: null,
|
||||
|
|
@ -239,7 +243,7 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
disabledState: {value: '{{false}}'}
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -258,28 +262,28 @@ export const componentTypes = [
|
|||
},
|
||||
properties: {
|
||||
value: { type: 'code', displayName: 'Default value' },
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' }
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' },
|
||||
},
|
||||
validation: {
|
||||
regex: { type: 'code', displayName: 'Regex' },
|
||||
minLength: { type: 'code', displayName: 'Min length' },
|
||||
maxLength: { type: 'code', displayName: 'Max length' },
|
||||
customRule: { type: 'code', displayName: 'Custom validation' }
|
||||
customRule: { type: 'code', displayName: 'Custom validation' },
|
||||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: '',
|
||||
value: '',
|
||||
},
|
||||
definition: {
|
||||
validation: {
|
||||
regex: { value: '' },
|
||||
minLength: { value: null },
|
||||
maxLength: { value: null },
|
||||
customRule: { value: null }
|
||||
customRule: { value: null },
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
|
|
@ -287,12 +291,12 @@ export const componentTypes = [
|
|||
},
|
||||
properties: {
|
||||
value: { value: '' },
|
||||
placeholder: { value: 'Placeholder text' }
|
||||
placeholder: { value: 'Placeholder text' },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -311,12 +315,12 @@ export const componentTypes = [
|
|||
},
|
||||
properties: {
|
||||
value: { type: 'code', displayName: 'Default value' },
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' }
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' },
|
||||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: 0,
|
||||
|
|
@ -328,12 +332,12 @@ export const componentTypes = [
|
|||
},
|
||||
properties: {
|
||||
value: { value: '' },
|
||||
placeholder: { value: '0' }
|
||||
placeholder: { value: '0' },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -347,24 +351,25 @@ export const componentTypes = [
|
|||
height: 30,
|
||||
},
|
||||
validation: {
|
||||
customRule: { type: 'code', displayName: 'Custom validation' }
|
||||
customRule: { type: 'code', displayName: 'Custom validation' },
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? ' },
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?' },
|
||||
},
|
||||
properties: {
|
||||
defaultValue: { type: 'code', displayName: 'Default value' },
|
||||
format: { type: 'code', displayName: 'Format' },
|
||||
enableTime: { type: 'code', displayName: 'Enable time selection?' },
|
||||
enableDate: { type: 'code', displayName: 'Enable date selection?' },
|
||||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: {},
|
||||
value: '',
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
|
|
@ -372,17 +377,18 @@ export const componentTypes = [
|
|||
showOnMobile: { value: false },
|
||||
},
|
||||
validation: {
|
||||
customRule: { value: null }
|
||||
customRule: { value: null },
|
||||
},
|
||||
properties: {
|
||||
defaultValue: { value: '' },
|
||||
format: { value: 'DD/MM/YYYY' },
|
||||
enableTime: { value: '{{false}}' },
|
||||
enableDate: { value: '{{true}}' },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -408,8 +414,8 @@ export const componentTypes = [
|
|||
},
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Text Color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -423,8 +429,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '#000' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -452,8 +458,8 @@ export const componentTypes = [
|
|||
},
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Text Color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -471,8 +477,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '#000' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -497,8 +503,8 @@ export const componentTypes = [
|
|||
},
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Text Color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -512,8 +518,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '#000' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -536,8 +542,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: {},
|
||||
|
|
@ -553,8 +559,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -576,8 +582,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
endDate: {},
|
||||
|
|
@ -593,8 +599,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -618,8 +624,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Text Color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -635,8 +641,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '#000' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -660,8 +666,8 @@ export const componentTypes = [
|
|||
onClick: { displayName: 'On click' },
|
||||
},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -675,8 +681,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -693,13 +699,12 @@ export const componentTypes = [
|
|||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? ' },
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?' },
|
||||
},
|
||||
properties: {
|
||||
},
|
||||
properties: {},
|
||||
events: {},
|
||||
styles: {
|
||||
backgroundColor: { type: 'color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
|
|
@ -713,8 +718,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value:'{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -732,7 +737,7 @@ export const componentTypes = [
|
|||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?' },
|
||||
},
|
||||
validation: {
|
||||
customRule: { type: 'code', displayName: 'Custom validation' }
|
||||
customRule: { type: 'code', displayName: 'Custom validation' },
|
||||
},
|
||||
properties: {
|
||||
label: { type: 'code', displayName: 'Label' },
|
||||
|
|
@ -744,8 +749,8 @@ export const componentTypes = [
|
|||
onSelect: { displayName: 'On select' },
|
||||
},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: null,
|
||||
|
|
@ -756,7 +761,7 @@ export const componentTypes = [
|
|||
showOnMobile: { value: false },
|
||||
},
|
||||
validation: {
|
||||
customRule: { value: null }
|
||||
customRule: { value: null },
|
||||
},
|
||||
properties: {
|
||||
label: { value: 'Select' },
|
||||
|
|
@ -767,8 +772,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -795,8 +800,8 @@ export const componentTypes = [
|
|||
onSelect: { displayName: 'On select' },
|
||||
},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
values: {},
|
||||
|
|
@ -815,8 +820,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -838,8 +843,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: {},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: {},
|
||||
|
|
@ -854,8 +859,8 @@ export const componentTypes = [
|
|||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -901,8 +906,8 @@ export const componentTypes = [
|
|||
onMarkerClick: { displayName: 'On marker click' },
|
||||
},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
center: {},
|
||||
|
|
@ -923,8 +928,8 @@ export const componentTypes = [
|
|||
addNewMarkers: { value: '{{false}}' },
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -946,8 +951,8 @@ export const componentTypes = [
|
|||
onDetect: { displayName: 'On detect' },
|
||||
},
|
||||
styles: {
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
lastDetectedValue: '',
|
||||
|
|
@ -960,8 +965,8 @@ export const componentTypes = [
|
|||
properties: {},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -990,8 +995,8 @@ export const componentTypes = [
|
|||
},
|
||||
styles: {
|
||||
textColor: { type: 'color', displayName: 'Star Color' },
|
||||
visibility: {type: 'code', displayName: 'Visibility'},
|
||||
disabledState: {type: 'code', displayName: 'Disable'}
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
value: 0,
|
||||
|
|
@ -1012,8 +1017,8 @@ export const componentTypes = [
|
|||
events: [],
|
||||
styles: {
|
||||
textColor: { value: '#ffb400' },
|
||||
visibility: {value: '{{true}}'},
|
||||
disabledState: {value: '{{false}}'}
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,39 +1,35 @@
|
|||
import React from 'react';
|
||||
|
||||
export const ConfigHandle = function ConfigHandle({
|
||||
id,
|
||||
component,
|
||||
configHandleClicked,
|
||||
dragRef,
|
||||
removeComponent
|
||||
}) {
|
||||
|
||||
return <div className="config-handle" ref={dragRef}>
|
||||
<span
|
||||
style={{cursor: 'move'}}
|
||||
onClick={(e) => { e.preventDefault(); e.stopPropagation(); configHandleClicked(id, component) }}
|
||||
className="badge badge bg-azure-lt"
|
||||
role="button"
|
||||
>
|
||||
<img
|
||||
style={{cursor: 'pointer'}}
|
||||
src="/assets/images/icons/menu.svg"
|
||||
width="8"
|
||||
height="8"
|
||||
style={{marginRight: '5px'}}
|
||||
/>
|
||||
{component.name}
|
||||
|
||||
</span>
|
||||
<img
|
||||
style={{cursor: 'pointer'}}
|
||||
src="/assets/images/icons/trash.svg"
|
||||
width="12"
|
||||
role="button"
|
||||
className="mx-2"
|
||||
height="12"
|
||||
onClick={() => removeComponent({id})}
|
||||
style={{marginRight: '5px'}}
|
||||
export const ConfigHandle = function ConfigHandle({ id, component, configHandleClicked, dragRef, removeComponent }) {
|
||||
return (
|
||||
<div className="config-handle" ref={dragRef}>
|
||||
<span
|
||||
style={{ cursor: 'move' }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
configHandleClicked(id, component);
|
||||
}}
|
||||
className="badge badge bg-azure-lt"
|
||||
role="button"
|
||||
>
|
||||
<img
|
||||
style={{ cursor: 'pointer', marginRight: '5px' }}
|
||||
src="/assets/images/icons/menu.svg"
|
||||
width="8"
|
||||
height="8"
|
||||
/>
|
||||
{component.name}
|
||||
</span>
|
||||
<img
|
||||
style={{ cursor: 'pointer', marginRight: '5px' }}
|
||||
src="/assets/images/icons/trash.svg"
|
||||
width="12"
|
||||
role="button"
|
||||
className="mx-2"
|
||||
height="12"
|
||||
onClick={() => removeComponent({ id })}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import update from 'immutability-helper';
|
|||
import { componentTypes } from './Components/components';
|
||||
import { computeComponentName } from '@/_helpers/utils';
|
||||
|
||||
|
||||
|
||||
function uuidv4() {
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
|
||||
(c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
|
||||
);
|
||||
}
|
||||
|
||||
export const Container = ({
|
||||
|
|
@ -31,13 +31,12 @@ export const Container = ({
|
|||
deviceWindowWidth,
|
||||
scaleValue,
|
||||
selectedComponent,
|
||||
darkMode
|
||||
darkMode,
|
||||
}) => {
|
||||
|
||||
const styles = {
|
||||
width: currentLayout === 'mobile' ? deviceWindowWidth : 1292,
|
||||
height: 2400,
|
||||
position: 'absolute'
|
||||
position: 'absolute',
|
||||
};
|
||||
|
||||
const components = appDefinition.components;
|
||||
|
|
@ -55,8 +54,8 @@ export const Container = ({
|
|||
setBoxes(
|
||||
update(boxes, {
|
||||
[id]: {
|
||||
$merge: { layouts }
|
||||
}
|
||||
$merge: { layouts },
|
||||
},
|
||||
})
|
||||
);
|
||||
console.log('new boxes - 1', boxes);
|
||||
|
|
@ -67,17 +66,18 @@ export const Container = ({
|
|||
useEffect(() => {
|
||||
console.log('new boxes - 2', boxes);
|
||||
appDefinitionChanged({ ...appDefinition, components: boxes });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [boxes]);
|
||||
|
||||
const { draggingState } = useDragLayer((monitor) => {
|
||||
if(monitor.isDragging()) {
|
||||
if(!monitor.getItem().parent) {
|
||||
return { draggingState: true }
|
||||
if (monitor.isDragging()) {
|
||||
if (!monitor.getItem().parent) {
|
||||
return { draggingState: true };
|
||||
} else {
|
||||
return { draggingState: false }
|
||||
return { draggingState: false };
|
||||
}
|
||||
} else {
|
||||
return { draggingState: false }
|
||||
return { draggingState: false };
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -89,8 +89,7 @@ export const Container = ({
|
|||
() => ({
|
||||
accept: ItemTypes.BOX,
|
||||
drop(item, monitor) {
|
||||
|
||||
if(item.parent) {
|
||||
if (item.parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +111,7 @@ export const Container = ({
|
|||
let deltaX = 0;
|
||||
let deltaY = 0;
|
||||
|
||||
if(delta) {
|
||||
if (delta) {
|
||||
deltaX = delta.x;
|
||||
deltaY = delta.y;
|
||||
}
|
||||
|
|
@ -135,13 +134,12 @@ export const Container = ({
|
|||
...boxes[id]['layouts'][item.currentLayout],
|
||||
top: top,
|
||||
left: left,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
setBoxes(newBoxes);
|
||||
|
||||
} else {
|
||||
// This is a new component
|
||||
componentMeta = componentTypes.find((component) => component.component === item.component.component);
|
||||
|
|
@ -153,8 +151,8 @@ export const Container = ({
|
|||
const offsetFromLeftOfWindow = canvasBoundingRect.left;
|
||||
const currentOffset = monitor.getSourceClientOffset();
|
||||
|
||||
left = Math.round(currentOffset.x + (currentOffset.x * (1 - zoomLevel)) - offsetFromLeftOfWindow);
|
||||
top = Math.round(currentOffset.y + (currentOffset.y * (1 - zoomLevel)) - offsetFromTopOfWindow);
|
||||
left = Math.round(currentOffset.x + currentOffset.x * (1 - zoomLevel) - offsetFromLeftOfWindow);
|
||||
top = Math.round(currentOffset.y + currentOffset.y * (1 - zoomLevel) - offsetFromTopOfWindow);
|
||||
|
||||
id = uuidv4();
|
||||
|
||||
|
|
@ -162,7 +160,7 @@ export const Container = ({
|
|||
[left, top] = doSnapToGrid(left, top);
|
||||
}
|
||||
|
||||
if(item.currentLayout === 'mobile') {
|
||||
if (item.currentLayout === 'mobile') {
|
||||
componentData.definition.others.showOnDesktop.value = false;
|
||||
componentData.definition.others.showOnMobile.value = true;
|
||||
}
|
||||
|
|
@ -177,14 +175,14 @@ export const Container = ({
|
|||
left: left,
|
||||
width: componentMeta.defaultSize.width,
|
||||
height: componentMeta.defaultSize.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
}),
|
||||
[moveBox]
|
||||
);
|
||||
|
|
@ -199,10 +197,10 @@ export const Container = ({
|
|||
top: 100,
|
||||
left: 0,
|
||||
width: 445,
|
||||
height: 500
|
||||
height: 500,
|
||||
};
|
||||
|
||||
let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData;
|
||||
let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData;
|
||||
|
||||
top = y;
|
||||
left = x;
|
||||
|
|
@ -220,10 +218,13 @@ export const Container = ({
|
|||
...boxes[id]['layouts'],
|
||||
[currentLayout]: {
|
||||
...boxes[id]['layouts'][currentLayout],
|
||||
width, height, top, left
|
||||
}
|
||||
}
|
||||
}
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
setBoxes(newBoxes);
|
||||
|
|
@ -241,12 +242,12 @@ export const Container = ({
|
|||
...boxes[id].component.definition,
|
||||
properties: {
|
||||
...boxes[id].component.definition.properties,
|
||||
[param]: value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[param]: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -255,60 +256,63 @@ export const Container = ({
|
|||
return (
|
||||
<div ref={drop} style={styles} className={`real-canvas ${isDragging || isResizing ? 'show-grid' : ''}`}>
|
||||
{Object.keys(boxes).map((key) => {
|
||||
|
||||
const box = boxes[key];
|
||||
const canShowInCurrentLayout = box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value;
|
||||
const canShowInCurrentLayout =
|
||||
box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value;
|
||||
|
||||
if(!box.parent && canShowInCurrentLayout) {
|
||||
return <DraggableBox
|
||||
onComponentClick={onComponentClick}
|
||||
onEvent={onEvent}
|
||||
onComponentOptionChanged={onComponentOptionChanged}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
key={key}
|
||||
currentState={currentState}
|
||||
onResizeStop={onResizeStop}
|
||||
paramUpdated={paramUpdated}
|
||||
id={key}
|
||||
{...boxes[key]}
|
||||
mode={mode}
|
||||
resizingStatusChanged={(status) => setIsResizing(status)}
|
||||
inCanvas={true}
|
||||
zoomLevel={zoomLevel}
|
||||
configHandleClicked={configHandleClicked}
|
||||
removeComponent={removeComponent}
|
||||
currentLayout={currentLayout}
|
||||
scaleValue={scaleValue}
|
||||
deviceWindowWidth={deviceWindowWidth}
|
||||
isSelectedComponent={selectedComponent? selectedComponent.id === key : false}
|
||||
darkMode={darkMode}
|
||||
containerProps={{
|
||||
mode,
|
||||
snapToGrid,
|
||||
onComponentClick,
|
||||
onEvent,
|
||||
appDefinition,
|
||||
appDefinitionChanged,
|
||||
currentState,
|
||||
onComponentOptionChanged,
|
||||
onComponentOptionsChanged,
|
||||
appLoading,
|
||||
zoomLevel,
|
||||
configHandleClicked,
|
||||
removeComponent,
|
||||
currentLayout,
|
||||
scaleValue,
|
||||
deviceWindowWidth,
|
||||
selectedComponent,
|
||||
darkMode
|
||||
}}
|
||||
/>
|
||||
if (!box.parent && canShowInCurrentLayout) {
|
||||
return (
|
||||
<DraggableBox
|
||||
onComponentClick={onComponentClick}
|
||||
onEvent={onEvent}
|
||||
onComponentOptionChanged={onComponentOptionChanged}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
key={key}
|
||||
currentState={currentState}
|
||||
onResizeStop={onResizeStop}
|
||||
paramUpdated={paramUpdated}
|
||||
id={key}
|
||||
{...boxes[key]}
|
||||
mode={mode}
|
||||
resizingStatusChanged={(status) => setIsResizing(status)}
|
||||
inCanvas={true}
|
||||
zoomLevel={zoomLevel}
|
||||
configHandleClicked={configHandleClicked}
|
||||
removeComponent={removeComponent}
|
||||
currentLayout={currentLayout}
|
||||
scaleValue={scaleValue}
|
||||
deviceWindowWidth={deviceWindowWidth}
|
||||
isSelectedComponent={selectedComponent ? selectedComponent.id === key : false}
|
||||
darkMode={darkMode}
|
||||
containerProps={{
|
||||
mode,
|
||||
snapToGrid,
|
||||
onComponentClick,
|
||||
onEvent,
|
||||
appDefinition,
|
||||
appDefinitionChanged,
|
||||
currentState,
|
||||
onComponentOptionChanged,
|
||||
onComponentOptionsChanged,
|
||||
appLoading,
|
||||
zoomLevel,
|
||||
configHandleClicked,
|
||||
removeComponent,
|
||||
currentLayout,
|
||||
scaleValue,
|
||||
deviceWindowWidth,
|
||||
selectedComponent,
|
||||
darkMode,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
)}
|
||||
})}
|
||||
{Object.keys(boxes).length === 0 && !appLoading && !isDragging && (
|
||||
<div className="mx-auto w-50 p-5 bg-light no-components-box" style={{ marginTop: '10%'}}>
|
||||
<center className="text-muted">You haven't added any components yet. Drag components from the right sidebar and drop here.</center>
|
||||
<div className="mx-auto w-50 p-5 bg-light no-components-box" style={{ marginTop: '10%' }}>
|
||||
<center className="text-muted">
|
||||
You haven't added any components yet. Drag components from the right sidebar and drop here.
|
||||
</center>
|
||||
</div>
|
||||
)}
|
||||
{appLoading && (
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ const layerStyles = {
|
|||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
function getItemStyles(delta, item, initialOffset, currentOffset, currentLayout) {
|
||||
if (!initialOffset || !currentOffset) {
|
||||
return {
|
||||
display: 'none'
|
||||
display: 'none',
|
||||
};
|
||||
}
|
||||
let { x, y } = currentOffset;
|
||||
|
|
@ -28,18 +28,19 @@ function getItemStyles(delta, item, initialOffset, currentOffset, currentLayout)
|
|||
|
||||
const realCanvasDelta = realCanvasBoundingRect.x - canvasContainerBoundingRect.x;
|
||||
|
||||
if (id) { // Dragging within the canvas
|
||||
if (id) {
|
||||
// Dragging within the canvas
|
||||
|
||||
x = Math.round(item.layouts[currentLayout].left + delta.x);
|
||||
y = Math.round(item.layouts[currentLayout].top + delta.y);
|
||||
|
||||
} else { // New component being dragged from components sidebar
|
||||
} else {
|
||||
// New component being dragged from components sidebar
|
||||
const offsetFromTopOfWindow = realCanvasBoundingRect.top;
|
||||
const offsetFromLeftOfWindow = realCanvasBoundingRect.left;
|
||||
const zoomLevel = item.zoomLevel;
|
||||
|
||||
x = Math.round(currentOffset.x + (currentOffset.x * (1 - zoomLevel)) - offsetFromLeftOfWindow);
|
||||
y = Math.round(currentOffset.y + (currentOffset.y * (1 - zoomLevel)) - offsetFromTopOfWindow);
|
||||
x = Math.round(currentOffset.x + currentOffset.x * (1 - zoomLevel) - offsetFromLeftOfWindow);
|
||||
y = Math.round(currentOffset.y + currentOffset.y * (1 - zoomLevel) - offsetFromTopOfWindow);
|
||||
}
|
||||
|
||||
[x, y] = snapToGrid(x, y);
|
||||
|
|
@ -49,24 +50,22 @@ function getItemStyles(delta, item, initialOffset, currentOffset, currentLayout)
|
|||
const transform = `translate(${x}px, ${y}px)`;
|
||||
return {
|
||||
transform,
|
||||
WebkitTransform: transform
|
||||
WebkitTransform: transform,
|
||||
};
|
||||
}
|
||||
export const CustomDragLayer = ({ currentLayout }) => {
|
||||
const {
|
||||
itemType, isDragging, item, initialOffset, currentOffset, delta
|
||||
} = useDragLayer((monitor) => ({
|
||||
const { itemType, isDragging, item, initialOffset, currentOffset, delta } = useDragLayer((monitor) => ({
|
||||
item: monitor.getItem(),
|
||||
itemType: monitor.getItemType(),
|
||||
initialOffset: monitor.getInitialSourceClientOffset(),
|
||||
currentOffset: monitor.getSourceClientOffset(),
|
||||
isDragging: monitor.isDragging(),
|
||||
delta: monitor.getDifferenceFromInitialOffset()
|
||||
delta: monitor.getDifferenceFromInitialOffset(),
|
||||
}));
|
||||
function renderItem() {
|
||||
switch (itemType) {
|
||||
case ItemTypes.BOX:
|
||||
return <BoxDragPreview item={item} currentLayout={currentLayout}/>;
|
||||
return <BoxDragPreview item={item} currentLayout={currentLayout} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
@ -78,9 +77,7 @@ export const CustomDragLayer = ({ currentLayout }) => {
|
|||
|
||||
return (
|
||||
<div style={layerStyles}>
|
||||
<div style={getItemStyles(delta, item, initialOffset, currentOffset, currentLayout)}>
|
||||
{renderItem()}
|
||||
</div>
|
||||
<div style={getItemStyles(delta, item, initialOffset, currentOffset, currentLayout)}>{renderItem()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ import { datasourceService, authenticationService } from '@/_services';
|
|||
import Modal from 'react-bootstrap/Modal';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { toast } from 'react-toastify';
|
||||
import { dataBaseSources, apiSources, DataSourceTypes } from './DataSourceTypes';
|
||||
import { defaultOptions } from './DefaultOptions';
|
||||
import { TestConnection } from './TestConnection';
|
||||
import { SourceComponents } from './SourceComponents';
|
||||
import { DataBaseSources, ApiSources, DataSourceTypes, SourceComponents } from './SourceComponents';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import config from 'config';
|
||||
|
||||
|
|
@ -204,7 +203,7 @@ class DataSourceManager extends React.Component {
|
|||
<div>
|
||||
<div className="row row-deck">
|
||||
<h4 className="text-muted mb-2">DATABASES</h4>
|
||||
{dataBaseSources.map((dataSource) => (
|
||||
{DataBaseSources.map((dataSource) => (
|
||||
<div className="col-md-2" key={dataSource.name}>
|
||||
<div className="card mb-3" role="button" onClick={() => this.selectDataSource(dataSource)}>
|
||||
<div className="card-body">
|
||||
|
|
@ -227,7 +226,7 @@ class DataSourceManager extends React.Component {
|
|||
</div>
|
||||
<div className="row row-deck mt-2">
|
||||
<h4 className="text-muted mb-2">APIS</h4>
|
||||
{apiSources.map((dataSource) => (
|
||||
{ApiSources.map((dataSource) => (
|
||||
<div className="col-md-2" key={dataSource.name}>
|
||||
<div className="card" role="button" onClick={() => this.selectDataSource(dataSource)}>
|
||||
<div className="card-body">
|
||||
|
|
@ -292,7 +291,11 @@ class DataSourceManager extends React.Component {
|
|||
|
||||
<div className="col">
|
||||
<small>
|
||||
<a href={`https://docs.tooljet.io/docs/data-sources/${selectedDataSource.kind}`} target="_blank">
|
||||
<a
|
||||
href={`https://docs.tooljet.io/docs/data-sources/${selectedDataSource.kind}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read documentation
|
||||
</a>
|
||||
</small>
|
||||
|
|
|
|||
|
|
@ -1,226 +0,0 @@
|
|||
export const dataBaseSources = [
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
kind: 'postgresql',
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
database: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true }
|
||||
},
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
kind: 'mysql',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
database: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'SQL Server',
|
||||
kind: 'mssql',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
database: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
kind: 'mongodb',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true },
|
||||
connection_type: { type: 'options'},
|
||||
connection_string: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Firestore',
|
||||
kind: 'firestore',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: [],
|
||||
rawData: []
|
||||
},
|
||||
options: {
|
||||
gcp_key: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'DynamoDB',
|
||||
kind: 'dynamodb',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
region: { type: 'string' },
|
||||
access_key: { type: 'string' },
|
||||
secret_key: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Elasticsearch',
|
||||
kind: 'elasticsearch',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Redis',
|
||||
kind: 'redis',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string', encrypted: true }
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const apiSources = [
|
||||
{
|
||||
name: 'Rest API',
|
||||
kind: 'restapi',
|
||||
options: {
|
||||
url: { type: 'string' },
|
||||
auth_type: { type: 'string' },
|
||||
grant_type: { type: 'string' },
|
||||
add_token_to: { type: 'string' },
|
||||
header_prefix: { type: 'string' },
|
||||
access_token_url: { type: 'string' },
|
||||
client_id: { type: 'string' },
|
||||
client_secret: { type: 'string', encrypted: true },
|
||||
scopes: { type: 'string' },
|
||||
auth_url: { type: 'string' },
|
||||
client_auth: { type: 'string' },
|
||||
headers: { type: 'array' },
|
||||
custom_auth_params: { type: 'array' }
|
||||
},
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
customTesting: true
|
||||
},
|
||||
{
|
||||
name: 'GraphQL',
|
||||
kind: 'graphql',
|
||||
options: {
|
||||
url: { type: 'string' },
|
||||
headers: { type: 'array' },
|
||||
url_params: { type: 'array' },
|
||||
body: { type: 'array' },
|
||||
},
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
customTesting: true
|
||||
},
|
||||
{
|
||||
name: 'Stripe',
|
||||
kind: 'stripe',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
api_key: { type: 'string', encrypted: true }
|
||||
},
|
||||
customTesting: true
|
||||
},
|
||||
{
|
||||
name: 'Airtable',
|
||||
kind: 'airtable',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
api_key: { type: 'string', encrypted: true }
|
||||
},
|
||||
customTesting: true
|
||||
},
|
||||
{
|
||||
name: 'Google Sheets',
|
||||
kind: 'googlesheets',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
api_key: { type: 'string', encrypted: true }
|
||||
},
|
||||
customTesting: true,
|
||||
hideSave: true
|
||||
},
|
||||
{
|
||||
name: 'Slack',
|
||||
kind: 'slack',
|
||||
exposedVariables: {
|
||||
isLoading: {},
|
||||
data: {},
|
||||
rawData: {}
|
||||
},
|
||||
options: {
|
||||
api_key: { type: 'string', encrypted: true }
|
||||
},
|
||||
customTesting: true,
|
||||
hideSave: true
|
||||
}
|
||||
];
|
||||
|
||||
export const DataSourceTypes = [
|
||||
...dataBaseSources,
|
||||
...apiSources
|
||||
];
|
||||
|
|
@ -5,7 +5,7 @@ export const defaultOptions = {
|
|||
database: { value: '' },
|
||||
username: { value: '' },
|
||||
password: { value: '' },
|
||||
ssl_enabled: { value: true }
|
||||
ssl_enabled: { value: true },
|
||||
},
|
||||
mysql: {
|
||||
host: { value: 'localhost' },
|
||||
|
|
@ -20,13 +20,13 @@ export const defaultOptions = {
|
|||
port: { value: 1433 },
|
||||
database: { value: '' },
|
||||
username: { value: '' },
|
||||
password: { value: '' }
|
||||
password: { value: '' },
|
||||
},
|
||||
redis: {
|
||||
host: { value: 'localhost' },
|
||||
port: { value: 6379 },
|
||||
username: { value: '' },
|
||||
password: { value: '' }
|
||||
password: { value: '' },
|
||||
},
|
||||
mongodb: {
|
||||
database: { value: '' },
|
||||
|
|
@ -35,7 +35,7 @@ export const defaultOptions = {
|
|||
username: { value: '' },
|
||||
password: { value: '' },
|
||||
connection_type: { value: 'manual' },
|
||||
connection_string: { value: ''}
|
||||
connection_string: { value: '' },
|
||||
},
|
||||
|
||||
elasticsearch: {
|
||||
|
|
@ -43,16 +43,16 @@ export const defaultOptions = {
|
|||
host: { value: 'localhost' },
|
||||
port: { value: 9200 },
|
||||
username: { value: '' },
|
||||
password: { value: '' }
|
||||
password: { value: '' },
|
||||
},
|
||||
stripe: {
|
||||
api_key: { value: '' }
|
||||
api_key: { value: '' },
|
||||
},
|
||||
airtable: {
|
||||
api_key: { value: '' }
|
||||
api_key: { value: '' },
|
||||
},
|
||||
firestore: {
|
||||
gcp_key: { value: '' }
|
||||
gcp_key: { value: '' },
|
||||
},
|
||||
restapi: {
|
||||
url: { value: '' },
|
||||
|
|
@ -67,22 +67,22 @@ export const defaultOptions = {
|
|||
auth_url: { value: '' },
|
||||
client_auth: { value: 'header' },
|
||||
headers: { value: [['', '']] },
|
||||
custom_auth_params: { value: [['', '']] }
|
||||
custom_auth_params: { value: [['', '']] },
|
||||
},
|
||||
graphql: {
|
||||
url: { value: '' },
|
||||
headers: { value: [['', '']] },
|
||||
url_params: { value: [['', '']] }
|
||||
url_params: { value: [['', '']] },
|
||||
},
|
||||
googlesheets: {
|
||||
access_type: { value: 'read' }
|
||||
access_type: { value: 'read' },
|
||||
},
|
||||
slack: {
|
||||
access_type: { value: 'read' }
|
||||
access_type: { value: 'read' },
|
||||
},
|
||||
dynamodb: {
|
||||
region: { value: ''},
|
||||
access_key: { value: ''},
|
||||
secret_key: { value: ''}
|
||||
}
|
||||
region: { value: '' },
|
||||
access_key: { value: '' },
|
||||
secret_key: { value: '' },
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,19 @@
|
|||
"title": "Airtable datasource",
|
||||
"description": "A schema defining airtable datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Airtable",
|
||||
"kind": "airtable",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"api_key": { "type": "string", "encrypted": true }
|
||||
},
|
||||
"customTesting": true
|
||||
},
|
||||
"properties": {
|
||||
"api_key": {
|
||||
"$label": "API key",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,20 @@
|
|||
"title": "Google Sheets datasource",
|
||||
"description": "A schema defining google sheets datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Google Sheets",
|
||||
"kind": "googlesheets",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"api_key": { "type": "string", "encrypted": true }
|
||||
},
|
||||
"customTesting": true,
|
||||
"hideSave": true
|
||||
},
|
||||
"properties": {
|
||||
"sheets": {
|
||||
"$label": "",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,22 @@
|
|||
"title": "Graphql datasource",
|
||||
"description": "A schema defining graphql datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "GraphQL",
|
||||
"kind": "graphql",
|
||||
"options": {
|
||||
"url": { "type": "string" },
|
||||
"headers": { "type": "array" },
|
||||
"url_params": { "type": "array" },
|
||||
"body": { "type": "array" }
|
||||
},
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"customTesting": true
|
||||
},
|
||||
"properties": {
|
||||
"url": {
|
||||
"$label": "URL",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,31 @@
|
|||
"title": "Restapi datasource",
|
||||
"description": "A schema defining restapi datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Rest API",
|
||||
"kind": "restapi",
|
||||
"options": {
|
||||
"url": { "type": "string" },
|
||||
"auth_type": { "type": "string" },
|
||||
"grant_type": { "type": "string" },
|
||||
"add_token_to": { "type": "string" },
|
||||
"header_prefix": { "type": "string" },
|
||||
"access_token_url": { "type": "string" },
|
||||
"client_id": { "type": "string" },
|
||||
"client_secret": { "type": "string", "encrypted": true },
|
||||
"scopes": { "type": "string" },
|
||||
"auth_url": { "type": "string" },
|
||||
"client_auth": { "type": "string" },
|
||||
"headers": { "type": "array" },
|
||||
"custom_auth_params": { "type": "array" }
|
||||
},
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"customTesting": true
|
||||
},
|
||||
"properties": {
|
||||
"url": {
|
||||
"$label": "URL",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,20 @@
|
|||
"title": "Slack datasource",
|
||||
"description": "A schema defining slack datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Slack",
|
||||
"kind": "slack",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"api_key": { "type": "string", "encrypted": true }
|
||||
},
|
||||
"customTesting": true,
|
||||
"hideSave": true
|
||||
},
|
||||
"properties": {
|
||||
"sheets": {
|
||||
"$label": "",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,19 @@
|
|||
"title": "Stripe datasource",
|
||||
"description": "A schema defining stripe datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Stripe",
|
||||
"kind": "stripe",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"api_key": { "type": "string", "encrypted": true }
|
||||
},
|
||||
"customTesting": true
|
||||
},
|
||||
"properties": {
|
||||
"api_key": {
|
||||
"$label": "API key",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,20 @@
|
|||
"title": "Dynamodb datasource",
|
||||
"description": "A schema defining dynamodb datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "DynamoDB",
|
||||
"kind": "dynamodb",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"region": { "type": "string" },
|
||||
"access_key": { "type": "string" },
|
||||
"secret_key": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"region": {
|
||||
"$label": "Region",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,21 @@
|
|||
"title": "Elastic search datasource",
|
||||
"description": "A schema defining elastic search datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Elasticsearch",
|
||||
"kind": "elasticsearch",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"host": {
|
||||
"$label": "Host",
|
||||
|
|
@ -21,13 +36,13 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
}
|
||||
},
|
||||
"required": ["scheme", "host", "port", "password"]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@
|
|||
"title": "Firestore datasource",
|
||||
"description": "A schema defining firestore datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Firestore",
|
||||
"kind": "firestore",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": [],
|
||||
"rawData": []
|
||||
},
|
||||
"options": {
|
||||
"gcp_key": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"gcp_key": {
|
||||
"$label": "Private key",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,23 @@
|
|||
"title": "Mongodb datasource",
|
||||
"description": "A schema defining mongodb datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "MongoDB",
|
||||
"kind": "mongodb",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true },
|
||||
"connection_type": { "type": "options" },
|
||||
"connection_string": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"connection_type": {
|
||||
"$label": "",
|
||||
|
|
@ -38,13 +55,13 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
}
|
||||
},
|
||||
"string": {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,22 @@
|
|||
"title": "Mssql datasource",
|
||||
"description": "A schema defining mssql datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "SQL Server",
|
||||
"kind": "mssql",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"database": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"host": {
|
||||
"$label": "Host",
|
||||
|
|
@ -27,19 +43,19 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
},
|
||||
"azure": {
|
||||
"$label": "Azure",
|
||||
"$key": "azure",
|
||||
"type": "toggle",
|
||||
"description": "Toggle for azure"
|
||||
"description": "Toggle for azure"
|
||||
}
|
||||
},
|
||||
"required": ["host", "port", "username", "password"]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,22 @@
|
|||
"title": "Mysql datasource",
|
||||
"description": "A schema defining mysql datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "MySQL",
|
||||
"kind": "mysql",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"database": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"host": {
|
||||
"$label": "Host",
|
||||
|
|
@ -33,13 +49,13 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
}
|
||||
},
|
||||
"required": ["host", "port", "username", "password"]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,22 @@
|
|||
"title": "Postgresql datasource",
|
||||
"description": "A schema defining postgresql datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "PostgreSQL",
|
||||
"kind": "postgresql",
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"database": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
},
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"host": {
|
||||
"$label": "Host",
|
||||
|
|
@ -33,13 +49,13 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
}
|
||||
},
|
||||
"required": ["host", "port", "username", "database", "password"]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,21 @@
|
|||
"title": "Redis datasource",
|
||||
"description": "A schema defining redis datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "Redis",
|
||||
"kind": "redis",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"host": { "type": "string" },
|
||||
"port": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"host": {
|
||||
"$label": "Host",
|
||||
|
|
@ -21,13 +36,13 @@
|
|||
"$label": "Username",
|
||||
"$key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username field"
|
||||
"description": "Enter username"
|
||||
},
|
||||
"password": {
|
||||
"$label": "Password",
|
||||
"$key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password field"
|
||||
"description": "Enter password"
|
||||
}
|
||||
},
|
||||
"required": ["scheme", "host", "port", "password"]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export const Mysql = ({
|
||||
optionchanged, options
|
||||
}) => {
|
||||
export const Mysql = ({ optionchanged, options }) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="row">
|
||||
|
|
@ -25,9 +23,7 @@ export const Mysql = ({
|
|||
/>
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<label className="form-label">
|
||||
SSL
|
||||
</label>
|
||||
<label className="form-label">SSL</label>
|
||||
<label className="form-check form-switch mt-3">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
@ -61,8 +57,13 @@ export const Mysql = ({
|
|||
<label className="form-label">
|
||||
Password
|
||||
<small className="text-green mx-2">
|
||||
<img className="mx-2 encrypted-icon encrypted-icon" src="/assets/images/icons/padlock.svg" width="12" height="12" />
|
||||
<span className="pt-2">Encrypted</span>
|
||||
<img
|
||||
className="mx-2 encrypted-icon encrypted-icon"
|
||||
src="/assets/images/icons/padlock.svg"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
<span className="pt-2">Encrypted</span>
|
||||
</small>
|
||||
</label>
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -1,38 +1,76 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import DynamicForm from '@/_components/DynamicForm';
|
||||
import DynamicForm from "@/_components/DynamicForm";
|
||||
|
||||
import AirtableSchema from './Api/Airtable.schema.json';
|
||||
import RestapiSchema from './Api/Restapi.schema.json';
|
||||
import GraphqlSchema from './Api/Graphql.schema.json';
|
||||
import StripeSchema from './Api/Stripe.schema.json';
|
||||
import GooglesheetSchema from './Api/Googlesheets.schema.json';
|
||||
import SlackSchema from './Api/Slack.schema.json';
|
||||
import AirtableSchema from "./Api/Airtable.schema.json";
|
||||
import RestapiSchema from "./Api/Restapi.schema.json";
|
||||
import GraphqlSchema from "./Api/Graphql.schema.json";
|
||||
import StripeSchema from "./Api/Stripe.schema.json";
|
||||
import GooglesheetSchema from "./Api/Googlesheets.schema.json";
|
||||
import SlackSchema from "./Api/Slack.schema.json";
|
||||
|
||||
import DynamodbSchema from './Database/Dynamodb.schema.json';
|
||||
import ElasticsearchSchema from './Database/Elasticsearch.schema.json';
|
||||
import RedisSchema from './Database/Redis.schema.json';
|
||||
import FirestoreSchema from './Database/Firestore.schema.json';
|
||||
import MongodbSchema from './Database/Mongodb.schema.json';
|
||||
import PostgresqlSchema from './Database/Postgresql.schema.json';
|
||||
import MysqlSchema from './Database/Mysql.schema.json';
|
||||
import MssqlSchema from './Database/Mssql.schema.json';
|
||||
import DynamodbSchema from "./Database/Dynamodb.schema.json";
|
||||
import ElasticsearchSchema from "./Database/Elasticsearch.schema.json";
|
||||
import RedisSchema from "./Database/Redis.schema.json";
|
||||
import FirestoreSchema from "./Database/Firestore.schema.json";
|
||||
import MongodbSchema from "./Database/Mongodb.schema.json";
|
||||
import PostgresqlSchema from "./Database/Postgresql.schema.json";
|
||||
import MysqlSchema from "./Database/Mysql.schema.json";
|
||||
import MssqlSchema from "./Database/Mssql.schema.json";
|
||||
|
||||
const Airtable = ({ ...rest }) => <DynamicForm schema={AirtableSchema} {...rest} />;
|
||||
const Restapi = ({ ...rest }) => <DynamicForm schema={RestapiSchema} {...rest} />;
|
||||
const Graphql = ({ ...rest }) => <DynamicForm schema={GraphqlSchema} {...rest} />;
|
||||
const Airtable = ({ ...rest }) => (
|
||||
<DynamicForm schema={AirtableSchema} {...rest} />
|
||||
);
|
||||
const Restapi = ({ ...rest }) => (
|
||||
<DynamicForm schema={RestapiSchema} {...rest} />
|
||||
);
|
||||
const Graphql = ({ ...rest }) => (
|
||||
<DynamicForm schema={GraphqlSchema} {...rest} />
|
||||
);
|
||||
const Stripe = ({ ...rest }) => <DynamicForm schema={StripeSchema} {...rest} />;
|
||||
const Googlesheets = ({ ...rest }) => <DynamicForm schema={GooglesheetSchema} {...rest} />;
|
||||
const Googlesheets = ({ ...rest }) => (
|
||||
<DynamicForm schema={GooglesheetSchema} {...rest} />
|
||||
);
|
||||
const Slack = ({ ...rest }) => <DynamicForm schema={SlackSchema} {...rest} />;
|
||||
const Dynamodb = ({ ...rest }) => <DynamicForm schema={DynamodbSchema} {...rest} />;
|
||||
const Elasticsearch = ({ ...rest }) => <DynamicForm schema={ElasticsearchSchema} {...rest} />;
|
||||
const Dynamodb = ({ ...rest }) => (
|
||||
<DynamicForm schema={DynamodbSchema} {...rest} />
|
||||
);
|
||||
const Elasticsearch = ({ ...rest }) => (
|
||||
<DynamicForm schema={ElasticsearchSchema} {...rest} />
|
||||
);
|
||||
const Redis = ({ ...rest }) => <DynamicForm schema={RedisSchema} {...rest} />;
|
||||
const Firestore = ({ ...rest }) => <DynamicForm schema={FirestoreSchema} {...rest} />;
|
||||
const Mongodb = ({ ...rest }) => <DynamicForm schema={MongodbSchema} {...rest} />;
|
||||
const Postgresql = ({ ...rest }) => <DynamicForm schema={PostgresqlSchema} {...rest} />;
|
||||
const Firestore = ({ ...rest }) => (
|
||||
<DynamicForm schema={FirestoreSchema} {...rest} />
|
||||
);
|
||||
const Mongodb = ({ ...rest }) => (
|
||||
<DynamicForm schema={MongodbSchema} {...rest} />
|
||||
);
|
||||
const Postgresql = ({ ...rest }) => (
|
||||
<DynamicForm schema={PostgresqlSchema} {...rest} />
|
||||
);
|
||||
const Mysql = ({ ...rest }) => <DynamicForm schema={MysqlSchema} {...rest} />;
|
||||
const Mssql = ({ ...rest }) => <DynamicForm schema={MssqlSchema} {...rest} />;
|
||||
|
||||
export const DataBaseSources = [
|
||||
DynamodbSchema.source,
|
||||
ElasticsearchSchema.source,
|
||||
RedisSchema.source,
|
||||
FirestoreSchema.source,
|
||||
MongodbSchema.source,
|
||||
PostgresqlSchema.source,
|
||||
MysqlSchema.source,
|
||||
MssqlSchema.source,
|
||||
];
|
||||
export const ApiSources = [
|
||||
AirtableSchema.source,
|
||||
RestapiSchema.source,
|
||||
GraphqlSchema.source,
|
||||
StripeSchema.source,
|
||||
GooglesheetSchema.source,
|
||||
SlackSchema.source,
|
||||
];
|
||||
export const DataSourceTypes = [...DataBaseSources, ...ApiSources];
|
||||
|
||||
export const SourceComponents = {
|
||||
Elasticsearch,
|
||||
Redis,
|
||||
|
|
@ -47,5 +85,5 @@ export const SourceComponents = {
|
|||
Airtable,
|
||||
Graphql,
|
||||
Mysql,
|
||||
Mssql
|
||||
Mssql,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { toast, ToastContainer } from 'react-toastify';
|
||||
import { datasourceService } from '@/_services';
|
||||
|
||||
export const TestConnection = ({ kind, options, onConnectionTestFailed }) => {
|
||||
|
|
@ -29,7 +28,7 @@ export const TestConnection = ({ kind, options, onConnectionTestFailed }) => {
|
|||
datasourceService.test(kind, options).then(
|
||||
(data) => {
|
||||
setTestingStatus(false);
|
||||
if(data.status === 'ok') {
|
||||
if (data.status === 'ok') {
|
||||
setConnectionStatus('success');
|
||||
} else {
|
||||
setConnectionStatus('failed');
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useDrag } from 'react-dnd';
|
||||
import { ItemTypes } from './ItemTypes';
|
||||
import { getEmptyImage } from 'react-dnd-html5-backend';
|
||||
import { Box } from './Box';
|
||||
import { Resizable } from 're-resizable';
|
||||
import { ConfigHandle } from './ConfigHandle';
|
||||
import { Rnd } from 'react-rnd';
|
||||
|
||||
|
|
@ -45,14 +45,13 @@ function getStyles(left, top, isDragging, component, isSelectedComponent) {
|
|||
// const transform = `translate3d(${left}px, ${top}px, 0)`;
|
||||
return {
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
// transform,
|
||||
// WebkitTransform: transform,
|
||||
zIndex: isSelectedComponent ? 2 : 1,
|
||||
// IE fallback: hide the real node using CSS when dragging
|
||||
// because IE will ignore our custom "empty image" drag preview.
|
||||
opacity: isDragging ? 0 : 1,
|
||||
height: isDragging ? 0 : '',
|
||||
height: isDragging ? 0 : '100%',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +61,6 @@ export const DraggableBox = function DraggableBox({
|
|||
title,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
parent,
|
||||
component,
|
||||
index,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { CustomDragLayer } from './CustomDragLayer';
|
|||
import { LeftSidebar } from './LeftSidebar';
|
||||
import { componentTypes } from './Components/components';
|
||||
import { Inspector } from './Inspector/Inspector';
|
||||
import { DataSourceTypes } from './DataSourceManager/DataSourceTypes';
|
||||
import { DataSourceTypes } from './DataSourceManager/SourceComponents';
|
||||
import { QueryManager } from './QueryManager';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
|
@ -22,7 +22,7 @@ import {
|
|||
onQueryCancel,
|
||||
runQuery,
|
||||
setStateAsync,
|
||||
computeComponentState
|
||||
computeComponentState,
|
||||
} from '@/_helpers/appUtils';
|
||||
import { Confirm } from './Viewer/Confirm';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
|
@ -74,7 +74,7 @@ class Editor extends React.Component {
|
|||
currentUser: userVars,
|
||||
urlparams: JSON.parse(JSON.stringify(queryString.parse(props.location.search))),
|
||||
},
|
||||
errors: {}
|
||||
errors: {},
|
||||
},
|
||||
apps: [],
|
||||
dataQueriesDefaultText: "You haven't created queries yet.",
|
||||
|
|
@ -89,7 +89,7 @@ class Editor extends React.Component {
|
|||
this.fetchApps(0);
|
||||
|
||||
appService.getApp(appId).then((data) => {
|
||||
const dataDefinition = data.definition || {components: {}}
|
||||
const dataDefinition = data.definition || { components: {} };
|
||||
this.setState(
|
||||
{
|
||||
app: data,
|
||||
|
|
@ -107,7 +107,7 @@ class Editor extends React.Component {
|
|||
|
||||
computeComponentState(this, this.state.appDefinition.components);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
this.fetchDataSources();
|
||||
|
|
@ -192,11 +192,11 @@ class Editor extends React.Component {
|
|||
};
|
||||
|
||||
setAppDefinitionFromVersion = (version) => {
|
||||
this.appDefinitionChanged(version.definition || {components: {}})
|
||||
this.appDefinitionChanged(version.definition || { components: {} });
|
||||
this.setState({
|
||||
editingVersion: version
|
||||
})
|
||||
}
|
||||
editingVersion: version,
|
||||
});
|
||||
};
|
||||
|
||||
dataSourcesChanged = () => {
|
||||
this.fetchDataSources();
|
||||
|
|
@ -274,7 +274,7 @@ class Editor extends React.Component {
|
|||
...this.state.appDefinition.components,
|
||||
[newDefinition.id]: {
|
||||
...this.state.appDefinition.components[newDefinition.id],
|
||||
component: newDefinition.component
|
||||
component: newDefinition.component,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -328,11 +328,11 @@ class Editor extends React.Component {
|
|||
|
||||
deleteDataQuery = () => {
|
||||
this.setState({ showDataQueryDeletionConfirmation: true });
|
||||
}
|
||||
};
|
||||
|
||||
cancelDeleteDataQuery = () => {
|
||||
this.setState({ showDataQueryDeletionConfirmation: false});
|
||||
}
|
||||
this.setState({ showDataQueryDeletionConfirmation: false });
|
||||
};
|
||||
|
||||
executeDataQueryDeletion = () => {
|
||||
this.setState({ showDataQueryDeletionConfirmation: false, isDeletingDataQuery: true });
|
||||
|
|
@ -351,7 +351,7 @@ class Editor extends React.Component {
|
|||
|
||||
setShowHiddenOptionsForDataQuery = (dataQueryId) => {
|
||||
this.setState({ showHiddenOptionsForDataQueryId: dataQueryId });
|
||||
}
|
||||
};
|
||||
|
||||
renderDataQuery = (dataQuery) => {
|
||||
const sourceMeta = DataSourceTypes.find((source) => source.kind === dataQuery.kind);
|
||||
|
|
@ -360,7 +360,7 @@ class Editor extends React.Component {
|
|||
if (this.state.selectedQuery) {
|
||||
isSeletedQuery = dataQuery.id === this.state.selectedQuery.id;
|
||||
}
|
||||
const isQueryBeingDeleted = this.state.isDeletingDataQuery && isSeletedQuery
|
||||
const isQueryBeingDeleted = this.state.isDeletingDataQuery && isSeletedQuery;
|
||||
const { currentState } = this.state;
|
||||
|
||||
const isLoading = currentState.queries[dataQuery.name] ? currentState.queries[dataQuery.name].isLoading : false;
|
||||
|
|
@ -376,6 +376,7 @@ class Editor extends React.Component {
|
|||
>
|
||||
<div className="col">
|
||||
<img
|
||||
className="svg-icon"
|
||||
src={`/assets/images/icons/editor/datasources/${sourceMeta.kind.toLowerCase()}.svg`}
|
||||
width="20"
|
||||
height="20"
|
||||
|
|
@ -383,7 +384,7 @@ class Editor extends React.Component {
|
|||
<span className="p-3">{dataQuery.name}</span>
|
||||
</div>
|
||||
<div className="col-auto mx-1">
|
||||
{ isQueryBeingDeleted ? (
|
||||
{isQueryBeingDeleted ? (
|
||||
<div className="px-2">
|
||||
<div className="text-center spinner-border spinner-border-sm" role="status"></div>
|
||||
</div>
|
||||
|
|
@ -518,7 +519,7 @@ class Editor extends React.Component {
|
|||
showDataQueryDeletionConfirmation,
|
||||
isDeletingDataQuery,
|
||||
apps,
|
||||
defaultComponentStateComputed
|
||||
defaultComponentStateComputed,
|
||||
} = this.state;
|
||||
const appLink = slug ? `/applications/${slug}` : '';
|
||||
|
||||
|
|
@ -651,7 +652,7 @@ class Editor extends React.Component {
|
|||
app={app}
|
||||
darkMode={this.props.darkMode}
|
||||
onVersionDeploy={this.onVersionDeploy}
|
||||
editingVersionId={this.state.editingVersion ? this.state.editingVersion.id : null }
|
||||
editingVersionId={this.state.editingVersion ? this.state.editingVersion.id : null}
|
||||
setAppDefinitionFromVersion={this.setAppDefinitionFromVersion}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -679,7 +680,7 @@ class Editor extends React.Component {
|
|||
style={{ transform: `scale(${zoomLevel})` }}
|
||||
>
|
||||
<div className="canvas-area" style={{ width: currentLayout === 'desktop' ? '1292px' : '450px' }}>
|
||||
{defaultComponentStateComputed &&
|
||||
{defaultComponentStateComputed && (
|
||||
<Container
|
||||
appDefinition={appDefinition}
|
||||
appDefinitionChanged={this.appDefinitionChanged}
|
||||
|
|
@ -708,7 +709,7 @@ class Editor extends React.Component {
|
|||
onComponentClick(this, id, component);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
<CustomDragLayer snapToGrid={true} currentLayout={currentLayout} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -728,19 +729,25 @@ class Editor extends React.Component {
|
|||
<h5 className="py-1 px-3 text-muted">QUERIES</h5>
|
||||
</div>
|
||||
<div className="col-auto px-3">
|
||||
<button className="btn btn-sm btn-light mx-2" onClick={this.toggleQuerySearch}>
|
||||
<button
|
||||
className="btn btn-sm btn-light mx-2"
|
||||
data-class="py-1 px-2"
|
||||
data-tip="Search query"
|
||||
onClick={this.toggleQuerySearch}>
|
||||
<img className="py-1" src="/assets/images/icons/lens.svg" width="17" height="17" />
|
||||
</button>
|
||||
|
||||
<span
|
||||
data-tip="Add new query"
|
||||
data-class="py-1 px-2"
|
||||
className="btn btn-sm btn-light text-muted"
|
||||
onClick={() => this.setState({
|
||||
selectedQuery: {},
|
||||
editingQuery: false,
|
||||
addingQuery: true
|
||||
})}
|
||||
className="btn btn-sm btn-light btn-px-1 text-muted"
|
||||
onClick={() =>
|
||||
this.setState({
|
||||
selectedQuery: {},
|
||||
editingQuery: false,
|
||||
addingQuery: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
+
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -2,44 +2,39 @@ import React, { useState, useEffect } from 'react';
|
|||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
|
||||
export function GotoApp({
|
||||
getAllApps,
|
||||
currentState,
|
||||
event,
|
||||
handlerChanged,
|
||||
eventIndex
|
||||
}) {
|
||||
export function GotoApp({ getAllApps, currentState, event, handlerChanged, eventIndex }) {
|
||||
const queryParamChangeHandler = (index, key, value) => {
|
||||
event.queryParams[index][key] = value
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams)
|
||||
}
|
||||
event.queryParams[index][key] = value;
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams);
|
||||
};
|
||||
|
||||
const addQueryParam = () => {
|
||||
if (!event.queryParams) {
|
||||
event.queryParams = []
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams)
|
||||
event.queryParams = [];
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams);
|
||||
}
|
||||
|
||||
event.queryParams.push(['', ''])
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams)
|
||||
setNumberOfQueryparams(numberOfQueryParams + 1)
|
||||
}
|
||||
event.queryParams.push(['', '']);
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams);
|
||||
setNumberOfQueryparams(numberOfQueryParams + 1);
|
||||
};
|
||||
|
||||
const deleteQueryParam = index => {
|
||||
event.queryParams.splice(index, 1)
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams)
|
||||
setNumberOfQueryparams(numberOfQueryParams - 1)
|
||||
}
|
||||
const deleteQueryParam = (index) => {
|
||||
event.queryParams.splice(index, 1);
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams);
|
||||
setNumberOfQueryparams(numberOfQueryParams - 1);
|
||||
};
|
||||
|
||||
const [numberOfQueryParams, setNumberOfQueryparams] = useState(0)
|
||||
const [numberOfQueryParams, setNumberOfQueryparams] = useState(0);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => {
|
||||
if (event.queryParams) {
|
||||
setNumberOfQueryparams(event.queryParams.length)
|
||||
setNumberOfQueryparams(event.queryParams.length);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return(
|
||||
return (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">App</label>
|
||||
<SelectSearch
|
||||
|
|
@ -54,38 +49,37 @@ export function GotoApp({
|
|||
/>
|
||||
<label className="form-label mt-2">Query params</label>
|
||||
|
||||
{Array(numberOfQueryParams).fill(0).map((_, index) =>
|
||||
<div key={index}>
|
||||
<div className="input-group mt-1">
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.queryParams[index][0]}
|
||||
onChange={(value) => queryParamChangeHandler(index, 0, value)}
|
||||
mode='javascript'
|
||||
className="form-control codehinter-query-editor-input"
|
||||
height={30}
|
||||
/>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.queryParams[index][1]}
|
||||
onChange={(value) => queryParamChangeHandler(index, 1, value)}
|
||||
mode='javascript'
|
||||
className="form-control codehinter-query-editor-input"
|
||||
height={30}
|
||||
/>
|
||||
<span
|
||||
className="input-group-text btn-sm"
|
||||
role="button"
|
||||
onClick={() => deleteQueryParam(index)}
|
||||
>x</span>
|
||||
{Array(numberOfQueryParams)
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
<div key={index}>
|
||||
<div className="input-group mt-1">
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.queryParams[index][0]}
|
||||
onChange={(value) => queryParamChangeHandler(index, 0, value)}
|
||||
mode="javascript"
|
||||
className="form-control codehinter-query-editor-input"
|
||||
height={30}
|
||||
/>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.queryParams[index][1]}
|
||||
onChange={(value) => queryParamChangeHandler(index, 1, value)}
|
||||
mode="javascript"
|
||||
className="form-control codehinter-query-editor-input"
|
||||
height={30}
|
||||
/>
|
||||
<span className="input-group-text btn-sm" role="button" onClick={() => deleteQueryParam(index)}>
|
||||
x
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
|
||||
<button
|
||||
className="btn btn-sm btn-outline-azure mt-2 mx-0 mb-0"
|
||||
onClick={addQueryParam}
|
||||
>+</button>
|
||||
<button className="btn btn-sm btn-outline-azure mt-2 mx-0 mb-0" onClick={addQueryParam}>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { renderElement } from '../Utils';
|
||||
import { Color } from '../Elements/Color';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
|
||||
class Chart extends React.Component {
|
||||
|
|
@ -8,7 +7,14 @@ class Chart extends React.Component {
|
|||
super(props);
|
||||
|
||||
const {
|
||||
dataQueries, component, paramUpdated, componentMeta, eventUpdated, eventOptionUpdated, components, currentState
|
||||
dataQueries,
|
||||
component,
|
||||
paramUpdated,
|
||||
componentMeta,
|
||||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState,
|
||||
} = props;
|
||||
|
||||
this.state = {
|
||||
|
|
@ -19,7 +25,7 @@ class Chart extends React.Component {
|
|||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
currentState,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +38,7 @@ class Chart extends React.Component {
|
|||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
currentState,
|
||||
} = this.props;
|
||||
|
||||
this.setState({
|
||||
|
|
@ -43,48 +49,67 @@ class Chart extends React.Component {
|
|||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
currentState,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
dataQueries,
|
||||
component,
|
||||
paramUpdated,
|
||||
componentMeta,
|
||||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
} = this.state;
|
||||
const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.state;
|
||||
|
||||
const data = this.state.component.component.definition.properties.data;
|
||||
|
||||
return (
|
||||
<div className="properties-container p-2">
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'title', 'properties', currentState, components)}
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'type', 'properties', currentState, components)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'title',
|
||||
'properties',
|
||||
currentState,
|
||||
components
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'type',
|
||||
'properties',
|
||||
currentState,
|
||||
components
|
||||
)}
|
||||
|
||||
<div className="field mb-3 chart-data-input">
|
||||
<label className="form-label">Chart data</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={data.value}
|
||||
theme={this.props.darkMode ? 'monokai' : 'duotone-light'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
className="chart-input pr-2"
|
||||
onChange={(value) => this.props.paramUpdated({ name: 'data' }, 'value', value, 'properties')}
|
||||
/>
|
||||
currentState={this.props.currentState}
|
||||
initialValue={data.value}
|
||||
theme={this.props.darkMode ? 'monokai' : 'duotone-light'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
className="chart-input pr-2"
|
||||
onChange={(value) => this.props.paramUpdated({ name: 'data' }, 'value', value, 'properties')}
|
||||
/>
|
||||
</div>
|
||||
{Object.keys(componentMeta.styles).map((style) => renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components))}
|
||||
{Object.keys(componentMeta.styles).map((style) =>
|
||||
renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components)
|
||||
)}
|
||||
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'loadingState', 'properties', currentState)}
|
||||
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'markerColor', 'properties', currentState)}
|
||||
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'showGridLines', 'properties', currentState)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'showGridLines',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|||
import Popover from 'react-bootstrap/Popover';
|
||||
import { Color } from '../Elements/Color';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EventManager } from '../EventManager';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
|
||||
|
|
@ -16,7 +16,14 @@ class Table extends React.Component {
|
|||
super(props);
|
||||
|
||||
const {
|
||||
dataQueries, component, paramUpdated, componentMeta, eventUpdated, eventOptionUpdated, components, currentState
|
||||
dataQueries,
|
||||
component,
|
||||
paramUpdated,
|
||||
componentMeta,
|
||||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState,
|
||||
} = props;
|
||||
|
||||
this.state = {
|
||||
|
|
@ -29,7 +36,7 @@ class Table extends React.Component {
|
|||
components,
|
||||
currentState,
|
||||
actionPopOverRootClose: true,
|
||||
showPopOver: false
|
||||
showPopOver: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +49,7 @@ class Table extends React.Component {
|
|||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
currentState,
|
||||
} = this.props;
|
||||
|
||||
this.setState({
|
||||
|
|
@ -53,7 +60,7 @@ class Table extends React.Component {
|
|||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
currentState,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -65,9 +72,9 @@ class Table extends React.Component {
|
|||
|
||||
actionButtonEventsChanged = (events, index) => {
|
||||
let actions = this.props.component.component.definition.properties.actions.value;
|
||||
actions[index]['events'] = events
|
||||
actions[index]['events'] = events;
|
||||
this.props.paramUpdated({ name: 'actions' }, 'value', actions, 'properties');
|
||||
}
|
||||
};
|
||||
|
||||
actionButtonEventUpdated = (event, value, extraData) => {
|
||||
const actions = this.props.component.component.definition.properties.actions;
|
||||
|
|
@ -75,7 +82,7 @@ class Table extends React.Component {
|
|||
|
||||
let newValues = actions.value;
|
||||
newValues[index][event.name] = {
|
||||
actionId: value
|
||||
actionId: value,
|
||||
};
|
||||
|
||||
this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties');
|
||||
|
|
@ -90,7 +97,7 @@ class Table extends React.Component {
|
|||
|
||||
newValues[index][event.name].options = {
|
||||
...options,
|
||||
[option]: value
|
||||
[option]: value,
|
||||
};
|
||||
|
||||
this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties');
|
||||
|
|
@ -114,7 +121,7 @@ class Table extends React.Component {
|
|||
{ name: 'Radio', value: 'radio' },
|
||||
{ name: 'Multiselect', value: 'multiselect' },
|
||||
{ name: 'Toggle switch', value: 'toggle' },
|
||||
{ name: 'Date Picker', value: 'datepicker' }
|
||||
{ name: 'Date Picker', value: 'datepicker' },
|
||||
]}
|
||||
value={column.columnType}
|
||||
search={true}
|
||||
|
|
@ -152,93 +159,95 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') &&
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Text color</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.textColor}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'Text color of the cell'}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'textColor', value)}
|
||||
/>
|
||||
</div>
|
||||
{column.isEditable &&
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Regex</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.regex}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'regex', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Min length</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.minLength}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'minLength', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Max length</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.maxLength}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'maxLength', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'customRule', value)}
|
||||
/>
|
||||
</div>
|
||||
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') && (
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Text color</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.textColor}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'Text color of the cell'}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'textColor', value)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
}
|
||||
{column.isEditable && (
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Regex</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.regex}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'regex', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Min length</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.minLength}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'minLength', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Max length</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.maxLength}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'maxLength', value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'customRule', value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{column.columnType === 'toggle' &&
|
||||
{column.columnType === 'toggle' && (
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<Color
|
||||
param={{ name: 'Active color' }}
|
||||
paramType="properties"
|
||||
componentMeta={{ properties: { color: { displayName: 'Active color'} } }}
|
||||
componentMeta={{ properties: { color: { displayName: 'Active color' } } }}
|
||||
definition={{ value: column.activeColor || '#3c92dc' }}
|
||||
onChange={(name, value, color) => this.onColumnItemChange(index, 'activeColor', color)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
{(column.columnType === 'dropdown' || column.columnType === 'multiselect' || column.columnType === 'badge' || column.columnType === 'badges' || column.columnType === 'radio') && (
|
||||
{(column.columnType === 'dropdown' ||
|
||||
column.columnType === 'multiselect' ||
|
||||
column.columnType === 'badge' ||
|
||||
column.columnType === 'badges' ||
|
||||
column.columnType === 'radio') && (
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Values</label>
|
||||
|
|
@ -246,7 +255,7 @@ class Table extends React.Component {
|
|||
currentState={this.props.currentState}
|
||||
initialValue={column.values}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'{{[1, 2, 3]}}'}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'values', value)}
|
||||
|
|
@ -258,7 +267,7 @@ class Table extends React.Component {
|
|||
currentState={this.props.currentState}
|
||||
initialValue={column.labels}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'{{["one", "two", "three"]}}'}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'labels', value)}
|
||||
|
|
@ -267,27 +276,27 @@ class Table extends React.Component {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{column.columnType === 'dropdown' &&
|
||||
{column.columnType === 'dropdown' && (
|
||||
<>
|
||||
{column.isEditable &&
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode= "javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'customRule', value)}
|
||||
/>
|
||||
{column.isEditable && (
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={''}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'customRule', value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
}
|
||||
)}
|
||||
|
||||
{column.columnType === 'datepicker' && (
|
||||
<div>
|
||||
|
|
@ -304,22 +313,20 @@ class Table extends React.Component {
|
|||
placeholder={'DD-MM-YYYY'}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<div className="field mb-2">
|
||||
<label className="form-check form-switch my-2">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
onClick={() => {
|
||||
this.onColumnItemChange(index, 'isTimeChecked', !column.isTimeChecked)
|
||||
this.onColumnItemChange(index, 'isTimeChecked', !column.isTimeChecked);
|
||||
}}
|
||||
checked={column.isTimeChecked}
|
||||
/>
|
||||
<span className="form-check-label">show time</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
)}
|
||||
|
||||
<label className="form-check form-switch my-4">
|
||||
|
|
@ -340,10 +347,10 @@ class Table extends React.Component {
|
|||
const dummyComponentForActionButton = {
|
||||
component: {
|
||||
definition: {
|
||||
events: this.props.component.component.definition.properties.actions.value[index].events || []
|
||||
}
|
||||
}
|
||||
}
|
||||
events: this.props.component.component.definition.properties.actions.value[index].events || [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover id="popover-basic">
|
||||
|
|
@ -370,7 +377,7 @@ class Table extends React.Component {
|
|||
value={action.position ?? 'right'}
|
||||
search={false}
|
||||
closeOnSelect={true}
|
||||
onChange={value => {
|
||||
onChange={(value) => {
|
||||
this.onActionButtonPropertyChanged(index, 'position', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
|
|
@ -394,15 +401,15 @@ class Table extends React.Component {
|
|||
/>
|
||||
<EventManager
|
||||
component={dummyComponentForActionButton}
|
||||
componentMeta={{events: { onClick: {displayName: 'On click' }}}}
|
||||
componentMeta={{ events: { onClick: { displayName: 'On click' } } }}
|
||||
currentState={this.state.currentState}
|
||||
dataQueries={this.props.dataQueries}
|
||||
components={this.props.components}
|
||||
eventsChanged={events => this.actionButtonEventsChanged(events, index)}
|
||||
eventsChanged={(events) => this.actionButtonEventsChanged(events, index)}
|
||||
apps={this.props.apps}
|
||||
popOverCallback={(showing) => {
|
||||
this.setState({actionPopOverRootClose: !showing})
|
||||
this.setState({showPopOver: showing})
|
||||
this.setState({ actionPopOverRootClose: !showing });
|
||||
this.setState({ showPopOver: showing });
|
||||
}}
|
||||
/>
|
||||
<button className="btn btn-sm btn-outline-danger mt-2 col" onClick={() => this.removeAction(index)}>
|
||||
|
|
@ -420,7 +427,7 @@ class Table extends React.Component {
|
|||
placement="left"
|
||||
rootClose={this.state.actionPopOverRootClose}
|
||||
overlay={this.actionPopOver(action, index)}
|
||||
onToggle={showing => this.setState({showPopOver: showing})}
|
||||
onToggle={(showing) => this.setState({ showPopOver: showing })}
|
||||
>
|
||||
<div className={`card p-2 ${this.props.darkMode ? 'bg-secondary' : 'bg-light'}`} role="button">
|
||||
<div className={`row ${this.props.darkMode ? '' : 'bg-light'}`}>
|
||||
|
|
@ -446,14 +453,14 @@ class Table extends React.Component {
|
|||
|
||||
while (!found) {
|
||||
columnName = `new_column${currentNumber}`;
|
||||
if (columns.find(column => column.name === columnName) === undefined) {
|
||||
if (columns.find((column) => column.name === columnName) === undefined) {
|
||||
found = true;
|
||||
}
|
||||
currentNumber += 1;
|
||||
}
|
||||
|
||||
return columnName;
|
||||
}
|
||||
};
|
||||
|
||||
addNewColumn = () => {
|
||||
const columns = this.props.component.component.definition.properties.columns;
|
||||
|
|
@ -506,16 +513,7 @@ class Table extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
dataQueries,
|
||||
component,
|
||||
paramUpdated,
|
||||
componentMeta,
|
||||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
components,
|
||||
currentState
|
||||
} = this.props;
|
||||
const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.props;
|
||||
|
||||
const columns = component.component.definition.properties.columns;
|
||||
const actions = component.component.definition.properties.actions || { value: [] };
|
||||
|
|
@ -527,7 +525,16 @@ class Table extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="properties-container p-2 " key={this.props.component.id}>
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'data', 'properties', currentState, components)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'data',
|
||||
'properties',
|
||||
currentState,
|
||||
components
|
||||
)}
|
||||
|
||||
<div className="field mb-2 mt-3">
|
||||
<div className="row g-2">
|
||||
|
|
@ -561,7 +568,13 @@ class Table extends React.Component {
|
|||
<div className="text">{item.name}</div>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<img onClick={() => this.removeColumn(index)} className="svg-icon" src="/assets/images/icons/trash.svg" width="12" height="12"/>
|
||||
<img
|
||||
onClick={() => this.removeColumn(index)}
|
||||
className="svg-icon"
|
||||
src="/assets/images/icons/trash.svg"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
|
|
@ -583,21 +596,92 @@ class Table extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
<div>{actions.value.map((action, index) => this.actionButton(action, index))}</div>
|
||||
{actions.value.length === 0 &&
|
||||
<center><small>This table doesn't have any action buttons</small></center>
|
||||
}
|
||||
{actions.value.length === 0 && (
|
||||
<center>
|
||||
<small>This table doesn't have any action buttons</small>
|
||||
</center>
|
||||
)}
|
||||
</div>
|
||||
<hr></hr>
|
||||
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'serverSidePagination', 'properties', currentState)}
|
||||
{!serverSidePagination && renderElement(component, componentMeta, paramUpdated, dataQueries, 'clientSidePagination', 'properties', currentState)}
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'displaySearchBox', 'properties', currentState)}
|
||||
{displaySearchBox && renderElement(component, componentMeta, paramUpdated, dataQueries, 'serverSideSearch', 'properties', currentState)}
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'showDownloadButton', 'properties', currentState)}
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'showFilterButton', 'properties', currentState)}
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'showBulkUpdateActions', 'properties', currentState)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'serverSidePagination',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{!serverSidePagination &&
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'clientSidePagination',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'displaySearchBox',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{displaySearchBox &&
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'serverSideSearch',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'showDownloadButton',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'showFilterButton',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'showBulkUpdateActions',
|
||||
'properties',
|
||||
currentState
|
||||
)}
|
||||
|
||||
{Object.keys(componentMeta.styles).map((style) => renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components))}
|
||||
{Object.keys(componentMeta.styles).map((style) =>
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
style,
|
||||
'styles',
|
||||
currentState,
|
||||
components
|
||||
)
|
||||
)}
|
||||
<div className="hr-text">Events</div>
|
||||
|
||||
<EventManager
|
||||
|
|
|
|||
|
|
@ -2,17 +2,7 @@ import React from 'react';
|
|||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
|
||||
export const Code = ({
|
||||
param,
|
||||
definition,
|
||||
onChange,
|
||||
paramType,
|
||||
dataQueries,
|
||||
components,
|
||||
componentMeta,
|
||||
currentState,
|
||||
darkMode,
|
||||
}) => {
|
||||
export const Code = ({ param, definition, onChange, paramType, componentMeta, currentState, darkMode }) => {
|
||||
const initialValue = definition ? definition.value : '';
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import React, { useState } from 'react';
|
|||
import { SketchPicker } from 'react-color';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
|
||||
export const Color = ({
|
||||
param, definition, onChange, paramType, componentMeta
|
||||
}) => {
|
||||
export const Color = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
|
||||
const coverStyles = {
|
||||
|
|
@ -12,7 +10,7 @@ export const Color = ({
|
|||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px'
|
||||
left: '0px',
|
||||
};
|
||||
|
||||
const paramMeta = componentMeta[paramType][param.name] || {};
|
||||
|
|
@ -20,7 +18,7 @@ export const Color = ({
|
|||
|
||||
return (
|
||||
<div className="field mb-3">
|
||||
<ToolTip label={displayName} meta={paramMeta}/>
|
||||
<ToolTip label={displayName} meta={paramMeta} />
|
||||
|
||||
{showPicker && (
|
||||
<div>
|
||||
|
|
@ -34,11 +32,11 @@ export const Color = ({
|
|||
)}
|
||||
|
||||
<div className="row mx-0 form-control color-picker-input" onClick={() => setShowPicker(true)}>
|
||||
<div className="col-auto" style={{float: 'right', width: '20px', height: '20px', backgroundColor: definition.value}}>
|
||||
</div>
|
||||
<div className="col">
|
||||
{definition.value}
|
||||
</div>
|
||||
<div
|
||||
className="col-auto"
|
||||
style={{ float: 'right', width: '20px', height: '20px', backgroundColor: definition.value }}
|
||||
></div>
|
||||
<div className="col">{definition.value}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,34 +4,27 @@ import Tooltip from 'react-bootstrap/Tooltip';
|
|||
|
||||
const tooltipStyle = {
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationStyle: 'dashed'
|
||||
}
|
||||
|
||||
export const ToolTip = ({
|
||||
label, meta, labelClass
|
||||
}) => {
|
||||
textDecorationStyle: 'dashed',
|
||||
};
|
||||
|
||||
export const ToolTip = ({ label, meta, labelClass }) => {
|
||||
function renderTooltip(props) {
|
||||
return <Tooltip id="button-tooltip" {...props}>
|
||||
{meta.tip}
|
||||
</Tooltip>
|
||||
};
|
||||
return (
|
||||
<Tooltip id="button-tooltip" {...props}>
|
||||
{meta.tip}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (meta.tip) {
|
||||
return (<OverlayTrigger
|
||||
placement="left"
|
||||
delay={{ show: 250, hide: 400 }}
|
||||
overlay={renderTooltip}
|
||||
>
|
||||
<label
|
||||
style={tooltipStyle}
|
||||
className={labelClass || 'form-label'}
|
||||
>
|
||||
return (
|
||||
<OverlayTrigger placement="left" delay={{ show: 250, hide: 400 }} overlay={renderTooltip}>
|
||||
<label style={tooltipStyle} className={labelClass || 'form-label'}>
|
||||
{label}
|
||||
</label>
|
||||
</OverlayTrigger>
|
||||
</label>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
} else {
|
||||
return (<label className={labelClass || 'form-label'}>{label}</label>);
|
||||
return <label className={labelClass || 'form-label'}>{label}</label>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ import CodeMirror from '@uiw/react-codemirror';
|
|||
import 'codemirror/theme/duotone-light.css';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
|
||||
export const Json = ({
|
||||
param, definition, onChange, paramType, componentMeta
|
||||
}) => {
|
||||
export const Json = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const value = definition
|
||||
? definition.value
|
||||
: `[{
|
||||
|
|
@ -15,11 +13,11 @@ export const Json = ({
|
|||
}]`;
|
||||
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
|
||||
return (
|
||||
<div className="field mb-2">
|
||||
<ToolTip label={displayName} meta={paramMeta}/>
|
||||
<ToolTip label={displayName} meta={paramMeta} />
|
||||
<CodeMirror
|
||||
height="300px"
|
||||
fontSize="2"
|
||||
|
|
@ -29,7 +27,7 @@ export const Json = ({
|
|||
theme: 'duotone-light',
|
||||
mode: 'json',
|
||||
lineWrapping: true,
|
||||
scrollbarStyle: null
|
||||
scrollbarStyle: null,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ import React from 'react';
|
|||
import { ToolTip } from './Components/ToolTip';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
|
||||
export const Select = ({
|
||||
param, definition, onChange, paramType, componentMeta
|
||||
}) => {
|
||||
|
||||
export const Select = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
const options = paramMeta.options;
|
||||
|
|
@ -13,7 +10,7 @@ export const Select = ({
|
|||
|
||||
return (
|
||||
<div className="field mb-3">
|
||||
<ToolTip label={displayName} meta={paramMeta}/>
|
||||
<ToolTip label={displayName} meta={paramMeta} />
|
||||
<SelectSearch
|
||||
options={options}
|
||||
value={value}
|
||||
|
|
@ -21,7 +18,7 @@ export const Select = ({
|
|||
onChange={(newVal) => onChange(param, 'value', newVal, paramType)}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
import React from 'react';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
|
||||
export const Text = ({
|
||||
param, definition, onChange, paramType, componentMeta
|
||||
}) => {
|
||||
export const Text = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const value = definition ? definition.value : '';
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
|
||||
|
||||
return (
|
||||
<div className="field mb-3">
|
||||
<ToolTip label={displayName} meta={paramMeta}/>
|
||||
<ToolTip label={displayName} meta={paramMeta} />
|
||||
<input
|
||||
type="text"
|
||||
onBlur={(e) => onChange(param, 'value', e.target.value, paramType)}
|
||||
onKeyDown={(e) => {
|
||||
if(e.key === 'Enter') {
|
||||
onChange(param, 'value', e.target.value, paramType)
|
||||
if (e.key === 'Enter') {
|
||||
onChange(param, 'value', e.target.value, paramType);
|
||||
}
|
||||
}}
|
||||
className="form-control text-field"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
|
||||
export const Toggle = ({
|
||||
param, definition, onChange, paramType, componentMeta
|
||||
}) => {
|
||||
export const Toggle = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const value = definition?.value !== false ?? false;
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
|
|
@ -11,14 +9,14 @@ export const Toggle = ({
|
|||
return (
|
||||
<div className="field mb-3">
|
||||
<label className="form-check form-switch my-2">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
onClick={() => onChange(param, 'value', !value, paramType)}
|
||||
checked={value}
|
||||
/>
|
||||
<ToolTip label={displayName} meta={paramMeta} labelClass="form-check-label"/>
|
||||
</label>
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
onClick={() => onChange(param, 'value', !value, paramType)}
|
||||
checked={value}
|
||||
/>
|
||||
<ToolTip label={displayName} meta={paramMeta} labelClass="form-check-label" />
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@ export const EventManager = ({
|
|||
apps,
|
||||
excludeEvents,
|
||||
popOverCallback,
|
||||
popoverPlacement
|
||||
popoverPlacement,
|
||||
}) => {
|
||||
|
||||
const [focusedEventIndex, setFocusedEventIndex] = useState(null);
|
||||
|
||||
let actionOptions = ActionTypes.map((action) => {
|
||||
|
|
@ -28,32 +27,39 @@ export const EventManager = ({
|
|||
excludeEvents = excludeEvents || [];
|
||||
|
||||
/* Filter events based on excludesEvents ( a list of event ids to exclude ) */
|
||||
let possibleEvents = Object.keys(componentMeta.events).filter(eventId => !excludeEvents.includes(eventId)).map(eventId => { return {
|
||||
name: componentMeta.events[eventId].displayName, value: eventId
|
||||
}})
|
||||
let possibleEvents = Object.keys(componentMeta.events)
|
||||
.filter((eventId) => !excludeEvents.includes(eventId))
|
||||
.map((eventId) => {
|
||||
return {
|
||||
name: componentMeta.events[eventId].displayName,
|
||||
value: eventId,
|
||||
};
|
||||
});
|
||||
|
||||
function getModalOptions() {
|
||||
let modalOptions = [];
|
||||
Object.keys(components || {}).forEach((key) => {
|
||||
if(components[key].component.component === 'Modal') {
|
||||
if (components[key].component.component === 'Modal') {
|
||||
modalOptions.push({
|
||||
name: components[key].component.name,
|
||||
value: key
|
||||
})
|
||||
value: key,
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return modalOptions;
|
||||
}
|
||||
|
||||
function getAllApps() {
|
||||
let appsOptionsList = [];
|
||||
apps.filter(item => item.slug != undefined).map((item) => {
|
||||
appsOptionsList.push({
|
||||
name: item.name,
|
||||
value: item.slug
|
||||
})
|
||||
})
|
||||
apps
|
||||
.filter((item) => item.slug != undefined)
|
||||
.map((item) => {
|
||||
appsOptionsList.push({
|
||||
name: item.name,
|
||||
value: item.slug,
|
||||
});
|
||||
});
|
||||
return appsOptionsList;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +85,7 @@ export const EventManager = ({
|
|||
newEvents.push({
|
||||
eventId: Object.keys(componentMeta.events)[0],
|
||||
actionId: 'show-alert',
|
||||
message: 'Hello world!'
|
||||
message: 'Hello world!',
|
||||
});
|
||||
eventsChanged(newEvents);
|
||||
}
|
||||
|
|
@ -90,9 +96,7 @@ export const EventManager = ({
|
|||
<Popover.Content>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
<span>
|
||||
Event
|
||||
</span>
|
||||
<span>Event</span>
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
|
|
@ -107,9 +111,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">
|
||||
<span>
|
||||
Action
|
||||
</span>
|
||||
<span>Action</span>
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
|
|
@ -124,192 +126,179 @@ export const EventManager = ({
|
|||
</div>
|
||||
|
||||
<div className="hr-text">Action options</div>
|
||||
<div>
|
||||
{event.actionId === 'show-alert' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
Message
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<div>
|
||||
{event.actionId === 'show-alert' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Message</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.message}
|
||||
onChange={(value) => handlerChanged(index, 'message', value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'open-webpage' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">URL</label>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.url}
|
||||
onChange={(value) => handlerChanged(index, 'url', value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'go-to-app' &&
|
||||
<GotoApp
|
||||
event={event}
|
||||
handlerChanged={handlerChanged}
|
||||
eventIndex={index}
|
||||
getAllApps={getAllApps}
|
||||
{event.actionId === 'open-webpage' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">URL</label>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
initialValue={event.url}
|
||||
onChange={(value) => handlerChanged(index, 'url', value)}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'show-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
Modal
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={getModalOptions()}
|
||||
value={event.model}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{event.actionId === 'go-to-app' && (
|
||||
<GotoApp
|
||||
event={event}
|
||||
handlerChanged={handlerChanged}
|
||||
eventIndex={index}
|
||||
getAllApps={getAllApps}
|
||||
currentState={currentState}
|
||||
/>
|
||||
)}
|
||||
|
||||
{event.actionId === 'close-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
Modal
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={getModalOptions()}
|
||||
value={event.model}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'copy-to-clipboard' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">Text</label>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
onChange={(value) => handlerChanged(index, 'contentToCopy', value)}
|
||||
{event.actionId === 'show-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Modal</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={getModalOptions()}
|
||||
value={event.model}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'run-query' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
Query
|
||||
</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={dataQueries.map((query) => {
|
||||
return { name: query.name, value: query.id };
|
||||
})}
|
||||
value={event.queryId}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
const query = dataQueries.find((dataquery) => dataquery.id === value);
|
||||
handlerChanged(index, 'queryId', query.id);
|
||||
handlerChanged(index, 'queryName', query.name);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
{event.actionId === 'close-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Modal</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={getModalOptions()}
|
||||
value={event.model}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'copy-to-clipboard' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">Text</label>
|
||||
<CodeHinter
|
||||
currentState={currentState}
|
||||
onChange={(value) => handlerChanged(index, 'contentToCopy', value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{event.actionId === 'run-query' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Query</div>
|
||||
<div className="col-9">
|
||||
<SelectSearch
|
||||
options={dataQueries.map((query) => {
|
||||
return { name: query.name, value: query.id };
|
||||
})}
|
||||
value={event.queryId}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
const query = dataQueries.find((dataquery) => dataquery.id === value);
|
||||
handlerChanged(index, 'queryId', query.id);
|
||||
handlerChanged(index, 'queryName', query.name);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function renderHandlers(events) {
|
||||
|
||||
return events.map((event, index) => {
|
||||
|
||||
const actionMeta = ActionTypes.find((action) => action.id === event.actionId);
|
||||
const rowClassName = `row g-0 border-bottom pb-2 pt-2 px-2 ${focusedEventIndex === index ? ' bg-azure-lt' : ''}`;
|
||||
|
||||
return <div>
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement={popoverPlacement || 'left'}
|
||||
rootClose={true}
|
||||
overlay={eventPopover(event, index)}
|
||||
onHide={(e) => setFocusedEventIndex(null) }
|
||||
onToggle={ (showing) => {
|
||||
if(showing) {
|
||||
setFocusedEventIndex(index);
|
||||
} else {
|
||||
setFocusedEventIndex(null);
|
||||
}
|
||||
if (typeof popOverCallback === 'function')
|
||||
popOverCallback(showing);
|
||||
}}
|
||||
>
|
||||
<div className={rowClassName} role="button">
|
||||
<div className="col">
|
||||
{componentMeta.events[event.eventId]['displayName']}
|
||||
return (
|
||||
<div key={index}>
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement={popoverPlacement || 'left'}
|
||||
rootClose={true}
|
||||
overlay={eventPopover(event, index)}
|
||||
onHide={() => setFocusedEventIndex(null)}
|
||||
onToggle={(showing) => {
|
||||
if (showing) {
|
||||
setFocusedEventIndex(index);
|
||||
} else {
|
||||
setFocusedEventIndex(null);
|
||||
}
|
||||
if (typeof popOverCallback === 'function') popOverCallback(showing);
|
||||
}}
|
||||
>
|
||||
<div className={rowClassName} role="button">
|
||||
<div className="col">{componentMeta.events[event.eventId]['displayName']}</div>
|
||||
<div className="col">
|
||||
<small className="text-muted">{actionMeta.name}</small>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<span
|
||||
className="text-danger"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeHandler(index);
|
||||
}}
|
||||
>
|
||||
<img className="svg-icon" src="/assets/images/icons/trash.svg" width="12" height="12" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col">
|
||||
<small className="text-muted">{actionMeta.name}</small>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<span className="text-danger" onClick={(e) => { e.stopPropagation(); removeHandler(index) }}>
|
||||
<img className="svg-icon" src="/assets/images/icons/trash.svg" width="12" height="12" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
})
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const events = component.component.definition.events || [];
|
||||
|
||||
if(events.length === 0) {
|
||||
return <div>
|
||||
<center>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-azure"
|
||||
onClick={addHandler}
|
||||
>
|
||||
Add event handler
|
||||
</button>
|
||||
</center>
|
||||
</div>
|
||||
if (events.length === 0) {
|
||||
return (
|
||||
<div>
|
||||
<center>
|
||||
<button className="btn btn-sm btn-outline-azure" onClick={addHandler}>
|
||||
Add event handler
|
||||
</button>
|
||||
</center>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="card">
|
||||
<div className="card-body p-0">
|
||||
{renderHandlers(events)}
|
||||
|
||||
</div>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-azure"
|
||||
onClick={addHandler}
|
||||
>
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-body p-0">{renderHandlers(events)}</div>
|
||||
<button className="btn btn-sm btn-outline-azure" onClick={addHandler}>
|
||||
Add handler
|
||||
</button>
|
||||
</div>
|
||||
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,24 +11,27 @@ export const Inspector = ({
|
|||
selectedComponentId,
|
||||
componentDefinitionChanged,
|
||||
dataQueries,
|
||||
removeComponent,
|
||||
allComponents,
|
||||
componentChanged,
|
||||
currentState,
|
||||
apps,
|
||||
darkMode,
|
||||
switchSidebarTab
|
||||
switchSidebarTab,
|
||||
}) => {
|
||||
|
||||
const selectedComponent = { id: selectedComponentId, component: allComponents[selectedComponentId].component, layouts: allComponents[selectedComponentId].layouts}
|
||||
const selectedComponent = {
|
||||
id: selectedComponentId,
|
||||
component: allComponents[selectedComponentId].component,
|
||||
layouts: allComponents[selectedComponentId].layouts,
|
||||
};
|
||||
const [component, setComponent] = useState(selectedComponent);
|
||||
|
||||
const [components, setComponents] = useState(allComponents);
|
||||
|
||||
|
||||
const componentMeta = componentTypes.find((comp) => component.component.component === comp.component);
|
||||
|
||||
useEffect(() => {
|
||||
setComponent(selectedComponent);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedComponent.component.definition]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -42,7 +45,9 @@ export const Inspector = ({
|
|||
setComponent(newComponent);
|
||||
componentChanged(newComponent);
|
||||
} else {
|
||||
toast.error('Invalid query name. Should be unique and only include letters, numbers and underscore.', { hideProgressBar: true });
|
||||
toast.error('Invalid query name. Should be unique and only include letters, numbers and underscore.', {
|
||||
hideProgressBar: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +61,7 @@ export const Inspector = ({
|
|||
allParams[param.name] = {};
|
||||
}
|
||||
|
||||
if(attr) {
|
||||
if (attr) {
|
||||
allParams[param.name][attr] = value;
|
||||
} else {
|
||||
allParams[param.name] = value;
|
||||
|
|
@ -68,65 +73,61 @@ export const Inspector = ({
|
|||
...component,
|
||||
component: {
|
||||
...component.component,
|
||||
definition: newDefinition
|
||||
}
|
||||
definition: newDefinition,
|
||||
},
|
||||
};
|
||||
|
||||
setComponent(newComponent);
|
||||
componentDefinitionChanged(newComponent);
|
||||
}
|
||||
|
||||
function layoutPropertyChanged(param, attr, value, paramType) {
|
||||
function layoutPropertyChanged(param, attr, value, paramType) {
|
||||
paramUpdated(param, attr, value, paramType);
|
||||
|
||||
// User wants to show the widget on mobile devices
|
||||
if(param.name === 'showOnMobile' && value === true) {
|
||||
|
||||
if (param.name === 'showOnMobile' && value === true) {
|
||||
let newComponent = {
|
||||
...component
|
||||
...component,
|
||||
};
|
||||
|
||||
const { width, height } = newComponent.layouts['desktop'];
|
||||
|
||||
newComponent['layouts'] = {
|
||||
...newComponent.layouts,
|
||||
mobile: {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: Math.min(width, 445),
|
||||
height: height
|
||||
}
|
||||
}
|
||||
mobile: {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: Math.min(width, 445),
|
||||
height: height,
|
||||
},
|
||||
};
|
||||
|
||||
setComponent(newComponent);
|
||||
componentDefinitionChanged(newComponent).then(() => {
|
||||
|
||||
// Child componets should also have a mobile layout
|
||||
const childComponents = Object.keys(allComponents).filter((key) => allComponents[key].parent === component.id);
|
||||
|
||||
childComponents.forEach((componentId) => {
|
||||
let newChild = {
|
||||
id: componentId,
|
||||
...allComponents[componentId]
|
||||
...allComponents[componentId],
|
||||
};
|
||||
|
||||
const { width, height } = newChild.layouts['desktop'];
|
||||
|
||||
newChild['layouts'] = {
|
||||
...newChild.layouts,
|
||||
mobile: {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: Math.min(width, 445),
|
||||
height: height
|
||||
}
|
||||
}
|
||||
mobile: {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: Math.min(width, 445),
|
||||
height: height,
|
||||
},
|
||||
};
|
||||
|
||||
componentDefinitionChanged(newChild);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ export const Inspector = ({
|
|||
newDefinition.events[event.name] = { actionId };
|
||||
|
||||
let newComponent = {
|
||||
...component
|
||||
...component,
|
||||
};
|
||||
|
||||
setComponent(newComponent);
|
||||
|
|
@ -147,7 +148,7 @@ export const Inspector = ({
|
|||
newDefinition.events = newEvents;
|
||||
|
||||
let newComponent = {
|
||||
...component
|
||||
...component,
|
||||
};
|
||||
|
||||
setComponent(newComponent);
|
||||
|
|
@ -163,7 +164,7 @@ export const Inspector = ({
|
|||
newDefinition.events[event.name] = { ...eventDefinition, options: { ...eventDefinition.options, [option]: value } };
|
||||
|
||||
let newComponent = {
|
||||
...component
|
||||
...component,
|
||||
};
|
||||
|
||||
setComponent(newComponent);
|
||||
|
|
@ -174,30 +175,26 @@ export const Inspector = ({
|
|||
<div className="inspector">
|
||||
<div className="header px-2 py-1 row">
|
||||
<div className="col-auto">
|
||||
<div className="input-icon">
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => handleComponentNameChange(e.target.value)}
|
||||
className="form-control-plaintext form-control-plaintext-sm mt-1"
|
||||
value={component.component.name}
|
||||
/>
|
||||
<span className="input-icon-addon">
|
||||
<img src="/assets/images/icons/edit-source.svg" width="12" height="12" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="input-icon">
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => handleComponentNameChange(e.target.value)}
|
||||
className="form-control-plaintext form-control-plaintext-sm mt-1"
|
||||
value={component.component.name}
|
||||
/>
|
||||
<span className="input-icon-addon">
|
||||
<img src="/assets/images/icons/edit-source.svg" width="12" height="12" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col py-1">
|
||||
<button
|
||||
className="btn btn-sm component-action-button btn-light"
|
||||
onClick={() => switchSidebarTab(2)}
|
||||
>
|
||||
<button className="btn btn-sm component-action-button btn-light" onClick={() => switchSidebarTab(2)}>
|
||||
x
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{componentMeta.component === 'Table' &&
|
||||
{componentMeta.component === 'Table' && (
|
||||
<Table
|
||||
component={component}
|
||||
paramUpdated={paramUpdated}
|
||||
|
|
@ -211,9 +208,9 @@ export const Inspector = ({
|
|||
eventsChanged={eventsChanged}
|
||||
apps={apps}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
|
||||
{componentMeta.component === 'Chart' &&
|
||||
{componentMeta.component === 'Chart' && (
|
||||
<Chart
|
||||
component={component}
|
||||
paramUpdated={paramUpdated}
|
||||
|
|
@ -225,16 +222,39 @@ export const Inspector = ({
|
|||
currentState={currentState}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
}
|
||||
|
||||
{!['Table', 'Chart'].includes(componentMeta.component) &&
|
||||
<div className="properties-container p-2">
|
||||
{Object.keys(componentMeta.properties).map((property) => renderElement(component, componentMeta, paramUpdated, dataQueries, property, 'properties', currentState, components, darkMode))}
|
||||
|
||||
{Object.keys(componentMeta.styles).length > 0 && <div className="hr-text">Style</div>}
|
||||
{Object.keys(componentMeta.styles).map((style) => renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components))}
|
||||
)}
|
||||
|
||||
{Object.keys(componentMeta.events).length > 0 &&
|
||||
{!['Table', 'Chart'].includes(componentMeta.component) && (
|
||||
<div className="properties-container p-2">
|
||||
{Object.keys(componentMeta.properties).map((property) =>
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
property,
|
||||
'properties',
|
||||
currentState,
|
||||
components,
|
||||
darkMode
|
||||
)
|
||||
)}
|
||||
|
||||
{Object.keys(componentMeta.styles).length > 0 && <div className="hr-text">Style</div>}
|
||||
{Object.keys(componentMeta.styles).map((style) =>
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
style,
|
||||
'styles',
|
||||
currentState,
|
||||
components
|
||||
)
|
||||
)}
|
||||
|
||||
{Object.keys(componentMeta.events).length > 0 && (
|
||||
<div>
|
||||
{Object.keys(componentMeta.events).length > 0 && <div className="hr-text">Events</div>}
|
||||
|
||||
|
|
@ -248,31 +268,61 @@ export const Inspector = ({
|
|||
apps={apps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
{Object.keys(componentMeta.validation || {}).length > 0 &&
|
||||
{Object.keys(componentMeta.validation || {}).length > 0 && (
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
{Object.keys(componentMeta.validation).map((property) => renderElement(component, componentMeta, paramUpdated, dataQueries, property, 'validation', currentState, components, darkMode))}
|
||||
{Object.keys(componentMeta.validation).map((property) =>
|
||||
renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
property,
|
||||
'validation',
|
||||
currentState,
|
||||
components,
|
||||
darkMode
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
{/* Show on desktop & show on mobile params */}
|
||||
<div className="hr-text">Layout</div>
|
||||
<div className="properties-container p-2 pb-3 mb-5">
|
||||
{renderElement(component, componentMeta, layoutPropertyChanged, dataQueries, 'showOnDesktop', 'others', currentState, components)}
|
||||
{renderElement(component, componentMeta, layoutPropertyChanged, dataQueries, 'showOnMobile', 'others', currentState, components)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
layoutPropertyChanged,
|
||||
dataQueries,
|
||||
'showOnDesktop',
|
||||
'others',
|
||||
currentState,
|
||||
components
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
layoutPropertyChanged,
|
||||
dataQueries,
|
||||
'showOnMobile',
|
||||
'others',
|
||||
currentState,
|
||||
components
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="widget-documentation-link p-2">
|
||||
<a href={`https://docs.tooljet.io/docs/widgets/${convertToKebabCase(componentMeta?.name ?? '')}`} target="_blank">
|
||||
<small>
|
||||
{componentMeta.name} documentation
|
||||
</small>
|
||||
<a
|
||||
href={`https://docs.tooljet.io/docs/widgets/${convertToKebabCase(componentMeta?.name ?? '')}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<small>{componentMeta.name} documentation</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,8 @@
|
|||
import React, { useState } from 'react';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import Collapse from 'react-bootstrap/Collapse'
|
||||
|
||||
export const QuerySelector = ({
|
||||
param,
|
||||
definition,
|
||||
eventUpdated,
|
||||
eventOptionUpdated,
|
||||
dataQueries,
|
||||
extraData,
|
||||
eventMeta,
|
||||
currentState,
|
||||
components
|
||||
}) => {
|
||||
import Collapse from 'react-bootstrap/Collapse';
|
||||
|
||||
export const QuerySelector = ({ param, definition, eventOptionUpdated, dataQueries, extraData, eventMeta }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
function onChange(value) {
|
||||
|
|
@ -34,9 +23,7 @@ export const QuerySelector = ({
|
|||
<div className="field mb-3 mt-1 px-2">
|
||||
<label className="form-label" role="button" onClick={() => setOpen(!open)}>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
{eventMeta.displayName}
|
||||
</div>
|
||||
<div className="col">{eventMeta.displayName}</div>
|
||||
<div className={`col-auto events-toggle ${open ? 'events-toggle-active' : ''}`}>
|
||||
<span className="toggle-icon"></span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ export const TypeMapping = {
|
|||
json: 'Json',
|
||||
code: 'Code',
|
||||
toggle: 'Toggle',
|
||||
select: 'Select'
|
||||
select: 'Select',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export const ItemTypes = {
|
||||
BOX: 'box'
|
||||
BOX: 'box',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,193 +6,207 @@ import _ from 'lodash';
|
|||
import moment from 'moment';
|
||||
import { SidebarPinnedButton } from './SidebarPinnedButton';
|
||||
|
||||
export const LeftSidebarDebugger = ({ darkMode, errors }) => {
|
||||
const [open, trigger, content, popoverPinned, updatePopoverPinnedState] = usePinnedPopover(false);
|
||||
const [currrentTab, setCurrentTab] = React.useState(1);
|
||||
const [errorLogs, setErrorLogs] = React.useState([]);
|
||||
const [unReadErrorCount, setUnReadErrorCount] = React.useState({ read: 0, unread: 0 });
|
||||
|
||||
export const LeftSidebarDebugger = ({ darkMode, components, errors }) => {
|
||||
const [open, trigger, content, popoverPinned, updatePopoverPinnedState] = usePinnedPopover(false)
|
||||
const [currrentTab, setCurrentTab] = React.useState(1)
|
||||
const [errorLogs, setErrorLogs] = React.useState([])
|
||||
const [unReadErrorCount, setUnReadErrorCount] = React.useState({read: 0, unread: 0})
|
||||
const switchCurrentTab = (tab) => {
|
||||
setCurrentTab(tab);
|
||||
};
|
||||
|
||||
const switchCurrentTab = (tab) => {
|
||||
setCurrentTab(tab)
|
||||
}
|
||||
const clearErrorLogs = () => {
|
||||
setUnReadErrorCount(() => {
|
||||
return { read: 0, unread: 0 };
|
||||
});
|
||||
|
||||
const clearErrorLogs = () => {
|
||||
setUnReadErrorCount(() => {
|
||||
return { read: 0, unread: 0 }
|
||||
})
|
||||
setErrorLogs(() => []);
|
||||
};
|
||||
|
||||
setErrorLogs(() => [])
|
||||
}
|
||||
React.useEffect(() => {
|
||||
setErrorLogs((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev));
|
||||
copy = copy.filter((val) => Object.keys(val).length !== 0);
|
||||
const newError = _.flow([
|
||||
Object.entries,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
(arr) => arr.filter(([key, value]) => value.data.status),
|
||||
Object.fromEntries,
|
||||
])(errors);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
setErrorLogs(prev => {
|
||||
let copy = JSON.parse(JSON.stringify(prev))
|
||||
copy = copy.filter(val => Object.keys(val).length !== 0)
|
||||
const newError = _.flow([
|
||||
Object.entries,
|
||||
arr => arr.filter(([key, value]) => value.data.status),
|
||||
Object.fromEntries
|
||||
])(errors);
|
||||
|
||||
const errorData = []
|
||||
Object.entries(newError).map(([key, value]) => {
|
||||
const variableNames = {
|
||||
options: '',
|
||||
response: ''
|
||||
}
|
||||
switch (value.type) {
|
||||
case 'query':
|
||||
variableNames.options = 'substitutedVariables';
|
||||
variableNames.response = 'response';
|
||||
break;
|
||||
default: 'options';
|
||||
}
|
||||
errorData.push({
|
||||
type: value.type,
|
||||
key: key,
|
||||
message: value.data.message,
|
||||
description: value.data.description,
|
||||
options: {name: variableNames.options, data: value.options},
|
||||
response: {name: variableNames.response, data: value.data.data},
|
||||
timestamp: moment()
|
||||
})
|
||||
})
|
||||
|
||||
const newData = [...errorData, ...copy]
|
||||
return newData
|
||||
|
||||
})
|
||||
},[errors])
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
if(open === false && errorLogs.length !== unReadErrorCount.read) {
|
||||
const unReadErrors = errorLogs.length - unReadErrorCount.read
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev))
|
||||
copy.unread = unReadErrors
|
||||
return copy
|
||||
})
|
||||
|
||||
if(popoverPinned) {
|
||||
setTimeout(() => {
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev))
|
||||
copy.read = errorLogs.length
|
||||
copy.unread = 0
|
||||
return copy
|
||||
})
|
||||
}, 900);
|
||||
}
|
||||
} else {
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev))
|
||||
copy.read = errorLogs.length
|
||||
copy.unread = 0
|
||||
return copy
|
||||
})
|
||||
const errorData = [];
|
||||
Object.entries(newError).map(([key, value]) => {
|
||||
const variableNames = {
|
||||
options: '',
|
||||
response: '',
|
||||
};
|
||||
switch (value.type) {
|
||||
case 'query':
|
||||
variableNames.options = 'substitutedVariables';
|
||||
variableNames.response = 'response';
|
||||
break;
|
||||
default:
|
||||
'options';
|
||||
}
|
||||
},[errorLogs.length, open])
|
||||
errorData.push({
|
||||
type: value.type,
|
||||
key: key,
|
||||
message: value.data.message,
|
||||
description: value.data.description,
|
||||
options: { name: variableNames.options, data: value.options },
|
||||
response: { name: variableNames.response, data: value.data.data },
|
||||
timestamp: moment(),
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
const newData = [...errorData, ...copy];
|
||||
return newData;
|
||||
});
|
||||
}, [errors]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open === false && errorLogs.length !== unReadErrorCount.read) {
|
||||
const unReadErrors = errorLogs.length - unReadErrorCount.read;
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev));
|
||||
copy.unread = unReadErrors;
|
||||
return copy;
|
||||
});
|
||||
|
||||
if (popoverPinned) {
|
||||
setTimeout(() => {
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev));
|
||||
copy.read = errorLogs.length;
|
||||
copy.unread = 0;
|
||||
return copy;
|
||||
});
|
||||
}, 900);
|
||||
}
|
||||
} else {
|
||||
setUnReadErrorCount((prev) => {
|
||||
let copy = JSON.parse(JSON.stringify(prev));
|
||||
copy.read = errorLogs.length;
|
||||
copy.unread = 0;
|
||||
return copy;
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [errorLogs.length, open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LeftSidebarItem tip='Debugger' {...trigger} icon='debugger' className={`left-sidebar-item ${open && 'active'}`} badge={true} count={unReadErrorCount.unread} />
|
||||
<div {...content} className={`card popover debugger-popover ${open || popoverPinned ? 'show' : 'hide'}`} style={{minWidth:'180px', minHeight:'108px', maxWidth:'480px'}} >
|
||||
<div className="row-header">
|
||||
<div className="nav-header">
|
||||
<ul className="nav nav-tabs d-flex justify-content-between" data-bs-toggle="tabs">
|
||||
<li className="nav-item">
|
||||
<a onClick={() => switchCurrentTab(1)} className={currrentTab === 1 ? "nav-link active" : "nav-link"}>
|
||||
Errors
|
||||
</a>
|
||||
</li>
|
||||
<li className="btn-group">
|
||||
{errorLogs.length > 0 && (
|
||||
<button onClick={clearErrorLogs} type="button" className="btn btn-light btn-sm m-1 py-1" aria-label="clear button">
|
||||
<span className="text-muted">clear</span>
|
||||
</button>
|
||||
)}
|
||||
<SidebarPinnedButton component={'Debugger'} state={popoverPinned} updateState={updatePopoverPinnedState} />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{currrentTab === 1 && (
|
||||
<div className="card-body">
|
||||
{errorLogs.length === 0 && (
|
||||
<center className="p-2 text-muted">
|
||||
No errors found.
|
||||
</center>
|
||||
<LeftSidebarItem
|
||||
tip="Debugger"
|
||||
{...trigger}
|
||||
icon="debugger"
|
||||
className={`left-sidebar-item ${open && 'active'}`}
|
||||
badge={true}
|
||||
count={unReadErrorCount.unread}
|
||||
/>
|
||||
<div
|
||||
{...content}
|
||||
className={`card popover debugger-popover ${open || popoverPinned ? 'show' : 'hide'}`}
|
||||
style={{ minWidth: '180px', minHeight: '108px', maxWidth: '480px' }}
|
||||
>
|
||||
<div className="row-header">
|
||||
<div className="nav-header">
|
||||
<ul className="nav nav-tabs d-flex justify-content-between" data-bs-toggle="tabs">
|
||||
<li className="nav-item">
|
||||
<a onClick={() => switchCurrentTab(1)} className={currrentTab === 1 ? 'nav-link active' : 'nav-link'}>
|
||||
Errors
|
||||
</a>
|
||||
</li>
|
||||
<li className="btn-group">
|
||||
{errorLogs.length > 0 && (
|
||||
<button
|
||||
onClick={clearErrorLogs}
|
||||
type="button"
|
||||
className="btn btn-light btn-sm m-1 py-1"
|
||||
aria-label="clear button"
|
||||
>
|
||||
<span className="text-muted">clear</span>
|
||||
</button>
|
||||
)}
|
||||
<SidebarPinnedButton
|
||||
component={'Debugger'}
|
||||
state={popoverPinned}
|
||||
updateState={updatePopoverPinnedState}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tab-content">
|
||||
{errorLogs.map((error, index) => (
|
||||
<LeftSidebarDebugger.ErrorLogs errorProps={error} idx={index} darkMode={darkMode} />
|
||||
))}
|
||||
</div>
|
||||
{currrentTab === 1 && (
|
||||
<div className="card-body">
|
||||
{errorLogs.length === 0 && <center className="p-2 text-muted">No errors found.</center>}
|
||||
|
||||
|
||||
<div className="tab-content">
|
||||
{errorLogs.map((error, index) => (
|
||||
<LeftSidebarDebugger.ErrorLogs key={index} errorProps={error} idx={index} darkMode={darkMode} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
function ErrorLogsComponent({ errorProps, idx, darkMode }) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
return (
|
||||
<div className="tab-content" key={`${errorProps.key}-${idx}`}>
|
||||
<p className="text-azure" onClick={() => setOpen((prev) => !prev)}>
|
||||
<img
|
||||
className={`svg-icon ${open ? 'iopen' : ''}`}
|
||||
src={`/assets/images/icons/caret-right.svg`}
|
||||
width="16"
|
||||
height="16"
|
||||
/>
|
||||
[{_.capitalize(errorProps.type)} {errorProps.key}]
|
||||
<span className="text-red">
|
||||
{`Query Failed: ${errorProps.description}`} {errorProps.message}.
|
||||
</span>
|
||||
<br />
|
||||
<small className="text-muted px-1">{moment(errorProps.timestamp).fromNow()}</small>
|
||||
</p>
|
||||
<div className={` queryData ${open ? 'open' : 'close'} py-0`}>
|
||||
<span>
|
||||
<ReactJson
|
||||
src={errorProps.options.data}
|
||||
theme={darkMode ? 'shapeshifter' : 'rjv-default'}
|
||||
name={errorProps.options.name}
|
||||
style={{ fontSize: '0.7rem', paddingLeft: '0.17rem' }}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapsed={true}
|
||||
displayObjectSize={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys={false}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ReactJson
|
||||
src={errorProps.response.data}
|
||||
theme={darkMode ? 'shapeshifter' : 'rjv-default'}
|
||||
name={errorProps.response.name}
|
||||
style={{ fontSize: '0.7rem', paddingLeft: '0.17rem' }}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapsed={true}
|
||||
displayObjectSize={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys={false}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<hr className="border-1 border-bottom bg-grey py-0" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function ErrorLogsComponent ({ errorProps, idx, darkMode }) {
|
||||
|
||||
const [open, setOpen] = React.useState(false)
|
||||
return (
|
||||
<div className="tab-content" key={`${errorProps.key}-${idx}`}>
|
||||
|
||||
<p className='text-azure' onClick={() => setOpen((prev) => !prev)}>
|
||||
<img className={`svg-icon ${open ? 'iopen': ''}`} src={`/assets/images/icons/caret-right.svg`} width="16" height="16"/>
|
||||
[{_.capitalize(errorProps.type)} {errorProps.key}]
|
||||
<span className="text-red">{`Query Failed: ${errorProps.description}`} {errorProps.message}.</span>
|
||||
<br />
|
||||
<small className="text-muted px-1">{moment(errorProps.timestamp).fromNow()}</small>
|
||||
</p>
|
||||
<div className={` queryData ${open ? 'open' : 'close'} py-0`} >
|
||||
<span>
|
||||
<ReactJson
|
||||
src={errorProps.options.data}
|
||||
theme={darkMode ? 'shapeshifter' : 'rjv-default'}
|
||||
name={errorProps.options.name}
|
||||
style={{ fontSize: '0.7rem', paddingLeft:'0.17rem' }}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapsed={true}
|
||||
displayObjectSize={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys={false}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ReactJson
|
||||
src={errorProps.response.data}
|
||||
theme={darkMode ? 'shapeshifter' : 'rjv-default'}
|
||||
name={errorProps.response.name}
|
||||
style={{ fontSize: '0.7rem', paddingLeft:'0.17rem' }}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapsed={true}
|
||||
displayObjectSize={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys={false}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<hr className="border-1 border-bottom bg-grey py-0" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
LeftSidebarDebugger.ErrorLogs = ErrorLogsComponent;
|
||||
LeftSidebarDebugger.ErrorLogs = ErrorLogsComponent;
|
||||
|
|
|
|||
|
|
@ -1,34 +1,36 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
|
||||
export const SidebarPinnedButton = ({ state, component, updateState }) => {
|
||||
|
||||
const tooltipMsg = state ? `Unpin ${component}` : `Pin ${component}`
|
||||
return (
|
||||
<SidebarPinnedButton.OverlayContainer tip={tooltipMsg}>
|
||||
<div className={`btn btn-sm m-1 ${state ? 'btn-light' : 'btn-default'} ${(component === 'Inspector') && "position-absolute end-0" }`} onClick={updateState} >
|
||||
<img className="svg-icon" src={`/assets/images/icons/editor/left-sidebar/pinned.svg`} width="16" height="16" />
|
||||
</div>
|
||||
</SidebarPinnedButton.OverlayContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const tooltipMsg = state ? `Unpin ${component}` : `Pin ${component}`;
|
||||
return (
|
||||
<SidebarPinnedButton.OverlayContainer tip={tooltipMsg}>
|
||||
<div
|
||||
className={`btn btn-sm m-1 ${state ? 'btn-light' : 'btn-outline-secondary'} ${
|
||||
component === 'Inspector' && 'position-absolute end-0'
|
||||
}`}
|
||||
onClick={updateState}
|
||||
>
|
||||
<img className="svg-icon" src={`/assets/images/icons/editor/left-sidebar/pinned.svg`} width="16" height="16" />
|
||||
</div>
|
||||
</SidebarPinnedButton.OverlayContainer>
|
||||
);
|
||||
};
|
||||
|
||||
function OverlayContainer({ children, tip }) {
|
||||
return (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
trigger={['click','hover', 'focus']}
|
||||
placement="top"
|
||||
delay={{ show: 800, hide: 100 }}
|
||||
overlay={<Tooltip id="button-tooltip">{tip}</Tooltip>}
|
||||
>
|
||||
{ children }
|
||||
</OverlayTrigger>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
SidebarPinnedButton.OverlayContainer = OverlayContainer
|
||||
return (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
trigger={['click', 'hover', 'focus']}
|
||||
placement="top"
|
||||
delay={{ show: 800, hide: 100 }}
|
||||
overlay={<Tooltip id="button-tooltip">{tip}</Tooltip>}
|
||||
>
|
||||
{children}
|
||||
</OverlayTrigger>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
SidebarPinnedButton.OverlayContainer = OverlayContainer;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import usePopover from '../../_hooks/use-popover';
|
||||
import { LeftSidebarItem } from './sidebar-item';
|
||||
import { DataSourceManager } from '../DataSourceManager';
|
||||
import { DataSourceTypes } from '../DataSourceManager/DataSourceTypes';
|
||||
import { DataSourceTypes } from '../DataSourceManager/SourceComponents';
|
||||
|
||||
export const LeftSidebarDataSources = ({ appId, darkMode, dataSources = [], dataSourcesChanged }) => {
|
||||
const [open, trigger, content] = usePopover(false);
|
||||
|
|
@ -68,7 +68,6 @@ export const LeftSidebarDataSources = ({ appId, darkMode, dataSources = [], data
|
|||
toggleDataSourceManagerModal(false);
|
||||
}}
|
||||
dataSourcesChanged={dataSourcesChanged}
|
||||
showDataSourceManagerModal={showDataSourceManagerModal}
|
||||
selectedDataSource={selectedDataSource}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
import React from 'react';
|
||||
import usePinnedPopover from '@/_hooks/usePinnedPopover';
|
||||
import { LeftSidebarItem } from './sidebar-item';
|
||||
import { SidebarPinnedButton} from './SidebarPinnedButton';
|
||||
import { SidebarPinnedButton } from './SidebarPinnedButton';
|
||||
import ReactJson from 'react-json-view';
|
||||
|
||||
export const LeftSidebarInspector = ({ darkMode, globals, components, queries }) => {
|
||||
const [open, trigger, content, popoverPinned, updatePopoverPinnedState] = usePinnedPopover(false)
|
||||
const [open, trigger, content, popoverPinned, updatePopoverPinnedState] = usePinnedPopover(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LeftSidebarItem tip='Inspector' {...trigger} icon='inspector' className={`left-sidebar-item ${open && 'active'}`} />
|
||||
<LeftSidebarItem
|
||||
tip="Inspector"
|
||||
{...trigger}
|
||||
icon="inspector"
|
||||
className={`left-sidebar-item ${open && 'active'}`}
|
||||
/>
|
||||
<div {...content} className={`card popover ${open || popoverPinned ? 'show' : 'hide'}`}>
|
||||
<SidebarPinnedButton component={'Inspector'} state={popoverPinned} updateState={updatePopoverPinnedState} />
|
||||
<div style={{marginTop:'1rem'}} className="card-body">
|
||||
<div style={{ marginTop: '1rem' }} className="card-body">
|
||||
<ReactJson
|
||||
src={queries}
|
||||
theme={darkMode ? 'shapeshifter' : 'rjv-default'}
|
||||
|
|
@ -49,10 +54,10 @@ export const LeftSidebarInspector = ({ darkMode, globals, components, queries })
|
|||
displayObjectSize={false}
|
||||
quotesOnKeys={false}
|
||||
sortKeys={true}
|
||||
// indentWidth={1}
|
||||
// indentWidth={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,41 +2,41 @@ import React from 'react';
|
|||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
|
||||
export const LeftSidebarItem = ({ tip = '', className, icon, text, onClick, badge=false, count,...rest }) => {
|
||||
|
||||
export const LeftSidebarItem = ({ tip = '', className, icon, text, onClick, badge = false, count, ...rest }) => {
|
||||
return (
|
||||
<OverlayTrigger
|
||||
trigger={['click','hover', 'focus']}
|
||||
trigger={['click', 'hover', 'focus']}
|
||||
placement="right"
|
||||
delay={{ show: 800, hide: 100 }}
|
||||
overlay={<Tooltip id="button-tooltip">
|
||||
{tip}
|
||||
</Tooltip>}
|
||||
overlay={<Tooltip id="button-tooltip">{tip}</Tooltip>}
|
||||
>
|
||||
<div {...rest} className={className} onClick={onClick && onClick}>
|
||||
{icon && <img className="svg-icon" src={`/assets/images/icons/editor/left-sidebar/${icon}.svg`} width="20" height="20" />}
|
||||
{badge && <LeftSidebarItem.Badge count={count} /> }
|
||||
{text && text}
|
||||
{icon && (
|
||||
<img
|
||||
className="svg-icon"
|
||||
src={`/assets/images/icons/editor/left-sidebar/${icon}.svg`}
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
)}
|
||||
{badge && <LeftSidebarItem.Badge count={count} />}
|
||||
{text && text}
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function NotificationBadge({count}) {
|
||||
const fontSize = count > 999 ? '7.5px' : '8.5px'
|
||||
function NotificationBadge({ count }) {
|
||||
const fontSize = count > 999 ? '7.5px' : '8.5px';
|
||||
return (
|
||||
<>
|
||||
{count > 0 && (
|
||||
<span
|
||||
class="badge bg-red rounded-circle debugger-badge p-0"
|
||||
style={{fontSize: fontSize}}
|
||||
>
|
||||
<span className="badge bg-red rounded-circle debugger-badge p-0" style={{ fontSize: fontSize }}>
|
||||
{count > 999 ? `999+` : count}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
LeftSidebarItem.Badge = NotificationBadge
|
||||
LeftSidebarItem.Badge = NotificationBadge;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue