@dxos/react-ui 0.7.5-main.9d2a38b → 0.7.5-main.ff8607b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +75 -76
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +76 -76
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +75 -76
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Avatars/Avatar.d.ts +5 -9
- package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts +1 -2
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Buttons/IconButton.d.ts +2 -0
- package/dist/types/src/components/Buttons/IconButton.d.ts.map +1 -1
- package/dist/types/src/components/Lists/ListDropIndicator.d.ts +3 -1
- package/dist/types/src/components/Lists/ListDropIndicator.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.d.ts +35 -22
- package/dist/types/src/components/Main/Main.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts +1 -1
- package/dist/types/src/components/Menus/DropdownMenu.d.ts +2 -6
- package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.stories.d.ts +12 -5
- package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
- package/package.json +42 -42
- package/src/components/Avatars/Avatar.tsx +3 -6
- package/src/components/Buttons/IconButton.tsx +4 -3
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Lists/ListDropIndicator.tsx +15 -7
- package/src/components/Main/Main.stories.tsx +1 -1
- package/src/components/Main/Main.tsx +78 -72
- package/src/components/Tag/Tag.stories.tsx +20 -31
- package/src/components/Tag/Tag.tsx +15 -6
|
@@ -35,12 +35,14 @@ const COMPLEMENTARY_SIDEBAR_NAME = 'ComplementarySidebar';
|
|
|
35
35
|
const MAIN_NAME = 'Main';
|
|
36
36
|
const GENERIC_CONSUMER_NAME = 'GenericConsumer';
|
|
37
37
|
|
|
38
|
+
type SidebarState = 'expanded' | 'collapsed' | 'closed';
|
|
39
|
+
|
|
38
40
|
type MainContextValue = {
|
|
39
41
|
resizing: boolean;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
navigationSidebarState: SidebarState;
|
|
43
|
+
setNavigationSidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
44
|
+
complementarySidebarState: SidebarState;
|
|
45
|
+
setComplementarySidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
const landmarkAttr = 'data-main-landmark';
|
|
@@ -73,73 +75,77 @@ const useLandmarkMover = (propsOnKeyDown: ComponentPropsWithoutRef<'div'>['onKey
|
|
|
73
75
|
|
|
74
76
|
const [MainProvider, useMainContext] = createContext<MainContextValue>(MAIN_NAME, {
|
|
75
77
|
resizing: false,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
navigationSidebarState: 'closed',
|
|
79
|
+
setNavigationSidebarState: (nextState) => {
|
|
78
80
|
// TODO(burdon): Standardize with other context missing errors using raise.
|
|
79
81
|
log.warn('Attempt to set sidebar state without initializing `MainRoot`');
|
|
80
82
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
complementarySidebarState: 'closed',
|
|
84
|
+
setComplementarySidebarState: (nextState) => {
|
|
83
85
|
// TODO(burdon): Standardize with other context missing errors using raise.
|
|
84
86
|
log.warn('Attempt to set sidebar state without initializing `MainRoot`');
|
|
85
87
|
},
|
|
86
88
|
});
|
|
87
89
|
|
|
88
90
|
const useSidebars = (consumerName = GENERIC_CONSUMER_NAME) => {
|
|
89
|
-
const {
|
|
91
|
+
const { setNavigationSidebarState, navigationSidebarState, setComplementarySidebarState, complementarySidebarState } =
|
|
90
92
|
useMainContext(consumerName);
|
|
91
93
|
return {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
navigationSidebarState,
|
|
95
|
+
setNavigationSidebarState,
|
|
94
96
|
toggleNavigationSidebar: useCallback(
|
|
95
|
-
() =>
|
|
96
|
-
[
|
|
97
|
+
() => setNavigationSidebarState(navigationSidebarState === 'expanded' ? 'closed' : 'expanded'),
|
|
98
|
+
[navigationSidebarState, setNavigationSidebarState],
|
|
97
99
|
),
|
|
98
|
-
openNavigationSidebar: useCallback(() =>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
openNavigationSidebar: useCallback(() => setNavigationSidebarState('expanded'), []),
|
|
101
|
+
collapseNavigationSidebar: useCallback(() => setNavigationSidebarState('collapsed'), []),
|
|
102
|
+
closeNavigationSidebar: useCallback(() => setNavigationSidebarState('closed'), []),
|
|
103
|
+
complementarySidebarState,
|
|
104
|
+
setComplementarySidebarState,
|
|
102
105
|
toggleComplementarySidebar: useCallback(
|
|
103
|
-
() =>
|
|
104
|
-
[
|
|
106
|
+
() => setComplementarySidebarState(complementarySidebarState === 'expanded' ? 'closed' : 'expanded'),
|
|
107
|
+
[complementarySidebarState, setComplementarySidebarState],
|
|
105
108
|
),
|
|
106
|
-
openComplementarySidebar: useCallback(() =>
|
|
107
|
-
|
|
109
|
+
openComplementarySidebar: useCallback(() => setComplementarySidebarState('expanded'), []),
|
|
110
|
+
collapseComplementarySidebar: useCallback(() => setComplementarySidebarState('collapsed'), []),
|
|
111
|
+
closeComplementarySidebar: useCallback(() => setComplementarySidebarState('closed'), []),
|
|
108
112
|
};
|
|
109
113
|
};
|
|
110
114
|
|
|
111
115
|
type MainRootProps = PropsWithChildren<{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
navigationSidebarState?: SidebarState;
|
|
117
|
+
defaultNavigationSidebarState?: SidebarState;
|
|
118
|
+
onNavigationSidebarStateChange?: (nextState: SidebarState) => void;
|
|
119
|
+
complementarySidebarState?: SidebarState;
|
|
120
|
+
defaultComplementarySidebarState?: SidebarState;
|
|
121
|
+
onComplementarySidebarStateChange?: (nextState: SidebarState) => void;
|
|
118
122
|
}>;
|
|
119
123
|
|
|
120
124
|
const resizeDebounce = 3000;
|
|
121
125
|
|
|
122
126
|
const MainRoot = ({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
navigationSidebarState: propsNavigationSidebarState,
|
|
128
|
+
defaultNavigationSidebarState,
|
|
129
|
+
onNavigationSidebarStateChange,
|
|
130
|
+
complementarySidebarState: propsComplementarySidebarState,
|
|
131
|
+
defaultComplementarySidebarState,
|
|
132
|
+
onComplementarySidebarStateChange,
|
|
129
133
|
children,
|
|
130
134
|
...props
|
|
131
135
|
}: MainRootProps) => {
|
|
132
136
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
133
|
-
const [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
const [navigationSidebarState = isLg ? 'expanded' : 'collapsed', setNavigationSidebarState] =
|
|
138
|
+
useControllableState<SidebarState>({
|
|
139
|
+
prop: propsNavigationSidebarState,
|
|
140
|
+
defaultProp: defaultNavigationSidebarState,
|
|
141
|
+
onChange: onNavigationSidebarStateChange,
|
|
142
|
+
});
|
|
143
|
+
const [complementarySidebarState = isLg ? 'expanded' : 'collapsed', setComplementarySidebarState] =
|
|
144
|
+
useControllableState<SidebarState>({
|
|
145
|
+
prop: propsComplementarySidebarState,
|
|
146
|
+
defaultProp: defaultComplementarySidebarState,
|
|
147
|
+
onChange: onComplementarySidebarStateChange,
|
|
148
|
+
});
|
|
143
149
|
|
|
144
150
|
const [resizing, setResizing] = useState(false);
|
|
145
151
|
const resizeInterval = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -164,10 +170,10 @@ const MainRoot = ({
|
|
|
164
170
|
<MainProvider
|
|
165
171
|
{...props}
|
|
166
172
|
{...{
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
173
|
+
navigationSidebarState,
|
|
174
|
+
setNavigationSidebarState,
|
|
175
|
+
complementarySidebarState,
|
|
176
|
+
setComplementarySidebarState,
|
|
171
177
|
}}
|
|
172
178
|
resizing={resizing}
|
|
173
179
|
>
|
|
@@ -184,15 +190,15 @@ const handleOpenAutoFocus = (event: Event) => {
|
|
|
184
190
|
|
|
185
191
|
type MainSidebarProps = ThemedClassName<ComponentPropsWithRef<typeof DialogContent>> & {
|
|
186
192
|
swipeToDismiss?: boolean;
|
|
187
|
-
|
|
193
|
+
state?: SidebarState;
|
|
188
194
|
resizing?: boolean;
|
|
189
|
-
|
|
195
|
+
onStateChange?: (nextState: SidebarState) => void;
|
|
190
196
|
side: 'inline-start' | 'inline-end';
|
|
191
197
|
};
|
|
192
198
|
|
|
193
199
|
const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
194
200
|
(
|
|
195
|
-
{ classNames, children, swipeToDismiss, onOpenAutoFocus,
|
|
201
|
+
{ classNames, children, swipeToDismiss, onOpenAutoFocus, state, resizing, onStateChange, side, ...props },
|
|
196
202
|
forwardedRef,
|
|
197
203
|
) => {
|
|
198
204
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
@@ -200,7 +206,7 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
200
206
|
const ref = useForwardedRef(forwardedRef);
|
|
201
207
|
const noopRef = useRef(null);
|
|
202
208
|
useSwipeToDismiss(swipeToDismiss ? ref : noopRef, {
|
|
203
|
-
onDismiss: () =>
|
|
209
|
+
onDismiss: () => onStateChange?.('closed'),
|
|
204
210
|
});
|
|
205
211
|
const handleKeyDown = useCallback(
|
|
206
212
|
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
@@ -213,16 +219,16 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
213
219
|
);
|
|
214
220
|
const Root = isLg ? Primitive.div : DialogContent;
|
|
215
221
|
return (
|
|
216
|
-
<DialogRoot open={
|
|
222
|
+
<DialogRoot open={state !== 'closed'} modal={false}>
|
|
217
223
|
<Root
|
|
218
224
|
{...(!isLg && { forceMount: true, tabIndex: -1, onOpenAutoFocus: onOpenAutoFocus ?? handleOpenAutoFocus })}
|
|
219
225
|
{...props}
|
|
220
226
|
data-side={side === 'inline-end' ? 'ie' : 'is'}
|
|
221
|
-
data-state={
|
|
227
|
+
data-state={state}
|
|
222
228
|
data-resizing={resizing ? 'true' : 'false'}
|
|
223
229
|
className={tx('main.sidebar', 'main__sidebar', {}, classNames)}
|
|
224
230
|
onKeyDown={handleKeyDown}
|
|
225
|
-
{...(
|
|
231
|
+
{...(state === 'closed' && { inert: 'true' })}
|
|
226
232
|
ref={ref}
|
|
227
233
|
>
|
|
228
234
|
{children}
|
|
@@ -232,17 +238,17 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
232
238
|
},
|
|
233
239
|
);
|
|
234
240
|
|
|
235
|
-
type MainNavigationSidebarProps = Omit<MainSidebarProps, '
|
|
241
|
+
type MainNavigationSidebarProps = Omit<MainSidebarProps, 'expanded' | 'side'>;
|
|
236
242
|
|
|
237
243
|
const MainNavigationSidebar = forwardRef<HTMLDivElement, MainNavigationSidebarProps>((props, forwardedRef) => {
|
|
238
|
-
const {
|
|
244
|
+
const { navigationSidebarState, setNavigationSidebarState, resizing } = useMainContext(NAVIGATION_SIDEBAR_NAME);
|
|
239
245
|
const mover = useLandmarkMover(props.onKeyDown, '0');
|
|
240
246
|
return (
|
|
241
247
|
<MainSidebar
|
|
242
248
|
{...mover}
|
|
243
249
|
{...props}
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
state={navigationSidebarState}
|
|
251
|
+
onStateChange={setNavigationSidebarState}
|
|
246
252
|
resizing={resizing}
|
|
247
253
|
side='inline-start'
|
|
248
254
|
ref={forwardedRef}
|
|
@@ -252,18 +258,18 @@ const MainNavigationSidebar = forwardRef<HTMLDivElement, MainNavigationSidebarPr
|
|
|
252
258
|
|
|
253
259
|
MainNavigationSidebar.displayName = NAVIGATION_SIDEBAR_NAME;
|
|
254
260
|
|
|
255
|
-
type MainComplementarySidebarProps = Omit<MainSidebarProps, '
|
|
261
|
+
type MainComplementarySidebarProps = Omit<MainSidebarProps, 'expanded' | 'side'>;
|
|
256
262
|
|
|
257
263
|
const MainComplementarySidebar = forwardRef<HTMLDivElement, MainComplementarySidebarProps>((props, forwardedRef) => {
|
|
258
|
-
const {
|
|
264
|
+
const { complementarySidebarState, setComplementarySidebarState, resizing } =
|
|
259
265
|
useMainContext(COMPLEMENTARY_SIDEBAR_NAME);
|
|
260
266
|
const mover = useLandmarkMover(props.onKeyDown, '2');
|
|
261
267
|
return (
|
|
262
268
|
<MainSidebar
|
|
263
269
|
{...mover}
|
|
264
270
|
{...props}
|
|
265
|
-
|
|
266
|
-
|
|
271
|
+
state={complementarySidebarState}
|
|
272
|
+
onStateChange={setComplementarySidebarState}
|
|
267
273
|
resizing={resizing}
|
|
268
274
|
side='inline-end'
|
|
269
275
|
ref={forwardedRef}
|
|
@@ -281,7 +287,7 @@ type MainProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.div>> &
|
|
|
281
287
|
|
|
282
288
|
const MainContent = forwardRef<HTMLDivElement, MainProps>(
|
|
283
289
|
({ asChild, classNames, bounce, handlesFocus, children, role, ...props }: MainProps, forwardedRef) => {
|
|
284
|
-
const {
|
|
290
|
+
const { navigationSidebarState, complementarySidebarState } = useMainContext(MAIN_NAME);
|
|
285
291
|
const { tx } = useThemeContext();
|
|
286
292
|
const Root = asChild ? Slot : role ? 'div' : 'main';
|
|
287
293
|
|
|
@@ -292,8 +298,8 @@ const MainContent = forwardRef<HTMLDivElement, MainProps>(
|
|
|
292
298
|
role={role}
|
|
293
299
|
{...(handlesFocus && { ...mover })}
|
|
294
300
|
{...props}
|
|
295
|
-
data-sidebar-inline-start-state={
|
|
296
|
-
data-sidebar-inline-end-state={
|
|
301
|
+
data-sidebar-inline-start-state={navigationSidebarState}
|
|
302
|
+
data-sidebar-inline-end-state={complementarySidebarState}
|
|
297
303
|
className={tx('main.content', 'main', { bounce, handlesFocus }, classNames)}
|
|
298
304
|
ref={forwardedRef}
|
|
299
305
|
>
|
|
@@ -309,23 +315,23 @@ type MainOverlayProps = ThemedClassName<Omit<ComponentPropsWithRef<typeof Primit
|
|
|
309
315
|
|
|
310
316
|
const MainOverlay = forwardRef<HTMLDivElement, MainOverlayProps>(({ classNames, ...props }, forwardedRef) => {
|
|
311
317
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
312
|
-
const {
|
|
318
|
+
const { navigationSidebarState, setNavigationSidebarState, complementarySidebarState, setComplementarySidebarState } =
|
|
313
319
|
useMainContext(MAIN_NAME);
|
|
314
320
|
const { tx } = useThemeContext();
|
|
315
321
|
return (
|
|
316
322
|
<div
|
|
317
323
|
onClick={() => {
|
|
318
|
-
|
|
319
|
-
|
|
324
|
+
setNavigationSidebarState('collapsed');
|
|
325
|
+
setComplementarySidebarState('collapsed');
|
|
320
326
|
}}
|
|
321
327
|
{...props}
|
|
322
328
|
className={tx(
|
|
323
329
|
'main.overlay',
|
|
324
330
|
'main__overlay',
|
|
325
|
-
{ isLg, inlineStartSidebarOpen:
|
|
331
|
+
{ isLg, inlineStartSidebarOpen: navigationSidebarState, inlineEndSidebarOpen: complementarySidebarState },
|
|
326
332
|
classNames,
|
|
327
333
|
)}
|
|
328
|
-
data-state={
|
|
334
|
+
data-state={navigationSidebarState === 'expanded' || complementarySidebarState === 'expanded' ? 'open' : 'closed'}
|
|
329
335
|
aria-hidden='true'
|
|
330
336
|
ref={forwardedRef}
|
|
331
337
|
/>
|
|
@@ -340,6 +346,6 @@ export const Main = {
|
|
|
340
346
|
ComplementarySidebar: MainComplementarySidebar,
|
|
341
347
|
};
|
|
342
348
|
|
|
343
|
-
export { useMainContext, useSidebars };
|
|
349
|
+
export { useMainContext, useSidebars, useLandmarkMover };
|
|
344
350
|
|
|
345
|
-
export type { MainRootProps, MainProps, MainOverlayProps, MainNavigationSidebarProps };
|
|
351
|
+
export type { MainRootProps, MainProps, MainOverlayProps, MainNavigationSidebarProps, SidebarState };
|
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
|
+
import React from 'react';
|
|
4
5
|
|
|
6
|
+
import { hueTokenThemes } from '@dxos/react-ui-theme';
|
|
5
7
|
import '@dxos-theme';
|
|
8
|
+
import { type ChromaticPalette, type MessageValence } from '@dxos/react-ui-types';
|
|
6
9
|
|
|
7
10
|
import { Tag } from './Tag';
|
|
8
11
|
import { withTheme } from '../../testing';
|
|
9
12
|
|
|
13
|
+
const palettes = ['neutral', 'success', 'info', 'warning', 'error', ...Object.keys(hueTokenThemes)] as (
|
|
14
|
+
| ChromaticPalette
|
|
15
|
+
| MessageValence
|
|
16
|
+
)[];
|
|
17
|
+
|
|
10
18
|
export default {
|
|
11
19
|
title: 'ui/react-ui-core/Tag',
|
|
12
20
|
component: Tag,
|
|
13
21
|
decorators: [withTheme],
|
|
14
22
|
parameters: { chromatic: { disableSnapshot: false } },
|
|
15
|
-
|
|
16
|
-
palette: {
|
|
17
|
-
control: 'select',
|
|
18
|
-
options: [
|
|
19
|
-
'neutral',
|
|
20
|
-
'success',
|
|
21
|
-
'info',
|
|
22
|
-
'warning',
|
|
23
|
-
'error',
|
|
24
|
-
'red',
|
|
25
|
-
'orange',
|
|
26
|
-
'amber',
|
|
27
|
-
'yellow',
|
|
28
|
-
'lime',
|
|
29
|
-
'green',
|
|
30
|
-
'emerald',
|
|
31
|
-
'teal',
|
|
32
|
-
'cyan',
|
|
33
|
-
'sky',
|
|
34
|
-
'blue',
|
|
35
|
-
'indigo',
|
|
36
|
-
'violet',
|
|
37
|
-
'purple',
|
|
38
|
-
'fuchsia',
|
|
39
|
-
'pink',
|
|
40
|
-
'rose',
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
} as any;
|
|
23
|
+
} as const;
|
|
45
24
|
|
|
46
|
-
export const Default = {
|
|
25
|
+
export const Default = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<div role='grid' className='grid grid-cols-5 gap-2 max-is-screen-md'>
|
|
28
|
+
{palettes.map((palette) => (
|
|
29
|
+
<Tag key={palette} palette={palette}>
|
|
30
|
+
{palette}
|
|
31
|
+
</Tag>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
@@ -16,8 +16,17 @@ export type TagProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.sp
|
|
|
16
16
|
asChild?: boolean;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const Tag = forwardRef<HTMLSpanElement, TagProps>(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export const Tag = forwardRef<HTMLSpanElement, TagProps>(
|
|
20
|
+
({ asChild, palette = 'neutral', classNames, ...props }, forwardedRef) => {
|
|
21
|
+
const { tx } = useThemeContext();
|
|
22
|
+
const Root = asChild ? Slot : Primitive.span;
|
|
23
|
+
return (
|
|
24
|
+
<Root
|
|
25
|
+
{...props}
|
|
26
|
+
className={tx('tag.root', 'dx-tag', { palette }, classNames)}
|
|
27
|
+
data-hue={palette}
|
|
28
|
+
ref={forwardedRef}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
},
|
|
32
|
+
);
|