[+] add taro project

This commit is contained in:
王昆 2022-06-06 19:17:47 +08:00
parent 0a456e4877
commit ea05f91d8a
349 changed files with 65818 additions and 154 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -41,7 +41,6 @@ applyPolyfills().then(() => {
defineCustomElements(window);
});
// create taro runtime in React
import { createRouter } from "@tarojs/taro";
import { createReactApp } from "@tarojs/runtime";
class Empty extends React.Component {
render() {
@ -49,18 +48,7 @@ class Empty extends React.Component {
}
}
const inst = createReactApp(Empty, React, ReactDOM, {});
// createRouter(
// inst,
// {
// routes: [],
// router: {
// mode: "browser",
// basename: "",
// pathname: "",
// },
// },
// "react",
// );
// add touch emulator
import "@vant/touch-emulator";
import "react-sortable-tree-patch-react-17/style.css";

View File

@ -1,29 +0,0 @@
#!/bin/sh
APPSMITH_MONGODB_URI="mongodb://10.10.13.50:27017/appsmith"
APPSMITH_REDIS_URL="redis://10.10.13.50:63799"
CLOUDOS_WECHAT_APPID="wx414ad0dbeda1a70b"
CLOUDOS_WECHAT_SECRET="d5289fd08b1fb31290f66ea2ce5ec7dc"
APPSMITH_MAIL_ENABLED=false
APPSMITH_ENCRYPTION_PASSWORD=abcd
APPSMITH_ENCRYPTION_SALT=abcd
APPSMITH_CODEC_SIZE=10
#APPSMITH_CLOUD_SERVICES_BASE_URL="https://release-cs.appsmith.com"
#APPSMITH_OAUTH2_GOOGLE_CLIENT_ID=""
#APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET=""
#APPSMITH_OAUTH2_GITHUB_CLIENT_ID=""
#APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=""
#APPSMITH_SENTRY_DSN=
#APPSMITH_SENTRY_ENVIRONMENT=
#APPSMITH_RECAPTCHA_SITE_KEY=""
#APPSMITH_RECAPTCHA_SECRET_KEY=""

View File

@ -12,5 +12,5 @@ node_modules
**/.project
**/.factorypath
container-volumes
!.env
*.env
dependency-reduced-pom.xml

12
app/taro/.editorconfig Normal file
View File

@ -0,0 +1,12 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

7
app/taro/.eslintrc.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
"extends": ["taro/react"],
"rules": {
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
}
}

7
app/taro/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
dist/
deploy_versions/
.temp/
.rn_temp/
node_modules/
.DS_Store
.linaria-cache/

1
app/taro/README.md Normal file
View File

@ -0,0 +1 @@
# taro viewer for PagePlug 💖

32
app/taro/babel.config.js Normal file
View File

@ -0,0 +1,32 @@
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
presets: [
['taro', {
framework: 'react',
ts: true
}],
'linaria/babel'
],
plugins: [
[
"import",
{
libraryName: "@taroify/core",
libraryDirectory: "",
style: true,
},
"@taroify/core",
],
[
"import",
{
libraryName: "@taroify/icons",
libraryDirectory: "",
camel2DashComponentName: false,
style: () => "@taroify/icons/style",
},
"@taroify/icons",
],
],
}

141
app/taro/config/dev.js Normal file
View File

@ -0,0 +1,141 @@
// eslint-disable-next-line import/no-commonjs
const TerserPlugin = require("terser-webpack-plugin");
const pxtransform = require("./postcss.pxtransform");
const transformEditorSize = (rule) => {
rule.oneOf('0').use('2').tap(options => ({
...options,
postcssOptions: {
plugins: [
// transform PagePlug component px(based on 450) to weapp unit rpx(based on 750)
pxtransform(),
...options.postcssOptions.plugins,
]
}
}));
}
module.exports = {
env: {
NODE_ENV: '"development"',
REACT_APP_CLIENT_LOG_LEVEL: '"error"', // debug or error
},
defineConstants: {
// PagePlug 接口地址
API_BASE_URL: '""',
// 默认应用ID
DEFAULT_APP: '""',
},
mini: {
webpackChain(chain, webpack) {
// add postcss plugin
transformEditorSize(chain.module.rule("less"));
transformEditorSize(chain.module.rule("nomorlCss"));
// lodash bundle reduction
// `shorthands`, `coercions`, `paths` are necessary to avoid some weird things
chain.plugin("lodash-webpack-plugin").use(require("lodash-webpack-plugin"), [
{
shorthands: true,
cloning: true,
caching: true,
collections: true,
exotics: true,
guards: true,
memoizing: true,
coercions: true,
flattening: true,
paths: true,
},
])
chain.merge({
resolve: {
modules: [
'./src',
],
},
optimization: {
splitChunks: {
// `all` or `initial`, `all` will have the smallest overall size, refer to
// https://stackoverflow.com/questions/50127185/webpack-what-is-the-difference-between-all-and-initial-options-in-optimizat
chunks: "all",
cacheGroups: {
lodash: {
name: "lodash",
priority: 100,
test(module) {
return /node_modules[\\/]lodash/.test(module.context)
},
},
taroify: {
name: "taroify",
test(module) {
if (/package[\\/](core|icons|hooks)/.test(module.resource)) {
return true
}
if (/bundles[\\/](core|icons|hooks)/.test(module.resource)) {
return true
}
return /node_modules[\\/]@taroify/.test(module.resource)
},
//
// test: /node_modules[\\/]@taroify/,
// just higher than 10 will be fine, refer to
// https://github.com/NervJS/taro/blob/bc6af68bda2cbc9163fbda36c15878fc96aec8f1/packages/taro-mini-runner/src/webpack/build.conf.ts#L220-L254
priority: 100,
},
},
},
// turn on below `minimize`, `minimizer` settings if bundle size is way too large
// to do remote debug in wechatdevtools
minimize: true,
minimizer: [
new TerserPlugin({
// add those `bundle`s your want to do size reduction
// refer to https://webpack.js.org/plugins/terser-webpack-plugin/#test
test: ["common.js", "taro.js", "vendors.js", "lodash.js", "taroify.js", "app.js", "pages/index/index.js"],
parallel: true,
// minify: TerserPlugin.swcMinify,
cache: true,
// remove comments
terserOptions: {
output: {
comments: false,
},
},
extractComments: false,
// should work with `mini.sourceMapType='source-map'`
// refer to https://webpack.js.org/plugins/terser-webpack-plugin/#note-about-source-maps
sourceMap: true,
}),
],
},
})
// enable webpack-bundle-analyzer
// if you would like to do some bundle reduction stuff
// chain.plugin("analyzer")
// .use(require("webpack-bundle-analyzer").BundleAnalyzerPlugin, [{
// analyzerPort: "auto",
// generateStatsFile: true,
// }])
chain.module
.rule('script')
.use('linariaLoader')
.loader('linaria/loader')
.options({
sourceMap: true,
});
},
commonChunks(commonChunks) {
commonChunks.push("lodash")
commonChunks.push("taroify")
return commonChunks
},
// turn to source-map if TerserPlugin is on
// refer to http://taro-docs.jd.com/taro/docs/config-detail/#minisourcemaptype
sourceMapType: "source-map",
},
h5: {}
}

128
app/taro/config/index.js Normal file
View File

@ -0,0 +1,128 @@
const pxtransform = require("./postcss.pxtransform");
const transformEditorSize = (rule) => {
rule.oneOf('0').use('2').tap(options => ({
...options,
postcssOptions: {
plugins: [
// transform PagePlug component px(based on 450) to weapp unit rpx(based on 750)
pxtransform(),
...options.postcssOptions.plugins,
]
}
}));
}
const config = {
projectName: 'b_d_m',
date: '2021-11-8',
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2
},
sourceRoot: 'src',
outputRoot: 'dist',
plugins: [
'@tarojs/plugin-html',
'@tarojs/plugin-sass',
],
defineConstants: {
// PagePlug 接口地址
API_BASE_URL: '""',
// 默认应用ID
DEFAULT_APP: '""',
// 默认空内容图片地址
EMPTY_IMAGE_URL: '"https://img.icons8.com/stickers/344/aquarium.png"',
},
copy: {
patterns: [
{ from: 'src/worker/', to: 'dist/worker/' },
],
options: {
}
},
framework: 'react',
mini: {
postcss: {
pxtransform: {
enable: true,
config: {},
},
url: {
enable: true,
config: {
limit: 1024 // 设定转换尺寸上限
}
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
},
webpackChain(chain, webpack) {
// add postcss plugin
transformEditorSize(chain.module.rule("less"));
transformEditorSize(chain.module.rule("nomorlCss"));
chain.merge({
resolve: {
modules: [
'./src',
],
}
});
chain.module
.rule('script')
.use('linariaLoader')
.loader('linaria/loader')
.options({
sourceMap: process.env.NODE_ENV !== 'production',
});
}
},
h5: {
publicPath: '/',
staticDirectory: 'static',
postcss: {
autoprefixer: {
enable: true,
config: {
}
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
},
webpackChain(chain, webpack) {
chain.merge({
resolve: {
modules: [
'./src',
],
}
});
chain.module
.rule('script')
.use('linariaLoader')
.loader('linaria/loader')
.options({
sourceMap: process.env.NODE_ENV !== 'production',
});
}
}
}
module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
}

View File

@ -0,0 +1,339 @@
'use strict'
const postcss = require('postcss')
const objectAssign = require('object-assign')
const pxRegex = /"[^"]+"|'[^']+'|url\([^\)]+\)|(\d*\.?\d+)px/g
/*eslint-disable*/
const filterPropList = {
exact: function (list) {
return list.filter(function (m) {
return m.match(/^[^\*\!]+$/)
})
},
contain: function (list) {
return list.filter(function (m) {
return m.match(/^\*.+\*$/)
}).map(function (m) {
return m.substr(1, m.length - 2)
})
},
endWith: function (list) {
return list.filter(function (m) {
return m.match(/^\*[^\*]+$/)
}).map(function (m) {
return m.substr(1)
})
},
startWith: function (list) {
return list.filter(function (m) {
return m.match(/^[^\*\!]+\*$/)
}).map(function (m) {
return m.substr(0, m.length - 1)
})
},
notExact: function (list) {
return list.filter(function (m) {
return m.match(/^\![^\*].*$/)
}).map(function (m) {
return m.substr(1)
})
},
notContain: function (list) {
return list.filter(function (m) {
return m.match(/^\!\*.+\*$/)
}).map(function (m) {
return m.substr(2, m.length - 3)
})
},
notEndWith: function (list) {
return list.filter(function (m) {
return m.match(/^\!\*[^\*]+$/)
}).map(function (m) {
return m.substr(2)
})
},
notStartWith: function (list) {
return list.filter(function (m) {
return m.match(/^\![^\*]+\*$/)
}).map(function (m) {
return m.substr(1, m.length - 2)
})
}
}
const defaults = {
rootValue: 16,
unitPrecision: 5,
selectorBlackList: [],
propList: ['*'],
replace: true,
mediaQuery: false,
minPixelValue: 0
}
const legacyOptions = {
root_value: 'rootValue',
unit_precision: 'unitPrecision',
selector_black_list: 'selectorBlackList',
prop_white_list: 'propList',
media_query: 'mediaQuery',
propWhiteList: 'propList'
}
const deviceRatio = {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2
}
// transform factor for fixed-width container
// (w / 320) * 20 * (pixels / rootValue) === (pixels / newRootValue) * 16 (default html font-size)
// so, newRootValue = (16 * 16 / w) * rootValue
const factor = 16 * 16
const baseFontSize = 40
const DEFAULT_WEAPP_OPTIONS = {
platform: 'weapp',
designWidth: 750,
deviceRatio,
h5Width: 450,
}
let targetUnit
module.exports = postcss.plugin('postcss-editorto', function (options) {
options = Object.assign(DEFAULT_WEAPP_OPTIONS, options || {})
switch (options.platform) {
case 'weapp': {
// pc editor px (450 based) => weapp rpx (750 based)
options.rootValue = (options.h5Width / options.designWidth) * (1 / options.deviceRatio[options.designWidth])
targetUnit = 'rpx'
break
}
case 'h5': {
options.rootValue = (baseFontSize * options.designWidth / 640) * (factor / options.h5Width)
targetUnit = 'rem'
break
}
case 'rn': {
options.rootValue = options.deviceRatio[options.designWidth] * 2
targetUnit = 'px'
break
}
}
convertLegacyOptions(options)
const opts = objectAssign({}, defaults, options)
const onePxTransform = typeof options.onePxTransform === 'undefined' ? true : options.onePxTransform
const pxReplace = createPxReplace(opts.rootValue, opts.unitPrecision,
opts.minPixelValue, onePxTransform)
const satisfyPropList = createPropListMatcher(opts.propList)
return function (css) {
const filePath = css.source.input.file;
for (let i = 0; i < css.nodes.length; i++) {
if (css.nodes[i].type === 'comment') {
if (css.nodes[i].text === 'postcss-pxtransform disable') {
return
} else {
break
}
}
}
// delete code between comment in RN
if (options.platform === 'rn') {
css.walkComments(comment => {
if (comment.text === 'postcss-pxtransform rn eject enable') {
let next = comment.next()
while (next) {
if (next.type === 'comment' && next.text === 'postcss-pxtransform rn eject disable') {
break
}
const temp = next.next()
next.remove()
next = temp
}
}
})
}
/* #ifdef %PLATFORM% */
// 平台特有样式
/* #endif */
css.walkComments(comment => {
const wordList = comment.text.split(' ')
// 指定平台保留
if (wordList.indexOf('#ifdef') > -1) {
// 非指定平台
if (wordList.indexOf(options.platform) === -1) {
let next = comment.next()
while (next) {
if (next.type === 'comment' && next.text.trim() === '#endif') {
break
}
const temp = next.next()
next.remove()
next = temp
}
}
}
})
/* #ifndef %PLATFORM% */
// 平台特有样式
/* #endif */
css.walkComments(comment => {
const wordList = comment.text.split(' ')
// 指定平台剔除
if (wordList.indexOf('#ifndef') > -1) {
// 指定平台
if (wordList.indexOf(options.platform) > -1) {
let next = comment.next()
while (next) {
if (next.type === 'comment' && next.text.trim() === '#endif') {
break
}
const temp = next.next()
next.remove()
next = temp
}
}
}
})
css.walkDecls(function (decl, i) {
// This should be the fastest test and will remove most declarations
if (decl.value.indexOf('px') === -1) return
if (!satisfyPropList(decl.prop)) return
if (blacklistedSelector(opts.selectorBlackList,
decl.parent.selector)) return
const value = decl.value.replace(pxRegex, pxReplace)
// if rem unit already exists, do not add or replace
if (declarationExists(decl.parent, decl.prop, value)) return
if (opts.replace) {
decl.value = value
} else {
decl.parent.insertAfter(i, decl.clone({ value: value }))
}
})
if (opts.mediaQuery) {
css.walkAtRules('media', function (rule) {
if (rule.params.indexOf('px') === -1) return
rule.params = rule.params.replace(pxRegex, pxReplace)
})
}
}
})
function convertLegacyOptions (options) {
if (typeof options !== 'object') return
if (
(
(typeof options.prop_white_list !== 'undefined' &&
options.prop_white_list.length === 0) ||
(typeof options.propWhiteList !== 'undefined' &&
options.propWhiteList.length === 0)
) &&
typeof options.propList === 'undefined'
) {
options.propList = ['*']
delete options.prop_white_list
delete options.propWhiteList
}
Object.keys(legacyOptions).forEach(function (key) {
if (options.hasOwnProperty(key)) {
options[legacyOptions[key]] = options[key]
delete options[key]
}
})
}
function createPxReplace (rootValue, unitPrecision, minPixelValue, onePxTransform) {
return function (m, $1) {
if (!$1) return m
if (!onePxTransform && parseInt($1, 10) === 1) {
return m
}
const pixels = parseFloat($1)
if (pixels < minPixelValue) return m
const fixedVal = toFixed((pixels / rootValue), unitPrecision)
return (fixedVal === 0) ? '0' : fixedVal + targetUnit
}
}
function toFixed (number, precision) {
const multiplier = Math.pow(10, precision + 1)
const wholeNumber = Math.floor(number * multiplier)
return Math.round(wholeNumber / 10) * 10 / multiplier
}
function declarationExists (decls, prop, value) {
return decls.some(function (decl) {
return (decl.prop === prop && decl.value === value)
})
}
function blacklistedSelector (blacklist, selector) {
if (typeof selector !== 'string') return
return blacklist.some(function (regex) {
if (typeof regex === 'string') return selector.indexOf(regex) !== -1
return selector.match(regex)
})
}
function createPropListMatcher (propList) {
const hasWild = propList.indexOf('*') > -1
const matchAll = (hasWild && propList.length === 1)
const lists = {
exact: filterPropList.exact(propList),
contain: filterPropList.contain(propList),
startWith: filterPropList.startWith(propList),
endWith: filterPropList.endWith(propList),
notExact: filterPropList.notExact(propList),
notContain: filterPropList.notContain(propList),
notStartWith: filterPropList.notStartWith(propList),
notEndWith: filterPropList.notEndWith(propList)
}
return function (prop) {
if (matchAll) return true
return (
(
hasWild ||
lists.exact.indexOf(prop) > -1 ||
lists.contain.some(function (m) {
return prop.indexOf(m) > -1
}) ||
lists.startWith.some(function (m) {
return prop.indexOf(m) === 0
}) ||
lists.endWith.some(function (m) {
return prop.indexOf(m) === prop.length - m.length
})
) &&
!(
lists.notExact.indexOf(prop) > -1 ||
lists.notContain.some(function (m) {
return prop.indexOf(m) > -1
}) ||
lists.notStartWith.some(function (m) {
return prop.indexOf(m) === 0
}) ||
lists.notEndWith.some(function (m) {
return prop.indexOf(m) === prop.length - m.length
})
)
)
}
}

22
app/taro/config/prod.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
env: {
NODE_ENV: '"production"',
},
defineConstants: {
// PagePlug 接口地址
API_BASE_URL: '""',
// 默认应用ID
DEFAULT_APP: '""',
},
mini: {},
h5: {
/**
* 如果h5端编译后体积过大可以使用webpack-bundle-analyzer插件对打包体积进行分析
* 参考代码如下
* webpackChain (chain) {
* chain.plugin('analyzer')
* .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
* }
*/
}
}

18
app/taro/global.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
declare module "*.png";
declare module "*.gif";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.svg";
declare module "*.css";
declare module "*.less";
declare module "*.scss";
declare module "*.sass";
declare module "*.styl";
// @ts-ignore
declare const process: {
env: {
TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd';
[key: string]: any;
}
}

View File

@ -0,0 +1,12 @@
// linaria 配置详见 https://github.com/callstack/linaria/blob/master/docs/CONFIGURATION.md#options
module.exports = {
rules: [
{
action: require("linaria/evaluators").shaker,
},
{
test: /node_modules[\/\\](?!@tarojs|@taroify)/,
action: "ignore"
}
]
}

110
app/taro/package.json Normal file
View File

