@arcblock/ux 1.16.61 → 1.16.64
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/lib/Blocklet/blocklet.js +179 -0
- package/lib/Blocklet/index.js +11 -251
- package/lib/Blocklet/utils.js +18 -0
- package/lib/BlockletNFT/index.js +257 -0
- package/lib/Layout/dashboard/header.js +15 -2
- package/lib/Layout/dashboard/index.js +2 -2
- package/lib/Layout/dashboard/sidebar.js +1 -1
- package/lib/Layout/index.js +1 -1
- package/package.json +4 -4
- package/src/Blocklet/blocklet.js +254 -0
- package/src/Blocklet/index.js +3 -422
- package/src/Blocklet/utils.js +49 -0
- package/src/BlockletNFT/index.js +379 -0
- package/src/Layout/dashboard/header.js +14 -14
- package/src/Layout/dashboard/index.js +2 -2
- package/src/Layout/dashboard/sidebar.js +1 -1
- package/src/Layout/index.js +2 -2
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import Portal from '@material-ui/core/Portal';
|
|
5
|
+
import Typography from '@material-ui/core/Typography';
|
|
6
|
+
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
7
|
+
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
8
|
+
import useTheme from '@material-ui/core/styles/useTheme';
|
|
9
|
+
import Avatar from '@arcblock/did-connect/lib/Avatar';
|
|
10
|
+
|
|
11
|
+
import Icon from '../Icon';
|
|
12
|
+
import Button from '../Button';
|
|
13
|
+
import Img from '../Img';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 美化数字 1000-->1k
|
|
17
|
+
* @param {*} _size
|
|
18
|
+
* @param {*} digits
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
function prettySize(_size, digits = 1) {
|
|
22
|
+
let size = _size;
|
|
23
|
+
const list = ['', 'k', 'm', 'b'];
|
|
24
|
+
let index = 0;
|
|
25
|
+
while (size > 1000 && index < list.length - 1) {
|
|
26
|
+
size = (size / 1000).toFixed(digits);
|
|
27
|
+
index += 1;
|
|
28
|
+
}
|
|
29
|
+
return _size && `${size}${list[index]}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Div = styled.div`
|
|
33
|
+
&.arcblock-blocklet {
|
|
34
|
+
padding: 0 16px;
|
|
35
|
+
background: ${props => props.theme.palette.common.white};
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
box-shadow: 0px 0px 8px #f0f0f0;
|
|
38
|
+
&:hover {
|
|
39
|
+
box-shadow: 0px 2px 12px #f0f0f0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
.arcblock-blocklet__content {
|
|
43
|
+
padding: 16px 0 0 0;
|
|
44
|
+
}
|
|
45
|
+
.arcblock-blocklet__content--main {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
}
|
|
50
|
+
.arcblock-blocklet__content--body {
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
flex: 1;
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: flex-start;
|
|
55
|
+
}
|
|
56
|
+
.arcblock-blocklet__addons {
|
|
57
|
+
padding: 16px 0;
|
|
58
|
+
}
|
|
59
|
+
.arcblock-blocklet__cover {
|
|
60
|
+
width: 80px;
|
|
61
|
+
height: 80px;
|
|
62
|
+
margin-right: 12px;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
border-radius: 12px;
|
|
65
|
+
/* see: https://stackoverflow.com/questions/49066011/overflow-hidden-with-border-radius-not-working-on-safari */
|
|
66
|
+
transform: translateZ(0);
|
|
67
|
+
}
|
|
68
|
+
.arcblock-blocklet__button--hover {
|
|
69
|
+
&:not(.Mui-disabled) {
|
|
70
|
+
position: relative;
|
|
71
|
+
z-index: 1;
|
|
72
|
+
&::before {
|
|
73
|
+
content: '';
|
|
74
|
+
position: absolute;
|
|
75
|
+
height: 100%;
|
|
76
|
+
width: 100%;
|
|
77
|
+
left: 0;
|
|
78
|
+
top: 0;
|
|
79
|
+
transition: opacity 0.3s;
|
|
80
|
+
}
|
|
81
|
+
&:hover::before {
|
|
82
|
+
opacity: 0;
|
|
83
|
+
}
|
|
84
|
+
&::after {
|
|
85
|
+
content: '';
|
|
86
|
+
position: absolute;
|
|
87
|
+
height: 100%;
|
|
88
|
+
width: 100%;
|
|
89
|
+
background-color: ${props => props.theme.palette.primary.main};
|
|
90
|
+
transform: scale(0.1);
|
|
91
|
+
opacity: 0;
|
|
92
|
+
z-index: -1;
|
|
93
|
+
transition: transform 0.3s, opacity 0.3s, background-color 0.3s;
|
|
94
|
+
}
|
|
95
|
+
&:hover::after {
|
|
96
|
+
opacity: 1;
|
|
97
|
+
transform-origin: center;
|
|
98
|
+
transform: scale(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&:not(.Mui-disabled) {
|
|
103
|
+
background-color: transparent !important;
|
|
104
|
+
color: ${props => props.theme.palette.primary.main};
|
|
105
|
+
}
|
|
106
|
+
&:not(.Mui-disabled) {
|
|
107
|
+
&:hover {
|
|
108
|
+
color: ${props => props.theme.palette.common.white};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
.arcblock-blocklet__info {
|
|
113
|
+
flex: 1;
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
.arcblock-blocklet__button {
|
|
116
|
+
margin-top: 16px;
|
|
117
|
+
display: inline-block;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
.arcblock-blocklet__title {
|
|
121
|
+
margin: 0;
|
|
122
|
+
font-size: 18px;
|
|
123
|
+
font-weight: 600;
|
|
124
|
+
overflow: hidden;
|
|
125
|
+
text-overflow: ellipsis;
|
|
126
|
+
white-space: nowrap;
|
|
127
|
+
}
|
|
128
|
+
.arcblock-blocklet__describe {
|
|
129
|
+
margin: 0 0 2px 0;
|
|
130
|
+
color: #999;
|
|
131
|
+
font-size: 14px;
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
text-overflow: ellipsis;
|
|
134
|
+
white-space: nowrap;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.arcblock-blocklet__addons {
|
|
138
|
+
display: flex;
|
|
139
|
+
justify-content: space-between;
|
|
140
|
+
color: #999;
|
|
141
|
+
font-size: 14px;
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
.arcblock-blocklet__addons--item {
|
|
145
|
+
white-space: nowrap;
|
|
146
|
+
}
|
|
147
|
+
&.arcblock-blocklet--size-md {
|
|
148
|
+
&:hover {
|
|
149
|
+
position: relative;
|
|
150
|
+
}
|
|
151
|
+
.arcblock-blocklet__title {
|
|
152
|
+
height: 2.3em;
|
|
153
|
+
margin-bottom: 3px;
|
|
154
|
+
display: -webkit-box;
|
|
155
|
+
-webkit-box-orient: vertical;
|
|
156
|
+
-webkit-line-clamp: 2;
|
|
157
|
+
overflow: hidden;
|
|
158
|
+
text-overflow: initial;
|
|
159
|
+
white-space: initial;
|
|
160
|
+
word-break: break-all;
|
|
161
|
+
}
|
|
162
|
+
.arcblock-blocklet__describe {
|
|
163
|
+
white-space: normal;
|
|
164
|
+
height: 2.86em;
|
|
165
|
+
}
|
|
166
|
+
.arcblock-blocklet__button {
|
|
167
|
+
margin-top: 5px;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
&.arcblock-blocklet--size-sm,
|
|
171
|
+
&.arcblock-blocklet--size-xs {
|
|
172
|
+
.arcblock-blocklet__cover {
|
|
173
|
+
width: 40px;
|
|
174
|
+
height: 40px;
|
|
175
|
+
border-radius: 6px;
|
|
176
|
+
}
|
|
177
|
+
.arcblock-blocklet__content {
|
|
178
|
+
padding: 16px 0;
|
|
179
|
+
}
|
|
180
|
+
.arcblock-blocklet__addons {
|
|
181
|
+
padding: 8px 0;
|
|
182
|
+
.arcblock-blocklet__addons--item {
|
|
183
|
+
font-size: 12px;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
&.arcblock-blocklet--size-xs {
|
|
188
|
+
.arcblock-blocklet__addons {
|
|
189
|
+
display: none !important;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
function BlockletIcon({ name }) {
|
|
194
|
+
return <Icon name={name} color="inherit" size={15} style={{ marginRight: 8 }} />;
|
|
195
|
+
}
|
|
196
|
+
BlockletIcon.propTypes = {
|
|
197
|
+
name: PropTypes.string.isRequired,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export default function BlockletNFT({
|
|
201
|
+
title,
|
|
202
|
+
did,
|
|
203
|
+
description,
|
|
204
|
+
cover,
|
|
205
|
+
size,
|
|
206
|
+
addons,
|
|
207
|
+
button,
|
|
208
|
+
buttonText,
|
|
209
|
+
buttonDisabled,
|
|
210
|
+
buttonLoading,
|
|
211
|
+
isStickyButton,
|
|
212
|
+
onButtonClick,
|
|
213
|
+
onMainClick,
|
|
214
|
+
onTagClick,
|
|
215
|
+
className,
|
|
216
|
+
scaleClickZone,
|
|
217
|
+
...rest
|
|
218
|
+
}) {
|
|
219
|
+
const wrapHandler =
|
|
220
|
+
(handler, stopFn = () => false) =>
|
|
221
|
+
(e, ...args) => {
|
|
222
|
+
if (stopFn()) {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
e.stopPropagation();
|
|
225
|
+
} else if (handler instanceof Function) {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
e.stopPropagation();
|
|
228
|
+
handler(...args);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const _onButtonClick = wrapHandler(onButtonClick, () => {
|
|
232
|
+
// stop click while custom button or buttonDisabled or buttondLoading
|
|
233
|
+
if (button || buttonDisabled || buttonLoading) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
});
|
|
238
|
+
const _onMainClick = wrapHandler(onMainClick);
|
|
239
|
+
|
|
240
|
+
const theme = useTheme();
|
|
241
|
+
const isDownSm = useMediaQuery(theme.breakpoints.down('sm'));
|
|
242
|
+
const isDownMd = useMediaQuery(theme.breakpoints.down('md'));
|
|
243
|
+
const isUpLg = useMediaQuery(theme.breakpoints.up('lg'));
|
|
244
|
+
|
|
245
|
+
// If size is auto, need calculate actual size according to screen size
|
|
246
|
+
// eslint-disable-next-line no-nested-ternary
|
|
247
|
+
const actualSize = size === 'auto' ? (isDownSm ? 'xs' : isDownMd ? 'sm' : 'md') : size;
|
|
248
|
+
// eslint-disable-next-line no-nested-ternary
|
|
249
|
+
const didAvatarSize = size === 'auto' ? (isUpLg ? 80 : 40) : size === 'md' ? 80 : 40;
|
|
250
|
+
const container = useRef(null);
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<Div
|
|
254
|
+
{...rest}
|
|
255
|
+
scaleClickZone={scaleClickZone}
|
|
256
|
+
className={`${className} arcblock-blocklet arcblock-blocklet--size-${actualSize}`}>
|
|
257
|
+
<div className="arcblock-blocklet__content">
|
|
258
|
+
<div className="arcblock-blocklet__content--main" onClick={_onMainClick} ref={container}>
|
|
259
|
+
<div className="arcblock-blocklet__content--body">
|
|
260
|
+
{cover ? (
|
|
261
|
+
<div className="arcblock-blocklet__cover">
|
|
262
|
+
<Img src={cover} />
|
|
263
|
+
</div>
|
|
264
|
+
) : (
|
|
265
|
+
did && (
|
|
266
|
+
<div className="arcblock-blocklet__cover">
|
|
267
|
+
<Avatar did={did} size={didAvatarSize} />
|
|
268
|
+
</div>
|
|
269
|
+
)
|
|
270
|
+
)}
|
|
271
|
+
<div className="arcblock-blocklet__info">
|
|
272
|
+
<Typography
|
|
273
|
+
component="h3"
|
|
274
|
+
variant="h3"
|
|
275
|
+
className="arcblock-blocklet__title"
|
|
276
|
+
title={title}>
|
|
277
|
+
{title}
|
|
278
|
+
</Typography>
|
|
279
|
+
{description && (
|
|
280
|
+
<Typography
|
|
281
|
+
component="div"
|
|
282
|
+
variant="body2"
|
|
283
|
+
className="arcblock-blocklet__describe"
|
|
284
|
+
title={description}>
|
|
285
|
+
{description}
|
|
286
|
+
</Typography>
|
|
287
|
+
)}
|
|
288
|
+
{['md', 'sm', 'xs'].includes(actualSize) && (
|
|
289
|
+
<Portal container={container.current} disablePortal={actualSize === 'md'}>
|
|
290
|
+
<div
|
|
291
|
+
className="arcblock-blocklet__button"
|
|
292
|
+
onClick={_onButtonClick}
|
|
293
|
+
style={{ display: isStickyButton ? 'block' : '' }}>
|
|
294
|
+
{button ||
|
|
295
|
+
(onButtonClick && (
|
|
296
|
+
<Button
|
|
297
|
+
className="arcblock-blocklet__button--hover"
|
|
298
|
+
variant="outlined"
|
|
299
|
+
color="primary"
|
|
300
|
+
size="small"
|
|
301
|
+
disabled={buttonDisabled || buttonLoading}
|
|
302
|
+
style={
|
|
303
|
+
actualSize === 'md'
|
|
304
|
+
? { padding: '3px 20px', fontSize: '14px' }
|
|
305
|
+
: { padding: '3px 15px', minWidth: '54px', fontSize: '13px' }
|
|
306
|
+
}>
|
|
307
|
+
{buttonLoading && (
|
|
308
|
+
<CircularProgress
|
|
309
|
+
size={actualSize === 'md' ? 15 : 13}
|
|
310
|
+
style={{ marginRight: 3, color: 'inherit' }}
|
|
311
|
+
/>
|
|
312
|
+
)}
|
|
313
|
+
{buttonText}
|
|
314
|
+
</Button>
|
|
315
|
+
))}
|
|
316
|
+
</div>
|
|
317
|
+
</Portal>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
<div className="arcblock-blocklet__addons">
|
|
324
|
+
{addons.map((item, index) => (
|
|
325
|
+
<Typography
|
|
326
|
+
component="span"
|
|
327
|
+
variant="caption"
|
|
328
|
+
className="arcblock-blocklet__addons--item"
|
|
329
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
330
|
+
key={index}
|
|
331
|
+
title={item.title}>
|
|
332
|
+
{item.empty ? null : (
|
|
333
|
+
<>
|
|
334
|
+
<BlockletIcon name={item.icon} />
|
|
335
|
+
{item.pretty ? prettySize(item.value) : item.value}
|
|
336
|
+
</>
|
|
337
|
+
)}
|
|
338
|
+
</Typography>
|
|
339
|
+
))}
|
|
340
|
+
</div>
|
|
341
|
+
</Div>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
BlockletNFT.propTypes = {
|
|
345
|
+
title: PropTypes.string.isRequired,
|
|
346
|
+
did: PropTypes.string,
|
|
347
|
+
isStickyButton: PropTypes.bool,
|
|
348
|
+
description: PropTypes.string,
|
|
349
|
+
cover: PropTypes.string,
|
|
350
|
+
buttonText: PropTypes.string,
|
|
351
|
+
buttonDisabled: PropTypes.bool,
|
|
352
|
+
buttonLoading: PropTypes.bool,
|
|
353
|
+
button: PropTypes.element,
|
|
354
|
+
addons: PropTypes.arrayOf(PropTypes.object),
|
|
355
|
+
size: PropTypes.oneOf(['xs', 'sm', 'md', 'auto']),
|
|
356
|
+
onButtonClick: PropTypes.func,
|
|
357
|
+
onMainClick: PropTypes.func,
|
|
358
|
+
onTagClick: PropTypes.func,
|
|
359
|
+
className: PropTypes.string,
|
|
360
|
+
scaleClickZone: PropTypes.number,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
BlockletNFT.defaultProps = {
|
|
364
|
+
description: null,
|
|
365
|
+
cover: null,
|
|
366
|
+
did: null,
|
|
367
|
+
isStickyButton: false,
|
|
368
|
+
buttonText: 'Install',
|
|
369
|
+
buttonDisabled: false,
|
|
370
|
+
buttonLoading: false,
|
|
371
|
+
button: null,
|
|
372
|
+
size: 'auto',
|
|
373
|
+
addons: [],
|
|
374
|
+
onButtonClick: null,
|
|
375
|
+
onMainClick: null,
|
|
376
|
+
onTagClick: null,
|
|
377
|
+
className: null,
|
|
378
|
+
scaleClickZone: 1.5,
|
|
379
|
+
};
|
|
@@ -6,6 +6,7 @@ import AppBar from '@material-ui/core/AppBar';
|
|
|
6
6
|
import Toolbar from '@material-ui/core/Toolbar';
|
|
7
7
|
import Box from '@material-ui/core/Box';
|
|
8
8
|
import Typography from '@material-ui/core/Typography';
|
|
9
|
+
import Hidden from '@material-ui/core/Hidden';
|
|
9
10
|
import MenuIcon from '@material-ui/icons/Menu';
|
|
10
11
|
import { Link } from 'react-router-dom';
|
|
11
12
|
|
|
@@ -70,19 +71,7 @@ const StyledAppBar = styled(AppBar)`
|
|
|
70
71
|
.header-menu {
|
|
71
72
|
display: none;
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
/*
|
|
75
|
-
以下样式用来替代 display:none, 如果为 logo prop 传入一个自定义的 svg, 并且 svg 中的元素通过 id 引用了 defs 中的元素 (比如 linearGradient),
|
|
76
|
-
这种情况下因为 header 和 sidebar 中会各有一个相同的 svg, defs 中元素的 id 会出现冲突,
|
|
77
|
-
当屏幕很窄的情况, header 会通过设置 display:none 将 svg logo 隐藏,
|
|
78
|
-
这会导致 svg defs 中的元素不能被正常引用 (比如 fill 引用的 color 失效), 这会进一步导致 sidebar 中的 logo 不能正常显示
|
|
79
|
-
*/
|
|
80
|
-
.header-logo {
|
|
81
|
-
position: absolute;
|
|
82
|
-
width: 0;
|
|
83
|
-
height: 0;
|
|
84
|
-
overflow: hidden;
|
|
85
|
-
}
|
|
74
|
+
${props => props.theme.breakpoints.down('sm')} {
|
|
86
75
|
.header-title {
|
|
87
76
|
display: none;
|
|
88
77
|
}
|
|
@@ -95,6 +84,15 @@ const StyledAppBar = styled(AppBar)`
|
|
|
95
84
|
}
|
|
96
85
|
`;
|
|
97
86
|
|
|
87
|
+
/*
|
|
88
|
+
自定义 logo 相关:
|
|
89
|
+
如果为 logo prop 传入一个自定义的 svg, 并且 svg 中的元素通过 id 引用了 defs 中的元素 (比如 linearGradient),
|
|
90
|
+
这种情况下因为 header 和 sidebar 中会各有一个相同的 svg, defs 中元素的 id 会出现冲突,
|
|
91
|
+
当屏幕较窄时, header 会通过设置 display:none 将 svg logo 隐藏,
|
|
92
|
+
这会导致 svg defs 中的元素不能被正常引用 (比如 fill 引用的 color 失效), 这会进一步导致 sidebar 中的 logo 不能正常显示.
|
|
93
|
+
考虑到上述问题, 目前使用 Hidden 组件控制 logo 的显示/隐藏
|
|
94
|
+
参考: https://blog.patw.me/archives/1820/inline-svg-same-id-and-display-none-issue/
|
|
95
|
+
*/
|
|
98
96
|
export default function Header({
|
|
99
97
|
children,
|
|
100
98
|
brand,
|
|
@@ -118,7 +116,9 @@ export default function Header({
|
|
|
118
116
|
<MenuIcon />
|
|
119
117
|
</Button>
|
|
120
118
|
<Link to={homeUrl} className="header-link">
|
|
121
|
-
<
|
|
119
|
+
<Hidden smDown>
|
|
120
|
+
<div className="header-logo">{logo || <Logo showText={false} />}</div>
|
|
121
|
+
</Hidden>
|
|
122
122
|
<div className="header-title">
|
|
123
123
|
<Typography component="h2" noWrap className="header-title__primary">
|
|
124
124
|
{brand}
|
|
@@ -64,11 +64,11 @@ export default function Dashboard({
|
|
|
64
64
|
}) {
|
|
65
65
|
const breakpoint = 960;
|
|
66
66
|
const { width } = useWindowSize();
|
|
67
|
-
const [drawerMode, setDrawerMode] = useState(width
|
|
67
|
+
const [drawerMode, setDrawerMode] = useState(width >= breakpoint ? 'permanent' : 'temporary');
|
|
68
68
|
const [drawerOpen, setDrawerOpen] = useState(drawerMode === 'permanent');
|
|
69
69
|
|
|
70
70
|
useEffect(() => {
|
|
71
|
-
const newMode = width
|
|
71
|
+
const newMode = width >= breakpoint ? 'permanent' : 'temporary';
|
|
72
72
|
setDrawerMode(newMode);
|
|
73
73
|
setDrawerOpen(newMode !== 'temporary');
|
|
74
74
|
}, [width]);
|
package/src/Layout/index.js
CHANGED
|
@@ -264,7 +264,7 @@ const Div = styled.div`
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
${props => props.theme.breakpoints.up('md')} {
|
|
268
268
|
.toolbar {
|
|
269
269
|
.menu-button {
|
|
270
270
|
display: none;
|
|
@@ -278,7 +278,7 @@ const Div = styled.div`
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
${props => props.theme.breakpoints.down('sm')} {
|
|
282
282
|
.toolbar {
|
|
283
283
|
.nav-links,
|
|
284
284
|
.menu-logo,
|