Enhance TypeScript support in frontend configuration (#15576)

* test: verify pre-commit hook

* fix: clean up code formatting and improve readability across multiple components

* chore: update subproject commit reference in frontend/ee

* chore: update eslint to version 9.26.0 and remove unused dependencies from package.json

fix: update submodule reference in server/ee

* chore: refactor ESLint configuration and add quiet linting script; update components to disable specific ESLint rules

* chore: add GitHub Copilot review instructions for App Builder team

Covers backward compatibility rules, styling conventions, state management,
resolution system, widget definitions, and common review flags.

* chore: add review instructions for App Builder, Data Migrations, Server Widget Config, Widget Components, and Widget Config

* Enhance TypeScript support in frontend configuration

- Added TypeScript parser and linting rules to ESLint configuration.
- Updated Babel configuration to include TypeScript preset.
- Modified package.json and package-lock.json to include TypeScript and related dependencies.
- Introduced tsconfig.json for TypeScript compiler options.
- Updated Webpack configuration to support .ts and .tsx file extensions.
- Adjusted linting and formatting scripts to include TypeScript files.

* chore: update TypeScript ESLint packages and subproject commits

---------

Co-authored-by: kavinvenkatachalam <kavin.saratha@gmail.com>
Co-authored-by: Johnson Cherian <johnsonc.dev@gmail.com>
This commit is contained in:
Nakul Nagargade 2026-03-19 12:41:32 +05:30 committed by GitHub
parent e9d68061c2
commit 433e1bd4c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
263 changed files with 4987 additions and 5776 deletions

View file

@ -1,2 +0,0 @@
node_modules/**
cypress-tests/**

48
.github/copilot-instructions.md vendored Normal file
View file

@ -0,0 +1,48 @@
# ToolJet — Shared Copilot Instructions
## Styling
- Tailwind classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-text-default`). Unprefixed Tailwind is a bug.
- NEVER hardcode hex/rgb colors. Use CSS variable tokens via Tailwind (`tw-text-default`, `tw-bg-page-default`) or `var(--text-default)`.
- Prefer Tailwind over Bootstrap for new code. Do not extend legacy `react-bootstrap` usage.
- Use custom typography utilities (`tw-font-title-default`, `tw-font-body-default`, etc.) instead of ad-hoc font-size/weight.
- Design tokens: `frontend/src/_styles/designtheme.scss` + `frontend/tailwind.config.js`.
## Component Patterns
- Check `frontend/src/_ui/` (53+ components) before creating new UI components.
- Functional components with hooks only. No class components.
- File structure: `ComponentName/index.js` + optional `ComponentName.jsx` + `style.scss`.
- Compose with Radix UI primitives for accessible interactive elements.
## Imports
- Use `@/` path alias (maps to `frontend/src/`): `import Button from '@/_ui/Button'`.
- No deep relative paths (`../../..` is a smell).
## State Management
- Zustand with Immer middleware only. No Redux/MobX/Recoil.
- Use `shallow` comparison in `useStore` when selecting objects/arrays. Flag missing `shallow`.
## Icons & Assets
- Use Tabler Icons (`@tabler/icons-react`) or Lucide React (`lucide-react`). Do NOT add new icon packages.
- Static assets: `frontend/assets/images/`.
## Security
- No API keys/secrets in client-side code.
- Backend: parameterized queries only, never concatenate user input into SQL.
## Common Review Flags
- Hardcoded colors (hex/rgb/hsl in JSX or SCSS)
- Missing `tw-` prefix on Tailwind classes
- New `react-bootstrap` imports
- Class components
- `console.log` / debug leftovers
- Unused imports
- Missing `key` props in `.map()`
- Missing `shallow` in `useStore` selectors
- Direct DOM manipulation (except canvas drop calculations)

View file

@ -0,0 +1,54 @@
---
applyTo: "frontend/src/AppBuilder/**/*"
excludeAgent: "coding-agent"
---
# App Builder — Code Review Rules
## Backward Compatibility (CRITICAL)
No change should break existing saved applications. When reviewing, always ask: "Would an app saved before this PR still load and behave correctly after it?"
## Resolution System (`{{}}`)
- Flow: unresolved value → `extractAndReplaceReferencesFromString``resolveDynamicValues``resolveCode` (via `new Function()`) → resolved value stored in `resolvedSlice`.
- `{{...}}` references MUST be registered in the dependency graph via `addReferencesForDependencyGraph`. Missing this causes stale renders.
- After `setExposedValue`, `updateDependencyValues` MUST be called to propagate changes.
- Inside ListView/Kanban, `customResolvables` provide row-scoped context (`listItem` / `cardData`).
## Rendering Pipeline
`AppCanvas → Container → WidgetWrapper → RenderWidget → Widget`
- Widgets receive resolved props from `RenderWidget`. They must NOT directly access store state.
- `setExposedVariable` and `fireEvent` are passed as callbacks — widgets use these to communicate outward.
## Subcontainer Architecture
- `SubcontainerContext` carries `contextPath` array: `[{ containerId, index }, ...]`.
- Row-scoped resolution uses prototype overlay (`prepareRowScope`/`updateRowScope`).
- Child-to-parent: `setExposedValuesPerRow``_deriveListviewChain` (no callback chains).
- ListView nesting limited to **2 levels**. Only row 0 is editable; others are read-only mirrors.
- `findNearestSubcontainerAncestor` is critical for dependency resolution — verify it's used when walking the component tree.
- Keywords: `listItem` (ListView), `cardData` (Kanban). Don't mix them up.
## Event & Query Systems
- Events: `fireEvent → handleEvent → executeActionsForEventId → executeAction`. Events support `runOnlyIf` and `debounce`.
- Queries: `runQuery → resolve options → API call → update exposedValues.queries[name] → trigger dependency updates`.
- Event definitions live in `eventsSlice`, not in component definitions.
## Bundle & Performance
- Viewer (`/applications/*`) and editor are separate lazy bundles via `RootRouter.jsx`. Do not import editor-only code into viewer paths.
- Avoid `JSON.parse(JSON.stringify(...))` or `_.cloneDeep` in render/hot paths. Use Immer.
- Flag O(N) loops inside already-O(N) resolution paths (eager resolution for ListView children).
## State Management
- Global stores (`appDataStore`, `currentStateStore`, `dataQueriesStore`, `resolverStore`) should NOT be used in AppBuilder code unless absolutely critical. Prefer the AppBuilder store. Flag any new usage.
- AppBuilder store: `AppBuilder/_stores/store.js` (30+ slices). All slices are namespaced by `moduleId` (default: `'canvas'`).
## Security
- `resolveCode` uses `new Function()` — be cautious about evaluated expressions.

View file

@ -0,0 +1,31 @@
---
applyTo: "server/data-migrations/**/*"
---
# Data Migration — Logging Guidelines
New migrations MUST include progress logging to help monitor deployments.
## Required Logging Pattern
1. **Baseline** — Before processing, query and log total records:
`[START] {Action} | Total: {Total}`
2. **Progress** — During execution, log using current/total format:
`[PROGRESS] {Current}/{Total} ({Percentage}%)`
3. **Confirmation** — Log final success:
`[SUCCESS] {Action} finished.`
## Reviewer Checklist
- [ ] Count query establishes a **total** before processing
- [ ] Log statements use the **`x/total`** ratio pattern
- [ ] Clear **success** log at the end
- [ ] No silent bulk updates without console feedback
## When Generating Migration Code
- Include a count query for the denominator before processing.
- Wrap iterative logic in a batch/loop that logs `current/total` progress.
- Avoid silent bulk updates that provide no console feedback.

View file

@ -0,0 +1,18 @@
---
applyTo: "server/src/modules/apps/services/widget-config/**/*"
excludeAgent: "coding-agent"
---
# Server Widget Config — Code Review Rules
## Frontend Sync (CRITICAL)
When any file here is modified, the corresponding config in `frontend/src/AppBuilder/WidgetManager/widgets/` MUST also be updated. Flag PRs that modify one without the other.
## Key Changes Require Migrations
If a config change moves, renames, or removes a key, a migration MUST be written in `server/migrations/` to transform saved app definitions. Flag any key restructuring that lacks an accompanying migration.
## Backward Compatibility
No change should break existing saved applications. Always ask: "Would an app saved before this PR still load and behave correctly after it?"

View file

@ -0,0 +1,14 @@
---
applyTo: "frontend/src/AppBuilder/Widgets/**/*"
excludeAgent: "coding-agent"
---
# Widget Components — Code Review Rules
## Component Rules
- New widgets MUST be lazy-loaded.
- Use `useBatchedUpdateEffectArray` for batched state updates.
- Widgets receive resolved props from `RenderWidget`. They must NOT directly access store state.
- `setExposedVariable` and `fireEvent` are passed as callbacks — widgets use these to communicate outward.
- Default values should use design tokens, not hardcoded hex colors.

View file

@ -0,0 +1,24 @@
---
applyTo: "frontend/src/AppBuilder/WidgetManager/widgets/**/*"
excludeAgent: "coding-agent"
---
# Widget Config — Code Review Rules
## Server-Side Sync (CRITICAL)
When any file here is modified, the corresponding config in `server/src/modules/apps/services/widget-config/` MUST also be updated. Flag PRs that modify one without the other.
## Key Changes Require Migrations
If a config change moves, renames, or removes a key (e.g., moving `loadingState` from `styles` to `properties`), this WILL break existing apps. A migration MUST be written in `server/migrations/` to transform saved app definitions. Flag any key restructuring that lacks an accompanying migration.
## Widget Definition Rules
- New widgets MUST be lazy-loaded.
- Use `useBatchedUpdateEffectArray` for batched state updates.
- Widget components must be registered in `componentTypes.js`.
## Backward Compatibility
Always ask: "Would an app saved before this PR still load and behave correctly after it?"

View file

@ -1,4 +1 @@
#!/bin/sh
# . "$(dirname "$0")/_/husky.sh"
# npx lint-staged
npx lint-staged

49
.vscode/settings.json vendored
View file

@ -1,28 +1,27 @@
{
"[javascript, typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"[javascript, typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"eslint.useFlatConfig": true,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"json.schemas": [
{
"fileMatch": ["/*.operations.json"],
"url": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/operations.schema.json"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"eslint.format.enable": true,
"editor.formatOnSave": true,
"json.schemas": [
{
"fileMatch": [
"/*.operations.json"
],
"url": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/operations.schema.json"
},
{
"fileMatch": [
"/*.manifest.json"
],
"url": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/manifest.schema.json"
}
],
"CodeGPT.apiKey": "CodeGPT Plus Beta"
{
"fileMatch": ["/*.manifest.json"],
"url": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/manifest.schema.json"
}
],
"CodeGPT.apiKey": "CodeGPT Plus Beta"
}

4
eslint.config.mjs Normal file
View file

@ -0,0 +1,4 @@
// Root ESLint flat config — delegates to frontend config.
// Uses dynamic import so plugin resolution happens from frontend/node_modules.
const { default: config } = await import('./frontend/eslint.config.mjs');
export default config;

View file

@ -1,2 +0,0 @@
build
assets

View file

@ -1,86 +0,0 @@
module.exports = {
root: true,
overrides: [
{
files: ['**/*.js', '**/*.jsx'],
env: {
browser: true,
amd: true,
es2021: true,
node: true,
'jest/globals': true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:prettier/recommended',
],
parser: '@babel/eslint-parser',
parserOptions: {
requireConfigFile: false,
babelOptions: {
configFile: __dirname + '/babel.config.js',
},
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react', 'prettier', 'jest'],
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': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'react/no-deprecated': 0,
'no-prototype-builtins': 0,
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
'jest/valid-expect': 'error',
'import/no-unresolved': [
'error',
{
ignore: ['^@/', 'react-hot-toast', 'react-i18next', 'react-loading-skeleton', 'react-spring'],
},
],
'react/no-unknown-property': 'off',
},
settings: {
react: {
version: 'detect',
},
'import/resolver': 'webpack',
},
globals: {
path: true,
fetch: true,
process: true,
module: true,
__dirname: true,
},
},
],
extends: ['plugin:storybook/recommended'],
};

View file

@ -19,6 +19,6 @@ if (process.env.NODE_ENV === 'developement') {
}
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
plugins,
};

@ -1 +1 @@
Subproject commit 2dd264546928e348bc043d7434c893420ea4af0d
Subproject commit 93b97db21954bb9f6223ae06b1a6cae0f76eba42

266
frontend/eslint.config.mjs Normal file
View file

@ -0,0 +1,266 @@
import js from '@eslint/js';
import globals from 'globals';
import babelParser from '@babel/eslint-parser';
import pluginReact from 'eslint-plugin-react';
import pluginReactHooks from 'eslint-plugin-react-hooks';
import pluginImportX from 'eslint-plugin-import-x';
import pluginJest from 'eslint-plugin-jest';
import pluginPrettier from 'eslint-plugin-prettier';
import configPrettier from 'eslint-config-prettier';
import pluginStorybook from 'eslint-plugin-storybook';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
// Remap import-x recommended rules from 'import-x/' prefix to 'import/' prefix
// so they match the plugin namespace and existing eslint-disable directives
function remapImportRules(rules) {
const remapped = {};
for (const [key, value] of Object.entries(rules)) {
remapped[key.replace(/^import-x\//, 'import/')] = value;
}
return remapped;
}
export default [
// Global ignores (replaces .eslintignore)
{
ignores: ['build/**', 'assets/**', 'cypress-tests/**'],
},
// Disable reporting unused eslint-disable directives (ESLint 9 defaults to "warn",
// ESLint 8 defaulted to "off"). This prevents --fix from stripping existing
// eslint-disable comments that plugins no longer flag.
{
linterOptions: {
reportUnusedDisableDirectives: 'off',
},
},
// Main config for JS/JSX files
{
files: ['**/*.js', '**/*.jsx'],
languageOptions: {
parser: babelParser,
parserOptions: {
requireConfigFile: false,
babelOptions: {
configFile: __dirname + '/babel.config.js',
},
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
globals: {
...globals.browser,
...globals.amd,
...globals.node,
...globals.es2021,
...globals.jest,
path: true,
fetch: true,
process: true,
module: true,
__dirname: true,
},
},
plugins: {
react: pluginReact,
'react-hooks': pluginReactHooks,
// Register import-x under the 'import' namespace so existing
// `eslint-disable import/...` directives continue to work
import: pluginImportX,
jest: pluginJest,
prettier: pluginPrettier,
},
settings: {
react: {
version: 'detect',
},
'import-x/resolver': 'webpack',
},
rules: {
// eslint:recommended
...js.configs.recommended.rules,
// react recommended
...pluginReact.configs.recommended.rules,
// react-hooks recommended
...pluginReactHooks.configs.recommended.rules,
// import errors + warnings (remapped from import-x/ to import/ namespace)
...remapImportRules(pluginImportX.configs.recommended.rules),
// prettier recommended (plugin + config)
...pluginPrettier.configs.recommended.rules,
// prettier config (disables conflicting rules)
...configPrettier.rules,
// Re-enable prettier/prettier as error (after configPrettier may disable it)
'prettier/prettier': [
'error',
{
semi: true,
trailingComma: 'es5',
printWidth: 120,
singleQuote: true,
arrowParens: 'always',
proseWrap: 'preserve',
},
],
// Project rules (preserved from .eslintrc.js)
'react/prop-types': 0,
'react/display-name': 'off',
'no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'react/no-deprecated': 0,
'no-prototype-builtins': 0,
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
'jest/valid-expect': 'error',
'import/no-unresolved': [
'error',
{
ignore: ['^@/', 'react-hot-toast', 'react-i18next', 'react-loading-skeleton', 'react-spring'],
},
],
'react/no-unknown-property': 'off',
},
},
// TypeScript config for TS/TSX files
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module',
project: __dirname + '/tsconfig.json',
},
globals: {
...globals.browser,
...globals.amd,
...globals.node,
...globals.es2021,
path: true,
fetch: true,
process: true,
module: true,
__dirname: true,
},
},
plugins: {
'@typescript-eslint': tsPlugin,
react: pluginReact,
'react-hooks': pluginReactHooks,
prettier: pluginPrettier,
},
settings: {
react: {
version: 'detect',
},
},
rules: {
// eslint:recommended
...js.configs.recommended.rules,
// @typescript-eslint/recommended rules (from v7 plugin)
// Disable base ESLint rules that conflict with TS equivalents
'no-unused-vars': 'off',
'no-undef': 'off', // TypeScript handles this
'no-redeclare': 'off',
'no-dupe-class-members': 'off',
// @typescript-eslint recommended
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
'@typescript-eslint/ban-types': 'error',
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-extra-non-null-assertion': 'error',
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-loss-of-precision': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-unnecessary-type-constraint': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
// react recommended
...pluginReact.configs.recommended.rules,
// react-hooks recommended
...pluginReactHooks.configs.recommended.rules,
// prettier recommended (plugin + config)
...pluginPrettier.configs.recommended.rules,
// prettier config (disables conflicting rules)
...configPrettier.rules,
// Re-enable prettier/prettier as error
'prettier/prettier': [
'error',
{
semi: true,
trailingComma: 'es5',
printWidth: 120,
singleQuote: true,
arrowParens: 'always',
proseWrap: 'preserve',
},
],
// Project rules
'react/prop-types': 'off',
'react/display-name': 'off',
// Override @typescript-eslint defaults with project preferences
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'import/no-unresolved': [
'error',
{
ignore: ['^@/', 'react-hot-toast', 'react-i18next', 'react-loading-skeleton', 'react-spring'],
},
],
'react/no-unknown-property': 'off',
},
},
// Storybook config
...pluginStorybook.configs['flat/recommended'],
];

File diff suppressed because it is too large Load diff

View file

@ -168,10 +168,11 @@
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/eslint-parser": "^7.19.1",
"@babel/eslint-parser": "^7.28.6",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.28.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
"@storybook/addon-docs": "^9.1.5",
"@storybook/addon-links": "^9.1.5",
@ -182,6 +183,8 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@typescript-eslint/eslint-plugin": "^8.39.0",
"@typescript-eslint/parser": "^8.39.0",
"autoprefixer": "^10.4.17",
"babel-loader": "^9.1.2",
"babel-plugin-console-source": "^2.0.5",
@ -191,19 +194,20 @@
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^7.0.2",
"esbuild": "0.25.9",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.6.0",
"eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^9.1.5",
"@eslint/js": "^9.26.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^8.10.2",
"eslint-import-resolver-webpack": "^0.13.10",
"eslint-plugin-import-x": "^4.15.0",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-prettier": "^4.2.5",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-storybook": "^0.12.0",
"globals": "^15.15.0",
"html-loader": "^4.2.0",
"html-webpack-plugin": "^5.5.0",
"jest": "^29.4.2",
"madge": "^8.0.0",
"mini-css-extract-plugin": "^2.9.4",
"path": "^0.12.7",
"postcss": "^8.4.35",
@ -216,6 +220,7 @@
"style-loader": "^3.3.1",
"tailwindcss": "^3.4.1",
"terser-webpack-plugin": "^5.3.6",
"typescript": "^5.9.3",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^5.0.1",
@ -266,7 +271,9 @@
"esbuild": "0.25.9",
"on-headers": "1.1.0",
"tar-fs": "^3.1.0",
"brace-expansion": ">=2.0.2"
"minimatch@3": {
"brace-expansion": "^1.1.11"
}
},
"scripts": {
"start": "webpack serve --hot --port 8082 --host 0.0.0.0",
@ -276,22 +283,19 @@
"analyze:dev": "ANALYZE=true webpack --mode=development",
"analyze:stats": "webpack --mode=production --json > bundle-stats.json && webpack-bundle-analyzer bundle-stats.json",
"build:analyze": "ANALYZE=true NODE_ENV=production webpack --mode=production",
"lint": "eslint . '**/*.{js,jsx}'",
"format": "eslint . --fix '**/*.{js,jsx}'",
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
"lint-quiet": "eslint --quiet 'src/**/*.{js,jsx,ts,tsx}'",
"format": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
"typecheck": "tsc --noEmit",
"test": "jest",
"storybook": "storybook dev -p 6006",
"build-storybook": "npx storybook build",
"check:circular": "bash scripts/check-circular-deps.sh",
"check:bundle": "node scripts/check-bundle-size.js",
"check:all": "npm run check:circular && npm run check:bundle"
},
"eslintConfig": {
"extends": "react-app"
"build-storybook": "npx storybook build"
},
"jest": {
"transform": {
"^.+\\.js?$": "babel-jest",
"^.+\\.jsx?$": "babel-jest",
"^.+\\.tsx?$": "babel-jest",
"^.+\\.svg$": "<rootDir>/__mocks__/svg.js"
},
"transformIgnorePatterns": [
@ -306,6 +310,8 @@
"src"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json",
"jsx"

View file

@ -1,37 +0,0 @@
const fs = require('fs');
const path = require('path');
const distPath = path.join(__dirname, '../build');
if (!fs.existsSync(distPath)) {
console.log('⚠️ No build folder found, skipping bundle check');
process.exit(0);
}
const files = fs.readdirSync(distPath).filter(f => f.endsWith('.js'));
const VIEWER_MAX = 1.5 * 1024 * 1024; // 1.5 MB
let failed = false;
console.log('\n📦 Bundle Size Report:\n');
files.forEach(file => {
const size = fs.statSync(path.join(distPath, file)).size;
const sizeMB = (size / 1024 / 1024).toFixed(2);
if (file.includes('viewer') && size > VIEWER_MAX) {
console.error(`${file}: ${sizeMB}MB (exceeds 1.5MB limit)`);
failed = true;
} else if (file.includes('viewer')) {
console.log(`${file}: ${sizeMB}MB (viewer bundle)`);
} else {
console.log(` ${file}: ${sizeMB}MB`);
}
});
if (failed) {
console.log('\n❌ Bundle size check failed!\n');
process.exit(1);
}
console.log('\n✅ All bundles within size limits\n');

View file

@ -1,13 +0,0 @@
#!/bin/bash
echo "🔍 Checking for circular dependencies..."
# Run madge to check for circular dependencies
npx madge --circular --extensions js,jsx src/
if [ $? -ne 0 ]; then
echo "❌ Circular dependencies found!"
exit 1
fi
echo "✅ No circular dependencies found"
exit 0

View file

@ -118,24 +118,24 @@ export const ConfigHandle = ({
const isHiddenOrModalOpen = visibility === false || (componentType === 'Modal' && isModalOpen);
const getConfigHandleButtonStyle = isHiddenOrModalOpen
? {
background: 'var(--interactive-selected)',
color: 'var(--text-default)',
padding: '2px 6px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
height: '24px',
}
background: 'var(--interactive-selected)',
color: 'var(--text-default)',
padding: '2px 6px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
height: '24px',
}
: {
color: 'var(--text-on-solid)',
padding: '2px 6px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
height: '24px',
};
color: 'var(--text-on-solid)',
padding: '2px 6px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
height: '24px',
};
if (isDynamicHeightEnabled && !isHiddenOrModalOpen) {
getConfigHandleButtonStyle.background = '#9747FF';
}
@ -195,7 +195,6 @@ export const ConfigHandle = ({
return null;
}
return (
<div
className={`config-handle ${customClassName}`}
@ -205,8 +204,8 @@ export const ConfigHandle = ({
componentType === 'Modal' && isModalOpen
? '0px'
: position === 'top'
? '-26px'
: `${height - (CONFIG_HANDLE_HEIGHT + BUFFER_HEIGHT)}px`,
? '-26px'
: `${height - (CONFIG_HANDLE_HEIGHT + BUFFER_HEIGHT)}px`,
visibility: _showHandle || visibility === false ? 'visible' : 'hidden',
left: '-1px',
display: 'flex',
@ -288,24 +287,21 @@ export const ConfigHandle = ({
<PencilRuler size={14} color="var(--icon-strong)" />
</ConfigHandleButton>
{
licenseValid && isRestricted && (
<ConfigHandleButton
customStyles={iconOnlyButtonStyle}
message={getTooltip()}
show={licenseValid && isRestricted && !draggingComponentId}
dataCy={`${componentName.toLowerCase()}-permissions-button`}
>
<Lock size={14} color="var(--icon-strong)" />
</ConfigHandleButton>
)
}
{
!isMultipleComponentsSelected && !shouldFreeze && (
<Suspense fallback={null}>
<MentionComponentInChat componentName={componentName} />
</Suspense >
)}
{licenseValid && isRestricted && (
<ConfigHandleButton
customStyles={iconOnlyButtonStyle}
message={getTooltip()}
show={licenseValid && isRestricted && !draggingComponentId}
dataCy={`${componentName.toLowerCase()}-permissions-button`}
>
<Lock size={14} color="var(--icon-strong)" />
</ConfigHandleButton>
)}
{!isMultipleComponentsSelected && !shouldFreeze && (
<Suspense fallback={null}>
<MentionComponentInChat componentName={componentName} />
</Suspense>
)}
<ConfigHandleButton
customStyles={iconOnlyButtonStyle}
onClick={() => {
@ -319,17 +315,15 @@ export const ConfigHandle = ({
<Trash size={14} color="var(--icon-strong)" />
</ConfigHandleButton>
{/* Tooltip for invalid license on ModuleViewer */}
{
(componentType === 'ModuleViewer' || componentType === 'ModuleContainer') && !isModulesEnabled && (
<Tooltip
delay={{ show: 500, hide: 50 }}
id={`invalid-license-modules-${componentName?.toLowerCase()}`}
className="tooltip"
isOpen={_showHandle && (componentType === 'ModuleViewer' || componentType === 'ModuleContainer')}
style={{ textAlign: 'center' }}
/>
)
}
</div >
{(componentType === 'ModuleViewer' || componentType === 'ModuleContainer') && !isModulesEnabled && (
<Tooltip
delay={{ show: 500, hide: 50 }}
id={`invalid-license-modules-${componentName?.toLowerCase()}`}
className="tooltip"
isOpen={_showHandle && (componentType === 'ModuleViewer' || componentType === 'ModuleContainer')}
style={{ textAlign: 'center' }}
/>
)}
</div>
);
};

View file

@ -52,8 +52,12 @@ const NoComponentCanvasContainer = () => {
<div className="box-icon" data-cy="create-a-query-icon">
<SolidIcon name="datasource" fill="#3E63DD" width="25" />
</div>
<div className={`title-text`} data-cy="create-a-query-label">Create a Query</div>
<div className="title-desc" data-cy="create-a-query-description">{queryBoxText}</div>
<div className={`title-text`} data-cy="create-a-query-label">
Create a Query
</div>
<div className="title-desc" data-cy="create-a-query-description">
{queryBoxText}
</div>
{!!sampleDataSource && !shouldFreeze && (
<div className="box-link" data-cy="connect-to-sample-data-source-link">
<div className="child">
@ -73,7 +77,9 @@ const NoComponentCanvasContainer = () => {
<div className="box-icon" data-cy="share-your-application-icon">
<BulkIcon name="invitecollab" width="25" viewBox="0 0 28 28" />
</div>
<div className={`title-text `} data-cy="share-your-application-label">Share your application!</div>
<div className={`title-text `} data-cy="share-your-application-label">
Share your application!
</div>
<div className="title-desc" data-cy="share-your-application-description">
Invite users to collaborate in real-time with multiplayer editing and comments for seamless development.
</div>

View file

@ -82,14 +82,9 @@ export const TrackedSuspense = ({ fallback = null, children }) => {
return <Suspense fallback={fallback}>{children}</Suspense>;
}
return (
<Suspense fallback={<SuspenseFallbackTracker fallback={fallback} />}>
{children}
</Suspense>
);
return <Suspense fallback={<SuspenseFallbackTracker fallback={fallback} />}>{children}</Suspense>;
};
// Loading overlay shown while lazy components are resolving
export const SuspenseLoadingOverlay = ({ darkMode }) => {
const isLoading = useSuspenseLoading();
@ -98,13 +93,15 @@ export const SuspenseLoadingOverlay = ({ darkMode }) => {
return (
<div
className={cx('suspense-loading-overlay tw-absolute tw-inset-0 tw-overflow-hidden', { 'theme-dark dark-theme': darkMode })}
className={cx('suspense-loading-overlay tw-absolute tw-inset-0 tw-overflow-hidden', {
'theme-dark dark-theme': darkMode,
})}
>
<div className='tw-sticky tw-top-0 tw-h-screen tw-flex tw-items-center tw-justify-center'>
<div className="tw-sticky tw-top-0 tw-h-screen tw-flex tw-items-center tw-justify-center">
<div className="suspense-loader-wrapper">
<TJLoader />
</div>
</div>
</div>
);
};
};

View file

@ -1,4 +1,5 @@
import React from 'react';
// eslint-disable-next-line import/no-unresolved
import CodeMirror from '@uiw/react-codemirror';
// import 'codemirror/theme/duotone-light.css';

View file

@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import useRouter from '@/_hooks/use-router';
import config from 'config';
import toast from 'react-hot-toast';
import './embed-loader.scss'
import './embed-loader.scss';
import Loader from '@/ToolJetUI/Loader/Loader';
// In-memory PAT token store
@ -78,13 +78,11 @@ export default function EmbedAppRedirect() {
}, [appId]);
return (
<div className="embed-loader">
<div className="embed-loader__content">
<Loader width={30} absolute={false} />
<div className="embed-loader__text">
Loading embedded app
<div className="embed-loader">
<div className="embed-loader__content">
<Loader width={30} absolute={false} />
<div className="embed-loader__text">Loading embedded app</div>
</div>
</div>
</div>
);
);
}

View file

@ -1 +1 @@
export { AppVersionsManager } from './AppVersionsManager';
export { AppVersionsManager } from './AppVersionsManager';

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState ,useRef} from 'react';
import React, { useEffect, useState, useRef } from 'react';
import AlertDialog from '@/_ui/AlertDialog';
import { Alert } from '@/_ui/Alert';
import { toast } from 'react-hot-toast';
@ -17,7 +17,7 @@ const CreateDraftVersionModal = ({
canCommit,
orgGit,
fetchingOrgGit,
handleCommitOnVersionCreation = () => { },
handleCommitOnVersionCreation = () => {},
}) => {
const { moduleId } = useModuleContext();
const [isCreatingVersion, setIsCreatingVersion] = useState(false);
@ -49,10 +49,7 @@ const CreateDraftVersionModal = ({
// Filter out draft versions - show all saved versions (PUBLISHED + any released)
const savedVersions = developmentVersions.filter((version) => version.status !== 'DRAFT');
useEffect(() => {
const gitSyncEnabled =
orgGit?.git_ssh?.is_enabled ||
orgGit?.git_https?.is_enabled ||
orgGit?.git_lab?.is_enabled;
const gitSyncEnabled = orgGit?.git_ssh?.is_enabled || orgGit?.git_https?.is_enabled || orgGit?.git_lab?.is_enabled;
setIsGitSyncEnabled(gitSyncEnabled);
}, [orgGit]);
@ -112,8 +109,8 @@ const CreateDraftVersionModal = ({
savedVersions.length > 0
? savedVersions.map((version) => ({ label: version.name, value: version.id }))
: selectedVersion && selectedVersion.status !== 'DRAFT'
? [{ label: selectedVersion.name, value: selectedVersion.id }]
: [];
? [{ label: selectedVersion.name, value: selectedVersion.id }]
: [];
const createVersion = () => {
if (versionName.trim().length > 25) {

View file

@ -18,7 +18,7 @@ const CreateVersionModal = ({
canCommit,
orgGit,
fetchingOrgGit,
handleCommitOnVersionCreation = () => { },
handleCommitOnVersionCreation = () => {},
versionId,
onVersionCreated,
}) => {
@ -229,8 +229,7 @@ const CreateVersionModal = ({
toast.error('Version name already exists.');
} else if (error?.error) {
toast.error(error?.error);
}
else {
} else {
toast.error('Error while creating version. Please try again.');
}
} finally {

View file

@ -1,2 +1,3 @@
// eslint-disable-next-line import/no-unresolved
import EnvironmentManager from './EnvironmentsManager';
export default EnvironmentManager;

View file

@ -4,14 +4,13 @@ import { Tooltip } from 'react-tooltip';
import { shallow } from 'zustand/shallow';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import useStore from '@/AppBuilder/_stores/store';
import { Button } from '@/components/ui/Button/Button';
import { Button, Button as ButtonComponent } from '@/components/ui/Button/Button';
import { Monitor, Smartphone, Play } from 'lucide-react';
import { Link } from 'react-router-dom';
import { useAppPreviewLink } from '@/_hooks/useAppPreviewLink';
import { ToggleLayoutButtons } from './ToggleLayoutButtons';
import { Button as ButtonComponent } from '@/components/ui/Button/Button';
const HeaderActions = function HeaderActions ({ darkMode, showFullWidth, showPreviewBtn = true }) {
const HeaderActions = function HeaderActions({ darkMode, showFullWidth, showPreviewBtn = true }) {
const {
currentLayout,
canUndo,
@ -60,8 +59,6 @@ const HeaderActions = function HeaderActions ({ darkMode, showFullWidth, showPre
/>
)}
{showPreviewBtn && (
<Link
title="Preview"
to={appPreviewLink}
@ -77,9 +74,8 @@ const HeaderActions = function HeaderActions ({ darkMode, showFullWidth, showPre
variant="outline"
leadingIcon="play"
data-cy="editor-preview-button"
style={{ padding: "7px 12px" }}
style={{ padding: '7px 12px' }}
>
Preview
</ButtonComponent>
</Link>

View file

@ -17,7 +17,7 @@ import { retrieveWhiteLabelText } from '@white-label/whiteLabelling';
import useStore from '@/AppBuilder/_stores/store';
class ManageAppUsersComponent extends React.Component {
constructor (props) {
constructor(props) {
super(props);
this.isUserAdmin = authenticationService.currentSessionValue?.admin;
this.whiteLabelText = retrieveWhiteLabelText();
@ -50,7 +50,7 @@ class ManageAppUsersComponent extends React.Component {
});
};
componentDidMount () {
componentDidMount() {
const appId = this.props.appId;
this.setState({ appId });
}
@ -171,7 +171,7 @@ class ManageAppUsersComponent extends React.Component {
}
};
render () {
render() {
const { appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state;
const appLink = `${getHostURL()}/applications/`;

View file

@ -20,7 +20,7 @@ import { Button } from '@/components/ui/Button/Button';
import { Share2 } from 'lucide-react';
class ManageAppUsersComponent extends React.Component {
constructor (props) {
constructor(props) {
super(props);
this.isUserAdmin = authenticationService.currentSessionValue?.admin;
this.whiteLabelText = retrieveWhiteLabelText();
@ -54,7 +54,7 @@ class ManageAppUsersComponent extends React.Component {
});
};
componentDidMount () {
componentDidMount() {
const appId = this.props.appId;
this.setState({ appId });
}
@ -180,7 +180,7 @@ class ManageAppUsersComponent extends React.Component {
handleMouseLeave = () => {
this.setState({ isHovered: false });
};
render () {
render() {
const { appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state;
const appLink = `${getHostURL()}/applications/`;

View file

@ -41,7 +41,7 @@ const VersionDropdownItem = ({
// This ensures we can find the parent even if it's in a different environment
const parentVersion = version.parentVersionId
? versions.find((v) => v.id === version.parentVersionId) ||
developmentVersions.find((v) => v.id === version.parentVersionId)
developmentVersions.find((v) => v.id === version.parentVersionId)
: null;
const createdFromVersionName = parentVersion?.name || version.createdFromVersion;

View file

@ -4,10 +4,7 @@ import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
function Debugger({ onClose, darkMode }) {
const [logs, clearLogs] = useStore(
(state) => [state.debugger.logs, state.debugger.clear],
shallow
);
const [logs, clearLogs] = useStore((state) => [state.debugger.logs, state.debugger.clear], shallow);
const currentPageId = useStore((state) => state.modules.canvas.currentPageId);

View file

@ -46,16 +46,8 @@ export const SidebarDebuggerHeader = ({ darkMode, onClear, onClose, activeTab, o
</div>
</div>
<div className="debugger-tabs-container" role="tablist">
<DebuggerTab
label="All logs"
isActive={activeTab === 'allLog'}
onClick={() => onTabChange('allLog')}
/>
<DebuggerTab
label="Errors"
isActive={activeTab === 'errors'}
onClick={() => onTabChange('errors')}
/>
<DebuggerTab label="All logs" isActive={activeTab === 'allLog'} onClick={() => onTabChange('allLog')} />
<DebuggerTab label="Errors" isActive={activeTab === 'errors'} onClick={() => onTabChange('errors')} />
</div>
</div>
);

View file

@ -31,12 +31,8 @@ const DebuggerTabContent = ({ logs, darkMode, tabName }) => {
const SidebarDebuggerTabs = ({ darkMode, errors, allLog, activeTab }) => {
return (
<div className="">
{activeTab === 'allLog' && (
<DebuggerTabContent logs={allLog} darkMode={darkMode} tabName="allLogs" />
)}
{activeTab === 'errors' && (
<DebuggerTabContent logs={errors} darkMode={darkMode} tabName="errors" />
)}
{activeTab === 'allLog' && <DebuggerTabContent logs={allLog} darkMode={darkMode} tabName="allLogs" />}
{activeTab === 'errors' && <DebuggerTabContent logs={errors} darkMode={darkMode} tabName="errors" />}
</div>
);
};

View file

@ -1,108 +0,0 @@
import { useState, useEffect } from 'react';
import { useCurrentStateStore } from '@/_stores/currentStateStore';
import { shallow } from 'zustand/shallow';
import { debuggerActions } from '@/_helpers/appUtils';
import { flow, cloneDeepWith } from 'lodash';
import moment from 'moment';
import { reservedKeywordReplacer } from '@/_lib/reserved-keyword-replacer';
const useDebugger = ({ currentPageId, isDebuggerOpen }) => {
const [errorLogs, setErrorLogs] = useState([]);
const [errorHistory, setErrorHistory] = useState({ appLevel: [], pageLevel: [] });
const [unReadErrorCount, setUnReadErrorCount] = useState({ read: 0, unread: 0 });
const [allLog, setAllLog] = useState([]);
const { errors, succededQuery } = useCurrentStateStore(
(state) => ({
errors: state.errors,
succededQuery: state.succededQuery,
}),
shallow
);
const clearErrorLogs = () => {
setUnReadErrorCount({ read: 0, unread: 0 });
setErrorLogs([]);
setAllLog([]);
setErrorHistory({ appLevel: [], pageLevel: [] });
};
useEffect(() => {
if (currentPageId) {
const olderPageErrorFromHistory = errorHistory.pageLevel[currentPageId] ?? [];
const olderAppErrorFromHistory = errorHistory.appLevel ?? [];
setErrorLogs(() => [...olderPageErrorFromHistory, ...olderAppErrorFromHistory]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPageId]);
useEffect(() => {
const newError = flow([
Object.entries,
// eslint-disable-next-line no-unused-vars
(arr) => arr.filter(([key, value]) => value.data?.status),
Object.fromEntries,
])(errors);
const newErrorLogs = debuggerActions.generateErrorLogs(newError);
const newPageLevelErrorLogs = newErrorLogs.filter((error) => error.strace === 'page_level');
const newAppLevelErrorLogs = newErrorLogs.filter((error) => error.strace === 'app_level');
if (newErrorLogs) {
setErrorLogs((prevErrors) => {
const copy = cloneDeepWith(prevErrors, (val, key) => reservedKeywordReplacer(key, val));
return [...newAppLevelErrorLogs, ...newPageLevelErrorLogs, ...copy];
});
setAllLog((prevLog) => [...newErrorLogs, ...prevLog]);
setErrorHistory((prevErrors) => {
const copy = cloneDeepWith(prevErrors, (val, key) => reservedKeywordReplacer(key, val));
return {
appLevel: [...newAppLevelErrorLogs, ...copy.appLevel],
pageLevel: {
[currentPageId]: [...newPageLevelErrorLogs, ...(copy.pageLevel[currentPageId] ?? [])],
},
};
});
}
debuggerActions.flush();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ errors }, reservedKeywordReplacer)]);
useEffect(() => {
const successQueryLogs = debuggerActions.generateQuerySuccessLogs(succededQuery);
if (successQueryLogs?.length) {
setAllLog((prevLogs) => {
const temp = [...successQueryLogs, ...prevLogs];
const sortedDatesDesc = temp.sort((a, b) => moment(b.timestamp).diff(moment(a.timestamp)));
return sortedDatesDesc;
});
debuggerActions.flushAllLog();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ succededQuery })]);
useEffect(() => {
if (isDebuggerOpen) {
// eslint-disable-next-line no-unused-vars
setUnReadErrorCount((prev) => ({ read: errorLogs.length, unread: 0 }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDebuggerOpen]);
useEffect(() => {
const unReadErrors = errorLogs.length - unReadErrorCount.read;
setUnReadErrorCount((prev) => {
return { ...prev, unread: unReadErrors };
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [errorLogs.length]);
return {
errorLogs,
clearErrorLogs,
unReadErrorCount,
allLog,
};
};
export default useDebugger;

View file

@ -36,8 +36,9 @@ export const SidebarItem = forwardRef(
const content = (
<Button
{...rest}
className={`${className} ${selectedSidebarItem === icon && selectedSidebarItem !== 'comments' && 'sidebar-item--active'
} ${icon}-icon`}
className={`${className} ${
selectedSidebarItem === icon && selectedSidebarItem !== 'comments' && 'sidebar-item--active'
} ${icon}-icon`}
onClick={onClick && onClick}
ref={ref}
type="button"
@ -77,7 +78,7 @@ export const SidebarItem = forwardRef(
}
);
function CommentBadge () {
function CommentBadge() {
return (
<svg
className="comment-badge"
@ -92,7 +93,7 @@ function CommentBadge () {
);
}
function NotificationBadge ({ count }) {
function NotificationBadge({ count }) {
const fontSize = count > 999 ? '7.5px' : '8.5px';
return (
<>

View file

@ -5,4 +5,4 @@ const SupportButton = () => {
return null;
};
export default withEditionSpecificComponent(SupportButton, 'Appbuilder');
export default withEditionSpecificComponent(SupportButton, 'Appbuilder');

View file

@ -103,7 +103,6 @@ function DataSourcePicker({ darkMode }) {
</label>
<div className="query-datasource-card-container d-flex justify-content-between mb-3 mt-2">
{updatedStaticDataSources.map((source) => {
return (
<ButtonSolid
key={`${source.id}-${source.kind}`}

View file

@ -154,31 +154,31 @@ function DataSourceSelect({
const groupedSampleDataSources =
sampleDataSources && sampleDataSources.length > 0
? Object.entries(groupBy(sampleDataSources, 'kind')).map(([kind, sources]) => ({
label: (
<div>
<DataSourceIcon source={sources[0]} height={16} />
<span className="ms-1 small">{dataSourcesKinds.find((dsk) => dsk.kind === kind)?.name || kind}</span>
</div>
),
options: sources.map((source) => ({
label: (
<div
key={source.id}
className="py-2 px-2 rounded option-nested-datasource-selector small text-truncate"
style={{ fontSize: '13px' }}
data-tooltip-id="tooltip-for-add-query-dd-option"
data-tooltip-content={decodeEntities(source.name)}
data-cy={`ds-${source.name.toLowerCase()}`}
>
{decodeEntities(source.name)}
<Tooltip id="tooltip-for-add-query-dd-option" className="tooltip query-manager-ds-select-tooltip" />
<div>
<DataSourceIcon source={sources[0]} height={16} />
<span className="ms-1 small">{dataSourcesKinds.find((dsk) => dsk.kind === kind)?.name || kind}</span>
</div>
),
value: source.id,
isNested: true,
source,
})),
}))
options: sources.map((source) => ({
label: (
<div
key={source.id}
className="py-2 px-2 rounded option-nested-datasource-selector small text-truncate"
style={{ fontSize: '13px' }}
data-tooltip-id="tooltip-for-add-query-dd-option"
data-tooltip-content={decodeEntities(source.name)}
data-cy={`ds-${source.name.toLowerCase()}`}
>
{decodeEntities(source.name)}
<Tooltip id="tooltip-for-add-query-dd-option" className="tooltip query-manager-ds-select-tooltip" />
</div>
),
value: source.id,
isNested: true,
source,
})),
}))
: [];
const dataSourcesAvailable = [
@ -206,18 +206,18 @@ function DataSourceSelect({
// Sample data sources group header
...(groupedSampleDataSources.length > 0
? [
{
label: (
<div>
<span className="color-slate9" style={{ fontWeight: 500 }}>
Sample data sources
</span>
</div>
),
isDisabled: true,
},
...groupedSampleDataSources,
]
{
label: (
<div>
<span className="color-slate9" style={{ fontWeight: 500 }}>
Sample data sources
</span>
</div>
),
isDisabled: true,
},
...groupedSampleDataSources,
]
: []),
];
@ -240,8 +240,8 @@ function DataSourceSelect({
source?.id !== 'if' && source?.id !== 'agent' && workflowDataSources
? onNewNode(source.kind, source.id, source.plugin_id, source)
: source && (source?.id === 'if' || source?.id === 'response' || source?.id === 'agent')
? onNewNode(source.id)
: handleChangeDataSource(source)
? onNewNode(source.id)
: handleChangeDataSource(source)
}
classNames={{
menu: () => 'tj-scrollbar',
@ -311,8 +311,8 @@ function DataSourceSelect({
},
...(isFocused &&
isNested && {
'.option-nested-datasource-selector': { backgroundColor: 'var(--slate4)' },
}),
'.option-nested-datasource-selector': { backgroundColor: 'var(--slate4)' },
}),
}),
container: (styles) => ({
...styles,

View file

@ -22,14 +22,14 @@ const ParameterList = ({
let totalWidth = 0;
const formattedParams = containerWidth
? parameters.map((param, index) => {
const boxWidth = Math.min((param?.name || '').length * 6 + 63 + 8, 178);
totalWidth = Math.min(totalWidth + boxWidth, containerWidth);
return {
...param,
isVisible: totalWidth < containerWidth - 178,
index,
};
})
const boxWidth = Math.min((param?.name || '').length * 6 + 63 + 8, 178);
totalWidth = Math.min(totalWidth + boxWidth, containerWidth);
return {
...param,
isVisible: totalWidth < containerWidth - 178,
index,
};
})
: [];
setFormattedParameters(formattedParams);
if (formattedParams.every((param) => param.isVisible)) {

View file

@ -203,7 +203,11 @@ const Preview = ({ darkMode, calculatePreviewHeight }) => {
<Tab.Container activeKey={key} onSelect={(k) => setKey(k)} defaultActiveKey="raw">
<div className="position-relative h-100">
{previewLoading && (
<center style={{ display: 'grid', placeItems: 'center' }} className="position-absolute w-100 h-100" data-cy="preview-loading-container">
<center
style={{ display: 'grid', placeItems: 'center' }}
className="position-absolute w-100 h-100"
data-cy="preview-loading-container"
>
<div className="spinner-border text-azure" role="status" data-cy="preview-loading-spinner"></div>
</center>
)}

View file

@ -249,7 +249,9 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
})}
data-cy="query-events-section"
>
<div className={`form-label`} data-cy="query-manager-events-label">{t('editor.queryManager.eventsHandler', 'Events')}</div>
<div className={`form-label`} data-cy="query-manager-events-label">
{t('editor.queryManager.eventsHandler', 'Events')}
</div>
<div className="query-manager-events pb-4">
<EventManager
sourceId={selectedQuery?.id}
@ -266,7 +268,9 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
const renderTimeout = () => {
return (
<div className="d-flex" data-cy="query-timeout-section">
<div className="form-label mt-2" data-cy="query-manager-timeout-label">{t('editor.queryManager.timeout', 'Timeout ( ms )')}</div>
<div className="form-label mt-2" data-cy="query-manager-timeout-label">
{t('editor.queryManager.timeout', 'Timeout ( ms )')}
</div>
<div className="query-manager-query-timeout">
<CodeHinter
theme={darkMode ? 'monokai' : 'base16-light'}
@ -288,7 +292,9 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
})}
data-cy="query-triggers-section"
>
<div className="form-label mt-2" data-cy="query-manager-triggers-label">{t('editor.queryManager.settings', 'Triggers')}</div>
<div className="form-label mt-2" data-cy="query-manager-triggers-label">
{t('editor.queryManager.settings', 'Triggers')}
</div>
<div className="flex-grow-1">
{Object.keys(customToggles).map((toggle, index) => (
<CustomToggleFlag
@ -314,7 +320,7 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
</div>
</div>
<div className="d-flex">
<div className="form-label">{ }</div>
<div className="form-label">{}</div>
<SuccessNotificationInputs
// currentState={currentState}
options={options}
@ -339,8 +345,8 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
const docLink = isSampleDb
? 'https://docs.tooljet.com/docs/data-sources/sample-data-sources'
: selectedDataSource?.plugin_id && selectedDataSource.plugin_id.trim() !== ''
? `https://docs.tooljet.com/docs/marketplace/plugins/marketplace-plugin-${selectedDataSource?.kind}/`
: `https://docs.tooljet.com/docs/data-sources/${selectedDataSource?.kind}`;
? `https://docs.tooljet.com/docs/marketplace/plugins/marketplace-plugin-${selectedDataSource?.kind}/`
: `https://docs.tooljet.com/docs/data-sources/${selectedDataSource?.kind}`;
return (
<>
<div className="" ref={paramListContainerRef}>
@ -362,7 +368,11 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
>
Source
</div>
<div className="d-flex flex-column align-items-start" style={{ width: '500px' }} data-cy="query-manager-change-data-source">
<div
className="d-flex flex-column align-items-start"
style={{ width: '500px' }}
data-cy="query-manager-change-data-source"
>
<ChangeDataSource
dataSources={selectableDataSources}
value={selectedDataSource}
@ -406,14 +416,15 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
const hasPermissions =
selectedDataSource?.scope === 'global' && selectedDataSource?.type !== DATA_SOURCE_TYPE.SAMPLE
? canUpdateDataSource(selectedQuery?.data_source_id) ||
canReadDataSource(selectedQuery?.data_source_id) ||
canDeleteDataSource()
canReadDataSource(selectedQuery?.data_source_id) ||
canDeleteDataSource()
: true;
return (
<div
className={`query-details ${selectedDataSource?.kind === 'tooljetdb' ? 'tooljetdb-query-details' : ''} ${!hasPermissions || isFreezed ? 'disabled' : ''
}`}
className={`query-details ${selectedDataSource?.kind === 'tooljetdb' ? 'tooljetdb-query-details' : ''} ${
!hasPermissions || isFreezed ? 'disabled' : ''
}`}
style={{
height: `calc(100% - ${selectedQuery ? previewHeight + 40 : 0}px)`,
overflowY: 'auto',

View file

@ -12,7 +12,7 @@ import useStore from '@/AppBuilder/_stores/store';
import { v4 as uuidv4 } from 'uuid';
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
const noop = () => { };
const noop = () => {};
const defaultValue = {
javascript: `// write your code here

View file

@ -219,7 +219,7 @@ const HierarchicalDropdown = ({ options, value, onChange, placeholder, disabled,
{isOpen && (
<div className="grpcv2-dropdown__menu">
{(isLoading || (options.length === 0 && !debouncedSearchTerm.trim())) ? (
{isLoading || (options.length === 0 && !debouncedSearchTerm.trim()) ? (
<div className="grpcv2-dropdown__loading">Loading services...</div>
) : getFilteredOptions.length === 0 ? (
<div className="grpcv2-dropdown__no-results">No results found</div>
@ -552,10 +552,10 @@ const GRPCv2Component = ({ darkMode, selectedDataSource, ...restProps }) => {
options?.service && options?.method
? `${options.service}${options.method}`
: isLoadingServices
? 'Loading services...'
: hierarchicalOptions.length === 0
? 'No services found'
: 'Select service'
? 'Loading services...'
: hierarchicalOptions.length === 0
? 'No services found'
: 'Select service'
}
disabled={
(!options?.service || !options?.method) && (isLoadingServices || hierarchicalOptions.length === 0)
@ -728,8 +728,9 @@ const TabContent = ({
/>
</div>
<button
className={`d-flex justify-content-center align-items-center delete-field-option bg-transparent border-0 rounded-0 border-top border-bottom border-end rounded-end qm-delete-btn ${darkMode ? 'delete-field-option-dark' : ''
}`}
className={`d-flex justify-content-center align-items-center delete-field-option bg-transparent border-0 rounded-0 border-top border-bottom border-end rounded-end qm-delete-btn ${
darkMode ? 'delete-field-option-dark' : ''
}`}
role="button"
onClick={() => {
removeKeyValuePair(index);

View file

@ -37,11 +37,11 @@ export default function GraphqlKeyValueTabs({ tabs = [], options = {}, optioncha
};
const handleInputChange = (tabKey, rowIndex) => (value) => {
const rows = getRows(tabKey);
if (rows.length > 0 && rows.length - 1 === rowIndex && value) {
addNewKeyValuePair(tabKey);
}
};
const rows = getRows(tabKey);
if (rows.length > 0 && rows.length - 1 === rowIndex && value) {
addNewKeyValuePair(tabKey);
}
};
if (!tabs.length) return null;
@ -51,11 +51,7 @@ export default function GraphqlKeyValueTabs({ tabs = [], options = {}, optioncha
<div className="keys d-flex justify-content-between query-pane-tabs-header graphql-tabs-header">
<ListGroup className="query-pane-rest-api-keys-list-group mx-1 mb-2" variant="flush">
{tabs.map(({ label, key }) => (
<ListGroup.Item
key={key}
eventKey={key}
data-cy={generateCypressDataCy(`graphql-tab-${key}-button`)}
>
<ListGroup.Item key={key} eventKey={key} data-cy={generateCypressDataCy(`graphql-tab-${key}-button`)}>
<span>{label}</span>
</ListGroup.Item>
))}
@ -76,11 +72,7 @@ export default function GraphqlKeyValueTabs({ tabs = [], options = {}, optioncha
</div>
<div className="col tw-pl-0">
<Tab.Content
bsPrefix="graphql-tab-content"
className="query-manager-border-color rounded"
>
<Tab.Content bsPrefix="graphql-tab-content" className="query-manager-border-color rounded">
{tabs.map(({ key }) => (
<Tab.Pane
key={key}
@ -94,7 +86,7 @@ export default function GraphqlKeyValueTabs({ tabs = [], options = {}, optioncha
onChange={handleChange}
removeKeyValuePair={removeKeyValuePair}
addNewKeyValuePair={addNewKeyValuePair}
onInputChange={handleInputChange}
onInputChange={handleInputChange}
componentName={componentName ?? 'graphql'}
tabType={key}
paramType={key}
@ -106,4 +98,4 @@ export default function GraphqlKeyValueTabs({ tabs = [], options = {}, optioncha
</Row>
</Tab.Container>
);
}
}

View file

@ -18,9 +18,7 @@ export default function GraphqlTabContent({
return (
<div className="tab-content-wrapper" data-cy={`${generateCypressDataCy(tabType)}-tab-content`}>
{options.length === 0 && (
<EmptyTabContent addNewKeyValuePair={addNewKeyValuePair} paramType={paramType} />
)}
{options.length === 0 && <EmptyTabContent addNewKeyValuePair={addNewKeyValuePair} paramType={paramType} />}
{options.map((option, index) => (
<div
className="row-container query-manager-border-color"
@ -65,4 +63,4 @@ export default function GraphqlTabContent({
))}
</div>
);
}
}

View file

@ -18,12 +18,7 @@ export const BaseUrl = ({ dataSourceURL, theme, className = 'col-auto', style =
...style,
}}
>
<OverflowTooltip
text={dataSourceURL}
width="559px"
whiteSpace="nowrap"
placement="auto"
>
<OverflowTooltip text={dataSourceURL} width="559px" whiteSpace="nowrap" placement="auto">
{dataSourceURL}
</OverflowTooltip>
</span>

View file

@ -30,7 +30,11 @@ export default ({
options.map((option, index) => {
return (
<>
<div className="row-container query-manager-border-color" key={index} data-cy={`${generateCypressDataCy(tabType)}-row-${index}`}>
<div
className="row-container query-manager-border-color"
key={index}
data-cy={`${generateCypressDataCy(tabType)}-row-${index}`}
>
<div className="fields-container mb-1 restapi-key-value">
<div className="field col-4 rounded-start rest-api-codehinter-key-field">
<CodeHinter

View file

@ -41,7 +41,7 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => {
case 'joinTable':
return joinTableOptionsChange;
default:
return () => { };
return () => {};
}
}, [operation, handleOptionsChange, joinTableOptionsChange]);
@ -216,12 +216,12 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => {
const isAnyAggregateTruthyValue = isEmpty(currentAggregates)
? false
: Object.values(currentAggregates).some((aggregate) => {
if (aggregate.aggFx && aggregate.column) {
return true;
} else {
return false;
}
});
if (aggregate.aggFx && aggregate.column) {
return true;
} else {
return false;
}
});
return !isAnyAggregateTruthyValue;
};
const getTableName = (id) => {
@ -360,8 +360,9 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => {
Aggregate
</label>
<div
className={`field-container col d-flex custom-gap-8 flex-column ${!isEmpty(operationDetails?.aggregates) && 'minw-400-w-400'
}`}
className={`field-container col d-flex custom-gap-8 flex-column ${
!isEmpty(operationDetails?.aggregates) && 'minw-400-w-400'
}`}
>
{isEmpty(operationDetails?.aggregates || {}) && <NoCondition />}
{operationDetails?.aggregates &&
@ -391,11 +392,11 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => {
value={
operation === 'joinTable'
? constructAggregateValue(
aggregateDetails.column,
'joinTable',
'column',
aggregateDetails?.table_id
)
aggregateDetails.column,
'joinTable',
'column',
aggregateDetails?.table_id
)
: constructAggregateValue(aggregateDetails.column, 'listRows', 'column')
}
options={operation === 'joinTable' ? tableListOptions : columnAccessorsOptions}

View file

@ -64,7 +64,7 @@ const DropDownSelect = ({
loader,
isLoading = false,
columnDefaultValue = '',
setColumnDefaultValue = () => { },
setColumnDefaultValue = () => {},
showControlComponent = false,
placeholder = '',
dataCy = 'show-ds-popover-button',
@ -115,7 +115,7 @@ const DropDownSelect = ({
// onChange && onChange(selected);
const badges = document.querySelectorAll('.dd-select-value-badge');
if (isEmpty(badges)) {
return () => { };
return () => {};
}
let isNewOverFlown = false;
for (let i = 0; i < badges.length; i++) {
@ -195,27 +195,28 @@ const DropDownSelect = ({
<Popover
key={'page.i'}
id={popoverId.current}
className={`${darkMode && 'popover-dark-themed dark-theme tj-dark-mode'
} tjdb-workflow-query-editor-popover-index`}
className={`${
darkMode && 'popover-dark-themed dark-theme tj-dark-mode'
} tjdb-workflow-query-editor-popover-index`}
style={{
width: isForeignKeyInEditCell
? '300px'
: foreignKeyAccess
? '403px'
: foreignKeyAccessInRowForm === true
? '494px'
: isCellEdit
? '266px'
: '244px',
? '403px'
: foreignKeyAccessInRowForm === true
? '494px'
: isCellEdit
? '266px'
: '244px',
maxWidth: isForeignKeyInEditCell
? '300px'
: foreignKeyAccess
? '403px'
: foreignKeyAccessInRowForm === true
? '494px'
: isCellEdit
? '266px'
: '246px',
? '403px'
: foreignKeyAccessInRowForm === true
? '494px'
: isCellEdit
? '266px'
: '246px',
overflow: 'hidden',
boxShadow: '0px 2px 4px -2px rgba(16, 24, 40, 0.06), 0px 4px 8px -2px rgba(16, 24, 40, 0.10)',
}}
@ -368,8 +369,8 @@ const DropDownSelect = ({
{foreignKeyAccessInRowForm || showPlaceHolderInForeignKeyDrawer
? topPlaceHolder
: placeholder
? placeholder
: 'Select...'}
? placeholder
: 'Select...'}
</span>
) : (
''

View file

@ -249,7 +249,7 @@ function DataSourceSelect({
setIsLoadingFKDetails(false);
toast.error(
error?.message ??
`Failed to fetch table "${foreignKeys?.length > 0 && foreignKeys[0].referenced_table_name}"`
`Failed to fetch table "${foreignKeys?.length > 0 && foreignKeys[0].referenced_table_name}"`
);
return;
}
@ -286,21 +286,21 @@ function DataSourceSelect({
if (scrollPercentage > 90 && !isLoadingFKDetails) {
isEmpty(searchValue)
? fetchForeignKeyDetails(
pageNumber,
totalRecords,
isInitialForeignKeyDataLoaded,
searchValue,
foreignKeys,
organizationId
)
pageNumber,
totalRecords,
isInitialForeignKeyDataLoaded,
searchValue,
foreignKeys,
organizationId
)
: fetchForeignKeyDetails(
searchPageNumber,
totalSearchRecords,
isInitialForeignKeySearchDataLoaded,
searchValue,
foreignKeys,
organizationId
);
searchPageNumber,
totalSearchRecords,
isInitialForeignKeySearchDataLoaded,
searchValue,
foreignKeys,
organizationId
);
}
}
@ -400,12 +400,12 @@ function DataSourceSelect({
isForeignKeyInEditCell
? 'tj-scrollbar tjdb-mainCellEdit-scrollbar'
: foreignKeyAccess
? 'tj-scrollbar tjdb-dashboard-scrollbar'
: foreignKeyAccessInRowForm
? 'tj-scrollbar tjdb-rowForm-scrollbar'
: isCellEdit
? 'tj-scrollbar tjdb-cellEdit-scrollbar'
: 'tj-scrollbar',
? 'tj-scrollbar tjdb-dashboard-scrollbar'
: foreignKeyAccessInRowForm
? 'tj-scrollbar tjdb-rowForm-scrollbar'
: isCellEdit
? 'tj-scrollbar tjdb-cellEdit-scrollbar'
: 'tj-scrollbar',
}}
ref={selectRef}
controlShouldRenderValue={false}
@ -475,7 +475,7 @@ function DataSourceSelect({
id={props.value}
className="me-1"
checked={props.isSelected}
// label={`default ${type}`}
// label={`default ${type}`}
/>
</div>
)}
@ -522,9 +522,9 @@ function DataSourceSelect({
style={{
...(props.isSelected &&
highlightSelected && {
marginRight: '10px',
marginTop: '3px',
}),
marginRight: '10px',
marginTop: '3px',
}),
}}
onClick={() => {
const data = { id: props.data.id, table_name: props.data.value };
@ -632,12 +632,12 @@ function DataSourceSelect({
isSelected && highlightSelected
? 'var(--indigo3, #F0F4FF)'
: isFocused && !isNested
? 'var(--slate4)'
: isDisabled
? 'transparent'
: isDisabled && isFocused
? 'var(--slate3, #f1f3f5)'
: 'transparent',
? 'var(--slate4)'
: isDisabled
? 'transparent'
: isDisabled && isFocused
? 'var(--slate3, #f1f3f5)'
: 'transparent',
...(isNested
? { padding: '0 8px', marginLeft: '19px', borderLeft: '1px solid var(--slate5)', width: 'auto' }
: {}),
@ -648,8 +648,8 @@ function DataSourceSelect({
},
...(isFocused &&
isNested && {
'.option-nested-datasource-selector': { backgroundColor: 'var(--slate4)' },
}),
'.option-nested-datasource-selector': { backgroundColor: 'var(--slate4)' },
}),
}),
group: (style) => ({
...style,

View file

@ -130,9 +130,9 @@ const ToolJetDbOperations = ({
...acc,
...(tableInfo[newTable]
? tableInfo[newTable].map((col) => ({
name: col.Header,
table: newTable,
}))
name: col.Header,
table: newTable,
}))
: []),
],
[]
@ -332,10 +332,10 @@ const ToolJetDbOperations = ({
newFields.push(
...(data?.result?.columns
? data.result.columns.map((col) => ({
name: col.column_name,
table: tableId,
// alias: `${tableId}_${col.column_name}`,
}))
name: col.column_name,
table: tableId,
// alias: `${tableId}_${col.column_name}`,
}))
: [])
);
@ -571,14 +571,14 @@ const ToolJetDbOperations = ({
activeTab === 'GUI mode' && !darkMode
? 'white'
: activeTab === 'GUI mode' && darkMode
? '#242f3c'
: 'transparent',
? '#242f3c'
: 'transparent',
color:
activeTab === 'GUI mode' && !darkMode
? '#3E63DD'
: activeTab === 'GUI mode' && darkMode
? 'white'
: '#687076',
? 'white'
: '#687076',
}}
className="row-tab-content"
data-cy="tooljetdb-gui-mode-tab"
@ -593,14 +593,14 @@ const ToolJetDbOperations = ({
activeTab === 'SQL mode' && !darkMode
? 'white'
: activeTab === 'SQL mode' && darkMode
? '#242f3c'
: 'transparent',
? '#242f3c'
: 'transparent',
color:
activeTab === 'SQL mode' && !darkMode
? '#3E63DD'
: activeTab === 'SQL mode' && darkMode
? 'white'
: '#687076',
? 'white'
: '#687076',
}}
className="row-tab-content"
data-cy="tooljetdb-sql-mode-tab"

View file

@ -49,17 +49,20 @@ export function Workflows({ options, optionsChanged, currentState }) {
useEffect(() => {
if (options.workflowId) {
appVersionService.getAll(options.workflowId).then((data) => {
const versions = (data?.versions || [])
.filter((v) => v.status === 'PUBLISHED')
.map((v) => ({
value: v.id,
name: v.name,
}));
setVersionOptions(versions);
}).catch(() => {
setVersionOptions([]);
});
appVersionService
.getAll(options.workflowId)
.then((data) => {
const versions = (data?.versions || [])
.filter((v) => v.status === 'PUBLISHED')
.map((v) => ({
value: v.id,
name: v.name,
}));
setVersionOptions(versions);
})
.catch(() => {
setVersionOptions([]);
});
} else {
setVersionOptions([]);
}

View file

@ -22,7 +22,12 @@ const QueryManager = ({ mode, darkMode }) => {
const [activeTab, setActiveTab] = useState(1);
useEffect(() => {
if (selectedQuery?.kind == 'runjs' || selectedQuery?.kind == 'runpy' || selectedQuery?.kind == 'restapi' || selectedQuery?.kind == 'postgresql') {
if (
selectedQuery?.kind == 'runjs' ||
selectedQuery?.kind == 'runpy' ||
selectedQuery?.kind == 'restapi' ||
selectedQuery?.kind == 'postgresql'
) {
setActiveTab(1);
}
}, [selectedQuery?.id]);

View file

@ -75,10 +75,10 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
const getTooltip = () => {
const permission = dataQuery.permissions?.[0];
if (!permission) return "Access restricted";
if (!permission) return 'Access restricted';
const users = permission.groups || permission.users || [];
if (users.length === 0) return "Access restricted";
if (users.length === 0) return 'Access restricted';
const isSingle = permission.type === 'SINGLE';
const isGroup = permission.type === 'GROUP';
@ -95,7 +95,7 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
: `Access restricted to ${users.length} user groups`;
}
return "Access restricted";
return 'Access restricted';
};
return (

View file

@ -177,7 +177,8 @@ export const QueryDataPane = ({ darkMode }) => {
</div>
) : (
<div
className={`query-list tj-scrollbar overflow-auto ${filteredQueries.length === 0 ? 'flex-grow-1 align-items-center justify-content-center' : ''
className={`query-list tj-scrollbar overflow-auto ${
filteredQueries.length === 0 ? 'flex-grow-1 align-items-center justify-content-center' : ''
}`}
>
<div>

View file

@ -12,6 +12,7 @@ import { deepClone } from '@/_helpers/utilities/utils.helpers';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
import QueryKeyHooks from './QueryKeyHooks';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
const MemoizedQueryDataPane = memo(QueryDataPane);
@ -184,7 +185,13 @@ export const QueryPanel = ({ darkMode }) => {
className="d-flex items-center justify-start mb-0 font-weight-500 text-dark select-none query-manager-toggle-button tw-gap-1.5"
onClick={toggleQueryEditor}
>
<span>{isQueryPaneExpanded ? <PanelBottomClose size='14' color='var(--icon-strong)' /> : <PanelBottomOpen size='14' color='var(--icon-strong)' />}</span>
<span>
{isQueryPaneExpanded ? (
<PanelBottomClose size="14" color="var(--icon-strong)" />
) : (
<PanelBottomOpen size="14" color="var(--icon-strong)" />
)}
</span>
<span>Queries</span>
</button>
</div>

View file

@ -26,8 +26,7 @@ export const QueryRenameInput = ({ dataQuery, darkMode, onUpdate }) => {
return (
<input
data-cy={`query-edit-input-field`}
className={`query-name query-name-input-field border-indigo-09 bg-transparent ${darkMode && 'text-white'
}`}
className={`query-name query-name-input-field border-indigo-09 bg-transparent ${darkMode && 'text-white'}`}
type="text"
value={value}
onChange={handleChange}
@ -36,4 +35,4 @@ export const QueryRenameInput = ({ dataQuery, darkMode, onUpdate }) => {
onBlur={handleBlur}
/>
);
};
};

View file

@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
import Accordion from '@/_ui/Accordion';
import { baseComponentProperties } from '../DefaultComponent';
import Select from '@/_ui/Select';
import useStore from '@/AppBuilder/_stores/store';
// eslint-disable-next-line import/no-unresolved
import flags from 'react-phone-number-input/flags';
import FxButton from '@/AppBuilder/CodeBuilder/Elements/FxButton';
import CodeHinter from '@/AppBuilder/CodeEditor';

View file

@ -79,8 +79,6 @@ const FxSelect = ({
);
};
const getPropertiesBySection = (propertiesMeta) => {
const properties = [];
const additionalActions = [];
@ -98,8 +96,6 @@ const getPropertiesBySection = (propertiesMeta) => {
return { properties, additionalActions, dataProperties };
};
export const FilePicker = ({ componentMeta, darkMode, ...restProps }) => {
const {
layoutPropertyChanged,
@ -143,12 +139,12 @@ export const FilePicker = ({ componentMeta, darkMode, ...restProps }) => {
// Insert FxSelect for file type
// Note: Adjusting index if necessary, assuming properties is always first index 0.
// Properties -> 0, Events -> 1, Validation -> 2
// We need to double check where the fileType is located.
// We need to double check where the fileType is located.
// It is properly located in Validation section which is usually 3rd if Events exist.
// baseComponentProperties returns [Properties, Events, Validation, ...].
// Safe way is to find the Validation section.
const validationSection = accordionItems.find(item => item.title === 'Validation');
const validationSection = accordionItems.find((item) => item.title === 'Validation');
if (validationSection) {
// Find the index of fileType
// This part is a bit brittle in original code too: accordionItems[2].children[1]

View file

@ -8,7 +8,7 @@ import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
import { isTrueValue, isPropertyFxControlled, getComponentIcon } from '../utils/utils';
export const FormField = ({ field, onDelete, activeMenu, onMenuToggle, onSave}) => {
export const FormField = ({ field, onDelete, activeMenu, onMenuToggle, onSave }) => {
const setSelectedComponents = useStore((state) => state.setSelectedComponents, shallow);
const [showPopover, setShowPopover] = useState(false);
const [fieldData, setFieldData] = useState(field);
@ -33,6 +33,7 @@ export const FormField = ({ field, onDelete, activeMenu, onMenuToggle, onSave})
const isCurrentlyMandatory = isTrueValue(fieldData.mandatory?.value);
// eslint-disable-next-line no-constant-binary-expression
const darkMode = localStorage.getItem('darkMode') === 'true' ?? false;
const mainPopover = (
@ -126,8 +127,9 @@ export const FormField = ({ field, onDelete, activeMenu, onMenuToggle, onSave})
overlay={mainPopover}
>
<div
className={`field-item tw-flex tw-items-center tw-justify-between tw-gap-2 hover:tw-cursor-pointer ${(fieldData.name === activeMenu || showPopover) && 'selected'
}`}
className={`field-item tw-flex tw-items-center tw-justify-between tw-gap-2 hover:tw-cursor-pointer ${
(fieldData.name === activeMenu || showPopover) && 'selected'
}`}
>
<div className="tw-flex tw-items-center tw-gap-[6px] tw-flex-1" style={{ width: 'calc(100% - 100px)' }}>
<div className="field-icon tw-w-6 tw-h-6 tw-flex tw-items-center tw-justify-center tw-rounded tw-bg-gray-100">

View file

@ -38,8 +38,7 @@ export const useFieldManager = ({ component, paramUpdated, currentState }) => {
const handleRemove = useCallback(
async (removedFields) => {
// Get existing deletion history
const existingFieldDeletionHistory =
component.component.definition.properties.fieldDeletionHistory?.value ?? [];
const existingFieldDeletionHistory = component.component.definition.properties.fieldDeletionHistory?.value ?? [];
// Add removed field keys to deletion history
const newFieldDeletionHistory = [

View file

@ -28,7 +28,9 @@ export const GroupMenuItem = ({ darkMode, item, highlight, onDeleteItem, onItemC
<div style={{ position: 'relative', width: '100%' }}>
<div
ref={optionsBtnRef}
className={`page-menu-item page-group-item ${highlight ? 'highlight' : ''} ${darkMode ? 'dark-theme theme-dark' : ''} ${isEditing ? 'is-selected' : ''}`}
className={`page-menu-item page-group-item ${highlight ? 'highlight' : ''} ${
darkMode ? 'dark-theme theme-dark' : ''
} ${isEditing ? 'is-selected' : ''}`}
onClick={(e) => {
e.preventDefault();
setShowEditPopover(!showEditPopover);
@ -69,11 +71,23 @@ export const GroupMenuItem = ({ darkMode, item, highlight, onDeleteItem, onItemC
className={`${darkMode ? 'dark-theme theme-dark' : ''} nav-item-actions-popover`}
>
<Popover.Body className="p-2">
<div className="nav-item-action-option" onClick={(e) => { e.stopPropagation(); handleEdit(); }}>
<div
className="nav-item-action-option"
onClick={(e) => {
e.stopPropagation();
handleEdit();
}}
>
<TablerIcon iconName="IconPencil" size={16} stroke={1.5} className="nav-item-action-option-icon" />
<span className="nav-item-action-option-label">Edit group</span>
</div>
<div className="nav-item-action-option nav-item-action-option-danger" onClick={(e) => { e.stopPropagation(); handleDelete(); }}>
<div
className="nav-item-action-option nav-item-action-option-danger"
onClick={(e) => {
e.stopPropagation();
handleDelete();
}}
>
<TablerIcon iconName="IconTrash" size={16} stroke={1.5} className="nav-item-action-option-icon" />
<span className="nav-item-action-option-label">Delete group</span>
</div>

View file

@ -37,7 +37,13 @@ export const MenuItem = ({ darkMode, item, onDeleteItem, onItemChange, getResolv
>
<div className="left">
<div className="main-page-icon-wrapper">
<TablerIcon iconName={item?.icon?.value || 'IconFile'} fallbackIcon="IconFile" size={20} stroke={1.5} className="nav-item-icon" />
<TablerIcon
iconName={item?.icon?.value || 'IconFile'}
fallbackIcon="IconFile"
size={20}
stroke={1.5}
className="nav-item-icon"
/>
</div>
<OverflowTooltip childrenClassName="page-name">
{getSafeRenderableValue(getResolvedValue?.(item?.label) ?? item?.label)}
@ -70,11 +76,23 @@ export const MenuItem = ({ darkMode, item, onDeleteItem, onItemChange, getResolv
className={`${darkMode ? 'dark-theme theme-dark' : ''} nav-item-actions-popover`}
>
<Popover.Body className="p-2">
<div className="nav-item-action-option" onClick={(e) => { e.stopPropagation(); handleEdit(); }}>
<div
className="nav-item-action-option"
onClick={(e) => {
e.stopPropagation();
handleEdit();
}}
>
<TablerIcon iconName="IconPencil" size={16} stroke={1.5} className="nav-item-action-option-icon" />
<span className="nav-item-action-option-label">Edit menu item</span>
</div>
<div className="nav-item-action-option nav-item-action-option-danger" onClick={(e) => { e.stopPropagation(); handleDelete(); }}>
<div
className="nav-item-action-option nav-item-action-option-danger"
onClick={(e) => {
e.stopPropagation();
handleDelete();
}}
>
<TablerIcon iconName="IconTrash" size={16} stroke={1.5} className="nav-item-action-option-icon" />
<span className="nav-item-action-option-label">Delete nav item</span>
</div>

View file

@ -4,7 +4,10 @@ import CodeHinter from '@/AppBuilder/CodeEditor';
import { Button as ButtonComponent } from '@/components/ui/Button/Button.jsx';
const NavItemPopover = forwardRef(
({ item, darkMode, onItemChange, onDeleteItem, onDuplicateItem, getResolvedValue, parentId = null, ...restProps }, ref) => {
(
{ item, darkMode, onItemChange, onDeleteItem, onDuplicateItem, getResolvedValue, parentId = null, ...restProps },
ref
) => {
const iconVisibility = item?.iconVisibility;
// Common CodeHinter props
@ -85,10 +88,7 @@ const NavItemPopover = forwardRef(
<div className="nav-item-popover-fields-section">
{/* Label field */}
<div data-cy="inspector-nav-item-details-label-field" className="nav-item-popover-field">
<label
data-cy="inspector-nav-item-details-label-label"
className="nav-item-popover-field-label"
>
<label data-cy="inspector-nav-item-details-label-label" className="nav-item-popover-field-label">
Label
</label>
<CodeHinter

View file

@ -61,7 +61,11 @@ const NavItemsList = ({
}, []);
return (
<div className="navigation-inspector" data-cy="inspector-navigation-menu-items-list" style={{ marginBottom: '12px' }}>
<div
className="navigation-inspector"
data-cy="inspector-navigation-menu-items-list"
style={{ marginBottom: '12px' }}
>
<SortableTree
menuItems={menuItems}
darkMode={darkMode}

View file

@ -9,7 +9,13 @@ const GhostMenuItem = ({ item, getResolvedValue }) => {
<div className="page-menu-item" style={{ width: '100%' }}>
<div className="left">
<div className="main-page-icon-wrapper">
<TablerIcon iconName={item?.icon?.value || 'IconFile'} fallbackIcon="IconFile" size={20} stroke={1.5} className="nav-item-icon" />
<TablerIcon
iconName={item?.icon?.value || 'IconFile'}
fallbackIcon="IconFile"
size={20}
stroke={1.5}
className="nav-item-icon"
/>
</div>
<OverflowTooltip childrenClassName="page-name">
{getSafeRenderableValue(getResolvedValue?.(item?.label) ?? item?.label)}

View file

@ -79,8 +79,14 @@ export const useMenuItemsManager = (component, paramUpdated) => {
const generateNewItem = (isGroup = false) => {
const id = generateUniqueId(isGroup ? 'group' : 'item');
const icons = [
'IconHome2', 'IconLayoutDashboard', 'IconSettings', 'IconUser',
'IconFolder', 'IconFile', 'IconStar', 'IconHeart'
'IconHome2',
'IconLayoutDashboard',
'IconSettings',
'IconUser',
'IconFolder',
'IconFile',
'IconStar',
'IconHeart',
];
const randomIcon = icons[Math.floor(Math.random() * icons.length)];

View file

@ -2,9 +2,11 @@ import React, { useMemo, useState } from 'react';
import Accordion from '@/_ui/Accordion';
import { baseComponentProperties } from '../DefaultComponent';
import Select from '@/_ui/Select';
import useStore from '@/AppBuilder/_stores/store';
// eslint-disable-next-line import/no-unresolved
import { getCountries } from 'react-phone-number-input/input';
// eslint-disable-next-line import/no-unresolved
import en from 'react-phone-number-input/locale/en';
// eslint-disable-next-line import/no-unresolved
import flags from 'react-phone-number-input/flags';
import FxButton from '@/AppBuilder/CodeBuilder/Elements/FxButton';
import CodeHinter from '@/AppBuilder/CodeEditor';

View file

@ -321,7 +321,9 @@ export function Select({ componentMeta, darkMode, ...restProps }) {
</div>
<div className="field mb-2" data-cy={`input-and-label-column-name`}>
<CodeHinter
initialValue={isMultiSelect || isTagsInput ? `{{${markedAsDefault?.includes(item?.value)}}}` : item?.default?.value}
initialValue={
isMultiSelect || isTagsInput ? `{{${markedAsDefault?.includes(item?.value)}}}` : item?.default?.value
}
theme={darkMode ? 'monokai' : 'default'}
mode="javascript"
lineNumbers={false}

View file

@ -78,9 +78,11 @@ const DatepickerProperties = ({ column, index, darkMode, currentState, onColumnI
const { t } = useTranslation();
const items = [];
const [isDateDisplayFormatFxOn, setIsDateDisplayFormatFxOn] = useState(
// eslint-disable-next-line no-constant-binary-expression
!column?.notActiveFxActiveFields?.includes('dateFormat') ?? true
);
const [isParseDateFormatFxOn, setIsParseDateFormatFxOn] = useState(
// eslint-disable-next-line no-constant-binary-expression
!column?.notActiveFxActiveFields?.includes('parseDateFormat') ?? true
);

View file

@ -68,5 +68,13 @@ export const useButtonManager = ({ column, index, onColumnItemChange }) => {
return (column.buttons || []).find((b) => b.id === buttonId);
};
return { addButton, removeButton, duplicateButton, updateButtonProperty, updateButtonProperties, reorderButtons, getButton };
return {
addButton,
removeButton,
duplicateButton,
updateButtonProperty,
updateButtonProperties,
reorderButtons,
getButton,
};
};

View file

@ -1,4 +1,5 @@
import React from 'react';
// eslint-disable-next-line import/no-unresolved
import CodeMirror from '@uiw/react-codemirror';
import { ToolTip } from './Components/ToolTip';

View file

@ -2,6 +2,7 @@ import React from 'react';
import { ToolTip } from './Components/ToolTip';
export const Toggle = ({ param, definition, onChange, paramType, componentMeta }) => {
// eslint-disable-next-line no-constant-binary-expression
const value = definition?.value !== false ?? false;
const paramMeta = componentMeta[paramType][param.name];
const displayName = paramMeta.displayName || param.name;

View file

@ -21,7 +21,9 @@ export function AddNewPageMenu({ darkMode }) {
};
return (
<div className={`page-type-buttons-container d-flex justify-content-between custom-gap-12 ${darkMode && 'dark-mode'}`}>
<div
className={`page-type-buttons-container d-flex justify-content-between custom-gap-12 ${darkMode && 'dark-mode'}`}
>
<Button
ref={newPageBtnRef}
key="new-page-btn"

View file

@ -594,8 +594,9 @@ const HidePageOnNavigation = ({ hidden, darkMode, updatePageVisibility, page, is
<div className={`field`}>
<InspectorTooltip
label={`${page?.type === 'default' ? 'Hide this page on navigation' : 'Hide this item on navigation'}`}
labelClass={`tj-text-xsm color-slate12 ${forceCodeBox ? 'mb-2' : 'mb-0'} ${darkMode && 'color-whitish-darkmode'
}`}
labelClass={`tj-text-xsm color-slate12 ${forceCodeBox ? 'mb-2' : 'mb-0'} ${
darkMode && 'color-whitish-darkmode'
}`}
/>
</div>
<div className={`flex-grow-1`}>

View file

@ -173,12 +173,13 @@ const MobileNavigationMenu = ({ currentPageId, darkMode, switchDarkMode, bgStyle
sheetProps={{
container: document.getElementsByClassName('canvas-wrapper')[0],
overlayClassName: 'tw-absolute tw-h-dvh',
className: `tw-absolute tw-p-0 mobile-page-menu-popup ${isMobilePreviewMode && !isPreviewInEditor
? 'tw-h-[calc(100%_-_44px)]' // To account for the preview settings header height
: currentMode === 'view' && !isMobilePreviewMode
className: `tw-absolute tw-p-0 mobile-page-menu-popup ${
isMobilePreviewMode && !isPreviewInEditor
? 'tw-h-[calc(100%_-_44px)]' // To account for the preview settings header height
: currentMode === 'view' && !isMobilePreviewMode
? 'tw-h-dvh' // In released app, the height should equal to mobile browsers viewport height
: 'tw-h-full'
}`,
}`,
style: bgStyles,
}}
className="group-data-[side=left]:!tw-border-r-0"

View file

@ -95,7 +95,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
id="rename-page"
text="Rename"
iconSrc={'assets/images/icons/input.svg'}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => {
toggleEditPageNameInput(true);
}}
@ -105,7 +105,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
id="mark-as-home-page"
text="Mark home"
iconSrc={'assets/images/icons/home.svg'}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => markAsHomePage(editingPage.id, moduleId)}
/>
)}
@ -114,7 +114,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
id={isHidden ? 'unhide-page' : 'hide-page'}
text={isHidden ? 'Show page on app menu' : 'Hide page on app menu'}
iconSrc={`assets/images/icons/${isHidden ? 'eye' : 'eye-off'}.svg`}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => {
updatePageVisibility(editingPage.id, !editingPage.hidden);
}}
@ -135,7 +135,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
text="Event Handlers"
customClass={'delete-btn'}
iconSrc={'assets/images/icons/editor/left-sidebar/page-settings.svg'}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => {
togglePageEventsModal(true);
}}
@ -145,7 +145,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
text={isDisabled ? 'Enable' : 'Disable'}
customClass={'delete-btn'}
iconSrc={`assets/images/icons/editor/left-sidebar/${isDisabled ? 'file-accept' : 'file-remove'}.svg`}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => {
disableOrEnablePage(editingPage.id, !editingPage.disabled);
}}
@ -159,7 +159,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
text={() => {
return (
<ToolTip
message={'You don\'t have access to page permissions. Upgrade your plan to access this feature.'}
message={"You don't have access to page permissions. Upgrade your plan to access this feature."}
placement="right"
show={!hasAppPermissionPages}
>
@ -182,7 +182,7 @@ export const PageHandlerMenu = ({ darkMode }) => {
text="Delete page"
iconSrc={'assets/images/icons/delete.svg'}
customClass={isHomePage ? 'delete-btn' : 'field__danger delete-btn'}
closeMenu={() => { }}
closeMenu={() => {}}
callback={() => {
toggleDeleteConfirmationModal(true);
}}

View file

@ -277,8 +277,9 @@ export const PageMenuItem = withRouter(
>
<>
<div
className={`page-menu-item ${darkMode && 'dark-theme theme-dark'} ${(showPageOptions || showEditPopover) && isEditingPage ? 'is-selected' : ''
}`}
className={`page-menu-item ${darkMode && 'dark-theme theme-dark'} ${
(showPageOptions || showEditPopover) && isEditingPage ? 'is-selected' : ''
}`}
style={{
position: 'relative',
width: '100%',
@ -458,7 +459,9 @@ export const PageMenuItem = withRouter(
<PageOptions
text={
<ToolTip
message={'You don\'t have access to page permissions. Upgrade your plan to access this feature.'}
message={
"You don't have access to page permissions. Upgrade your plan to access this feature."
}
placement="auto"
show={!hasAppPermissionPages}
tooltipClassName="!tw-z-[100000]"

View file

@ -22,32 +22,34 @@ export function SortableTree({ collapsible, indicator = false, indentationWidth
const treeItems = useMemo(() => buildTree(allpages, PROPERTY_NAMES), [allpages]);
// When reorder happens, flatten and persist via debounced store action
const handleReorder = useCallback((newTreeItems) => {
const flatItems = flattenTree(newTreeItems, PROPERTY_NAMES);
debouncedReorderPages(flatItems);
}, [debouncedReorderPages]);
const handleReorder = useCallback(
(newTreeItems) => {
const flatItems = flattenTree(newTreeItems, PROPERTY_NAMES);
debouncedReorderPages(flatItems);
},
[debouncedReorderPages]
);
const renderItem = useCallback((item, props) => {
if (!item?.isPageGroup) {
const renderItem = useCallback(
(item, props) => {
if (!item?.isPageGroup) {
return <PageMenuItem darkMode={darkMode} page={item} treeRef={treeRef} />;
}
return (
<PageMenuItem darkMode={darkMode} page={item} treeRef={treeRef} />
<PageGroupItem
darkMode={darkMode}
highlight={props.isHighlighted}
collapsed={props.collapsed}
onCollapse={props.onCollapse}
page={item}
treeRef={treeRef}
/>
);
}
return (
<PageGroupItem
darkMode={darkMode}
highlight={props.isHighlighted}
collapsed={props.collapsed}
onCollapse={props.onCollapse}
page={item}
treeRef={treeRef}
/>
);
}, [darkMode, treeRef]);
},
[darkMode, treeRef]
);
const renderGhost = useCallback((item) => (
<PageMenuItemGhost darkMode={darkMode} page={item} />
), [darkMode]);
const renderGhost = useCallback((item) => <PageMenuItemGhost darkMode={darkMode} page={item} />, [darkMode]);
return (
<SharedSortableTree

View file

@ -32,9 +32,7 @@ export const SidebarItem = forwardRef(
data-cy={`right-sidebar-${generateCypressDataCy(typeof tip === 'object' ? icon : tip) || 'unknown'}-button`}
>
{children && (
<div
className={`sidebar-svg-icon position-relative ${selectedSidebarItem && 'sidebar-item'}`}
>
<div className={`sidebar-svg-icon position-relative ${selectedSidebarItem && 'sidebar-item'}`}>
{children}
</div>
)}

View file

@ -43,12 +43,15 @@ const PreviewSettings = ({ isMobileLayout, showHeader, darkMode }) => {
Preview settings
</span>
{editingVersion && appType !== 'module' && (
<Suspense fallback={
<div className="d-flex justify-content-center" style={{ width: '304px' }}>
<div className="d-flex align-items-center" style={{ width: '16px', height: '16px' }}>
<Loader width={16} height={16} />
<Suspense
fallback={
<div className="d-flex justify-content-center" style={{ width: '304px' }}>
<div className="d-flex align-items-center" style={{ width: '16px', height: '16px' }}>
<Loader width={16} height={16} />
</div>
</div>
</div>}>
}
>
<AppVersionsManager darkMode={darkMode} />
<div className="navbar-seperator"></div>
<AppEnvironments darkMode={darkMode} />

View file

@ -179,8 +179,8 @@ export const Viewer = ({
isPagesSidebarHidden || currentLayout === 'mobile'
? 'auto'
: position === 'top'
? '0px'
: '256px',
? '0px'
: '256px',
}}
>
<div

View file

@ -1,161 +1,161 @@
export const audioRecorderConfig = {
name: 'AudioRecorder',
displayName: 'Audio Recorder',
description: 'Records audio',
component: 'AudioRecorder',
defaultSize: {
width: 10,
height: 70,
name: 'AudioRecorder',
displayName: 'Audio Recorder',
description: 'Records audio',
component: 'AudioRecorder',
defaultSize: {
width: 10,
height: 70,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: { schema: { type: 'string' } },
accordian: 'Content',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onRecordingStart: { displayName: 'On recording start' },
onRecordingSave: { displayName: 'On recording save' },
},
styles: {
recorderIcon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
visibility: false,
accordian: 'recorder',
},
recorderIconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
defaultValue: '#F6430D',
accordian: 'recorder',
},
labelColor: {
type: 'colorSwatches',
displayName: 'Label text',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-text)',
accordian: 'recorder',
},
accentColor: {
type: 'colorSwatches',
displayName: 'Accent color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-brand)',
accordian: 'recorder',
},
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-surface1-surface)',
accordian: 'container',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-default-border)',
accordian: 'container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
defaultValue: 6,
accordian: 'container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: '0px 0px 0px 0px #00000040',
accordian: 'container',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
isLoading: false,
dataURL: null,
},
actions: [
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'resetAudio',
displayName: 'Reset audio',
},
],
definition: {
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: { schema: { type: 'string' } },
accordian: 'Content',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onRecordingStart: { displayName: 'On recording start' },
onRecordingSave: { displayName: 'On recording save' },
label: { value: 'Click to start recording' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
recorderIcon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
visibility: false,
accordian: 'recorder',
},
recorderIconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
defaultValue: '#F6430D',
accordian: 'recorder',
},
labelColor: {
type: 'colorSwatches',
displayName: 'Label text',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-text)',
accordian: 'recorder',
},
accentColor: {
type: 'colorSwatches',
displayName: 'Accent color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-brand)',
accordian: 'recorder',
},
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-surface1-surface)',
accordian: 'container',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-default-border)',
accordian: 'container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
defaultValue: 6,
accordian: 'container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: '0px 0px 0px 0px #00000040',
accordian: 'container',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
isLoading: false,
dataURL: null,
},
actions: [
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'resetAudio',
displayName: 'Reset audio',
}
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Click to start recording' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
recorderIcon: { value: 'IconMicrophone' },
recorderIconColor: { value: '#F6430D' },
labelColor: { value: 'var(--cc-primary-text)' },
accentColor: { value: 'var(--cc-primary-brand)' },
backgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColor: { value: 'var(--cc-default-border)' },
borderRadius: { value: '{{6}}' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
iconVisibility: { value: true },
},
recorderIcon: { value: 'IconMicrophone' },
recorderIconColor: { value: '#F6430D' },
labelColor: { value: 'var(--cc-primary-text)' },
accentColor: { value: 'var(--cc-primary-brand)' },
backgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColor: { value: 'var(--cc-default-border)' },
borderRadius: { value: '{{6}}' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
iconVisibility: { value: true },
},
},
};

View file

@ -1,142 +1,142 @@
export const cameraConfig = {
name: 'Camera',
displayName: 'Camera',
description: 'Captures video & photos from camera',
component: 'Camera',
defaultSize: {
width: 15,
height: 500,
name: 'Camera',
displayName: 'Camera',
description: 'Captures video & photos from camera',
component: 'Camera',
defaultSize: {
width: 15,
height: 500,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
content: {
type: 'switch',
displayName: 'Content',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Image', value: 'image' },
{ displayName: 'Video', value: 'video' },
],
accordian: 'Content',
defaultValue: 'image',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onRecordingStart: { displayName: 'On recording start' },
onRecordingSave: { displayName: 'On recording save' },
onImageSave: { displayName: 'On image save' },
},
styles: {
textColor: {
type: 'colorSwatches',
displayName: 'Text color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-text)',
accordian: 'recorder',
},
accentColor: {
type: 'colorSwatches',
displayName: 'Accent color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-brand)',
accordian: 'recorder',
},
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-surface1-surface)',
accordian: 'container',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-default-border)',
accordian: 'container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
defaultValue: 6,
accordian: 'container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: '0px 0px 0px 0px #00000040',
accordian: 'container',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
imageDataURL: null,
videoDataURL: null,
},
actions: [
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'resetVideo',
displayName: 'Reset video',
},
{
handle: 'resetImage',
displayName: 'Reset image',
},
],
definition: {
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
content: {
type: 'switch',
displayName: 'Content',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Image', value: 'image' },
{ displayName: 'Video', value: 'video' },
],
accordian: 'Content',
defaultValue: 'image',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onRecordingStart: { displayName: 'On recording start' },
onRecordingSave: { displayName: 'On recording save' },
onImageSave: { displayName: 'On image save' },
content: { value: 'image' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
textColor: {
type: 'colorSwatches',
displayName: 'Text color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-text)',
accordian: 'recorder',
},
accentColor: {
type: 'colorSwatches',
displayName: 'Accent color',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-primary-brand)',
accordian: 'recorder',
},
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-surface1-surface)',
accordian: 'container',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' } },
defaultValue: 'var(--cc-default-border)',
accordian: 'container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
defaultValue: 6,
accordian: 'container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: '0px 0px 0px 0px #00000040',
accordian: 'container',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
imageDataURL: null,
videoDataURL: null,
},
actions: [
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'resetVideo',
displayName: 'Reset video',
},
{
handle: 'resetImage',
displayName: 'Reset image',
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
content: { value: 'image' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
textColor: { value: 'var(--cc-primary-text)' },
accentColor: { value: 'var(--cc-primary-brand)' },
backgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColor: { value: 'var(--cc-default-border)' },
borderRadius: { value: '{{6}}' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
},
textColor: { value: 'var(--cc-primary-text)' },
accentColor: { value: 'var(--cc-primary-brand)' },
backgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColor: { value: 'var(--cc-default-border)' },
borderRadius: { value: '{{6}}' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
},
},
};

View file

@ -1,29 +1,335 @@
export const chatConfig = {
name: 'Chat',
displayName: 'Chat',
description: 'Chat interface with message history',
component: 'Chat',
defaultSize: {
width: 15,
height: 400,
name: 'Chat',
displayName: 'Chat',
description: 'Chat interface with message history',
component: 'Chat',
defaultSize: {
width: 15,
height: 400,
},
properties: {
chatTitle: {
type: 'code',
displayName: 'Chat title',
validation: {
schema: { type: 'string' },
defaultValue: 'Chat',
},
},
initialChat: {
type: 'code',
displayName: 'Initial chat',
validation: {
schema: {
type: 'array',
element: { type: 'object' },
defaultValue: `{{[{
message: 'Ask me anything!',
messageId: 'e3dd6f60-d5e8-46c5-b73b-006f2f4a34f2',
timestamp: 'new Date().toISOString()',
name: 'Assistant',
avatar: '',
type: 'response',
},
{
message: 'Explain software development cycle',
messageId: 'aad219d2-0349-4f61-a959-424bf62795f6',
timestamp: 'new Date().toISOString()',
name: 'User',
avatar: '',
type: 'message',
}]}}`,
},
},
},
userName: {
type: 'code',
displayName: 'User name',
validation: {
schema: { type: 'string' },
defaultValue: '{{globals.currentUser.firstName}}',
},
},
userAvatar: {
type: 'code',
displayName: 'User avatar',
validation: {
schema: { type: 'string' },
defaultValue: '',
},
},
respondentName: {
type: 'code',
displayName: 'Respondent name',
validation: {
schema: { type: 'string' },
defaultValue: 'Assistant',
},
},
respondentAvatar: {
type: 'code',
displayName: 'Respondent avatar',
validation: {
schema: { type: 'string' },
defaultValue: '',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disableInput: {
type: 'toggle',
displayName: 'Disable input state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
loadingHistory: {
type: 'toggle',
displayName: 'History loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
loadingResponse: {
type: 'toggle',
displayName: 'Response loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
enableClearHistoryButton: {
type: 'toggle',
displayName: 'Enable clear history icon',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
enableDownloadHistoryButton: {
type: 'toggle',
displayName: 'Enable download history icon',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
placeholder: {
type: 'code',
displayName: 'Placeholder for input field',
validation: {
schema: { type: 'string' },
defaultValue: 'Ask me anything!',
},
},
},
events: {
onMessageSent: { displayName: 'On message sent' },
onClearHistory: { displayName: 'On history cleared' },
},
styles: {
name: {
type: 'colorSwatches',
displayName: 'Name',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Message',
},
message: {
type: 'colorSwatches',
displayName: 'Message',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Message',
},
timestamp: {
type: 'colorSwatches',
displayName: 'Timestamp',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-placeholder-text)',
},
accordian: 'Message',
},
backgroundColorField: {
type: 'colorSwatches',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-surface1-surface)',
},
accordian: 'Field',
},
borderColorField: {
type: 'colorSwatches',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-default-border)',
},
accordian: 'Field',
},
accentColorField: {
type: 'colorSwatches',
displayName: 'Accent',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-brand)',
},
accordian: 'Field',
},
textColorField: {
type: 'colorSwatches',
displayName: 'Text',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Field',
},
sendIconColorField: {
type: 'colorSwatches',
displayName: 'Send icon',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-brand)',
},
accordian: 'Field',
},
containerBackgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-surface1-surface)',
},
accordian: 'Container',
},
borderColorContainer: {
type: 'colorSwatches',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-default-border)',
},
accordian: 'Container',
},
boxShadowContainer: {
type: 'colorSwatches',
displayName: 'Box shadow',
validation: {
schema: { type: 'string' },
defaultValue: '#121212',
},
accordian: 'Container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 6,
},
accordian: 'Container',
},
},
exposedVariables: {
history: [],
isHistoryLoading: false,
isInputDisabled: false,
isResponseLoading: false,
isVisible: true,
lastMessage: {},
lastResponse: {},
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
actions: [
{
handle: 'appendHistory',
displayName: 'Append history',
params: [{ handle: 'message', displayName: 'Message', defaultValue: '{{{}}}', type: 'code' }],
},
{
handle: 'clearHistory',
displayName: 'Clear history',
params: [],
},
{
handle: 'downloadChat',
displayName: 'Download chat',
params: [],
},
{
handle: 'sendMessage',
displayName: 'Send message',
params: [{ handle: 'message', displayName: 'Message', defaultValue: '{{{}}}', type: 'code' }],
},
{
handle: 'setError',
displayName: 'Set error',
params: [{ handle: 'error', displayName: 'Error', defaultValue: '', type: 'code' }],
},
{
handle: 'setHistory',
displayName: 'Set history',
params: [{ handle: 'history', displayName: 'History', defaultValue: '{{[]}}', type: 'code' }],
},
{
handle: 'setHistoryLoading',
displayName: 'Set history loading',
params: [{ handle: 'loading', displayName: 'Loading', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setInputDisable',
displayName: 'Set input disable',
params: [{ handle: 'disabled', displayName: 'Disabled', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setResponderAvatar',
displayName: 'Set respondent avatar',
params: [{ handle: 'avatar', displayName: 'Avatar', defaultValue: '', type: 'code' }],
},
{
handle: 'setResponseLoading',
displayName: 'Set response loading',
params: [{ handle: 'loading', displayName: 'Loading', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setUserAvatar',
displayName: 'Set user avatar',
params: [{ handle: 'avatar', displayName: 'Avatar', defaultValue: '', type: 'code' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'visible', displayName: 'Visible', defaultValue: '{{true}}', type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
chatTitle: {
type: 'code',
displayName: 'Chat title',
validation: {
schema: { type: 'string' },
defaultValue: 'Chat',
},
},
initialChat: {
type: 'code',
displayName: 'Initial chat',
validation: {
schema: {
type: 'array',
element: { type: 'object' },
defaultValue: `{{[{
chatTitle: { value: 'Chat' },
initialChat: {
value: `{{[{
message: 'Ask me anything!',
messageId: 'e3dd6f60-d5e8-46c5-b73b-006f2f4a34f2',
timestamp: 'new Date().toISOString()',
@ -39,339 +345,33 @@ export const chatConfig = {
avatar: '',
type: 'message',
}]}}`,
},
},
},
userName: {
type: 'code',
displayName: 'User name',
validation: {
schema: { type: 'string' },
defaultValue: '{{globals.currentUser.firstName}}',
},
},
userAvatar: {
type: 'code',
displayName: 'User avatar',
validation: {
schema: { type: 'string' },
defaultValue: '',
},
},
respondentName: {
type: 'code',
displayName: 'Respondent name',
validation: {
schema: { type: 'string' },
defaultValue: 'Assistant',
},
},
respondentAvatar: {
type: 'code',
displayName: 'Respondent avatar',
validation: {
schema: { type: 'string' },
defaultValue: '',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disableInput: {
type: 'toggle',
displayName: 'Disable input state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
loadingHistory: {
type: 'toggle',
displayName: 'History loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
loadingResponse: {
type: 'toggle',
displayName: 'Response loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
enableClearHistoryButton: {
type: 'toggle',
displayName: 'Enable clear history icon',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
enableDownloadHistoryButton: {
type: 'toggle',
displayName: 'Enable download history icon',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
placeholder: {
type: 'code',
displayName: 'Placeholder for input field',
validation: {
schema: { type: 'string' },
defaultValue: 'Ask me anything!',
},
},
},
events: {
onMessageSent: { displayName: 'On message sent' },
onClearHistory: { displayName: 'On history cleared' },
},
userName: { value: '{{globals.currentUser.firstName}}' },
userAvatar: { value: '' },
respondentName: { value: 'Assistant' },
respondentAvatar: { value: '' },
visibility: { value: '{{true}}' },
disableInput: { value: '{{false}}' },
loadingHistory: { value: '{{false}}' },
loadingResponse: { value: '{{false}}' },
enableClearHistoryButton: { value: '{{true}}' },
enableDownloadHistoryButton: { value: '{{true}}' },
placeholder: { value: 'Ask me anything!' },
},
events: [],
styles: {
name: {
type: 'colorSwatches',
displayName: 'Name',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Message',
},
message: {
type: 'colorSwatches',
displayName: 'Message',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Message',
},
timestamp: {
type: 'colorSwatches',
displayName: 'Timestamp',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-placeholder-text)',
},
accordian: 'Message',
},
backgroundColorField: {
type: 'colorSwatches',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-surface1-surface)',
},
accordian: 'Field',
},
borderColorField: {
type: 'colorSwatches',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-default-border)',
},
accordian: 'Field',
},
accentColorField: {
type: 'colorSwatches',
displayName: 'Accent',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-brand)',
},
accordian: 'Field',
},
textColorField: {
type: 'colorSwatches',
displayName: 'Text',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-text)',
},
accordian: 'Field',
},
sendIconColorField: {
type: 'colorSwatches',
displayName: 'Send icon',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-primary-brand)',
},
accordian: 'Field',
},
containerBackgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-surface1-surface)',
},
accordian: 'Container',
},
borderColorContainer: {
type: 'colorSwatches',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: 'var(--cc-default-border)',
},
accordian: 'Container',
},
boxShadowContainer: {
type: 'colorSwatches',
displayName: 'Box shadow',
validation: {
schema: { type: 'string' },
defaultValue: '#121212',
},
accordian: 'Container',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 6,
},
accordian: 'Container',
},
},
exposedVariables: {
history: [],
isHistoryLoading: false,
isInputDisabled: false,
isResponseLoading: false,
isVisible: true,
lastMessage: {},
lastResponse: {},
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
actions: [
{
handle: 'appendHistory',
displayName: 'Append history',
params: [{ handle: 'message', displayName: 'Message', defaultValue: '{{{}}}', type: 'code' }],
},
{
handle: 'clearHistory',
displayName: 'Clear history',
params: [],
},
{
handle: 'downloadChat',
displayName: 'Download chat',
params: [],
},
{
handle: 'sendMessage',
displayName: 'Send message',
params: [{ handle: 'message', displayName: 'Message', defaultValue: '{{{}}}', type: 'code' }],
},
{
handle: 'setError',
displayName: 'Set error',
params: [{ handle: 'error', displayName: 'Error', defaultValue: '', type: 'code' }],
},
{
handle: 'setHistory',
displayName: 'Set history',
params: [{ handle: 'history', displayName: 'History', defaultValue: '{{[]}}', type: 'code' }],
},
{
handle: 'setHistoryLoading',
displayName: 'Set history loading',
params: [{ handle: 'loading', displayName: 'Loading', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setInputDisable',
displayName: 'Set input disable',
params: [{ handle: 'disabled', displayName: 'Disabled', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setResponderAvatar',
displayName: 'Set respondent avatar',
params: [{ handle: 'avatar', displayName: 'Avatar', defaultValue: '', type: 'code' }],
},
{
handle: 'setResponseLoading',
displayName: 'Set response loading',
params: [{ handle: 'loading', displayName: 'Loading', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setUserAvatar',
displayName: 'Set user avatar',
params: [{ handle: 'avatar', displayName: 'Avatar', defaultValue: '', type: 'code' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'visible', displayName: 'Visible', defaultValue: '{{true}}', type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
chatTitle: { value: 'Chat' },
initialChat: {
value: `{{[{
message: 'Ask me anything!',
messageId: 'e3dd6f60-d5e8-46c5-b73b-006f2f4a34f2',
timestamp: 'new Date().toISOString()',
name: 'Assistant',
avatar: '',
type: 'response',
},
{
message: 'Explain software development cycle',
messageId: 'aad219d2-0349-4f61-a959-424bf62795f6',
timestamp: 'new Date().toISOString()',
name: 'User',
avatar: '',
type: 'message',
}]}}`,
},
userName: { value: '{{globals.currentUser.firstName}}' },
userAvatar: { value: '' },
respondentName: { value: 'Assistant' },
respondentAvatar: { value: '' },
visibility: { value: '{{true}}' },
disableInput: { value: '{{false}}' },
loadingHistory: { value: '{{false}}' },
loadingResponse: { value: '{{false}}' },
enableClearHistoryButton: { value: '{{true}}' },
enableDownloadHistoryButton: { value: '{{true}}' },
placeholder: { value: 'Ask me anything!' },
},
events: [],
styles: {
name: { value: 'var(--cc-primary-text)' },
message: { value: 'var(--cc-primary-text)' },
timestamp: { value: 'var(--cc-placeholder-text)' },
backgroundColorField: { value: 'var(--cc-surface1-surface)' },
borderColorField: { value: 'var(--cc-default-border)' },
accentColorField: { value: 'var(--cc-primary-brand)' },
textColorField: { value: 'var(--cc-primary-text)' },
sendIconColorField: { value: 'var(--cc-primary-brand)' },
containerBackgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColorContainer: { value: 'var(--cc-weak-border)' },
boxShadowContainer: { value: '#121212' },
borderRadius: { value: '{{6}}' },
},
name: { value: 'var(--cc-primary-text)' },
message: { value: 'var(--cc-primary-text)' },
timestamp: { value: 'var(--cc-placeholder-text)' },
backgroundColorField: { value: 'var(--cc-surface1-surface)' },
borderColorField: { value: 'var(--cc-default-border)' },
accentColorField: { value: 'var(--cc-primary-brand)' },
textColorField: { value: 'var(--cc-primary-text)' },
sendIconColorField: { value: 'var(--cc-primary-brand)' },
containerBackgroundColor: { value: 'var(--cc-surface1-surface)' },
borderColorContainer: { value: 'var(--cc-weak-border)' },
boxShadowContainer: { value: '#121212' },
borderRadius: { value: '{{6}}' },
},
},
};

View file

@ -37,32 +37,6 @@ export const dividerConfig = {
},
},
events: {},
styles: {
dividerColor: {
type: 'colorSwatches',
displayName: 'Divider color',
validation: {
schema: { type: 'string' },
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {},
styles: {
dividerColor: {
type: 'colorSwatches',

View file

@ -85,7 +85,7 @@ export const iframeConfig = {
{
handle: 'reload',
displayName: 'Reload',
}
},
],
definition: {
others: {

View file

@ -186,14 +186,14 @@ export const modalV2Config = {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
accordian: 'trigger',
accordian: 'trigger button',
visibility: false,
},
iconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
accordian: 'trigger',
accordian: 'trigger button',
visibility: false,
},
direction: {
@ -206,7 +206,7 @@ export const modalV2Config = {
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'trigger',
accordian: 'trigger button',
},
headerBackgroundColor: {
type: 'colorSwatches',
@ -271,32 +271,6 @@ export const modalV2Config = {
},
accordian: 'trigger button',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
accordian: 'trigger button',
visibility: false,
},
iconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
accordian: 'trigger button',
visibility: false,
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' } },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'trigger button',
},
},
exposedVariables: {
show: false,

View file

@ -1,289 +1,288 @@
export const popoverMenuConfig = {
name: 'PopoverMenu',
displayName: 'Popover Menu',
description: 'Popover Menu',
component: 'PopoverMenu',
defaultSize: {
width: 6,
height: 40,
name: 'PopoverMenu',
displayName: 'Popover Menu',
description: 'Popover Menu',
component: 'PopoverMenu',
defaultSize: {
width: 6,
height: 40,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Button label',
validation: { schema: { type: 'string' }, defaultValue: 'Menu' },
accordian: 'Menu',
},
buttonType: {
type: 'switch',
displayName: 'Button type',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Primary', value: 'primary' },
{ displayName: 'Outline', value: 'outline' },
],
accordian: 'Menu',
},
trigger: {
type: 'switch',
displayName: 'Show menu',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'On hover', value: 'hover' },
{ displayName: 'On click', value: 'click' },
],
accordian: 'Menu',
},
advanced: {
type: 'toggle',
displayName: 'Dynamic options',
validation: {
schema: { type: 'boolean' },
},
accordian: 'Options',
},
schema: {
type: 'code',
displayName: 'Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
accordian: 'Options',
},
optionsLoadingState: {
type: 'toggle',
displayName: 'Options loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
accordian: 'Options',
conditionallyRender: {
key: 'advanced',
value: true,
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onSelect: { displayName: 'On select' },
},
styles: {
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-brand)' },
conditionallyRender: {
key: 'buttonType',
value: 'primary',
},
accordian: 'Menu',
},
textColor: {
type: 'colorSwatches',
displayName: 'Text',
validation: {
schema: { type: 'string' },
defaultValue: '#FFFFFF',
},
accordian: 'Menu',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-brand)' },
accordian: 'Menu',
},
loaderColor: {
type: 'colorSwatches',
displayName: 'Loader',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-surface1-surface)' },
accordian: 'Menu',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
accordian: 'Menu',
visibility: false,
},
iconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
accordian: 'Menu',
visibility: false,
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' } },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'Menu',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
accordian: 'Menu',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
conditionallyRender: {
key: 'buttonType',
value: 'primary',
},
accordian: 'Menu',
},
optionsTextColor: {
type: 'colorSwatches',
displayName: 'Label',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-text)' },
accordian: 'Options',
},
optionsIconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-default-icon)' },
visibility: false,
accordian: 'Options',
},
optionsDescriptionColor: {
type: 'colorSwatches',
displayName: 'Description',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-placeholder-text)' },
accordian: 'Options',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
isLoading: false,
},
actions: [
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: {
type: 'code',
displayName: 'Button label',
validation: { schema: { type: 'string' }, defaultValue: 'Menu' },
accordian: 'Menu',
},
buttonType: {
type: 'switch',
displayName: 'Button type',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Primary', value: 'primary' },
{ displayName: 'Outline', value: 'outline' },
],
accordian: 'Menu',
},
trigger: {
type: 'switch',
displayName: 'Show menu',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'On hover', value: 'hover' },
{ displayName: 'On click', value: 'click' },
],
accordian: 'Menu',
},
advanced: {
type: 'toggle',
displayName: 'Dynamic options',
validation: {
schema: { type: 'boolean' },
},
accordian: 'Options',
},
schema: {
type: 'code',
displayName: 'Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
accordian: 'Options',
},
optionsLoadingState: {
type: 'toggle',
displayName: 'Options loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
accordian: 'Options',
conditionallyRender: {
key: 'advanced',
value: true,
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onSelect: { displayName: 'On select' },
label: { value: 'Menu' },
buttonType: { value: 'primary' },
trigger: { value: 'click' },
advanced: { value: '{{false}}' },
schema: {
value:
'{{[{"label":"option1","description":"","value":"1","icon":"IconBolt", "iconVisibility":false, "disable":false,"visible":true},{"label":"option2","description":"","value":"2","icon":"IconBulb", "iconVisibility":false, "disable":false,"visible":true},{"label":"option3","description":"","value":"3","icon":"IconTag", "iconVisibility":false, "disable":false,"visible":true}]}}',
},
options: {
value: [
{
format: 'plain',
label: 'option1',
description: '',
value: '1',
icon: { value: 'IconBolt' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
{
format: 'plain',
label: 'option2',
description: '',
value: '2',
icon: { value: 'IconBulb' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
{
format: 'plain',
label: 'option3',
description: '',
value: '3',
icon: { value: 'IconTag' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
],
},
optionsLoadingState: { value: '{{false}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
backgroundColor: {
type: 'colorSwatches',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-brand)' },
conditionallyRender: {
key: 'buttonType',
value: 'primary',
},
accordian: 'Menu',
},
textColor: {
type: 'colorSwatches',
displayName: 'Text',
validation: {
schema: { type: 'string' },
defaultValue: '#FFFFFF',
},
accordian: 'Menu',
},
borderColor: {
type: 'colorSwatches',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-brand)' },
accordian: 'Menu',
},
loaderColor: {
type: 'colorSwatches',
displayName: 'Loader',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-surface1-surface)' },
accordian: 'Menu',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
accordian: 'Menu',
visibility: false,
},
iconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
accordian: 'Menu',
visibility: false,
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' } },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'Menu',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
accordian: 'Menu',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
conditionallyRender: {
key: 'buttonType',
value: 'primary',
},
accordian: 'Menu',
},
optionsTextColor: {
type: 'colorSwatches',
displayName: 'Label',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-primary-text)' },
accordian: 'Options',
},
optionsIconColor: {
type: 'colorSwatches',
displayName: 'Icon color',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-default-icon)' },
visibility: false,
accordian: 'Options',
},
optionsDescriptionColor: {
type: 'colorSwatches',
displayName: 'Description',
validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-placeholder-text)' },
accordian: 'Options',
},
},
exposedVariables: {
isVisible: true,
isDisabled: false,
isLoading: false,
},
actions: [
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
}
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Menu' },
buttonType: { value: 'primary' },
trigger: { value: 'click' },
advanced: { value: '{{false}}' },
schema: {
value:
'{{[{"label":"option1","description":"","value":"1","icon":"IconBolt", "iconVisibility":false, "disable":false,"visible":true},{"label":"option2","description":"","value":"2","icon":"IconBulb", "iconVisibility":false, "disable":false,"visible":true},{"label":"option3","description":"","value":"3","icon":"IconTag", "iconVisibility":false, "disable":false,"visible":true}]}}',
},
options: {
value: [
{
format: 'plain',
label: 'option1',
description: '',
value: '1',
icon: { value: 'IconBolt' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
{
format: 'plain',
label: 'option2',
description: '',
value: '2',
icon: { value: 'IconBulb' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
{
format: 'plain',
label: 'option3',
description: '',
value: '3',
icon: { value: 'IconTag' },
iconVisibility: false,
disable: { value: false },
visible: { value: true },
},
],
},
optionsLoadingState: { value: '{{false}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
backgroundColor: { value: 'var(--cc-primary-brand)' },
textColor: { value: '#FFFFFF' },
borderColor: { value: 'var(--cc-primary-brand)' },
loaderColor: { value: 'var(--cc-surface1-surface)' },
icon: { value: 'IconMenu2' },
iconVisibility: { value: '{{true}}' },
iconColor: { value: '#FFFFFF' },
direction: { value: 'left' },
borderRadius: { value: '6' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
optionsTextColor: { value: 'var(--cc-primary-text)' },
optionsIconColor: { value: 'var(--cc-default-icon)' },
optionsDescriptionColor: { value: 'var(--cc-placeholder-text)' },
},
backgroundColor: { value: 'var(--cc-primary-brand)' },
textColor: { value: '#FFFFFF' },
borderColor: { value: 'var(--cc-primary-brand)' },
loaderColor: { value: 'var(--cc-surface1-surface)' },
icon: { value: 'IconMenu2' },
iconVisibility: { value: '{{true}}' },
iconColor: { value: '#FFFFFF' },
direction: { value: 'left' },
borderRadius: { value: '6' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
optionsTextColor: { value: 'var(--cc-primary-text)' },
optionsIconColor: { value: 'var(--cc-default-icon)' },
optionsDescriptionColor: { value: 'var(--cc-placeholder-text)' },
},
},
};

View file

@ -113,4 +113,4 @@ export const rangeSliderConfig = {
visibility: { value: '{{true}}' },
},
},
};
};

View file

@ -368,7 +368,6 @@ export const rangeSliderV2Config = {
auto: { value: '{{true}}' },
padding: { value: 'default' },
visibility: { value: '{{true}}' },
padding: { value: 'default' },
widthType: { value: 'ofComponent' },
},
},

View file

@ -75,8 +75,7 @@ export const BaseInput = ({
const { label, placeholder } = properties;
const _width = getLabelWidthOfInput(widthType, width);
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
const hasLabel =
(label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0);
const hasLabel = (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0);
const hasValue = value !== '' && value !== null && value !== undefined;
const shouldShowClearBtn = showClearBtn && hasValue && !disable && !loading;
@ -84,8 +83,8 @@ export const BaseInput = ({
color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
? textColor
: disable || loading
? 'var(--text-disabled)'
: 'var(--text-primary)',
? 'var(--text-disabled)'
: 'var(--text-primary)',
textOverflow: 'ellipsis',
backgroundColor: 'inherit',
};
@ -145,11 +144,12 @@ export const BaseInput = ({
return (
<>
<div
className={`text-input scrollbar-container d-flex ${defaultAlignment === 'top' &&
className={`text-input scrollbar-container d-flex ${
defaultAlignment === 'top' &&
((width != 0 && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
? 'flex-column'
: ''
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
? 'flex-column'
: ''
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
${direction === 'right' && defaultAlignment === 'top' ? 'text-right' : ''}
${visibility || 'invisible'}`}
style={{
@ -195,30 +195,30 @@ export const BaseInput = ({
!isValid && showValidationError
? 'var(--cc-error-systemStatus)'
: isFocused
? accentColor != '4368E3'
? accentColor
: 'var(--primary-accent-strong)'
: borderColor != '#CCD1D5'
? borderColor
: disable || loading
? '1px solid var(--borders-disabled-on-white)'
: 'var(--borders-default)',
? accentColor != '4368E3'
? accentColor
: 'var(--primary-accent-strong)'
: borderColor != '#CCD1D5'
? borderColor
: disable || loading
? '1px solid var(--borders-disabled-on-white)'
: 'var(--borders-default)',
'--tblr-input-border-color-darker': getModifiedColor(borderColor, 8),
backgroundColor:
backgroundColor != '#fff'
? backgroundColor
: disable || loading
? darkMode
? 'var(--surfaces-app-bg-default)'
: 'var(--surfaces-surface-03)'
: 'var(--surfaces-surface-01)',
? darkMode
? 'var(--surfaces-app-bg-default)'
: 'var(--surfaces-surface-03)'
: 'var(--surfaces-surface-01)',
boxShadow,
...(isDynamicHeightEnabled && { minHeight: `${height}px` }),
...(defaultAlignment === 'top' &&
label?.length != 0 && {
height: `calc(100% - 20px - ${padding === 'default' ? BOX_PADDING * 2 : 0}px)`, // 20px is label height
flex: 1,
}),
height: `calc(100% - 20px - ${padding === 'default' ? BOX_PADDING * 2 : 0}px)`, // 20px is label height
flex: 1,
}),
...getWidthTypeOfComponentStyles(widthType, width, auto, alignment),
}}
>

View file

@ -18,10 +18,10 @@ export const BoundedBox = ({ properties, fireEvent, darkMode, setExposedVariable
const [typeState, setType] = useState(properties.selector);
const labels = _.isArray(properties.labels)
? [
...properties.labels.map((label) => {
return { name: getSafeRenderableValue(label), value: label };
}),
]
...properties.labels.map((label) => {
return { name: getSafeRenderableValue(label), value: label };
}),
]
: [];
const annotateRef = useRef(null);

View file

@ -73,8 +73,8 @@ export const Button = function Button(props) {
? 'var(--cc-primary-brand)'
: 'transparent'
: type === 'primary'
? backgroundColor
: 'transparent';
? backgroundColor
: 'transparent';
const computedStyles = {
backgroundColor: computedBgColor,

View file

@ -53,4 +53,4 @@ const GetAvatar = ({ chatType, userAvatar, respondentAvatar, chatAvatar }) => {
}
};
export default GetAvatar;
export default GetAvatar;

View file

@ -42,4 +42,4 @@ export const ChatHeader = ({ title, onDownload, onClear, enableDownloadHistoryBu
</ToolTip>
</div>
</div>
);
);

View file

@ -88,4 +88,4 @@ export const ChatInput = ({
</div>
</div>
);
};
};

View file

@ -57,4 +57,4 @@ export const ChatMessage = React.memo(
);
// Add display name for debugging
ChatMessage.displayName = 'ChatMessage';
ChatMessage.displayName = 'ChatMessage';

Some files were not shown because too many files have changed in this diff Show more