@ -0,0 +1,110 @@
{
"name": "b_d_m",
"version": "1.0.0",
"private": true,
"description": "PagePlug 跨端展示",
"templateInfo": {
"name": "redux",
"typescript": true,
"css": "less"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.7.7",
"@manaflair/redux-batch": "^1.0.0",
"@taroify/core": "0.0.28-alpha.1",
"@tarojs/components": "3.3.12",
"@tarojs/plugin-html": "3.3.12",
"@tarojs/react": "3.3.12",
"@tarojs/runtime": "3.3.12",
"@tarojs/taro": "3.3.12",
"@types/deep-diff": "^1.0.1",
"@types/downloadjs": "^1.4.2",
"@types/jshint": "^2.12.1",
"@types/nanoid": "^3.0.0",
"@types/toposort": "^2.0.3",
"astring": "^1.7.5",
"clsx": "^1.1.1",
"copy-to-clipboard": "^3.3.1",
"dayjs": "^1.10.7",
"deep-diff": "^1.0.2",
"downloadjs": "^1.4.7",
"eval5": "^1.4.7",
"fast-deep-equal": "^3.1.3",
"fast-xml-parser": "^3.21.1",
"fuse.js": "^6.4.6",
"immer": "^9.0.6",
"jshint": "^2.13.1",
"linaria": "^3.0.0-beta.17",
"lodash": "^4.17.21",
"loglevel": "^1.7.1",
"nanoid": "^3.1.30",
"node-forge": "^0.10.0",
"normalizr": "^3.6.1",
"path-to-regexp": "^6.2.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-redux": "^7.2.0",
"redux": "^4.0.0",
"redux-form": "^8.3.7",
"redux-logger": "^3.0.6",
"redux-saga": "^1.1.3",
"reselect": "^4.1.2",
"shallowequal": "^1.1.0",
"taro-axios": "^1.1.1",
"tinycolor2": "^1.4.2",
"toposort": "^2.0.2",
"unescape-js": "^1.1.4",
"url-search-params-polyfill": "^8.1.1",
"worker-loader": "^3.0.8"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@tarojs/mini-runner": "3.3.12",
"@tarojs/plugin-sass": "^2.2.10",
"@tarojs/webpack-runner": "3.3.12",
"@types/react": "^17.0.2",
"@types/webpack-env": "^1.13.6",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"babel-plugin-import": "^1.13.3",
"babel-preset-taro": "3.3.12",
"eslint": "^6.8.0",
"eslint-config-taro": "3.3.12",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-react-hooks": "^4.2.0",
"lodash-webpack-plugin": "^0.11.6",
"redux-devtools-extension": "^2.13.9",
"stylelint": "9.3.0",
"terser-webpack-plugin": "4",
"typescript": "^4.1.0",
"webpack-bundle-analyzer": "^4.5.0"
}
}

View File

@ -0,0 +1,47 @@
{
"miniprogramRoot": "dist/",
"projectname": "b_d_m",
"description": "PagePlug 跨端展示",
"appid": "",
"setting": {
"urlCheck": false,
"es6": false,
"enhance": false,
"postcss": false,
"preloadBackgroundData": false,
"minified": true,
"newFeature": false,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"lazyloadPlaceholderEnable": false,
"useMultiFrameRuntime": true,
"useApiHook": true,
"useApiHostProcess": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"useIsolateContext": true,
"userConfirmedBundleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"disableUseStrict": false,
"minifyWXML": true,
"showES6CompileOption": false,
"useCompilerPlugins": false,
"ignoreUploadUnusedFiles": true
},
"compileType": "miniprogram",
"libVersion": "2.21.0",
"condition": {}
}

View File

@ -0,0 +1,17 @@
{
"setting": {},
"condition": {
"plugin": {
"list": []
},
"game": {
"list": []
},
"gamePlugin": {
"list": []
},
"miniprogram": {
"list": []
}
}
}

13
app/taro/project.tt.json Normal file
View File

@ -0,0 +1,13 @@
{
"miniprogramRoot": "./",
"projectname": "b_d_m",
"description": "PagePlug 跨端展示",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
}

View File

@ -0,0 +1,300 @@
import { PaginationField, ActionResponse } from "api/ActionAPI";
import {
ReduxActionTypes,
ReduxAction,
ReduxActionErrorTypes,
EvaluationReduxAction,
ReduxActionWithoutPayload,
} from "constants/ReduxActionConstants";
import { Action } from "entities/Action";
import { batchAction } from "actions/batchActions";
export const createActionRequest = (payload: Partial<Action>) => {
return {
type: ReduxActionTypes.CREATE_ACTION_INIT,
payload,
};
};
export const createActionSuccess = (payload: Action) => {
return {
type: ReduxActionTypes.CREATE_ACTION_SUCCESS,
payload,
};
};
export type FetchActionsPayload = {
applicationId: string;
};
export const fetchActions = (
applicationId: string,
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<unknown> => {
return {
type: ReduxActionTypes.FETCH_ACTIONS_INIT,
payload: { applicationId },
postEvalActions,
};
};
export const fetchActionsForView = (
applicationId: string,
): ReduxAction<FetchActionsPayload> => {
return {
type: ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_INIT,
payload: { applicationId },
};
};
export const fetchActionsForPage = (
pageId: string,
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload> = [],
): EvaluationReduxAction<unknown> => {
return {
type: ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_INIT,
payload: { pageId },
postEvalActions,
};
};
export const fetchActionsForPageSuccess = (
actions: Action[],
postEvalActions?: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<unknown> => {
return {
type: ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_SUCCESS,
payload: actions,
postEvalActions,
};
};
export const setActionTabsInitialIndex = (index: number) => {
return {
type: ReduxActionTypes.SET_ACTION_TABS_INITIAL_INDEX,
payload: index,
};
};
export const runActionViaShortcut = () => {
return {
type: ReduxActionTypes.RUN_ACTION_SHORTCUT_REQUEST,
};
};
export const runAction = (id: string, paginationField?: PaginationField) => {
return {
type: ReduxActionTypes.RUN_ACTION_REQUEST,
payload: {
id,
paginationField,
},
};
};
export const runActionInit = (
id: string,
paginationField?: PaginationField,
) => {
return {
type: ReduxActionTypes.RUN_ACTION_INIT,
payload: {
id,
paginationField,
},
};
};
export const showRunActionConfirmModal = (show: boolean) => {
return {
type: ReduxActionTypes.SHOW_RUN_ACTION_CONFIRM_MODAL,
payload: show,
};
};
export const cancelRunActionConfirmModal = () => {
return {
type: ReduxActionTypes.CANCEL_RUN_ACTION_CONFIRM_MODAL,
};
};
export const acceptRunActionConfirmModal = () => {
return {
type: ReduxActionTypes.ACCEPT_RUN_ACTION_CONFIRM_MODAL,
};
};
export const updateAction = (payload: { id: string }) => {
return batchAction({
type: ReduxActionTypes.UPDATE_ACTION_INIT,
payload,
});
};
export const updateActionSuccess = (payload: { data: Action }) => {
return {
type: ReduxActionTypes.UPDATE_ACTION_SUCCESS,
payload,
};
};
export const deleteAction = (payload: {
id: string;
name: string;
onSuccess?: () => void;
}) => {
return {
type: ReduxActionTypes.DELETE_ACTION_INIT,
payload,
};
};
export const deleteActionSuccess = (payload: { id: string }) => {
return {
type: ReduxActionTypes.DELETE_ACTION_SUCCESS,
payload,
};
};
export const moveActionRequest = (payload: {
id: string;
destinationPageId: string;
originalPageId: string;
name: string;
}) => {
return {
type: ReduxActionTypes.MOVE_ACTION_INIT,
payload,
};
};
export const moveActionSuccess = (payload: Action) => {
return {
type: ReduxActionTypes.MOVE_ACTION_SUCCESS,
payload,
};
};
export const moveActionError = (payload: {
id: string;
originalPageId: string;
}) => {
return {
type: ReduxActionErrorTypes.MOVE_ACTION_ERROR,
payload,
};
};
export const copyActionRequest = (payload: {
id: string;
destinationPageId: string;
name: string;
}) => {
return {
type: ReduxActionTypes.COPY_ACTION_INIT,
payload,
};
};
export const copyActionSuccess = (payload: Action) => {
return {
type: ReduxActionTypes.COPY_ACTION_SUCCESS,
payload,
};
};
export const copyActionError = (payload: {
id: string;
destinationPageId: string;
}) => {
return {
type: ReduxActionErrorTypes.COPY_ACTION_ERROR,
payload,
};
};
export const executeApiActionRequest = (payload: { id: string }) => ({
type: ReduxActionTypes.EXECUTE_API_ACTION_REQUEST,
payload: payload,
});
export const executeApiActionSuccess = (payload: {
id: string;
response: ActionResponse;
isPageLoad?: boolean;
}) => ({
type: ReduxActionTypes.EXECUTE_API_ACTION_SUCCESS,
payload: payload,
});
export const saveActionName = (payload: { id: string; name: string }) => ({
type: ReduxActionTypes.SAVE_ACTION_NAME_INIT,
payload: payload,
});
export type SetActionPropertyPayload = {
actionId: string;
propertyName: string;
value: any;
};
export const setActionProperty = (payload: SetActionPropertyPayload) => ({
type: ReduxActionTypes.SET_ACTION_PROPERTY,
payload,
});
export type UpdateActionPropertyActionPayload = {
id: string;
field: string;
value: any;
};
export const updateActionProperty = (
payload: UpdateActionPropertyActionPayload,
) => {
return batchAction({
type: ReduxActionTypes.UPDATE_ACTION_PROPERTY,
payload,
});
};
export const executePageLoadActionsComplete = () => {
return {
type: ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS_COMPLETE,
};
};
export const setActionsToExecuteOnPageLoad = (
actions: Array<{
executeOnLoad: boolean;
id: string;
name: string;
}>,
) => {
return {
type: ReduxActionTypes.SET_ACTION_TO_EXECUTE_ON_PAGELOAD,
payload: actions,
};
};
export const bindDataOnCanvas = (payload: {
queryId: string;
applicationId: string;
pageId: string;
}) => {
return {
type: ReduxActionTypes.BIND_DATA_ON_CANVAS,
payload,
};
};
export default {
createAction: createActionRequest,
fetchActions,
runAction: runAction,
deleteAction,
deleteActionSuccess,
updateAction,
updateActionSuccess,
bindDataOnCanvas,
};

View File

@ -0,0 +1,81 @@
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
import { EventLocation } from "utils/AnalyticsUtil";
import { ApiContentTypes } from "constants/ApiEditorConstants";
export const changeApi = (
id: string,
isSaas: boolean,
newApi?: boolean,
): ReduxAction<{ id: string; isSaas: boolean; newApi?: boolean }> => {
return {
type: ReduxActionTypes.API_PANE_CHANGE_API,
payload: { id, isSaas, newApi },
};
};
export const initApiPane = (urlId?: string): ReduxAction<{ id?: string }> => {
return {
type: ReduxActionTypes.INIT_API_PANE,
payload: { id: urlId },
};
};
export const setCurrentCategory = (
category: string,
): ReduxAction<{ category: string }> => {
return {
type: ReduxActionTypes.SET_CURRENT_CATEGORY,
payload: { category },
};
};
export const setLastUsedEditorPage = (
path: string,
): ReduxAction<{ path: string }> => {
return {
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
payload: { path },
};
};
export const setLastSelectedPage = (
selectedPageId: string,
): ReduxAction<{ selectedPageId: string }> => {
return {
type: ReduxActionTypes.SET_LAST_SELECTED_PAGE_PAGE,
payload: { selectedPageId },
};
};
export const createNewApiAction = (
pageId: string,
from: EventLocation,
): ReduxAction<{ pageId: string; from: EventLocation }> => ({
type: ReduxActionTypes.CREATE_NEW_API_ACTION,
payload: { pageId, from },
});
export const createNewQueryAction = (
pageId: string,
from: EventLocation,
): ReduxAction<{ pageId: string; from: EventLocation }> => ({
type: ReduxActionTypes.CREATE_NEW_QUERY_ACTION,
payload: { pageId, from },
});
export const updateBodyContentType = (
title: ApiContentTypes,
apiId: string,
): ReduxAction<{ title: ApiContentTypes; apiId: string }> => ({
type: ReduxActionTypes.UPDATE_API_ACTION_BODY_CONTENT_TYPE,
payload: { title, apiId },
});
export const redirectToNewIntegrations = (
applicationId: string,
pageId: string,
params?: any,
): ReduxAction<{ applicationId: string; pageId: string; params: any }> => ({
type: ReduxActionTypes.REDIRECT_TO_NEW_INTEGRATIONS,
payload: { applicationId, pageId, params },
});

View File

@ -0,0 +1,105 @@
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
import { APP_MODE } from "entities/App";
import {
UpdateApplicationPayload,
ImportApplicationRequest,
} from "api/ApplicationApi";
export const setDefaultApplicationPageSuccess = (
pageId: string,
applicationId: string,
) => {
return {
type: ReduxActionTypes.SET_DEFAULT_APPLICATION_PAGE_SUCCESS,
payload: {
pageId,
applicationId,
},
};
};
export interface FetchApplicationPayload {
applicationId: string;
mode: APP_MODE;
}
export const fetchApplication = (
applicationId: string,
mode: APP_MODE,
): ReduxAction<FetchApplicationPayload> => {
return {
type: ReduxActionTypes.FETCH_APPLICATION_INIT,
payload: {
applicationId,
mode,
},
};
};
export const updateApplicationLayout = (
id: string,
data: UpdateApplicationPayload,
) => {
return {
type: ReduxActionTypes.UPDATE_APP_LAYOUT,
payload: {
id,
...data,
},
};
};
export const updateApplication = (
id: string,
data: UpdateApplicationPayload,
) => {
return {
type: ReduxActionTypes.UPDATE_APPLICATION,
payload: {
id,
...data,
},
};
};
export const publishApplication = (applicationId: string) => {
return {
type: ReduxActionTypes.PUBLISH_APPLICATION_INIT,
payload: {
applicationId,
},
};
};
export const duplicateApplication = (applicationId: string) => {
return {
type: ReduxActionTypes.DUPLICATE_APPLICATION_INIT,
payload: {
applicationId,
},
};
};
export const importApplication = (appDetails: ImportApplicationRequest) => {
return {
type: ReduxActionTypes.IMPORT_APPLICATION_INIT,
payload: appDetails,
};
};
export const getAllApplications = () => {
return {
type: ReduxActionTypes.GET_ALL_APPLICATION_INIT,
};
};
export const resetCurrentApplication = () => {
return {
type: ReduxActionTypes.RESET_CURRENT_APPLICATION,
};
};
export const setShowAppInviteUsersDialog = (payload: boolean) => ({
type: ReduxActionTypes.SET_SHOW_APP_INVITE_USERS_MODAL,
payload,
});

View File

@ -0,0 +1,5 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const getCurrentUser = () => ({
type: ReduxActionTypes.FETCH_USER_INIT,
});

View File

@ -0,0 +1,13 @@
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
export const batchAction = (action: ReduxAction<any>) => ({
type: ReduxActionTypes.BATCHED_UPDATE,
payload: action,
});
export type BatchAction<T> = ReduxAction<ReduxAction<T>>;
export const batchActionSuccess = (actions: ReduxAction<any>[]) => ({
type: ReduxActionTypes.BATCH_UPDATES_SUCCESS,
payload: actions,
});

View File

@ -0,0 +1,7 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const fetchImportedCollections = () => {
return {
type: ReduxActionTypes.FETCH_IMPORTED_COLLECTIONS_INIT,
};
};

View File

@ -0,0 +1,95 @@
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
import { RenderMode } from "constants/WidgetConstants";
import { DynamicPath } from "utils/DynamicBindingUtils";
export const updateWidgetPropertyRequest = (
widgetId: string,
propertyPath: string,
propertyValue: any,
renderMode: RenderMode,
): ReduxAction<UpdateWidgetPropertyRequestPayload> => {
return {
type: ReduxActionTypes.UPDATE_WIDGET_PROPERTY_REQUEST,
payload: {
widgetId,
propertyPath,
propertyValue,
renderMode,
},
};
};
export interface BatchPropertyUpdatePayload {
modify?: Record<string, unknown>; //Key value pairs of paths and values to update
remove?: string[]; //Array of paths to delete
triggerPaths?: string[]; // Array of paths in the modify and remove list which are trigger paths
}
export const batchUpdateWidgetProperty = (
widgetId: string,
updates: BatchPropertyUpdatePayload,
): ReduxAction<UpdateWidgetPropertyPayload> => ({
type: ReduxActionTypes.BATCH_UPDATE_WIDGET_PROPERTY,
payload: {
widgetId,
updates,
},
});
export const deleteWidgetProperty = (
widgetId: string,
propertyPaths: string[],
): ReduxAction<DeleteWidgetPropertyPayload> => ({
type: ReduxActionTypes.DELETE_WIDGET_PROPERTY,
payload: {
widgetId,
propertyPaths,
},
});
export const setWidgetDynamicProperty = (
widgetId: string,
propertyPath: string,
isDynamic: boolean,
): ReduxAction<SetWidgetDynamicPropertyPayload> => {
return {
type: ReduxActionTypes.SET_WIDGET_DYNAMIC_PROPERTY,
payload: {
widgetId,
propertyPath,
isDynamic,
},
};
};
export interface UpdateWidgetPropertyRequestPayload {
widgetId: string;
propertyPath: string;
propertyValue: any;
renderMode: RenderMode;
}
export interface UpdateWidgetPropertyPayload {
widgetId: string;
updates: BatchPropertyUpdatePayload;
dynamicUpdates?: {
dynamicBindingPathList: DynamicPath[];
dynamicTriggerPathList: DynamicPath[];
};
}
export interface UpdateCanvasLayout {
width: number;
height: number;
}
export interface SetWidgetDynamicPropertyPayload {
widgetId: string;
propertyPath: string;
isDynamic: boolean;
}
export interface DeleteWidgetPropertyPayload {
widgetId: string;
propertyPaths: string[];
}

View File

@ -0,0 +1,239 @@
import {
ReduxAction,
ReduxActionTypes,
ReduxActionWithCallbacks,
} from "constants/ReduxActionConstants";
import { CreateDatasourceConfig } from "api/DatasourcesApi";
import { Datasource } from "entities/Datasource";
import { PluginType } from "entities/Action";
import { executeDatasourceQueryRequest } from "../api/DatasourcesApi";
import { ResponseMeta } from "../api/ApiResponses";
export const createDatasourceFromForm = (payload: CreateDatasourceConfig) => {
return {
type: ReduxActionTypes.CREATE_DATASOURCE_FROM_FORM_INIT,
payload,
};
};
export const updateDatasource = (
payload: Datasource,
onSuccess?: ReduxAction<unknown>,
onError?: ReduxAction<unknown>,
): ReduxActionWithCallbacks<Datasource, unknown, unknown> => {
return {
type: ReduxActionTypes.UPDATE_DATASOURCE_INIT,
payload,
onSuccess,
onError,
};
};
export type UpdateDatasourceSuccessAction = {
type: string;
payload: Datasource;
redirect: boolean;
queryParams?: Record<string, string>;
};
export const updateDatasourceSuccess = (
payload: Datasource,
redirect = true,
queryParams = {},
): UpdateDatasourceSuccessAction => ({
type: ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS,
payload,
redirect,
queryParams,
});
export const redirectAuthorizationCode = (
pageId: string,
datasourceId: string,
pluginType: PluginType,
) => {
return {
type: ReduxActionTypes.REDIRECT_AUTHORIZATION_CODE,
payload: {
pageId,
datasourceId,
pluginType,
},
};
};
export const fetchDatasourceStructure = (id: string, ignoreCache?: boolean) => {
return {
type: ReduxActionTypes.FETCH_DATASOURCE_STRUCTURE_INIT,
payload: {
id,
ignoreCache,
},
};
};
export const expandDatasourceEntity = (id: string) => {
return {
type: ReduxActionTypes.EXPAND_DATASOURCE_ENTITY,
payload: id,
};
};
export const refreshDatasourceStructure = (id: string) => {
return {
type: ReduxActionTypes.REFRESH_DATASOURCE_STRUCTURE_INIT,
payload: {
id,
},
};
};
export const saveDatasourceName = (payload: { id: string; name: string }) => ({
type: ReduxActionTypes.SAVE_DATASOURCE_NAME,
payload: payload,
});
export const changeDatasource = (payload: Datasource) => {
return {
type: ReduxActionTypes.CHANGE_DATASOURCE,
payload,
};
};
export const switchDatasource = (id: string) => {
return {
type: ReduxActionTypes.SWITCH_DATASOURCE,
payload: { datasourceId: id },
};
};
export const testDatasource = (payload: Partial<Datasource>) => {
return {
type: ReduxActionTypes.TEST_DATASOURCE_INIT,
payload,
};
};
export const deleteDatasource = (
payload: Partial<Datasource>,
onSuccess?: ReduxAction<unknown>,
onError?: ReduxAction<unknown>,
onSuccessCallback?: () => void,
): ReduxActionWithCallbacks<Partial<Datasource>, unknown, unknown> => {
return {
type: ReduxActionTypes.DELETE_DATASOURCE_INIT,
payload,
onSuccess,
onError,
onSuccessCallback,
};
};
export const setDatsourceEditorMode = (payload: {
id: string;
viewMode: boolean;
}) => {
return {
type: ReduxActionTypes.SET_DATASOURCE_EDITOR_MODE,
payload,
};
};
export const fetchDatasources = () => {
return {
type: ReduxActionTypes.FETCH_DATASOURCES_INIT,
};
};
export const fetchMockDatasources = () => {
return {
type: ReduxActionTypes.FETCH_MOCK_DATASOURCES_INIT,
};
};
export interface addMockRequest
extends ReduxAction<{
name: string;
organizationId: string;
pluginId: string;
packageName: string;
isGeneratePageMode?: string;
}> {
extraParams?: any;
}
export const addMockDatasourceToOrg = (
name: string,
organizationId: string,
pluginId: string,
packageName: string,
isGeneratePageMode?: string,
): addMockRequest => {
return {
type: ReduxActionTypes.ADD_MOCK_DATASOURCES_INIT,
payload: { name, packageName, pluginId, organizationId },
extraParams: { isGeneratePageMode },
};
};
export const initDatasourcePane = (
pluginType: string,
urlId?: string,
): ReduxAction<{ pluginType: string; id?: string }> => {
return {
type: ReduxActionTypes.INIT_DATASOURCE_PANE,
payload: { id: urlId, pluginType },
};
};
export const storeAsDatasource = () => {
return {
type: ReduxActionTypes.STORE_AS_DATASOURCE_INIT,
};
};
export const getOAuthAccessToken = (datasourceId: string) => {
return {
type: ReduxActionTypes.SAAS_GET_OAUTH_ACCESS_TOKEN,
payload: { datasourceId },
};
};
export type executeDatasourceQuerySuccessPayload = {
responseMeta: ResponseMeta;
data: {
body: Array<{ id: string; name: string }>;
headers: Record<string, string[]>;
statusCode: string;
isExecutionSuccess: boolean;
};
};
type errorPayload = unknown;
export type executeDatasourceQueryReduxAction = ReduxActionWithCallbacks<
executeDatasourceQueryRequest,
executeDatasourceQuerySuccessPayload,
errorPayload
>;
export const executeDatasourceQuery = ({
onErrorCallback,
onSuccessCallback,
payload,
}: {
onErrorCallback?: (payload: errorPayload) => void;
onSuccessCallback?: (payload: executeDatasourceQuerySuccessPayload) => void;
payload: executeDatasourceQueryRequest;
}): executeDatasourceQueryReduxAction => {
return {
type: ReduxActionTypes.EXECUTE_DATASOURCE_QUERY_INIT,
payload,
onErrorCallback,
onSuccessCallback,
};
};
export default {
fetchDatasources,
initDatasourcePane,
};

View File

@ -0,0 +1,49 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { Message, ENTITY_TYPE } from "entities/AppsmithConsole";
import { EventName } from "utils/AnalyticsUtil";
export interface LogDebuggerErrorAnalyticsPayload {
entityName: string;
entityId: string;
entityType: ENTITY_TYPE;
eventName: EventName;
propertyPath: string;
errorMessages: { message: string }[];
}
export const debuggerLogInit = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_LOG_INIT,
payload,
});
export const debuggerLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_LOG,
payload,
});
export const clearLogs = () => ({
type: ReduxActionTypes.CLEAR_DEBUGGER_LOGS,
});
export const showDebugger = (payload?: boolean) => ({
type: ReduxActionTypes.SHOW_DEBUGGER,
payload,
});
export const errorLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_ERROR_LOG,
payload,
});
export const updateErrorLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOG,
payload,
});
// Only used for analytics
export const logDebuggerErrorAnalytics = (
payload: LogDebuggerErrorAnalyticsPayload,
) => ({
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
payload,
});

