Skip to content

Commit f3b1feb

Browse files
Merge pull request #1692 from kamalqureshi/scanner_camera_component
Switching cameras on Scanner Component
2 parents 1fc4e6c + 7fbdd10 commit f3b1feb

File tree

1 file changed

+144
-75
lines changed

1 file changed

+144
-75
lines changed

client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx

Lines changed: 144 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { default as Button } from "antd/es/button";
2-
import { default as Dropdown } from "antd/es/dropdown";
3-
import { default as Menu } from "antd/es/menu";
42
import { default as Skeleton } from "antd/es/skeleton";
53
import {
64
Button100,
@@ -14,16 +12,29 @@ import { DropdownStyle } from "comps/controls/styleControlConstants";
1412
import { withDefault } from "comps/generators";
1513
import { UICompBuilder } from "comps/generators/uiCompBuilder";
1614
import { CustomModal, Section, sectionNames } from "lowcoder-design";
17-
import styled from "styled-components";
18-
import { CommonNameConfig, NameConfig, withExposingConfigs } from "../../generators/withExposing";
19-
import { hiddenPropertyView, disabledPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils";
15+
import styled, { keyframes } from "styled-components";
16+
import {
17+
CommonNameConfig,
18+
NameConfig,
19+
withExposingConfigs,
20+
} from "../../generators/withExposing";
21+
import {
22+
hiddenPropertyView,
23+
disabledPropertyView,
24+
showDataLoadingIndicatorsPropertyView,
25+
} from "comps/utils/propertyUtils";
2026
import { trans } from "i18n";
21-
import React, { Suspense, useEffect, useRef, useState, useContext } from "react";
27+
import React, {
28+
Suspense,
29+
useEffect,
30+
useRef,
31+
useState,
32+
useContext,
33+
} from "react";
2234
import { arrayStringExposingStateControl } from "comps/controls/codeStateControl";
2335
import { BoolControl } from "comps/controls/boolControl";
24-
import type { ItemType } from "antd/es/menu/interface";
2536
import { RefControl } from "comps/controls/refControl";
26-
import { EditorContext } from "comps/editorState";
37+
import { EditorContext } from "comps/editorState";
2738

2839
const Error = styled.div`
2940
color: #f5222d;
@@ -51,6 +62,50 @@ const Wrapper = styled.div`
5162
}
5263
`;
5364

65+
const dropdownShow = keyframes`
66+
from {
67+
opacity: 0;
68+
transform: translateY(-8px) scaleY(0.98);
69+
}
70+
to {
71+
opacity: 1;
72+
transform: translateY(0) scaleY(1);
73+
}
74+
`;
75+
76+
const DropdownContainer = styled.div`
77+
position: absolute;
78+
top: 44px;
79+
right: 0;
80+
min-width: 150px;
81+
background: #fff;
82+
border: 1px solid #e0e0e0;
83+
border-radius: 8px;
84+
box-shadow:
85+
0 8px 24px rgba(0, 0, 0, 0.12),
86+
0 1.5px 3px rgba(0, 0, 0, 0.08);
87+
z-index: 1000;
88+
padding: 6px 0;
89+
animation: ${dropdownShow} 0.22s cubic-bezier(0.22, 1, 0.36, 1);
90+
transition: box-shadow 0.2s;
91+
`;
92+
93+
const DropdownItem = styled.div`
94+
padding: 10px 20px;
95+
cursor: pointer;
96+
font-size: 14px;
97+
color: #222;
98+
background: transparent;
99+
transition: background 0.15s;
100+
&:hover {
101+
background: #f0f5ff;
102+
color: #1677ff;
103+
}
104+
&:active {
105+
background: #e6f7ff;
106+
}
107+
`;
108+
54109
const CustomModalStyled = styled(CustomModal)`
55110
top: 10vh;
56111
.react-draggable {
@@ -59,7 +114,9 @@ const CustomModalStyled = styled(CustomModal)`
59114
}
60115
`;
61116

62-
const BarcodeScannerComponent = React.lazy(() => import("react-qr-barcode-scanner"));
117+
const BarcodeScannerComponent = React.lazy(
118+
() => import("react-qr-barcode-scanner")
119+
);
63120

64121
const ScannerTmpComp = (function () {
65122
const childrenMap = {
@@ -70,17 +127,20 @@ const ScannerTmpComp = (function () {
70127
maskClosable: withDefault(BoolControl, true),
71128
onEvent: ScannerEventHandlerControl,
72129
disabled: BoolCodeControl,
73-
style: styleControl(DropdownStyle, 'style'),
130+
style: styleControl(DropdownStyle, "style"),
74131
viewRef: RefControl<HTMLElement>,
75132
};
76133
return new UICompBuilder(childrenMap, (props) => {
77134
const [showModal, setShowModal] = useState(false);
78135
const [errMessage, setErrMessage] = useState("");
79-
const [videoConstraints, setVideoConstraints] = useState<MediaTrackConstraints>({
80-
facingMode: "environment",
81-
});
82-
const [modeList, setModeList] = useState<ItemType[]>([]);
83-
const [dropdownShow, setDropdownShow] = useState(false);
136+
const [videoConstraints, setVideoConstraints] =
137+
useState<MediaTrackConstraints>({
138+
facingMode: "environment",
139+
});
140+
const [modeList, setModeList] = useState<{ label: string; key: string }[]>(
141+
[]
142+
);
143+
const [handleDropdown, setHandleDropdown] = useState(false);
84144
const [success, setSuccess] = useState(false);
85145

86146
useEffect(() => {
@@ -92,7 +152,7 @@ const ScannerTmpComp = (function () {
92152
const continuousValue = useRef<string[]>([]);
93153

94154
const handleUpdate = (err: any, result: any) => {
95-
if (!!result) {
155+
if (result) {
96156
if (props.continuous) {
97157
continuousValue.current = [...continuousValue.current, result.text];
98158
const val = props.uniqueData
@@ -109,15 +169,16 @@ const ScannerTmpComp = (function () {
109169
setSuccess(false);
110170
}
111171
};
172+
112173
const handleErr = (err: any) => {
113174
if (typeof err === "string") {
114175
setErrMessage(err);
176+
} else if (
177+
err.message === "getUserMedia is not implemented in this browser"
178+
) {
179+
setErrMessage(trans("scanner.errTip"));
115180
} else {
116-
if (err.message === "getUserMedia is not implemented in this browser") {
117-
setErrMessage(trans("scanner.errTip"));
118-
} else {
119-
setErrMessage(err.message);
120-
}
181+
setErrMessage(err.message);
121182
}
122183
setSuccess(false);
123184
};
@@ -157,6 +218,8 @@ const ScannerTmpComp = (function () {
157218
onCancel={() => {
158219
setShowModal(false);
159220
props.onEvent("close");
221+
setVideoConstraints({ facingMode: "environment" });
222+
setHandleDropdown(false);
160223
}}
161224
>
162225
{!!errMessage ? (
@@ -173,36 +236,33 @@ const ScannerTmpComp = (function () {
173236
videoConstraints={videoConstraints}
174237
/>
175238
</Suspense>
176-
<div
177-
style={{ height: "42px" }}
178-
onClick={() => {
179-
setDropdownShow(false);
180-
}}
181-
>
182-
<Dropdown
183-
placement="bottomRight"
184-
trigger={["click"]}
185-
open={dropdownShow}
186-
onOpenChange={(value) => setDropdownShow(value)}
187-
dropdownRender={() => (
188-
<Menu
189-
items={modeList}
190-
onClick={(value) =>
191-
setVideoConstraints({ ...videoConstraints, deviceId: value.key })
192-
}
193-
/>
194-
)}
239+
240+
<div style={{ position: "relative", marginTop: 10 }}>
241+
<Button
242+
style={{ float: "right" }}
243+
onClick={() => {
244+
getModeList();
245+
setHandleDropdown(!handleDropdown);
246+
}}
195247
>
196-
<Button
197-
style={{ float: "right", marginTop: "10px" }}
198-
onClick={(e) => {
199-
e.stopPropagation();
200-
getModeList();
201-
}}
202-
>
203-
{trans("scanner.changeCamera")}
204-
</Button>
205-
</Dropdown>
248+
{trans("scanner.changeCamera")}
249+
</Button>
250+
251+
{handleDropdown && (
252+
<DropdownContainer>
253+
{modeList.map(({ key, label }) => (
254+
<DropdownItem
255+
key={key}
256+
onClick={() => {
257+
setVideoConstraints({ deviceId: { exact: key } });
258+
setHandleDropdown(false);
259+
}}
260+
>
261+
{label}
262+
</DropdownItem>
263+
))}
264+
</DropdownContainer>
265+
)}
206266
</div>
207267
</Wrapper>
208268
)
@@ -211,35 +271,44 @@ const ScannerTmpComp = (function () {
211271
</ButtonCompWrapper>
212272
);
213273
})
214-
.setPropertyViewFn((children) => {
215-
return (
216-
<>
217-
<Section name={sectionNames.basic}>
218-
{children.text.propertyView({ label: trans("text") })}
219-
</Section>
274+
.setPropertyViewFn((children) => (
275+
<>
276+
<Section name={sectionNames.basic}>
277+
{children.text.propertyView({ label: trans("text") })}
278+
</Section>
220279

221-
{(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
222-
<><Section name={sectionNames.interaction}>
223-
{children.onEvent.getPropertyView()}
224-
{disabledPropertyView(children)}
225-
{hiddenPropertyView(children)}
226-
{showDataLoadingIndicatorsPropertyView(children)}
227-
</Section>
228-
<Section name={sectionNames.advanced}>
229-
{children.continuous.propertyView({ label: trans("scanner.continuous") })}
280+
{(useContext(EditorContext).editorModeStatus === "logic" ||
281+
useContext(EditorContext).editorModeStatus === "both") && (
282+
<>
283+
<Section name={sectionNames.interaction}>
284+
{children.onEvent.getPropertyView()}
285+
{disabledPropertyView(children)}
286+
{hiddenPropertyView(children)}
287+
{showDataLoadingIndicatorsPropertyView(children)}
288+
</Section>
289+
<Section name={sectionNames.advanced}>
290+
{children.continuous.propertyView({
291+
label: trans("scanner.continuous"),
292+
})}
230293
{children.continuous.getView() &&
231-
children.uniqueData.propertyView({ label: trans("scanner.uniqueData") })}
232-
{children.maskClosable.propertyView({ label: trans("scanner.maskClosable") })}
294+
children.uniqueData.propertyView({
295+
label: trans("scanner.uniqueData"),
296+
})}
297+
{children.maskClosable.propertyView({
298+
label: trans("scanner.maskClosable"),
299+
})}
233300
</Section>
234-
</>
235-
)}
301+
</>
302+
)}
236303

237-
{(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && (
238-
<><Section name={sectionNames.style}>{children.style.getPropertyView()}</Section></>
239-
)}
240-
</>
241-
);
242-
})
304+
{(useContext(EditorContext).editorModeStatus === "layout" ||
305+
useContext(EditorContext).editorModeStatus === "both") && (
306+
<Section name={sectionNames.style}>
307+
{children.style.getPropertyView()}
308+
</Section>
309+
)}
310+
</>
311+
))
243312
.setExposeMethodConfigs(buttonRefMethods)
244313
.build();
245314
})();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy