Skip to content

Commit f752d3f

Browse files
committed
feat(platform): add detail-view component
1 parent 736d900 commit f752d3f

8 files changed

Lines changed: 144 additions & 27 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { isArray, isNull, isNumber, isString, isUndefined } from 'lodash';
2+
import React from 'react';
3+
4+
import { getClassName } from '@react-devui/utils';
5+
6+
export interface AppDetailViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
7+
aList: {
8+
label: string;
9+
content: React.ReactNode;
10+
isEmpty?: boolean;
11+
}[];
12+
aCol?: number | true | Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number | true>;
13+
aGutter?: number | [number?, number?];
14+
aLabelWidth?: string | number;
15+
aEmpty?: React.ReactNode;
16+
aVertical?: boolean;
17+
}
18+
19+
export function AppDetailView(props: AppDetailViewProps): JSX.Element | null {
20+
const {
21+
aList,
22+
aCol = { xs: 12, md: 6, lg: 4, xxl: 3 },
23+
aGutter,
24+
aLabelWidth,
25+
aEmpty = '-',
26+
aVertical = false,
27+
28+
...restProps
29+
} = props;
30+
31+
const [gutterX, gutterY] = isArray(aGutter) ? aGutter : [aGutter, aGutter];
32+
const col = (() => {
33+
if (aCol === true) {
34+
return 'col';
35+
}
36+
if (isNumber(aCol)) {
37+
return `col-${aCol}`;
38+
}
39+
40+
const classNames: string[] = [];
41+
Object.entries(aCol).forEach(([breakpoint, col]) => {
42+
const className: (string | number)[] = ['col'];
43+
if (breakpoint !== 'xs') {
44+
className.push(breakpoint);
45+
}
46+
if (col !== true) {
47+
className.push(col);
48+
}
49+
classNames.push(className.join('-'));
50+
});
51+
return classNames.join(' ');
52+
})();
53+
54+
const labelWidth = (() => {
55+
if (aVertical) {
56+
return undefined;
57+
}
58+
59+
let maxLength = 0;
60+
if (aList) {
61+
aList.forEach((item) => {
62+
maxLength = Math.max(item.label.length, maxLength);
63+
});
64+
}
65+
66+
return isUndefined(aLabelWidth) ? maxLength + 1 + 'em' : aLabelWidth;
67+
})();
68+
69+
return (
70+
<div
71+
{...restProps}
72+
className={getClassName(restProps.className, 'app-detail-view', 'row', {
73+
'app-detail-view--vertical': aVertical,
74+
[`gx-${gutterX}`]: gutterX,
75+
[`gy-${gutterY}`]: gutterY,
76+
})}
77+
>
78+
{aList.map(({ label, content, isEmpty: _isEmpty }) => {
79+
const isEmpty = isUndefined(_isEmpty)
80+
? (isString(content) && content.length === 0) || isUndefined(content) || isNull(content)
81+
: _isEmpty;
82+
return (
83+
<div key={label} className={getClassName('app-detail-view__item', col)}>
84+
<div className="app-detail-view__item-label" style={{ width: labelWidth }}>
85+
{label}
86+
</div>
87+
<div className="app-detail-view__item-content">{isEmpty ? aEmpty : content}</div>
88+
</div>
89+
);
90+
})}
91+
</div>
92+
);
93+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './DetailView';

packages/platform/src/app/components/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
export type { AppChartProps } from './chart';
22
export { AppChart } from './chart';
33

4+
export type { AppDetailViewProps } from './detail-view';
5+
export { AppDetailView } from './detail-view';
6+
47
export { AppFCPLoader } from './fcp-loader';
58

69
export { AppLanguage } from './language';

packages/platform/src/app/routes/list/standard-table/StandardTable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export default function StandardTable(): JSX.Element | null {
252252
isEmpty: deviceQuery.status.length === 0,
253253
},
254254
]}
255+
aLabelWidth={72}
255256
aSearchValue={deviceQuery.keyword}
256257
aSearchPlaceholder="ID, Name"
257258
onSearchValueChange={(value) => {

packages/platform/src/app/routes/list/standard-table/detail/Detail.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { useAsync, useImmer, useMount } from '@react-devui/hooks';
1010
import { EditOutlined } from '@react-devui/icons';
1111
import { DButton, DCard, DSeparator, DSpinner, DTable, FormControl, FormGroup, useForm, Validators } from '@react-devui/ui';
1212

13-
import { AppRouteHeader } from '../../../../components';
13+
import { AppDetailView, AppRouteHeader } from '../../../../components';
1414
import { useAPI } from '../../../../hooks';
1515
import { AppDeviceModal } from '../DeviceModal';
1616
import styles from './Detail.module.scss';
@@ -128,24 +128,24 @@ export default function Detail(): JSX.Element | null {
128128
<DCard>
129129
<DCard.Content>
130130
<div className="app-title mb-3">Title 1</div>
131-
<div className="row g-3">
132-
{Array.from({ length: 5 }).map((_, n) => (
133-
<div key={n} className="col-12 col-md-6 col-lg-4">
134-
<span className="app-label">Label {n}</span>
135-
<span>Content {n}</span>
136-
</div>
137-
))}
138-
</div>
131+
<AppDetailView
132+
aGutter={3}
133+
aList={Array.from({ length: 5 }).map((_, n) => ({
134+
label: `Label ${n}`,
135+
content: n === 1 ? null : n === 3 ? 'This is a long long long long long long long long text' : `Content ${n}`,
136+
}))}
137+
aLabelWidth={72}
138+
/>
139139
<DSeparator />
140140
<div className="app-title mb-3">Title 2</div>
141-
<div className="row g-3">
142-
{Array.from({ length: 5 }).map((_, n) => (
143-
<div key={n} className="col-12 col-md-6 col-lg-4">
144-
<span className="app-label">Label {n}</span>
145-
<span>Content {n}</span>
146-
</div>
147-
))}
148-
</div>
141+
<AppDetailView
142+
aGutter={3}
143+
aList={Array.from({ length: 5 }).map((_, n) => ({
144+
label: `Label ${n}`,
145+
content: n === 1 ? null : n === 3 ? 'This is a long long long long long long long long text' : `Content ${n}`,
146+
}))}
147+
aVertical
148+
/>
149149
</DCard.Content>
150150
</DCard>
151151
<DCard className="mt-3">

packages/platform/src/styles/_app.scss

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,3 @@ body {
5151
font-size: 1.1em;
5252
font-weight: 500;
5353
}
54-
55-
.app-label {
56-
margin-right: 8px;
57-
color: var(--#{$rd-prefix}text-color-sub);
58-
59-
&::after {
60-
margin-left: 2px;
61-
content: ':';
62-
}
63-
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@include b(detail-view) {
2+
@include m(vertical) {
3+
@include e(item) {
4+
flex-direction: column;
5+
}
6+
7+
@include e(item-label) {
8+
&::after {
9+
content: none;
10+
}
11+
}
12+
}
13+
14+
@include e(item) {
15+
display: inline-flex;
16+
gap: 8px;
17+
}
18+
19+
@include e(item-label) {
20+
flex-shrink: 0;
21+
color: var(--#{$rd-prefix}text-color-sub);
22+
23+
&::after {
24+
margin-left: 2px;
25+
content: ':';
26+
}
27+
}
28+
}

packages/platform/src/styles/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
@import 'root';
99

1010
@import 'components/chart';
11+
@import 'components/detail-view';
1112
@import 'components/fcp-loader';
1213
@import 'components/language';
1314
@import 'components/list';

0 commit comments

Comments
 (0)