@abi-software/map-side-bar 2.7.2-beta.2 → 2.7.2-beta.4

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.
@@ -213,60 +213,7 @@ export default {
213
213
  )
214
214
  return value
215
215
  },
216
- processOptions: function () {
217
- // create top level of options in cascader
218
- this.options.forEach((facet, i) => {
219
- this.options[i].total = this.countTotalFacet(facet)
220
-
221
- this.options[i].label = convertReadableLabel(facet.label)
222
- this.options[i].value = this.createCascaderItemValue(
223
- facet.key,
224
- undefined
225
- )
226
-
227
- // put "Show all" as first option
228
- this.options[i].children.unshift({
229
- value: this.createCascaderItemValue('Show all'),
230
- label: 'Show all',
231
- })
232
-
233
- // populate second level of options
234
- this.options[i].children.forEach((facetItem, j) => {
235
- // Format labels except funding program
236
- if (this.options[i].children[j].facetPropPath !== 'pennsieve.organization.name') {
237
- this.options[i].children[j].label = convertReadableLabel(
238
- facetItem.label
239
- )
240
- }
241
- this.options[i].children[j].value =
242
- this.createCascaderItemValue(facet.label, facetItem.label)
243
- if (
244
- this.options[i].children[j].children &&
245
- this.options[i].children[j].children.length > 0
246
- ) {
247
- this.options[i].children[j].children.forEach((term, k) => {
248
- this.options[i].children[j].children[k].label =
249
- convertReadableLabel(term.label)
250
- this.options[i].children[j].children[k].value =
251
- this.createCascaderItemValue(
252
- facet.label,
253
- facetItem.label,
254
- term.label
255
- )
256
- })
257
- }
258
- })
259
- })
260
- },
261
216
  populateCascader: function () {
262
- if (this.entry.options) {
263
- return new Promise((resolve) => {
264
- this.facets = this.entry.options
265
- this.options = this.entry.options
266
- this.processOptions()
267
- resolve();
268
- });
269
- }
270
217
  return new Promise((resolve) => {
271
218
  // Algolia facet serach
272
219
  this.algoliaClient
@@ -275,7 +222,50 @@ export default {
275
222
  this.facets = data
276
223
  EventBus.emit('available-facets', data)
277
224
  this.options = data
278
- this.processOptions()
225
+
226
+ // create top level of options in cascader
227
+ this.options.forEach((facet, i) => {
228
+ this.options[i].total = this.countTotalFacet(facet)
229
+
230
+ this.options[i].label = convertReadableLabel(facet.label)
231
+ this.options[i].value = this.createCascaderItemValue(
232
+ facet.key,
233
+ undefined
234
+ )
235
+
236
+ // put "Show all" as first option
237
+ this.options[i].children.unshift({
238
+ value: this.createCascaderItemValue('Show all'),
239
+ label: 'Show all',
240
+ })
241
+
242
+ // populate second level of options
243
+ this.options[i].children.forEach((facetItem, j) => {
244
+ // Format labels except funding program
245
+ if (this.options[i].children[j].facetPropPath !== 'pennsieve.organization.name') {
246
+ this.options[i].children[j].label = convertReadableLabel(
247
+ facetItem.label
248
+ )
249
+ }
250
+ this.options[i].children[j].value =
251
+ this.createCascaderItemValue(facet.label, facetItem.label)
252
+ if (
253
+ this.options[i].children[j].children &&
254
+ this.options[i].children[j].children.length > 0
255
+ ) {
256
+ this.options[i].children[j].children.forEach((term, k) => {
257
+ this.options[i].children[j].children[k].label =
258
+ convertReadableLabel(term.label)
259
+ this.options[i].children[j].children[k].value =
260
+ this.createCascaderItemValue(
261
+ facet.label,
262
+ facetItem.label,
263
+ term.label
264
+ )
265
+ })
266
+ }
267
+ })
268
+ })
279
269
  })
280
270
  .finally(() => {
281
271
  resolve()
@@ -745,18 +735,13 @@ export default {
745
735
  removeTopLevelCascaderCheckboxes: function () {
746
736
  // Next tick allows the cascader menu to change
747
737
  this.$nextTick(() => {
748
- const cascadePanels = document.querySelectorAll('.sidebar-cascader-popper .el-cascader-menu__list');
749
-
750
- cascadePanels.forEach(panel => {
751
- const panelText = panel.textContent;
752
- const expandArrow = panel.querySelector('.el-icon.arrow-right');
753
-
754
- if (!panelText.includes('Show all') && expandArrow) {
755
- panel.querySelectorAll('.el-checkbox__input').forEach(checkbox => {
756
- checkbox.style.display = 'none';
757
- });
758
- }
759
- });
738
+ let cascadePanels = document.querySelectorAll(
739
+ '.sidebar-cascader-popper .el-cascader-menu__list'
740
+ )
741
+ // Hide the checkboxes on the first level of the cascader
742
+ cascadePanels[0]
743
+ .querySelectorAll('.el-checkbox__input')
744
+ .forEach((el) => (el.style.display = 'none'))
760
745
  })
761
746
  },
762
747
  /*
@@ -436,6 +436,7 @@ export default {
436
436
  display: flex;
437
437
  align-items: center;
438
438
  gap: 6px;
439
+ transform: translateZ(0);
439
440
  }
440
441
 
441
442
  .search-tag.el-tag {
@@ -22,15 +22,29 @@
22
22
  </div>
23
23
  <div class="sidebar-container">
24
24
  <Tabs
25
- v-if="tabEntries.length > 1"
26
- :tabEntries="tabEntries"
25
+ v-if="activeTabs.length > 1"
26
+ :tabTitles="activeTabs"
27
27
  :activeId="activeTabId"
28
- @tabClicked="tabClicked"
29
- @tabClosed="tabClosed"
28
+ @titleClicked="tabClicked"
29
+ @tab-close="tabClose"
30
30
  />
31
31
  <template v-for="tab in tabs" key="tab.id">
32
- <template v-if="tab.type === 'annotation'">
33
- <AnnotationTool
32
+ <!-- Connectivity Info -->
33
+ <template v-if="tab.type === 'connectivity' && connectivityInfo">
34
+ <connectivity-info
35
+ :entry="connectivityInfo"
36
+ :availableAnatomyFacets="availableAnatomyFacets"
37
+ v-if="tab.id === activeTabId"
38
+ :envVars="envVars"
39
+ :ref="'connectivityTab_' + tab.id"
40
+ :useDOIFormatter="useDOIFormatter"
41
+ @show-connectivity="showConnectivity"
42
+ @show-reference-connectivities="onShowReferenceConnectivities"
43
+ @connectivity-component-click="onConnectivityComponentClick"
44
+ />
45
+ </template>
46
+ <template v-else-if="tab.type === 'annotation'">
47
+ <annotation-tool
34
48
  :ref="'annotationTab_' + tab.id"
35
49
  v-show="tab.id === activeTabId"
36
50
  :annotationEntry="annotationEntry"
@@ -41,23 +55,6 @@
41
55
  @confirm-delete="$emit('confirm-delete', $event)"
42
56
  />
43
57
  </template>
44
- <template v-else-if="tab.type === 'connectivityExplorer'">
45
- <ConnectivityExplorer
46
- :ref="'connectivityExplorerTab_' + tab.id"
47
- v-show="tab.id === activeTabId"
48
- :connectivityKnowledge="connectivityKnowledge"
49
- :envVars="envVars"
50
- :connectivityEntry="connectivityEntry"
51
- :availableAnatomyFacets="availableAnatomyFacets"
52
- @search-changed="searchChanged(tab.id, $event)"
53
- @hover-changed="hoverChanged($event)"
54
- @show-connectivity="showConnectivity"
55
- @show-reference-connectivities="onShowReferenceConnectivities"
56
- @connectivity-clicked="onConnectivityClicked"
57
- @connectivity-hovered="onConnectivityHovered"
58
- @connectivity-explorer-clicked="onConnectivityExplorerClicked"
59
- />
60
- </template>
61
58
  <template v-else>
62
59
  <SidebarContent
63
60
  class="sidebar-content-container"
@@ -87,7 +84,7 @@ import SidebarContent from './SidebarContent.vue'
87
84
  import EventBus from './EventBus.js'
88
85
  import Tabs from './Tabs.vue'
89
86
  import AnnotationTool from './AnnotationTool.vue'
90
- import ConnectivityExplorer from './ConnectivityExplorer.vue'
87
+ import ConnectivityInfo from './ConnectivityInfo.vue'
91
88
 
92
89
  /**
93
90
  * Aims to provide a sidebar for searching capability for SPARC portal.
@@ -100,8 +97,8 @@ export default {
100
97
  ElIconArrowRight,
101
98
  Drawer,
102
99
  Icon,
100
+ ConnectivityInfo,
103
101
  AnnotationTool,
104
- ConnectivityExplorer,
105
102
  },
106
103
  name: 'SideBar',
107
104
  props: {
@@ -122,6 +119,24 @@ export default {
122
119
  type: Object,
123
120
  default: () => {},
124
121
  },
122
+ /**
123
+ * The array of objects to show multiple sidebar contents.
124
+ */
125
+ tabs: {
126
+ type: Array,
127
+ default: () => [
128
+ { id: 1, title: 'Search', type: 'search' },
129
+ { id: 2, title: 'Connectivity', type: 'connectivity' },
130
+ { id: 3, title: 'Annotation', type: 'annotation' }
131
+ ],
132
+ },
133
+ /**
134
+ * The active tab id for default tab.
135
+ */
136
+ activeTabId: {
137
+ type: Number,
138
+ default: 1,
139
+ },
125
140
  /**
126
141
  * The option to show or hide sidebar on page load.
127
142
  */
@@ -132,16 +147,16 @@ export default {
132
147
  /**
133
148
  * The connectivity info data to show in sidebar.
134
149
  */
135
- connectivityEntry: {
150
+ connectivityInfo: {
136
151
  type: Object,
137
- default: {},
152
+ default: null,
138
153
  },
139
154
  /**
140
155
  * The annotation data to show in sidebar.
141
156
  */
142
157
  annotationEntry: {
143
158
  type: Object,
144
- default: {},
159
+ default: null,
145
160
  },
146
161
  createData: {
147
162
  type: Object,
@@ -153,27 +168,23 @@ export default {
153
168
  y: 0,
154
169
  },
155
170
  },
156
- connectivityKnowledge: {
157
- type: Array,
158
- default: [],
171
+ /**
172
+ * The option to use DOI.org as citation formatter.
173
+ * If set to `false`, citation.js will be used.
174
+ * Default is `true`.
175
+ */
176
+ useDOIFormatter: {
177
+ type: Boolean,
178
+ default: true,
159
179
  },
160
180
  },
161
181
  data: function () {
162
182
  return {
163
183
  drawerOpen: false,
164
- availableAnatomyFacets: [],
165
- activeTabId: 1,
166
- tabs: [
167
- { title: 'Search', id: 1, type: 'search', closable: false },
168
- { title: 'Connectivity Explorer', id: 2, type: 'connectivityExplorer', closable: false },
169
- { title: 'Annotation', id: 3, type: 'annotation', closable: true },
170
- ]
184
+ availableAnatomyFacets: []
171
185
  }
172
186
  },
173
187
  methods: {
174
- onConnectivityExplorerClicked: function (data) {
175
- this.$emit('connectivity-explorer-clicked', data)
176
- },
177
188
  /**
178
189
  * This event is emitted when the mouse hover are changed.
179
190
  * @arg data
@@ -196,18 +207,11 @@ export default {
196
207
  this.$emit('show-reference-connectivities', refSource);
197
208
  },
198
209
  /**
199
- * This function is triggered after connectivity term is clicked.
200
- * @arg data
201
- */
202
- onConnectivityClicked: function (data) {
203
- this.$emit('connectivity-clicked', data);
204
- },
205
- /**
206
- * This function is triggered after connectivity term is hovered.
210
+ * This function is triggered after a connectivity component is clicked.
207
211
  * @arg data
208
212
  */
209
- onConnectivityHovered: function (data) {
210
- this.$emit('connectivity-hovered', data);
213
+ onConnectivityComponentClick: function (data) {
214
+ this.$emit('connectivity-component-click', data);
211
215
  },
212
216
  /**
213
217
  * This event is emitted when the search filters are changed.
@@ -230,31 +234,43 @@ export default {
230
234
  toggleDrawer: function () {
231
235
  this.drawerOpen = !this.drawerOpen
232
236
  },
233
- openConnectivitySearch: function (facets, query) {
234
- this.drawerOpen = true;
235
- // Because refs are in v-for, nextTick is needed here
236
- this.$nextTick(() => {
237
- const connectivityExplorerTabRef = this.getTabRef(2, 'connectivityExplorer', true);
238
- connectivityExplorerTabRef.openSearch(facets, query);
239
- })
240
- },
241
237
  openSearch: function (facets, query) {
242
238
  this.drawerOpen = true
243
239
  // Because refs are in v-for, nextTick is needed here
244
240
  this.$nextTick(() => {
245
- const searchTabRef = this.getTabRef(1, 'search', true);
241
+ const searchTabRef = this.getSearchTabRefById(1);
246
242
  searchTabRef.openSearch(facets, query);
247
243
  })
248
244
  },
245
+ /**
246
+ * Get the tab object by tab id and type.
247
+ * If not found, return the first available tab.
248
+ */
249
+ getTabByIdAndType: function (id, type) {
250
+ const tabId = id || this.activeTabId;
251
+ const tabType = type || 'search'; // default to search tab
252
+ const tabObj = this.activeTabs.find((tab) => tab.id === tabId && tab.type === tabType);
253
+ const firstAvailableTab = this.activeTabs[0];
254
+ return tabObj || firstAvailableTab;
255
+ },
249
256
  /**
250
257
  * Get the ref id of the tab by id and type.
251
258
  */
252
- getTabRef: function (id = 1, type = 'search', open = false) {
253
- const matchedTab = this.tabEntries.filter((tab) => tab.id === id && tab.type === type);
254
- const tabInfo = matchedTab.length ? matchedTab : this.tabEntries;
255
- const tabRef = type + 'Tab_' + tabInfo[0].id;
256
- if (open) this.tabClicked({ id, type });
257
- return this.$refs[tabRef][0];
259
+ getTabRefId: function (id, type) {
260
+ let refIdPrefix = 'searchTab_'; // default to search tab
261
+ if (type === 'connectivity') {
262
+ refIdPrefix = 'connectivityTab_';
263
+ } else if (type === 'annotation') {
264
+ refIdPrefix = 'annotationTab_';
265
+ }
266
+ const tabObj = this.getTabByIdAndType(id, type);
267
+ const tabRefId = refIdPrefix + tabObj.id;
268
+ return tabRefId;
269
+ },
270
+ getSearchTabRefById: function (id) {
271
+ const searchTabId = id || 1; // to use id when there are multiple search tabs
272
+ const searchTabRefId = this.getTabRefId(searchTabId, 'search');
273
+ return this.$refs[searchTabRefId][0];
258
274
  },
259
275
  /**
260
276
  * The function to add filters to sidebar search.
@@ -268,7 +284,7 @@ export default {
268
284
 
269
285
  // Because refs are in v-for, nextTick is needed here
270
286
  this.$nextTick(() => {
271
- const searchTabRef = this.getTabRef(1, 'search', true);
287
+ const searchTabRef = this.getSearchTabRefById(1);
272
288
  searchTabRef.addFilter(filter)
273
289
  })
274
290
  },
@@ -276,7 +292,7 @@ export default {
276
292
  this.drawerOpen = true
277
293
  // Because refs are in v-for, nextTick is needed here
278
294
  this.$nextTick(() => {
279
- const searchTabRef = this.getTabRef(1, 'search', true);
295
+ const searchTabRef = this.getSearchTabRefById(1);
280
296
  searchTabRef.openSearch(
281
297
  '',
282
298
  undefined,
@@ -286,17 +302,27 @@ export default {
286
302
  })
287
303
  },
288
304
  getAlgoliaFacets: async function () {
289
- const searchTabRef = this.getTabRef(1, 'search');
305
+ const searchTabRef = this.getSearchTabRefById(1);
290
306
  return await searchTabRef.getAlgoliaFacets()
291
307
  },
292
308
  setDrawerOpen: function (value = true) {
293
309
  this.drawerOpen = value
294
310
  },
295
- tabClicked: function (tab) {
296
- this.activeTabId = tab.id
311
+ /**
312
+ * The function to emit 'tabClicked' event with tab's `id` and tab's `type`
313
+ * when user clicks the sidebar tab.
314
+ * @param {Object} {id, type}
315
+ * @public
316
+ */
317
+ tabClicked: function ({id, type}) {
318
+ /**
319
+ * This event is emitted when user click sidebar's tab.
320
+ * @arg {Object} {id, type}
321
+ */
322
+ this.$emit('tabClicked', {id, type});
297
323
  },
298
- tabClosed: function (tab) {
299
- this.$emit('tabClosed', tab);
324
+ tabClose: function (id) {
325
+ this.$emit('tab-close', id);
300
326
  },
301
327
  /**
302
328
  * To receive error message for connectivity graph
@@ -308,12 +334,22 @@ export default {
308
334
  },
309
335
  computed: {
310
336
  // This should respect the information provided by the property
311
- tabEntries: function () {
312
- return this.tabs.filter((tab) =>
313
- tab.type === "search" ||
314
- tab.type === "connectivityExplorer" ||
315
- (tab.type === "annotation" && this.annotationEntry && Object.keys(this.annotationEntry).length > 0)
316
- );
337
+ activeTabs: function() {
338
+ const tabs = []
339
+ this.tabs.forEach((tab) => {
340
+ if (tab.type === "search") {
341
+ tabs.push(tab)
342
+ } else if (tab.type === "connectivity") {
343
+ if (this.connectivityInfo) {
344
+ tabs.push(tab);
345
+ }
346
+ } else if (tab.type === "annotation") {
347
+ if (this.annotationEntry && Object.keys(this.annotationEntry).length > 0) {
348
+ tabs.push(tab);
349
+ }
350
+ }
351
+ })
352
+ return tabs;
317
353
  },
318
354
  },
319
355
  created: function () {
@@ -369,6 +405,7 @@ export default {
369
405
  EventBus.on('available-facets', (payLoad) => {
370
406
  this.availableAnatomyFacets = payLoad.find((facet) => facet.label === 'Anatomical Structure').children
371
407
  })
408
+
372
409
  },
373
410
  }
374
411
  </script>
@@ -164,8 +164,7 @@ export default {
164
164
  },
165
165
  methods: {
166
166
  hoverChanged: function (data) {
167
- const payload = data ? { ...data, type: 'dataset' } : data
168
- this.$emit('hover-changed', payload)
167
+ this.$emit('hover-changed', data)
169
168
  },
170
169
  resetSearch: function () {
171
170
  this.numberOfHits = 0
@@ -1,35 +1,40 @@
1
1
  <template>
2
- <div class="tabs-container">
2
+ <div class="tab-container">
3
3
  <div
4
- class="tab"
5
- v-for="tab in tabs"
6
- :key="tab.id"
7
- :class="{ 'active-tab': tab.id == activeId }"
8
- @click="tabClicked(tab)"
4
+ class="title"
5
+ v-for="title in tabTitles"
6
+ :key="title.id"
7
+ :class="{ 'active-tab': title.id == activeId }"
9
8
  >
10
- <span class="tab-title">{{ tab.title }} </span>
11
- <el-icon
12
- v-if="tab.closable"
13
- @click.stop="tabClosed(tab)"
14
- class="tab-close-icon"
9
+ <div
10
+ class="title-text-table"
11
+ v-bind:class="{ highlightText: title.id == activeId }"
12
+ v-on:click="titleClicked(title.id, title.type)"
15
13
  >
16
- <el-icon-close />
17
- </el-icon>
14
+ <div class="title-text">
15
+ {{ title.title }}
16
+ </div>
17
+ </div>
18
+ <el-button
19
+ v-if="title.id > 1"
20
+ @click="tabClose(title.id)"
21
+ class="button-tab-close"
22
+ aria-label="Close"
23
+ >
24
+ &times;
25
+ <span class="visually-hidden">Close</span>
26
+ </el-button>
18
27
  </div>
19
28
  </div>
20
29
  </template>
21
30
 
22
31
  <script>
23
32
  /* eslint-disable no-alert, no-console */
24
- import { Close as ElIconClose } from "@element-plus/icons-vue";
25
33
 
26
34
  export default {
27
- name: "Tabs",
28
- components: {
29
- ElIconClose,
30
- },
35
+ name: 'Tabs',
31
36
  props: {
32
- tabEntries: {
37
+ tabTitles: {
33
38
  type: Array,
34
39
  default: () => [],
35
40
  },
@@ -37,72 +42,106 @@ export default {
37
42
  type: Number,
38
43
  default: 1,
39
44
  },
40
- contextArray: {
41
- type: Array,
42
- default: () => [],
43
- },
44
- },
45
- computed: {
46
- tabs: function () {
47
- // permanent tabs always show in the front
48
- const permanent = this.tabEntries.filter((t) => !t.closable);
49
- const temporary = this.tabEntries.filter((t) => t.closable);
50
- let entries = permanent.concat(temporary);
51
- if (this.contextArray.length) {
52
- for (let i in entries) {
53
- entries[i].contextCard = this.contextArray[i];
54
- }
55
- }
56
- return entries;
57
- },
58
45
  },
59
46
  methods: {
60
- tabClicked: function (tab) {
61
- this.$emit("tabClicked", { id: tab.id, type: tab.type });
47
+ titleClicked: function (id, type) {
48
+ this.$emit('titleClicked', {id, type})
62
49
  },
63
- tabClosed: function (tab) {
64
- this.$emit("tabClosed", { id: tab.id, type: tab.type });
50
+ tabClose: function (id) {
51
+ this.$emit('tab-close', id);
65
52
  },
66
53
  },
67
- };
54
+ }
68
55
  </script>
69
56
 
70
57
  <style lang="scss" scoped>
71
- .tabs-container {
58
+ $tab-height: 30px;
59
+
60
+ .tab-container {
61
+ height: $tab-height + 2;
72
62
  display: flex;
73
- flex-wrap: nowrap;
74
- align-items: center;
63
+ flex-direction: row;
64
+ position: relative;
65
+ z-index: 1;
75
66
  }
76
67
 
77
- .tab {
78
- height: 30px;
68
+ .title {
69
+ height: $tab-height;
79
70
  border: 1px solid var(--el-border-color);
80
71
  border-top-color: transparent;
81
- border-bottom-color: transparent;
72
+ background-color: white;
82
73
  display: flex;
74
+ width: fit-content;
83
75
  align-items: center;
76
+ position: relative;
84
77
  cursor: pointer;
85
78
  }
86
79
 
87
- .tab:hover,
80
+ .title-text {
81
+ text-align: center;
82
+ display: table-cell;
83
+ vertical-align: middle;
84
+ font-size: 14px;
85
+ padding: 0 1rem;
86
+ }
87
+
88
+ .title-text-table {
89
+ display: table;
90
+ height: 100%;
91
+ width: 100%;
92
+ }
93
+
94
+ .parent-dialog:hover .title-text {
95
+ color: $app-primary-color;
96
+ }
97
+
98
+ .title:hover,
88
99
  .active-tab {
89
100
  background-color: #f9f2fc;
90
- border: 1px solid #8300bf;
101
+ border: solid #8300bf;
102
+ border-width: 1px 1px .125em;
91
103
  color: #8300bf;
92
104
  font-weight: 500;
93
105
  }
94
106
 
95
- .tab-title {
96
- text-align: center;
97
- font-size: 14px;
98
- padding: 0 1rem;
107
+ .highlightText {
108
+ color: $app-primary-color;
99
109
  }
100
110
 
101
- .tab-close-icon {
111
+ .button-tab-close {
102
112
  width: 20px !important;
103
113
  height: 20px !important;
104
- font-size: 20px !important;
114
+ line-height: 20px !important;
115
+ padding: 0 !important;
105
116
  padding-right: 4px !important;
117
+ font-size: 24px !important;
106
118
  color: $app-primary-color !important;
119
+ border: 0 none !important;
120
+ box-shadow: none !important;
121
+ outline: none !important;
122
+ background-color: transparent !important;
123
+
124
+ :deep(> span) {
125
+ height: $tab-height - 2 !important; // tab height minus border
126
+ font-family: Arial !important; // to fix font alignment on different browsers
127
+ }
128
+
129
+ &:hover,
130
+ &:focus {
131
+ border: 0 none !important;
132
+ outline: none !important;
133
+ box-shadow: none !important;
134
+ background-color: transparent !important;
135
+ }
136
+ }
137
+
138
+ .visually-hidden {
139
+ clip: rect(0 0 0 0);
140
+ clip-path: inset(50%);
141
+ height: 1px;
142
+ overflow: hidden;
143
+ position: absolute;
144
+ white-space: nowrap;
145
+ width: 1px;
107
146
  }
108
147
  </style>