Skip to content

Commit a8e7046

Browse files
committed
Merge branch 'dev' of github.com:lowcoder-org/lowcoder into fix/1758-tabbed
2 parents 4838337 + dafdca9 commit a8e7046

28 files changed

+893
-210
lines changed

client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,7 @@ export function getEchartsConfig(
172172
}
173173
},
174174
tooltip: props.tooltip && {
175-
trigger: "axis",
176-
axisPointer: {
177-
type: "line",
178-
lineStyle: {
179-
color: "rgba(0,0,0,0.2)",
180-
width: 2,
181-
type: "solid"
182-
}
183-
}
175+
trigger: "item",
184176
},
185177
grid: {
186178
...gridPos,

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,8 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
209209
{columns.map(column => {
210210
const id = String(column.id);
211211
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
212-
if(!containers[id]) return null
212+
if(!containers[id] || column.hidden) return null
213213
const containerProps = containers[id].children;
214-
const noOfColumns = columns.length;
215214
return (
216215
<React.Fragment key={id}>
217216
<BackgroundColorContext.Provider value={props.columnStyle.background}>

client/packages/lowcoder/src/comps/comps/dateComp/dateCompUtil.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ export const getMobileStyle = (style: DateTimeStyleType) =>
171171

172172
export const dateRefMethods = refMethods<CommonPickerMethods>([focusMethod, blurMethod]);
173173

174+
export const parseInputFormats = (inputFormat?: string): string | string[] =>
175+
inputFormat?.includes(',') ? inputFormat.split(',').map(f => f.trim()) : inputFormat || '';
176+
174177
export const StyledPickerPanel = styled.div<{
175178
$style: ChildrenMultiSelectStyleType
176179
}>`

client/packages/lowcoder/src/comps/comps/dateComp/dateRangeUIView.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dayjs from "dayjs";
22
import type { DateCompViewProps } from "./dateComp";
3-
import { disabledDate, getStyle, StyledPickerPanel } from "comps/comps/dateComp/dateCompUtil";
3+
import { disabledDate, getStyle, StyledPickerPanel, parseInputFormats } from "comps/comps/dateComp/dateCompUtil";
44
import { useUIView } from "../../utils/useUIView";
55
import { checkIsMobile } from "util/commonUtils";
66
import React, { useContext } from "react";
@@ -68,20 +68,15 @@ export interface DateRangeUIViewProps extends DateCompViewProps {
6868

6969
export const DateRangeUIView = (props: DateRangeUIViewProps) => {
7070
const editorState = useContext(EditorContext);
71+
const placeholders: [string, string] = Array.isArray(props.placeholder)
72+
? props.placeholder
73+
: [props.placeholder || 'Start Date', props.placeholder || 'End Date'];
7174

72-
// Extract or compute the placeholder values
73-
let placeholders: [string, string];
74-
if (Array.isArray(props.placeholder)) {
75-
placeholders = props.placeholder;
76-
} else {
77-
// Use the same placeholder for both start and end if it's a single string
78-
placeholders = [props.placeholder || 'Start Date', props.placeholder || 'End Date'];
79-
}
8075
return useUIView(
8176
<DateRangeMobileUIView {...props} />,
8277
<RangePickerStyled
8378
{...omit(props, "onChange" , "format", "inputFormat", "pickerMode", "$childrenInputFieldStyle")}
84-
format={props.inputFormat}
79+
format={parseInputFormats(props.inputFormat)}
8580
ref={props.viewRef as any}
8681
picker={props.pickerMode as any}
8782
value={[props.start, props.end]}

client/packages/lowcoder/src/comps/comps/dateComp/dateUIView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dayjs from "dayjs";
22
import type { DateCompViewProps } from "./dateComp";
3-
import { disabledDate, getStyle, StyledPickerPanel } from "comps/comps/dateComp/dateCompUtil";
3+
import { disabledDate, getStyle, StyledPickerPanel, parseInputFormats } from "comps/comps/dateComp/dateCompUtil";
44
import { useUIView } from "../../utils/useUIView";
55
import { checkIsMobile } from "util/commonUtils";
66
import React, { useContext } from "react";
@@ -67,15 +67,15 @@ const DateMobileUIView = React.lazy(() =>
6767

6868
export const DateUIView = (props: DataUIViewProps) => {
6969
const editorState = useContext(EditorContext);
70-
7170
const placeholder = Array.isArray(props.placeholder) ? props.placeholder[0] : props.placeholder;
71+
7272
return useUIView(
7373
<DateMobileUIView {...props} />,
7474
<DatePickerStyled
7575
{...omit(props, "format", "inputFormat", "pickerMode", "$childrenInputFieldStyle")}
7676
$disabledStyle={props.$disabledStyle}
7777
multiple={false}
78-
format={props.inputFormat}
78+
format={parseInputFormats(props.inputFormat)}
7979
ref={props.viewRef as any}
8080
minDate={props.minDate ? dayjs(props.minDate, DateParser) : undefined}
8181
maxDate={props.maxDate ? dayjs(props.maxDate, DateParser) : undefined}

client/packages/lowcoder/src/comps/comps/fileComp/ImageCaptureModal.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Suspense, useCallback, useEffect, useRef, useState } from "react";
1+
import React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import { default as Button } from "antd/es/button";
33
import Dropdown from "antd/es/dropdown";
44
import type { ItemType } from "antd/es/menu/interface";
@@ -8,6 +8,7 @@ import Flex from "antd/es/flex";
88
import styled from "styled-components";
99
import { trans } from "i18n";
1010
import { CustomModal } from "lowcoder-design";
11+
import { CaptureResolution, RESOLUTION_CONSTRAINTS } from "./fileComp";
1112

1213
const CustomModalStyled = styled(CustomModal)`
1314
top: 10vh;
@@ -53,23 +54,32 @@ const ReactWebcam = React.lazy(() => import("react-webcam"));
5354

5455
export const ImageCaptureModal = (props: {
5556
showModal: boolean;
57+
captureResolution?: CaptureResolution;
5658
onModalClose: () => void;
5759
onImageCapture: (image: string) => void;
5860
}) => {
5961
const [errMessage, setErrMessage] = useState("");
60-
const [videoConstraints, setVideoConstraints] = useState<MediaTrackConstraints>({
61-
facingMode: "environment",
62-
});
62+
const [selectedDeviceId, setSelectedDeviceId] = useState<string | null>(null);
6363
const [modeList, setModeList] = useState<ItemType[]>([]);
6464
const [dropdownShow, setDropdownShow] = useState(false);
6565
const [imgSrc, setImgSrc] = useState<string>();
6666
const webcamRef = useRef<any>(null);
6767

68+
const resolution = props.captureResolution ?? "auto";
69+
const resolutionSize = RESOLUTION_CONSTRAINTS[resolution] ?? {};
70+
71+
const videoConstraints = useMemo<MediaTrackConstraints>(() => {
72+
const base: MediaTrackConstraints = selectedDeviceId
73+
? { deviceId: { exact: selectedDeviceId } }
74+
: { facingMode: "environment" };
75+
return { ...base, ...resolutionSize };
76+
}, [selectedDeviceId, resolutionSize]);
77+
6878
useEffect(() => {
6979
if (props.showModal) {
7080
setImgSrc("");
7181
setErrMessage("");
72-
setVideoConstraints({ facingMode: "environment" });
82+
setSelectedDeviceId(null);
7383
setDropdownShow(false);
7484
}
7585
}, [props.showModal]);
@@ -125,6 +135,8 @@ export const ImageCaptureModal = (props: {
125135
ref={webcamRef}
126136
onUserMediaError={handleMediaErr}
127137
screenshotFormat="image/jpeg"
138+
screenshotQuality={1}
139+
forceScreenshotSourceSize
128140
videoConstraints={videoConstraints}
129141
/>
130142
</Suspense>
@@ -172,7 +184,7 @@ export const ImageCaptureModal = (props: {
172184
<Menu
173185
items={modeList}
174186
onClick={(value) => {
175-
setVideoConstraints({ deviceId: { exact: value.key } });
187+
setSelectedDeviceId(value.key);
176188
setDropdownShow(false);
177189
}}
178190
/>

client/packages/lowcoder/src/comps/comps/fileComp/draggerUpload.tsx

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { default as AntdUpload } from "antd/es/upload";
22
import { default as Button } from "antd/es/button";
33
import { UploadFile, UploadChangeParam, UploadFileStatus, RcFile } from "antd/es/upload/interface";
4-
import { useState, useEffect } from "react";
4+
import { useState, useMemo } from "react";
55
import styled, { css } from "styled-components";
66
import { trans } from "i18n";
77
import _ from "lodash";
@@ -11,8 +11,7 @@ import {
1111
multiChangeAction,
1212
} from "lowcoder-core";
1313
import { hasIcon } from "comps/utils";
14-
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
15-
import { resolveValue, resolveParsedValue, commonProps } from "./fileComp";
14+
import { resolveValue, resolveParsedValue, commonProps, validateFile, CaptureResolution } from "./fileComp";
1615
import { FileStyleType, AnimationStyleType, heightCalculator, widthCalculator } from "comps/controls/styleControlConstants";
1716
import { ImageCaptureModal } from "./ImageCaptureModal";
1817
import { v4 as uuidv4 } from "uuid";
@@ -149,9 +148,11 @@ interface DraggerUploadProps {
149148
prefixIcon: any;
150149
suffixIcon: any;
151150
forceCapture: boolean;
151+
captureResolution: CaptureResolution;
152152
minSize: number;
153153
maxSize: number;
154154
maxFiles: number;
155+
fileNamePattern: string;
155156
uploadType: "single" | "multiple" | "directory";
156157
text: string;
157158
dragHintText?: string;
@@ -162,25 +163,27 @@ interface DraggerUploadProps {
162163

163164
export const DraggerUpload = (props: DraggerUploadProps) => {
164165
const { dispatch, files, style, autoHeight, animationStyle } = props;
165-
const [fileList, setFileList] = useState<UploadFile[]>(
166-
files.map((f) => ({ ...f, status: "done" })) as UploadFile[]
167-
);
166+
// Track only files currently being uploaded (not yet in props.files)
167+
const [uploadingFiles, setUploadingFiles] = useState<UploadFile[]>([]);
168168
const [showModal, setShowModal] = useState(false);
169169
const isMobile = checkIsMobile(window.innerWidth);
170170

171-
useEffect(() => {
172-
if (files.length === 0 && fileList.length !== 0) {
173-
setFileList([]);
174-
}
175-
}, [files]);
171+
// Derive fileList from props.files (source of truth) + currently uploading files
172+
const fileList = useMemo<UploadFile[]>(() => [
173+
...(files.map((f) => ({ ...f, status: "done" as const })) as UploadFile[]),
174+
...uploadingFiles,
175+
], [files, uploadingFiles]);
176176

177177
const handleOnChange = (param: UploadChangeParam) => {
178-
const uploadingFiles = param.fileList.filter((f) => f.status === "uploading");
179-
if (uploadingFiles.length !== 0) {
180-
setFileList(param.fileList);
178+
const currentlyUploading = param.fileList.filter((f) => f.status === "uploading");
179+
if (currentlyUploading.length !== 0) {
180+
setUploadingFiles(currentlyUploading);
181181
return;
182182
}
183183

184+
// Clear uploading state when all uploads complete
185+
setUploadingFiles([]);
186+
184187
let maxFiles = props.maxFiles;
185188
if (props.uploadType === "single") {
186189
maxFiles = 1;
@@ -240,8 +243,6 @@ export const DraggerUpload = (props: DraggerUploadProps) => {
240243
props.onEvent("parse");
241244
});
242245
}
243-
244-
setFileList(uploadedFiles.slice(-maxFiles));
245246
};
246247

247248
return (
@@ -254,21 +255,11 @@ export const DraggerUpload = (props: DraggerUploadProps) => {
254255
$auto={autoHeight}
255256
capture={props.forceCapture}
256257
openFileDialogOnClick={!(props.forceCapture && !isMobile)}
257-
beforeUpload={(file) => {
258-
if (!file.size || file.size <= 0) {
259-
messageInstance.error(`${file.name} ` + trans("file.fileEmptyErrorMsg"));
260-
return AntdUpload.LIST_IGNORE;
261-
}
262-
263-
if (
264-
(!!props.minSize && file.size < props.minSize) ||
265-
(!!props.maxSize && file.size > props.maxSize)
266-
) {
267-
messageInstance.error(`${file.name} ` + trans("file.fileSizeExceedErrorMsg"));
268-
return AntdUpload.LIST_IGNORE;
269-
}
270-
return true;
271-
}}
258+
beforeUpload={(file) => validateFile(file, {
259+
minSize: props.minSize,
260+
maxSize: props.maxSize,
261+
fileNamePattern: props.fileNamePattern,
262+
})}
272263
onChange={handleOnChange}
273264
>
274265
<p className="ant-upload-drag-icon">
@@ -301,6 +292,7 @@ export const DraggerUpload = (props: DraggerUploadProps) => {
301292

302293
<ImageCaptureModal
303294
showModal={showModal}
295+
captureResolution={props.captureResolution as CaptureResolution}
304296
onModalClose={() => setShowModal(false)}
305297
onImageCapture={async (image) => {
306298
setShowModal(false);

0 commit comments

Comments
 (0)