View File

@ -0,0 +1,16 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const flushErrors = () => {
return {
type: ReduxActionTypes.FLUSH_ERRORS,
};
};
export const flushErrorsAndRedirect = (url: string) => {
return {
type: ReduxActionTypes.FLUSH_AND_REDIRECT,
payload: {
url,
},
};
};

View File

@ -0,0 +1,82 @@
import {
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
} from "../constants/ReduxActionConstants";
import _ from "lodash";
import { DataTree } from "../entities/DataTree/dataTreeFactory";
import { DependencyMap } from "../utils/DynamicBindingUtils";
import { Diff } from "deep-diff";
export const FIRST_EVAL_REDUX_ACTIONS = [
// Pages
ReduxActionTypes.FETCH_PAGE_SUCCESS,
ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
];
export const EVALUATE_REDUX_ACTIONS = [
...FIRST_EVAL_REDUX_ACTIONS,
// Actions
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS,
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_SUCCESS,
ReduxActionTypes.SUBMIT_CURL_FORM_SUCCESS,
ReduxActionTypes.CREATE_ACTION_SUCCESS,
ReduxActionTypes.UPDATE_ACTION_PROPERTY,
ReduxActionTypes.DELETE_ACTION_SUCCESS,
ReduxActionTypes.COPY_ACTION_SUCCESS,
ReduxActionTypes.MOVE_ACTION_SUCCESS,
ReduxActionTypes.RUN_ACTION_SUCCESS,
ReduxActionErrorTypes.RUN_ACTION_ERROR,
ReduxActionTypes.EXECUTE_API_ACTION_SUCCESS,
ReduxActionErrorTypes.EXECUTE_ACTION_ERROR,
// App Data
ReduxActionTypes.SET_APP_MODE,
ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
ReduxActionTypes.UPDATE_APP_PERSISTENT_STORE,
ReduxActionTypes.UPDATE_APP_TRANSIENT_STORE,
// Widgets
ReduxActionTypes.UPDATE_LAYOUT,
ReduxActionTypes.UPDATE_WIDGET_PROPERTY,
ReduxActionTypes.UPDATE_WIDGET_NAME_SUCCESS,
// Widget Meta
ReduxActionTypes.SET_META_PROP,
ReduxActionTypes.RESET_WIDGET_META,
// Batches
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
];
export const shouldProcessBatchedAction = (action: ReduxAction<unknown>) => {
if (
action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS &&
Array.isArray(action.payload)
) {
const batchedActionTypes = action.payload.map(
(batchedAction) => batchedAction.type,
);
return (
_.intersection(EVALUATE_REDUX_ACTIONS, batchedActionTypes).length > 0
);
}
return true;
};
export const setEvaluatedTree = (
dataTree: DataTree,
updates: Diff<DataTree, DataTree>[],
): ReduxAction<{ dataTree: DataTree; updates: Diff<DataTree, DataTree>[] }> => {
return {
type: ReduxActionTypes.SET_EVALUATED_TREE,
payload: { dataTree, updates },
};
};
export const setDependencyMap = (
inverseDependencyMap: DependencyMap,
): ReduxAction<{ inverseDependencyMap: DependencyMap }> => {
return {
type: ReduxActionTypes.SET_EVALUATION_INVERSE_DEPENDENCY_MAP,
payload: { inverseDependencyMap },
};
};

View File

@ -0,0 +1,10 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const initExplorerEntityNameEdit = (actionId: string) => {
return {
type: ReduxActionTypes.INIT_EXPLORER_ENTITY_NAME_EDIT,
payload: {
id: actionId,
},
};
};

View File

@ -0,0 +1,14 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const setHelpDefaultRefinement = (payload: string) => {
return {
type: ReduxActionTypes.SET_DEFAULT_REFINEMENT,
payload,
};
};
export const setHelpModalVisibility = (payload: boolean) => {
return {
type: ReduxActionTypes.SET_HELP_MODAL_OPEN,
payload,
};
};

View File

@ -0,0 +1,26 @@
import {
ReduxActionTypes,
ReduxAction,
InitializeEditorPayload,
} from "constants/ReduxActionConstants";
export const initEditor = (
applicationId: string,
pageId: string,
queryParams: any,
): ReduxAction<InitializeEditorPayload> => ({
type: ReduxActionTypes.INITIALIZE_EDITOR,
payload: {
applicationId,
pageId,
queryParams,
},
});
export const resetEditorRequest = () => ({
type: ReduxActionTypes.RESET_EDITOR_REQUEST,
});
export const resetEditorSuccess = () => ({
type: ReduxActionTypes.RESET_EDITOR_SUCCESS,
});

View File

@ -0,0 +1,44 @@
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
import { BatchAction, batchAction } from "actions/batchActions";
export interface UpdateWidgetMetaPropertyPayload {
widgetId: string;
propertyName: string;
propertyValue: any;
}
export const updateWidgetMetaProperty = (
widgetId: string,
propertyName: string,
propertyValue: any,
): BatchAction<UpdateWidgetMetaPropertyPayload> => {
return batchAction({
type: ReduxActionTypes.SET_META_PROP,
payload: {
widgetId,
propertyName,
propertyValue,
},
});
};
export const resetWidgetMetaProperty = (
widgetId: string,
): BatchAction<{ widgetId: string }> => {
return batchAction({
type: ReduxActionTypes.RESET_WIDGET_META,
payload: {
widgetId,
},
});
};
export const resetChildrenMetaProperty = (
widgetId: string,
): ReduxAction<{ widgetId: string }> => {
return {
type: ReduxActionTypes.RESET_CHILDREN_WIDGET_META,
payload: {
widgetId,
},
};
};

View File

@ -0,0 +1,58 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { AppsmithNotification } from "entities/Notification";
export const fetchNotificationsRequest = (beforeTime?: string) => ({
type: ReduxActionTypes.FETCH_NOTIFICATIONS_REQUEST,
payload: beforeTime,
});
export const fetchNotificationsSuccess = (payload: {
notifications: Array<AppsmithNotification>;
}) => ({
type: ReduxActionTypes.FETCH_NOTIFICATIONS_SUCCESS,
payload,
});
export const newNotificationEvent = (payload: Notification) => ({
type: ReduxActionTypes.NEW_NOTIFICATION_EVENT,
payload,
});
export const setIsNotificationsListVisible = (payload: boolean) => ({
type: ReduxActionTypes.SET_IS_NOTIFICATIONS_LIST_VISIBLE,
payload,
});
export const markAllNotificationsAsReadRequest = () => ({
type: ReduxActionTypes.MARK_ALL_NOTIFICATIONS_AS_READ_REQUEST,
});
export const markAllNotificationsAsReadSuccess = () => ({
type: ReduxActionTypes.MARK_ALL_NOTIFICATIONS_AS_READ_SUCCESS,
});
export const resetNotifications = (payload: {
notifications: Array<AppsmithNotification>;
}) => ({
type: ReduxActionTypes.RESET_NOTIFICATIONS,
payload,
});
export const fetchUnreadNotificationsCountRequest = () => ({
type: ReduxActionTypes.FETCH_UNREAD_NOTIFICATIONS_COUNT_REQUEST,
});
export const fetchUnreadNotificationsCountSuccess = (payload: number) => ({
type: ReduxActionTypes.FETCH_UNREAD_NOTIFICATIONS_COUNT_SUCCESS,
payload,
});
export const markNotificationAsReadRequest = (payload: string) => ({
type: ReduxActionTypes.MARK_NOTIFICATION_AS_READ_REQUEST,
payload,
});
export const markNotificationAsReadSuccess = (payload: string) => ({
type: ReduxActionTypes.MARK_NOTIFICATION_AS_READ_SUCCESS,
payload,
});

View File

@ -0,0 +1,73 @@
import {
OnboardingHelperConfig,
OnboardingStep,
} from "constants/OnboardingConstants";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const showIndicator = (payload: OnboardingStep) => {
return {
type: ReduxActionTypes.SHOW_ONBOARDING_INDICATOR,
payload,
};
};
export const endOnboarding = () => {
return {
type: ReduxActionTypes.END_ONBOARDING,
};
};
export const setCurrentStep = (payload: number) => {
return {
type: ReduxActionTypes.SET_CURRENT_STEP,
payload,
};
};
export const setOnboardingState = (payload: boolean) => {
return {
type: ReduxActionTypes.SET_ONBOARDING_STATE,
payload,
};
};
export const showOnboardingHelper = (payload: boolean) => {
return {
type: ReduxActionTypes.SHOW_ONBOARDING_HELPER,
payload,
};
};
export const setHelperConfig = (payload: OnboardingHelperConfig) => {
return {
type: ReduxActionTypes.SET_HELPER_CONFIG,
payload,
};
};
export const setCurrentSubstep = (payload: number) => {
return {
type: ReduxActionTypes.SET_ONBOARDING_SUBSTEP,
payload,
};
};
export const showWelcomeHelper = (payload: boolean) => {
return {
type: ReduxActionTypes.SHOW_ONBOARDING_WELCOME_HELPER,
payload,
};
};
export const showOnboardingLoader = (payload: boolean) => {
return {
type: ReduxActionTypes.SHOW_ONBOARDING_LOADER,
payload,
};
};
export const showEndOnboardingHelper = () => {
return {
type: ReduxActionTypes.SHOW_END_ONBOARDING_HELPER,
};
};

View File

@ -0,0 +1,76 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { SaveOrgLogo, SaveOrgRequest } from "api/OrgApi";
export const fetchOrg = (orgId: string, skipValidation?: boolean) => {
return {
type: ReduxActionTypes.FETCH_CURRENT_ORG,
payload: {
orgId,
skipValidation,
},
};
};
export const changeOrgUserRole = (
orgId: string,
role: string,
username: string,
) => {
return {
type: ReduxActionTypes.CHANGE_ORG_USER_ROLE_INIT,
payload: {
orgId,
role,
username,
},
};
};
export const deleteOrgUser = (orgId: string, username: string) => {
return {
type: ReduxActionTypes.DELETE_ORG_USER_INIT,
payload: {
orgId,
username,
},
};
};
export const fetchUsersForOrg = (orgId: string) => {
return {
type: ReduxActionTypes.FETCH_ALL_USERS_INIT,
payload: {
orgId,
},
};
};
export const fetchRolesForOrg = (orgId: string) => {
return {
type: ReduxActionTypes.FETCH_ALL_ROLES_INIT,
payload: {
orgId,
},
};
};
export const saveOrg = (orgSettings: SaveOrgRequest) => {
return {
type: ReduxActionTypes.SAVE_ORG_INIT,
payload: orgSettings,
};
};
export const uploadOrgLogo = (orgLogo: SaveOrgLogo) => {
return {
type: ReduxActionTypes.UPLOAD_ORG_LOGO,
payload: orgLogo,
};
};
export const deleteOrgLogo = (id: string) => {
return {
type: ReduxActionTypes.REMOVE_ORG_LOGO,
payload: {
id: id,
},
};
};

View File

@ -0,0 +1,366 @@
import { WidgetType } from "constants/WidgetConstants";
import {
EvaluationReduxAction,
ReduxAction,
ReduxActionTypes,
ReduxActionWithoutPayload,
UpdateCanvasPayload,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
import { WidgetOperation } from "widgets/BaseWidget";
import { FetchPageRequest, PageLayout, SavePageResponse } from "api/PageApi";
import { UrlDataState } from "reducers/entityReducers/appReducer";
import { APP_MODE } from "entities/App";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import { GenerateTemplatePageRequest } from "../api/PageApi";
import { WidgetReduxActionTypes } from "../constants/ReduxActionConstants";
export interface FetchPageListPayload {
applicationId: string;
mode: APP_MODE;
}
export const fetchPageList = (
applicationId: string,
mode: APP_MODE
): ReduxAction<FetchPageListPayload> => {
return {
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
payload: {
applicationId,
mode,
},
};
};
export const fetchPage = (
pageId: string,
isFirstLoad = false
): ReduxAction<FetchPageRequest> => {
return {
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: {
id: pageId,
isFirstLoad,
},
};
};
export const fetchPublishedPage = (
pageId: string,
bustCache = false,
urlData?: UrlDataState
) => ({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
payload: {
pageId,
bustCache,
urlData,
},
});
export const fetchPageSuccess = (
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>
): EvaluationReduxAction<undefined> => {
return {
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
postEvalActions,
payload: undefined,
};
};
export const fetchPublishedPageSuccess = (
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>
): EvaluationReduxAction<undefined> => ({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
postEvalActions,
payload: undefined,
});
export const updateCurrentPage = (id: string) => ({
type: ReduxActionTypes.SWITCH_CURRENT_PAGE_ID,
payload: { id },
});
export const initCanvasLayout = (
payload: UpdateCanvasPayload
): ReduxAction<UpdateCanvasPayload> => {
return {
type: ReduxActionTypes.INIT_CANVAS_LAYOUT,
payload,
};
};
export const setLastUpdatedTime = (payload: number): ReduxAction<number> => ({
type: ReduxActionTypes.SET_LAST_UPDATED_TIME,
payload,
});
export const savePageSuccess = (payload: SavePageResponse) => {
return {
type: ReduxActionTypes.SAVE_PAGE_SUCCESS,
payload,
};
};
export const updateWidgetNameSuccess = () => {
return {
type: ReduxActionTypes.UPDATE_WIDGET_NAME_SUCCESS,
};
};
export const deletePageSuccess = () => {
return {
type: ReduxActionTypes.DELETE_PAGE_SUCCESS,
};
};
export const updateAndSaveLayout = (
widgets: CanvasWidgetsReduxState,
isRetry?: boolean
) => {
return {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets, isRetry },
};
};
export const saveLayout = (isRetry?: boolean) => {
return {
type: ReduxActionTypes.SAVE_PAGE_INIT,
payload: { isRetry },
};
};
export const createPage = (
applicationId: string,
pageName: string,
layouts: Partial<PageLayout>[]
) => {
return {
type: ReduxActionTypes.CREATE_PAGE_INIT,
payload: {
applicationId,
name: pageName,
layouts,
},
};
};
export const clonePageInit = (pageId: string) => {
return {
type: ReduxActionTypes.CLONE_PAGE_INIT,
payload: {
id: pageId,
},
};
};
export const clonePageSuccess = (
pageId: string,
pageName: string,
layoutId: string
) => {
return {
type: ReduxActionTypes.CLONE_PAGE_SUCCESS,
payload: {
pageId,
pageName,
layoutId,
},
};
};
export const updatePage = (id: string, name: string, isHidden: boolean) => {
return {
type: ReduxActionTypes.UPDATE_PAGE_INIT,
payload: {
id,
name,
isHidden,
},
};
};
export type WidgetAddChild = {
widgetId: string;
widgetName?: string;
type: WidgetType;
leftColumn: number;
topRow: number;
columns: number;
rows: number;
parentRowSpace: number;
parentColumnSpace: number;
newWidgetId: string;
tabId: string;
props?: Record<string, any>;
};
export type WidgetMove = {
widgetId: string;
leftColumn: number;
topRow: number;
parentId: string;
/*
If newParentId is different from what we have in redux store,
then we have to delete this,
as it has been dropped in another container somewhere.
*/
newParentId: string;
};
export type WidgetRemoveChild = {
widgetId: string;
childWidgetId: string;
};
export type WidgetDelete = {
widgetId?: string;
parentId?: string;
disallowUndo?: boolean;
isShortcut?: boolean;
};
export type MultipleWidgetDeletePayload = {
widgetIds: string[];
disallowUndo?: boolean;
isShortcut?: boolean;
};
export type WidgetResize = {
widgetId: string;
leftColumn: number;
rightColumn: number;
topRow: number;
bottomRow: number;
};
export type WidgetAddChildren = {
widgetId: string;
children: Array<{
type: WidgetType;
widgetId: string;
parentId: string;
parentRowSpace: number;
parentColumnSpace: number;
leftColumn: number;
rightColumn: number;
topRow: number;
bottomRow: number;
isLoading: boolean;
}>;
};
export type WidgetUpdateProperty = {
widgetId: string;
propertyPath: string;
propertyValue: any;
};
export const updateWidget = (
operation: WidgetOperation,
widgetId: string,
payload: any
): ReduxAction<
| WidgetAddChild
| WidgetMove
| WidgetResize
| WidgetDelete
| WidgetAddChildren
| WidgetUpdateProperty
> => {
return {
type: WidgetReduxActionTypes["WIDGET_" + operation],
payload: { widgetId, ...payload },
};
};
export const setUrlData = (
payload: UrlDataState
): ReduxAction<UrlDataState> => {
return {
type: ReduxActionTypes.SET_URL_DATA,
payload,
};
};
export const setAppMode = (payload: APP_MODE): ReduxAction<APP_MODE> => {
return {
type: ReduxActionTypes.SET_APP_MODE,
payload,
};
};
export const updateAppTransientStore = (
payload: Record<string, unknown>
): ReduxAction<Record<string, unknown>> => ({
type: ReduxActionTypes.UPDATE_APP_TRANSIENT_STORE,
payload,
});
export const updateAppPersistentStore = (
payload: Record<string, unknown>
): ReduxAction<Record<string, unknown>> => {
return {
type: ReduxActionTypes.UPDATE_APP_PERSISTENT_STORE,
payload,
};
};
export interface ReduxActionWithExtraParams<T> extends ReduxAction<T> {
extraParams: Record<any, any>;
}
export const generateTemplateSuccess = ({
isNewPage,
layoutId,
pageId,
pageName,
}: {
layoutId: string;
pageId: string;
pageName: string;
isNewPage: boolean;
}) => {
return {
type: ReduxActionTypes.GENERATE_TEMPLATE_PAGE_SUCCESS,
payload: {
layoutId,
pageId,
pageName,
isNewPage,
},
};
};
export const generateTemplateError = () => {
return {
type: ReduxActionErrorTypes.GENERATE_TEMPLATE_PAGE_ERROR,
};
};
export const generateTemplateToUpdatePage = ({
applicationId,
columns,
datasourceId,
mode,
pageId,
searchColumn,
tableName,
}: GenerateTemplatePageRequest): ReduxActionWithExtraParams<GenerateTemplatePageRequest> => {
return {
type: ReduxActionTypes.GENERATE_TEMPLATE_PAGE_INIT,
payload: {
pageId,
tableName,
datasourceId,
applicationId,
columns,
searchColumn,
},
extraParams: {
mode,
},
};
};

