@cdc/core 4.24.9 → 4.24.10
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/LICENSE +201 -0
- package/assets/icon-combo-chart.svg +1 -0
- package/assets/icon-epi-chart.svg +27 -0
- package/components/BlurStrokeText.tsx +44 -0
- package/components/DataTable/DataTable.tsx +51 -35
- package/components/DataTable/DataTableStandAlone.tsx +37 -6
- package/components/DataTable/components/ChartHeader.tsx +31 -26
- package/components/DataTable/components/MapHeader.tsx +19 -10
- package/components/DataTable/components/SortIcon/index.tsx +25 -0
- package/components/DataTable/components/SortIcon/sort-icon.css +21 -0
- package/{styles/_data-table.scss → components/DataTable/data-table.css} +268 -298
- package/components/DataTable/helpers/customSort.ts +11 -15
- package/components/DataTable/helpers/getDataSeriesColumns.ts +5 -1
- package/components/DataTable/helpers/getNewSortBy.ts +35 -0
- package/components/DataTable/helpers/tests/customSort.test.ts +52 -0
- package/components/DataTable/helpers/tests/getNewSortBy.test.ts +26 -0
- package/components/EditorPanel/DataTableEditor.tsx +132 -26
- package/components/EditorPanel/Inputs.tsx +42 -4
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +25 -7
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +1 -1
- package/components/{Filters.tsx → Filters/Filters.tsx} +48 -39
- package/components/Filters/helpers/applyQueuedActive.ts +12 -0
- package/components/Filters/helpers/getNestedOptions.ts +29 -0
- package/components/Filters/helpers/handleSorting.ts +18 -0
- package/components/Filters/helpers/tests/applyQueuedActive.test.ts +49 -0
- package/components/Filters/helpers/tests/getNestedOptions.test.ts +93 -0
- package/components/Filters/helpers/tests/handleSorting.test.ts +68 -0
- package/components/Filters/index.ts +5 -0
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +1 -3
- package/components/Legend/Legend.Gradient.tsx +2 -9
- package/components/Loader/Loader.tsx +33 -0
- package/components/Loader/index.ts +1 -0
- package/components/Loader/loader.styles.css +13 -0
- package/components/NestedDropdown/NestedDropdown.tsx +90 -48
- package/components/NestedDropdown/nestedDropdownHelpers.ts +34 -0
- package/components/NestedDropdown/nesteddropdown.styles.css +7 -0
- package/components/NestedDropdown/tests/nestedDropdownHelpers.test.ts +58 -0
- package/components/Table/components/GroupRow.tsx +1 -1
- package/components/_stories/BlurStrokeTest.stories.tsx +27 -0
- package/components/_stories/NestedDropdown.stories.tsx +22 -46
- package/components/_stories/_mocks/nested-dropdown.json +30 -0
- package/components/_stories/styles.scss +0 -1
- package/components/ui/{Tooltip.jsx → Tooltip.tsx} +38 -14
- package/data/colorPalettes.js +107 -10
- package/dist/cove-main.css +6114 -0
- package/dist/cove-main.css.map +1 -0
- package/helpers/addValuesToFilters.ts +8 -3
- package/helpers/cove/number.js +46 -25
- package/helpers/coveUpdateWorker.ts +6 -7
- package/helpers/formatConfigBeforeSave.ts +16 -1
- package/helpers/gatherQueryParams.ts +12 -2
- package/helpers/pivotData.ts +52 -11
- package/helpers/tests/gatherQueryParams.test.ts +34 -0
- package/helpers/tests/pivotData.test.ts +50 -0
- package/helpers/ver/4.24.10.ts +47 -0
- package/helpers/ver/4.24.9.ts +0 -3
- package/helpers/ver/tests/4.24.10.test.ts +45 -0
- package/helpers/viewports.ts +9 -0
- package/package.json +7 -3
- package/styles/_button-section.scss +4 -0
- package/styles/_global-variables.scss +19 -1
- package/styles/_global.scss +1 -8
- package/styles/_reset.scss +2 -15
- package/styles/base.scss +0 -1
- package/styles/cove-main.scss +6 -0
- package/styles/filters.scss +6 -4
- package/styles/v2/components/ui/tooltip.scss +42 -40
- package/styles/v2/layout/_component.scss +0 -6
- package/styles/v2/layout/index.scss +0 -1
- package/types/Axis.ts +2 -0
- package/types/General.ts +1 -0
- package/types/Table.ts +2 -1
- package/types/Visualization.ts +13 -1
- package/types/VizFilter.ts +2 -1
- package/components/DataTable/components/Icons.tsx +0 -10
- package/components/_stories/EditorPanel.stories.tsx +0 -54
- package/components/_stories/Layout.Debug.stories.tsx +0 -91
package/styles/_reset.scss
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
.cdc-open-viz-module {
|
|
2
2
|
margin: 0;
|
|
3
|
-
font: 1em/1.6 system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Droid Sans,
|
|
3
|
+
font: 1em/1.6 system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Droid Sans,
|
|
4
|
+
Helvetica Neue, Fira Sans, sans-serif;
|
|
4
5
|
font-weight: 400;
|
|
5
6
|
font-style: normal;
|
|
6
7
|
text-rendering: optimizeLegibility;
|
|
@@ -69,14 +70,7 @@
|
|
|
69
70
|
form,
|
|
70
71
|
label,
|
|
71
72
|
legend,
|
|
72
|
-
table,
|
|
73
73
|
caption,
|
|
74
|
-
tbody,
|
|
75
|
-
tfoot,
|
|
76
|
-
thead,
|
|
77
|
-
tr,
|
|
78
|
-
th,
|
|
79
|
-
td,
|
|
80
74
|
article,
|
|
81
75
|
aside,
|
|
82
76
|
canvas,
|
|
@@ -146,11 +140,4 @@
|
|
|
146
140
|
half as far down as the superscript moved up */
|
|
147
141
|
bottom: -0.25em;
|
|
148
142
|
}
|
|
149
|
-
|
|
150
|
-
ul > li:first-child {
|
|
151
|
-
margin-top: auto;
|
|
152
|
-
}
|
|
153
|
-
ul > li:last-of-type {
|
|
154
|
-
margin-bottom: auto;
|
|
155
|
-
}
|
|
156
143
|
}
|
package/styles/base.scss
CHANGED
package/styles/filters.scss
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
gap: 7px 15px;
|
|
6
6
|
margin-top: 15px;
|
|
7
7
|
margin-bottom: 15px;
|
|
8
|
+
label {
|
|
9
|
+
display: inherit;
|
|
10
|
+
margin-bottom: 5px;
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
font-size: 16px;
|
|
13
|
+
}
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
&__intro-text {
|
|
@@ -28,10 +34,6 @@ section.filters-section {
|
|
|
28
34
|
border-radius: 10px;
|
|
29
35
|
margin-bottom: 10px;
|
|
30
36
|
display: block !important;
|
|
31
|
-
|
|
32
|
-
&.legend_visible_top {
|
|
33
|
-
margin-bottom: 0px;
|
|
34
|
-
}
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
div.single-filters {
|
|
@@ -95,56 +95,58 @@
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
&.
|
|
113
|
-
|
|
114
|
-
|
|
98
|
+
.react-tooltip {
|
|
99
|
+
&.cove-tooltip__content {
|
|
100
|
+
max-width: var(--cove-tooltip-maxWidth);
|
|
101
|
+
padding: 10px 8px;
|
|
102
|
+
font-size: 0.875rem;
|
|
103
|
+
line-height: 1.125rem;
|
|
104
|
+
text-align: left;
|
|
105
|
+
color: var(--cove-tooltip-color);
|
|
106
|
+
background-color: var(--cove-tooltip-bg);
|
|
107
|
+
border-radius: 5px;
|
|
108
|
+
user-select: none;
|
|
109
|
+
cursor: default;
|
|
110
|
+
z-index: 1;
|
|
111
|
+
|
|
112
|
+
&.place-top {
|
|
113
|
+
&.has-shadow {
|
|
114
|
+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15), 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
115
|
+
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
&.cove-tooltip__content--animated[class*='styles-module_show__'] {
|
|
118
|
+
animation: tooltip-btt var(--cove-tooltip-animation);
|
|
119
|
+
}
|
|
118
120
|
}
|
|
119
|
-
}
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
&.place-right {
|
|
123
|
+
&.has-shadow {
|
|
124
|
+
box-shadow: -4px 4px 14px rgba(0, 0, 0, 0.15), -4px 4px 8px rgba(0, 0, 0, 0.1);
|
|
125
|
+
}
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
&.cove-tooltip__content--animated[class*='styles-module_show__'] {
|
|
128
|
+
animation: tooltip-ltr var(--cove-tooltip-animation);
|
|
129
|
+
}
|
|
128
130
|
}
|
|
129
|
-
}
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
&.place-bottom {
|
|
133
|
+
&.has-shadow {
|
|
134
|
+
box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.15), 0 8px 8px rgba(0, 0, 0, 0.1);
|
|
135
|
+
}
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
&.cove-tooltip__content--animated[class*='styles-module_show__'] {
|
|
138
|
+
animation: tooltip-ttb var(--cove-tooltip-animation);
|
|
139
|
+
}
|
|
138
140
|
}
|
|
139
|
-
}
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
&.place-left {
|
|
143
|
+
&.has-shadow {
|
|
144
|
+
box-shadow: 4px 4px 14px rgba(0, 0, 0, 0.15), 4px 4px 8px rgba(0, 0, 0, 0.1);
|
|
145
|
+
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
&.cove-tooltip__content--animated[class*='styles-module_show__'] {
|
|
148
|
+
animation: tooltip-rtl var(--cove-tooltip-animation);
|
|
149
|
+
}
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
.cove-component {
|
|
2
2
|
border-radius: 3px;
|
|
3
|
-
// box-shadow: rgba(0, 0, 0, 0.2) 3px 6px 10px; // no box shadows
|
|
4
3
|
}
|
|
5
4
|
|
|
6
5
|
.cove-component__content {
|
|
7
|
-
padding-top: 1rem;
|
|
8
6
|
border: solid 1px #ccc;
|
|
9
7
|
border-top: none;
|
|
10
8
|
border-radius: 0 0 3px 3px;
|
|
@@ -13,10 +11,6 @@
|
|
|
13
11
|
border: none !important;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
// .cdc-data-bite-inner-container:not(.component--has-title) .cove-component__content:not(.no-borders) {
|
|
17
|
-
// border-top: 1px solid #ccc;
|
|
18
|
-
// }
|
|
19
|
-
|
|
20
14
|
.cove-component__content:not(.component--hideBackgroundColor) {
|
|
21
15
|
background: $lightestGray;
|
|
22
16
|
}
|
package/types/Axis.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type Axis = {
|
|
|
20
20
|
label?: string
|
|
21
21
|
labelOffset?: number
|
|
22
22
|
labelPlacement?: string
|
|
23
|
+
labelsAboveGridlines?: boolean
|
|
23
24
|
manual?: boolean
|
|
24
25
|
manualStep?: number
|
|
25
26
|
max?: string
|
|
@@ -39,6 +40,7 @@ export type Axis = {
|
|
|
39
40
|
sortKey?: string
|
|
40
41
|
showTargetLabel?: boolean
|
|
41
42
|
size?: number
|
|
43
|
+
showYearsOnce?: boolean
|
|
42
44
|
target?: number
|
|
43
45
|
targetLabel?: string
|
|
44
46
|
tickRotation?: number
|
package/types/General.ts
CHANGED
package/types/Table.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type Pivot = {
|
|
2
2
|
columnName: string
|
|
3
|
-
|
|
3
|
+
valueColumns: string[]
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export type Table = {
|
|
@@ -23,6 +23,7 @@ export type Table = {
|
|
|
23
23
|
show?: boolean
|
|
24
24
|
showDataTableLink?: boolean
|
|
25
25
|
showDownloadImgButton?: boolean
|
|
26
|
+
showDownloadLinkBelow?: boolean
|
|
26
27
|
showDownloadPdfButton?: boolean
|
|
27
28
|
showDownloadUrl?: boolean
|
|
28
29
|
showVertical?: boolean
|
package/types/Visualization.ts
CHANGED
|
@@ -28,6 +28,8 @@ type DeprecatedVisualizationType = {
|
|
|
28
28
|
legend: Legend
|
|
29
29
|
multiDashboards?: any[]
|
|
30
30
|
newViz: boolean
|
|
31
|
+
isResponsiveTicks: boolean
|
|
32
|
+
barThickness: string
|
|
31
33
|
openModal: boolean
|
|
32
34
|
orientation: 'vertical' | 'horizontal'
|
|
33
35
|
originalFormattedData: any
|
|
@@ -36,7 +38,17 @@ type DeprecatedVisualizationType = {
|
|
|
36
38
|
table: Table
|
|
37
39
|
theme: string
|
|
38
40
|
title: string
|
|
39
|
-
type:
|
|
41
|
+
type:
|
|
42
|
+
| 'dashboard'
|
|
43
|
+
| 'chart'
|
|
44
|
+
| 'footnotes'
|
|
45
|
+
| 'map'
|
|
46
|
+
| 'data-bite'
|
|
47
|
+
| 'waffle-chart'
|
|
48
|
+
| 'markup-include'
|
|
49
|
+
| 'filtered-text'
|
|
50
|
+
| 'table'
|
|
51
|
+
| 'navigation'
|
|
40
52
|
usesSharedFilter: any
|
|
41
53
|
visualizationSubType: string
|
|
42
54
|
visualizationType: string
|
package/types/VizFilter.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type OrderBy = 'asc' | 'desc' | 'cust'
|
|
|
2
2
|
|
|
3
3
|
export type FilterBase = {
|
|
4
4
|
columnName: string
|
|
5
|
+
orderedValues?: string[]
|
|
5
6
|
values: string[]
|
|
6
7
|
showDropdown: boolean
|
|
7
8
|
id: number
|
|
@@ -18,7 +19,7 @@ export type VizFilterStyle =
|
|
|
18
19
|
| 'nested-dropdown'
|
|
19
20
|
export type GeneralFilter = FilterBase & {
|
|
20
21
|
active: string
|
|
21
|
-
queuedActive: string
|
|
22
|
+
queuedActive: string | string[]
|
|
22
23
|
filterStyle: VizFilterStyle
|
|
23
24
|
label: string
|
|
24
25
|
order: OrderBy
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const UpIcon = () => (
|
|
2
|
-
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'>
|
|
3
|
-
<path d='M0 5l5-5 5 5z' />
|
|
4
|
-
</svg>
|
|
5
|
-
)
|
|
6
|
-
export const DownIcon = () => (
|
|
7
|
-
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'>
|
|
8
|
-
<path d='M0 0l5 5 5-5z' />
|
|
9
|
-
</svg>
|
|
10
|
-
)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
|
|
3
|
-
import DataTableEditor from '../EditorPanel/DataTableEditor'
|
|
4
|
-
import { Accordion, AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
|
|
5
|
-
import { useState } from 'react'
|
|
6
|
-
|
|
7
|
-
const EditorPanel = () => {
|
|
8
|
-
const { config, isDashboard } = Primary.args
|
|
9
|
-
const [_config, setConfig] = useState(config)
|
|
10
|
-
const updateField = (section, subsection, fieldName, value) => {
|
|
11
|
-
setConfig({
|
|
12
|
-
..._config,
|
|
13
|
-
[section]: {
|
|
14
|
-
..._config[section],
|
|
15
|
-
[fieldName]: value
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
}
|
|
19
|
-
return (
|
|
20
|
-
<Accordion>
|
|
21
|
-
<AccordionItem>
|
|
22
|
-
<AccordionItemHeading>
|
|
23
|
-
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
24
|
-
</AccordionItemHeading>
|
|
25
|
-
<AccordionItemPanel>
|
|
26
|
-
<DataTableEditor config={_config} isDashboard={isDashboard} updateField={updateField} isLoadedFromUrl={false} />
|
|
27
|
-
</AccordionItemPanel>
|
|
28
|
-
</AccordionItem>
|
|
29
|
-
</Accordion>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const meta: Meta<typeof DataTableEditor> = {
|
|
34
|
-
title: 'Components/Organisms/EditorPanel',
|
|
35
|
-
component: EditorPanel
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default meta
|
|
39
|
-
|
|
40
|
-
type Story = StoryObj<typeof DataTableEditor>
|
|
41
|
-
|
|
42
|
-
export const Primary: Story = {
|
|
43
|
-
args: {
|
|
44
|
-
config: {
|
|
45
|
-
table: {
|
|
46
|
-
label: 'Data Table',
|
|
47
|
-
show: true
|
|
48
|
-
},
|
|
49
|
-
columns: {},
|
|
50
|
-
visualizationType: 'Pie'
|
|
51
|
-
},
|
|
52
|
-
isDashboard: true
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Meta, StoryObj } from '@storybook/react'
|
|
3
|
-
import './styles.scss'
|
|
4
|
-
import CdcChart from '@cdc/chart'
|
|
5
|
-
import CdcMap from '@cdc/map'
|
|
6
|
-
import CdcMarkupInclude from '@cdc/markup-include'
|
|
7
|
-
import CdcFilteredText from '@cdc/filtered-text'
|
|
8
|
-
import CdcDashboard from '@cdc/dashboard'
|
|
9
|
-
import CdcWaffleChart from '@cdc/waffle-chart'
|
|
10
|
-
import config from './../../../chart/src/_stories/_mock/bar-chart-suppressed.json'
|
|
11
|
-
import mapConfig from './../../../map/examples/default-usa.json'
|
|
12
|
-
import markupConfig from './../../../markup-include/src/data/initial-state.js'
|
|
13
|
-
import filteredTextConfig from './_mocks/filtered-text-config.json'
|
|
14
|
-
import waffleChartConfig from './../../../waffle-chart/examples/gallery/count.json'
|
|
15
|
-
import dashboardConfig from './../../../dashboard/examples/full-dashboard.json'
|
|
16
|
-
|
|
17
|
-
// Determine the component based on config.type
|
|
18
|
-
const getComponent = config => {
|
|
19
|
-
switch (config.type) {
|
|
20
|
-
case 'map':
|
|
21
|
-
return CdcMap
|
|
22
|
-
case 'chart':
|
|
23
|
-
return CdcChart
|
|
24
|
-
case 'markup-include':
|
|
25
|
-
return CdcMarkupInclude
|
|
26
|
-
case 'waffle-chart':
|
|
27
|
-
return CdcWaffleChart
|
|
28
|
-
case 'dashboard':
|
|
29
|
-
return CdcDashboard
|
|
30
|
-
default:
|
|
31
|
-
return () => <div>Unknown component type</div>
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const meta = {
|
|
36
|
-
title: 'Components/Templates/Layout/Debug',
|
|
37
|
-
component: args => {
|
|
38
|
-
const Component = getComponent(args.config)
|
|
39
|
-
return (
|
|
40
|
-
<div className=''>
|
|
41
|
-
<Component {...args} />
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
} as Meta
|
|
46
|
-
|
|
47
|
-
export default meta
|
|
48
|
-
|
|
49
|
-
type Story = StoryObj
|
|
50
|
-
|
|
51
|
-
export const Chart: Story = {
|
|
52
|
-
args: {
|
|
53
|
-
config,
|
|
54
|
-
isEditor: true
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const Map: Story = {
|
|
59
|
-
args: {
|
|
60
|
-
config: mapConfig,
|
|
61
|
-
isEditor: true
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const MarkupInclude: StoryObj<typeof CdcMarkupInclude> = {
|
|
66
|
-
args: {
|
|
67
|
-
config: markupConfig,
|
|
68
|
-
isEditor: true
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const WaffleChart: StoryObj<typeof CdcWaffleChart> = {
|
|
73
|
-
args: {
|
|
74
|
-
config: waffleChartConfig,
|
|
75
|
-
isEditor: true
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export const Dashboard: StoryObj<typeof CdcDashboard> = {
|
|
80
|
-
args: {
|
|
81
|
-
config: dashboardConfig,
|
|
82
|
-
isEditor: true
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// export const WaffleChart: StoryObj<typeof CdcWaffleChart> = {
|
|
87
|
-
// args: {
|
|
88
|
-
// config: waffleChartConfig,
|
|
89
|
-
// isEditor: true
|
|
90
|
-
// }
|
|
91
|
-
// }
|