Commit 3b2c40bec818238bde165656dc17e885f242aa81
1 parent
1c1755cf
refactor(virtual-scroll): refactor virtualScroll component
Showing
15 changed files
with
200 additions
and
203 deletions
src/components/Markdown/index.ts
1 | -import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; | ||
2 | -export const MarkDown = createAsyncComponent(() => import('./src/Markdown.vue')); | 1 | +import { withInstall } from '/@/utils'; |
2 | +import markDown from './src/Markdown.vue'; | ||
3 | 3 | ||
4 | -export * from './src/types'; | 4 | +export const MarkDown = withInstall(markDown); |
5 | +export * from './src/typing'; |
src/components/Markdown/src/Markdown.vue
@@ -14,18 +14,17 @@ | @@ -14,18 +14,17 @@ | ||
14 | } from 'vue'; | 14 | } from 'vue'; |
15 | import Vditor from 'vditor'; | 15 | import Vditor from 'vditor'; |
16 | import 'vditor/dist/index.css'; | 16 | import 'vditor/dist/index.css'; |
17 | - | ||
18 | - import { propTypes } from '/@/utils/propTypes'; | ||
19 | import { useLocale } from '/@/locales/useLocale'; | 17 | import { useLocale } from '/@/locales/useLocale'; |
20 | import { useModalContext } from '../../Modal'; | 18 | import { useModalContext } from '../../Modal'; |
21 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | 19 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
22 | 20 | ||
23 | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; | 21 | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; |
22 | + | ||
24 | export default defineComponent({ | 23 | export default defineComponent({ |
25 | inheritAttrs: false, | 24 | inheritAttrs: false, |
26 | props: { | 25 | props: { |
27 | - height: propTypes.number.def(360), | ||
28 | - value: propTypes.string.def(''), | 26 | + height: { type: Number, default: 360 }, |
27 | + value: { type: String, default: '' }, | ||
29 | }, | 28 | }, |
30 | emits: ['change', 'get'], | 29 | emits: ['change', 'get'], |
31 | setup(props, { attrs, emit }) { | 30 | setup(props, { attrs, emit }) { |
src/components/Markdown/src/types.ts renamed to src/components/Markdown/src/typing.ts
src/components/StrengthMeter/index.ts
src/components/StrengthMeter/src/StrengthMeter.vue
@@ -20,10 +20,7 @@ | @@ -20,10 +20,7 @@ | ||
20 | 20 | ||
21 | <script lang="ts"> | 21 | <script lang="ts"> |
22 | import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue'; | 22 | import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue'; |
23 | - | ||
24 | import { Input } from 'ant-design-vue'; | 23 | import { Input } from 'ant-design-vue'; |
25 | - | ||
26 | - // @ts-ignore | ||
27 | import { zxcvbn } from '@zxcvbn-ts/core'; | 24 | import { zxcvbn } from '@zxcvbn-ts/core'; |
28 | import { useDesign } from '/@/hooks/web/useDesign'; | 25 | import { useDesign } from '/@/hooks/web/useDesign'; |
29 | import { propTypes } from '/@/utils/propTypes'; | 26 | import { propTypes } from '/@/utils/propTypes'; |
src/components/Time/index.ts
src/components/Time/src/Time.vue
@@ -3,10 +3,8 @@ | @@ -3,10 +3,8 @@ | ||
3 | </template> | 3 | </template> |
4 | <script lang="ts"> | 4 | <script lang="ts"> |
5 | import { defineComponent, ref, watch } from 'vue'; | 5 | import { defineComponent, ref, watch } from 'vue'; |
6 | - | ||
7 | import { useI18n } from '/@/hooks/web/useI18n'; | 6 | import { useI18n } from '/@/hooks/web/useI18n'; |
8 | import { useIntervalFn } from '@vueuse/core'; | 7 | import { useIntervalFn } from '@vueuse/core'; |
9 | - | ||
10 | import { formatToDateTime, formatToDate, dateUtil } from '/@/utils/dateUtil'; | 8 | import { formatToDateTime, formatToDate, dateUtil } from '/@/utils/dateUtil'; |
11 | import { isNumber, isObject, isString } from '/@/utils/is'; | 9 | import { isNumber, isObject, isString } from '/@/utils/is'; |
12 | import { propTypes } from '/@/utils/propTypes'; | 10 | import { propTypes } from '/@/utils/propTypes'; |
@@ -15,6 +13,7 @@ | @@ -15,6 +13,7 @@ | ||
15 | const ONE_MINUTES = ONE_SECONDS * 60; | 13 | const ONE_MINUTES = ONE_SECONDS * 60; |
16 | const ONE_HOUR = ONE_MINUTES * 60; | 14 | const ONE_HOUR = ONE_MINUTES * 60; |
17 | const ONE_DAY = ONE_HOUR * 24; | 15 | const ONE_DAY = ONE_HOUR * 24; |
16 | + | ||
18 | export default defineComponent({ | 17 | export default defineComponent({ |
19 | name: 'Time', | 18 | name: 'Time', |
20 | props: { | 19 | props: { |
src/components/Tinymce/index.ts
src/components/Tinymce/src/Editor.vue
@@ -16,7 +16,6 @@ | @@ -16,7 +16,6 @@ | ||
16 | import type { RawEditorSettings } from 'tinymce'; | 16 | import type { RawEditorSettings } from 'tinymce'; |
17 | import tinymce from 'tinymce/tinymce'; | 17 | import tinymce from 'tinymce/tinymce'; |
18 | import 'tinymce/themes/silver'; | 18 | import 'tinymce/themes/silver'; |
19 | - | ||
20 | import 'tinymce/icons/default/icons'; | 19 | import 'tinymce/icons/default/icons'; |
21 | import 'tinymce/plugins/advlist'; | 20 | import 'tinymce/plugins/advlist'; |
22 | import 'tinymce/plugins/anchor'; | 21 | import 'tinymce/plugins/anchor'; |
@@ -58,11 +57,8 @@ | @@ -58,11 +57,8 @@ | ||
58 | onUnmounted, | 57 | onUnmounted, |
59 | onDeactivated, | 58 | onDeactivated, |
60 | } from 'vue'; | 59 | } from 'vue'; |
61 | - | ||
62 | import ImgUpload from './ImgUpload.vue'; | 60 | import ImgUpload from './ImgUpload.vue'; |
63 | - | ||
64 | import { toolbar, plugins } from './tinymce'; | 61 | import { toolbar, plugins } from './tinymce'; |
65 | - | ||
66 | import { buildShortUUID } from '/@/utils/uuid'; | 62 | import { buildShortUUID } from '/@/utils/uuid'; |
67 | import { bindHandlers } from './helper'; | 63 | import { bindHandlers } from './helper'; |
68 | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; | 64 | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; |
@@ -96,7 +92,6 @@ | @@ -96,7 +92,6 @@ | ||
96 | required: false, | 92 | required: false, |
97 | default: 400, | 93 | default: 400, |
98 | }, | 94 | }, |
99 | - | ||
100 | width: { | 95 | width: { |
101 | type: [Number, String] as PropType<string | number>, | 96 | type: [Number, String] as PropType<string | number>, |
102 | required: false, | 97 | required: false, |
src/components/Tinymce/src/ImgUpload.vue
@@ -52,9 +52,9 @@ | @@ -52,9 +52,9 @@ | ||
52 | function handleChange(info: Recordable) { | 52 | function handleChange(info: Recordable) { |
53 | const file = info.file; | 53 | const file = info.file; |
54 | const status = file?.status; | 54 | const status = file?.status; |
55 | - | ||
56 | const url = file?.response?.url; | 55 | const url = file?.response?.url; |
57 | const name = file?.name; | 56 | const name = file?.name; |
57 | + | ||
58 | if (status === 'uploading') { | 58 | if (status === 'uploading') { |
59 | if (!uploading) { | 59 | if (!uploading) { |
60 | emit('uploading', name); | 60 | emit('uploading', name); |
src/components/VirtualScroll/index.ts
1 | -import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; | 1 | +import { withInstall } from '/@/utils/index'; |
2 | +import vScroll from './src/VirtualScroll.vue'; | ||
2 | 3 | ||
3 | -export const VScroll = createAsyncComponent(() => import('./src/VirtualScroll')); | 4 | +export const VScroll = withInstall(vScroll); |
src/components/VirtualScroll/src/VirtualScroll.tsx renamed to src/components/VirtualScroll/src/VirtualScroll.vue
1 | -import { | ||
2 | - defineComponent, | ||
3 | - computed, | ||
4 | - ref, | ||
5 | - unref, | ||
6 | - reactive, | ||
7 | - onMounted, | ||
8 | - watch, | ||
9 | - nextTick, | ||
10 | - CSSProperties, | ||
11 | -} from 'vue'; | ||
12 | -import { useEventListener } from '/@/hooks/event/useEventListener'; | ||
13 | - | ||
14 | -import { props as basicProps } from './props'; | ||
15 | -import { getSlot } from '/@/utils/helper/tsxHelper'; | ||
16 | -import './index.less'; | ||
17 | - | ||
18 | -const prefixCls = 'virtual-scroll'; | ||
19 | - | ||
20 | -function convertToUnit(str: string | number | null | undefined, unit = 'px'): string | undefined { | ||
21 | - if (str == null || str === '') { | ||
22 | - return undefined; | ||
23 | - } else if (isNaN(+str!)) { | ||
24 | - return String(str); | ||
25 | - } else { | ||
26 | - return `${Number(str)}${unit}`; | 1 | +<script lang="tsx"> |
2 | + import { | ||
3 | + defineComponent, | ||
4 | + computed, | ||
5 | + ref, | ||
6 | + unref, | ||
7 | + reactive, | ||
8 | + onMounted, | ||
9 | + watch, | ||
10 | + nextTick, | ||
11 | + CSSProperties, | ||
12 | + } from 'vue'; | ||
13 | + import { useEventListener } from '/@/hooks/event/useEventListener'; | ||
14 | + import { getSlot } from '/@/utils/helper/tsxHelper'; | ||
15 | + | ||
16 | + type NumberOrNumberString = PropType<string | number | undefined>; | ||
17 | + | ||
18 | + const props = { | ||
19 | + height: [Number, String] as NumberOrNumberString, | ||
20 | + maxHeight: [Number, String] as NumberOrNumberString, | ||
21 | + maxWidth: [Number, String] as NumberOrNumberString, | ||
22 | + minHeight: [Number, String] as NumberOrNumberString, | ||
23 | + minWidth: [Number, String] as NumberOrNumberString, | ||
24 | + width: [Number, String] as NumberOrNumberString, | ||
25 | + bench: { | ||
26 | + type: [Number, String] as NumberOrNumberString, | ||
27 | + default: 0, | ||
28 | + }, | ||
29 | + itemHeight: { | ||
30 | + type: [Number, String] as NumberOrNumberString, | ||
31 | + required: true, | ||
32 | + }, | ||
33 | + items: { | ||
34 | + type: Array as PropType<any[]>, | ||
35 | + default: () => [], | ||
36 | + }, | ||
37 | + }; | ||
38 | + | ||
39 | + const prefixCls = 'virtual-scroll'; | ||
40 | + | ||
41 | + function convertToUnit(str: string | number | null | undefined, unit = 'px'): string | undefined { | ||
42 | + if (str == null || str === '') { | ||
43 | + return undefined; | ||
44 | + } else if (isNaN(+str!)) { | ||
45 | + return String(str); | ||
46 | + } else { | ||
47 | + return `${Number(str)}${unit}`; | ||
48 | + } | ||
27 | } | 49 | } |
28 | -} | ||
29 | - | ||
30 | -export default defineComponent({ | ||
31 | - name: 'VirtualScroll', | ||
32 | - props: basicProps, | ||
33 | - setup(props, { slots }) { | ||
34 | - const wrapElRef = ref<HTMLDivElement | null>(null); | ||
35 | - const state = reactive({ | ||
36 | - first: 0, | ||
37 | - last: 0, | ||
38 | - scrollTop: 0, | ||
39 | - }); | ||
40 | - | ||
41 | - const getBenchRef = computed(() => { | ||
42 | - return parseInt(props.bench as string, 10); | ||
43 | - }); | ||
44 | - | ||
45 | - const getItemHeightRef = computed(() => { | ||
46 | - return parseInt(props.itemHeight as string, 10); | ||
47 | - }); | ||
48 | - | ||
49 | - const getFirstToRenderRef = computed(() => { | ||
50 | - return Math.max(0, state.first - unref(getBenchRef)); | ||
51 | - }); | ||
52 | - | ||
53 | - const getLastToRenderRef = computed(() => { | ||
54 | - return Math.min((props.items || []).length, state.last + unref(getBenchRef)); | ||
55 | - }); | ||
56 | - | ||
57 | - const getContainerStyleRef = computed((): CSSProperties => { | ||
58 | - return { | ||
59 | - height: convertToUnit((props.items || []).length * unref(getItemHeightRef)), | ||
60 | - }; | ||
61 | - }); | ||
62 | - | ||
63 | - const getWrapStyleRef = computed((): CSSProperties => { | ||
64 | - const styles: Recordable<string> = {}; | ||
65 | - const height = convertToUnit(props.height); | ||
66 | - const minHeight = convertToUnit(props.minHeight); | ||
67 | - const minWidth = convertToUnit(props.minWidth); | ||
68 | - const maxHeight = convertToUnit(props.maxHeight); | ||
69 | - const maxWidth = convertToUnit(props.maxWidth); | ||
70 | - const width = convertToUnit(props.width); | ||
71 | - | ||
72 | - if (height) styles.height = height; | ||
73 | - if (minHeight) styles.minHeight = minHeight; | ||
74 | - if (minWidth) styles.minWidth = minWidth; | ||
75 | - if (maxHeight) styles.maxHeight = maxHeight; | ||
76 | - if (maxWidth) styles.maxWidth = maxWidth; | ||
77 | - if (width) styles.width = width; | ||
78 | - return styles; | ||
79 | - }); | ||
80 | - | ||
81 | - watch([() => props.itemHeight, () => props.height], () => { | ||
82 | - onScroll(); | ||
83 | - }); | ||
84 | - | ||
85 | - function getLast(first: number): number { | ||
86 | - const wrapEl = unref(wrapElRef); | ||
87 | - if (!wrapEl) { | ||
88 | - return 0; | ||
89 | - } | ||
90 | - const height = parseInt(props.height || 0, 10) || wrapEl.clientHeight; | ||
91 | 50 | ||
92 | - return first + Math.ceil(height / unref(getItemHeightRef)); | ||
93 | - } | 51 | + export default defineComponent({ |
52 | + name: 'VirtualScroll', | ||
53 | + props, | ||
54 | + setup(props, { slots }) { | ||
55 | + const wrapElRef = ref<HTMLDivElement | null>(null); | ||
56 | + const state = reactive({ | ||
57 | + first: 0, | ||
58 | + last: 0, | ||
59 | + scrollTop: 0, | ||
60 | + }); | ||
94 | 61 | ||
95 | - function getFirst(): number { | ||
96 | - return Math.floor(state.scrollTop / unref(getItemHeightRef)); | ||
97 | - } | 62 | + const getBenchRef = computed(() => { |
63 | + return parseInt(props.bench as string, 10); | ||
64 | + }); | ||
98 | 65 | ||
99 | - function onScroll() { | ||
100 | - const wrapEl = unref(wrapElRef); | ||
101 | - if (!wrapEl) { | ||
102 | - return; | ||
103 | - } | ||
104 | - state.scrollTop = wrapEl.scrollTop; | ||
105 | - state.first = getFirst(); | ||
106 | - state.last = getLast(state.first); | ||
107 | - } | 66 | + const getItemHeightRef = computed(() => { |
67 | + return parseInt(props.itemHeight as string, 10); | ||
68 | + }); | ||
108 | 69 | ||
109 | - function renderChildren() { | ||
110 | - const { items = [] } = props; | ||
111 | - return items.slice(unref(getFirstToRenderRef), unref(getLastToRenderRef)).map(genChild); | ||
112 | - } | 70 | + const getFirstToRenderRef = computed(() => { |
71 | + return Math.max(0, state.first - unref(getBenchRef)); | ||
72 | + }); | ||
113 | 73 | ||
114 | - function genChild(item: any, index: number) { | ||
115 | - index += unref(getFirstToRenderRef); | 74 | + const getLastToRenderRef = computed(() => { |
75 | + return Math.min((props.items || []).length, state.last + unref(getBenchRef)); | ||
76 | + }); | ||
116 | 77 | ||
117 | - const top = convertToUnit(index * unref(getItemHeightRef)); | ||
118 | - return ( | ||
119 | - <div class={`${prefixCls}__item`} style={{ top }} key={index}> | ||
120 | - {getSlot(slots, 'default', { index, item })} | ||
121 | - </div> | ||
122 | - ); | ||
123 | - } | 78 | + const getContainerStyleRef = computed((): CSSProperties => { |
79 | + return { | ||
80 | + height: convertToUnit((props.items || []).length * unref(getItemHeightRef)), | ||
81 | + }; | ||
82 | + }); | ||
83 | + | ||
84 | + const getWrapStyleRef = computed((): CSSProperties => { | ||
85 | + const styles: Recordable<string> = {}; | ||
86 | + const height = convertToUnit(props.height); | ||
87 | + const minHeight = convertToUnit(props.minHeight); | ||
88 | + const minWidth = convertToUnit(props.minWidth); | ||
89 | + const maxHeight = convertToUnit(props.maxHeight); | ||
90 | + const maxWidth = convertToUnit(props.maxWidth); | ||
91 | + const width = convertToUnit(props.width); | ||
92 | + | ||
93 | + if (height) styles.height = height; | ||
94 | + if (minHeight) styles.minHeight = minHeight; | ||
95 | + if (minWidth) styles.minWidth = minWidth; | ||
96 | + if (maxHeight) styles.maxHeight = maxHeight; | ||
97 | + if (maxWidth) styles.maxWidth = maxWidth; | ||
98 | + if (width) styles.width = width; | ||
99 | + return styles; | ||
100 | + }); | ||
101 | + | ||
102 | + watch([() => props.itemHeight, () => props.height], () => { | ||
103 | + onScroll(); | ||
104 | + }); | ||
124 | 105 | ||
125 | - onMounted(() => { | ||
126 | - state.last = getLast(0); | ||
127 | - nextTick(() => { | 106 | + function getLast(first: number): number { |
107 | + const wrapEl = unref(wrapElRef); | ||
108 | + if (!wrapEl) { | ||
109 | + return 0; | ||
110 | + } | ||
111 | + const height = parseInt(props.height || 0, 10) || wrapEl.clientHeight; | ||
112 | + | ||
113 | + return first + Math.ceil(height / unref(getItemHeightRef)); | ||
114 | + } | ||
115 | + | ||
116 | + function getFirst(): number { | ||
117 | + return Math.floor(state.scrollTop / unref(getItemHeightRef)); | ||
118 | + } | ||
119 | + | ||
120 | + function onScroll() { | ||
128 | const wrapEl = unref(wrapElRef); | 121 | const wrapEl = unref(wrapElRef); |
129 | if (!wrapEl) { | 122 | if (!wrapEl) { |
130 | return; | 123 | return; |
131 | } | 124 | } |
132 | - useEventListener({ | ||
133 | - el: wrapEl, | ||
134 | - name: 'scroll', | ||
135 | - listener: onScroll, | ||
136 | - wait: 0, | 125 | + state.scrollTop = wrapEl.scrollTop; |
126 | + state.first = getFirst(); | ||
127 | + state.last = getLast(state.first); | ||
128 | + } | ||
129 | + | ||
130 | + function renderChildren() { | ||
131 | + const { items = [] } = props; | ||
132 | + return items.slice(unref(getFirstToRenderRef), unref(getLastToRenderRef)).map(genChild); | ||
133 | + } | ||
134 | + | ||
135 | + function genChild(item: any, index: number) { | ||
136 | + index += unref(getFirstToRenderRef); | ||
137 | + const top = convertToUnit(index * unref(getItemHeightRef)); | ||
138 | + return ( | ||
139 | + <div class={`${prefixCls}__item`} style={{ top }} key={index}> | ||
140 | + {getSlot(slots, 'default', { index, item })} | ||
141 | + </div> | ||
142 | + ); | ||
143 | + } | ||
144 | + | ||
145 | + onMounted(() => { | ||
146 | + state.last = getLast(0); | ||
147 | + nextTick(() => { | ||
148 | + const wrapEl = unref(wrapElRef); | ||
149 | + if (!wrapEl) { | ||
150 | + return; | ||
151 | + } | ||
152 | + useEventListener({ | ||
153 | + el: wrapEl, | ||
154 | + name: 'scroll', | ||
155 | + listener: onScroll, | ||
156 | + wait: 0, | ||
157 | + }); | ||
137 | }); | 158 | }); |
138 | }); | 159 | }); |
139 | - }); | ||
140 | - return () => ( | ||
141 | - <div class={prefixCls} style={unref(getWrapStyleRef)} ref={wrapElRef}> | ||
142 | - <div class={`${prefixCls}__container`} style={unref(getContainerStyleRef)}> | ||
143 | - {renderChildren()} | 160 | + |
161 | + return () => ( | ||
162 | + <div class={prefixCls} style={unref(getWrapStyleRef)} ref={wrapElRef}> | ||
163 | + <div class={`${prefixCls}__container`} style={unref(getContainerStyleRef)}> | ||
164 | + {renderChildren()} | ||
165 | + </div> | ||
144 | </div> | 166 | </div> |
145 | - </div> | ||
146 | - ); | ||
147 | - }, | ||
148 | -}); | 167 | + ); |
168 | + }, | ||
169 | + }); | ||
170 | +</script> | ||
171 | +<style scoped lang="less"> | ||
172 | + .virtual-scroll { | ||
173 | + position: relative; | ||
174 | + display: block; | ||
175 | + width: 100%; | ||
176 | + max-width: 100%; | ||
177 | + overflow: auto; | ||
178 | + flex: 1 1 auto; | ||
179 | + | ||
180 | + &__container { | ||
181 | + display: block; | ||
182 | + } | ||
183 | + | ||
184 | + &__item { | ||
185 | + position: absolute; | ||
186 | + right: 0; | ||
187 | + left: 0; | ||
188 | + } | ||
189 | + } | ||
190 | +</style> |
src/components/VirtualScroll/src/index.less deleted
100644 → 0
src/components/VirtualScroll/src/props.ts deleted
100644 → 0
1 | -// Helpers | ||
2 | - | ||
3 | -import type { PropType } from 'vue'; | ||
4 | -// Types | ||
5 | - | ||
6 | -export type NumberOrNumberString = PropType<string | number | undefined>; | ||
7 | - | ||
8 | -export const props = { | ||
9 | - height: [Number, String] as NumberOrNumberString, | ||
10 | - maxHeight: [Number, String] as NumberOrNumberString, | ||
11 | - maxWidth: [Number, String] as NumberOrNumberString, | ||
12 | - minHeight: [Number, String] as NumberOrNumberString, | ||
13 | - minWidth: [Number, String] as NumberOrNumberString, | ||
14 | - width: [Number, String] as NumberOrNumberString, | ||
15 | - bench: { | ||
16 | - type: [Number, String] as NumberOrNumberString, | ||
17 | - default: 0, | ||
18 | - }, | ||
19 | - itemHeight: { | ||
20 | - type: [Number, String] as NumberOrNumberString, | ||
21 | - required: true, | ||
22 | - }, | ||
23 | - items: { | ||
24 | - type: Array as PropType<any[]>, | ||
25 | - default: () => [], | ||
26 | - }, | ||
27 | -}; |
src/components/registerGlobComp.ts
@@ -11,7 +11,7 @@ import { App } from 'vue'; | @@ -11,7 +11,7 @@ import { App } from 'vue'; | ||
11 | const compList = [Icon, AntButton.Group]; | 11 | const compList = [Icon, AntButton.Group]; |
12 | 12 | ||
13 | export function registerGlobComp(app: App) { | 13 | export function registerGlobComp(app: App) { |
14 | - compList.forEach((comp: any) => { | 14 | + compList.forEach((comp) => { |
15 | app.component(comp.name || comp.displayName, comp); | 15 | app.component(comp.name || comp.displayName, comp); |
16 | }); | 16 | }); |
17 | 17 |