View File

@ -0,0 +1,62 @@
import {
ReduxAction,
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxActionWithoutPayload,
} from "constants/ReduxActionConstants";
import { PluginFormPayload } from "api/PluginApi";
import { DependencyMap } from "utils/DynamicBindingUtils";
export const fetchPlugins = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.FETCH_PLUGINS_REQUEST,
});
export const fetchPluginFormConfigs = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_REQUEST,
});
export type PluginFormsPayload = {
formConfigs: Record<string, any[]>;
editorConfigs: Record<string, any[]>;
settingConfigs: Record<string, any[]>;
dependencies: Record<string, DependencyMap>;
};
export const fetchPluginFormConfigsSuccess = (
payload: PluginFormsPayload,
): ReduxAction<PluginFormsPayload> => ({
type: ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS,
payload,
});
export interface PluginFormPayloadWithId extends PluginFormPayload {
id: string;
}
export const fetchPluginFormConfigSuccess = (
payload: PluginFormPayloadWithId,
): ReduxAction<PluginFormPayloadWithId> => ({
type: ReduxActionTypes.FETCH_PLUGIN_FORM_SUCCESS,
payload,
});
export const fetchPluginFormConfigError = (
payload: GetPluginFormConfigRequest,
): ReduxAction<GetPluginFormConfigRequest> => ({
type: ReduxActionErrorTypes.FETCH_PLUGIN_FORM_ERROR,
payload,
});
export interface GetPluginFormConfigRequest {
id: string;
}
// To fetch plugin form config for individual plugin
export const fetchPluginFormConfig = ({
pluginId: id,
}: {
pluginId: GetPluginFormConfigRequest;
}) => ({
type: ReduxActionTypes.GET_PLUGIN_FORM_CONFIG_INIT,
payload: id,
});

View File

@ -0,0 +1,32 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const updateWidgetName = (widgetId: string, newName: string) => {
return {
type: ReduxActionTypes.UPDATE_WIDGET_NAME_INIT,
payload: {
id: widgetId,
newName,
},
};
};
export const hidePropertyPane = () => {
return {
type: ReduxActionTypes.HIDE_PROPERTY_PANE,
};
};
export const bindDataToWidget = (payload: { widgetId: string }) => {
return {
type: ReduxActionTypes.BIND_DATA_TO_WIDGET,
payload,
};
};
export const setSnipingMode = (payload: boolean) => ({
type: ReduxActionTypes.SET_SNIPING_MODE,
payload,
});
export const resetSnipingMode = () => ({
type: ReduxActionTypes.RESET_SNIPING_MODE,
});

View File

@ -0,0 +1,61 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import {
AddApiToPageRequest,
FetchProviderWithCategoryRequest,
SearchApiOrProviderRequest,
} from "api/ProvidersApi";
export const fetchProviders = () => {
return {
type: ReduxActionTypes.FETCH_PROVIDERS_INIT,
};
};
export const searchApiOrProvider = (payload: SearchApiOrProviderRequest) => {
return {
type: ReduxActionTypes.SEARCH_APIORPROVIDERS_INIT,
payload,
};
};
export const fetchProviderCategories = () => {
return {
type: ReduxActionTypes.FETCH_PROVIDERS_CATEGORIES_INIT,
};
};
export const getProviderDetailsByProviderId = (providerId: string) => {
return {
type: ReduxActionTypes.FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_INIT,
payload: { providerId },
};
};
export const fetchProviderTemplates = (providerId: string) => {
return {
type: ReduxActionTypes.FETCH_PROVIDER_TEMPLATES_INIT,
payload: { providerId },
};
};
export const addApiToPage = (payload: AddApiToPageRequest) => {
return {
type: ReduxActionTypes.ADD_API_TO_PAGE_INIT,
payload,
};
};
export const fetchProvidersWithCategory = (
payload: FetchProviderWithCategoryRequest,
) => {
return {
type: ReduxActionTypes.FETCH_PROVIDERS_WITH_CATEGORY_INIT,
payload,
};
};
export const clearProviders = () => {
return {
type: ReduxActionTypes.CLEAR_PROVIDERS,
};
};

View File

@ -0,0 +1,29 @@
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
import { Action } from "entities/Action";
export const createQueryRequest = (payload: Partial<Action>) => {
return {
type: ReduxActionTypes.CREATE_QUERY_INIT,
payload,
};
};
export const initQueryPane = (
pluginType: string,
urlId?: string,
): ReduxAction<{ pluginType: string; id?: string }> => {
return {
type: ReduxActionTypes.INIT_QUERY_PANE,
payload: { id: urlId, pluginType },
};
};
export const changeQuery = (
id: string,
newQuery?: boolean,
): ReduxAction<{ id: string; newQuery?: boolean }> => {
return {
type: ReduxActionTypes.QUERY_PANE_CHANGE,
payload: { id, newQuery },
};
};

View File

@ -0,0 +1,6 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const handlePathUpdated = (location: typeof window.location) => ({
type: ReduxActionTypes.HANDLE_PATH_UPDATED,
payload: { location },
});

View File

@ -0,0 +1,5 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const resetReleasesCount = () => ({
type: ReduxActionTypes.RESET_UNREAD_RELEASES_COUNT,
});

View File

@ -0,0 +1,7 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { ThemeMode } from "../selectors/themeSelectors";
export const setThemeMode = (mode: ThemeMode) => ({
type: ReduxActionTypes.SET_THEME,
payload: mode,
});

View File

@ -0,0 +1,22 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { TourType } from "entities/Tour";
export const setActiveTour = (tourType: TourType) => ({
type: ReduxActionTypes.SET_ACTIVE_TOUR,
payload: tourType,
});
export const resetActiveTour = () => ({
type: ReduxActionTypes.RESET_ACTIVE_TOUR,
payload: undefined,
});
export const setActiveTourIndex = (index: number) => ({
type: ReduxActionTypes.SET_ACTIVE_TOUR_INDEX,
payload: index,
});
export const proceedToNextTourStep = () => ({
type: ReduxActionTypes.PROCEED_TO_NEXT_TOUR_STEP,
payload: undefined,
});

View File

@ -0,0 +1,102 @@
import {
ReduxActionErrorTypes,
ReduxActionTypes,
} from "constants/ReduxActionConstants";
import { CurrentUserDetailsRequestPayload } from "constants/userConstants";
import {
TokenPasswordUpdateRequest,
UpdateUserRequest,
VerifyTokenRequest,
} from "api/UserApi";
export const logoutUser = (payload?: { redirectURL: string }) => ({
type: ReduxActionTypes.LOGOUT_USER_INIT,
payload,
});
export const logoutUserSuccess = () => ({
type: ReduxActionTypes.LOGOUT_USER_SUCCESS,
});
export const logoutUserError = (error: any) => ({
type: ReduxActionErrorTypes.LOGOUT_USER_ERROR,
payload: {
error,
},
});
export const setCurrentUserDetails = () => ({
type: ReduxActionTypes.SET_CURRENT_USER_INIT,
payload: CurrentUserDetailsRequestPayload,
});
export const verifyInviteSuccess = () => ({
type: ReduxActionTypes.VERIFY_INVITE_SUCCESS,
});
export const verifyInvite = (payload: VerifyTokenRequest) => ({
type: ReduxActionTypes.VERIFY_INVITE_INIT,
payload,
});
export const verifyInviteError = (error: any) => ({
type: ReduxActionErrorTypes.VERIFY_INVITE_ERROR,
payload: { error },
});
export const invitedUserSignup = (
payload: TokenPasswordUpdateRequest & { resolve: any; reject: any },
) => ({
type: ReduxActionTypes.INVITED_USER_SIGNUP_INIT,
payload,
});
export const invitedUserSignupSuccess = () => ({
type: ReduxActionTypes.INVITED_USER_SIGNUP_SUCCESS,
});
export const invitedUserSignupError = (error: any) => ({
type: ReduxActionErrorTypes.INVITED_USER_SIGNUP_ERROR,
payload: {
error,
},
});
export const updateUserDetails = (payload: UpdateUserRequest) => ({
type: ReduxActionTypes.UPDATE_USER_DETAILS_INIT,
payload,
});
export const updatePhoto = (payload: {
file: File;
callback?: () => void;
}) => ({
type: ReduxActionTypes.UPLOAD_PROFILE_PHOTO,
payload,
});
export const removePhoto = (callback: () => void) => ({
type: ReduxActionTypes.REMOVE_PROFILE_PHOTO,
payload: { callback },
});
export const leaveOrganization = (orgId: string) => {
return {
type: ReduxActionTypes.LEAVE_ORG_INIT,
payload: {
orgId,
},
};
};
export const fetchFeatureFlagsInit = () => ({
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_INIT,
});
export const fetchFeatureFlagsSuccess = () => ({
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS,
});
export const fetchFeatureFlagsError = (error: any) => ({
type: ReduxActionErrorTypes.FETCH_FEATURE_FLAGS_ERROR,
payload: { error, show: false },
});

View File

@ -0,0 +1,15 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const historyPush = (url: string) => ({
type: ReduxActionTypes.HISTORY_PUSH,
payload: {
url,
},
});
export const windowRedirect = (url: string) => ({
type: ReduxActionTypes.REDIRECT_WINDOW_LOCATION,
payload: {
url,
},
});

View File

@ -0,0 +1,25 @@
import {
ReduxActionTypes,
ReduxSagaChannels,
} from "constants/ReduxActionConstants";
import { reconnectWebsocketEvent } from "constants/WebsocketConstants";
export const setIsWebsocketConnected = (payload: boolean) => ({
type: ReduxActionTypes.SET_IS_WEBSOCKET_CONNECTED,
payload,
});
export const websocketWriteEvent = (payload: {
type: string;
payload?: any;
}) => ({
type: ReduxSagaChannels.WEBSOCKET_WRITE_CHANNEL,
payload,
});
export const reconnectWebsocket = () =>
websocketWriteEvent(reconnectWebsocketEvent());
export const retrySocketConnection = () => ({
type: ReduxActionTypes.RETRY_WEBSOCKET_CONNECTION,
});

View File

@ -0,0 +1,148 @@
import {
ReduxActionTypes,
ReduxAction,
ReduxActionErrorTypes,
ReduxActionWithoutPayload,
WidgetReduxActionTypes,
} from "constants/ReduxActionConstants";
import {
ExecuteActionPayload,
ExecuteErrorPayload,
} from "constants/AppsmithActionConstants/ActionConstants";
import { BatchAction, batchAction } from "actions/batchActions";
import { WidgetProps } from "widgets/BaseWidget";
export const executeAction = (
payload: ExecuteActionPayload,
): BatchAction<ExecuteActionPayload> =>
batchAction({
type: ReduxActionTypes.EXECUTE_ACTION,
payload,
});
export const executeActionError = (
executeErrorPayload: ExecuteErrorPayload,
): ReduxAction<ExecuteErrorPayload> => {
return {
type: ReduxActionErrorTypes.EXECUTE_ACTION_ERROR,
payload: executeErrorPayload,
};
};
export const executePageLoadActions = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
});
export const disableDragAction = (
isDraggingDisabled: boolean,
): ReduxAction<{ isDraggingDisabled: boolean }> => {
return {
type: ReduxActionTypes.DISABLE_WIDGET_DRAG,
payload: {
isDraggingDisabled,
},
};
};
export const createModalAction = (
modalName: string,
): ReduxAction<{ modalName: string }> => {
return {
type: ReduxActionTypes.CREATE_MODAL_INIT,
payload: {
modalName,
},
};
};
export const focusWidget = (
widgetId?: string,
): ReduxAction<{ widgetId?: string }> => ({
type: ReduxActionTypes.FOCUS_WIDGET,
payload: { widgetId },
});
export const showModal = (id: string) => {
return {
type: ReduxActionTypes.SHOW_MODAL,
payload: {
modalId: id,
},
};
};
export const closeAllModals = () => {
return {
type: ReduxActionTypes.CLOSE_MODAL,
payload: {},
};
};
export const forceOpenPropertyPane = (id: string) => {
return {
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
payload: {
widgetId: id,
force: true,
},
};
};
export const closePropertyPane = () => {
return {
type: ReduxActionTypes.HIDE_PROPERTY_PANE,
payload: {
force: false,
},
};
};
export const closeTableFilterPane = () => {
return {
type: ReduxActionTypes.HIDE_TABLE_FILTER_PANE,
payload: {
force: false,
},
};
};
export const copyWidget = (isShortcut: boolean) => {
return {
type: ReduxActionTypes.COPY_SELECTED_WIDGET_INIT,
payload: {
isShortcut: !!isShortcut,
},
};
};
export const pasteWidget = () => {
return {
type: ReduxActionTypes.PASTE_COPIED_WIDGET_INIT,
};
};
export const deleteSelectedWidget = (
isShortcut: boolean,
disallowUndo = false,
) => {
return {
type: WidgetReduxActionTypes.WIDGET_DELETE,
payload: {
isShortcut,
disallowUndo,
},
};
};
export const cutWidget = () => {
return {
type: ReduxActionTypes.CUT_SELECTED_WIDGET,
};
};
export const addSuggestedWidget = (payload: Partial<WidgetProps>) => {
return {
type: ReduxActionTypes.ADD_SUGGESTED_WIDGET,
payload,
};
};

View File

@ -0,0 +1,71 @@
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
export const selectWidgetAction = (
widgetId?: string,
isMultiSelect?: boolean,
): ReduxAction<{ widgetId?: string; isMultiSelect?: boolean }> => ({
type: ReduxActionTypes.SELECT_WIDGET,
payload: { widgetId, isMultiSelect },
});
export const selectWidgetInitAction = (
widgetId?: string,
isMultiSelect?: boolean,
): ReduxAction<{ widgetId?: string; isMultiSelect?: boolean }> => ({
type: ReduxActionTypes.SELECT_WIDGET_INIT,
payload: { widgetId, isMultiSelect },
});
export const selectMultipleWidgetsAction = (
widgetIds?: string[],
): ReduxAction<{ widgetIds?: string[] }> => {
return {
type: ReduxActionTypes.SELECT_MULTIPLE_WIDGETS,
payload: { widgetIds },
};
};
export const silentAddSelectionsAction = (
widgetIds?: string[],
): ReduxAction<{ widgetIds?: string[] }> => {
return {
type: ReduxActionTypes.SELECT_WIDGETS,
payload: { widgetIds },
};
};
export const deselectMultipleWidgetsAction = (
widgetIds?: string[],
): ReduxAction<{ widgetIds?: string[] }> => {
return {
type: ReduxActionTypes.DESELECT_WIDGETS,
payload: { widgetIds },
};
};
export const selectAllWidgetsInCanvasInitAction = (
canvasId = MAIN_CONTAINER_WIDGET_ID,
): ReduxAction<{ canvasId: string }> => {
return {
type: ReduxActionTypes.SELECT_ALL_WIDGETS_IN_CANVAS_INIT,
payload: {
canvasId,
},
};
};
export const selectMultipleWidgetsInitAction = (widgetIds: string[]) => {
return {
type: ReduxActionTypes.SELECT_MULTIPLE_WIDGETS_INIT,
payload: { widgetIds },
};
};
export const shiftSelectWidgetsEntityExplorerInitAction = (
widgetId: string,
siblingWidgets: string[],
): ReduxAction<{ widgetId: string; siblingWidgets: string[] }> => ({
type: ReduxActionTypes.SHIFT_SELECT_WIDGET_INIT,
payload: { widgetId, siblingWidgets },
});

View File

@ -0,0 +1,33 @@
import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
import { WidgetCardProps } from "widgets/BaseWidget";
export const fetchWidgetCards = () => {
return {
type: ReduxActionTypes.FETCH_WIDGET_CARDS,
};
};
export const errorFetchingWidgetCards = (error: any) => {
return {
type: ReduxActionErrorTypes.FETCH_WIDGET_CARDS_ERROR,
error,
};
};
export const successFetchingWidgetCards = (cards: {
[id: string]: WidgetCardProps[];
}) => {
return {
type: ReduxActionTypes.FETCH_WIDGET_CARDS_SUCCESS,
cards,
};
};
export default {
fetchWidgetCards,
errorFetchingWidgetCards,
successFetchingWidgetCards,
};

View File

@ -0,0 +1,188 @@
import API, { HttpMethod } from "api/Api";
import { ApiResponse, GenericApiResponse, ResponseMeta } from "./ApiResponses";
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "constants/ApiConstants";
import axios, { AxiosPromise, CancelTokenSource } from "taro-axios";
import { Action, ActionViewMode } from "entities/Action";
import { APIRequest } from "constants/AppsmithActionConstants/ActionConstants";
import { WidgetType } from "constants/WidgetConstants";
export interface CreateActionRequest<T> extends APIRequest {
datasourceId: string;
pageId: string;
name: string;
actionConfiguration: T;
}
export interface UpdateActionRequest<T> extends CreateActionRequest<T> {
actionId: string;
}
export interface Property {
key: string;
value?: string;
}
export interface BodyFormData {
editable: boolean;
mandatory: boolean;
description: string;
key: string;
value?: string;
type: string;
}
export interface QueryConfig {
queryString: string;
}
export interface ActionCreateUpdateResponse extends ApiResponse {
id: string;
jsonPathKeys: Record<string, string>;
}
export type PaginationField = "PREV" | "NEXT";
export interface ExecuteActionRequest extends APIRequest {
actionId: string;
params?: Property[];
paginationField?: PaginationField;
viewMode: boolean;
}
export interface ExecuteActionResponse extends ApiResponse {
actionId: string;
data: any;
}
export interface ActionApiResponseReq {
headers: Record<string, string[]>;
body: Record<string, unknown> | null;
httpMethod: HttpMethod | "";
url: string;
}
export interface ActionExecutionResponse {
responseMeta: ResponseMeta;
data: {
body: Record<string, unknown> | string;
headers: Record<string, string[]>;
statusCode: string;
isExecutionSuccess: boolean;
request: ActionApiResponseReq;
};
clientMeta: {
duration: string;
size: string;
};
}
export interface SuggestedWidget {
type: WidgetType;
bindingQuery: string;
}
export interface ActionResponse {
body: unknown;
headers: Record<string, string[]>;
request?: ActionApiResponseReq;
statusCode: string;
duration: string;
size: string;
isExecutionSuccess?: boolean;
suggestedWidgets?: SuggestedWidget[];
messages?: Array<string>;
}
export interface MoveActionRequest {
action: Action;
destinationPageId: string;
}
export interface CopyActionRequest {
action: Action;
pageId: string;
}
export interface UpdateActionNameRequest {
pageId: string;
actionId: string;
layoutId: string;
newName: string;
oldName: string;
}
class ActionAPI extends API {
static url = "v1/actions";
static apiUpdateCancelTokenSource: CancelTokenSource;
static queryUpdateCancelTokenSource: CancelTokenSource;
static createAction(
apiConfig: Partial<Action>,
): AxiosPromise<ActionCreateUpdateResponse> {
return API.post(ActionAPI.url, apiConfig);
}
static fetchActions(
applicationId: string,
): AxiosPromise<GenericApiResponse<Action[]>> {
return API.get(ActionAPI.url, { applicationId });
}
static fetchActionsForViewMode(
applicationId: string,
): AxiosPromise<GenericApiResponse<ActionViewMode[]>> {
return API.get(`${ActionAPI.url}/view`, { applicationId });
}
static fetchActionsByPageId(
pageId: string,
): AxiosPromise<GenericApiResponse<Action[]>> {
return API.get(ActionAPI.url, { pageId });
}
static updateAction(
apiConfig: Partial<Action>,
): AxiosPromise<ActionCreateUpdateResponse> {
if (ActionAPI.apiUpdateCancelTokenSource) {
ActionAPI.apiUpdateCancelTokenSource.cancel();
}
ActionAPI.apiUpdateCancelTokenSource = axios.CancelToken.source();
const action = Object.assign({}, apiConfig);
// While this line is not required, name can not be changed from this endpoint
delete action.name;
return API.put(`${ActionAPI.url}/${action.id}`, action, undefined, {
cancelToken: ActionAPI.apiUpdateCancelTokenSource.token,
});
}
static updateActionName(updateActionNameRequest: UpdateActionNameRequest) {
return API.put(ActionAPI.url + "/refactor", updateActionNameRequest);
}
static deleteAction(id: string) {
return API.delete(`${ActionAPI.url}/${id}`);
}
static executeAction(
executeAction: ExecuteActionRequest,
timeout?: number,
): AxiosPromise<ActionExecutionResponse> {
return API.post(ActionAPI.url + "/execute", executeAction, undefined, {
timeout: timeout || DEFAULT_EXECUTE_ACTION_TIMEOUT_MS,
});
}
static moveAction(moveRequest: MoveActionRequest) {
return API.put(ActionAPI.url + "/move", moveRequest, undefined, {
timeout: DEFAULT_EXECUTE_ACTION_TIMEOUT_MS,
});
}
static toggleActionExecuteOnLoad(actionId: string, shouldExecute: boolean) {
return API.put(ActionAPI.url + `/executeOnLoad/${actionId}`, undefined, {
flag: shouldExecute.toString(),
});
}
}
export default ActionAPI;

