Skip to content

Commit 327e4f8

Browse files
authored
Merge pull request #1114 from lowcoder-org/table-summary-row
Table summary row + Inline add new row
2 parents 64d5bbd + 7f157db commit 327e4f8

21 files changed

+1056
-187
lines changed

client/packages/lowcoder/src/components/table/EditableCell.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface CellProps {
3737
candidateStatus?: { text: string; status: StatusType }[];
3838
textOverflow?: boolean;
3939
cellTooltip?: string;
40+
editMode?: string;
4041
onTableEvent?: (eventName: any) => void;
4142
}
4243

@@ -94,17 +95,19 @@ export function EditableCell<T extends JSONValue>(props: EditableCellProps<T>) {
9495
candidateTags,
9596
// tagColors
9697
candidateStatus,
98+
editMode,
9799
onTableEvent,
98100
} = props;
99101
const status = _.isNil(changeValue) ? "normal" : "toSave";
100102
const editable = editViewFn ? props.editable : false;
101103
const { isEditing, setIsEditing } = useContext(TableCellContext);
102104
const value = changeValue ?? baseValue!;
103105
const [tmpValue, setTmpValue] = useState<T | null>(value);
106+
const singleClickEdit = editMode === 'single';
104107

105108
useEffect(() => {
106109
setTmpValue(value);
107-
}, [value]);
110+
}, [JSON.stringify(value)]);
108111

109112
const onChange = useCallback(
110113
(value: T) => {
@@ -125,21 +128,27 @@ export function EditableCell<T extends JSONValue>(props: EditableCellProps<T>) {
125128
if(!_.isEqual(tmpValue, value)) {
126129
onTableEvent?.('columnEdited');
127130
}
128-
}, [dispatch, baseValue, tmpValue]);
131+
}, [dispatch, JSON.stringify(baseValue), JSON.stringify(tmpValue)]);
132+
129133
const editView = useMemo(
130134
() => editViewFn?.({ value, onChange, onChangeEnd }) ?? <></>,
131-
[editViewFn, value, onChange, onChangeEnd]
135+
[editViewFn, JSON.stringify(value), onChange, onChangeEnd]
132136
);
137+
133138
const enterEditFn = useCallback(() => {
134139
if (editable) setIsEditing(true);
135140
}, [editable]);
136141

