Skip to content

Commit ecf25de

Browse files
Merge pull request #2147 from iamfaran/feat/2145-theme-nav
[Feat]: #2145 Add theme settings for the Navigation App type
2 parents 2bd102e + 61ef19b commit ecf25de

6 files changed

Lines changed: 133 additions & 36 deletions

File tree

client/packages/lowcoder/src/components/ThemeSettingsSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
254254
};
255255

256256
const gridPaddingInputBlur = (padding: string) => {
257-
let result = 20;
257+
let result = 0;
258258
if (padding !== '') {
259259
result = Number(padding);
260260
}

client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ const childrenMap = {
219219
gridColumns: RangeControl.closed(1, 48, 24),
220220
gridRowHeight: RangeControl.closed(4, 100, 8),
221221
gridRowCount: withDefault(NumberControl, DEFAULT_ROW_COUNT),
222-
gridPaddingX: withDefault(NumberControl, 20),
223-
gridPaddingY: withDefault(NumberControl, 20),
222+
gridPaddingX: withDefault(NumberControl, 0),
223+
gridPaddingY: withDefault(NumberControl, 0),
224224
gridBg: ColorControl,
225225
gridBgImage: StringControl,
226226
gridBgImageRepeat: StringControl,
@@ -342,6 +342,10 @@ function AppGeneralSettingsModal(props: ChildrenInstance) {
342342

343343
function AppCanvasSettingsModal(props: ChildrenInstance) {
344344
const isPublicApp = useSelector(isPublicApplication);
345+
const application = useSelector(currentApplication);
346+
const isAggregation = !!application && isAggregationApp(
347+
AppUILayoutType[application.applicationType]
348+
);
345349
const {
346350
themeList,
347351
defaultTheme,
@@ -397,7 +401,7 @@ function AppCanvasSettingsModal(props: ChildrenInstance) {
397401
return (
398402
<>
399403
<BaseSection
400-
name={"Theme Settings"}
404+
name={trans("appSetting.themeSettings")}
401405
width={288}
402406
noMargin
403407
style={{
@@ -454,33 +458,33 @@ function AppCanvasSettingsModal(props: ChildrenInstance) {
454458
}}
455459
>
456460
<DivStyled>
457-
{maxWidth.propertyView({
461+
{!isAggregation && maxWidth.propertyView({
458462
dropdownLabel: trans("appSetting.canvasMaxWidth"),
459463
inputLabel: trans("appSetting.userDefinedMaxWidth"),
460464
inputPlaceholder: trans("appSetting.inputUserDefinedPxValue"),
461465
placement: "bottom",
462466
min: 350,
463467
lastNode: <span>{trans("appSetting.maxWidthTip")}</span>,
464468
})}
465-
{gridColumns.propertyView({
469+
{!isAggregation && gridColumns.propertyView({
466470
label: trans("appSetting.gridColumns"),
467471
placeholder: '24',
468472
})}
469-
{gridRowHeight.propertyView({
473+
{!isAggregation && gridRowHeight.propertyView({
470474
label: trans("appSetting.gridRowHeight"),
471475
placeholder: '8',
472476
})}
473-
{gridRowCount.propertyView({
477+
{!isAggregation && gridRowCount.propertyView({
474478
label: trans("appSetting.gridRowCount"),
475479
placeholder: 'Infinity',
476480
})}
477481
{gridPaddingX.propertyView({
478482
label: trans("appSetting.gridPaddingX"),
479-
placeholder: '20',
483+
placeholder: '0',
480484
})}
481485
{gridPaddingY.propertyView({
482486
label: trans("appSetting.gridPaddingY"),
483-
placeholder: '20',
487+
placeholder: '0',
484488
})}
485489
{gridBg.propertyView({
486490
label: trans("style.background"),

client/packages/lowcoder/src/comps/comps/layout/mobileTabLayout.tsx

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import { AppSelectComp } from "comps/comps/layout/appSelectComp";
1111
import { NameAndExposingInfo } from "comps/utils/exposingTypes";
1212
import { ConstructorToComp, ConstructorToDataType } from "lowcoder-core";
1313
import { CanvasContainer } from "comps/comps/gridLayoutComp/canvasView";
14-
import { CanvasContainerID } from "constants/domLocators";
15-
import { PreviewContainerID } from "constants/domLocators";
14+
import { CanvasContainerID, PreviewContainerID } from "constants/domLocators";
1615
import { EditorContainer, EmptyContent } from "pages/common/styledComponent";
1716
import { Layers } from "constants/Layers";
1817
import { ExternalEditorContext } from "util/context/ExternalEditorContext";
18+
import { EditorContext } from "comps/editorState";
1919
import { default as Skeleton } from "antd/es/skeleton";
2020
import { hiddenPropertyView } from "comps/utils/propertyUtils";
2121
import { dropdownControl } from "@lowcoder-ee/comps/controls/dropdownControl";
@@ -47,6 +47,21 @@ const TabBarItem = React.lazy(() =>
4747
);
4848
const EventOptions = [clickEvent] as const;
4949

50+
/** Mobile nav editor: tab bar uses position:absolute bottom; this root is the containing block */
51+
const MobileNavCanvasRoot = styled(CanvasContainer)`
52+
position: relative;
53+
`;
54+
55+
/** Strip shared EditorContainer defaults (16px padding + scrollbar-gutter: stable) for mobile nav */
56+
const MobileNavEditorContainer = styled(EditorContainer)`
57+
padding: 0;
58+
padding-right: 0;
59+
scrollbar-gutter: auto;
60+
overflow-x: auto;
61+
overflow-y: auto;
62+
background: transparent;
63+
`;
64+
5065
const AppViewContainer = styled.div`
5166
position: absolute;
5267
width: 100%;
@@ -221,17 +236,17 @@ const TabBarWrapper = styled.div<{
221236
$readOnly: boolean,
222237
$canvasBg: string,
223238
$tabBarHeight: string,
224-
$maxWidth: number,
225239
$verticalAlignment: string;
226240
}>`
241+
box-sizing: border-box;
227242
max-width: inherit;
228243
background: ${(props) => (props.$canvasBg)};
229244
margin: 0 auto;
230-
position: fixed;
245+
position: ${(props) => (props.$readOnly ? "fixed" : "absolute")};
231246
bottom: 0;
232247
left: 0;
233248
right: 0;
234-
width: ${(props) => props.$readOnly ? "100%" : `${props.$maxWidth - 30}px`};
249+
width: 100%;
235250
z-index: ${Layers.tabBar};
236251
padding-bottom: env(safe-area-inset-bottom, 0);
237252
@@ -389,7 +404,6 @@ function convertTreeData(data: any) {
389404

390405
function TabBarView(props: TabBarProps & {
391406
tabBarHeight: string;
392-
maxWidth: number;
393407
verticalAlignment: string;
394408
showSeparator: boolean;
395409
navIconSize: string;
@@ -404,7 +418,6 @@ function TabBarView(props: TabBarProps & {
404418
$readOnly={props.readOnly}
405419
$canvasBg={canvasBg}
406420
$tabBarHeight={props.tabBarHeight}
407-
$maxWidth={props.maxWidth}
408421
$verticalAlignment={props.verticalAlignment}
409422
>
410423
<StyledTabBar
@@ -664,6 +677,36 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
664677
const bgColor = (useContext(ThemeContext)?.theme || defaultTheme).canvas;
665678
const onEvent = comp.children.onEvent.getView();
666679

680+
// Pull app-level Theme / Canvas Settings (managed via the left-sidebar
681+
// "Canvas" pane and shared with normal apps + modules). Mobile nav already
682+
// owns its own maxWidth + grid behaviour, so we only consume the
683+
// background + padding subset here.
684+
const editorState = useContext(EditorContext);
685+
const appSettings = editorState?.getAppSettings();
686+
const canvasBg = appSettings?.gridBg;
687+
const canvasBgImage = appSettings?.gridBgImage;
688+
const canvasBgImageRepeat = appSettings?.gridBgImageRepeat || "no-repeat";
689+
const canvasBgImageSize = appSettings?.gridBgImageSize || "cover";
690+
const canvasBgImagePosition = appSettings?.gridBgImagePosition || "center";
691+
const canvasBgImageOrigin = appSettings?.gridBgImageOrigin || "padding-box";
692+
const canvasPaddingX = appSettings?.gridPaddingX ?? 0;
693+
const canvasPaddingY = appSettings?.gridPaddingY ?? 0;
694+
695+
const canvasBackgroundStyle: React.CSSProperties = {
696+
background: "#FFFFFF",
697+
};
698+
if (canvasBg) {
699+
canvasBackgroundStyle.background = canvasBg;
700+
}
701+
if (canvasBgImage) {
702+
canvasBackgroundStyle.backgroundImage = `url('${canvasBgImage}')`;
703+
canvasBackgroundStyle.backgroundRepeat = canvasBgImageRepeat;
704+
canvasBackgroundStyle.backgroundSize = canvasBgImageSize;
705+
canvasBackgroundStyle.backgroundPosition = canvasBgImagePosition;
706+
canvasBackgroundStyle.backgroundOrigin = canvasBgImageOrigin;
707+
}
708+
const canvasContentPadding = `${canvasPaddingY}px ${canvasPaddingX}px`;
709+
667710
const getContainer = useCallback(() =>
668711
document.querySelector(`#${PreviewContainerID}`) ||
669712
document.querySelector(`#${CanvasContainerID}`) ||
@@ -702,7 +745,7 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
702745
currentTab.children.app.getView()) || (
703746
<EmptyContent
704747
text={readOnly ? "" : trans("aggregation.emptyTabTooltip")}
705-
style={{ height: "100%", backgroundColor: "white" }}
748+
style={{ height: "100%" }}
706749
/>
707750
);
708751
}
@@ -712,7 +755,7 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
712755
currentTab.children.action.getView()) || (
713756
<EmptyContent
714757
text={readOnly ? "" : trans("aggregation.emptyTabTooltip")}
715-
style={{ height: "100%", backgroundColor: "white" }}
758+
style={{ height: "100%" }}
716759
/>
717760
)
718761
}, [tabIndex, tabViews, dataOptionType]);
@@ -769,7 +812,6 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
769812
tabItemActiveStyle={navItemActiveStyle}
770813
tabBarHeight={tabBarHeight}
771814
navIconSize={navIconSize}
772-
maxWidth={maxWidth}
773815
verticalAlignment={verticalAlignment}
774816
showSeparator={showSeparator}
775817
/>
@@ -870,8 +912,12 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
870912

871913
if (readOnly) {
872914
return (
873-
<TabLayoutViewContainer maxWidth={maxWidth} tabBarHeight={containerTabBarHeight}>
874-
<AppViewContainer>{appView}</AppViewContainer>
915+
<TabLayoutViewContainer
916+
maxWidth={maxWidth}
917+
tabBarHeight={containerTabBarHeight}
918+
style={canvasBackgroundStyle}
919+
>
920+
<AppViewContainer style={{ padding: canvasContentPadding }}>{appView}</AppViewContainer>
875921
{menuMode === MobileMode.Hamburger ? (
876922
<>
877923
{hamburgerButton}
@@ -885,8 +931,12 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
885931
}
886932

887933
return (
888-
<CanvasContainer $maxWidth={maxWidth} id={CanvasContainerID}>
889-
<EditorContainer>{appView}</EditorContainer>
934+
<MobileNavCanvasRoot
935+
$maxWidth={maxWidth}
936+
id={CanvasContainerID}
937+
style={canvasBackgroundStyle}
938+
>
939+
<MobileNavEditorContainer style={{ padding: canvasContentPadding }}>{appView}</MobileNavEditorContainer>
890940
{menuMode === MobileMode.Hamburger ? (
891941
<>
892942
{hamburgerButton}
@@ -895,7 +945,7 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
895945
) : (
896946
tabBarView
897947
)}
898-
</CanvasContainer>
948+
</MobileNavCanvasRoot>
899949
);
900950
});
901951

client/packages/lowcoder/src/comps/comps/layout/navLayout.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import MainContent from "components/layout/MainContent";
66
import { LayoutMenuItemComp, LayoutMenuItemListComp } from "comps/comps/layout/layoutMenuItemComp";
77
import { menuPropertyView } from "comps/comps/navComp/components/MenuItemList";
88
import { registerLayoutMap } from "comps/comps/uiComp";
9+
import { EditorContext } from "comps/editorState";
910
import { MultiCompBuilder, withDefault, withViewFn } from "comps/generators";
1011
import { withDispatchHook } from "comps/generators/withDispatchHook";
1112
import { NameAndExposingInfo } from "comps/utils/exposingTypes";
@@ -14,7 +15,7 @@ import { TopHeaderHeight } from "constants/style";
1415
import { Section, controlItem, sectionNames } from "lowcoder-design";
1516
import { trans } from "i18n";
1617
import { EditorContainer, EmptyContent } from "pages/common/styledComponent";
17-
import { useCallback, useEffect, useMemo, useState } from "react";
18+
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
1819
import styled from "styled-components";
1920
import { isUserViewMode, useAppPathParam } from "util/hooks";
2021
import { StringControl, jsonControl } from "comps/controls/codeControl";
@@ -381,6 +382,21 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
381382
const dataOptionType = comp.children.dataOptionType.getView();
382383
const onEvent = comp.children.onEvent.getView();
383384

385+
// Pull app-level Theme / Canvas Settings (managed via the left-sidebar
386+
// "Canvas" pane and shared with normal apps + modules). For aggregation
387+
// apps the grid sizing fields are intentionally hidden in the settings UI;
388+
// we only consume the background + padding subset here.
389+
const editorState = useContext(EditorContext);
390+
const appSettings = editorState?.getAppSettings();
391+
const canvasBg = appSettings?.gridBg;
392+
const canvasBgImage = appSettings?.gridBgImage;
393+
const canvasBgImageRepeat = appSettings?.gridBgImageRepeat || "no-repeat";
394+
const canvasBgImageSize = appSettings?.gridBgImageSize || "cover";
395+
const canvasBgImagePosition = appSettings?.gridBgImagePosition || "center";
396+
const canvasBgImageOrigin = appSettings?.gridBgImageOrigin || "padding-box";
397+
const canvasPaddingX = appSettings?.gridPaddingX ?? 0;
398+
const canvasPaddingY = appSettings?.gridPaddingY ?? 0;
399+
384400
// filter out hidden. unauthorised items filtered by server
385401
const filterItem = useCallback((item: LayoutMenuItemComp): boolean => {
386402
return !item.children.hidden.getView();
@@ -685,8 +701,25 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
685701
/>
686702
);
687703

704+
// Build canvas background style (color + optional image), driven by the
705+
// shared app-level Canvas Settings.
706+
const canvasBackgroundStyle: React.CSSProperties = {};
707+
if (canvasBg) {
708+
canvasBackgroundStyle.background = canvasBg;
709+
}
710+
if (canvasBgImage) {
711+
canvasBackgroundStyle.backgroundImage = `url('${canvasBgImage}')`;
712+
canvasBackgroundStyle.backgroundRepeat = canvasBgImageRepeat;
713+
canvasBackgroundStyle.backgroundSize = canvasBgImageSize;
714+
canvasBackgroundStyle.backgroundPosition = canvasBgImagePosition;
715+
canvasBackgroundStyle.backgroundOrigin = canvasBgImageOrigin;
716+
}
717+
688718
let content = (
689-
<Layout style={{ height: isPreview ? undefined : "100vh" }}>
719+
<Layout style={{
720+
height: isPreview ? undefined : "100vh",
721+
...canvasBackgroundStyle,
722+
}}>
690723
{(navPosition === 'top') && (
691724
<Header style={{ display: 'flex', alignItems: 'center', padding: 0 }}>
692725
{ navMenu }
@@ -697,7 +730,15 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
697730
{navMenu}
698731
</StyledSide>
699732
)}
700-
<ViewerMainContent $isPreview={isPreview}>{pageView}</ViewerMainContent>
733+
<ViewerMainContent
734+
$isPreview={isPreview}
735+
style={{
736+
padding: `${canvasPaddingY}px ${canvasPaddingX}px`,
737+
...canvasBackgroundStyle,
738+
}}
739+
>
740+
{pageView}
741+
</ViewerMainContent>
701742
{(navPosition === 'bottom') && (
702743
<Footer style={{ display: 'flex', alignItems: 'center', padding: 0 }}>
703744
{ navMenu }

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,6 +3791,7 @@ export const en = {
37913791
"inputUserDefinedPxValue": "Please Enter a Custom Pixel Value",
37923792
"maxWidthTip": "Max Width Should Be Greater Than or Equal to 350",
37933793
"themeSetting": "Applied Style Theme",
3794+
"themeSettings": "Theme Settings",
37943795
"themeSettingDefault": "Default",
37953796
"themeCreate": "Create Theme",
37963797
"appTitle": "Title",

client/packages/lowcoder/src/pages/editor/editorView.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ const aggregationSiderItems = [
340340
key: SiderKey.Setting,
341341
icon: <LeftSettingIcon />,
342342
},
343+
{
344+
key: SiderKey.Canvas,
345+
icon: <LeftColorPaletteIcon />,
346+
},
343347
{
344348
key: SiderKey.JS,
345349
icon: <LeftJSSettingIcon />,
@@ -725,14 +729,11 @@ function EditorView(props: EditorViewProps) {
725729
{menuKey === SiderKey.Canvas && (
726730
<SettingsDiv>
727731
<ScrollBar>
728-
{application &&
729-
!isAggregationApp(
730-
AppUILayoutType[application.applicationType]
731-
) && (
732-
<>
733-
{appSettingsComp.getPropertyView()}
734-
</>
735-
)}
732+
{application && (
733+
<>
734+
{appSettingsComp.getPropertyView()}
735+
</>
736+
)}
736737
</ScrollBar>
737738
</SettingsDiv>
738739
)}

0 commit comments

Comments
 (0)