101
app/taro/src/api/Api.ts Normal file
View File

@ -0,0 +1,101 @@
import axios, { AxiosInstance, AxiosRequestConfig } from "taro-axios";
import { REQUEST_TIMEOUT_MS } from "constants/ApiConstants";
import { convertObjectToQueryParams } from "utils/AppsmithUtils";
import {
apiFailureResponseInterceptor,
apiRequestInterceptor,
apiSuccessResponseInterceptor,
} from "api/ApiUtils";
import { API_REQUEST_HEADERS } from "constants/AppsmithActionConstants/ActionConstants";
//TODO(abhinav): Refactor this to make more composable.
export const apiRequestConfig = {
baseURL: API_BASE_URL,
timeout: REQUEST_TIMEOUT_MS,
headers: API_REQUEST_HEADERS,
withCredentials: true,
};
const axiosInstance: AxiosInstance = axios.create();
axiosInstance.interceptors.request.use(apiRequestInterceptor);
axiosInstance.interceptors.response.use(
apiSuccessResponseInterceptor,
apiFailureResponseInterceptor,
);
class Api {
static get(
url: string,
queryParams?: any,
config: Partial<AxiosRequestConfig> = {},
) {
return axiosInstance.get(url + convertObjectToQueryParams(queryParams), {
...apiRequestConfig,
...config,
});
}
static post(
url: string,
body?: any,
queryParams?: any,
config: Partial<AxiosRequestConfig> = {},
) {
return axiosInstance.post(
url + convertObjectToQueryParams(queryParams),
body,
{
...apiRequestConfig,
...config,
},
);
}
static put(
url: string,
body?: any,
queryParams?: any,
config: Partial<AxiosRequestConfig> = {},
) {
return axiosInstance.put(
url + convertObjectToQueryParams(queryParams),
body,
{
...apiRequestConfig,
...config,
},
);
}
static patch(
url: string,
body?: any,
queryParams?: any,
config: Partial<AxiosRequestConfig> = {},
) {
return axiosInstance.patch(
url + convertObjectToQueryParams(queryParams),
body,
{
...apiRequestConfig,
...config,
},
);
}
static delete(
url: string,
queryParams?: any,
config: Partial<AxiosRequestConfig> = {},
) {
return axiosInstance.delete(url + convertObjectToQueryParams(queryParams), {
...apiRequestConfig,
...config,
});
}
}
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
export default Api;

View File

@ -0,0 +1,31 @@
export type APIResponseError = {
code: number;
message: string;
};
export type ResponseMeta = {
status: number;
success: boolean;
error?: APIResponseError;
};
export type ApiResponse = {
responseMeta: ResponseMeta;
data: any;
};
export type GenericApiResponse<T> = {
responseMeta: ResponseMeta;
data: T;
};
// NO_DATASOURCES_FOUND, 1000, "Unable to find {0} with id {1}"
// INVALID_PARAMTER, 4000, "Invalid parameter {0} provided in the input"
// PLUGIN_NOT_INSTALLED, 4001, "Plugin {0} not installed"
// MISSING_PLUGIN_ID, 4002, "Missing plugin id. Please input correct plugin id"
// MISSING_DATASOURCES_ID, 4003, "Missing datasource id. Please input correct datasource id"
// MISSING_PAGE_ID, 4004, "Missing page id. Pleaes input correct page id"
// PAGE_DOES_NOT_EXIST_IN_ORG, 4006, "Page {0} does not belong to the current user {1} organization."
// UNAUTHORIZED_DOMAIN, 4001, "Invalid email domain provided. Please sign in with a valid work email ID"
// INTERNAL_SERVER_ERROR, 5000, "Internal server error while processing request"
// REPOSITORY_SAVE_FAILED, 5001, "Repository save failed."

View File

@ -0,0 +1,147 @@
import {
createMessage,
ERROR_0,
ERROR_500,
SERVER_API_TIMEOUT_ERROR,
} from "constants/messages";
import axios, { AxiosRequestConfig, AxiosResponse } from "taro-axios";
import {
API_STATUS_CODES,
ERROR_CODES,
SERVER_ERROR_CODES,
} from "constants/ApiConstants";
import log from "loglevel";
import { ActionExecutionResponse } from "api/ActionAPI";
import { logoutUser } from "actions/userActions";
import { AUTH_LOGIN_URL } from "constants/routes";
const executeActionRegex = /actions\/execute/;
const timeoutErrorRegex = /timeout of (\d+)ms exceeded/;
export const axiosConnectionAbortedCode = "ECONNABORTED";
// polyfill
const performance = Date;
const makeExecuteActionResponse = (response: any): ActionExecutionResponse => ({
...response.data,
clientMeta: {
size: response.headers["content-length"],
duration: Number(performance.now() - response.config.timer).toFixed(),
},
});
const is404orAuthPath = () => {
const pathName = window.location.pathname;
return /^\/404/.test(pathName) || /^\/user\/\w+/.test(pathName);
};
// Request interceptor will add a timer property to the request.
// this will be used to calculate the time taken for an action
// execution request
export const apiRequestInterceptor = (config: AxiosRequestConfig) => {
return { ...config, timer: performance.now() };
};
// On success of an API, if the api is an action execution,
// add the client meta object with size and time taken info
// otherwise just return the data
export const apiSuccessResponseInterceptor = (
response: AxiosResponse,
): AxiosResponse["data"] => {
if (response.config.url) {
if (response.config.url.match(executeActionRegex)) {
return makeExecuteActionResponse(response);
}
}
return response.data;
};
// Handle different api failure scenarios
export const apiFailureResponseInterceptor = (error: any) => {
// Return error when there is no internet
if (!window.navigator.onLine) {
return Promise.reject({
...error,
message: createMessage(ERROR_0),
});
}
// Return if the call was cancelled via cancel token
if (axios.isCancel(error)) {
return;
}
// Return modified response if action execution failed
if (error.config && error.config.url.match(executeActionRegex)) {
return makeExecuteActionResponse(error.response);
}
// Return error if any timeout happened in other api calls
if (
error.code === axiosConnectionAbortedCode &&
error.message &&
error.message.match(timeoutErrorRegex)
) {
return Promise.reject({
...error,
message: createMessage(SERVER_API_TIMEOUT_ERROR),
code: ERROR_CODES.REQUEST_TIMEOUT,
});
}
if (error.response) {
if (error.response.status === API_STATUS_CODES.SERVER_ERROR) {
return Promise.reject({
...error,
code: ERROR_CODES.SERVER_ERROR,
message: createMessage(ERROR_500),
});
}
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
if (!is404orAuthPath()) {
const currentUrl = `${window.location.href}`;
if (error.response.status === API_STATUS_CODES.REQUEST_NOT_AUTHORISED) {
// Redirect to login and set a redirect url.
console.log("redirect logout");
// store.dispatch(
// logoutUser({
// redirectURL: `${AUTH_LOGIN_URL}?redirectUrl=${encodeURIComponent(
// currentUrl,
// )}`,
// }),
// );
return Promise.reject({
code: ERROR_CODES.REQUEST_NOT_AUTHORISED,
message: "Unauthorized. Redirecting to login page...",
show: false,
});
}
const errorData = error.response.data.responseMeta;
if (
errorData.status === API_STATUS_CODES.RESOURCE_NOT_FOUND &&
errorData.error.code === SERVER_ERROR_CODES.RESOURCE_NOT_FOUND
) {
return Promise.reject({
code: ERROR_CODES.PAGE_NOT_FOUND,
message: "Resource Not Found",
show: false,
});
}
}
if (error.response.data.responseMeta) {
return Promise.resolve(error.response.data);
}
return Promise.reject(error.response.data);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
log.error(error.request);
} else {
// Something happened in setting up the request that triggered an Error
log.error("Error", error.message);
}
console.log(error.config);
return Promise.resolve(error);
};

View File

@ -0,0 +1,217 @@
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
import { AxiosPromise } from "taro-axios";
import { AppLayoutConfig } from "reducers/entityReducers/pageListReducer";
export interface PublishApplicationRequest {
applicationId: string;
}
export interface ChangeAppViewAccessRequest {
applicationId: string;
publicAccess: boolean;
}
export interface PublishApplicationResponse extends ApiResponse {
data: unknown;
}
export interface ApplicationPagePayload {
id: string;
name: string;
isDefault: boolean;
}
export interface ApplicationResponsePayload {
id: string;
name: string;
organizationId: string;
pages?: ApplicationPagePayload[];
appIsExample: boolean;
appLayout?: AppLayoutConfig;
unreadCommentThreads?: number;
}
export interface FetchApplicationResponse extends ApiResponse {
data: ApplicationResponsePayload & { pages: ApplicationPagePayload[] };
}
export interface FetchApplicationsResponse extends ApiResponse {
data: Array<ApplicationResponsePayload & { pages: ApplicationPagePayload[] }>;
}
export interface SetDefaultPageRequest {
id: string;
applicationId: string;
}
export interface DeleteApplicationRequest {
applicationId: string;
}
export interface DuplicateApplicationRequest {
applicationId: string;
}
export interface ForkApplicationRequest {
applicationId: string;
organizationId: string;
}
export interface GetAllApplicationResponse extends ApiResponse {
data: Array<ApplicationResponsePayload & { pages: ApplicationPagePayload[] }>;
}
export type UpdateApplicationPayload = {
icon?: string;
color?: string;
name?: string;
currentApp?: boolean;
appLayout?: AppLayoutConfig;
};
export type UpdateApplicationRequest = UpdateApplicationPayload & {
id: string;
};
export interface ApplicationObject {
id: string;
name: string;
icon?: string;
color?: string;
organizationId: string;
pages: ApplicationPagePayload[];
userPermissions: string[];
}
export interface UserRoles {
name: string;
roleName: string;
username: string;
}
export interface OrganizationApplicationObject {
applications: Array<ApplicationObject>;
organization: {
id: string;
name: string;
};
userRoles: Array<UserRoles>;
}
export interface FetchUsersApplicationsOrgsResponse extends ApiResponse {
data: {
organizationApplications: Array<OrganizationApplicationObject>;
user: string;
newReleasesCount: string;
releaseItems: Array<Record<string, any>>;
};
}
export interface ImportApplicationRequest {
orgId: string;
applicationFile?: File;
progress?: (progressEvent: ProgressEvent) => void;
onSuccessCallback?: () => void;
}
class ApplicationApi extends Api {
static baseURL = "v1/applications/";
static publishURLPath = (applicationId: string) => `publish/${applicationId}`;
static createApplicationPath = (orgId: string) => `?orgId=${orgId}`;
static changeAppViewAccessPath = (applicationId: string) =>
`${applicationId}/changeAccess`;
static setDefaultPagePath = (request: SetDefaultPageRequest) =>
`${ApplicationApi.baseURL}${request.applicationId}/page/${request.id}/makeDefault`;
static publishApplication(
publishApplicationRequest: PublishApplicationRequest,
): AxiosPromise<PublishApplicationResponse> {
return Api.post(
ApplicationApi.baseURL +
ApplicationApi.publishURLPath(publishApplicationRequest.applicationId),
undefined,
{},
);
}
static fetchApplications(): AxiosPromise<FetchApplicationsResponse> {
return Api.get(ApplicationApi.baseURL);
}
static getAllApplication(): AxiosPromise<GetAllApplicationResponse> {
return Api.get(ApplicationApi.baseURL + "new");
}
static fetchApplication(
applicationId: string,
): AxiosPromise<FetchApplicationResponse> {
return Api.get(ApplicationApi.baseURL + applicationId);
}
static fetchApplicationForViewMode(
applicationId: string,
): AxiosPromise<FetchApplicationResponse> {
return Api.get(ApplicationApi.baseURL + `view/${applicationId}`);
}
static setDefaultApplicationPage(
request: SetDefaultPageRequest,
): AxiosPromise<ApiResponse> {
return Api.put(ApplicationApi.setDefaultPagePath(request));
}
static changeAppViewAccess(
request: ChangeAppViewAccessRequest,
): AxiosPromise<ApiResponse> {
return Api.put(
ApplicationApi.baseURL +
ApplicationApi.changeAppViewAccessPath(request.applicationId),
{ publicAccess: request.publicAccess },
);
}
static updateApplication(
request: UpdateApplicationRequest,
): AxiosPromise<ApiResponse> {
const { id, ...rest } = request;
return Api.put(ApplicationApi.baseURL + id, rest);
}
static deleteApplication(
request: DeleteApplicationRequest,
): AxiosPromise<ApiResponse> {
return Api.delete(ApplicationApi.baseURL + request.applicationId);
}
static duplicateApplication(
request: DuplicateApplicationRequest,
): AxiosPromise<ApiResponse> {
return Api.post(ApplicationApi.baseURL + "clone/" + request.applicationId);
}
static forkApplication(
request: ForkApplicationRequest,
): AxiosPromise<ApiResponse> {
return Api.post(
"v1/applications/" +
request.applicationId +
"/fork/" +
request.organizationId,
);
}
static importApplicationToOrg(
request: ImportApplicationRequest,
): AxiosPromise<ApiResponse> {
const formData = new FormData();
if (request.applicationFile) {
formData.append("file", request.applicationFile);
}
return Api.post("v1/applications/import/" + request.orgId, formData, null, {
headers: {
"Content-Type": "multipart/form-data",
},
onUploadProgress: request.progress,
});
}
}
export default ApplicationApi;

View File

@ -0,0 +1,12 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ImportedCollections } from "constants/collectionsConstants";
class ImportedCollectionsApi extends Api {
static importedCollectionsURL = "v1/import/templateCollections";
static fetchImportedCollections(): AxiosPromise<ImportedCollections> {
return Api.get(ImportedCollectionsApi.importedCollectionsURL);
}
}
export default ImportedCollectionsApi;

View File

@ -0,0 +1,105 @@
import { DEFAULT_TEST_DATA_SOURCE_TIMEOUT_MS } from "constants/ApiConstants";
import API from "api/Api";
import { GenericApiResponse } from "./ApiResponses";
import { AxiosPromise } from "taro-axios";
import { DatasourceAuthentication, Datasource } from "entities/Datasource";
export interface CreateDatasourceConfig {
name: string;
pluginId: string;
datasourceConfiguration: {
url: string;
databaseName?: string;
authentication?: DatasourceAuthentication;
};
//Passed for logging purposes.
appName?: string;
}
export interface EmbeddedRestDatasourceRequest {
datasourceConfiguration: { url: string };
invalids: Array<string>;
isValid: boolean;
name: string;
organizationId: string;
pluginId: string;
}
type executeQueryData = Array<{ key: string; value?: string }>;
export interface executeDatasourceQueryRequest {
datasourceId: string;
data: executeQueryData;
}
class DatasourcesApi extends API {
static url = "v1/datasources";
static fetchDatasources(
orgId: string,
): AxiosPromise<GenericApiResponse<Datasource[]>> {
return API.get(DatasourcesApi.url + `?organizationId=${orgId}`);
}
static createDatasource(datasourceConfig: Partial<Datasource>): Promise<any> {
return API.post(DatasourcesApi.url, datasourceConfig);
}
static testDatasource(datasourceConfig: Partial<Datasource>): Promise<any> {
return API.post(`${DatasourcesApi.url}/test`, datasourceConfig, undefined, {
timeout: DEFAULT_TEST_DATA_SOURCE_TIMEOUT_MS,
});
}
static updateDatasource(
datasourceConfig: Partial<Datasource>,
id: string,
): Promise<any> {
return API.put(DatasourcesApi.url + `/${id}`, datasourceConfig);
}
static deleteDatasource(id: string): Promise<any> {
return API.delete(DatasourcesApi.url + `/${id}`);
}
static fetchDatasourceStructure(
id: string,
ignoreCache = false,
): Promise<any> {
return API.get(
DatasourcesApi.url + `/${id}/structure?ignoreCache=${ignoreCache}`,
);
}
static fetchMockDatasources(): AxiosPromise<
GenericApiResponse<Datasource[]>
> {
return API.get(DatasourcesApi.url + "/mocks");
}
static addMockDbToDatasources(
name: string,
organizationId: string,
pluginId: string,
packageName: string,
): Promise<any> {
return API.post(DatasourcesApi.url + `/mocks`, {
name,
organizationId,
pluginId,
packageName,
});
}
static executeDatasourceQuery({
data,
datasourceId,
}: executeDatasourceQueryRequest) {
return API.put(
DatasourcesApi.url + `/datasource-query` + `/${datasourceId}`,
data,
);
}
}
export default DatasourcesApi;

View File

@ -0,0 +1,27 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
export interface CurlImportRequest {
type: string;
pageId: string;
name: string;
curl: string;
organizationId: string;
}
class CurlImportApi extends Api {
static curlImportURL = `v1/import`;
static curlImport(request: CurlImportRequest): AxiosPromise<ApiResponse> {
const { curl, name, organizationId, pageId } = request;
return Api.post(CurlImportApi.curlImportURL, curl, {
type: "CURL",
pageId,
name,
organizationId,
});
}
}
export default CurlImportApi;

View File

@ -0,0 +1,33 @@
import { AxiosPromise } from "taro-axios";
import Api from "./Api";
import { ApiResponse } from "./ApiResponses";
class NotificationsApi extends Api {
static baseURL = "v1/notifications";
static markAsReadURL = `${NotificationsApi.baseURL}/isRead`;
static markAllAsReadURL = `${NotificationsApi.markAsReadURL}/all`;
static fetchUnreadNotificationsCountURL = `${NotificationsApi.baseURL}/count/unread`;
static fetchNotifications(beforeDate?: string): AxiosPromise<ApiResponse> {
return Api.get(NotificationsApi.baseURL, beforeDate ? { beforeDate } : {});
}
static markAllNotificationsAsRead(): AxiosPromise<ApiResponse> {
return Api.patch(NotificationsApi.markAllAsReadURL, { isRead: true });
}
static fetchUnreadNotificationsCount(): AxiosPromise<ApiResponse> {
return Api.get(NotificationsApi.fetchUnreadNotificationsCountURL);
}
static markNotificationsAsRead(
ids: Array<string>,
): AxiosPromise<ApiResponse> {
return Api.patch(NotificationsApi.markAsReadURL, {
isRead: true,
idList: ids,
});
}
}
export default NotificationsApi;

132
app/taro/src/api/OrgApi.ts Normal file
View File