137142
if (isEditing) {
138143
return (
139144
<>
140-
<BorderDiv />
145+
<BorderDiv className="editing-border" />
141146
<TagsContext.Provider value={candidateTags ?? []}>
142-
<StatusContext.Provider value={candidateStatus ?? []}>{editView}</StatusContext.Provider>
147+
<StatusContext.Provider value={candidateStatus ?? []}>
148+
<div className="editing-wrapper">
149+
{editView}
150+
</div>
151+
</StatusContext.Provider>
143152
</TagsContext.Provider>
144153
</>
145154
);
@@ -151,7 +160,12 @@ export function EditableCell<T extends JSONValue>(props: EditableCellProps<T>) {
151160
>
152161
{status === "toSave" && !isEditing && <EditableChip />}
153162
<CellWrapper tooltipTitle={props.cellTooltip}>
154-
{normalView}
163+
<div
164+
tabIndex={editable ? 0 : -1 }
165+
onFocus={enterEditFn}
166+
>
167+
{normalView}
168+
</div>
155169
</CellWrapper>
156170
{/* overlay on normal view to handle double click for editing */}
157171
{editable && (
@@ -164,7 +178,8 @@ export function EditableCell<T extends JSONValue>(props: EditableCellProps<T>) {
164178
width: '100%',
165179
height: '100%',
166180
}}
167-
onDoubleClick={enterEditFn}
181+
onDoubleClick={!singleClickEdit ? enterEditFn : undefined}
182+
onClick={singleClickEdit ? enterEditFn : undefined}
168183
>
169184
</div>
170185
</CellWrapper>

client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,12 @@ export const SelectUIView = (
253253
inputFieldStyle: SelectStyleType;
254254
onChange: (value: any) => void;
255255
dispatch: DispatchType;
256+
autoFocus?: boolean;
256257
}
257258
) => {
258259
return <Select
259260
ref={props.viewRef}
261+
autoFocus={props.autoFocus}
260262
mode={props.mode}
261263
$inputFieldStyle={props.inputFieldStyle}
262264
$style={props.style as SelectStyleType & MultiSelectStyleType}

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const InputNumberWrapper = styled.div`
1414
width: 100%;
1515
border-radius: 0;
1616
background: transparent !important;
17-
padding: 0 !important;
17+
// padding: 0 !important;
1818
box-shadow: none;
1919
2020
input {

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateComp.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ const DatePickerStyled = styled(DatePicker)<{ $open: boolean }>`
4949
top: 0.5px;
5050
display: flex;
5151
align-items: center;
52-
background: #fff;
52+
// background: #fff;
5353
padding: 0 3px;
54-
border-left: 1px solid #d7d9e0;
54+
// border-left: 1px solid #d7d9e0;
5555
}
5656
`;
5757

@@ -183,7 +183,7 @@ export const DateEdit = (props: DateEditProps) => {
183183
nextIcon={<IconNext />}
184184
superNextIcon={<IconSuperNext />}
185185
superPrevIcon={<SuperPrevIcon />}
186-
allowClear={false}
186+
allowClear={true}
187187
variant="borderless"
188188
autoFocus
189189
value={tempValue}
@@ -197,7 +197,9 @@ export const DateEdit = (props: DateEditProps) => {
197197
overflow: "hidden",
198198
}}
199199
onOpenChange={(open) => setPanelOpen(open)}
200-
onChange={(value, dateString) => props.onChange(dateString as string)}
200+
onChange={(value, dateString) => {
201+
props.onChange(dateString as string)
202+
}}
201203
onBlur={() => props.onChangeEnd()}
202204
/>
203205
</Wrapper>

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,73 @@ import { StringControl } from "comps/controls/codeControl";
77
import { trans } from "i18n";
88
import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
99
import { ColumnValueTooltip } from "../simpleColumnTypeComps";
10+
import { styled } from "styled-components";
11+
12+
const Wrapper = styled.div`
13+
display: inline-flex;
14+
align-items: center;
15+
width: 100%;
16+
height: 100%;
17+
position: absolute;
18+
top: 0;
19+
background: transparent !important;
20+
padding: 8px;
21+
22+
> div {
23+
width: 100%;
24+
height: 100%;
25+
}
26+
27+
.ant-select {
28+
height: 100%;
29+
.ant-select-selector {
30+
padding: 0 7px;
31+
height: 100%;
32+
overflow: hidden;
33+
.ant-select-selection-item {
34+
display: inline-flex;
35+
align-items: center;
36+
padding-right: 24px;
37+
}
38+
}
39+
.ant-select-arrow {
40+
height: calc(100% - 3px);
41+
width: fit-content;
42+
top: 1.5px;
43+
margin-top: 0;
44+
background-color: white;
45+
right: 1.5px;
46+
border-right: 1px solid #d7d9e0;
47+
cursor: pointer;
48+
pointer-events: auto;
49+
svg {
50+
min-width: 18px;
51+
min-height: 18px;
52+
}
53+
&:hover svg path {
54+
fill: #315efb;
55+
}
56+
}
57+
.ant-select-selector .ant-select-selection-search {
58+
left: 7px;
59+
input {
60+
height: 100%;
61+
}
62+
}
63+
&.ant-select-open {
64+
.ant-select-arrow {
65+
border-right: none;
66+
border-left: 1px solid #d7d9e0;
67+
svg g path {
68+
fill: #315efb;
69+
}
70+
}
71+
.ant-select-selection-item {
72+
opacity: 0.4;
73+
}
74+
}
75+
}
76+
`;
1077

1178
const childrenMap = {
1279
text: StringControl,
@@ -28,6 +95,8 @@ const SelectEdit = (props: SelectEditProps) => {
2895
const [currentValue, setCurrentValue] = useState(props.initialValue);
2996
return (
3097
<SelectUIView
98+
autoFocus
99+
allowClear
31100
{...defaultProps}
32101
value={currentValue}
33102
options={props.options}
@@ -67,12 +136,14 @@ export const ColumnSelectComp = (function () {
67136
)
68137
.setEditViewFn((props) => {
69138
return (
70-
<SelectEdit
71-
initialValue={props.value}
72-
options={options}
73-
onChange={props.onChange}
74-
onChangeEnd={props.onChangeEnd}
75-
/>
139+
<Wrapper>
140+
<SelectEdit
141+
initialValue={props.value}
142+
options={options}
143+
onChange={props.onChange}
144+
onChangeEnd={props.onChangeEnd}
145+
/>
146+
</Wrapper>
76147
)
77148
})
78149
.setPropertyViewFn((children) => {

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnStatusComp.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const StatusEdit = (props: StatusEditPropsType) => {
4848
const defaultStatus = useContext(StatusContext);
4949
const [status, setStatus] = useState(defaultStatus);
5050
const [allOptions, setAllOptions] = useState(BadgeStatusOptions);
51-
const [open, setOpen] = useState(true);
51+
const [open, setOpen] = useState(false);
5252

5353
return (
5454
<Wrapper>
@@ -84,18 +84,20 @@ const StatusEdit = (props: StatusEditPropsType) => {
8484
value,
8585
status: status.find((item) => item.text === value)?.status || "none",
8686
});
87+
setOpen(false)
8788
}}
8889
dropdownRender={(originNode: ReactNode) => (
8990
<DropdownStyled>
9091
<ScrollBar style={{ maxHeight: "256px" }}>{originNode}</ScrollBar>
9192
</DropdownStyled>
9293
)}
9394
dropdownStyle={{ marginTop: "7px", padding: "8px 0 6px 0" }}
94-
onBlur={props.onChangeEnd}
95-
onKeyDown={(e) => {
96-
if (e.key === "Enter") {
97-
props.onChangeEnd();
98-
}
95+
onBlur={() => {
96+
props.onChangeEnd();
97+
setOpen(false);
98+
}}
99+
onFocus={() => {
100+
setOpen(true);
99101
}}
100102
onClick={() => setOpen(!open)}
101103
>

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ export const Wrapper = styled.div`
9292
position: absolute;
9393
top: 0;
9494
background: transparent !important;
95+
padding: 8px;
96+
9597
> div {
9698
width: 100%;
9799
height: 100%;
@@ -147,7 +149,7 @@ export const Wrapper = styled.div`
147149
}
148150
}
149151
.ant-tag {
150-
margin-left: 20px;
152+
margin-left: 5px;
151153
}
152154
.ant-tag svg {
153155
margin-right: 4px;
@@ -159,6 +161,10 @@ export const DropdownStyled = styled.div`
159161
padding: 3px 8px;
160162
margin: 0 0 2px 8px;
161163
border-radius: 4px;
164+
165+
&.ant-select-item-option-active {
166+
background-color: #f2f7fc;
167+
}
162168
}
163169
.ant-select-item-option-content {
164170
display: flex;
@@ -193,7 +199,7 @@ const TagEdit = (props: TagEditPropsType) => {
193199
});
194200
return result;
195201
});
196-
const [open, setOpen] = useState(true);
202+
const [open, setOpen] = useState(false);
197203
return (
198204
<Wrapper>
199205
<CustomSelect
@@ -205,6 +211,7 @@ const TagEdit = (props: TagEditPropsType) => {
205211
defaultValue={props.value}
206212
style={{ width: "100%" }}
207213
open={open}
214+
allowClear={true}
208215
suffixIcon={<PackUpIcon />}
209216
onSearch={(value: string) => {
210217
if (defaultTags.findIndex((item) => item.includes(value)) < 0) {
@@ -216,18 +223,20 @@ const TagEdit = (props: TagEditPropsType) => {
216223
}}
217224
onChange={(value: string | string[]) => {
218225
props.onChange(value);
226+
setOpen(false)
219227
}}
220228
dropdownRender={(originNode: ReactNode) => (
221229
<DropdownStyled>
222230
<ScrollBar style={{ maxHeight: "256px" }}>{originNode}</ScrollBar>
223231
</DropdownStyled>
224232
)}
225233
dropdownStyle={{ marginTop: "7px", padding: "8px 0 6px 0" }}
226-
onBlur={props.onChangeEnd}
227-
onKeyDown={(e) => {
228-
if (e.key === "Enter") {
229-
props.onChangeEnd();
230-
}
234+
onFocus={() => {
235+
setOpen(true);
236+
}}
237+
onBlur={() => {
238+
props.onChangeEnd();
239+
setOpen(false);
231240
}}
232241
onClick={() => setOpen(!open)}
233242
>
@@ -259,7 +268,7 @@ export const ColumnTagsComp = (function () {
259268
tagOptionsList = props.tagColors;
260269
let value = props.changeValue ?? getBaseValue(props, dispatch);
261270
value = typeof value === "string" && value.split(",")[1] ? value.split(",") : value;
262-
const tags = _.isArray(value) ? value : [value];
271+
const tags = _.isArray(value) ? value : (value.length ? [value] : []);
263272
const view = tags.map((tag, index) => {
264273
// The actual eval value is of type number or boolean
265274
const tagText = String(tag);

client/packages/lowcoder/src/comps/comps/tableComp/column/simpleColumnTypeComps.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const ButtonComp = (function () {
4545
loading={props.loading}
4646
disabled={props.disabled}
4747
$buttonStyle={props.buttonType === "primary" ? style : undefined}
48+
style={{margin: 0}}
4849
>
4950
{/* prevent the button from disappearing */}
5051
{!props.text ? " " : props.text}

0 commit comments

Comments
 (0)