From 47b7d6bcfa7f848df4d0c982edfd91755bf3b567 Mon Sep 17 00:00:00 2001 From: Kamal Qureshi Date: Thu, 24 Jul 2025 20:29:48 +0500 Subject: [PATCH 01/31] Styling update for username in user groups --- .../src/pages/setting/permission/styledComponents.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/pages/setting/permission/styledComponents.tsx b/client/packages/lowcoder/src/pages/setting/permission/styledComponents.tsx index 656e0eed55..b0921887a7 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/styledComponents.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/styledComponents.tsx @@ -123,14 +123,20 @@ export const UserTableCellWrapper = styled.div` svg { margin-left: 8px; + flex-shrink: 0; } > span { - max-width: 120px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; word-break: keep-all; + flex-shrink: 1; + } + + /* Prevent profile image from shrinking */ + > div:first-child { + flex-shrink: 0; } `; From ee2c4f26773e4aeef7773f5161debe219443c5f6 Mon Sep 17 00:00:00 2001 From: Kamal Qureshi Date: Thu, 24 Jul 2025 23:36:23 +0500 Subject: [PATCH 02/31] Updated editable styling for Text comp --- .../comps/tableComp/tableSummaryComp.tsx | 22 ++++++++++++++++++- .../lowcoder/src/comps/comps/textComp.tsx | 7 ++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx index 56e4584c26..e026d5aef4 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx @@ -7,6 +7,24 @@ import Table from "antd/es/table"; import { ReactNode, useMemo, memo, useCallback } from "react"; import Tooltip from "antd/es/tooltip"; +const CellContainer = styled.div<{ + $textAlign?: 'left' | 'center' | 'right'; +}>` + display: flex; + justify-content: ${(props) => { + switch (props.$textAlign) { + case 'left': + return 'flex-start'; + case 'center': + return 'center'; + case 'right': + return 'flex-end'; + default: + return 'flex-start'; + } + }}; +`; + const TableSummaryRow = styled(Table.Summary.Row)<{ $istoolbarPositionBelow: boolean; $background: string; @@ -170,7 +188,9 @@ const TableSummaryCellView = memo(function TableSummaryCellView(props: { $autoHeight={autoHeight} > -
{children}
+ + {children} +
); diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx index dcc5ccdb2b..708a4f4aa3 100644 --- a/client/packages/lowcoder/src/comps/comps/textComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx @@ -37,6 +37,9 @@ const getStyle = (style: TextStyleType) => { text-transform:${style.textTransform} !important; text-decoration:${style.textDecoration} !important; background: ${style.background}; + line-height: ${style.lineHeight}; + margin: ${style.margin} !important; + padding: ${style.padding}; .markdown-body a { color: ${style.links}; } @@ -68,7 +71,7 @@ const getStyle = (style: TextStyleType) => { h6 { color: ${style.text}; font-weight: ${style.textWeight} !important; - line-height:${style.lineHeight}; + line-height: ${style.lineHeight} !important; } img, pre { @@ -101,9 +104,9 @@ const TextContainer = React.memo(styled.div<{ margin: ${props.$styleConfig.margin}; padding: ${props.$styleConfig.padding}; `}; - ${(props) => props.$styleConfig && getStyle(props.$styleConfig)} display: flex; ${markdownCompCss}; + ${(props) => props.$styleConfig && getStyle(props.$styleConfig)} overflow-wrap: anywhere; .markdown-body { overflow-wrap: anywhere; From e0fa0b7914eb85ae5ff9e400c6da71b255ff79c8 Mon Sep 17 00:00:00 2001 From: Kamal Qureshi Date: Fri, 25 Jul 2025 00:06:36 +0500 Subject: [PATCH 03/31] Consisten styles for button when disabled --- .../comps/buttonComp/buttonCompConstants.tsx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx index d838efbcd1..a7c8e48dcd 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx @@ -11,27 +11,28 @@ export function getButtonStyle(buttonStyle: ButtonStyleType, disabledStyle: Disa const hoverColor = buttonStyle.background && genHoverColor(buttonStyle.background); const activeColor = buttonStyle.background && genActiveColor(buttonStyle.background); return css` - & { + &&& { border-radius: ${buttonStyle.radius}; border-width:${buttonStyle.borderWidth}; margin: ${buttonStyle.margin}; padding: ${buttonStyle.padding}; rotate: ${buttonStyle.rotation}; + --antd-wave-shadow-color: ${buttonStyle.border}; + border-color: ${buttonStyle.border}; + color: ${buttonStyle.text}; + font-size: ${buttonStyle.textSize}; + font-weight: ${buttonStyle.textWeight}; + font-family: ${buttonStyle.fontFamily}; + font-style: ${buttonStyle.fontStyle}; + text-transform:${buttonStyle.textTransform}; + text-decoration:${buttonStyle.textDecoration}; + border-radius: ${buttonStyle.radius}; + margin: ${buttonStyle.margin}; + padding: ${buttonStyle.padding}; + &:not(:disabled) { - --antd-wave-shadow-color: ${buttonStyle.border}; - border-color: ${buttonStyle.border}; - color: ${buttonStyle.text}; - font-size: ${buttonStyle.textSize}; - font-weight: ${buttonStyle.textWeight}; - font-family: ${buttonStyle.fontFamily}; - font-style: ${buttonStyle.fontStyle}; - text-transform:${buttonStyle.textTransform}; - text-decoration:${buttonStyle.textDecoration}; background: ${buttonStyle.background}; - border-radius: ${buttonStyle.radius}; - margin: ${buttonStyle.margin}; - padding: ${buttonStyle.padding}; - + &:hover, &:focus { color: ${buttonStyle.text}; @@ -48,14 +49,13 @@ export function getButtonStyle(buttonStyle: ButtonStyleType, disabledStyle: Disa : buttonStyle.border} !important; } } - - /* Disabled state styling */ &:disabled, - &.ant-btn-disabled { - color: ${disabledStyle.disabledText}; - background: ${disabledStyle.disabledBackground}; - border-color: ${disabledStyle.disabledBorder}; - cursor: not-allowed; + &.ant-btn-disabled, + &[disabled] { + background: ${disabledStyle.disabledBackground} !important; + cursor: not-allowed !important; + color: ${disabledStyle.disabledText || buttonStyle.text} !important; + border-color: ${disabledStyle.disabledBorder || buttonStyle.border} !important; } } `; From 4d0d20348dfe094c61c2c8267397945003c40728 Mon Sep 17 00:00:00 2001 From: FARAN Date: Fri, 25 Jul 2025 18:16:50 +0500 Subject: [PATCH 04/31] [Fix]: #1900 workspace create / all orgs --- .../pages/setting/organization/orgList.tsx | 41 ++++++++++++------- .../lowcoder/src/redux/sagas/orgSagas.ts | 9 ++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/client/packages/lowcoder/src/pages/setting/organization/orgList.tsx b/client/packages/lowcoder/src/pages/setting/organization/orgList.tsx index 0e9c8a01c0..78b48b185f 100644 --- a/client/packages/lowcoder/src/pages/setting/organization/orgList.tsx +++ b/client/packages/lowcoder/src/pages/setting/organization/orgList.tsx @@ -173,6 +173,9 @@ type DataItemInfo = { logoUrl: string; createdAt?: number; updatedAt?: number; + isCurrentOrg?: boolean; + isAdmin: boolean; + userRole: string; }; function OrganizationSetting() { @@ -198,21 +201,29 @@ function OrganizationSetting() { - // Filter to only show orgs where user has admin permissions - const adminOrgs = displayWorkspaces.filter((org: Org) => { + // Show all organizations with role information + const allOrgs = displayWorkspaces; + const adminOrgCount = displayWorkspaces.filter((org: Org) => { const role = user.orgRoleMap.get(org.id); return role === ADMIN_ROLE || role === SUPER_ADMIN_ROLE; - }); + }).length; - const dataSource = adminOrgs.map((org: Org) => ({ - id: org.id, - del: adminOrgs.length > 1, - orgName: org.name, - logoUrl: org.logoUrl || "", - createdAt: org.createdAt, - updatedAt: org.updatedAt, - isCurrentOrg: org.isCurrentOrg, - })); + const dataSource = allOrgs.map((org: Org) => { + const userRole = user.orgRoleMap.get(org.id); + const isAdmin = userRole === ADMIN_ROLE || userRole === SUPER_ADMIN_ROLE; + + return { + id: org.id, + del: isAdmin && adminOrgCount > 1, + orgName: org.name, + logoUrl: org.logoUrl || "", + createdAt: org.createdAt, + updatedAt: org.updatedAt, + isCurrentOrg: org.isCurrentOrg, + isAdmin, + userRole, + }; + }); @@ -321,13 +332,15 @@ function OrganizationSetting() { {trans("profile.switchWorkspace")} )} + {item.isAdmin && ( history.push(buildOrgId(item.id))} > - {trans("edit")} - + {trans("edit")} + + )} {item.del && ( { diff --git a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts index e4157abde7..9ac80186f2 100644 --- a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts @@ -266,9 +266,12 @@ export function* createOrgSaga(action: ReduxAction<{ orgName: string }>) { if (isValidResponse) { // update org list yield call(getUserSaga); - yield put({ - type: ReduxActionTypes.CREATE_ORG_SUCCESS, - }); + // Refetch workspaces to update the profile dropdown + yield put(fetchWorkspacesAction(1, 10)); + yield put({ + type: ReduxActionTypes.CREATE_ORG_SUCCESS, + }); + } } catch (error: any) { yield put({ From 48b26197de48198e5d9b7c5cfd0c94df3b46cb3e Mon Sep 17 00:00:00 2001 From: FARAN Date: Fri, 25 Jul 2025 22:06:32 +0500 Subject: [PATCH 05/31] remove min 3 chars search logic from groupUsers --- .../src/pages/setting/permission/addGroupUserDialog.tsx | 2 +- .../src/pages/setting/permission/groupUsersPermission.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx index 46cad16b67..0aa12370c4 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/addGroupUserDialog.tsx @@ -55,7 +55,7 @@ function AddGroupUserDialog(props: { ); useEffect(() => { - if (searchValue.length > 2 || searchValue === "") { + if (searchValue.length > 0 || searchValue === "") { debouncedFetchPotentialMembers(searchValue); } return () => { diff --git a/client/packages/lowcoder/src/pages/setting/permission/groupUsersPermission.tsx b/client/packages/lowcoder/src/pages/setting/permission/groupUsersPermission.tsx index c2d260fe49..b3aa8278ff 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/groupUsersPermission.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/groupUsersPermission.tsx @@ -127,7 +127,7 @@ const GroupUsersPermission: React.FC = (props) => { ); useEffect(() => { - if (searchValue.length > 2 || searchValue === "" || roleFilter) { + if (searchValue.length > 0 || searchValue === "" || roleFilter) { debouncedFetchPotentialMembers(searchValue, roleFilter); } return () => { From f549801da7eedd03b948e29275d23f87407d7937 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Mon, 28 Jul 2025 14:08:33 +0500 Subject: [PATCH 06/31] [Fix]: #1826 refresh folder on press enter --- .../lowcoder/src/pages/ApplicationV2/useCreateFolder.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/useCreateFolder.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/useCreateFolder.tsx index 04c50f22c7..49f827dfbb 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/useCreateFolder.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/useCreateFolder.tsx @@ -50,7 +50,12 @@ export function useCreateFolder(setModify: any, modify: boolean) { onPressEnter={() => { form.validateFields().then(() => { dispatchCreateFolder( - () => modal?.destroy(), + () => { + modal?.destroy(); + setTimeout(() => { + setModify(!modify); + }, 200); + }, () => {} ); }); From fa20d2e59c93a25596fe8687d7bd082dcfb56383 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Mon, 28 Jul 2025 19:08:58 +0500 Subject: [PATCH 07/31] [Fix]: #1862 able to edit the usergroups without navigate --- .../src/pages/setting/permission/permissionList.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/pages/setting/permission/permissionList.tsx b/client/packages/lowcoder/src/pages/setting/permission/permissionList.tsx index c2a5f3778b..add0cdbca7 100644 --- a/client/packages/lowcoder/src/pages/setting/permission/permissionList.tsx +++ b/client/packages/lowcoder/src/pages/setting/permission/permissionList.tsx @@ -187,7 +187,14 @@ export default function PermissionSetting(props: PermissionSettingProps) { scroll={{ x: "100%" }} pagination={false} onRow={(record) => ({ - onClick: () => history.push(buildGroupId((record as DataItemInfo).key)), + onClick: (e) => { + // Don't navigate if this row is in rename mode + if ((record as DataItemInfo).key === needRenameId) { + e.stopPropagation(); + return; + } + history.push(buildGroupId((record as DataItemInfo).key)); + }, })} columns={[ { From f3d08e99f08a97402a931f5a178deac1249be3ad Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Mon, 28 Jul 2025 20:44:35 +0500 Subject: [PATCH 08/31] [Feat]: #1883 add tab-index on buttons/checkbox/select --- .../src/comps/comps/buttonComp/buttonComp.tsx | 12 ++++++++--- .../src/comps/comps/buttonComp/linkComp.tsx | 5 ++++- .../comps/buttonComp/toggleButtonComp.tsx | 5 ++++- .../comps/comps/meetingComp/controlButton.tsx | 7 +++++-- .../comps/selectInputComp/checkboxComp.tsx | 20 ++++++++++++++++++- .../selectInputComp/selectCompConstants.tsx | 5 ++++- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx index a6d6f88890..bd019ab66c 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx @@ -1,4 +1,4 @@ -import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { IconControl } from "comps/controls/iconControl"; @@ -137,7 +137,8 @@ const childrenMap = { disabledStyle: DisabledButtonStyleControl, animationStyle: styleControl(AnimationStyle, 'animationStyle'), viewRef: RefControl, - tooltip: StringControl + tooltip: StringControl, + tabIndex: NumberControl }; type ChildrenType = NewChildren>; @@ -162,8 +163,12 @@ const ButtonPropertyView = React.memo((props: { disabledPropertyView(props.children), hiddenPropertyView(props.children), loadingPropertyView(props.children), + props.children.tabIndex.propertyView({ label: trans("prop.tabIndex") }), ] - : props.children.form.getPropertyView()} + : [ + props.children.form.getPropertyView(), + props.children.tabIndex.propertyView({ label: trans("prop.tabIndex") }), + ]} )} @@ -222,6 +227,7 @@ const ButtonView = React.memo((props: ToViewReturn) => { (!isDefault(props.type) && getForm(editorState, props.form)?.disableSubmit()) } onClick={handleClick} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} > {props.prefixIcon && {props.prefixIcon}} { diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx index 43cb5959a3..b96fe77ea2 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx @@ -1,6 +1,6 @@ import { default as Button } from "antd/es/button"; import { ButtonCompWrapper, buttonRefMethods } from "comps/comps/buttonComp/buttonCompConstants"; -import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { styleControl } from "comps/controls/styleControl"; import { AnimationStyle, AnimationStyleType, LinkStyle, LinkStyleType } from "comps/controls/styleControlConstants"; @@ -91,6 +91,7 @@ const LinkTmpComp = (function () { prefixIcon: IconControl, suffixIcon: IconControl, viewRef: RefControl, + tabIndex: NumberControl, }; return new UICompBuilder(childrenMap, (props) => { // chrome86 bug: button children should not contain only empty span @@ -105,6 +106,7 @@ const LinkTmpComp = (function () { disabled={props.disabled} onClick={() => props.onEvent("click")} type={"link"} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} > {hasChildren && ( @@ -131,6 +133,7 @@ const LinkTmpComp = (function () { {hiddenPropertyView(children)} {loadingPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
{children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })} diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx index 654ec6659d..ce82c9ff51 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx @@ -1,4 +1,4 @@ -import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { withDefault } from "comps/generators"; import { UICompBuilder } from "comps/generators/uiCompBuilder"; import { @@ -68,6 +68,7 @@ const ToggleTmpComp = (function () { showBorder: withDefault(BoolControl, true), viewRef: RefControl, tooltip: StringControl, + tabIndex: NumberControl, }; return new UICompBuilder(childrenMap, (props) => { const text = props.showText @@ -92,6 +93,7 @@ const ToggleTmpComp = (function () { props.onEvent("change"); props.value.onChange(!props.value.value); }} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} > {props.iconPosition === "right" && text} {{props.value.value ? props.trueIcon : props.falseIcon}} @@ -117,6 +119,7 @@ const ToggleTmpComp = (function () { {hiddenPropertyView(children)} {loadingPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
{children.showText.propertyView({ label: trans("toggleButton.showText") })} diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx index 543644b552..c5e7709c97 100644 --- a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx +++ b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx @@ -1,4 +1,4 @@ -import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { IconControl } from "comps/controls/iconControl"; @@ -204,7 +204,8 @@ const childrenMap = { style: ButtonStyleControl, viewRef: RefControl, restrictPaddingOnRotation:withDefault(StringControl, 'controlButton'), - tooltip: StringControl + tooltip: StringControl, + tabIndex: NumberControl }; let ButtonTmpComp = (function () { @@ -294,6 +295,7 @@ let ButtonTmpComp = (function () { ? handleClickEvent() : submitForm(editorState, props.form) } + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} > {props.sourceMode === 'standard' && props.prefixIcon && ( )} diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx index 9a7abb8bd1..6f3cfec54b 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx @@ -214,8 +214,20 @@ let CheckboxBasicComp = (function () { return new UICompBuilder(childrenMap, (props) => { const mountedRef = useRef(true); + const checkboxRef = useRef(null); const [validateState, handleChange] = useSelectInputValidate(props); + useEffect(() => { + if (!mountedRef.current) return; + if (checkboxRef.current && typeof props.tabIndex === 'number') { + const checkboxInputs = checkboxRef.current.querySelectorAll('input[type="checkbox"]'); + checkboxInputs.forEach((input, index) => { + // Set sequential tabindex for each checkbox + input.setAttribute('tabindex', (props.tabIndex + index).toString()); + }); + } + }, [props.tabIndex, props.options]); + useEffect(() => { return () => { mountedRef.current = false; @@ -251,7 +263,13 @@ let CheckboxBasicComp = (function () { layout={props.layout} options={filteredOptions()} onChange={handleValidateChange} - viewRef={props.viewRef} + viewRef={(el) => { + if (!mountedRef.current) return; + if (el) { + props.viewRef(el); + checkboxRef.current = el; + } + }} tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} /> ), diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx index a5d55ca935..a75b9f05ce 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx @@ -6,7 +6,7 @@ import { } from "lowcoder-core"; import { BoolControl } from "../../controls/boolControl"; import { LabelControl } from "../../controls/labelControl"; -import { BoolCodeControl, StringControl } from "../../controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "../../controls/codeControl"; import { PaddingControl } from "../../controls/paddingControl"; import { MarginControl } from "../../controls/marginControl"; import { @@ -242,6 +242,7 @@ export const SelectChildrenMap = { margin: MarginControl, padding: PaddingControl, inputFieldStyle:styleControl(SelectStyle), + tabIndex: NumberControl, ...SelectInputValidationChildren, ...formDataChildren, }; @@ -269,6 +270,7 @@ export const SelectUIView = ( placeholder={props.placeholder} value={props.value} showSearch={props.showSearch} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} filterOption={(input, option) => { if (!option) return false; return String(option.label ?? option.value ?? "").toLowerCase().includes(input.toLowerCase()); @@ -348,6 +350,7 @@ export const SelectPropertyView = ( {disabledPropertyView(children)} {hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children as any)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
)} From 62e83b80b5d2c46b2b9b40b8bda27b07440d1634 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Tue, 29 Jul 2025 15:51:36 +0500 Subject: [PATCH 09/31] fix remote components not working properly in list/grid comps --- .../lowcoder/src/comps/comps/remoteComp/remoteComp.tsx | 2 +- .../lowcoder/src/comps/generators/withMultiContext.tsx | 2 ++ .../src/comps/generators/withSelectedMultiContext.tsx | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx b/client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx index 8399535d7e..173c8c8613 100644 --- a/client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx @@ -45,7 +45,7 @@ function ViewLoading(props: { padding?: number }) { ); } -interface RemoteCompReadyAction { +export interface RemoteCompReadyAction { type: "RemoteCompReady"; comp: Comp; } diff --git a/client/packages/lowcoder/src/comps/generators/withMultiContext.tsx b/client/packages/lowcoder/src/comps/generators/withMultiContext.tsx index ec843eb91f..e934f009ab 100644 --- a/client/packages/lowcoder/src/comps/generators/withMultiContext.tsx +++ b/client/packages/lowcoder/src/comps/generators/withMultiContext.tsx @@ -26,6 +26,7 @@ import { setFieldsNoTypeCheck } from "util/objectUtils"; import { map } from "./map"; import { paramsEqual, withParamsWithDefault } from "./withParams"; import { LazyCompReadyAction } from "../comps/lazyLoadComp/lazyLoadComp"; +import { RemoteCompReadyAction } from "../comps/remoteComp/remoteComp"; export const COMP_KEY = "__comp__"; export const MAP_KEY = "__map__"; @@ -171,6 +172,7 @@ export function withMultiContext(VariantComp && ( !thisCompMap.hasOwnProperty(action.path[1]) || isCustomAction(action, "LazyCompReady") + || isCustomAction(action, "RemoteCompReady") ) ) { /** diff --git a/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx b/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx index 8ba85913b7..d34f0ad76f 100644 --- a/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx +++ b/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx @@ -14,6 +14,7 @@ import { COMP_KEY, MAP_KEY, withMultiContext } from "./withMultiContext"; import { paramsEqual } from "./withParams"; import { LazyCompReadyAction } from "../comps/lazyLoadComp/lazyLoadComp"; import { ModuleReadyAction } from "../comps/moduleComp/moduleComp"; +import { RemoteCompReadyAction } from "../comps/remoteComp/remoteComp"; const SELECTED_KEY = "SELECTED"; @@ -75,6 +76,7 @@ export function withSelectedMultiContext( } else if (( !action.editDSL && !isCustomAction(action, "LazyCompReady") + && !isCustomAction(action, "RemoteCompReady") && !isCustomAction(action, "moduleReady") ) || action.path[0] !== MAP_KEY || _.isNil(action.path[1]) ) { @@ -85,6 +87,7 @@ export function withSelectedMultiContext( } else if (( action.editDSL || isCustomAction(action, "LazyCompReady") + || isCustomAction(action, "RemoteCompReady") || isCustomAction(action, "moduleReady") ) && ( action.path[1] === SELECTED_KEY @@ -104,6 +107,7 @@ export function withSelectedMultiContext( && ( isCustomAction(action, "moduleReady") || isCustomAction(action, "LazyCompReady") + || isCustomAction(action, "RemoteCompReady") ) && action.path[0] === MAP_KEY ) { comp = super.reduce(action); From 8fdbf5a8afe69db2678d8ea6ca7fc2f791d6fb8c Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Tue, 29 Jul 2025 20:00:05 +0500 Subject: [PATCH 10/31] [Fix]: #1904 add permissions data sources --- .../DatasourcePermissionDialog.tsx | 4 ++ .../PermissionDialog/Permission.tsx | 66 +++++++++++-------- .../PermissionDialog/PermissionDialog.tsx | 6 +- .../lowcoder/src/util/pagination/axios.ts | 54 +++++++++++++++ 4 files changed, 101 insertions(+), 29 deletions(-) diff --git a/client/packages/lowcoder/src/components/PermissionDialog/DatasourcePermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/DatasourcePermissionDialog.tsx index 9bad678a13..41273467a7 100644 --- a/client/packages/lowcoder/src/components/PermissionDialog/DatasourcePermissionDialog.tsx +++ b/client/packages/lowcoder/src/components/PermissionDialog/DatasourcePermissionDialog.tsx @@ -13,6 +13,7 @@ import { DatasourceRole } from "../../api/datasourcePermissionApi"; import { getDataSourcePermissionInfo } from "../../redux/selectors/datasourceSelectors"; import { StyledLoading } from "./commonComponents"; import { PermissionRole } from "./Permission"; +import { getUser } from "../../redux/selectors/usersSelectors"; export const DatasourcePermissionDialog = (props: { datasourceId: string; @@ -22,6 +23,7 @@ export const DatasourcePermissionDialog = (props: { const { datasourceId } = props; const dispatch = useDispatch(); const permissionInfo = useSelector(getDataSourcePermissionInfo)[datasourceId]; + const user = useSelector(getUser); useEffect(() => { dispatch(fetchDatasourcePermissions({ datasourceId: datasourceId })); @@ -75,6 +77,8 @@ export const DatasourcePermissionDialog = (props: { { label: trans("share.datasourceOwner"), value: PermissionRole.Owner }, ]} permissionItems={permissions} + contextType="organization" + organizationId={user.currentOrgId} viewBodyRender={(list) => { if (!permissionInfo) { return ; diff --git a/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx b/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx index adb9e9ffb3..f6fffc37d3 100644 --- a/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx +++ b/client/packages/lowcoder/src/components/PermissionDialog/Permission.tsx @@ -27,7 +27,7 @@ import { EmptyContent } from "pages/common/styledComponent"; import { trans } from "i18n"; import { PermissionItem } from "./PermissionList"; import { currentApplication } from "@lowcoder-ee/redux/selectors/applicationSelector"; -import { fetchAvailableGroupsMembers } from "@lowcoder-ee/util/pagination/axios"; +import { fetchAvailableGroupsMembers, fetchAvailableOrgGroupsMembers } from "@lowcoder-ee/util/pagination/axios"; const AddAppUserContent = styled.div` display: flex; @@ -186,6 +186,13 @@ const AddRoleSelect = styled(StyledRoleSelect)<{ $isVisible: boolean }>` display: ${(props) => (props.$isVisible ? "unset" : "none")}; `; +type PermissionContextType = "application" | "organization"; + +type PermissionContextProps = { + contextType: PermissionContextType; + organizationId?: string; +}; + type AddAppOptionView = { type: ApplicationPermissionType; id: string; @@ -294,8 +301,10 @@ const PermissionSelector = (props: { user: User; filterItems: PermissionItem[]; supportRoles: { label: string; value: PermissionRole }[]; + contextType: PermissionContextType; + organizationId?: string; }) => { - const { selectedItems, setSelectRole, setSelectedItems, user } = props; + const { selectedItems, setSelectRole, setSelectedItems, user, contextType, organizationId } = props; const [roleSelectVisible, setRoleSelectVisible] = useState(false); const selectRef = useRef(null); const [optionViews, setOptionViews] = useState() @@ -305,42 +314,41 @@ const PermissionSelector = (props: { const debouncedUserSearch = useCallback( debounce((searchTerm: string) => { - if (!application) return; - setIsLoading(true); - fetchAvailableGroupsMembers(application.applicationId, searchTerm).then(res => { - if(res.success) { - setOptionViews(getPermissionOptionView(res.data, props.filterItems)) - } - setIsLoading(false); - }).catch(() => { + + if (contextType === "application" && application) { + fetchAvailableGroupsMembers(application.applicationId, searchTerm).then(res => { + if(res.success) { + setOptionViews(getPermissionOptionView(res.data, props.filterItems)) + } + setIsLoading(false); + }).catch(() => { + setIsLoading(false); + }); + } else if (contextType === "organization" && organizationId) { + fetchAvailableOrgGroupsMembers(organizationId, searchTerm).then(res => { + if(res.success) { + setOptionViews(getPermissionOptionView(res.data || [], props.filterItems)) + } + setIsLoading(false); + }).catch(() => { + setIsLoading(false); + }); + } else { setIsLoading(false); - }); + } }, 500), - [application, props.filterItems] + [application, props.filterItems, contextType, organizationId] ); useEffect(() => { debouncedUserSearch(searchValue); - return () => { debouncedUserSearch.cancel(); }; }, [searchValue, debouncedUserSearch]); - useEffect(() => { - if (!application) return; - - setIsLoading(true); - fetchAvailableGroupsMembers(application.applicationId, "").then(res => { - if(res.success) { - setOptionViews(getPermissionOptionView(res.data, props.filterItems)) - } - setIsLoading(false); - }).catch(() => { - setIsLoading(false); - }); - }, [application, props.filterItems]); + useEffect(() => { setRoleSelectVisible(selectedItems.length > 0); @@ -425,8 +433,8 @@ export const Permission = (props: { supportRoles: { label: string; value: PermissionRole }[]; onCancel: () => void; addPermission: (userIds: string[], groupIds: string[], role: string) => void; -}) => { - const { onCancel } = props; +} & PermissionContextProps) => { + const { onCancel, contextType = "application", organizationId } = props; const user = useSelector(getUser); const [selectRole, setSelectRole] = useState("viewer"); const [selectedItems, setSelectedItems] = useState([]); @@ -443,6 +451,8 @@ export const Permission = (props: { user={user} filterItems={props.filterItems} supportRoles={props.supportRoles} + contextType={contextType} + organizationId={organizationId} /> diff --git a/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx index b3645f7887..42fb2a070a 100644 --- a/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx +++ b/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx @@ -57,8 +57,10 @@ export const PermissionDialog = (props: { ) => void; updatePermission: (permissionId: string, role: string) => void; deletePermission: (permissionId: string) => void; + contextType?: "application" | "organization"; + organizationId?: string; }) => { - const { supportRoles, permissionItems, visible, onVisibleChange, addPermission, viewBodyRender } = + const { supportRoles, permissionItems, visible, onVisibleChange, addPermission, viewBodyRender, contextType, organizationId } = props; const [activeStepKey, setActiveStepKey] = useState("view"); @@ -117,6 +119,8 @@ export const PermissionDialog = (props: { addPermission={(userIds, groupIds, role) => addPermission(userIds, groupIds, role, props.back) } + contextType={contextType || "application"} + organizationId={organizationId} /> ), footerRender: (props) => null, diff --git a/client/packages/lowcoder/src/util/pagination/axios.ts b/client/packages/lowcoder/src/util/pagination/axios.ts index e59ccbfec1..13a81157ed 100644 --- a/client/packages/lowcoder/src/util/pagination/axios.ts +++ b/client/packages/lowcoder/src/util/pagination/axios.ts @@ -118,6 +118,60 @@ export const fetchAvailableGroupsMembers = async (applicationId: string, search: } } +export const fetchAvailableOrgGroupsMembers = async (orgId: string, search: string = "") => { + try { + + + // Fetch org members and groups in parallel + const [orgMembersResponse, groupsResponse] = await Promise.all([ + OrgApi.fetchOrgUsers(orgId), + OrgApi.fetchGroup() + ]); + + const orgMembers = orgMembersResponse.data.data.members || []; + const groups = groupsResponse.data.data || []; + + // Transform to the same format as application groups/members + const transformedData = [ + // Add groups + ...groups.map((group: any) => ({ + type: "Group", + data: { + groupId: group.groupId, + groupName: group.groupName, + } + })), + // Add users + ...orgMembers.map((member: any) => ({ + type: "User", + data: { + userId: member.userId, + name: member.name, + avatarUrl: member.avatarUrl, + } + })) + ]; + + // Filter by search term if provided + const filteredData = search ? + transformedData.filter((item: any) => { + const name = item.type === "Group" ? item.data.groupName : item.data.name; + return name.toLowerCase().includes(search.toLowerCase()); + }) : transformedData; + + return { + success: true, + data: filteredData + }; + } catch (error: any) { + console.error('Failed to fetch org data: ', error); + return { + success: false, + error: error + }; + } +} + export const fetchOrgUsrPagination = async (request: fetchOrgUserRequestType)=> { try { const response = await OrgApi.fetchOrgUsersPagination(request); From 4ca237d27a9cd3ae894eb89c2f2489729e42dfac Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Tue, 29 Jul 2025 22:12:38 +0500 Subject: [PATCH 11/31] [Feat]: #1903 add isEdit mode util --- client/packages/lowcoder/src/comps/hooks/utilsComp.ts | 11 +++++++++++ .../lowcoder/src/comps/utils/globalSettings.ts | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/client/packages/lowcoder/src/comps/hooks/utilsComp.ts b/client/packages/lowcoder/src/comps/hooks/utilsComp.ts index f91bae1948..2a8689efcb 100644 --- a/client/packages/lowcoder/src/comps/hooks/utilsComp.ts +++ b/client/packages/lowcoder/src/comps/hooks/utilsComp.ts @@ -10,6 +10,7 @@ import { logoutAction } from "redux/reduxActions/userActions"; import StoreRegistry from "@lowcoder-ee/redux/store/storeRegistry"; import UserApi from "@lowcoder-ee/api/userApi"; import { messageInstance } from "components/GlobalInstances"; +import { isEditMode } from "../utils/globalSettings"; const UtilsCompBase = simpleMultiComp({}); export let UtilsComp = withExposingConfigs(UtilsCompBase, []); @@ -30,6 +31,16 @@ interface DownloadFileOptions { } UtilsComp = withMethodExposing(UtilsComp, [ + { + method: { + name: "isEditMode", + description: trans("utilsComp.isEditMode"), + params: [], + }, + execute: (comp, params) => { + return isEditMode(); + }, + }, { method: { name: "openUrl", diff --git a/client/packages/lowcoder/src/comps/utils/globalSettings.ts b/client/packages/lowcoder/src/comps/utils/globalSettings.ts index 636f4bfc9a..173511f3f6 100644 --- a/client/packages/lowcoder/src/comps/utils/globalSettings.ts +++ b/client/packages/lowcoder/src/comps/utils/globalSettings.ts @@ -19,3 +19,9 @@ export function setGlobalSettings(patch: GlobalSettings) { export function getGlobalSettings() { return globalSettings; } + + +export function isEditMode(): boolean { + // Edit mode is when we're not in view mode + return globalSettings.isViewMode !== true; +} From f62bf22e7d459cf36e80aeda8bfe32b1187c01af Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Tue, 29 Jul 2025 23:55:43 +0500 Subject: [PATCH 12/31] fixed min-width not working in responsive layout --- .../src/comps/comps/responsiveLayout/responsiveLayout.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx index c9c8229ed4..b6649d8f96 100644 --- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx @@ -62,8 +62,7 @@ const RowWrapper = styled(Row)<{ padding: ${(props) => props.$style.padding}; rotate: ${(props) => props.$style.rotation}; overflow: ${(props) => (props.$showScrollbar ? 'auto' : 'hidden')}; - display: flex; - flex-wrap: wrap; // Ensure columns wrap properly when rowBreak = true + ::-webkit-scrollbar { display: ${(props) => (props.$showScrollbar ? 'block' : 'none')}; } From 9a52ed3804f8fd87d0b43804b8f506847dc574c4 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 30 Jul 2025 00:37:26 +0500 Subject: [PATCH 13/31] after updating input comp value inside form comp, it doesn't reflect form initial values --- .../src/comps/comps/textInputComp/textInputConstants.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx index 0765b638b5..ef31521c08 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx @@ -233,6 +233,10 @@ export const useTextInputProps = (props: RecordConstructorToView { + touchRef.current = false; + }; + // Cleanup refs on unmount useEffect(() => { return () => { @@ -252,6 +256,7 @@ export const useTextInputProps = (props: RecordConstructorToView Date: Wed, 30 Jul 2025 17:43:07 +0500 Subject: [PATCH 14/31] [Fix]: #1905 redux applications fix --- .../packages/lowcoder/src/redux/sagas/folderSagas.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/client/packages/lowcoder/src/redux/sagas/folderSagas.ts b/client/packages/lowcoder/src/redux/sagas/folderSagas.ts index 9db0a1eee6..da608bab61 100644 --- a/client/packages/lowcoder/src/redux/sagas/folderSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/folderSagas.ts @@ -118,17 +118,6 @@ export function* fetchFolderElementsSaga(action: ReduxAction m.folder), }); - - // filter out applications with NORMAL status - - const applications = response.data.data.filter((item): item is ApplicationMeta => - !item.folder && item.applicationStatus === "NORMAL" - ); - - yield put({ - type: ReduxActionTypes.FETCH_ALL_APPLICATIONS_SUCCESS, - payload: applications, - }); } yield put({ From 069c744216c34122d8ca861b14b04fa45b43d68b Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Wed, 30 Jul 2025 21:26:12 +0500 Subject: [PATCH 15/31] [Fix]: #1824 query as a last item in folder --- .../components/DraggableTree/DroppableMenuItem.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/packages/lowcoder/src/components/DraggableTree/DroppableMenuItem.tsx b/client/packages/lowcoder/src/components/DraggableTree/DroppableMenuItem.tsx index 7c9eac729f..29ebb14b09 100644 --- a/client/packages/lowcoder/src/components/DraggableTree/DroppableMenuItem.tsx +++ b/client/packages/lowcoder/src/components/DraggableTree/DroppableMenuItem.tsx @@ -67,6 +67,9 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) { const canDropBefore = checkDroppableFlag(item.canDropBefore, activeNode?.data); const canDropAfter = checkDroppableFlag(item.canDropAfter, activeNode?.data); + const lastChildNode = items[items.length - 1]; + const canDropAfterLastChild = checkDroppableFlag(lastChildNode?.canDropAfter, activeNode?.data); + const dropData: IDropData = { targetListSize: items.length, targetPath: dropInAsSub ? [...path, 0] : [...path.slice(0, -1), path[path.length - 1] + 1], @@ -136,6 +139,15 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) { /> ))} + {activeNode && canDropAfterLastChild && ( +
+ +
+ )} )} From a6c5cf38168032bf12cbcad8034f25e2d3346992 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Thu, 31 Jul 2025 22:02:43 +0500 Subject: [PATCH 16/31] [Fix]: #1836 race condition barchart --- .../src/comps/barChartComp/barChartComp.tsx | 17 +++++++++++-- .../src/comps/barChartComp/barChartUtils.ts | 24 +++++++++++++++++++ .../chartConfigs/barChartConfig.tsx | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx index df7fc06232..7b64f0f6c5 100644 --- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx @@ -61,6 +61,8 @@ BarChartTmpComp = withViewFn(BarChartTmpComp, (comp) => { const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); + const [chartKey, setChartKey] = useState(0); + const prevRaceMode = useRef(); const defaultChartTheme = { color: chartColorPalette, backgroundColor: "#fff", @@ -73,6 +75,16 @@ BarChartTmpComp = withViewFn(BarChartTmpComp, (comp) => { log.error('theme chart error: ', error); } + // Detect race mode changes and force chart recreation + const currentRaceMode = comp.children.chartConfig?.children?.comp?.children?.race?.getView(); + useEffect(() => { + if (prevRaceMode.current !== undefined && prevRaceMode.current !== currentRaceMode) { + // Force chart recreation when race mode changes + setChartKey(prev => prev + 1); + } + prevRaceMode.current = currentRaceMode; + }, [currentRaceMode]); + const triggerClickEvent = async (dispatch: any, action: CompAction) => { await getPromiseAfterDispatch( dispatch, @@ -176,10 +188,11 @@ BarChartTmpComp = withViewFn(BarChartTmpComp, (comp) => { return (
(echartsCompRef.current = e)} style={{ height: "100%" }} - notMerge - lazyUpdate + notMerge={!currentRaceMode} + lazyUpdate={!currentRaceMode} opts={{ locale: getEchartsLocale() }} option={option} mode={mode} diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts index 72abe79f77..84b4ea05c6 100644 --- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts +++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts @@ -201,6 +201,15 @@ export function getEchartsConfig( animationEasing: 'linear', animationEasingUpdate: 'linear', } + } else { + // Ensure proper animation settings when race is disabled + config = { + ...config, + animationDuration: 1000, + animationDurationUpdate: 1000, + animationEasing: 'cubicOut', + animationEasingUpdate: 'cubicOut', + } } if (props.data.length <= 0) { // no data @@ -333,6 +342,21 @@ export function getEchartsConfig( animationDurationUpdate: 300 }, } + } else { + // Reset axis animations when race is disabled + config = { + ...config, + xAxis: { + ...config.xAxis, + animationDuration: undefined, + animationDurationUpdate: undefined + }, + yAxis: { + ...config.yAxis, + animationDuration: undefined, + animationDurationUpdate: undefined + }, + } } } // console.log("Echarts transformedData and config", transformedData, config); diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx index dd7a369934..d1450007a6 100644 --- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx +++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx @@ -53,7 +53,7 @@ export const BarChartConfig = (function () { type: "bar", subtype: props.type, realtimeSort: props.race, - seriesLayoutBy: props.race?'column':undefined, + seriesLayoutBy: props.race?'column':'row', label: { show: props.showLabel, position: "top", From a3626356b74c6e9911f5c6065d1884a51369cb67 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 1 Aug 2025 15:02:41 +0500 Subject: [PATCH 17/31] fix responsiveLayout issues --- .../responsiveLayout/responsiveLayout.tsx | 143 ++++++++++-------- 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx index b6649d8f96..e4a7fd0b57 100644 --- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx @@ -51,7 +51,6 @@ const RowWrapper = styled(Row)<{ $style: ResponsiveLayoutRowStyleType; $animationStyle: AnimationStyleType; $showScrollbar: boolean; - $columnCount: number; }>` ${(props) => props.$animationStyle} height: 100%; @@ -67,8 +66,6 @@ const RowWrapper = styled(Row)<{ display: ${(props) => (props.$showScrollbar ? 'block' : 'none')}; } ${props => getBackgroundStyle(props.$style)} - - --columns: ${(props) => props.$columnCount || 3}; `; const ColWrapper = styled(Col)<{ @@ -79,17 +76,31 @@ const ColWrapper = styled(Col)<{ }>` display: flex; flex-direction: column; - flex-grow: 1; - - // When rowBreak is true, columns are stretched evenly based on configured number - // When rowBreak is false, they stay at minWidth but break only if necessary - flex-basis: ${(props) => - props.$rowBreak - ? `calc(100% / var(--columns))` // Force exact column distribution - : `clamp(${props.$minWidth || "0px"}, calc(100% / var(--columns)), 100%)`}; // MinWidth respected + + /* When rowBreak is true: columns stretch evenly to fill available space */ + /* When rowBreak is false: columns take available space but respect minWidth */ + flex-grow: ${(props) => props.$rowBreak ? '1' : '1'}; + flex-shrink: ${(props) => { + if (props.$rowBreak) { + return '1'; // Can shrink when rowBreak is true + } else { + // When rowBreak is false, only allow shrinking if no minWidth is set + return props.$minWidth ? '0' : '1'; + } + }}; + flex-basis: ${(props) => { + if (props.$rowBreak) { + // When rowBreak is true, distribute columns evenly + return '0%'; + } else { + // When rowBreak is false, use minWidth if specified, otherwise auto + return props.$minWidth || 'auto'; + } + }}; - min-width: ${(props) => props.$minWidth}; // Ensure minWidth is respected - max-width: 100%; // Prevent more columns than allowed + /* Ensure minWidth is respected */ + min-width: ${(props) => props.$minWidth || 'auto'}; + max-width: 100%; > div { height: ${(props) => (props.$matchColumnsHeight ? "100%" : "auto")}; @@ -203,68 +214,70 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => { const effectiveWidth = useComponentWidth ? componentWidth ?? safeScreenInfo.width : safeScreenInfo.width; const effectiveDeviceType = useComponentWidth ? getDeviceType(effectiveWidth || 1000) : safeScreenInfo.deviceType; - // Get columns per row based on device type - let configuredColumnsPerRow = effectiveDeviceType === "mobile" + // Get current columns per row based on device type + const currentColumnsPerRow = effectiveDeviceType === "mobile" ? columnPerRowSM : effectiveDeviceType === "tablet" ? columnPerRowMD : columnPerRowLG; - // Calculate max columns that fit based on minWidth - let maxColumnsThatFit = componentWidth - ? Math.floor(componentWidth / Math.max(...columns.map((col) => parseFloat(col.minWidth || "0")))) - : configuredColumnsPerRow; - - // Determine actual number of columns - let numberOfColumns = rowBreak ? configuredColumnsPerRow : Math.min(maxColumnsThatFit, totalColumns); + // Group columns into rows based on currentColumnsPerRow only when rowBreak is true + const columnRows = rowBreak ? (() => { + const rows = []; + for (let i = 0; i < columns.length; i += currentColumnsPerRow) { + rows.push(columns.slice(i, i + currentColumnsPerRow)); + } + return rows; + })() : [columns]; // When rowBreak is false, put all columns in a single row return (
- - {columns.map((column) => { - const id = String(column.id); - const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id); - if (!containers[id] || column.hidden) return null; - const containerProps = containers[id].children; - - // Use the actual minWidth from column configuration instead of calculated width - const columnMinWidth = column.minWidth || `${100 / numberOfColumns}px`; - - return ( - - - - ); - })} - + {columnRows.map((row, rowIndex) => ( + + {row.map((column) => { + const id = String(column.id); + const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id); + if (!containers[id] || column.hidden) return null; + const containerProps = containers[id].children; + + const columnMinWidth = column.minWidth; + + return ( + + + + ); + })} + + ))}
From e025b6461dbf85831488f3ffd6390133ceb542f2 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Fri, 1 Aug 2025 17:08:17 +0500 Subject: [PATCH 18/31] [Fix]: #1905 create app issue --- .../src/redux/reducers/uiReducers/applicationReducer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts b/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts index 6725028072..bf5369b869 100644 --- a/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts +++ b/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts @@ -129,7 +129,10 @@ const usersReducer = createReducer(initialState, { action: ReduxAction ): ApplicationReduxState => ({ ...state, - applicationList: [action.payload.applicationInfoView, ...state.applicationList], + // Might be unnecessary to add the new application to the list + // TODO: Remove this after testing + // applicationList: [action.payload.applicationInfoView, ...state.applicationList], + applicationList:[], loadingStatus: { ...state.loadingStatus, isApplicationCreating: false, From 81d8e1fed6c7e519f5629fb390f73c687ee9e5ca Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 1 Aug 2025 17:22:25 +0500 Subject: [PATCH 19/31] fix form component fields not resetting --- .../lowcoder/src/comps/comps/formComp/formComp.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx index 02ca347ff0..69227d6940 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx @@ -392,9 +392,9 @@ let FormTmpComp = class extends FormBaseComp implements IForm { switch (action.type) { case CompActionTypes.UPDATE_NODES_V2: { const ret = super.reduce(action); - // When the initial value changes, update the form - if (action.value["initialData"] !== undefined) { - queueMicrotask(() => { + if (ret.children.initialData !== this.children.initialData) { + // FIXME: kill setTimeout ? + setTimeout(() => { this.dispatch( customAction( { @@ -404,7 +404,7 @@ let FormTmpComp = class extends FormBaseComp implements IForm { false ) ); - }); + }, 1000); } return ret; } From 6e9f52d6a04c8b742193b1cf70d4069e73de7726 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 1 Aug 2025 17:38:33 +0500 Subject: [PATCH 20/31] fix table column's padding --- .../comps/tableComp/column/columnTypeComps/columnTagsComp.tsx | 1 + .../lowcoder/src/comps/comps/tableComp/tableCompView.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx index 3bdbbed9dc..1e6a6e1a8a 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx @@ -142,6 +142,7 @@ export const Wrapper = styled.div` height: 100%; position: absolute; top: 0; + left: 0; background: transparent !important; padding: 8px; diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx index 9702008dad..dc6c88b0d1 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx @@ -411,9 +411,9 @@ const TableTd = styled.td` justify-content: ${(props) => props.$customAlign === 'center' ? 'center' : props.$customAlign === 'right' ? 'flex-end' : 'flex-start'}; align-items: center; text-align: ${(props) => props.$customAlign || 'left'}; - padding: 0 8px; box-sizing: border-box; ${(props) => props.$tableSize === 'small' && ` + padding: 1px 8px; font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '14px !important' : props.$style.textSize + ' !important'}; font-style:${props.$style.fontStyle} !important; min-height: ${props.$style.rowHeight || '14px'}; @@ -424,6 +424,7 @@ const TableTd = styled.td` `}; `}; ${(props) => props.$tableSize === 'middle' && ` + padding: 8px 8px; font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '16px !important' : props.$style.textSize + ' !important'}; font-style:${props.$style.fontStyle} !important; min-height: ${props.$style.rowHeight || '24px'}; @@ -434,6 +435,7 @@ const TableTd = styled.td` `}; `}; ${(props) => props.$tableSize === 'large' && ` + padding: 16px 16px; font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '18px !important' : props.$style.textSize + ' !important'}; font-style:${props.$style.fontStyle} !important; min-height: ${props.$style.rowHeight || '48px'}; From 6660a8e5c997560e9a2414a9e3baba3871318d80 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 1 Aug 2025 19:17:17 +0500 Subject: [PATCH 21/31] fix module autoheight taking more height --- .../lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx index c5bb0cced9..d830a8e867 100644 --- a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx +++ b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx @@ -327,7 +327,7 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => { bgColor={bgColor} radius="0px" emptyRows={defaultRowCount} - minHeight={defaultMinHeight} + minHeight={!isModule ? defaultMinHeight : undefined} extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined } /> @@ -368,7 +368,7 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => { bgColor={bgColor} positionParams={positionParams} emptyRows={defaultRowCount} - minHeight={defaultMinHeight} + minHeight={!isModule ? defaultMinHeight : undefined} extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined} /> From 9ac3a8a82584a3a303656bf2c8cdfb8fc530472b Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Fri, 1 Aug 2025 19:21:14 +0500 Subject: [PATCH 22/31] [Fix]: #1883 add tab-index on more controls --- .../comps/comps/buttonComp/scannerComp.tsx | 5 +++- .../src/comps/comps/fileComp/fileComp.tsx | 24 ++++++++++++------ .../lowcoder/src/comps/comps/ratingComp.tsx | 25 ++++++++++++++++++- .../comps/selectInputComp/cascaderComp.tsx | 1 + .../selectInputComp/cascaderContants.tsx | 6 +++-- .../comps/comps/treeComp/treeSelectComp.tsx | 5 +++- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx index a4ecd85a6c..8b061cb4c1 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx @@ -6,7 +6,7 @@ import { buttonRefMethods, ButtonStyleControl, } from "comps/comps/buttonComp/buttonCompConstants"; -import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { ScannerEventHandlerControl } from "comps/controls/eventHandlerControl"; import { withDefault } from "comps/generators"; import { UICompBuilder } from "comps/generators/uiCompBuilder"; @@ -128,6 +128,7 @@ const ScannerTmpComp = (function () { disabled: BoolCodeControl, style: ButtonStyleControl, viewRef: RefControl, + tabIndex: NumberControl, }; return new UICompBuilder(childrenMap, (props) => { const [showModal, setShowModal] = useState(false); @@ -199,6 +200,7 @@ const ScannerTmpComp = (function () { ref={props.viewRef} $buttonStyle={props.style} disabled={props.disabled} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} onClick={() => { props.onEvent("click"); setShowModal(true); @@ -284,6 +286,7 @@ const ScannerTmpComp = (function () { {disabledPropertyView(children)} {hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
{children.continuous.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx index 8df84e1584..8580e2d5e1 100644 --- a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx @@ -3,6 +3,7 @@ import { default as AntdUpload } from "antd/es/upload"; import { default as Dropdown } from "antd/es/dropdown"; import { UploadFile, UploadProps, UploadChangeParam, UploadFileStatus, RcFile } from "antd/es/upload/interface"; import { Buffer } from "buffer"; +import { v4 as uuidv4 } from "uuid"; import { darkenColor } from "components/colorSelect/colorUtils"; import { Section, sectionNames } from "components/Section"; import { IconControl } from "comps/controls/iconControl"; @@ -448,6 +449,7 @@ const Upload = ( text: string; dispatch: (action: CompAction) => void; forceCapture: boolean; + tabIndex?: number; }, ) => { const { dispatch, files, style } = props; @@ -564,13 +566,17 @@ const Upload = ( onChange={handleOnChange} > -
{children.fileType.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/ratingComp.tsx b/client/packages/lowcoder/src/comps/comps/ratingComp.tsx index 42a80c8ee0..025453c256 100644 --- a/client/packages/lowcoder/src/comps/comps/ratingComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/ratingComp.tsx @@ -52,12 +52,31 @@ const RatingBasicComp = (function () { 'labelStyle', ), inputFieldStyle: migrateOldData(styleControl(RatingStyle, 'inputFieldStyle'), fixOldData), + tabIndex: NumberControl, ...formDataChildren, }; return new UICompBuilder(childrenMap, (props) => { const defaultValue = { ...props.defaultValue }.value; const value = { ...props.value }.value; const changeRef = useRef(false); + const mountedRef = useRef(true); + const rateRef = useRef(null); + + useEffect(() => { + if (!mountedRef.current) return; + if (rateRef.current && typeof props.tabIndex === 'number') { + const stars = rateRef.current.querySelectorAll('li'); + stars.forEach((star, index) => { + (star as HTMLElement).setAttribute('tabindex', (props.tabIndex + index).toString()); + }); + } + }, [props.tabIndex, props.max]); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); useEffect(() => { props.value.onChange(defaultValue); @@ -76,7 +95,8 @@ const RatingBasicComp = (function () { inputFieldStyle:props.inputFieldStyle, animationStyle:props.animationStyle, children: ( - + { @@ -86,7 +106,9 @@ const RatingBasicComp = (function () { allowHalf={props.allowHalf} disabled={props.disabled} $style={props.inputFieldStyle} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} /> +
), }); }) @@ -108,6 +130,7 @@ const RatingBasicComp = (function () { {disabledPropertyView(children)} {hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
{children.allowHalf.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderComp.tsx index c71b85e2dd..b96f8eb3e8 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderComp.tsx @@ -56,6 +56,7 @@ let CascaderBasicComp = (function () { showSearch={props.showSearch} $style={props.inputFieldStyle} $childrenInputFieldStyle={props.childrenInputFieldStyle} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} onFocus={() => props.onEvent("focus")} onBlur={() => props.onEvent("blur")} popupRender={(menus: React.ReactNode) => ( diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx index 330c94120d..d88289c880 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx @@ -1,7 +1,7 @@ import { SelectEventHandlerControl } from "../../controls/eventHandlerControl"; import { Section, sectionNames } from "lowcoder-design"; import { RecordConstructorToComp } from "lowcoder-core"; -import { BoolCodeControl, JSONObjectArrayControl, StringControl } from "comps/controls/codeControl"; +import { BoolCodeControl, JSONObjectArrayControl, StringControl, NumberControl } from "comps/controls/codeControl"; import { arrayStringExposingStateControl } from "comps/controls/codeStateControl"; import { BoolControl } from "comps/controls/boolControl"; import { LabelControl } from "comps/controls/labelControl"; @@ -43,7 +43,8 @@ export const CascaderChildren = { padding: PaddingControl, inputFieldStyle:styleControl(CascaderStyle , 'inputFieldStyle'), childrenInputFieldStyle:styleControl(ChildrenMultiSelectStyle), - animationStyle:styleControl(AnimationStyle ,'animationStyle') + animationStyle:styleControl(AnimationStyle ,'animationStyle'), + tabIndex: NumberControl }; export const CascaderPropertyView = ( @@ -62,6 +63,7 @@ export const CascaderPropertyView = ( {disabledPropertyView(children)} {hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children as any)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })}
)} diff --git a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx index 1155345689..482316e0fe 100644 --- a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx @@ -21,7 +21,7 @@ import { } from "./treeUtils"; import { baseSelectRefMethods, getStyle } from "../selectInputComp/selectCompConstants"; import { useSelectInputValidate, SelectInputValidationSection } from "../selectInputComp/selectInputConstants"; -import { StringControl } from "comps/controls/codeControl"; +import { StringControl, NumberControl } from "comps/controls/codeControl"; import { SelectEventHandlerControl } from "comps/controls/eventHandlerControl"; import { selectInputValidate } from "../selectInputComp/selectInputConstants"; import { BoolControl } from "comps/controls/boolControl"; @@ -70,6 +70,7 @@ const childrenMap = { labelStyle:styleControl(LabelStyle , 'labelStyle'), inputFieldStyle: styleControl(TreeSelectStyle, 'inputFieldStyle'), viewRef: RefControl, + tabIndex: NumberControl, }; function getCheckedStrategy(v: ValueFromOption) { @@ -123,6 +124,7 @@ const TreeCompView = ( treeLine={props.showLine ? { showLeafIcon: props.showLeafIcon } : false} // fix expand issue when searching treeExpandedKeys={inputValue ? undefined : expanded.value} + tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} onTreeExpand={(keys) => { expanded.onChange(keys as (string | number)[]); }} @@ -172,6 +174,7 @@ let TreeBasicComp = (function () { {allowClearPropertyView(children)} {showSearchPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.tabIndex.propertyView({ label: trans("prop.tabIndex") })} )} From d16e4751eeacb5c0aee847929c1a82db8c1fa46c Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Fri, 1 Aug 2025 21:50:00 +0500 Subject: [PATCH 23/31] [Fix]: #1928 folder edit/create issues --- .../src/pages/ApplicationV2/HomeResCard.tsx | 15 +++++--- .../pages/ApplicationV2/HomeResOptions.tsx | 38 +++++++++---------- .../pages/ApplicationV2/MoveToFolderModal.tsx | 15 ++++++-- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx index db2758e737..627146ffcf 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx @@ -143,7 +143,6 @@ export const StyledTypographyText = styled(AntdTypographyText)` &:hover { color: #315efb; } - } `; const MONTH_MILLIS = 30 * 24 * 60 * 60 * 1000; @@ -244,11 +243,15 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi const Icon = resInfo.icon; const handleModalOk = (values: any) => { - res.type === HomeResTypeEnum.Folder && - dispatch(updateFolder({ id: res.id, name: values.appName || res.name })) - dispatch( - updateAppMetaAction({ applicationId: res.id, name: values.appName || res.name, folderId: folderId }) - ); + if (res.type === HomeResTypeEnum.Folder) { + // Update folder + dispatch(updateFolder({ id: res.id, name: values.appName || res.name })); + } else { + // Update application + dispatch( + updateAppMetaAction({ applicationId: res.id, name: values.appName || res.name, folderId: folderId }) + ); + } setDialogVisible(false); setTimeout(() => { diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx index 38e4b68953..ce19949fbe 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx @@ -12,8 +12,7 @@ import { AppTypeEnum } from "constants/applicationConstants"; import { CopyModal } from "pages/common/copyModal"; import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"; import ApplicationApi from "../../api/applicationApi"; -import { FolderApi } from "../../api/folderApi"; -import { ReduxActionTypes } from "constants/reduxActionConstants"; +import { deleteFolder } from "../../redux/reduxActions/folderActions"; const PopoverIcon = styled(PointIcon)` cursor: pointer; @@ -120,27 +119,24 @@ export const HomeResOptions = (props: { type: HomeResInfo[res.type].name.toLowerCase(), name: {res.name}, }), - onConfirm: async () => { - try { - await FolderApi.deleteFolder({ + onConfirm: () => { + dispatch(deleteFolder( + { folderId: res.id, parentFolderId: folderId || "" - }); - - // Update Redux state to remove deleted folder from dropdown - dispatch({ - type: ReduxActionTypes.DELETE_FOLDER_SUCCESS, - payload: { folderId: res.id, parentFolderId: folderId || "" } - }); - - messageInstance.success(trans("home.deleteSuccessMsg")); - setTimeout(() => { - setModify(!modify); - }, 200); - } catch (error) { - console.error("Failed to delete folder:", error); - messageInstance.error("Failed to delete folder"); - } + }, + () => { + // Success callback + messageInstance.success(trans("home.deleteSuccessMsg")); + setTimeout(() => { + setModify(!modify); + }, 200); + }, + () => { + // Error callback + messageInstance.error("Failed to delete folder"); + } + )); }, confirmBtnType: "delete", okText: trans("delete"), diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MoveToFolderModal.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MoveToFolderModal.tsx index 2e3d4888e1..c97f48bd0a 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/MoveToFolderModal.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/MoveToFolderModal.tsx @@ -1,6 +1,6 @@ import { HomeRes } from "./HomeLayout"; import { default as Form } from "antd/es/form"; -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; import { @@ -11,10 +11,10 @@ import { FormSelectItem, TacoButton, } from "lowcoder-design"; -import { moveToFolder } from "../../redux/reduxActions/folderActions"; +import { moveToFolder, fetchFolderElements } from "../../redux/reduxActions/folderActions"; import styled from "styled-components"; import { trans } from "../../i18n"; -import { foldersSelector } from "../../redux/selectors/folderSelector"; +import { foldersSelector, isFetchingFolderElements } from "../../redux/selectors/folderSelector"; const MoveLabel = styled.div` font-size: 13px; @@ -47,11 +47,20 @@ export const MoveToFolderModal = (props: { source?: HomeRes; onClose: () => void const [loading, setLoading] = useState(false); const folders = useSelector(foldersSelector); + const isFetching = useSelector(isFetchingFolderElements); const dispatch = useDispatch(); const { folderId } = useParams<{ folderId: string }>(); + // Fetch folders when modal opens to populate Redux state (only if not already loaded or fetching) + useEffect(() => { + if (props.source && folders.length === 0 && !isFetching) { + // Dispatch the Redux action to fetch folders (empty folderId fetches all folders) + dispatch(fetchFolderElements({})); + } + }, [props.source, dispatch, folders.length, isFetching]); + return ( Date: Mon, 4 Aug 2025 17:13:07 +0500 Subject: [PATCH 24/31] fix js query trigger on page load when used exposed variables are referred e.g. utils, toast, input1 --- client/packages/lowcoder-core/lib/index.js | 9 +++++---- client/packages/lowcoder-core/src/eval/codeNode.tsx | 9 +++++---- client/packages/lowcoder/src/comps/queries/queryComp.tsx | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/client/packages/lowcoder-core/lib/index.js b/client/packages/lowcoder-core/lib/index.js index f972c7ba4b..719756c00b 100644 --- a/client/packages/lowcoder-core/lib/index.js +++ b/client/packages/lowcoder-core/lib/index.js @@ -1692,11 +1692,12 @@ class CodeNode extends AbstractNode { // if query is dependent on itself, mark as ready if (pathsArr?.[0] === options?.queryName) return; + // TODO: check if this is needed after removing lazy load // wait for lazy loaded comps to load before executing query on page load - if (value && !Object.keys(value).length && paths.size) { - isFetching = true; - ready = false; - } + // if (value && !Object.keys(value).length && paths.size) { + // isFetching = true; + // ready = false; + // } if (_.has(value, IS_FETCHING_FIELD)) { isFetching = isFetching || value.isFetching === true; } diff --git a/client/packages/lowcoder-core/src/eval/codeNode.tsx b/client/packages/lowcoder-core/src/eval/codeNode.tsx index 2b67e7bbfa..e2e69fab1b 100644 --- a/client/packages/lowcoder-core/src/eval/codeNode.tsx +++ b/client/packages/lowcoder-core/src/eval/codeNode.tsx @@ -176,11 +176,12 @@ export class CodeNode extends AbstractNode> { // if query is dependent on itself, mark as ready if (pathsArr?.[0] === options?.queryName) return; + // TODO: check if this is needed after removing lazy load // wait for lazy loaded comps to load before executing query on page load - if (value && !Object.keys(value).length && paths.size) { - isFetching = true; - ready = false; - } + // if (value && !Object.keys(value).length && paths.size) { + // isFetching = true; + // ready = false; + // } if (_.has(value, IS_FETCHING_FIELD)) { isFetching = isFetching || value.isFetching === true; } diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx index e47694f963..c5b983cfee 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx @@ -335,7 +335,7 @@ function QueryView(props: QueryViewProps) { ) { setTimeout(() => { comp.dispatch(deferAction(executeQueryAction({}))); - }, 300); + }, 600); } if(getTriggerType(comp) === "onTimeout") { From 485cc3dee43cd8e7f3a4c615a6d5a4b16dc9bd0a Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Mon, 4 Aug 2025 20:23:11 +0500 Subject: [PATCH 25/31] fixed form comp's resetAfterSuccess doesn't work --- .../packages/lowcoder/src/comps/comps/formComp/formComp.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx index 69227d6940..8255813932 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx @@ -330,11 +330,6 @@ let FormTmpComp = class extends FormBaseComp implements IForm { // For the properties, first find in data, then initialData, subcomponent default value (resetValue), empty value (clearValue) const newData = { ...(initialData ?? this.children.initialData.getView()), ...data }; - // Only proceed if we have data to set - if (!Object.keys(newData).length) { - return Promise.resolve(); - } - return this.runMethodOfItems( { name: "setValue", From 16715857a634b22c096a08fcb1a6c12093169575 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Tue, 5 Aug 2025 15:25:49 +0500 Subject: [PATCH 26/31] reduce input change debounce time --- .../src/comps/comps/textInputComp/textInputConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx index ef31521c08..0b6ca8f2e9 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx @@ -221,7 +221,7 @@ export const useTextInputProps = (props: RecordConstructorToView) => { From ccd5d1747fa601c9e92f0808caf434df53f25c5d Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Thu, 7 Aug 2025 21:50:58 +0500 Subject: [PATCH 27/31] [Fix]: #1935 add instant save for boolean, select and switch --- .../src/components/table/EditableCell.tsx | 22 +++++++++++++++++-- .../columnTypeComps/columnBooleanComp.tsx | 11 ++++++++-- .../columnTypeComps/columnSelectComp.tsx | 9 +++++++- .../columnTypeComps/columnSwitchComp.tsx | 4 ++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/client/packages/lowcoder/src/components/table/EditableCell.tsx b/client/packages/lowcoder/src/components/table/EditableCell.tsx index 42d7123457..b886167448 100644 --- a/client/packages/lowcoder/src/components/table/EditableCell.tsx +++ b/client/packages/lowcoder/src/components/table/EditableCell.tsx @@ -54,6 +54,7 @@ export type EditViewFn = (props: { value: T; onChange: (value: T) => void; onChangeEnd: () => void; + onImmediateSave?: (value: T) => void; otherProps?: Record; }) => ReactNode; @@ -168,9 +169,26 @@ function EditableCellComp(props: EditableCellProps) { } }, [dispatch, tmpValue, baseValue, value, onTableEvent, setIsEditing]); + const onImmediateSave = useCallback((newValue: T) => { + if (!mountedRef.current) return; + + setTmpValue(newValue); + const changeValue = _.isNil(newValue) || _.isEqual(newValue, baseValue) ? null : newValue; + dispatch( + changeChildAction( + "changeValue", + changeValue, + false + ) + ); + if(!_.isEqual(newValue, value)) { + onTableEvent?.('columnEdited'); + } + }, [dispatch, baseValue, value, onTableEvent]); + const editView = useMemo( - () => editViewFn?.({ value, onChange, onChangeEnd, otherProps }) ?? <>, - [editViewFn, value, onChange, onChangeEnd, otherProps] + () => editViewFn?.({ value, onChange, onChangeEnd, onImmediateSave, otherProps }) ?? <>, + [editViewFn, value, onChange, onChangeEnd, onImmediateSave, otherProps] ); const enterEditFn = useCallback(() => { diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx index d1d530eb6c..1cc26a5eae 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx @@ -64,6 +64,7 @@ type CheckBoxEditPropsType = { value: boolean; onChange: (value: boolean) => void; onChangeEnd: () => void; + onImmediateSave?: (value: boolean) => void; }; // Memoized checkbox edit component @@ -92,8 +93,13 @@ const CheckBoxEdit = React.memo((props: CheckBoxEditPropsType) => { const handleChange = useCallback((e: CheckboxChangeEvent) => { if (!mountedRef.current) return; - props.onChange(e.target.checked); - }, [props.onChange]); + const newValue = e.target.checked; + props.onChange(newValue); + // Use immediate save to show Save Changes button without exiting edit mode + if (props.onImmediateSave) { + props.onImmediateSave(newValue); + } + }, [props.onChange, props.onImmediateSave]); return ( ); }) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx index b54be87997..adc2c4d887 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx @@ -124,6 +124,7 @@ type SelectEditProps = { initialValue: string; onChange: (value: string) => void; onChangeEnd: () => void; + onImmediateSave?: (value: string) => void; options: any[]; onMainEvent?: (eventName: string) => void; }; @@ -146,6 +147,11 @@ const SelectEdit = React.memo((props: SelectEditProps) => { props.onChange(val); setCurrentValue(val); + // Use immediate save to show Save Changes button without exiting edit mode + if (props.onImmediateSave) { + props.onImmediateSave(val); + } + // Trigger the specific option's event handler const selectedOption = props.options.find(option => option.value === val); if (selectedOption?.onEvent) { @@ -156,7 +162,7 @@ const SelectEdit = React.memo((props: SelectEditProps) => { if (props.onMainEvent) { props.onMainEvent("click"); } - }, [props.onChange, props.options, props.onMainEvent]); + }, [props.onChange, props.onImmediateSave, props.options, props.onMainEvent]); const handleEvent = useCallback(async (eventName: string) => { if (!mountedRef.current) return [] as unknown[]; @@ -209,6 +215,7 @@ export const ColumnSelectComp = (function () { options={props.otherProps?.options || []} onChange={props.onChange} onChangeEnd={props.onChangeEnd} + onImmediateSave={props.onImmediateSave} onMainEvent={props.otherProps?.onEvent} /> diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx index 0cdeee48a4..d005f2ecc5 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx @@ -144,6 +144,10 @@ export const SwitchComp = (function () { disabled={false} onChange={(checked, e) => { props.onChange(checked); + // Use immediate save to show Save Changes button without exiting edit mode + if (props.onImmediateSave) { + props.onImmediateSave(checked); + } props.otherProps?.onEvent?.("change"); props.otherProps?.onEvent?.(checked ? "true" : "false"); }} From b5221dbcc5d5a75973425c76e86926b0b8d94838 Mon Sep 17 00:00:00 2001 From: FARAN Date: Fri, 8 Aug 2025 17:23:26 +0500 Subject: [PATCH 28/31] [Fix]: #1907 mariadb in generate form datasource --- .../lowcoder/src/comps/comps/formComp/generate/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/packages/lowcoder/src/comps/comps/formComp/generate/index.tsx b/client/packages/lowcoder/src/comps/comps/formComp/generate/index.tsx index 7fdffe2b7a..c5b8705fd3 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/generate/index.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/generate/index.tsx @@ -12,6 +12,8 @@ export function getDataSourceTypeConfig( switch (dataSourceType) { case "mysql": return mysqlConfig; + case "mariadb": + return mysqlConfig; case "postgres": return postgreSqlConfig; case "mssql": From 27ad5f93a7b8eb99112c8844a3ca6e8950ebef9e Mon Sep 17 00:00:00 2001 From: FARAN Date: Mon, 11 Aug 2025 16:09:28 +0500 Subject: [PATCH 29/31] [Fix]: remove duplicate event listeners --- .../src/comps/comps/tableComp/tableToolbarComp.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx index 2969e3c46d..6b6f619efa 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx @@ -793,18 +793,15 @@ export const TableToolbar = memo(function TableToolbar(props: { const handleDownload = useCallback(() => { onDownload(); - onEvent("download"); - }, [onDownload, onEvent]); + }, [onDownload]); const handleSaveChanges = useCallback(() => { onSaveChanges(); - onEvent("saveChanges"); - }, [onSaveChanges, onEvent]); + }, [onSaveChanges]); const handleCancelChanges = useCallback(() => { onCancelChanges(); - onEvent("cancelChanges"); - }, [onCancelChanges, onEvent]); + }, [onCancelChanges]); const handleColumnFilterChange = useCallback((filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => { if ( From 5745ad0d5439d292b0bbc7ef15af874fa6a2c92e Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Mon, 11 Aug 2025 18:09:30 +0500 Subject: [PATCH 30/31] fixed js query triggers multiple time on page load --- client/packages/lowcoder/src/comps/queries/queryComp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx index c5b983cfee..47bfdf8f90 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx @@ -301,7 +301,7 @@ QueryCompTmp = class extends QueryCompTmp { // If the dsl has not changed, but the dependent node value has changed, then trigger the query execution // FIXME, this should be changed to a reference judgement, but for unknown reasons if the reference is modified once, it will change twice. - if (dependsChanged) { + if (dependsChanged && !isJsQuery) { if (dslNotChanged) { this.execute(next); } From 1053d198f2840dfe971a3df28758cdbf757921ac Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Tue, 12 Aug 2025 00:31:37 +0500 Subject: [PATCH 31/31] fixed color picker input styles --- .../comps/comps/mediaComp/colorPickerComp.tsx | 54 +++++++++++++++---- .../lowcoder/src/constants/themeConstants.ts | 1 + 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/mediaComp/colorPickerComp.tsx b/client/packages/lowcoder/src/comps/comps/mediaComp/colorPickerComp.tsx index b11e528a3f..ab7bf8b30e 100644 --- a/client/packages/lowcoder/src/comps/comps/mediaComp/colorPickerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/mediaComp/colorPickerComp.tsx @@ -1,7 +1,7 @@ import { Section, sectionNames } from "lowcoder-design"; import { BoolControl } from "comps/controls/boolControl"; import { styleControl } from "comps/controls/styleControl"; -import { ColorPickerStyle, ColorPickerStyleType } from "comps/controls/styleControlConstants"; +import { AnimationStyle, ColorPickerStyle, ColorPickerStyleType, DisabledInputStyle, DisabledInputStyleType, InputFieldStyle, InputLikeStyle, InputLikeStyleType, LabelStyle } from "comps/controls/styleControlConstants"; import { NameConfig } from "comps/generators/withExposing"; import styled, { css } from "styled-components"; import { UICompBuilder, withDefault } from "../../generators"; @@ -16,18 +16,22 @@ import { changeEvent, eventHandlerControl } from "comps/controls/eventHandlerCon import { jsonObjectExposingStateControl, stringExposingStateControl } from "comps/controls/codeStateControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { ArrayOrJSONObjectControl } from "comps/controls/codeControl"; -import { JSONObject } from "@lowcoder-ee/util/jsonTypes"; -export function getStyle(style: ColorPickerStyleType) { +export function getStyle(style: InputLikeStyleType) { return css` border-radius: ${style.radius}; &:not(.ant-input-disabled, .ant-input-affix-wrapper-disabled), - input { + .ant-color-picker-trigger { + color: ${style.text}; + font-weight: ${style.textWeight}; + font-family: ${style.fontFamily}; + font-style:${style.fontStyle}; + text-transform:${style.textTransform}; + text-decoration:${style.textDecoration}; background-color: ${style.background}; - color:${style.text}; - font-weight:${style.textWeight}; - font-family:${style.fontFamily}; border-color: ${style.border}; + padding: ${style.padding}; + margin: ${style.margin}; &:focus, &.ant-input-affix-wrapper-focused { border-color: ${style.accent}; @@ -38,15 +42,31 @@ export function getStyle(style: ColorPickerStyleType) { .ant-input-clear-icon svg:hover { opacity: 0.65; } + .ant-color-picker-trigger-text { + font-size: ${style.textSize}; + } } `; } -const ColorPickerWrapper = styled(ColorPicker) <{ $style: ColorPickerStyleType }>` - width: 100%; +const ColorPickerWrapper = styled(ColorPicker) <{ + $style: InputLikeStyleType; + $disabledStyle?: DisabledInputStyleType; +}>` display: flex; justify-content: flex-start; + box-shadow: ${(props) => + `${props.$style?.boxShadow} ${props.$style?.boxShadowColor}`}; ${(props) => props.$style && getStyle(props.$style)} + + /* Disabled state styling */ + &:disabled, + &.ant-input-disabled { + color: ${(props) => props.$disabledStyle?.disabledText}; + background: ${(props) => props.$disabledStyle?.disabledBackground}; + border-color: ${(props) => props.$disabledStyle?.disabledBorder}; + cursor: not-allowed; + } `; const colorPickerTriggerOption = [ @@ -61,20 +81,25 @@ export const colorPickerEvent = eventHandlerControl([ const childrenMap = { ...textInputChildren, value: stringExposingStateControl('value', '#3377ff'), - style: styleControl(ColorPickerStyle , 'style'), color: jsonObjectExposingStateControl('color', {}), trigger: dropdownControl(colorPickerTriggerOption, 'click'), disabledAlpha: BoolControl, showPresets: BoolControl, onEvent: colorPickerEvent, presets: withDefault(ArrayOrJSONObjectControl, JSON.stringify(presets, null, 2)), + style: styleControl(InputFieldStyle, 'style'), + labelStyle:styleControl(LabelStyle, 'labelStyle'), + inputFieldStyle: styleControl(InputLikeStyle, 'inputFieldStyle'), + animationStyle: styleControl(AnimationStyle, 'animationStyle'), + disabledStyle: styleControl(DisabledInputStyle, 'disabledStyle'), }; export const ColorPickerComp = new UICompBuilder(childrenMap, (props) => { return props.label({ children: ( value.toHexString().toUpperCase()} @@ -94,6 +119,9 @@ export const ColorPickerComp = new UICompBuilder(childrenMap, (props) => { /> ), style: props.style, + labelStyle: props.labelStyle, + inputFieldStyle:props.inputFieldStyle, + animationStyle:props.animationStyle, }); }) .setPropertyViewFn((children) => { @@ -121,6 +149,10 @@ export const ColorPickerComp = new UICompBuilder(childrenMap, (props) => {
{hiddenPropertyView(children)}
{children.style.getPropertyView()}
+
{children.labelStyle.getPropertyView()}
+
{children.inputFieldStyle.getPropertyView()}
+
{children.disabledStyle.getPropertyView()}
+
{children.animationStyle.getPropertyView()}
); }) diff --git a/client/packages/lowcoder/src/constants/themeConstants.ts b/client/packages/lowcoder/src/constants/themeConstants.ts index e4d1a0cf98..4cd50ba9b1 100644 --- a/client/packages/lowcoder/src/constants/themeConstants.ts +++ b/client/packages/lowcoder/src/constants/themeConstants.ts @@ -219,6 +219,7 @@ export const defaultTheme: ThemeDetail = { dateRange: input, time: input, timeRange: input, + colorPicker: input, rangeSlider: slider, segmentedControl, select: select,