@ -0,0 +1,132 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
import { OrgRole, Org } from "constants/orgConstants";
export interface FetchOrgRolesResponse extends ApiResponse {
data: OrgRole[];
}
export interface FetchOrgsResponse extends ApiResponse {
data: Org[];
}
export interface FetchOrgResponse extends ApiResponse {
data: Org;
}
export interface FetchAllUsersResponse extends ApiResponse {
data: OrgRole[];
}
export interface FetchAllRolesResponse extends ApiResponse {
data: Org[];
}
export interface FetchOrgRequest {
orgId: string;
skipValidation?: boolean;
}
export interface FetchAllUsersRequest {
orgId: string;
}
export interface ChangeUserRoleRequest {
orgId: string;
role: string;
username: string;
}
export interface DeleteOrgUserRequest {
orgId: string;
username: string;
}
export interface FetchAllRolesRequest {
orgId: string;
}
export interface SaveOrgRequest {
id: string;
name?: string;
website?: string;
email?: string;
}
export interface SaveOrgLogo {
id: string;
logo: File;
progress: (progressEvent: ProgressEvent) => void;
}
export interface CreateOrgRequest {
name: string;
}
class OrgApi extends Api {
static rolesURL = "v1/groups";
static orgsURL = "v1/organizations";
static fetchRoles(): AxiosPromise<FetchOrgRolesResponse> {
return Api.get(OrgApi.rolesURL);
}
static fetchOrgs(): AxiosPromise<FetchOrgsResponse> {
return Api.get(OrgApi.orgsURL);
}
static fetchOrg(request: FetchOrgRequest): AxiosPromise<FetchOrgResponse> {
return Api.get(OrgApi.orgsURL + "/" + request.orgId);
}
static saveOrg(request: SaveOrgRequest): AxiosPromise<ApiResponse> {
return Api.put(OrgApi.orgsURL + "/" + request.id, request);
}
static createOrg(request: CreateOrgRequest): AxiosPromise<ApiResponse> {
return Api.post(OrgApi.orgsURL, request);
}
static fetchAllUsers(
request: FetchAllUsersRequest,
): AxiosPromise<FetchAllUsersResponse> {
return Api.get(OrgApi.orgsURL + "/" + request.orgId + "/members");
}
static fetchAllRoles(
request: FetchAllRolesRequest,
): AxiosPromise<FetchAllRolesResponse> {
return Api.get(OrgApi.orgsURL + `/roles?organizationId=${request.orgId}`);
}
static changeOrgUserRole(
request: ChangeUserRoleRequest,
): AxiosPromise<ApiResponse> {
return Api.put(OrgApi.orgsURL + "/" + request.orgId + "/role", {
username: request.username,
roleName: request.role,
});
}
static deleteOrgUser(
request: DeleteOrgUserRequest,
): AxiosPromise<ApiResponse> {
return Api.put(OrgApi.orgsURL + "/" + request.orgId + "/role", {
username: request.username,
roleName: null,
});
}
static saveOrgLogo(request: SaveOrgLogo): AxiosPromise<ApiResponse> {
const formData = new FormData();
if (request.logo) {
formData.append("file", request.logo);
}
return Api.post(
OrgApi.orgsURL + "/" + request.id + "/logo",
formData,
null,
{
headers: {
"Content-Type": "multipart/form-data",
},
onUploadProgress: request.progress,
},
);
}
static deleteOrgLogo(request: { id: string }): AxiosPromise<ApiResponse> {
return Api.delete(OrgApi.orgsURL + "/" + request.id + "/logo");
}
}
export default OrgApi;

View File

@ -0,0 +1,229 @@
import Api from "api/Api";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { ApiResponse } from "./ApiResponses";
import { WidgetProps } from "widgets/BaseWidget";
import axios, { AxiosPromise, CancelTokenSource } from "taro-axios";
import { PageAction } from "constants/AppsmithActionConstants/ActionConstants";
export interface FetchPageRequest {
id: string;
isFirstLoad?: boolean;
}
export interface FetchPublishedPageRequest {
pageId: string;
bustCache?: boolean;
}
export interface SavePageRequest {
dsl: ContainerWidgetProps<WidgetProps>;
layoutId: string;
pageId: string;
}
export interface PageLayout {
id: string;
dsl: Partial<ContainerWidgetProps<any>>;
layoutOnLoadActions: PageAction[][];
layoutActions: PageAction[];
}
export type FetchPageResponse = ApiResponse & {
data: {
id: string;
name: string;
applicationId: string;
layouts: Array<PageLayout>;
};
};
export type FetchPublishedPageResponse = ApiResponse & {
data: {
id: string;
dsl: Partial<ContainerWidgetProps<any>>;
pageId: string;
};
};
export interface SavePageResponse extends ApiResponse {
data: {
id: string;
layoutOnLoadActions: PageAction[][];
dsl: Partial<ContainerWidgetProps<any>>;
messages: string[];
actionUpdates: Array<{
executeOnLoad: boolean;
id: string;
name: string;
}>;
};
}
export interface CreatePageRequest {
applicationId: string;
name: string;
layouts: Partial<PageLayout>[];
}
export interface UpdatePageRequest {
id: string;
name: string;
isHidden?: boolean;
}
export interface CreatePageResponse extends ApiResponse {
data: unknown;
}
export interface FetchPageListResponse extends ApiResponse {
data: {
pages: Array<{
id: string;
name: string;
isDefault: boolean;
isHidden?: boolean;
layouts: Array<PageLayout>;
icon?: string;
}>;
organizationId: string;
};
}
export interface DeletePageRequest {
id: string;
}
export interface ClonePageRequest {
id: string;
}
export interface UpdateWidgetNameRequest {
pageId: string;
layoutId: string;
newName: string;
oldName: string;
}
export interface UpdateWidgetNameResponse extends ApiResponse {
data: PageLayout;
}
export interface GenerateTemplatePageRequest {
pageId: string;
tableName: string;
datasourceId: string;
applicationId: string;
columns?: string[];
searchColumn?: string;
mode?: string;
}
export type GenerateTemplatePageRequestResponse = ApiResponse & {
data: {
id: string;
name: string;
applicationId: string;
layouts: Array<PageLayout>;
};
};
class PageApi extends Api {
static url = "v1/pages";
static refactorLayoutURL = "v1/layouts/refactor";
static pageUpdateCancelTokenSource?: CancelTokenSource = undefined;
static getLayoutUpdateURL = (pageId: string, layoutId: string) => {
return `v1/layouts/${layoutId}/pages/${pageId}`;
};
static getGenerateTemplateURL = (pageId?: string) => {
return `${PageApi.url}/crud-page${pageId ? `/${pageId}` : ""}`;
};
static getPublishedPageURL = (pageId: string, bustCache?: boolean) => {
const url = `v1/pages/${pageId}/view`;
return !!bustCache ? url + "?v=" + +new Date() : url;
};
static updatePageUrl = (pageId: string) => `${PageApi.url}/${pageId}`;
static fetchPage(
pageRequest: FetchPageRequest,
): AxiosPromise<FetchPageResponse> {
return Api.get(PageApi.url + "/" + pageRequest.id);
}
static savePage(
savePageRequest: SavePageRequest,
): AxiosPromise<SavePageResponse> | undefined {
if (PageApi.pageUpdateCancelTokenSource) {
PageApi.pageUpdateCancelTokenSource.cancel();
}
const body = { dsl: savePageRequest.dsl };
PageApi.pageUpdateCancelTokenSource = axios.CancelToken.source();
return Api.put(
PageApi.getLayoutUpdateURL(
savePageRequest.pageId,
savePageRequest.layoutId,
),
body,
undefined,
{ cancelToken: PageApi.pageUpdateCancelTokenSource.token },
);
}
static fetchPublishedPage(
pageRequest: FetchPublishedPageRequest,
): AxiosPromise<FetchPublishedPageResponse> {
return Api.get(
PageApi.getPublishedPageURL(pageRequest.pageId, pageRequest.bustCache),
);
}
static createPage(
createPageRequest: CreatePageRequest,
): AxiosPromise<FetchPageResponse> {
return Api.post(PageApi.url, createPageRequest);
}
static updatePage(request: UpdatePageRequest): AxiosPromise<ApiResponse> {
return Api.put(PageApi.updatePageUrl(request.id), request);
}
static generateTemplatePage(
request: GenerateTemplatePageRequest,
): AxiosPromise<ApiResponse> {
if (request.pageId) {
return Api.put(PageApi.getGenerateTemplateURL(request.pageId), request);
} else {
return Api.post(PageApi.getGenerateTemplateURL(), request);
}
}
static fetchPageList(
applicationId: string,
): AxiosPromise<FetchPageListResponse> {
return Api.get(PageApi.url + "/application/" + applicationId);
}
static fetchPageListViewMode(
applicationId: string,
): AxiosPromise<FetchPageListResponse> {
return Api.get(PageApi.url + "/view/application/" + applicationId);
}
static deletePage(request: DeletePageRequest): AxiosPromise<ApiResponse> {
return Api.delete(PageApi.url + "/" + request.id);
}
static clonePage(request: ClonePageRequest): AxiosPromise<ApiResponse> {
return Api.post(PageApi.url + "/clone/" + request.id);
}
static updateWidgetName(
request: UpdateWidgetNameRequest,
): AxiosPromise<UpdateWidgetNameResponse> {
return Api.put(PageApi.refactorLayoutURL, request);
}
}
export default PageApi;

View File

@ -0,0 +1,48 @@
import Api from "api/Api";
import { AxiosPromise } from "taro-axios";
import { GenericApiResponse } from "api/ApiResponses";
import { PluginType } from "entities/Action";
import { DependencyMap } from "utils/DynamicBindingUtils";
export type PluginId = string;
export type PluginPackageName = string;
export type GenerateCRUDEnabledPluginMap = Record<PluginId, PluginPackageName>;
export interface Plugin {
id: string;
name: string;
type: PluginType;
packageName: string;
iconLocation?: string;
uiComponent: "ApiEditorForm" | "RapidApiEditorForm" | "DbEditorForm";
datasourceComponent: "RestAPIDatasourceForm" | "AutoForm";
allowUserDatasources?: boolean;
templates: Record<string, string>;
responseType?: "TABLE" | "JSON";
documentationLink?: string;
generateCRUDPageComponent?: string;
}
export interface PluginFormPayload {
form: any[];
editor: any[];
setting: any[];
dependencies: DependencyMap;
}
class PluginsApi extends Api {
static url = "v1/plugins";
static fetchPlugins(
orgId: string,
): AxiosPromise<GenericApiResponse<Plugin[]>> {
return Api.get(PluginsApi.url, { organizationId: orgId });
}
static fetchFormConfig(
id: string,
): AxiosPromise<GenericApiResponse<PluginFormPayload>> {
return Api.get(PluginsApi.url + `/${id}/form`);
}
}
export default PluginsApi;

View File

@ -0,0 +1,126 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
import {
Providers,
ProviderTemplates,
SearchResultsProviders,
ProvidersDataArray,
} from "constants/providerConstants";
export interface FetchProvidersResponse extends ApiResponse {
data: Providers;
}
export interface FetchProviderDetailsResponse extends ApiResponse {
data: ProvidersDataArray;
}
export interface FetchProviderCategoriesResponse extends ApiResponse {
data: string[];
}
export interface FetchProviderTemplateResponse extends ApiResponse {
data: ProviderTemplates[];
}
export interface SearchApiOrProviderResponse extends ApiResponse {
data: {
providers: SearchResultsProviders[];
};
}
export interface FetchProviderTemplatesRequest {
providerId: string;
}
export interface FetchProviderDetailsByProviderIdRequest {
providerId: string;
}
export interface FetchProviderWithCategoryRequest {
category: string;
page: number;
}
export interface SearchApiOrProviderRequest {
searchKey: string;
}
export interface AddApiToPageRequest {
name: string;
pageId: string;
marketplaceElement: any;
organizationId?: string;
// Added for analytics
source?: string;
}
export class ProvidersApi extends Api {
static providersURL = "v1/providers";
static providerCategoriesURL = "v1/providers/categories";
static providerDetailsByIdURL = (providerId: string) => {
return `v1/marketplace/providers/${providerId}`;
};
static providerTemplateURL = (providerId: string) => {
return `v1/marketplace/templates?providerId=${providerId}`;
};
static searchApiOrProviderUrl = (searchKey: string) => {
return `v1/marketplace/search?searchKey=${searchKey}`;
};
static providersWithCategoryURL = (category: string, page: number) => {
return `v1/marketplace/providers?category=${category}&page=${page}&size=50`;
};
static addApiToPageURL = `v1/items/addToPage`;
static fetchProviders(): AxiosPromise<FetchProvidersResponse> {
return Api.get(ProvidersApi.providersURL);
}
static fetchProviderTemplates(
request: FetchProviderTemplatesRequest,
): AxiosPromise<FetchProviderTemplateResponse> {
const { providerId } = request;
return Api.get(ProvidersApi.providerTemplateURL(providerId));
}
static seachApiOrProvider(
request: SearchApiOrProviderRequest,
): AxiosPromise<SearchApiOrProviderResponse> {
const { searchKey } = request;
return Api.get(ProvidersApi.searchApiOrProviderUrl(searchKey));
}
static addApiToPage(request: AddApiToPageRequest): AxiosPromise<ApiResponse> {
return Api.post(ProvidersApi.addApiToPageURL, request);
}
static fetchProvidersCategories(): AxiosPromise<
FetchProviderCategoriesResponse
> {
return Api.get(ProvidersApi.providerCategoriesURL);
}
static fetchProvidersWithCategory(
request: FetchProviderWithCategoryRequest,
): AxiosPromise<FetchProvidersResponse> {
const { page } = request;
return Api.get(
ProvidersApi.providersWithCategoryURL(request.category, page),
);
}
static fetchProviderDetailsByProviderId(
request: FetchProviderDetailsByProviderIdRequest,
): AxiosPromise<FetchProviderDetailsResponse> {
const { providerId } = request;
return Api.get(ProvidersApi.providerDetailsByIdURL(providerId));
}
}
export default ProvidersApi;

View File

@ -0,0 +1,13 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
class ReleasesAPI extends Api {
static markAsReadURL = `v1/users/setReleaseNotesViewed`;
static markAsRead(): AxiosPromise<ApiResponse> {
return Api.put(ReleasesAPI.markAsReadURL);
}
}
export default ReleasesAPI;

View File

@ -0,0 +1,25 @@
import Api from "./Api";
import { AxiosPromise } from "taro-axios";
import { GenericApiResponse } from "api/ApiResponses";
import { Datasource } from "entities/Datasource";
class SaasApi extends Api {
static url = "v1/saas";
static getAppsmithToken(
datasourceId: string,
pageId: string,
): AxiosPromise<GenericApiResponse<string>> {
return Api.post(`${SaasApi.url}/${datasourceId}/pages/${pageId}/oauth`);
}
static getAccessToken(
datasourceId: string,
token: string,
): AxiosPromise<GenericApiResponse<Datasource>> {
return Api.post(
`${SaasApi.url}/${datasourceId}/token?appsmithToken=${token}`,
);
}
}
export default SaasApi;

View File

