diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..39649ae48
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "workbench.colorCustomizations": {
+ "activityBar.background": "#2A3012",
+ "titleBar.activeBackground": "#3B431A",
+ "titleBar.activeForeground": "#F9FAF2"
+ }
+}
\ No newline at end of file
diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json
index 43386ccf9..01733833e 100644
--- a/client/packages/lowcoder-comps/package.json
+++ b/client/packages/lowcoder-comps/package.json
@@ -1,15 +1,19 @@
{
"name": "lowcoder-comps",
- "version": "0.0.24",
+ "version": "0.0.26",
"type": "module",
"license": "MIT",
"dependencies": {
+ "@fullcalendar/adaptive": "^6.1.11",
"@fullcalendar/core": "^6.1.6",
"@fullcalendar/daygrid": "^6.1.6",
"@fullcalendar/interaction": "^6.1.6",
"@fullcalendar/list": "^6.1.9",
"@fullcalendar/moment": "^6.1.6",
"@fullcalendar/react": "^6.1.6",
+ "@fullcalendar/resource": "^6.1.11",
+ "@fullcalendar/resource-timegrid": "^6.1.11",
+ "@fullcalendar/resource-timeline": "^6.1.11",
"@fullcalendar/timegrid": "^6.1.6",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx
index 72f292ece..c65de7873 100644
--- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx
@@ -26,6 +26,10 @@ import { default as Form } from "antd/es/form";
import { default as Input } from "antd/es/input";
import { trans, getCalendarLocale } from "../../i18n/comps";
import { createRef, useContext, useRef, useState } from "react";
+import resourceTimelinePlugin from "@fullcalendar/resource-timeline";
+import resourceTimeGridPlugin from "@fullcalendar/resource-timegrid";
+import adaptivePlugin from "@fullcalendar/adaptive";
+
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
@@ -35,7 +39,8 @@ import allLocales from "@fullcalendar/core/locales-all";
import { EventContentArg, DateSelectArg } from "@fullcalendar/core";
import momentPlugin from "@fullcalendar/moment";
import {
- DefaultViewOptions,
+ DefaultWithFreeViewOptions,
+ DefaultWithPremiumViewOptions,
FirstDayOptions,
Wrapper,
Event,
@@ -52,13 +57,20 @@ import {
} from "./calendarConstants";
import dayjs from "dayjs";
+function filterViews() {}
+
const childrenMap = {
events: jsonValueExposingStateControl("events", defaultData),
onEvent: ChangeEventHandlerControl,
editable: withDefault(BoolControl, true),
defaultDate: withDefault(StringControl, "{{ new Date() }}"),
- defaultView: dropdownControl(DefaultViewOptions, "timeGridWeek"),
+ defaultFreeView: dropdownControl(DefaultWithFreeViewOptions, "timeGridWeek"),
+ defaultPremiumView: dropdownControl(
+ DefaultWithPremiumViewOptions,
+ "timeGridWeek"
+ ),
+
firstDay: dropdownControl(FirstDayOptions, "1"),
showEventTime: withDefault(BoolControl, true),
showWeekends: withDefault(BoolControl, true),
@@ -66,6 +78,7 @@ const childrenMap = {
dayMaxEvents: withDefault(NumberControl, 2),
eventMaxStack: withDefault(NumberControl, 0),
style: styleControl(CalendarStyle),
+ licenceKey: withDefault(StringControl, ""),
};
let CalendarBasicComp = (function () {
@@ -83,14 +96,17 @@ let CalendarBasicComp = (function () {
start: dayjs(item.start, DateParser).format(),
end: dayjs(item.end, DateParser).format(),
allDay: item.allDay,
- color: isValidColor(item.color || "") ? item.color : theme?.theme?.primary,
+ color: isValidColor(item.color || "")
+ ? item.color
+ : theme?.theme?.primary,
...(item.groupId ? { groupId: item.groupId } : null),
};
});
const {
defaultDate,
- defaultView,
+ defaultFreeView,
+ defaultPremiumView,
showEventTime,
showWeekends,
showAllDay,
@@ -99,13 +115,19 @@ let CalendarBasicComp = (function () {
style,
firstDay,
editable,
+ licenceKey,
} = props;
function renderEventContent(eventInfo: EventContentArg) {
const isList = eventInfo.view.type === "listWeek";
let sizeClass = "";
- if ([ViewType.WEEK, ViewType.DAY].includes(eventInfo.view.type as ViewType)) {
- const duration = dayjs(eventInfo.event.end).diff(dayjs(eventInfo.event.start), "minutes");
+ if (
+ [ViewType.WEEK, ViewType.DAY].includes(eventInfo.view.type as ViewType)
+ ) {
+ const duration = dayjs(eventInfo.event.end).diff(
+ dayjs(eventInfo.event.start),
+ "minutes"
+ );
if (duration <= 30 || eventInfo.event.allDay) {
sizeClass = "small";
} else if (duration <= 60) {
@@ -137,7 +159,9 @@ let CalendarBasicComp = (function () {
onClick={(e) => {
e.stopPropagation();
props.onEvent("change");
- const event = events.filter((item: EventType) => item.id !== eventInfo.event.id);
+ const event = events.filter(
+ (item: EventType) => item.id !== eventInfo.event.id
+ );
props.events.onChange(event);
}}
onMouseDown={(e) => {
@@ -195,7 +219,9 @@ let CalendarBasicComp = (function () {
};
const showModal = (event: EventType, ifEdit: boolean) => {
- const modalTitle = ifEdit ? trans("calendar.editEvent") : trans("calendar.creatEvent");
+ const modalTitle = ifEdit
+ ? trans("calendar.editEvent")
+ : trans("calendar.creatEvent");
form && form.setFieldsValue(event);
const eventId = editEvent.current?.id;
CustomModal.confirm({
@@ -209,14 +235,18 @@ let CalendarBasicComp = (function () {
}
name="id"
- rules={[{ required: true, message: trans("calendar.eventIdRequire") }]}
+ rules={[
+ { required: true, message: trans("calendar.eventIdRequire") },
+ ]}
>
@@ -239,9 +269,13 @@ let CalendarBasicComp = (function () {
form.submit();
return form.validateFields().then(() => {
const { id, groupId, color, title = "" } = form.getFieldsValue();
- const idExist = props.events.value.findIndex((item: EventType) => item.id === id);
+ const idExist = props.events.value.findIndex(
+ (item: EventType) => item.id === id
+ );
if (idExist > -1 && id !== eventId) {
- form.setFields([{ name: "id", errors: [trans("calendar.eventIdExist")] }]);
+ form.setFields([
+ { name: "id", errors: [trans("calendar.eventIdExist")] },
+ ]);
throw new Error();
}
if (ifEdit) {
@@ -287,6 +321,10 @@ let CalendarBasicComp = (function () {
} catch (error) {
initialDate = undefined;
}
+ let defaultView = defaultFreeView;
+ if (licenceKey != "") {
+ defaultView = defaultPremiumView;
+ }
return (
{
let left = 0;
@@ -319,15 +366,19 @@ let CalendarBasicComp = (function () {
}
} else {
if (info.allDay) {
- left = ele.offsetParent?.parentElement?.parentElement?.offsetLeft || 0;
+ left =
+ ele.offsetParent?.parentElement?.parentElement?.offsetLeft ||
+ 0;
} else {
left =
- ele.offsetParent?.parentElement?.parentElement?.parentElement?.offsetLeft || 0;
+ ele.offsetParent?.parentElement?.parentElement?.parentElement
+ ?.offsetLeft || 0;
}
}
setLeft(left);
}}
buttonText={buttonText}
+ schedulerLicenseKey={licenceKey}
views={views}
eventClassNames={() => (!showEventTime ? "no-time" : "")}
slotLabelFormat={slotLabelFormat}
@@ -346,7 +397,9 @@ let CalendarBasicComp = (function () {
eventContent={renderEventContent}
select={(info) => handleCreate(info)}
eventClick={(info) => {
- const event = events.find((item: EventType) => item.id === info.event.id);
+ const event = events.find(
+ (item: EventType) => item.id === info.event.id
+ );
editEvent.current = event;
setTimeout(() => {
editEvent.current = undefined;
@@ -385,10 +438,18 @@ let CalendarBasicComp = (function () {
);
})
.setPropertyViewFn((children) => {
+ let licence = children.licenceKey.getView();
return (
<>
- {children.events.propertyView({})}
- {children.onEvent.getPropertyView()}
+
+ {children.events.propertyView({})}
+
+
+ {children.licenceKey.propertyView({
+ label: trans("calendar.licence"),
+ })}
+ {children.onEvent.getPropertyView()}
+
{children.editable.propertyView({
label: trans("calendar.editable"),
@@ -397,10 +458,15 @@ let CalendarBasicComp = (function () {
label: trans("calendar.defaultDate"),
tooltip: trans("calendar.defaultDateTooltip"),
})}
- {children.defaultView.propertyView({
- label: trans("calendar.defaultView"),
- tooltip: trans("calendar.defaultViewTooltip"),
- })}
+ {licence == ""
+ ? children.defaultFreeView.propertyView({
+ label: trans("calendar.defaultView"),
+ tooltip: trans("calendar.defaultViewTooltip"),
+ })
+ : children.defaultPremiumView.propertyView({
+ label: trans("calendar.defaultView"),
+ tooltip: trans("calendar.defaultViewTooltip"),
+ })}
{children.firstDay.propertyView({
label: trans("calendar.startWeek"),
})}
@@ -424,8 +490,12 @@ let CalendarBasicComp = (function () {
tooltip: trans("calendar.eventMaxStackTooltip"),
})}
- {hiddenPropertyView(children)}
- {children.style.getPropertyView()}
+
+ {hiddenPropertyView(children)}
+
+
+ {children.style.getPropertyView()}
+
>
);
})
diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx
index bbc02cafd..394c0427a 100644
--- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx
@@ -205,9 +205,14 @@ export const Wrapper = styled.div<{
flex-direction: inherit;
}
.fc-day-today .fc-daygrid-day-number {
- background-color: ${(props) => props.$theme?.primary ? props.$theme.primary : props.$style.background};
+ background-color: ${(props) =>
+ props.$theme?.primary ? props.$theme.primary : props.$style.background};
color: ${(props) =>
- contrastText(props.$theme?.primary || "", props.$theme?.textDark || "#000000", props.$theme?.textLight || "#ffffff")};
+ contrastText(
+ props.$theme?.primary || "",
+ props.$theme?.textDark || "#000000",
+ props.$theme?.textLight || "#ffffff"
+ )};
}
.fc-daygrid-day-events {
padding: 1px 0 5px 0;
@@ -330,7 +335,8 @@ export const Wrapper = styled.div<{
height: 20px;
padding-left: 15px;
font-weight: 500;
- background-color: ${(props) => lightenColor(props.$style.background, 0.1)};
+ background-color: ${(props) =>
+ lightenColor(props.$style.background, 0.1)};
}
}
}
@@ -368,7 +374,7 @@ export const Wrapper = styled.div<{
}
&:hover {
.event-remove {
- opacity: ${(props) => props.$editable ? 1 : undefined};
+ opacity: ${(props) => (props.$editable ? 1 : undefined)};
}
}
}
@@ -398,7 +404,8 @@ export const Wrapper = styled.div<{
// border-radius, bg
.fc-theme-standard .fc-list {
background-color: ${(props) => props.$style.background};
- border-radius: ${(props) => `0 0 ${props.$style.radius} ${props.$style.radius}`};
+ border-radius: ${(props) =>
+ `0 0 ${props.$style.radius} ${props.$style.radius}`};
border-color: ${(props) => props.$style.border};
border-top-color: ${(props) =>
toHex(props.$style.border) === "#D7D9E0"
@@ -406,7 +413,8 @@ export const Wrapper = styled.div<{
: lightenColor(props.$style.border, 0.03)};
}
.fc-scrollgrid-liquid {
- border-radius: ${(props) => `0 0 ${props.$style.radius} ${props.$style.radius}`};
+ border-radius: ${(props) =>
+ `0 0 ${props.$style.radius} ${props.$style.radius}`};
overflow: hidden;
border-right-width: 1px;
border-bottom-width: 1px;
@@ -459,7 +467,8 @@ export const Wrapper = styled.div<{
margin-bottom: 0;
border: 1px solid ${(props) => props.$style.border};
border-bottom: none;
- border-radius: ${(props) => `${props.$style.radius} ${props.$style.radius} 0 0`};
+ border-radius: ${(props) =>
+ `${props.$style.radius} ${props.$style.radius} 0 0`};
background-color: ${(props) => props.$style.background};
}
.fc-toolbar-title {
@@ -488,7 +497,9 @@ export const Wrapper = styled.div<{
border-color: ${(props) =>
toHex(props.$style.headerBtnBackground) === "#FFFFFF"
? "#D7D9E0"
- : backgroundToBorder(genHoverColor(props.$style.headerBtnBackground))};
+ : backgroundToBorder(
+ genHoverColor(props.$style.headerBtnBackground)
+ )};
}
}
&:not(:disabled):focus {
@@ -500,7 +511,8 @@ export const Wrapper = styled.div<{
&,
&:hover {
background-color: ${(props) => props.$style.headerBtnBackground};
- border-color: ${(props) => backgroundToBorder(props.$style.headerBtnBackground)};
+ border-color: ${(props) =>
+ backgroundToBorder(props.$style.headerBtnBackground)};
color: ${(props) =>
toHex(props.$style.btnText) === "#222222"
? "#B8B9BF"
@@ -518,7 +530,8 @@ export const Wrapper = styled.div<{
font-size: 14px;
margin-left: 8px;
background-color: ${(props) => props.$style.headerBtnBackground};
- border-color: ${(props) => backgroundToBorder(props.$style.headerBtnBackground)};
+ border-color: ${(props) =>
+ backgroundToBorder(props.$style.headerBtnBackground)};
color: ${(props) => props.$style.btnText};
&.fc-today-button {
min-width: 52px;
@@ -538,8 +551,8 @@ export const Wrapper = styled.div<{
toHex(props.$style.headerBtnBackground) === "#FFFFFF"
? "#EFEFF1"
: isDarkColor(props.$style.headerBtnBackground)
- ? props.$style.headerBtnBackground
- : darkenColor(props.$style.headerBtnBackground, 0.1)};
+ ? props.$style.headerBtnBackground
+ : darkenColor(props.$style.headerBtnBackground, 0.1)};
border-radius: 4px;
margin-left: 16px;
.fc-button-primary {
@@ -585,10 +598,13 @@ export const Wrapper = styled.div<{
}
.fc-day-today.fc-col-header-cell {
background-color: ${(props) =>
- isDarkColor(props.$style.background) ? "#ffffff19" : toHex(props.$theme?.primary!) + "19"};
+ isDarkColor(props.$style.background)
+ ? "#ffffff19"
+ : toHex(props.$theme?.primary!) + "19"};
a {
color: ${(props) =>
- !isDarkColor(props.$style.background) && darkenColor(props.$theme?.primary!, 0.1)};
+ !isDarkColor(props.$style.background) &&
+ darkenColor(props.$theme?.primary!, 0.1)};
}
}
.fc-col-header-cell-cushion {
@@ -649,7 +665,8 @@ export const Event = styled.div<{
box-shadow: ${(props) => !props.isList && "0 0 5px 0 rgba(0, 0, 0, 0.15)"};
border: 1px solid ${(props) => props.$style.border};
display: ${(props) => props.isList && "flex"};
- background-color: ${(props) => !props.isList && lightenColor(props.$style.background, 0.1)};
+ background-color: ${(props) =>
+ !props.isList && lightenColor(props.$style.background, 0.1)};
overflow: hidden;
font-size: 13px;
line-height: 19px;
@@ -671,7 +688,9 @@ export const Event = styled.div<{
.event-time {
color: ${(props) =>
!props.isList &&
- (isDarkColor(props.$style.text) ? lightenColor(props.$style.text, 0.2) : props.$style.text)};
+ (isDarkColor(props.$style.text)
+ ? lightenColor(props.$style.text, 0.2)
+ : props.$style.text)};
margin-left: 15px;
white-space: pre-wrap;
margin-top: 2px;
@@ -710,14 +729,15 @@ export const Event = styled.div<{
}
}
&.past {
- background-color: ${(props) => isDarkColor(props.$style.background) && props.$style.background};
+ background-color: ${(props) =>
+ isDarkColor(props.$style.background) && props.$style.background};
&::before {
background-color: ${(props) =>
toHex(props.$style.text) === "#3C3C3C"
? "#8B8FA3"
: isDarkColor(props.$style.text)
- ? lightenColor(props.$style.text, 0.3)
- : props.$style.text};
+ ? lightenColor(props.$style.text, 0.3)
+ : props.$style.text};
}
&::before,
.event-title,
@@ -758,9 +778,34 @@ export enum ViewType {
WEEK = "timeGridWeek",
DAY = "timeGridDay",
LIST = "listWeek",
+ TIMEGRID = "timeGridDay",
}
-export const DefaultViewOptions = [
+
+export const DefaultWithPremiumViewOptions = [
+ {
+ label: trans("calendar.month"),
+ value: "dayGridMonth",
+ },
+ {
+ label: trans("calendar.week"),
+ value: "timeGridWeek",
+ },
+ {
+ label: trans("calendar.timeline"),
+ value: "resourceTimeline",
+ },
+ {
+ label: trans("calendar.day"),
+ value: "timeGridDay",
+ },
+ {
+ label: trans("calendar.list"),
+ value: "listWeek",
+ },
+] as const;
+
+export const DefaultWithFreeViewOptions = [
{
label: trans("calendar.month"),
value: "dayGridMonth",
@@ -815,7 +860,7 @@ export const defaultData = [
id: "1",
title: "Coding",
start: dayjs().hour(10).minute(0).second(0).format(DATE_TIME_FORMAT),
- end: dayjs().hour(11).minute(30).second(0).format(DATE_TIME_FORMAT),
+ end: dayjs().hour(12).minute(30).second(0).format(DATE_TIME_FORMAT),
color: "#079968",
},
{
@@ -831,6 +876,7 @@ export const buttonText = {
today: trans("calendar.today"),
month: trans("calendar.month"),
week: trans("calendar.week"),
+ timeline: trans("calendar.timeline"),
day: trans("calendar.day"),
list: trans("calendar.list"),
};
@@ -843,7 +889,9 @@ export const headerToolbar = {
const weekHeadContent = (info: DayHeaderContentArg) => {
const text = info.text.split(" ");
return {
- html: `
+ html: `
${text[0]}
${text[1]}
`,
diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
index 9cac1339b..18c022991 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -1,145 +1,153 @@
export const en = {
- "chart": {
- "delete": "Delete",
- "data": "Data",
- "mode": "Mode",
- "config": "Configuration",
- "UIMode": "UI Mode",
- "chartType": "Chart Type",
- "xAxis": "X-axis",
- "chartSeries": "Chart Series",
- "customSeries": "Custom Series",
- "add": "Add",
- "confirmDelete": "Confirm Delete: ",
- "seriesName": "Series Name",
- "dataColumns": "Data Columns",
- "title": "Title",
- "xAxisDirection": "X-axis Direction",
- "xAxisName": "X-axis Name",
- "xAxisType": "X-axis Type",
- "xAxisTypeTooltip": "Automatically detected based on X-axis data. For type description, refer to: ",
- "logBase": "Log Base",
- "yAxisName": "Y-axis Name",
- "yAxisType": "Y-axis Type",
- "yAxisDataFormat": "Y-axis Data Type",
- "yAxisDataFormatTooltip": "Indicates the value of each coordinate. Example: '{{value * 100 + \"%\"}}'",
- "basicBar": "Basic Bar",
- "stackedBar": "Stacked Bar",
- "barType": "Bar Chart Type",
- "categoryAxis": "Category Axis",
- "valueAxis": "Value Axis",
- "timeAxis": "Time Axis",
- "logAxis": "Log Axis",
- "auto": "Default",
- "legendPosition": "Legend Position",
- "basicLine": "Basic Line",
- "stackedLine": "Stacked Line",
- "areaLine": "Area Line",
- "smooth": "Smooth Curve",
- "lineType": "Line Chart Type",
- "basicPie": "Basic Pie",
- "doughnutPie": "Doughnut Pie",
- "rosePie": "Rose Pie",
- "pieType": "Pie Chart Type",
- "spending": "Spending",
- "budget": "Budget",
- "bar": "Bar Chart",
- "line": "Line Chart",
- "scatter": "Scatter Chart",
- "pie": "Pie Chart",
- "horizontal": "Horizontal",
- "vertical": "Vertical",
- "noData": "No Data",
- "unknown": "Unknown",
- "select": "Select",
- "unSelect": "Unselect",
- "echartsOptionLabel": "Option",
- "echartsOptionTooltip": "ECharts Option",
- "echartsOptionExamples": "ECharts Examples",
- "echartsMapOptionTooltip": "ECharts Map Option",
- "echartsMapOptionExamples": "ECharts Map Examples",
- "selectDesc": "Triggered when a user selects part of the data in the chart",
- "unselectDesc": "Triggered when a user unselects part of the data in the chart",
- "selectedPointsDesc": "Selected Points",
- "dataDesc": "JSON Data for the Chart",
- "titleDesc": "Current Chart Title",
- "scatterShape": "Scatter Shape",
- "circle": "Circle",
- "rect": "Rectangle",
- "triangle": "Triangle",
- "diamond": "Diamond",
- "pin": "Pin",
- "arrow": "Arrow",
- "pointColorLabel": "Point Color",
- "pointColorTooltip": "Set point color based on series name and value. Variables: seriesName, value. Example: '{{value < 25000 ? \"red\" : \"green\"}}'",
- "mapReady": "Map Ready",
- "mapReadyDesc": "Triggers when the map is ready",
- "zoomLevelChange": "Zoom Level Change",
- "zoomLevelChangeDesc": "Triggers when the map zoom level changes",
- "centerPositionChange": "Center Position Change",
- "centerPositionChangeDesc": "Triggers when the map center position changes"
+ chart: {
+ delete: "Delete",
+ data: "Data",
+ mode: "Mode",
+ config: "Configuration",
+ UIMode: "UI Mode",
+ chartType: "Chart Type",
+ xAxis: "X-axis",
+ chartSeries: "Chart Series",
+ customSeries: "Custom Series",
+ add: "Add",
+ confirmDelete: "Confirm Delete: ",
+ seriesName: "Series Name",
+ dataColumns: "Data Columns",
+ title: "Title",
+ xAxisDirection: "X-axis Direction",
+ xAxisName: "X-axis Name",
+ xAxisType: "X-axis Type",
+ xAxisTypeTooltip:
+ "Automatically detected based on X-axis data. For type description, refer to: ",
+ logBase: "Log Base",
+ yAxisName: "Y-axis Name",
+ yAxisType: "Y-axis Type",
+ yAxisDataFormat: "Y-axis Data Type",
+ yAxisDataFormatTooltip:
+ "Indicates the value of each coordinate. Example: '{{value * 100 + \"%\"}}'",
+ basicBar: "Basic Bar",
+ stackedBar: "Stacked Bar",
+ barType: "Bar Chart Type",
+ categoryAxis: "Category Axis",
+ valueAxis: "Value Axis",
+ timeAxis: "Time Axis",
+ logAxis: "Log Axis",
+ auto: "Default",
+ legendPosition: "Legend Position",
+ basicLine: "Basic Line",
+ stackedLine: "Stacked Line",
+ areaLine: "Area Line",
+ smooth: "Smooth Curve",
+ lineType: "Line Chart Type",
+ basicPie: "Basic Pie",
+ doughnutPie: "Doughnut Pie",
+ rosePie: "Rose Pie",
+ pieType: "Pie Chart Type",
+ spending: "Spending",
+ budget: "Budget",
+ bar: "Bar Chart",
+ line: "Line Chart",
+ scatter: "Scatter Chart",
+ pie: "Pie Chart",
+ horizontal: "Horizontal",
+ vertical: "Vertical",
+ noData: "No Data",
+ unknown: "Unknown",
+ select: "Select",
+ unSelect: "Unselect",
+ echartsOptionLabel: "Option",
+ echartsOptionTooltip: "ECharts Option",
+ echartsOptionExamples: "ECharts Examples",
+ echartsMapOptionTooltip: "ECharts Map Option",
+ echartsMapOptionExamples: "ECharts Map Examples",
+ selectDesc: "Triggered when a user selects part of the data in the chart",
+ unselectDesc:
+ "Triggered when a user unselects part of the data in the chart",
+ selectedPointsDesc: "Selected Points",
+ dataDesc: "JSON Data for the Chart",
+ titleDesc: "Current Chart Title",
+ scatterShape: "Scatter Shape",
+ circle: "Circle",
+ rect: "Rectangle",
+ triangle: "Triangle",
+ diamond: "Diamond",
+ pin: "Pin",
+ arrow: "Arrow",
+ pointColorLabel: "Point Color",
+ pointColorTooltip:
+ 'Set point color based on series name and value. Variables: seriesName, value. Example: \'{{value < 25000 ? "red" : "green"}}\'',
+ mapReady: "Map Ready",
+ mapReadyDesc: "Triggers when the map is ready",
+ zoomLevelChange: "Zoom Level Change",
+ zoomLevelChangeDesc: "Triggers when the map zoom level changes",
+ centerPositionChange: "Center Position Change",
+ centerPositionChangeDesc: "Triggers when the map center position changes",
},
- "imageEditor": {
- "defaultSrc": "",
- "save": "Save",
- "saveDesc": "Save Image",
- "src": "Image Source",
- "name": "Image Name",
- "buttonText": "Button Text",
- "srcDesc": "Image Source",
- "nameDesc": "Image Name",
- "dataURIDesc": "Image Data URI",
- "dataDesc": "Image Data",
- "buttonTextDesc": "Button Text"
+ imageEditor: {
+ defaultSrc: "",
+ save: "Save",
+ saveDesc: "Save Image",
+ src: "Image Source",
+ name: "Image Name",
+ buttonText: "Button Text",
+ srcDesc: "Image Source",
+ nameDesc: "Image Name",
+ dataURIDesc: "Image Data URI",
+ dataDesc: "Image Data",
+ buttonTextDesc: "Button Text",
},
- "calendar": {
- "events": "Events Data",
- "editable": "Editable",
- "defaultDate": "Default Date",
- "defaultDateTooltip": "Initial display date of the calendar",
- "defaultView": "Default View",
- "defaultViewTooltip": "Initial view of the calendar",
- "showEventTime": "Show Event Times",
- "showEventTimeTooltip": "Display event time text",
- "showWeekends": "Show Weekends",
- "showAllDay": "Show All-Day",
- "showAllDayTooltip": "Display all-day slot in week and day views",
- "dayMaxEvents": "Day Max Events",
- "dayMaxEventsTooltip": "Max events per day in month view, 0 for cell height limit",
- "eventMaxStack": "Event Max Stack",
- "eventMaxStackTooltip": "Max events to stack horizontally in week and day views, 0 for no limit",
- "selectInterval": "Selected Interval",
- "selectEvent": "Selected Event",
- "changeSet": "Changed Event Object",
- "headerBtnBackground": "Button Background",
- "btnText": "Button Text",
- "title": "Title",
- "selectBackground": "Selected Background",
- "today": "Today",
- "month": "Month",
- "week": "Week",
- "day": "Day",
- "list": "List",
- "monday": "Monday",
- "tuesday": "Tuesday",
- "wednesday": "Wednesday",
- "thursday": "Thursday",
- "friday": "Friday",
- "saturday": "Saturday",
- "sunday": "Sunday",
- "startWeek": "Start From",
- "creatEvent": "Create Event",
- "editEvent": "Edit Event",
- "eventName": "Event Name",
- "eventColor": "Event Color",
- "eventGroupId": "Group ID",
- "groupIdTooltip": "Group ID groups events for drag and resize together.",
- "more": "More",
- "allDay": "All Day",
- "eventNameRequire": "Enter Event Name",
- "eventId": "Event ID",
- "eventIdRequire": "Enter Event ID",
- "eventIdTooltip": "Unique ID for each event",
- "eventIdExist": "ID Exists"
+ calendar: {
+ events: "Events Data",
+ editable: "Editable",
+ licence: "Licence Key",
+ defaultDate: "Default Date",
+ defaultDateTooltip: "Initial display date of the calendar",
+ defaultView: "Default View",
+ defaultViewTooltip: "Initial view of the calendar",
+ showEventTime: "Show Event Times",
+ showEventTimeTooltip: "Display event time text",
+ showWeekends: "Show Weekends",
+ showAllDay: "Show All-Day",
+ showAllDayTooltip: "Display all-day slot in week and day views",
+ dayMaxEvents: "Day Max Events",
+ dayMaxEventsTooltip:
+ "Max events per day in month view, 0 for cell height limit",
+ eventMaxStack: "Event Max Stack",
+ eventMaxStackTooltip:
+ "Max events to stack horizontally in week and day views, 0 for no limit",
+ selectInterval: "Selected Interval",
+ selectEvent: "Selected Event",
+ changeSet: "Changed Event Object",
+ headerBtnBackground: "Button Background",
+ btnText: "Button Text",
+ title: "Title",
+ selectBackground: "Selected Background",
+ today: "Today",
+ month: "Month",
+ week: "Week",
+ day: "Day",
+ list: "List",
+ timeline: "TimeLine", //added by fred
+ monday: "Monday",
+ tuesday: "Tuesday",
+ wednesday: "Wednesday",
+ thursday: "Thursday",
+ friday: "Friday",
+ saturday: "Saturday",
+ sunday: "Sunday",
+ startWeek: "Start From",
+ creatEvent: "Create Event",
+ editEvent: "Edit Event",
+ eventName: "Event Name",
+ eventColor: "Event Color",
+ eventGroupId: "Group ID",
+ groupIdTooltip: "Group ID groups events for drag and resize together.",
+ more: "More",
+ allDay: "All Day",
+ eventNameRequire: "Enter Event Name",
+ eventId: "Event ID",
+ eventIdRequire: "Enter Event ID",
+ eventIdTooltip: "Unique ID for each event",
+ eventIdExist: "ID Exists",
},
};
diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts
index 6fdc18332..99c7b553c 100644
--- a/client/packages/lowcoder-design/src/icons/index.ts
+++ b/client/packages/lowcoder-design/src/icons/index.ts
@@ -171,22 +171,6 @@ export { ReactComponent as videoPlayTriangle } from "./icon-video-play-triangle.
export { ReactComponent as DrawerCompIcon } from "./icon-drawer.svg";
export { ReactComponent as LeftMeetingIcon } from "./icon-left-comp-video.svg";
export { ReactComponent as PlusIcon } from "./icon-plus.svg";
-export { ReactComponent as HomeIcon } from "./icon-application-home.svg";
-export { ReactComponent as HomeModuleIcon } from "./icon-application-module.svg";
-export { ReactComponent as HomeQueryLibraryIcon } from "./icon-application-query-library.svg";
-export { ReactComponent as HomeDataSourceIcon } from "./icon-application-datasource.svg";
-export { ReactComponent as RecyclerIcon } from "./icon-application-recycler.svg";
-export { ReactComponent as MarketplaceIcon } from "./icon-application-marketplace.svg";
-export { ReactComponent as LowcoderMarketplaceIcon } from "./icon-lowcoder-marketplace.svg";
-export { ReactComponent as HomeActiveIcon } from "./icon-application-home-active.svg";
-export { ReactComponent as HomeModuleActiveIcon } from "./icon-application-module-active.svg";
-export { ReactComponent as HomeQueryLibraryActiveIcon } from "./icon-application-query-library-active.svg";
-export { ReactComponent as HomeDataSourceActiveIcon } from "./icon-application-datasource-active.svg";
-export { ReactComponent as RecyclerActiveIcon } from "./icon-application-recycler-active.svg";
-export { ReactComponent as MarketplaceActiveIcon } from "./icon-application-marketplace-active.svg";
-export { ReactComponent as LowcoderMarketplaceActiveIcon } from "./icon-lowcoder-marketplace-active.svg";
-export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg";
-export { ReactComponent as HomeSettingIcon } from "./icon-application-setting.svg";
export { ReactComponent as FolderIcon } from "./icon-application-folder.svg";
export { ReactComponent as AllTypesIcon } from "./icon-application-all.svg";
export { ReactComponent as InviteUserIcon } from "./icon-application-invite-user.svg";
@@ -314,6 +298,23 @@ export { ReactComponent as LeftShow } from "./remix/eye-off-line.svg";
export { ReactComponent as LeftHide } from "./remix/eye-line.svg";
export { ReactComponent as LeftLock } from "./remix/lock-line.svg";
export { ReactComponent as LeftUnlock } from "./remix/lock-unlock-line.svg";
+export { ReactComponent as UserGroupIcon } from "./remix/group-line.svg";
+export { ReactComponent as UserIcon } from "./remix/user-line.svg";
+export { ReactComponent as UserAddIcon } from "./remix/user-add-line.svg";
+export { ReactComponent as UserDeleteIcon } from "./remix/user-unfollow-line.svg";
+export { ReactComponent as UserShieldIcon } from "./remix/shield-user-line.svg";
+export { ReactComponent as ThemeIcon } from "./remix/palette-line.svg";
+export { ReactComponent as AppsIcon } from "./remix/apps-2-line.svg";
+export { ReactComponent as WorkspacesIcon } from "./remix/hotel-line.svg";
+
+export { ReactComponent as HomeIcon } from "./remix/home-3-line.svg";
+export { ReactComponent as HomeModuleIcon } from "./remix/focus-mode.svg";
+export { ReactComponent as HomeQueryLibraryIcon } from "./remix/braces-line.svg";
+export { ReactComponent as HomeDataSourceIcon } from "./remix/database-2-line.svg";
+export { ReactComponent as RecyclerIcon } from "./remix/delete-bin-line.svg";
+export { ReactComponent as MarketplaceIcon } from "./icon-application-marketplace.svg";
+export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg";
+export { ReactComponent as HomeSettingIcon } from "./remix/settings-4-line.svg";
// new
diff --git a/client/packages/lowcoder/src/components/layout/SubSideBar.tsx b/client/packages/lowcoder/src/components/layout/SubSideBar.tsx
index c8af9a5e6..097f1fb38 100644
--- a/client/packages/lowcoder/src/components/layout/SubSideBar.tsx
+++ b/client/packages/lowcoder/src/components/layout/SubSideBar.tsx
@@ -2,8 +2,8 @@ import { PropsWithChildren } from "react";
import styled from "styled-components";
const Wrapper = styled.div`
- min-width: 232px;
- width: 232px;
+ min-width: 280px;
+ width: 280px;
height: 100%;
background: #ffffff;
border-right: 1px solid #f0f0f0;
@@ -17,6 +17,7 @@ const Wrapper = styled.div`
color: #222222;
margin: 0 0 20px 20px;
}
+
.ant-menu-inline .ant-menu-item {
margin: 4px 0;
padding: 10px 20px !important;
diff --git a/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx b/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
index 9fa023cae..1782120df 100644
--- a/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
@@ -1,7 +1,7 @@
import { ThemeDetail, ThemeType } from "api/commonSettingApi";
import { RecordConstructorToComp } from "lowcoder-core";
import { dropdownInputSimpleControl } from "comps/controls/dropdownInputSimpleControl";
-import { MultiCompBuilder, valueComp } from "comps/generators";
+import { MultiCompBuilder, valueComp, withDefault } from "comps/generators";
import { AddIcon, Dropdown } from "lowcoder-design";
import { EllipsisSpan } from "pages/setting/theme/styledComponents";
import { useEffect } from "react";
@@ -14,6 +14,10 @@ import { default as Divider } from "antd/es/divider";
import { THEME_SETTING } from "constants/routesURL";
import { CustomShortcutsComp } from "./customShortcutsComp";
import { DEFAULT_THEMEID } from "comps/utils/themeUtil";
+import { StringControl } from "comps/controls/codeControl";
+import { IconControl } from "comps/controls/iconControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { ApplicationCategoriesEnum } from "constants/applicationConstants";
const TITLE = trans("appSetting.title");
const USER_DEFINE = "__USER_DEFINE";
@@ -92,9 +96,37 @@ const SettingsStyled = styled.div`
`;
const DivStyled = styled.div`
- div {
- width: 100%;
- display: block;
+ > div {
+ flex-wrap: wrap;
+ margin-bottom: 12px;
+
+ > div {
+ width: 100%;
+ display: block;
+ }
+
+ > div:first-child {
+ margin-bottom: 6px;
+ }
+
+ .tooltipLabel {
+ white-space: nowrap;
+ }
+
+ }
+ // custom styles for icon selector
+ .app-icon {
+ > div {
+ margin-bottom: 0;
+
+ > div:first-child {
+ margin-bottom: 6px;
+ }
+ > div:nth-child(2) {
+ width: 88%;
+ display: inline-block;
+ }
+ }
}
`;
@@ -134,7 +166,22 @@ const DividerStyled = styled(Divider)`
border-color: #e1e3eb;
`;
+type AppCategoriesEnumKey = keyof typeof ApplicationCategoriesEnum
+const AppCategories = Object.keys(ApplicationCategoriesEnum).map(
+ (cat) => {
+ const value = ApplicationCategoriesEnum[cat as AppCategoriesEnumKey];
+ return {
+ label: value,
+ value: cat
+ }
+ }
+)
+
const childrenMap = {
+ title: withDefault(StringControl, ''),
+ description: withDefault(StringControl, ''),
+ icon: IconControl,
+ category: dropdownControl(AppCategories, ApplicationCategoriesEnum.BUSINESS),
maxWidth: dropdownInputSimpleControl(OPTIONS, USER_DEFINE, "1920"),
themeId: valueComp(DEFAULT_THEMEID),
customShortcuts: CustomShortcutsComp,
@@ -146,7 +193,16 @@ type ChildrenInstance = RecordConstructorToComp & {
};
function AppSettingsModal(props: ChildrenInstance) {
- const { themeList, defaultTheme, themeId, maxWidth } = props;
+ const {
+ themeList,
+ defaultTheme,
+ themeId,
+ maxWidth,
+ title,
+ description,
+ icon,
+ category,
+ } = props;
const THEME_OPTIONS = themeList?.map((theme) => ({
label: theme.name,
value: theme.id + "",
@@ -182,6 +238,23 @@ function AppSettingsModal(props: ChildrenInstance) {
{TITLE}
+ {title.propertyView({
+ label: trans("appSetting.appTitle"),
+ placeholder: trans("appSetting.appTitle")
+ })}
+ {description.propertyView({
+ label: trans("appSetting.appDescription"),
+ placeholder: trans("appSetting.appDescription")
+ })}
+ {category.propertyView({
+ label: trans("appSetting.appCategory"),
+ })}
+
+ {icon.propertyView({
+ label: trans("icon"),
+ tooltip: trans("aggregation.iconTooltip"),
+ })}
+
{maxWidth.propertyView({
dropdownLabel: trans("appSetting.canvasMaxWidth"),
inputLabel: trans("appSetting.userDefinedMaxWidth"),
@@ -189,9 +262,6 @@ function AppSettingsModal(props: ChildrenInstance) {
placement: "bottom",
min: 350,
lastNode: {trans("appSetting.maxWidthTip")},
- labelStyle: {marginBottom: "8px"},
- dropdownStyle: {marginBottom: "12px"},
- inputStyle: {marginBottom: "12px"}
})}
}
preNode={() => (
<>
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
index e770014f7..8ebcd250c 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
@@ -24,6 +24,8 @@ export function getButtonStyle(buttonStyle: ButtonStyleType) {
font-weight: ${buttonStyle.textWeight};
font-family: ${buttonStyle.fontFamily};
font-style: ${buttonStyle.fontStyle};
+ text-transform:${buttonStyle.textTransform};
+ text-decoration:${buttonStyle.textDecoration};
background-color: ${buttonStyle.background};
border-radius: ${buttonStyle.radius};
margin: ${buttonStyle.margin};
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
index 0f83804f7..3a00a887d 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
@@ -25,8 +25,10 @@ import {
const StyledDropdownButton = styled(DropdownButton)`
width: 100%;
+
.ant-btn-group {
width: 100%;
+
}
`;
@@ -34,6 +36,11 @@ const LeftButtonWrapper = styled.div<{ $buttonStyle: ButtonStyleType }>`
width: calc(100%);
${(props) => `margin: ${props.$buttonStyle.margin};`}
margin-right: 0;
+ .ant-btn span {
+ ${(props) => `text-decoration: ${props.$buttonStyle.textDecoration};`}
+ ${(props) => `font-family: ${props.$buttonStyle.fontFamily};`}
+ }
+
.ant-btn {
${(props) => getButtonStyle(props.$buttonStyle)}
margin: 0 !important;
@@ -41,14 +48,18 @@ const LeftButtonWrapper = styled.div<{ $buttonStyle: ButtonStyleType }>`
&.ant-btn-default {
margin: 0 !important;
${(props) => `border-radius: ${props.$buttonStyle.radius} 0 0 ${props.$buttonStyle.radius};`}
+ ${(props) => `text-transform: ${props.$buttonStyle.textTransform};`}
+ ${(props) => `font-weight: ${props.$buttonStyle.textWeight};`}
}
${(props) => `background-color: ${props.$buttonStyle.background};`}
${(props) => `color: ${props.$buttonStyle.text};`}
${(props) => `padding: ${props.$buttonStyle.padding};`}
${(props) => `font-size: ${props.$buttonStyle.textSize};`}
${(props) => `font-style: ${props.$buttonStyle.fontStyle};`}
+
width: 100%;
}
+
`;
const RightButtonWrapper = styled.div<{ $buttonStyle: ButtonStyleType }>`
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
index cbd5b26b2..274a29e13 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
@@ -34,6 +34,8 @@ const Link = styled(Button) <{ $style: LinkStyleType }>`
font-weight:${props.$style.textWeight};
border: ${props.$style.borderWidth} solid ${props.$style.border};
border-radius:${props.$style.radius ? props.$style.radius:'0px'};
+ text-transform:${props.$style.textTransform ? props.$style.textTransform:''};
+ text-decoration:${props.$style.textDecoration ? props.$style.textDecoration:''} !important;
background-color: ${props.$style.background};
&:hover {
color: ${props.$style.hoverText} !important;
diff --git a/client/packages/lowcoder/src/comps/comps/dividerComp.tsx b/client/packages/lowcoder/src/comps/comps/dividerComp.tsx
index 032807de2..906db77c3 100644
--- a/client/packages/lowcoder/src/comps/comps/dividerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/dividerComp.tsx
@@ -30,6 +30,8 @@ const StyledDivider = styled(Divider) `
font-size: ${(props) => props.$style.textSize};
font-weight: ${(props) => props.$style.textWeight};
font-family: ${(props) => props.$style.fontFamily};
+ text-transform:${(props)=>props.$style.textTransform};
+ text-decoration:${(props)=>props.$style.textDecoration};
font-style:${(props) => props.$style.fontStyle}
}
min-width: 0;
diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx
index d2db61596..c04c5bbb3 100644
--- a/client/packages/lowcoder/src/comps/comps/iconComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx
@@ -144,3 +144,4 @@ IconBasicComp = class extends IconBasicComp {
export const IconComp = withExposingConfigs(IconBasicComp, [
NameConfigHidden,
]);
+
diff --git a/client/packages/lowcoder/src/comps/comps/multiIconDisplay.tsx b/client/packages/lowcoder/src/comps/comps/multiIconDisplay.tsx
new file mode 100644
index 000000000..cccb2a1fc
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/multiIconDisplay.tsx
@@ -0,0 +1,88 @@
+
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { findIconDefinition, library } from '@fortawesome/fontawesome-svg-core';
+import { fas } from '@fortawesome/free-solid-svg-icons';
+import { far } from '@fortawesome/free-regular-svg-icons';
+import * as AntdIcons from '@ant-design/icons';
+
+library.add(far,fas);
+
+function parseIconIdentifier(identifier: string) {
+ if (identifier.startsWith('/icon:antd/')) {
+ let name = identifier.split('/')[2];
+ return { type: 'antd', name };
+ }
+ else if (identifier.startsWith('/icon:solid/') || identifier.startsWith('/icon:regular/')) {
+ const [style, name] = identifier.substring(6).split('/');
+ return { type: 'fontAwesome', style, name };
+ }
+ else if (identifier.startsWith('data:image')) {
+ return { type: 'base64', data: identifier, name: "" };
+ }
+ else if (identifier.startsWith('http')) {
+ return { type: 'url', url: identifier, name: "" };
+ }
+ else {
+ return { type: 'unknown', name: "" };
+ }
+}
+
+interface IconProps {
+ identifier: string;
+ width?: string;
+ height?: string;
+ style?: React.CSSProperties;
+}
+
+const convertToCamelCase = (name: string) => {
+ return name.replace(/(-\w)/g, (match) => match[1].toUpperCase());
+}
+
+const appendStyleSuffix = (name: string) => {
+ if (name.endsWith('outlined')) {
+ return name.replace('outlined', 'Outlined');
+ } else if (name.endsWith('filled')) {
+ return name.replace('filled', 'Filled');
+ } else if (name.endsWith('twotone')) {
+ return name.replace('twotone', 'TwoTone');
+ }
+ return name;
+}
+
+// Multi icon Display Component
+
+const baseMultiIconDisplay: React.FC = ({ identifier, width = '24px', height = '24px', style }) => {
+
+ const iconData = parseIconIdentifier(identifier);
+
+ if (iconData.type === 'fontAwesome') {
+ const prefix = iconData.style === 'solid' ? 'fas' : 'far'; // 'fas' for solid, 'far' for regular
+ // Find the icon definition using prefix and iconName
+ const iconLookup = findIconDefinition({ prefix: prefix as any, iconName: iconData.name as any });
+
+ if (!iconLookup) {
+ console.error(`Icon ${iconData.name} with prefix ${prefix} not found`);
+ return null;
+ }
+ return ;
+ }
+ else if (iconData.type === 'antd') {
+ let iconName = convertToCamelCase(iconData.name);
+ iconName = appendStyleSuffix(iconName);
+ iconName = iconName.charAt(0).toUpperCase() + iconName.slice(1);
+ const AntdIcon = (AntdIcons as any)[iconName];
+ if (!AntdIcon) {
+ console.error(`ANTd Icon ${iconData.name} not found`);
+ return null;
+ }
+ return ;
+ }
+ else if (iconData.type === 'url' || iconData.type === 'base64') {
+ return
;
+ }
+ else {
+ return null; // Unknown type
+ }
+};
+
+export const MultiIconDisplay = baseMultiIconDisplay;
\ No newline at end of file
diff --git a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
index 44b782b47..25882da30 100644
--- a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
@@ -52,6 +52,8 @@ const Item = styled.div<{
$textSize: string;
$margin: string;
$padding: string;
+ $textTransform:string;
+ $textDecoration:string;
}>`
height: 30px;
line-height: 30px;
@@ -61,6 +63,8 @@ const Item = styled.div<{
font-family:${(props) => (props.$fontFamily ? props.$fontFamily : 'sans-serif')};
font-style:${(props) => (props.$fontStyle ? props.$fontStyle : 'normal')};
font-size:${(props) => (props.$textSize ? props.$textSize : '14px')};
+ text-transform:${(props) => (props.$textTransform ? props.$textTransform : '')};
+ text-decoration:${(props) => (props.$textDecoration ? props.$textDecoration : '')};
margin:${(props) => props.$margin ? props.$margin : '0px'};
&:hover {
@@ -161,6 +165,8 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
$textWeight={props.style.textWeight}
$textSize={props.style.textSize}
$padding={props.style.padding}
+ $textTransform={props.style.textTransform}
+ $textDecoration={props.style.textDecoration}
$margin={props.style.margin}
onClick={() => onEvent("click")}
>
diff --git a/client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx b/client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx
index 24038fb45..93011ee73 100644
--- a/client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx
+++ b/client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx
@@ -1,11 +1,18 @@
import { NPM_PLUGIN_ASSETS_BASE_URL } from "constants/npmPlugins";
import { trans } from "i18n";
import { CompConstructor } from "lowcoder-core";
-import { RemoteCompInfo, RemoteCompLoader, RemoteCompSource } from "types/remoteComp";
+import {
+ RemoteCompInfo,
+ RemoteCompLoader,
+ RemoteCompSource,
+} from "types/remoteComp";
-async function npmLoader(remoteInfo: RemoteCompInfo): Promise {
+async function npmLoader(
+ remoteInfo: RemoteCompInfo
+): Promise {
const { packageName, packageVersion = "latest", compName } = remoteInfo;
const entry = `${NPM_PLUGIN_ASSETS_BASE_URL}/${packageName}@${packageVersion}/index.js`;
+ // const entry = `../../../../../public/package/index.js`;
// console.log("Entry", entry);
try {
const module = await import(/* webpackIgnore: true */ entry);
@@ -21,7 +28,9 @@ async function npmLoader(remoteInfo: RemoteCompInfo): Promise {
+async function bundleLoader(
+ remoteInfo: RemoteCompInfo
+): Promise {
const { packageName, packageVersion = "latest", compName } = remoteInfo;
const entry = `/${packageName}/${packageVersion}/index.js?v=${REACT_APP_COMMIT_ID}`;
const module = await import(/* webpackIgnore: true */ entry);
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx
index c7198c17c..f8b154e42 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx
@@ -51,13 +51,25 @@ export const getStyle = (style: CheckboxStyleType) => {
border-radius: ${style.radius};
}
}
-
+
.ant-checkbox-inner {
border-radius: ${style.radius};
background-color: ${style.uncheckedBackground};
border-color: ${style.uncheckedBorder};
border-width:${!!style.borderWidth ? style.borderWidth : '2px'};
}
+
+ &:hover .ant-checkbox-inner,
+ .ant-checkbox:hover .ant-checkbox-inner,
+ .ant-checkbox-input + ant-checkbox-inner {
+ background-color:${style.hoverBackground ? style.hoverBackground :'#fff'};
+ }
+
+ &:hover .ant-checkbox-checked .ant-checkbox-inner,
+ .ant-checkbox:hover .ant-checkbox-inner,
+ .ant-checkbox-input + ant-checkbox-inner {
+ background-color:${style.hoverBackground ? style.hoverBackground:'#ffff'};
+ }
&:hover .ant-checkbox-inner,
.ant-checkbox:hover .ant-checkbox-inner,
@@ -67,11 +79,15 @@ export const getStyle = (style: CheckboxStyleType) => {
}
}
+
+
.ant-checkbox-group-item {
font-family:${style.fontFamily};
font-size:${style.textSize};
font-weight:${style.textWeight};
font-style:${style.fontStyle};
+ text-transform:${style.textTransform};
+ text-decoration:${style.textDecoration};
}
.ant-checkbox-wrapper {
padding: ${style.padding};
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx
index a1ea41fe6..11bfceed0 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx
@@ -25,6 +25,8 @@ const getStyle = (style: RadioStyleType) => {
font-size:${style.textSize};
font-weight:${style.textWeight};
font-style:${style.fontStyle};
+ text-transform:${style.textTransform};
+ text-decoration:${style.textDecoration};
}
.ant-radio-checked {
@@ -47,6 +49,12 @@ const getStyle = (style: RadioStyleType) => {
}
}
+ &:hover .ant-radio-inner,
+ .ant-radio:hover .ant-radio-inner,
+ .ant-radio-input + ant-radio-inner {
+ background-color:${style.hoverBackground ? style.hoverBackground:'#ffff'};
+ }
+
&:hover .ant-radio-inner,
.ant-radio:hover .ant-radio-inner,
.ant-radio-input:focus + .ant-radio-inner {
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
index 156c83799..73a7d4675 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
@@ -52,6 +52,14 @@ const getStyle = (style: SegmentStyleType) => {
.ant-segmented-item-selected {
border-radius: ${style.radius};
}
+ &.ant-segmented, .ant-segmented-item-label {
+ font-family:${style.fontFamily};
+ font-style:${style.fontStyle};
+ font-size:${style.textSize};
+ font-weight:${style.textWeight};
+ text-transform:${style.textTransform};
+ text-decoration:${style.textDecoration};
+ }
`;
};
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
index 51c525fd8..1eac631ac 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
@@ -76,6 +76,8 @@ export const getStyle = (
}
.ant-select-selection-search-input {
font-family:${(style as SelectStyleType).fontFamily} !important;
+ text-transform:${(style as SelectStyleType).textTransform} !important;
+ text-decoration:${(style as SelectStyleType).textDecoration} !important;
font-size:${(style as SelectStyleType).textSize} !important;
font-weight:${(style as SelectStyleType).textWeight};
color:${(style as SelectStyleType).text} !important;
@@ -268,7 +270,6 @@ export const SelectUIView = (
label={option.label}
disabled={option.disabled}
key={option.value}
- style={{fontFamily:"Montserrat"}}
>
{props.options.findIndex((option) => hasIcon(option.prefixIcon)) >
diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx
index 58f8145c0..f45edf49b 100644
--- a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx
@@ -272,7 +272,7 @@ function ColumnPropertyView>(props: {
{
- console.log("comp", comp);
+ // console.log("comp", comp);
comp.dispatch(
wrapChildAction(
"columns",
diff --git a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
index 06a5ad955..1bdc2ea4e 100644
--- a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
@@ -111,6 +111,14 @@ const getStyle = (
}
}
+ .ant-tabs-tab-btn {
+ font-family:${style.fontFamily};
+ font-weight:${style.textWeight};
+ text-transform:${style.textTransform};
+ text-decoration:${style.textDecoration};
+ font-style:${style.fontStyle};
+ }
+
.ant-tabs-ink-bar {
background-color: ${style.accent};
}
diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx
index 4dc46d0f6..fd9ce1f18 100644
--- a/client/packages/lowcoder/src/comps/comps/textComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx
@@ -30,6 +30,8 @@ const getStyle = (style: TextStyleType) => {
font-weight: ${style.textWeight} !important;
font-family: ${style.fontFamily} !important;
font-style:${style.fontStyle} !important;
+ text-transform:${style.textTransform} !important;
+ text-decoration:${style.textDecoration} !important;
background-color: ${style.background};
.markdown-body a {
color: ${style.links};
@@ -146,7 +148,6 @@ let TextTmpComp = (function () {
.setPropertyViewFn((children) => {
return (
<>
-
{children.type.propertyView({
label: trans("value"),
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
index c760e054b..51815260f 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
@@ -58,7 +58,14 @@ import { EditorContext } from "comps/editorState";
const Wrapper = styled.div<{
$style: InputLikeStyleType;
}>`
- height: 100%;
+ box-sizing:border-box;
+ .rc-textarea {
+ background-color:${(props) => props.$style.background};
+ padding:${(props) => props.$style.padding};
+ text-transform:${(props)=>props.$style.textTransform};
+ text-decoration:${(props)=>props.$style.textDecoration};
+ margin: 0px 3px 0px 3px !important;
+ }
.ant-input-clear-icon {
opacity: 0.45;
@@ -196,7 +203,7 @@ let MentionTmpComp = (function () {
height: "100%",
maxHeight: "100%",
resize: "none",
- padding: props.style.padding,
+ // padding: props.style.padding,
fontStyle: props.style.fontStyle,
fontFamily: props.style.fontFamily,
borderWidth: props.style.borderWidth,
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
index 69d040151..1d01266af 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
@@ -248,6 +248,8 @@ export function getStyle(style: InputLikeStyleType) {
font-weight: ${style.textWeight};
font-family: ${style.fontFamily};
font-style:${style.fontStyle};
+ text-transform:${style.textTransform};
+ text-decoration:${style.textDecoration};
background-color: ${style.background};
border-color: ${style.border};
diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx
index 7295a9783..109801a97 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx
@@ -547,6 +547,8 @@ export function styleControl(colorConfig
name === "cardRadius" ||
name === "textSize" ||
name === "textWeight" ||
+ name === "textTransform" ||
+ name === "textDecoration" ||
name === "fontFamily" ||
name === "fontStyle" ||
name === "backgroundImage" ||
diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
index 8b81237e4..51c88abd7 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
@@ -74,13 +74,21 @@ export type PaddingConfig = CommonColorConfig & {
readonly padding: string;
};
+export type TextTransformConfig = CommonColorConfig & {
+ readonly textTransform: string;
+}
+
+export type TextDecorationConfig = CommonColorConfig & {
+ readonly textDecoration: string;
+}
+
export type DepColorConfig = CommonColorConfig & {
readonly depName?: string;
readonly depTheme?: keyof ThemeDetail;
readonly depType?: DEP_TYPE;
transformer: (color: string, ...rest: string[]) => string;
};
-export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | BackgroundImageConfig | BackgroundImageRepeatConfig | BackgroundImageSizeConfig | BackgroundImagePositionConfig | BackgroundImageOriginConfig | TextSizeConfig | TextWeightConfig | FontFamilyConfig | FontStyleConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig | HeaderBackgroundImageConfig | HeaderBackgroundImageRepeatConfig | HeaderBackgroundImageSizeConfig | HeaderBackgroundImagePositionConfig | HeaderBackgroundImageOriginConfig | FooterBackgroundImageConfig | FooterBackgroundImageRepeatConfig | FooterBackgroundImageSizeConfig | FooterBackgroundImagePositionConfig | FooterBackgroundImageOriginConfig;
+export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | BackgroundImageConfig | BackgroundImageRepeatConfig | BackgroundImageSizeConfig | BackgroundImagePositionConfig | BackgroundImageOriginConfig | TextSizeConfig | TextWeightConfig | TextTransformConfig | TextDecorationConfig | FontFamilyConfig | FontStyleConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig | HeaderBackgroundImageConfig | HeaderBackgroundImageRepeatConfig | HeaderBackgroundImageSizeConfig | HeaderBackgroundImagePositionConfig | HeaderBackgroundImageOriginConfig | FooterBackgroundImageConfig | FooterBackgroundImageRepeatConfig | FooterBackgroundImageSizeConfig | FooterBackgroundImagePositionConfig | FooterBackgroundImageOriginConfig;
export const defaultTheme: ThemeDetail = {
primary: "#3377FF",
@@ -350,6 +358,12 @@ const TEXT_WEIGHT = {
textWeight: "textWeight",
} as const;
+const HOVER_BACKGROUND_COLOR = {
+ name: "hoverBackground",
+ label: trans("style.hoverBackground"),
+ hoverBackground: "hoverBackground"
+}
+
const FONT_FAMILY = {
name: "fontFamily",
label: trans("style.fontFamily"),
@@ -381,6 +395,18 @@ const CONTAINERBODYPADDING = {
containerbodypadding: "padding",
} as const;
+const TEXT_TRANSFORM = {
+ name: "textTransform",
+ label: trans("style.textTransform"),
+ textTransform: "textTransform"
+}
+
+const TEXT_DECORATION = {
+ name: "textDecoration",
+ label: trans("style.textDecoration"),
+ textDecoration: "textDecoration"
+}
+
const getStaticBorder = (color: string = SECOND_SURFACE_COLOR) =>
({
name: "border",
@@ -399,6 +425,8 @@ const HEADER_BACKGROUND = {
const BG_STATIC_BORDER_RADIUS = [getBackground(), getStaticBorder(), RADIUS] as const;
const STYLING_FIELDS_SEQUENCE = [
TEXT,
+ TEXT_TRANSFORM,
+ TEXT_DECORATION,
TEXT_SIZE,
TEXT_WEIGHT,
FONT_FAMILY,
@@ -476,7 +504,6 @@ function replaceAndMergeMultipleStyles(originalArray: any[], styleToReplace: str
}
export const ButtonStyle = [
- // ...getBgBorderRadiusByBg("primary"),
getBackground('primary'),
...STYLING_FIELDS_SEQUENCE
] as const;
@@ -527,6 +554,7 @@ export const MarginStyle = [
export const ContainerStyle = [
// ...BG_STATIC_BORDER_RADIUS,
getStaticBorder(),
+ // ...STYLING_FIELDS_SEQUENCE.filter((style) => style.name !== 'border'),
getBackground(),
RADIUS,
BORDER_WIDTH,
@@ -729,23 +757,12 @@ export const SwitchStyle = [
] as const;
export const SelectStyle = [
- // LABEL,
...replaceAndMergeMultipleStyles(STYLING_FIELDS_SEQUENCE, 'border', [...getStaticBgBorderRadiusByBg(SURFACE_COLOR, "pc")]),
-
- // ...getStaticBgBorderRadiusByBg(SURFACE_COLOR, "pc"),
- // TEXT,
- // MARGIN,
- // PADDING,
...ACCENT_VALIDATE,
] as const;
const multiSelectCommon = [
...replaceAndMergeMultipleStyles(STYLING_FIELDS_SEQUENCE, 'border', [...getStaticBgBorderRadiusByBg(SURFACE_COLOR, "pc")]),
- // LABEL,
- // ...getStaticBgBorderRadiusByBg(SURFACE_COLOR, "pc"),
- // TEXT,
- // MARGIN,
- // PADDING,
{
name: "tags",
label: trans("style.tags"),
@@ -776,13 +793,14 @@ export const MultiSelectStyle = [
] as const;
export const TabContainerStyle = [
- {
+ // Keep background related properties of container as STYLING_FIELDS_SEQUENCE has rest of the properties
+ ...replaceAndMergeMultipleStyles([...ContainerStyle.filter((style)=> ['border','radius','borderWidth','margin','padding'].includes(style.name) === false),...STYLING_FIELDS_SEQUENCE], 'text', [{
name: "tabText",
label: trans("style.tabText"),
depName: "headerBackground",
depType: DEP_TYPE.CONTRAST_TEXT,
transformer: contrastText,
- },
+ },]),
{
name: "accent",
label: trans("style.tabAccent"),
@@ -790,7 +808,6 @@ export const TabContainerStyle = [
depType: DEP_TYPE.SELF,
transformer: toSelf,
},
- ...ContainerStyle,
] as const;
export const ModalStyle = [
@@ -838,7 +855,6 @@ function checkAndUncheck() {
}
export const CheckboxStyle = [
- // LABEL,
...replaceAndMergeMultipleStyles(STYLING_FIELDS_SEQUENCE, 'text', [LABEL, STATIC_TEXT, VALIDATE]).filter((style) => style.name !== 'border'),
...checkAndUncheck(),
{
@@ -848,15 +864,10 @@ export const CheckboxStyle = [
depType: DEP_TYPE.CONTRAST_TEXT,
transformer: contrastText,
},
- // RADIUS,
- // STATIC_TEXT,
- // VALIDATE,
- // MARGIN,
- // PADDING,
+ HOVER_BACKGROUND_COLOR
] as const;
export const RadioStyle = [
- // LABEL,
...replaceAndMergeMultipleStyles(STYLING_FIELDS_SEQUENCE, 'text', [LABEL, STATIC_TEXT, VALIDATE]).filter((style) => style.name !== 'border' && style.name !== 'radius'),
...checkAndUncheck(),
{
@@ -866,14 +877,12 @@ export const RadioStyle = [
depType: DEP_TYPE.SELF,
transformer: toSelf,
},
- // STATIC_TEXT,
- // VALIDATE,
- // MARGIN,
- // PADDING,
+ HOVER_BACKGROUND_COLOR
] as const;
export const SegmentStyle = [
LABEL,
+ ...STYLING_FIELDS_SEQUENCE.filter((style)=> ['border','borderWidth'].includes(style.name) === false),
{
name: "indicatorBackground",
label: trans("style.indicatorBackground"),
@@ -892,10 +901,7 @@ export const SegmentStyle = [
depType: DEP_TYPE.CONTRAST_TEXT,
transformer: contrastText,
},
- RADIUS,
VALIDATE,
- MARGIN,
- PADDING,
] as const;
const LinkTextStyle = [
@@ -1089,7 +1095,7 @@ export const ProgressStyle = [
depTheme: "canvas",
depType: DEP_TYPE.CONTRAST_TEXT,
transformer: contrastText,
- }]).filter((style) => ['border', 'borderWidth'].includes(style.name) === false),
+ }]).filter((style) => ['border', 'borderWidth', 'textTransform', 'textDecoration'].includes(style.name) === false),
TRACK,
FILL,
SUCCESS,
diff --git a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
index 1d04e984d..2cfb5e1ca 100644
--- a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
@@ -35,6 +35,35 @@ const DrawerWrapper = styled.div`
pointer-events: auto;
`;
+const ButtonStyle = styled(Button)<{$closePosition?: string}>`
+ position: absolute;
+ ${(props) => props.$closePosition === "right" ? "right: 0;" : "left: 0;"}
+ top: 0;
+ z-index: 10;
+ font-weight: 700;
+ box-shadow: none;
+ color: rgba(0, 0, 0, 0.45);
+ height: 54px;
+ width: 54px;
+
+ svg {
+ width: 16px;
+ height: 16px;
+ }
+
+ &,
+ :hover,
+ :focus {
+ background-color: transparent;
+ border: none;
+ }
+
+ :hover,
+ :focus {
+ color: rgba(0, 0, 0, 0.75);
+ }
+`;
+
// If it is a number, use the px unit by default
function transToPxSize(size: string | number) {
return isNumeric(size) ? size + "px" : (size as string);
@@ -103,34 +132,6 @@ let TmpDrawerComp = (function () {
},
[dispatch, isTopBom]
);
- const ButtonStyle = styled(Button)`
- position: absolute;
- ${props.closePosition === "right" ? "right: 0;" : "left: 0;"}
- top: 0;
- z-index: 10;
- font-weight: 700;
- box-shadow: none;
- color: rgba(0, 0, 0, 0.45);
- height: 54px;
- width: 54px;
-
- svg {
- width: 16px;
- height: 16px;
- }
-
- &,
- :hover,
- :focus {
- background-color: transparent;
- border: none;
- }
-
- :hover,
- :focus {
- color: rgba(0, 0, 0, 0.75);
- }
- `;
return (
@@ -168,6 +169,7 @@ let TmpDrawerComp = (function () {
mask={props.showMask}
>
{
props.visible.onChange(false);
}}
diff --git a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx
index dd2a6b1dc..8002436e1 100644
--- a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx
@@ -29,6 +29,7 @@ import { useInterval, useTitle, useWindowSize } from "react-use";
import { useCurrentUser } from "util/currentUser";
import { LocalStorageComp } from "./localStorageComp";
import { MessageComp } from "./messageComp";
+import { ToastComp } from "./toastComp";
import { ThemeComp } from "./themeComp";
import UrlParamsHookComp from "./UrlParamsHookComp";
import { UtilsComp } from "./utilsComp";
@@ -94,6 +95,7 @@ const HookMap: HookCompMapRawType = {
momentJsLib: DayJsLib, // old components use this hook
utils: UtilsComp,
message: MessageComp,
+ toast: ToastComp,
localStorage: LocalStorageComp,
modal: ModalComp,
meeting: VideoMeetingControllerComp,
diff --git a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx
index a985a8e72..617f2f6c1 100644
--- a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx
@@ -12,6 +12,7 @@ const AllHookComp = [
"momentJsLib",
"utils",
"message",
+ "toast",
"localStorage",
"currentUser",
"screenInfo",
@@ -58,6 +59,7 @@ const HookCompConfig: Record<
},
utils: { category: "hide" },
message: { category: "hide" },
+ toast: { category: "hide" },
};
// Get hook component category
diff --git a/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx
index 3f28e2d1a..7b267ec19 100644
--- a/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx
@@ -19,6 +19,7 @@ const defaultHookListValue = [
{ compType: "lodashJsLib", name: "_" },
{ compType: "utils", name: "utils" },
{ compType: "message", name: "message" },
+ { compType: "toast", name: "toast" },
{ compType: "localStorage", name: "localStorage" },
{ compType: "currentUser", name: "currentUser" },
{ compType: "screenInfo", name: "screenInfo" },
diff --git a/client/packages/lowcoder/src/comps/hooks/messageComp.ts b/client/packages/lowcoder/src/comps/hooks/messageComp.ts
index e0e6451cb..028c37691 100644
--- a/client/packages/lowcoder/src/comps/hooks/messageComp.ts
+++ b/client/packages/lowcoder/src/comps/hooks/messageComp.ts
@@ -11,7 +11,7 @@ const params: ParamsConfig = [
{ name: "options", type: "JSON" },
];
-const showMessage = (params: EvalParamType[], level: "info" | "success" | "warning" | "error") => {
+const showMessage = (params: EvalParamType[], level: "info" | "success" | "loading" | "warning" | "error") => {
const text = params?.[0];
const options = params?.[1] as JSONObject;
const duration = options?.["duration"] ?? 3;
@@ -35,6 +35,12 @@ MessageComp = withMethodExposing(MessageComp, [
showMessage(params, "success");
},
},
+ {
+ method: { name: "loading", description: trans("messageComp.loading"), params: params },
+ execute: (comp, params) => {
+ showMessage(params, "loading");
+ },
+ },
{
method: { name: "warn", description: trans("messageComp.warn"), params: params },
execute: (comp, params) => {
diff --git a/client/packages/lowcoder/src/comps/hooks/toastComp.ts b/client/packages/lowcoder/src/comps/hooks/toastComp.ts
new file mode 100644
index 000000000..8b80e89b9
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/hooks/toastComp.ts
@@ -0,0 +1,95 @@
+import { withMethodExposing } from "../generators/withMethodExposing";
+import { simpleMultiComp } from "../generators";
+import { withExposingConfigs } from "../generators/withExposing";
+import { EvalParamType, ParamsConfig } from "../controls/actionSelector/executeCompTypes";
+import { JSONObject } from "../../util/jsonTypes";
+import { trans } from "i18n";
+import { notificationInstance } from "lowcoder-design";
+import type { ArgsProps, NotificationPlacement } from 'antd/es/notification/interface';
+
+const params: ParamsConfig = [
+ { name: "text", type: "string" },
+ { name: "options", type: "JSON" },
+];
+
+const showNotification = (
+ params: EvalParamType[],
+ level: "open" | "info" | "success" | "warning" | "error"
+) => {
+ const text = params?.[0] as string;
+ const options = params?.[1] as JSONObject;
+
+ const { message , duration, id, placement, dismissible } = options;
+
+ const closeIcon: boolean | undefined = dismissible === true ? undefined : (dismissible === false ? false : undefined);
+
+ const durationNumberOrNull: number | null = typeof duration === 'number' ? duration : null;
+
+ const notificationArgs: ArgsProps = {
+ message: text,
+ description: message as React.ReactNode,
+ duration: durationNumberOrNull ?? 3,
+ key: id as React.Key,
+ placement: placement as NotificationPlacement ?? "bottomRight",
+ closeIcon: closeIcon as boolean,
+ };
+
+ // Use notificationArgs to trigger the notification
+
+ text && notificationInstance[level](notificationArgs);
+};
+
+const destroy = (
+ params: EvalParamType[]
+) => {
+ // Extract the id from the params
+ const id = params[0] as React.Key;
+
+ // Call notificationInstance.destroy with the provided id
+ notificationInstance.destroy(id);
+};
+
+//what we would like to expose: title, text, duration, id, btn-obj, onClose, placement
+
+const ToastCompBase = simpleMultiComp({});
+
+export let ToastComp = withExposingConfigs(ToastCompBase, []);
+
+ToastComp = withMethodExposing(ToastComp, [
+ {
+ method: { name: "destroy", description: trans("toastComp.destroy"), params: params },
+ execute: (comp, params) => destroy(params),
+ },
+ {
+ method: { name: "open", description: trans("toastComp.info"), params: params },
+ execute: (comp, params) => {
+ showNotification(params, "open");
+ },
+ },
+ {
+ method: { name: "info", description: trans("toastComp.info"), params: params },
+ execute: (comp, params) => {
+ showNotification(params, "info");
+ },
+ },
+ {
+ method: { name: "success", description: trans("toastComp.success"), params: params },
+ execute: (comp, params) => {
+ showNotification(params, "success");
+ },
+ },
+ {
+ method: { name: "warn", description: trans("toastComp.warn"), params: params },
+ execute: (comp, params) => {
+ showNotification(params, "warning");
+ },
+ },
+ {
+ method: { name: "error", description: trans("toastComp.error"), params: params },
+ execute: (comp, params) => {
+ showNotification(params, "error");
+ },
+ },
+]);
+
+
diff --git a/client/packages/lowcoder/src/constants/applicationConstants.ts b/client/packages/lowcoder/src/constants/applicationConstants.ts
index 2e09ba8c3..895d78153 100644
--- a/client/packages/lowcoder/src/constants/applicationConstants.ts
+++ b/client/packages/lowcoder/src/constants/applicationConstants.ts
@@ -16,6 +16,7 @@ export enum AppTypeEnum {
}
export enum ApplicationCategoriesEnum {
+ SUPPORT = "Support",
BUSINESS = "Business",
DASHBOARD = "Dashboards & Reporting",
SLIDES = "Slides & Presentations",
@@ -78,7 +79,7 @@ export interface ApplicationMeta {
creatorEmail?: string;
title?: string;
description?: string;
- icon?: string;
+ image?: string;
category?: ApplicationCategoriesEnum;
showheader?: boolean;
orgId: string;
diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts
index cc41a8d1e..ac395eb65 100644
--- a/client/packages/lowcoder/src/i18n/locales/de.ts
+++ b/client/packages/lowcoder/src/i18n/locales/de.ts
@@ -324,6 +324,9 @@ export const de = {
"tableCellText": "Zelle Text",
"selectedRowBackground": "Ausgewählter Zeilenhintergrund",
"hoverRowBackground": "Hover Row Hintergrund",
+ "hoverBackground":"Hover-Hintergrund",
+ "textTransform":"Texttransformation",
+ "textDecoration":"Textdekoration",
"alternateRowBackground": "Alternativer Reihenhintergrund",
"tableHeaderBackground": "Kopfzeile Hintergrund",
"tableHeaderText": "Überschrift Text",
@@ -1612,10 +1615,18 @@ export const de = {
},
"messageComp": {
"info": "Eine Benachrichtigung senden",
+ "loading": "Ladebestätigung senden",
"success": "Erfolgsbenachrichtigung senden",
"warn": "Eine Warnmeldung senden",
"error": "Eine Fehlerbenachrichtigung senden"
},
+ "tostComp": {
+ "info": "Eine Benachrichtigung senden",
+ "loading": "Ladebestätigung senden",
+ "success": "Erfolgsbenachrichtigung senden",
+ "warn": "Eine Warnmeldung senden",
+ "error": "Eine Fehlerbenachrichtigung senden"
+},
"themeComp": {
"switchTo": "Thema wechseln"
},
@@ -1849,7 +1860,10 @@ export const de = {
"maxWidthTip": "Die maximale Breite sollte größer als oder gleich 350 sein",
"themeSetting": "Angewandter Stil Thema",
"themeSettingDefault": "Standard",
- "themeCreate": "Thema erstellen"
+ "themeCreate": "Thema erstellen",
+ "appTitle": "Titel",
+ "appDescription": "Beschreibung",
+ "appCategory": "Kategorie",
},
"customShortcut": {
"title": "Benutzerdefinierte Abkürzungen",
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 9e71926f0..19f6daf67 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -345,6 +345,9 @@ export const en = {
"tableCellText": "Cell Text",
"selectedRowBackground": "Selected Row Background",
"hoverRowBackground": "Hover Row Background",
+ "hoverBackground":"Hover Background",
+ "textTransform":"Text Transform",
+ "textDecoration":"Text Decoration",
"alternateRowBackground": "Alternate Row Background",
"tableHeaderBackground": "Header Background",
"tableHeaderText": "Header Text",
@@ -1578,7 +1581,7 @@ export const en = {
"advanced": "Advanced",
"lab": "Lab",
"branding": "Branding",
- "oauthProviders": "OAuth Providers",
+ "oauthProviders": "User Authentication",
"appUsage": "App Usage Logs",
"environments": "Environments",
"premium": "Premium"
@@ -1775,6 +1778,15 @@ export const en = {
},
"messageComp": {
"info": "Send a Notification",
+ "loading": "Send a Loading Notification",
+ "success": "Send a Success Notification",
+ "warn": "Send a Warning Notification",
+ "error": "Send an Error Notification"
+ },
+ "toastComp": {
+ "destroy": "close a Notification",
+ "info": "Send a Notification",
+ "loading": "Send a Loading Notification",
"success": "Send a Success Notification",
"warn": "Send a Warning Notification",
"error": "Send an Error Notification"
@@ -2033,7 +2045,10 @@ export const en = {
"maxWidthTip": "Max Width Should Be Greater Than or Equal to 350",
"themeSetting": "Applied Style Theme",
"themeSettingDefault": "Default",
- "themeCreate": "Create Theme"
+ "themeCreate": "Create Theme",
+ "appTitle": "Title",
+ "appDescription": "Description",
+ "appCategory": "Category",
},
"customShortcut": {
"title": "Custom Shortcuts",
@@ -2251,6 +2266,7 @@ export const en = {
"module": "Module",
"trash": "Trash",
"marketplace": "Marketplace",
+ "allCategories": "All Categories",
"queryLibrary": "Query Library",
"datasource": "Data Sources",
"selectDatasourceType": "Select Data Source Type",
@@ -2606,7 +2622,7 @@ export const en = {
"table": table,
},
"idSource": {
- "title": "OAuth Providers",
+ "title": "User Authentication Provider",
"form": "Email",
"pay": "Premium",
"enable": "Enable",
diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts
index 12821ebf6..d2e868a61 100644
--- a/client/packages/lowcoder/src/i18n/locales/zh.ts
+++ b/client/packages/lowcoder/src/i18n/locales/zh.ts
@@ -334,6 +334,9 @@ style: {
tableCellText: "单元格文本",
selectedRowBackground: "选中行背景",
hoverRowBackground: "悬停行背景",
+ hoverBackground:"悬停背景",
+ textTransform:"文本变换",
+ textDecoration:"文字装饰",
alternateRowBackground: "交替行背景",
tableHeaderBackground: "表头背景",
tableHeaderText: "表头文本",
@@ -1493,7 +1496,7 @@ settings: {
advanced: "高级",
lab: "实验室",
branding: "品牌",
- oauthProviders: "OAuth 提供商",
+ oauthProviders: "User Authentication",
appUsage: "应用程序使用日志",
environments: "环境",
premium: "高级版",
@@ -1683,6 +1686,14 @@ utilsComp: {
},
messageComp: {
info: "发送通知",
+ loading: "发送加载通知",
+ success: "发送成功通知",
+ warn: "发送警告通知",
+ error: "发送错误通知",
+},
+toastComp: {
+ info: "发送通知",
+ loading: "发送加载通知",
success: "发送成功通知",
warn: "发送警告通知",
error: "发送错误通知",
@@ -1922,6 +1933,9 @@ appSetting: {
themeSetting: "主题设置",
themeSettingDefault: "默认",
themeCreate: "创建主题",
+ appTitle: "标题",
+ appDescription: "描述",
+ appCategory: "类别",
},
customShortcut: {
title: "自定义快捷键",
@@ -2196,6 +2210,7 @@ home: {
"errorMarketplaceApps": "获取市场应用程序错误",
"localMarketplaceTitle": "本地市场",
"globalMarketplaceTitle": "Lowcoder 市场",
+ "allCategories": "所有类别",
memberPermissionList: "成员权限:",
orgName: "{orgName}管理员",
addMember: "添加成员",
@@ -2546,7 +2561,7 @@ componentDocExtra: {
table: table,
},
idSource: {
- title: "OAuth 提供商",
+ title: "用户认证提供商",
form: "电子邮件",
pay: "高级",
enable: "启用",
diff --git a/client/packages/lowcoder/src/layout/gridLayout.tsx b/client/packages/lowcoder/src/layout/gridLayout.tsx
index 6b697c0c0..83facb75e 100644
--- a/client/packages/lowcoder/src/layout/gridLayout.tsx
+++ b/client/packages/lowcoder/src/layout/gridLayout.tsx
@@ -172,12 +172,25 @@ class GridLayout extends React.Component {
}
componentDidUpdate(prevProps: GridLayoutProps, prevState: GridLayoutState) {
- const uiLayout = this.getUILayout();
if (!draggingUtils.isDragging()) {
// log.debug("render. clear ops. layout: ", uiLayout);
// only change in changeHs, don't change state
if (_.size(this.state.ops) > 0) {
- this.setState({ layout: uiLayout, changedHs: undefined, ops: undefined });
+ // temporary fix for components becomes invisible in drawer/modal
+ // TODO: find a way to call DELETE_ITEM operation after layouts are updated in state
+ const ops = [...this.state.ops as any[]];
+ const [firstOp] = ops;
+ const { droppingItem } = this.props;
+ if(
+ ops.length === 1
+ && firstOp.type === 'DELETE_ITEM'
+ && firstOp.key === droppingItem?.i
+ ) {
+ this.setState({ changedHs: undefined, ops: undefined });
+ } else {
+ const uiLayout = this.getUILayout();
+ this.setState({ layout: uiLayout, changedHs: undefined, ops: undefined })
+ }
}
}
if (!draggingUtils.isDragging() && _.isNil(this.state.ops)) {
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeCardView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeCardView.tsx
index e9f73cce8..ac515b574 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeCardView.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeCardView.tsx
@@ -1,14 +1,16 @@
import styled from "styled-components";
import { HomeRes } from "./HomeLayout";
import { HomeResCard } from "./HomeResCard";
+import { MarketplaceResCard } from "./MarketplaceResCard";
import React, { useState } from "react";
import { MoveToFolderModal } from "./MoveToFolderModal";
const ApplicationCardsWrapper = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(408px, 1fr));
- grid-template-rows: repeat(auto-fill, min(68px, 100%));
+ grid-template-rows: repeat(auto-fill, min(auto, 100%));
grid-column-gap: 112px;
+ grid-row-gap: 20px;
margin: 48px 26px 80px;
overflow: hidden;
@media screen and (max-width: 500px) {
@@ -23,6 +25,8 @@ export function HomeCardView(props: { resources: HomeRes[] }) {
return (
{props.resources.map((res) => (
+ res.isMarketplace ?
+ :
))}
setNeedMoveRes(undefined)} />
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx
index 1442e1620..5cf5d7e36 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx
@@ -35,6 +35,7 @@ import { checkIsMobile } from "util/commonUtils";
import MarketplaceHeaderImage from "assets/images/marketplaceHeaderImage.jpg";
import { Divider } from "antd";
import { Margin } from "../setting/theme/styledComponents";
+import { ApplicationCategoriesEnum } from "constants/applicationConstants";
const Wrapper = styled.div`
display: flex;
@@ -171,7 +172,7 @@ const FilterDropdown = styled(Select)`
const FilterMenuItem = styled.div`
display: flex;
- align-items: center;
+ align-items: left;
height: 29px;
width: 100%;
`;
@@ -253,6 +254,10 @@ export interface HomeRes {
key: string;
id: string;
name: string;
+ title?: string;
+ description?: string;
+ category?: string;
+ icon?: string;
type: HomeResTypeEnum;
creator: string;
lastModifyTime: number;
@@ -276,20 +281,37 @@ export interface HomeLayoutProps {
}
export function HomeLayout(props: HomeLayoutProps) {
+
+
const { breadcrumb = [], elements = [], localMarketplaceApps = [], globalMarketplaceApps = [],mode } = props;
+
+ const categoryOptions = [
+ { label: {trans("home.allCategories")}, value: 'All' },
+ ...Object.entries(ApplicationCategoriesEnum).map(([key, value]) => ({
+ label: (
+
+ {value}
+
+ ),
+ value: key,
+ })),
+ ];
+
const user = useSelector(getUser);
const isFetching = useSelector(isFetchingFolderElements);
const isSelfHost = window.location.host !== 'app.lowcoder.cloud';
- const [filterBy, setFilterBy] = useState("All");
+ const [typeFilter, setTypeFilter] = useState("All");
+ const [categoryFilter, setCategoryFilter] = useState("All");
const [searchValue, setSearchValue] = useState("");
const [layout, setLayout] = useState(
checkIsMobile(window.innerWidth) ? "card" : getHomeLayout()
);
+
useEffect(() => saveHomeLayout(layout), [layout]);
useEffect(() => {
- // remove collision status from localstorage
+ // remove collision status from localstorage, as the next selected app may have another collision status
removeCollisionStatus();
}, []);
@@ -300,6 +322,7 @@ export function HomeLayout(props: HomeLayoutProps) {
}
var displayElements = elements;
+
if (mode === "marketplace" && isSelfHost) {
const markedLocalApps = localMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: true }));
const markedGlobalApps = globalMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: false }));
@@ -319,18 +342,27 @@ export function HomeLayout(props: HomeLayoutProps) {
: true
)
.filter((e) => {
- if (HomeResTypeEnum[filterBy].valueOf() === HomeResTypeEnum.All) {
+ if (HomeResTypeEnum[typeFilter].valueOf() === HomeResTypeEnum.All) {
return true;
}
if (e.folder) {
- return HomeResTypeEnum[filterBy] === HomeResTypeEnum.Folder;
+ return HomeResTypeEnum[typeFilter] === HomeResTypeEnum.Folder;
} else {
- if (filterBy === "Navigation") {
+ if (typeFilter === "Navigation") {
return NavigationTypes.map((t) => t.valueOf()).includes(e.applicationType);
}
- return HomeResTypeEnum[filterBy].valueOf() === e.applicationType;
+ return HomeResTypeEnum[typeFilter].valueOf() === e.applicationType;
+ }
+ })
+ .filter((e) => {
+ // If "All" is selected, do not filter out any elements based on category
+ if (categoryFilter === 'All' || !categoryFilter) {
+ return true;
}
+ // Otherwise, filter elements based on the selected category
+ return !e.folder && e.category === categoryFilter.toString();
})
+
.map((e) =>
e.folder
? {
@@ -347,6 +379,10 @@ export function HomeLayout(props: HomeLayoutProps) {
key: e.applicationId,
id: e.applicationId,
name: e.name,
+ title: e.title,
+ description: e.description,
+ category: e.category,
+ icon: e.image,
type: HomeResTypeEnum[HomeResTypeEnum[e.applicationType] as HomeResKey],
creator: e?.creatorEmail ?? e.createBy,
lastModifyTime: e.lastModifyTime,
@@ -385,6 +421,14 @@ export function HomeLayout(props: HomeLayoutProps) {
}))
]
+ const testOptions = [
+ getFilterMenuItem(HomeResTypeEnum.All),
+ getFilterMenuItem(HomeResTypeEnum.Application),
+ getFilterMenuItem(HomeResTypeEnum.Module),
+ ...(mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Navigation)] : []),
+ ...(mode !== "trash" && mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Folder)] : []),
+ ];
+
return (
@@ -414,19 +458,27 @@ export function HomeLayout(props: HomeLayoutProps) {
{mode !== "folders" && mode !== "module" && (
setFilterBy(value as HomeResKey)}
+ value={typeFilter}
+ onChange={(value: any) => setTypeFilter(value as HomeResKey)}
options={[
getFilterMenuItem(HomeResTypeEnum.All),
getFilterMenuItem(HomeResTypeEnum.Application),
getFilterMenuItem(HomeResTypeEnum.Module),
...(mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Navigation)] : []),
...(mode !== "trash" && mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Folder)] : []),
-
]}
getPopupContainer={(node: any) => node}
- suffixIcon={}
- />
+ suffixIcon={} />
+ )}
+ {mode === "marketplace" && (
+ setCategoryFilter(value as ApplicationCategoriesEnum)}
+ options={categoryOptions}
+ // getPopupContainer={(node) => node}
+ suffixIcon={} />
)}
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx
index ceef8b54a..4e6069f05 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx
@@ -73,7 +73,7 @@ const Card = styled.div`
align-items: center;
height: 100%;
width: 100%;
- border-bottom: 1px solid #f5f5f6;
+
padding: 0 10px;
button {
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceResCard.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceResCard.tsx
new file mode 100644
index 000000000..9f3f6881e
--- /dev/null
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceResCard.tsx
@@ -0,0 +1,195 @@
+import { TacoButton } from "lowcoder-design";
+import styled from "styled-components";
+import { timestampToHumanReadable } from "util/dateTimeUtils";
+import { HomeRes } from "./HomeLayout";
+import {
+ handleMarketplaceAppViewClick,
+ HomeResInfo,
+} from "../../util/homeResUtils";
+import { trans } from "../../i18n";
+import { checkIsMobile } from "util/commonUtils";
+import history from "util/history";
+import { APPLICATION_VIEW_URL } from "constants/routesURL";
+import { TypographyText } from "../../components/TypographyText";
+import { messageInstance } from "lowcoder-design";
+import { Typography } from "antd";
+import { MultiIconDisplay } from "../../comps/comps/multiIconDisplay";
+
+const { Text } = Typography;
+
+const EditButton = styled(TacoButton)`
+ width: 52px;
+ height: 24px;
+ padding: 5px 12px;
+ margin-right: 12px;
+ @media screen and (max-width: 500px) {
+ display: none;
+ }
+`;
+
+const ExecButton = styled(TacoButton)`
+ width: 52px;
+ height: 24px;
+ padding: 5px 12px;
+ margin-right: 24px;
+ background: #fafbff;
+ border: 1px solid #c9d1fc;
+ border-radius: 4px;
+ font-weight: 500;
+ color: #4965f2;
+
+ &:hover {
+ background: #f9fbff;
+ border: 1px solid #c2d6ff;
+ color: #315efb;
+ }
+
+ @media screen and (max-width: 500px) {
+ margin-right: 0;
+ display: none;
+ }
+`;
+
+const Wrapper = styled.div`
+ height: auto;
+ padding: 0 6px;
+ border-radius: 8px;
+ margin-bottom: -1px;
+ margin-top: 1px;
+ background-color: #fcfcfc;
+
+ &:hover {
+ background-color: #f5f7fa;
+ }
+`;
+
+const Card = styled.div`
+ display: flex;
+ align-items: center;
+ height: 100%;
+ min-height:100px;
+ width: 100%;
+ border-bottom: 1px solid #f5f5f6;
+ padding: 0 10px;
+
+ button {
+ opacity: 0;
+ }
+
+ &:hover {
+ button {
+ opacity: 1;
+ }
+ }
+
+ @media screen and (max-width: 500px) {
+ button {
+ opacity: 1;
+ }
+
+ padding: 0;
+ }
+`;
+
+const CardInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin-left: 14px;
+ white-space: wrap;
+ width: 284px;
+ max-height: 150px;
+ flex-grow: 1;
+ cursor: pointer;
+ overflow: hidden;
+ padding-right: 12px;
+ padding-top: 12px;
+
+ &:hover {
+ .ant-typography {
+ color: #315efb;
+ }
+ }
+
+ .ant-typography {
+ padding: 2px 2px 8px 2px;
+ }
+`;
+
+const AppTimeOwnerInfoLabel = styled.div`
+ font-size: 13px;
+ color: #8b8fa3;
+ line-height: 15px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+`;
+
+const AppDescription = styled.div`
+ font-size: 13px;
+ color: #8b8fa3;
+ line-height: 15px;
+ overflow: hidden;
+ white-space: wrap;
+ text-overflow: ellipsis;
+ margin-top: 10px;
+ margin-bottom: 10px;
+`;
+
+const OperationWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ @media screen and (max-width: 500px) {
+ > svg {
+ display: none;
+ }
+ }
+`;
+
+const MONTH_MILLIS = 30 * 24 * 60 * 60 * 1000;
+
+export function MarketplaceResCard(props: { res: HomeRes; }) {
+ const { res } = props;
+
+ const subTitle = trans("home.resCardSubTitle", { time: timestampToHumanReadable(res.lastModifyTime, MONTH_MILLIS), creator: res.creator});
+
+ const resInfo = HomeResInfo[res.type];
+ if (!resInfo) { return null; }
+
+ return (
+
+
+ {res.icon && typeof res.icon === 'string' && (
+
+ )}
+ {
+ if (checkIsMobile(window.innerWidth)) {
+ history.push(APPLICATION_VIEW_URL(res.id, "view"));
+ return;
+ }
+ if(res.isMarketplace) {
+ handleMarketplaceAppViewClick(res.id);
+ return;
+ }
+ }}
+ >
+ {}}
+ />
+ {subTitle}
+ {res.description &&
+ {res.description.length > 150 ? res.description.substring(0, 150) + '...' : res.description}
+ }
+
+
+ handleMarketplaceAppViewClick(res.id)}>
+ {trans("view")}
+
+
+
+
+ );
+}
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
index 55df189c3..10e5b5090 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx
@@ -1,20 +1,20 @@
import { useEffect, useState } from "react";
import { HomeLayout } from "./HomeLayout";
-import { MARKETPLACE_TYPE_URL, MARKETPLACE_URL } from "constants/routesURL";
+import { MARKETPLACE_URL } from "constants/routesURL";
import { trans } from "../../i18n";
import axios, { AxiosResponse } from "axios";
import ApplicationApi from "@lowcoder-ee/api/applicationApi";
-import { ApplicationMeta, MarketplaceType } from "@lowcoder-ee/constants/applicationConstants";
+import { ApplicationMeta } from "@lowcoder-ee/constants/applicationConstants";
import { GenericApiResponse } from "@lowcoder-ee/api/apiResponses";
import { validateResponse } from "@lowcoder-ee/api/apiUtils";
import { messageInstance } from "lowcoder-design";
-import { matchPath } from "react-router";
-import log from "loglevel";
export function MarketplaceView() {
const [ marketplaceApps, setMarketplaceApps ] = useState>([]);
const [ localMarketplaceApps, setLocalMarketplaceApps ] = useState>([]);
+ // console.log("localMarketplaceApps", localMarketplaceApps);
+
const fetchMarketplaceApps = async () => {
try {
let response: AxiosResponse>;
@@ -46,8 +46,11 @@ export function MarketplaceView() {
}
useEffect(() => {
+ // Make sure we are fetching local marketplace apps for self-hosted environments
+ if (window.location.host !== 'app.lowcoder.cloud') {
+ fetchLocalMarketplaceApps();
+ }
fetchMarketplaceApps();
- fetchLocalMarketplaceApps();
}, []);
return (
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
index 76ccf765d..db14b3b01 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
@@ -5,7 +5,6 @@ import {
FOLDER_URL_PREFIX,
FOLDERS_URL,
MARKETPLACE_URL,
- MARKETPLACE_URL_BY_TYPE,
MODULE_APPLICATIONS_URL,
QUERY_LIBRARY_URL,
SETTING,
@@ -17,25 +16,17 @@ import {
EditPopover,
EllipsisTextCss,
FolderIcon,
- HomeActiveIcon,
- HomeDataSourceActiveIcon,
HomeDataSourceIcon,
HomeIcon,
- HomeModuleActiveIcon,
HomeModuleIcon,
- HomeQueryLibraryActiveIcon,
HomeQueryLibraryIcon,
- HomeSettingsActiveIcon,
- HomeSettingsIcon,
+ HomeSettingIcon,
InviteUserIcon,
PlusIcon,
PointIcon,
- RecyclerActiveIcon,
RecyclerIcon,
MarketplaceIcon,
- MarketplaceActiveIcon,
- LowcoderMarketplaceActiveIcon,
- LowcoderMarketplaceIcon,
+ AppsIcon
} from "lowcoder-design";
import React, { useEffect, useState } from "react";
import { fetchAllApplications, fetchHomeData } from "redux/reduxActions/applicationActions";
@@ -345,48 +336,28 @@ export default function ApplicationHome() {
text: {trans("home.allApplications")},
routePath: ALL_APPLICATIONS_URL,
routeComp: HomeView,
- icon: ({ selected, ...otherProps }) =>
- selected ? : ,
+ icon: ({ selected, ...otherProps }) => selected ? : ,
},
{
text: {trans("home.allModules")},
routePath: MODULE_APPLICATIONS_URL,
routeComp: ModuleView,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
},
{
- text: (
-
- {trans("home.marketplace")}
-
- ),
+ text: {trans("home.marketplace")},
routePath: MARKETPLACE_URL,
routePathExact: false,
routeComp: MarketplaceView,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
},
{
text: {trans("home.trash")},
routePath: TRASH_URL,
routeComp: TrashView,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
},
],
@@ -414,12 +385,7 @@ export default function ApplicationHome() {
text: {trans("home.queryLibrary")},
routePath: QUERY_LIBRARY_URL,
routeComp: QueryLibraryEditor,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
},
{
@@ -427,12 +393,7 @@ export default function ApplicationHome() {
routePath: DATASOURCE_URL,
routePathExact: false,
routeComp: DatasourceHome,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
onSelected: (_, currentPath) => currentPath.split("/")[1] === "datasource",
},
@@ -441,12 +402,7 @@ export default function ApplicationHome() {
routePath: SETTING,
routePathExact: false,
routeComp: Setting,
- icon: ({ selected, ...otherProps }) =>
- selected ? (
-
- ) : (
-
- ),
+ icon: ({ selected, ...otherProps }) => selected ? : ,
visible: ({ user }) => user.orgDev,
onSelected: (_, currentPath) => currentPath.split("/")[1] === "setting",
},
diff --git a/client/packages/lowcoder/src/pages/common/styledComponent.tsx b/client/packages/lowcoder/src/pages/common/styledComponent.tsx
index a119953c1..95fbb3cfe 100644
--- a/client/packages/lowcoder/src/pages/common/styledComponent.tsx
+++ b/client/packages/lowcoder/src/pages/common/styledComponent.tsx
@@ -39,6 +39,7 @@ const RightStyledCard = styled(Card)`
export const LeftPanel = styled(StyledCard)`
display: block;
+ z-index: ${Layers.rightPanel};
`;
export const RightPanelWrapper = styled(RightStyledCard)`
display: flex;
diff --git a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx
index f2d6c6122..178e25138 100644
--- a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx
+++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx
@@ -256,7 +256,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => {
children[types[0]]?.dispatchChangeValueAction(color);
}
else if(types.length === 2) { // nested object e.g. style.background
- console.log(children[types[0]]);
+ // (children[types[0]]);
if (!children[types[0]]) {
if (children[compType].children[types[0]]?.children[types[1]]) {
children[compType].children[types[0]].children[types[1]]?.dispatchChangeValueAction(color);
@@ -332,7 +332,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => {
children[types[0]]?.dispatchChangeValueAction(value);
}
else if(types.length === 2) { // nested object e.g. style.background
- console.log(children[types[0]]);
+ // console.log(children[types[0]]);
if (!children[types[0]]) {
if (children[compType].children[types[0]]?.children[types[1]]) {
children[compType].children[types[0]].children[types[1]]?.dispatchChangeValueAction(value);
diff --git a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx
index b1125d4c9..d8cb3a4a0 100644
--- a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx
+++ b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx
@@ -148,6 +148,6 @@ export const CollapseWrapper = styled.div<{ $clientX?: number }>`
display: none;
}
.simplebar-content > div {
- padding: 0;
+ // padding: 0;
}
`;
diff --git a/client/packages/lowcoder/src/pages/setting/settingHome.tsx b/client/packages/lowcoder/src/pages/setting/settingHome.tsx
index f54815278..388369a43 100644
--- a/client/packages/lowcoder/src/pages/setting/settingHome.tsx
+++ b/client/packages/lowcoder/src/pages/setting/settingHome.tsx
@@ -8,7 +8,14 @@ import AuditSetting from "@lowcoder-ee/pages/setting/audit";
import { isEE, isEnterpriseMode, isSelfDomain, showAuditLog } from "util/envUtils";
import { TwoColumnSettingPageContent } from "./styled";
import SubSideBar from "components/layout/SubSideBar";
-import { Menu } from "lowcoder-design";
+import {
+ Menu,
+ UserGroupIcon,
+ UserShieldIcon,
+ LeftSettingIcon,
+ ThemeIcon,
+ WorkspacesIcon
+ } from "lowcoder-design";
import { useSelector } from "react-redux";
import { getUser } from "redux/selectors/usersSelectors";
import history from "util/history";
@@ -37,25 +44,35 @@ export function SettingHome() {
const selectKey = useParams<{ setting: string }>().setting || SettingPageEnum.UserGroups;
const items = [
- {
- key: SettingPageEnum.UserGroups,
- label: trans("settings.userGroups"),
- },
{
key: SettingPageEnum.Organization,
label: trans("settings.organization"),
+ icon: ,
+ },
+ {
+ key: SettingPageEnum.OAuthProvider,
+ label: (trans("settings.oauthProviders")),
+ disabled: !currentOrgAdmin(user),
+ icon: ,
+ },
+ {
+ key: SettingPageEnum.UserGroups,
+ label: trans("settings.userGroups"),
+ icon: ,
},
{
key: SettingPageEnum.Theme,
label: trans("settings.theme"),
+ icon: ,
},
{
- key: SettingPageEnum.OAuthProvider,
- label: (
- {trans("settings.oauthProviders")}
- ),
- disabled: !currentOrgAdmin(user),
+ key: SettingPageEnum.Advanced,
+ label: trans("settings.advanced"),
+ icon: ,
},
+
+ // Premium features
+
{
key: SettingPageEnum.Environments,
label: (
@@ -107,16 +124,13 @@ export function SettingHome() {
!enableCustomBrand(config) ||
(!isSelfDomain(config) && !isEnterpriseMode(config)),
},
- {
- key: SettingPageEnum.Advanced,
- label: trans("settings.advanced"),
- },
];
return (
-