@ -0,0 +1,155 @@
import { AxiosPromise } from "taro-axios";
import Api from "api/Api";
import { ApiResponse } from "./ApiResponses";
export interface LoginUserRequest {
email: string;
password: string;
}
export interface CreateUserRequest {
email: string;
password: string;
}
export interface CreateUserResponse extends ApiResponse {
email: string;
id: string;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface TokenPasswordUpdateRequest {
token: string;
password: string;
email: string;
}
export interface VerifyTokenRequest {
email: string;
token: string;
}
export interface FetchUserResponse extends ApiResponse {
id: string;
}
export interface FetchUserRequest {
id: string;
}
export interface LeaveOrgRequest {
orgId: string;
}
export interface InviteUserRequest {
email: string;
groupIds: string[];
status?: string;
}
export interface UpdateUserRequest {
name?: string;
email?: string;
}
class UserApi extends Api {
static usersURL = "v1/users";
static forgotPasswordURL = `${UserApi.usersURL}/forgotPassword`;
static verifyResetPasswordTokenURL = `${UserApi.usersURL}/verifyPasswordResetToken`;
static resetPasswordURL = `${UserApi.usersURL}/resetPassword`;
static inviteUserURL = "v1/users/invite";
static verifyInviteTokenURL = `${UserApi.inviteUserURL}/verify`;
static confirmUserInviteURL = `${UserApi.inviteUserURL}/confirm`;
static addOrgURL = `${UserApi.usersURL}/addOrganization`;
static leaveOrgURL = `${UserApi.usersURL}/leaveOrganization`;
static logoutURL = "v1/logout";
static currentUserURL = "v1/users/me";
static photoURL = "v1/users/photo";
static featureFlagsURL = "v1/users/features";
static createUser(
request: CreateUserRequest,
): AxiosPromise<CreateUserResponse> {
return Api.post(UserApi.usersURL, request);
}
static updateUser(request: UpdateUserRequest): AxiosPromise<ApiResponse> {
return Api.put(UserApi.usersURL, request);
}
static fetchUser(request: FetchUserRequest): AxiosPromise<FetchUserResponse> {
return Api.get(UserApi.usersURL + "/" + request.id);
}
static getCurrentUser(): AxiosPromise<ApiResponse> {
return Api.get(UserApi.currentUserURL);
}
static forgotPassword(
request: ForgotPasswordRequest,
): AxiosPromise<ApiResponse> {
return Api.post(UserApi.forgotPasswordURL, request);
}
static verifyResetPasswordToken(
request: VerifyTokenRequest,
): AxiosPromise<ApiResponse> {
return Api.get(UserApi.verifyResetPasswordTokenURL, request);
}
static resetPassword(
request: TokenPasswordUpdateRequest,
): AxiosPromise<ApiResponse> {
return Api.put(UserApi.resetPasswordURL, request);
}
static inviteUser(request: InviteUserRequest): AxiosPromise<ApiResponse> {
return Api.post(UserApi.inviteUserURL, request);
}
static verifyUserInvite(
request: VerifyTokenRequest,
): AxiosPromise<ApiResponse> {
return Api.get(UserApi.verifyInviteTokenURL, request);
}
static confirmInvitedUserSignup(
request: TokenPasswordUpdateRequest,
): AxiosPromise<ApiResponse> {
return Api.put(UserApi.confirmUserInviteURL, request);
}
static logoutUser(): AxiosPromise<ApiResponse> {
return Api.post(UserApi.logoutURL);
}
static uploadPhoto(request: { file: File }): AxiosPromise<ApiResponse> {
const formData = new FormData();
if (request.file) {
formData.append("file", request.file);
}
return Api.post(UserApi.photoURL, formData, null, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
static deletePhoto(): AxiosPromise<ApiResponse> {
return Api.delete(UserApi.photoURL);
}
static leaveOrg(request: LeaveOrgRequest): AxiosPromise<LeaveOrgRequest> {
return Api.put(UserApi.leaveOrgURL + "/" + request.orgId);
}
static fetchFeatureFlags(): AxiosPromise<ApiResponse> {
return Api.get(UserApi.featureFlagsURL);
}
}
export default UserApi;

View File

@ -0,0 +1,18 @@
import Api from "api/Api";
import { WidgetType } from "constants/WidgetConstants";
import { WidgetProps } from "widgets/BaseWidget";
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
import { AxiosPromise } from "taro-axios";
export interface WidgetConfigsResponse {
config: Record<WidgetType, Partial<WidgetProps> & WidgetConfigProps>;
}
class WidgetConfigsApi extends Api {
static url = "/widgetConfigs";
static fetchWidgetConfigs(): AxiosPromise<WidgetConfigsResponse> {
return Api.get(WidgetConfigsApi.url);
}
}
export default WidgetConfigsApi;

View File

@ -0,0 +1,17 @@
import Api from "api/Api";
import { WidgetCardProps } from "widgets/BaseWidget";
import { AxiosPromise } from "taro-axios";
export interface WidgetSidebarResponse {
cards: { [id: string]: WidgetCardProps[] };
}
// export interface WidgetCardsPaneRequest {}
class WidgetSidebarApi extends Api {
static url = "/widgetCards";
static fetchWidgetCards(): AxiosPromise<WidgetSidebarResponse> {
return Api.get(WidgetSidebarApi.url);
}
}
export default WidgetSidebarApi;

View File

@ -0,0 +1,14 @@
export default {
pages: [
'pages/index/index',
'pages/page1/index',
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
},
workers: 'worker',
"lazyCodeLoading": "requiredComponents",
}

17
app/taro/src/app.less Normal file
View File

@ -0,0 +1,17 @@
.h5-span {
display: inline;
}
view {
box-sizing: border-box;
line-height: 1.6;
}
.rich-p {
margin: 0;
font-size: 0;
}
.rich-img {
width: 100%;
}

25
app/taro/src/app.tsx Normal file
View File

@ -0,0 +1,25 @@
import { Component } from "react";
import { appInitializer } from "utils/AppsmithUtils";
import "./app.less";
// init
appInitializer();
class App extends Component {
componentDidMount() {}
componentDidShow() {}
componentDidHide() {}
componentDidCatchError() {}
// 在 App 类中的 render() 函数没有实际作用
// 请勿修改此函数
render() {
return this.props.children;
}
}
export default App;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="34" fill="none" viewBox="0 0 34 34"><circle cx="17" cy="17" r="17" fill="#C4C4C4"/></svg>

After

Width:  |  Height:  |  Size: 148 B

View File

@ -0,0 +1,35 @@
import React, { useContext } from "react";
import { styled } from "linaria/react";
import { WidgetProps } from "widgets/BaseWidget";
import { RenderModes } from "constants/WidgetConstants";
import WidgetFactory from "utils/WidgetFactory";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout";
import ReduxContext from "./ReduxContext";
const PageView = styled.div<{ width: number }>`
height: 100%;
position: relative;
width: ${(props) => props.width}px;
margin: 0 auto;
`;
type AppPageProps = {
dsl: ContainerWidgetProps<WidgetProps>;
pageName?: string;
appName?: string;
};
export function AppPage(props: AppPageProps) {
const { useDispatch, useSelector, context } = useContext(ReduxContext);
const dispatch = useDispatch();
useDynamicAppLayout(dispatch, useSelector);
return (
<PageView width={props.dsl.rightColumn}>
{props.dsl.widgetId &&
WidgetFactory.createWidget({ ...props.dsl, context }, RenderModes.PAGE)}
</PageView>
);
}
export default AppPage;

View File

@ -0,0 +1,113 @@
import { useEffect, useContext } from "react";
import { styled } from "linaria/react";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { getIsInitialized } from "selectors/appViewSelectors";
import { executeAction } from "actions/widgetActions";
import { ExecuteActionPayload } from "constants/AppsmithActionConstants/ActionConstants";
import {
resetChildrenMetaProperty,
updateWidgetMetaProperty,
} from "actions/metaActions";
import { getShowTabBar } from "selectors/editorSelectors";
import { EditorContext } from "components/editorComponents/EditorContextProvider";
import AppViewerPageContainer from "./AppViewerPageContainer";
import { ConfigProvider } from "@taroify/core";
import { taroifyTheme } from "constants/DefaultTheme";
import TabBar from "components/designSystems/taro/TabBar";
import Taro from "@tarojs/taro";
import ReduxContext from "./ReduxContext";
import { editorInitializer } from "utils/EditorUtils";
const AppViewerBody = styled.section<{
showTabBar: boolean;
}>`
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: flex-start;
height: calc(
100vh -
${(props) =>
props.showTabBar ? "100rpx - constant(safe-area-inset-bottom)" : "0px"}
);
height: calc(
100vh -
${(props) =>
props.showTabBar ? "100rpx - env(safe-area-inset-bottom)" : "0px"}
);
`;
const ContainerWrapper = styled.div`
display: flex;
width: 100%;
height: 100%;
transform: translate(0, 0);
`;
const AppViewerBodyContainer = styled.div`
flex: 1;
overflow: auto;
margin: 0 auto;
`;
const LATEST_SCENE_APP_ID = "PAGEPLUG_LATEST_SCENE_APP_ID";
const AppViewer = () => {
const { useDispatch, useSelector } = useContext(ReduxContext);
const dispatch = useDispatch();
const initializeAppViewer = (applicationId: string, pageId?: string) => {
dispatch({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
payload: { applicationId, pageId },
});
};
const isInitialized = useSelector(getIsInitialized);
const showTabBar = useSelector(getShowTabBar);
useEffect(() => {
const { applicationId, pageId, scene } =
Taro.getCurrentInstance().router?.params || {};
editorInitializer();
if (scene) {
Taro.setStorageSync(LATEST_SCENE_APP_ID, scene);
initializeAppViewer(scene);
} else if (applicationId) {
initializeAppViewer(applicationId, pageId);
} else {
const lastScene = Taro.getStorageSync(LATEST_SCENE_APP_ID);
initializeAppViewer(lastScene || DEFAULT_APP);
}
}, [dispatch]);
return (
<EditorContext.Provider
value={{
executeAction: (actionPayload: ExecuteActionPayload) =>
dispatch(executeAction(actionPayload)),
updateWidgetMetaProperty: (
widgetId: string,
propertyName: string,
propertyValue: any
) =>
dispatch(
updateWidgetMetaProperty(widgetId, propertyName, propertyValue)
),
resetChildrenMetaProperty: (widgetId: string) =>
dispatch(resetChildrenMetaProperty(widgetId)),
}}
>
<ConfigProvider theme={taroifyTheme}>
<ContainerWrapper>
<AppViewerBodyContainer>
<AppViewerBody showTabBar={showTabBar}>
{isInitialized && <AppViewerPageContainer />}
</AppViewerBody>
</AppViewerBodyContainer>
</ContainerWrapper>
<TabBar />
</ConfigProvider>
</EditorContext.Provider>
);
};
export default AppViewer;

View File

@ -0,0 +1,85 @@
import React, { useContext } from "react";
import { getIsFetchingPage } from "selectors/appViewSelectors";
import { styled } from "linaria/react";
import { Loading, SafeArea } from "@taroify/core";
import AppPage from "./AppPage";
import {
getCanvasWidgetDsl,
getCurrentPageName,
} from "selectors/editorSelectors";
import { getCurrentApplication } from "selectors/applicationSelectors";
import ReduxContext from "./ReduxContext";
const LoadingContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100%;
.taroify-loading {
color: var(--primary-color);
.taroify-loading__text {
color: var(--primary-color);
}
}
`;
const Section = styled.section`
background: #f6f6f6;
height: max-content;
min-height: 100%;
margin: 0 auto;
position: relative;
overflow-x: auto;
overflow-y: auto;
`;
const SafeFixedArea = styled.div<{
height: number;
}>`
margin-bottom: ${(props) => props.height}px;
`;
const AppViewerPageContainer: any = () => {
const { useSelector } = useContext(ReduxContext);
const currentApp = useSelector(getCurrentApplication);
const widgets = useSelector(getCanvasWidgetDsl);
const isFetchingPage = useSelector(getIsFetchingPage);
const currentPageName = useSelector(getCurrentPageName);
const currentAppName = currentApp?.name;
const hasFixedWidget = widgets.children?.find(
(w) => w.type === "TARO_BOTTOM_BAR_WIDGET"
);
const pageNotFound = <div>😅</div>;
if (isFetchingPage) {
return (
<LoadingContainer>
<Loading size="48px" />
</LoadingContainer>
);
} else if (
!isFetchingPage &&
!(widgets && widgets.children && widgets.children.length > 0)
) {
return pageNotFound;
} else if (!isFetchingPage && widgets) {
return (
<Section>
<AppPage
appName={currentAppName}
dsl={widgets}
pageName={currentPageName}
/>
{hasFixedWidget ? (
<SafeFixedArea height={hasFixedWidget?.height || 0} />
) : null}
<SafeArea position="bottom" />
</Section>
);
}
};
export default AppViewerPageContainer;

View File

@ -0,0 +1,33 @@
import React from "react";
import {
Provider,
createStoreHook,
createDispatchHook,
createSelectorHook,
} from "react-redux";
import { newStore } from "store";
import AppViewer from "./AppViewer";
import ReduxContext from "./ReduxContext";
const Page = () => {
const context: any = React.createContext(null);
const useStore = createStoreHook(context);
const useDispatch = createDispatchHook(context);
const useSelector = createSelectorHook(context);
const store = newStore();
const contextValue = {
useStore,
useSelector,
useDispatch,
context,
};
return (
<ReduxContext.Provider value={contextValue}>
<Provider store={store} context={context}>
<AppViewer />
</Provider>
</ReduxContext.Provider>
);
};
export default Page;

View File

@ -0,0 +1,15 @@
import React from "react";
const noop = () => {};
const ReduxContext = React.createContext<{
useStore: any;
useSelector: any;
useDispatch: any;
context: any;
}>({
useStore: noop,
useSelector: noop,
useDispatch: noop,
context: null,
});
export default ReduxContext;

View File

@ -0,0 +1,17 @@
import { Component } from "react";
import { Color } from "constants/Colors";
/***
* Components are responsible for binding render inputs to corresponding UI SDKs
*/
abstract class BaseComponent<T extends ComponentProps> extends Component<T> {}
export interface ComponentProps {
widgetId: string;
widgetName?: string;
isDisabled?: boolean;
isVisible?: boolean;
backgroundColor?: Color;
}
export default BaseComponent;

View File

@ -0,0 +1,47 @@
import React, { ReactNode, useRef, RefObject } from "react";
import { styled } from "linaria/react";
import { ComponentProps } from "./BaseComponent";
import { Color } from "constants/Colors";
const StyledContainerComponent: any = styled.div`
height: 100%;
width: 100%;
background: ${(props) => props.backgroundColor || "unset"};
opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")};
position: relative;
box-shadow: ${(props) =>
props.selected ? "0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"};
z-index: ${(props) => (props.focused ? "3" : props.selected ? "2" : "1")};
`;
function ContainerComponent(props: ContainerComponentProps) {
const containerStyle = props.containerStyle || "card";
const containerRef: RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
return (
<StyledContainerComponent
{...props}
containerStyle={containerStyle}
// Before you remove: generateClassName is used for bounding the resizables within this canvas
// getCanvasClassName is used to add a scrollable parent.
ref={containerRef}
>
{props.children}
</StyledContainerComponent>
);
}
export type ContainerStyle = "border" | "card" | "rounded-border" | "none";
export interface ContainerComponentProps extends ComponentProps {
containerStyle?: ContainerStyle;
children?: ReactNode;
className?: string;
backgroundColor?: Color;
shouldScrollContents?: boolean;
resizeDisabled?: boolean;
selected?: boolean;
focused?: boolean;
minHeight?: number;
}
export default ContainerComponent;

View File

@ -0,0 +1,75 @@
import React, { CSSProperties, ReactNode, useMemo } from "react";
import { BaseStyle } from "widgets/BaseWidget";
import { WIDGET_PADDING } from "constants/WidgetConstants";
import { generateClassName } from "utils/generators";
import { styled } from 'linaria/react';
import { Layers } from "constants/Layers";
const PositionedWidget = styled.div`
&:hover {
z-index: 2 !important;
}
`;
type PositionedContainerProps = {
style: BaseStyle;
children: ReactNode;
widgetId: string;
widgetType: string;
selected?: boolean;
focused?: boolean;
resizeDisabled?: boolean;
};
export function PositionedContainer(props: PositionedContainerProps) {
const x = props.style.xPosition + (props.style.xPositionUnit || "px");
const y = props.style.yPosition + (props.style.yPositionUnit || "px");
const padding = WIDGET_PADDING;
// memoized classname
const containerClassName = useMemo(() => {
return (
generateClassName(props.widgetId) +
" positioned-widget " +
`t--widget-${props.widgetType
.split("_")
.join("")
.toLowerCase()}`
);
}, [props.widgetType, props.widgetId]);
const containerStyle: CSSProperties = useMemo(() => {
return {
position: "absolute",
left: x,
top: y,
height: props.style.componentHeight + (props.style.heightUnit || "px"),
width: props.style.componentWidth + (props.style.widthUnit || "px"),
padding: padding + "px",
zIndex:
props.selected || props.focused
? Layers.selectedWidget
: Layers.positionedWidget,
backgroundColor: "inherit",
};
}, [props.style]);
const stopEventPropagation = (e: any) => {
e.stopPropagation();
};
return (
<PositionedWidget
className={containerClassName}
data-testid="test-widget"
id={props.widgetId}
key={`positioned-container-${props.widgetId}`}
onClick={stopEventPropagation}
//Before you remove: This is used by property pane to reference the element
style={containerStyle}
>
{props.children}
</PositionedWidget>
);
}
PositionedContainer.padding = WIDGET_PADDING;
export default PositionedContainer;

View File

@ -0,0 +1,225 @@
import { isString } from "lodash";
import dayjs from "dayjs";
import { TextSize } from "constants/WidgetConstants";
export type TableSizes = {
COLUMN_HEADER_HEIGHT: number;
TABLE_HEADER_HEIGHT: number;
ROW_HEIGHT: number;
ROW_FONT_SIZE: number;
};
export enum CompactModeTypes {
SHORT = "SHORT",
DEFAULT = "DEFAULT",
TALL = "TALL",
}
export enum CellAlignmentTypes {
LEFT = "LEFT",
RIGHT = "RIGHT",
CENTER = "CENTER",
}
export enum VerticalAlignmentTypes {
TOP = "TOP",
BOTTOM = "BOTTOM",
CENTER = "CENTER",
}
export const TABLE_SIZES: { [key: string]: TableSizes } = {
[CompactModeTypes.DEFAULT]: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 40,
ROW_FONT_SIZE: 14,
},
[CompactModeTypes.SHORT]: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 20,
ROW_FONT_SIZE: 12,
},
[CompactModeTypes.TALL]: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 60,
ROW_FONT_SIZE: 18,
},
};
export enum ColumnTypes {
DATE = "date",
VIDEO = "video",
IMAGE = "image",
TEXT = "text",
NUMBER = "number",
URL = "url",
}
export enum OperatorTypes {
OR = "OR",
AND = "AND",
}
export interface TableStyles {
cellBackground?: string;
textColor?: string;
textSize?: TextSize;
fontStyle?: string;
horizontalAlignment?: CellAlignment;
verticalAlignment?: VerticalAlignment;
}
export type CompactMode = keyof typeof CompactModeTypes;
export type Condition = keyof typeof ConditionFunctions | "";
export type Operator = keyof typeof OperatorTypes;
export type CellAlignment = keyof typeof CellAlignmentTypes;
export type VerticalAlignment = keyof typeof VerticalAlignmentTypes;
export interface ReactTableFilter {
column: string;
operator: Operator;
condition: Condition;
value: any;
}
export interface CellLayoutProperties {
horizontalAlignment?: CellAlignment;
verticalAlignment?: VerticalAlignment;
textSize?: TextSize;
fontStyle?: string;
textColor?: string;
cellBackground?: string;
buttonStyle?: string;
buttonLabelColor?: string;
buttonLabel?: string;
displayText?: string;
}
export interface TableColumnMetaProps {
isHidden: boolean;
format?: string;
inputFormat?: string;
type: string;
}
export interface TableColumnProps {
Header: string;
accessor: string;
width?: number;
minWidth: number;
draggable: boolean;
isHidden?: boolean;
isAscOrder?: boolean;
metaProperties?: TableColumnMetaProps;
isDerived?: boolean;
columnProperties: ColumnProperties;
}
export interface ReactTableColumnProps extends TableColumnProps {
Cell: (props: any) => JSX.Element;
}
export interface ColumnProperties {
id: string;
label: string;
columnType: string;
isVisible: boolean;
index: number;
width: number;
cellBackground?: string;
horizontalAlignment?: CellAlignment;
verticalAlignment?: VerticalAlignment;
textSize?: TextSize;
fontStyle?: string;
textColor?: string;
enableFilter?: boolean;
enableSort?: boolean;
isDerived: boolean;
computedValue: string;
buttonLabel?: string;
buttonStyle?: string;
buttonLabelColor?: string;
onClick?: string;
outputFormat?: string;
inputFormat?: string;
dropdownOptions?: string;
onOptionChange?: string;
displayText?: string;
}
export const ConditionFunctions: {
[key: string]: (a: any, b: any) => boolean;
} = {
isExactly: (a: any, b: any) => {
return a.toString() === b.toString();
},
empty: (a: any) => {
return a === "" || a === undefined || a === null;
},
notEmpty: (a: any) => {
return a !== "" && a !== undefined && a !== null;
},
notEqualTo: (a: any, b: any) => {
return a.toString() !== b.toString();
},
isEqualTo: (a: any, b: any) => {
return a.toString() === b.toString();
},
lessThan: (a: any, b: any) => {
const numericB = Number(b);
const numericA = Number(a);
return numericA < numericB;
},
lessThanEqualTo: (a: any, b: any) => {
const numericB = Number(b);
const numericA = Number(a);
return numericA <= numericB;
},
greaterThan: (a: any, b: any) => {
const numericB = Number(b);
const numericA = Number(a);
return numericA > numericB;
},
greaterThanEqualTo: (a: any, b: any) => {
const numericB = Number(b);
const numericA = Number(a);
return numericA >= numericB;
},
contains: (a: any, b: any) => {
if (isString(a) && isString(b)) {
return a.includes(b);
}
return false;
},
doesNotContain: (a: any, b: any) => {
if (isString(a) && isString(b)) {
return !a.includes(b);
}
return false;
},
startsWith: (a: any, b: any) => {
if (isString(a) && isString(b)) {
return a.indexOf(b) === 0;
}
return false;
},
endsWith: (a: any, b: any) => {
if (isString(a) && isString(b)) {
return a.length === a.lastIndexOf(b) + b.length;
}
return false;
},
is: (a: any, b: any) => {
return dayjs(a).isSame(dayjs(b), "day");
},
isNot: (a: any, b: any) => {
return !dayjs(a).isSame(dayjs(b), "day");
},
isAfter: (a: any, b: any) => {
return !dayjs(a).isAfter(dayjs(b), "day");
},
isBefore: (a: any, b: any) => {
return !dayjs(a).isBefore(dayjs(b), "day");
},
};

View File

@ -0,0 +1,50 @@
import { uniq, without } from "lodash";
import { ColumnProperties } from "./Constants";
export const removeSpecialChars = (value: string, limit?: number) => {
const separatorRegex = /\s/;
return value
.split(separatorRegex)
.join("_")
.slice(0, limit || 30);
};
export const getAllTableColumnKeys = (
tableData?: Array<Record<string, unknown>>,
) => {
const columnKeys: string[] = [];
if (tableData) {
for (let i = 0, tableRowCount = tableData.length; i < tableRowCount; i++) {
const row = tableData[i];
for (const key in row) {
// Replace all special characters to _, limit key length to 200 characters.
const sanitizedKey = removeSpecialChars(key, 200);
if (!columnKeys.includes(sanitizedKey)) {
columnKeys.push(sanitizedKey);
}
}
}
}
return columnKeys;
};
export const reorderColumns = (
columns: Record<string, ColumnProperties>,
columnOrder: string[],
) => {
const newColumnsInOrder: Record<string, ColumnProperties> = {};
uniq(columnOrder).forEach((id: string, index: number) => {
if (columns[id]) newColumnsInOrder[id] = { ...columns[id], index };
});
const remaining = without(
Object.keys(columns),
...Object.keys(newColumnsInOrder),
);
const len = Object.keys(newColumnsInOrder).length;
if (remaining && remaining.length > 0) {
remaining.forEach((id: string, index: number) => {
newColumnsInOrder[id] = { ...columns[id], index: len + index };
});
}
return newColumnsInOrder;
};

View File

@ -0,0 +1,37 @@
import React, { useContext, useMemo } from "react";
import clsx from "clsx";
import { ActionBarProps } from "./PropsType";
import ActionBarContext from "./ActionBarContext";
import { BEM, createBEM } from "../SkuComponent/bem";
const ActionBar: React.FC<ActionBarProps> = (props: any) => {
const bem = createBEM("rv-action-bar");
const children = useMemo(() => React.Children.toArray(props.children), [
props.children,
]);
return (
<ActionBarContext.Provider value={{ parent: { children } }}>
<div
className={clsx(props.className, bem(), {
"rv-safe-area-bottom": props.safeAreaInsetBottom,
})}
style={props.style}
>
{React.Children.toArray(props.children)
.filter(Boolean)
.map((child: any, index: number) =>
React.cloneElement(child, {
index,
}),
)}
</div>
</ActionBarContext.Provider>
);
};
ActionBar.defaultProps = {
safeAreaInsetBottom: true,
};
export default ActionBar;

View File

@ -0,0 +1,63 @@
import React, { useContext, useMemo } from "react";
import clsx from "clsx";
import { ActionBarButtonProps } from "./PropsType";
import { Button } from "@taroify/core";
import ActionBarContext from "./ActionBarContext";
import { BEM, createBEM } from "../SkuComponent/bem";
const ActionBarButton: React.FC<ActionBarButtonProps> = (props) => {
const { type, icon, text, color, loading, disabled, index } = props;
const bem = createBEM("rv-action-bar-button");
const { parent } = useContext(ActionBarContext);
const isFirst = useMemo(() => {
if (parent && typeof index !== "undefined") {
const prev = parent.children[index - 1];
return !(prev && "isButton" in prev.type);
}
return false;
}, [index, parent]);
const isLast = useMemo(() => {
if (parent && typeof index !== "undefined") {
const next = parent.children[index + 1];
return !(next && "isButton" in next.type);
}
return false;
}, [index, parent]);
const style = {
"--rv-action-bar-theme-color": color || "unset",
...props.style,
};
return (
<Button
className={clsx(
props.className,
bem([
type,
{
last: isLast,
first: isFirst,
},
]),
)}
style={style}
size="large"
icon={icon}
color={type}
loading={loading}
disabled={disabled}
onClick={props.onClick}
>
{props.children ? props.children : text}
</Button>
);
};
const ActionBarButtonNameSpace = Object.assign(ActionBarButton, {
isButton: true,
});
export default ActionBarButtonNameSpace;

View File

@ -0,0 +1,9 @@
import { createContext, Context } from "react";
export interface ActionBarState {
parent?: Record<string, any>;
}
const ActionButtonContext: Context<ActionBarState> = createContext({});
export default ActionButtonContext;

View File

@ -0,0 +1,38 @@
import React, { isValidElement } from "react";
import clsx from "clsx";
import { ActionBarIconProps } from "./PropsType";
import { createVanIconComponent } from "@taroify/icons/van";
import { Badge } from "@taroify/core";
import { BEM, createBEM } from "../SkuComponent/bem";
const ActionBarIcon: React.FC<ActionBarIconProps> = (props) => {
const bem = createBEM("rv-action-bar-icon");
const renderIcon = () => {
const { badge, icon, color } = props;
let iconContent = icon;
if (typeof icon === "string") {
const Icon = createVanIconComponent(icon);
iconContent = <Icon color={color} size={20} />;
}
if (isValidElement(iconContent)) {
return <Badge {...badge}>{iconContent}</Badge>;
}
return null;
};
return (
<div
role="button"
className={clsx(props.className, bem())}
style={props.style}
tabIndex={0}
onClick={props.onClick}
>
{renderIcon()}
{props.children || props.text}
</div>
);
};
export default ActionBarIcon;

View File

@ -0,0 +1,45 @@
import React, { CSSProperties } from "react";
export interface BaseTypeProps {
style?: CSSProperties | any;
className?: string;
}
export interface ActionBarProps extends BaseTypeProps {
/** 是否开启底部安全区适配 */
safeAreaInsetBottom?: boolean;
}
export interface ActionBarIconProps extends BaseTypeProps {
/** 按钮文字 */
text?: React.ReactNode;
/** 图标 */
icon?: string | React.ReactNode;
/** 图标颜色 */
color?: string;
/** 图标额外类名 */
iconClass?: string;
/** 图标类名前缀,等同于 Icon 组件的 class-prefix 属性 */
iconPrefix?: string;
/** 图标右上角徽标的内容 */
badge?: any;
onClick?: (event: any) => void;
}
export interface ActionBarButtonProps extends BaseTypeProps {
/** 按钮文字 */
text?: React.ReactNode;
/** 按钮类型 */
type?: any;
/** 按钮图标 */
icon?: string | React.ReactNode;
/** 按钮颜色,支持传入 linear-gradient 渐变色 */
color?: string;
/** 是否禁用按钮 */
disabled?: boolean;
/** 是否显示为加载状态 */
loading?: boolean;
onClick?: (event: any) => void;
/** @private */
index?: number;
}

View File

@ -0,0 +1,12 @@
import ActionBar from "./ActionBar";
import ActionBarIcon from "./ActionBarIcon";
import ActionBarButton from "./ActionBarButton";
import "./style/index.less";
const ActionBarNamespace = Object.assign(ActionBar, {
Icon: ActionBarIcon,
Button: ActionBarButton,
});
export { ActionBarNamespace as ActionBar, ActionBarIcon, ActionBarButton };
export default ActionBarNamespace;

View File

@ -0,0 +1,104 @@
@import './var.less';
.@{rv-prefix}-action-bar {
--rv-action-bar-theme-color: @red;
--rv-action-bar-background-color: @action-bar-background-color;
--rv-action-bar-height: @action-bar-height;
--rv-action-bar-button-height: @action-bar-button-height;
--rv-action-bar-icon-width: @action-bar-icon-width;
--rv-action-bar-icon-height: @action-bar-icon-height;
--rv-action-bar-icon-color: @action-bar-icon-color;
--rv-action-bar-icon-size: @action-bar-icon-size;
--rv-action-bar-icon-font-size: @action-bar-icon-font-size;
--rv-action-bar-icon-active-color: @action-bar-icon-active-color;
--rv-action-bar-icon-text-color: @action-bar-icon-text-color;
--rv-action-bar-icon-background-color: @action-bar-icon-background-color;
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
box-sizing: content-box;
height: var(--rv-action-bar-height);
background-color: var(--rv-action-bar-background-color);
}
.@{rv-prefix}-action-bar-button {
flex: 1;
height: var(--rv-action-bar-button-height);
font-weight: @font-weight-bold;
font-size: @font-size-md;
border: none;
border-radius: 0;
&--first {
margin-left: 5 * @hd;
border-top-left-radius: @border-radius-max;
border-bottom-left-radius: @border-radius-max;
}
&--last {
margin-right: 5 * @hd;
border-top-right-radius: @border-radius-max;
border-bottom-right-radius: @border-radius-max;
}
&--warning {
background: transparent;
color: var(--rv-action-bar-theme-color);
&::after {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
content: ' ';
transform: translate(-50%, -50%);
background: currentColor;
opacity: 0.2;
border-color: currentColor;
border-style: inherit;
border-width: inherit;
border-radius: inherit;
}
}
&--danger {
background: var(--rv-action-bar-theme-color);
}
@media (max-width: 321px) {
font-size: 13 * @hd;
}
}
.@{rv-prefix}-action-bar-icon {
display: flex;
flex-direction: column;
justify-content: center;
min-width: var(--rv-action-bar-icon-width);
height: var(--rv-action-bar-icon-height);
color: var(--rv-action-bar-icon-text-color);
font-size: var(--rv-action-bar-icon-font-size);
line-height: 1;
text-align: center;
background-color: var(--rv-action-bar-icon-background-color);
cursor: pointer;
& > .taroify-badge-wrapper {
align-self: center;
}
&:active {
background-color: var(--rv-action-bar-icon-active-color);
}
&__icon {
margin: 0 auto @padding-base;
color: var(--rv-action-bar-icon-color);
font-size: var(--rv-action-bar-icon-size);
}
}

View File

@ -0,0 +1,17 @@
@import '../../rvStyle/var.less';
@action-bar-background-color: transparent;
@action-bar-height: 50 * @hd;
@action-bar-button-height: 40 * @hd;
@action-bar-button-warning-color: @gradient-orange;
@action-bar-button-danger-color: @gradient-red;
@action-bar-icon-width: 48 * @hd;
@action-bar-icon-height: 100%;
@action-bar-icon-color: @text-color;
@action-bar-icon-size: 18 * @hd;
@action-bar-icon-font-size: @font-size-xs;
@action-bar-icon-active-color: @active-color;
@action-bar-icon-text-color: @gray-7;
@action-bar-icon-background-color: transparent;

View File

@ -0,0 +1,63 @@
import React, { ReactNode, useContext } from "react";
import { styled } from "linaria/react";
import { getCanvasClassName } from "utils/generators";
import { Popup } from "@taroify/core";
import { PopupProps } from "@taroify/core/popup/popup";
import { getShowTabBar } from "selectors/editorSelectors";
import ReduxContext from "components/common/ReduxContext";
import { transformDynamicSize } from "utils/AppsmithUtils";
const Container = styled(Popup)<
{
height: number;
showTabBar: boolean;
} & PopupProps
>`
height: calc(
${(props) => transformDynamicSize(props.height)} +
${(props) =>
props.showTabBar ? "0px" : "constant(safe-area-inset-bottom)"}
);
height: calc(
${(props) => transformDynamicSize(props.height)} +
${(props) => (props.showTabBar ? "0px" : "env(safe-area-inset-bottom)")}
);
max-height: 200px;
min-height: 80px;
overflow: visible;
background: #fff;
z-index: 1009;
`;
const Content = styled.div`
width: 100%;
height: 100%;
`;
export type BottomBarComponentProps = {
children: ReactNode;
className?: string;
height?: number;
};
/* eslint-disable react/display-name */
export function BottomBarComponent(props: BottomBarComponentProps) {
const { useSelector } = useContext(ReduxContext);
const showTabBar = useSelector(getShowTabBar);
return (
<Container
defaultOpen
placement="bottom"
height={props.height}
duration={0}
showTabBar={showTabBar}
>
<Popup.Backdrop open={false} closeable={false} />
<Content className={`${getCanvasClassName()} ${props.className}`}>
{props.children}
</Content>
</Container>
);
}
export default BottomBarComponent;

View File

@ -0,0 +1,56 @@
import React from "react";
import { Button } from "@taroify/core";
import { transformDynamicSize } from "utils/AppsmithUtils";
export enum FontWeight {
BOLD = "bold",
NORMAL = "normal",
}
interface ButtonComponentProps {
text?: string;
color?: string;
textColor?: string;
fontSize?: string;
isBold?: boolean;
onClick?: any;
rounded?: boolean;
isDisabled?: boolean;
isLoading?: boolean;
}
const ButtonComponent = ({
text,
color,
textColor,
fontSize,
isBold,
onClick,
rounded,
isDisabled,
isLoading,
}: ButtonComponentProps) => {
const style = {
height: "100%",
backgroundColor: color || "var(--primary-color)",
color: textColor || "#fff",
fontSize: transformDynamicSize(parseInt(fontSize || "20px")),
fontWeight: isBold ? FontWeight.BOLD : undefined,
"--loading-color": textColor || "#fff",
};
const shape = rounded ? "round" : "square";
return (
<Button
disabled={isDisabled}
loading={!!isLoading}
block
style={style}
onClick={onClick}
shape={shape}
>
{text || "好的"}
</Button>
);
};
export default ButtonComponent;

View File

@ -0,0 +1,91 @@
import React from "react";
import { ScrollView, Text } from "@tarojs/components";
import { Cell, Image } from "@taroify/core";
import { Arrow, PhotoFail } from "@taroify/icons";
import { createVanIconComponent } from "@taroify/icons/van";
import _ from "lodash";
export interface CellComponentProps {
cells: Array<{
id: string;
label: string;
widgetId: string;
picType: "none" | "icon" | "image" | "text";
prefix?: string;
icon?: string;
iconColor?: string;
picSrc?: string;
isVisible?: boolean;
showArrow?: boolean;
content?: string;
brief?: string;
onClick?: string;
}>;
title?: string;
inset: boolean;
bordered: boolean;
runAction: (a: string) => void;
}
const CellComponent = (props: CellComponentProps) => {
const { cells, title, inset, bordered, runAction } = props;
const onClickCellItem = (action: string) => (e: any) => {
if (action) {
runAction(action);
}
};
return (
<ScrollView style={{ height: "100%" }} scrollY>
<Cell.Group title={title} inset={inset} bordered={bordered}>
{cells.map((cell, index) => {
let icon: any = null;
if (cell.picType === "image") {
icon = (
<Image
key={cell.picSrc}
src={cell.picSrc}
style={{
width: "32px",
height: "32px",
borderRadius: "4px",
marginRight: "20px",
}}
mode="aspectFit"
fallback={<PhotoFail />}
/>
);
} else if (cell.picType === "icon" && cell.icon) {
const Icon = createVanIconComponent(cell.icon);
icon = <Icon color={cell.iconColor} />;
} else if (cell.picType === "text") {
icon = (
<Text
style={{ color: "#999", fontSize: "16px", marginRight: "20px" }}
>
{cell.prefix}
</Text>
);
}
return (
<Cell
key={index}
clickable
bordered
title={cell.label}
brief={cell.brief}
icon={icon}
rightIcon={cell.showArrow ? <Arrow /> : null}
onClick={onClickCellItem(cell.onClick || "")}
>
{cell.content}
</Cell>
);
})}
</Cell.Group>
</ScrollView>
);
};
export default CellComponent;

View File

@ -0,0 +1,64 @@
import React from "react";
import { styled } from "linaria/react";
import { View } from "@tarojs/components";
import { Image, Button } from "@taroify/core";
import { PhotoFail } from "@taroify/icons";
const EmptyContainer = styled(View)`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.taroify-image {
width: 200px;
height: 200px;
}
`;
const EmptyText = styled(View)`
text-align: center;
color: #666;
font-size: 16px;
`;
const EmptyButton = styled(Button)`
margin-top: 8px;
`;
export interface EmptyProps {
text?: string;
pic?: string;
enableButton?: boolean;
buttonText?: string;
onClick: () => void;
}
const Empty = ({
text,
pic,
enableButton,
buttonText,
onClick,
}: EmptyProps) => {
return (
<EmptyContainer>
<View>
<Image
mode="aspectFit"
fallback={<PhotoFail />}
src={pic || EMPTY_IMAGE_URL}
/>
<EmptyText>{text || "暂无数据"}</EmptyText>
{enableButton ? (
<EmptyButton onClick={onClick} color="primary" block>
{buttonText || "登录"}
</EmptyButton>
) : null}
</View>
</EmptyContainer>
);
};
export default Empty;

View File

@ -0,0 +1,113 @@
import React, { useEffect, useRef, useState } from "react";
import { ArrowRight } from "@taroify/icons";
import { Form, AreaPicker, Input, Popup } from "@taroify/core";
import { FormItemInstance } from "@taroify/core/form";
import { areaList } from "@vant/area-data";
export interface FieldProps {
label: string;
name: string;
required: boolean;
placeholder?: string;
}
const provinceList: any = areaList.province_list;
const cityList: any = areaList.city_list;
const countyList: any = areaList.county_list;
const hiddenStyle = {
height: 0,
padding: 0,
};
function PickerField({ label, name, required, placeholder }: FieldProps) {
const itemRef = useRef<FormItemInstance>();
const provinceNameRef = useRef<FormItemInstance>();
const cityNameRef = useRef<FormItemInstance>();
const countyNameRef = useRef<FormItemInstance>();
const [open, setOpen] = useState(false);
const [areaValue, setAreaValue] = useState<string[]>();
const current = itemRef.current?.getValue();
useEffect(() => {
if (current) {
const currentProvince = current.substr(0, 2) + "0000";
const currentCity = current.substr(0, 4) + "00";
setAreaValue([currentProvince, currentCity, current]);
}
}, [current]);
return (
<>
<Form.Item
ref={itemRef}
name={name}
clickable
rightIcon={<ArrowRight />}
rules={[{ required, message: `请选择${label}` }]}
>
<Form.Label>{label}</Form.Label>
<Form.Control>
{(props) => {
let currentLabel = "";
const county = props.value;
if (county?.length === 6) {
const province = county.substr(0, 2) + "0000";
const city = county.substr(0, 4) + "00";
currentLabel = [
provinceList[province],
cityList[city],
countyList[county],
].join("");
}
return (
<Input
readonly
placeholder={placeholder || `请选择${label}`}
onClick={() => setOpen(true)}
value={currentLabel}
/>
);
}}
</Form.Control>
</Form.Item>
<Form.Item name="province" ref={provinceNameRef} style={hiddenStyle}>
<Input readonly />
</Form.Item>
<Form.Item name="city" ref={cityNameRef} style={hiddenStyle}>
<Input readonly />
</Form.Item>
<Form.Item name="county" ref={countyNameRef} style={hiddenStyle}>
<Input readonly />
</Form.Item>
<Popup
style={{ height: "375px" }}
mountOnEnter={false}
open={open}
rounded
placement="bottom"
onClose={setOpen}
>
<AreaPicker
value={areaValue}
onCancel={() => setOpen(false)}
onConfirm={(newValue) => {
itemRef.current?.setValue(newValue[2]);
provinceNameRef.current?.setValue(provinceList[newValue[0]]);
cityNameRef.current?.setValue(cityList[newValue[1]]);
countyNameRef.current?.setValue(countyList[newValue[2]]);
setOpen(false);
}}
>
<AreaPicker.Toolbar>
<AreaPicker.Button></AreaPicker.Button>
<AreaPicker.Button></AreaPicker.Button>
</AreaPicker.Toolbar>
<AreaPicker.Columns>{areaList}</AreaPicker.Columns>
</AreaPicker>
</Popup>
</>
);
}
export default PickerField;

View File

@ -0,0 +1,36 @@
import React from "react";
import { Form, Checkbox } from "@taroify/core";
export interface FieldProps {
label: string;
name: string;
required: boolean;
options: Array<{
label: string;
value: string;
}>;
}
function Field({ label, name, required, options }: FieldProps) {
return (
<Form.Item name={name} rules={[{ required, message: `请选择${label}` }]}>
<Form.Label>{label}</Form.Label>
<Form.Control>
<Checkbox.Group direction="horizontal">
{options?.map((o, index) => (
<Checkbox
name={o.value}
key={index}
shape="square"
style={{ marginBottom: "8px" }}
>
{o.label}
</Checkbox>
))}
</Checkbox.Group>
</Form.Control>
</Form.Item>
);
}
export default Field;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Form, Input } from "@taroify/core";
export interface FieldProps {
label: string;
name: string;
required: boolean;
inputType: "text" | "number" | "idcard" | "digit" | "password";
placeholder?: string;
}
function Field({ label, name, required, inputType, placeholder }: FieldProps) {
const isPassword = inputType === "password";
return (
<Form.Item name={name} rules={[{ required, message: `请填写${label}` }]}>
<Form.Label>{label}</Form.Label>
<Form.Control>
<Input
type={isPassword ? "text" : inputType}
password={isPassword}
placeholder={placeholder || label}
/>
</Form.Control>
</Form.Item>
);
}
export default Field;

View File

@ -0,0 +1,85 @@
import React, { useRef, useState } from "react";
import { ArrowRight } from "@taroify/icons";
import { Form, Picker, Input, Popup } from "@taroify/core";
import { FormItemInstance } from "@taroify/core/form";
import _ from "lodash";
export interface FieldProps {
label: string;
name: string;
required: boolean;
options: Array<{
label: string;
value: string;
}>;
placeholder?: string;
}
function PickerField({
label,
name,
required,
options,
placeholder,
}: FieldProps) {
const itemRef = useRef<FormItemInstance>();
const [open, setOpen] = useState(false);
const current = itemRef.current?.getValue();
let currentLabel: any = undefined;
if (current?.length === 1) {
currentLabel = _.find(options, { value: current[0] })?.label;
}
return (
<>
<Form.Item
ref={itemRef}
name={name}
clickable
rightIcon={<ArrowRight />}
rules={[{ required, message: `请选择${label}` }]}
>
<Form.Label>{label}</Form.Label>
<Form.Control>
<Input
readonly
placeholder={placeholder || `请选择${label}`}
onClick={() => setOpen(true)}
value={currentLabel}
/>
</Form.Control>
</Form.Item>
<Popup
style={{ height: "375px" }}
mountOnEnter={false}
open={open}
rounded
placement="bottom"
onClose={setOpen}
>
<Picker
onCancel={() => setOpen(false)}
onConfirm={(v) => {
itemRef.current?.setValue(v);
setOpen(false);
}}
>
<Picker.Toolbar>
<Picker.Button></Picker.Button>
<Picker.Button></Picker.Button>
</Picker.Toolbar>
<Picker.Column>
{options?.map((o, index) => (
<Picker.Option key={index} value={o.value}>
{o.label}
</Picker.Option>
))}
</Picker.Column>
</Picker>
</Popup>
</>
);
}
export default PickerField;

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