xt-element-ui 2.2.3 → 2.2.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.
Files changed (116) hide show
  1. package/README.md +140 -192
  2. package/docs/README.md +162 -88
  3. package/docs/components/base/theme.md +248 -0
  4. package/docs/components/base/xt-list.md +1 -1
  5. package/docs/components/base/xt-scroll-arrow.md +87 -1
  6. package/docs/components/base/xt-scroll.md +237 -0
  7. package/docs/components/utils/config.md +18 -4
  8. package/docs/components/utils/theme.md +280 -183
  9. package/lib/index.common.1.js +4 -26
  10. package/lib/index.common.2.js +1 -1
  11. package/lib/index.common.js +1871 -671
  12. package/lib/index.css +1 -1
  13. package/lib/index.umd.1.js +4 -26
  14. package/lib/index.umd.2.js +1 -1
  15. package/lib/index.umd.js +1871 -671
  16. package/lib/index.umd.min.1.js +1 -1
  17. package/lib/index.umd.min.js +1 -1
  18. package/lib/theme/element-overrides.css +607 -0
  19. package/lib/theme/element-vars.css +280 -0
  20. package/lib/xt-badge/xt-badge.common.js +1 -1
  21. package/lib/xt-badge/xt-badge.umd.js +1 -1
  22. package/lib/xt-button/xt-button.common.js +1 -1
  23. package/lib/xt-button/xt-button.umd.js +1 -1
  24. package/lib/xt-card/xt-card.common.js +1 -1
  25. package/lib/xt-card/xt-card.umd.js +1 -1
  26. package/lib/xt-card-item/xt-card-item.common.js +1 -1
  27. package/lib/xt-card-item/xt-card-item.umd.js +1 -1
  28. package/lib/xt-chart/xt-chart.common.js +5 -5
  29. package/lib/xt-chart/xt-chart.umd.js +5 -5
  30. package/lib/xt-config-provider/xt-config-provider.css +1 -1
  31. package/lib/xt-date-picker/xt-date-picker.common.1.js +4 -26
  32. package/lib/xt-date-picker/xt-date-picker.common.2.js +1 -1
  33. package/lib/xt-date-picker/xt-date-picker.common.js +1 -47
  34. package/lib/xt-date-picker/xt-date-picker.css +1 -1
  35. package/lib/xt-date-picker/xt-date-picker.umd.1.js +4 -26
  36. package/lib/xt-date-picker/xt-date-picker.umd.2.js +1 -1
  37. package/lib/xt-date-picker/xt-date-picker.umd.js +1 -47
  38. package/lib/xt-date-picker/xt-date-picker.umd.min.1.js +1 -1
  39. package/lib/xt-date-picker/xt-date-picker.umd.min.js +1 -1
  40. package/lib/xt-flex-box/xt-flex-box.common.js +1 -1
  41. package/lib/xt-flex-box/xt-flex-box.umd.js +1 -1
  42. package/lib/xt-flow/xt-flow.common.js +1 -1
  43. package/lib/xt-flow/xt-flow.umd.js +1 -1
  44. package/lib/xt-form-schema/xt-form-schema.common.js +1 -1
  45. package/lib/xt-form-schema/xt-form-schema.umd.js +1 -1
  46. package/lib/xt-grid-box/xt-grid-box.common.js +1 -1
  47. package/lib/xt-grid-box/xt-grid-box.umd.js +1 -1
  48. package/lib/xt-grid-item/xt-grid-item.common.js +1 -1
  49. package/lib/xt-grid-item/xt-grid-item.umd.js +1 -1
  50. package/lib/xt-icon/xt-icon.common.js +1 -1
  51. package/lib/xt-icon/xt-icon.umd.js +1 -1
  52. package/lib/xt-input/xt-input.common.js +1 -1
  53. package/lib/xt-input/xt-input.umd.js +1 -1
  54. package/lib/xt-list/xt-list.common.js +457 -118
  55. package/lib/xt-list/xt-list.css +1 -1
  56. package/lib/xt-list/xt-list.umd.js +457 -118
  57. package/lib/xt-list/xt-list.umd.min.js +1 -1
  58. package/lib/xt-map/xt-map.common.js +1 -1
  59. package/lib/xt-map/xt-map.umd.js +1 -1
  60. package/lib/xt-map-provider/xt-map-provider.common.js +1 -1
  61. package/lib/xt-map-provider/xt-map-provider.umd.js +1 -1
  62. package/lib/xt-page/xt-page.common.js +1 -1
  63. package/lib/xt-page/xt-page.umd.js +1 -1
  64. package/lib/xt-progress/xt-progress.common.js +1 -1
  65. package/lib/xt-progress/xt-progress.umd.js +1 -1
  66. package/lib/xt-scroll/demo.html +10 -0
  67. package/lib/xt-scroll/index.js +1 -0
  68. package/lib/xt-scroll/xt-scroll.common.js +551 -0
  69. package/lib/xt-scroll/xt-scroll.css +1 -0
  70. package/lib/xt-scroll/xt-scroll.umd.js +561 -0
  71. package/lib/xt-scroll/xt-scroll.umd.min.js +1 -0
  72. package/lib/xt-scroll-arrow/xt-scroll-arrow.common.js +591 -170
  73. package/lib/xt-scroll-arrow/xt-scroll-arrow.css +1 -1
  74. package/lib/xt-scroll-arrow/xt-scroll-arrow.umd.js +591 -170
  75. package/lib/xt-scroll-arrow/xt-scroll-arrow.umd.min.js +1 -1
  76. package/lib/xt-select-tree/xt-select-tree.common.js +1 -1
  77. package/lib/xt-select-tree/xt-select-tree.umd.js +1 -1
  78. package/lib/xt-step-price/xt-step-price.common.js +2 -2
  79. package/lib/xt-step-price/xt-step-price.umd.js +2 -2
  80. package/lib/xt-step-price-item/xt-step-price-item.common.js +1 -1
  81. package/lib/xt-step-price-item/xt-step-price-item.umd.js +1 -1
  82. package/lib/xt-tab-pane/xt-tab-pane.common.js +1 -1
  83. package/lib/xt-tab-pane/xt-tab-pane.umd.js +1 -1
  84. package/lib/xt-table/xt-table.common.js +2 -2
  85. package/lib/xt-table/xt-table.umd.js +2 -2
  86. package/lib/xt-tabs/xt-tabs.common.js +1 -1
  87. package/lib/xt-tabs/xt-tabs.umd.js +1 -1
  88. package/lib/xt-text/xt-text.common.js +1 -1
  89. package/lib/xt-text/xt-text.umd.js +1 -1
  90. package/lib/xt-time/xt-time.common.js +1 -1
  91. package/lib/xt-time/xt-time.umd.js +1 -1
  92. package/lib/xt-transfer-tree/xt-transfer-tree.common.js +1 -1
  93. package/lib/xt-transfer-tree/xt-transfer-tree.umd.js +1 -1
  94. package/lib/xt-upload/xt-upload.common.js +2 -2
  95. package/lib/xt-upload/xt-upload.umd.js +2 -2
  96. package/package.json +5 -3
  97. package/src/components/index.scss +3 -0
  98. package/src/components/theme/element-overrides.css +607 -0
  99. package/src/components/theme/element-vars.css +280 -0
  100. package/src/components/theme/iframe-injector.js +502 -0
  101. package/src/components/theme/index.js +64 -0
  102. package/src/components/xt-config-provider/style/index.scss +0 -11
  103. package/src/components/xt-date-picker/component/RangeDate.vue +1 -29
  104. package/src/components/xt-date-picker/style/index.scss +80 -53
  105. package/src/components/xt-list/index.vue +23 -12
  106. package/src/components/xt-scroll/index.js +9 -0
  107. package/src/components/xt-scroll/index.vue +363 -0
  108. package/src/components/xt-scroll/style/index.scss +107 -0
  109. package/src/components/xt-scroll-arrow/index.vue +200 -83
  110. package/src/components/xt-scroll-arrow/style/index.scss +16 -5
  111. package/src/index.js +9 -2
  112. package/src/utils/color.js +105 -0
  113. package/src/utils/index.js +456 -468
  114. package/src/utils/theme-vars.js +134 -0
  115. package/lib/css/1.87a493be.css +0 -1
  116. package/lib/xt-date-picker/css/1.87a493be.css +0 -1
@@ -103,6 +103,13 @@ return /******/ (function(modules) { // webpackBootstrap
103
103
 
104
104
  /***/ }),
105
105
 
106
+ /***/ "f699":
107
+ /***/ (function(module, exports, __webpack_require__) {
108
+
109
+ // extracted by mini-css-extract-plugin
110
+
111
+ /***/ }),
112
+
106
113
  /***/ "fb15":
107
114
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
108
115
 
@@ -132,32 +139,465 @@ if (typeof window !== 'undefined') {
132
139
  // EXTERNAL MODULE: ./src/components/xt-scroll-arrow/style/index.scss
133
140
  var style = __webpack_require__("4e56");
134
141
 
135
- // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d8e97dbc-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/xt-scroll-arrow/index.vue?vue&type=template&id=3d6a271c
136
- var render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"xt-scroll-arrow",class:[`xt-scroll-arrow--${_vm.direction}`],style:(_vm.containerStyle)},[(_vm.showLeftArrow)?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--left",on:{"click":_vm.scrollLeft}},[_c('i',{staticClass:"el-icon-arrow-left"})]):_vm._e(),_c('div',{ref:"scrollContainer",staticClass:"xt-scroll-arrow__content",on:{"scroll":_vm.handleScroll,"click":_vm.handleClick}},[_vm._t("default")],2),(_vm.showRightArrow)?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--right",on:{"click":_vm.scrollRight}},[_c('i',{staticClass:"el-icon-arrow-right"})]):_vm._e(),(_vm.showTopArrow && _vm.direction === 'vertical')?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--top",on:{"click":_vm.scrollTop}},[_c('i',{staticClass:"el-icon-arrow-up"})]):_vm._e(),(_vm.showBottomArrow && _vm.direction === 'vertical')?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--bottom",on:{"click":_vm.scrollBottom}},[_c('i',{staticClass:"el-icon-arrow-down"})]):_vm._e()])
142
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"1d9e3ae5-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/xt-scroll-arrow/index.vue?vue&type=template&id=6d59f4e4
143
+ var render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"xt-scroll-arrow",class:[`xt-scroll-arrow--${_vm.direction}`],style:(_vm.containerStyle)},[(_vm.showLeftArrow)?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--left",on:{"click":_vm.scrollLeft}},[_c('i',{staticClass:"el-icon-arrow-left"})]):_vm._e(),(_vm.showTopArrow && _vm.direction === 'vertical')?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--top",on:{"click":_vm.scrollTop}},[_c('i',{staticClass:"el-icon-arrow-up"})]):_vm._e(),_c('xt-scroll',{ref:"scrollRef",class:['xt-scroll-arrow__content', _vm.scrollClass],attrs:{"v-scroll":_vm.vScroll,"data":_vm.vScrollData,"item-size":_vm.itemSize,"key-field":_vm.keyField,"buffer-size":_vm.bufferSize,"scroll-direction":_vm.direction,"loading":_vm.vScrollLoading,"load-more":_vm.loadMore,"load-more-text":_vm.loadMoreText,"load-more-loading":_vm.loadMoreLoading},on:{"scroll":_vm.onScroll,"load-more":function($event){return _vm.$emit('load-more')}},scopedSlots:_vm._u([(_vm.vScroll)?{key:"item",fn:function({ item, index }){return [_vm._t("vitem",function(){return [_c('div',{staticClass:"xt-scroll-arrow__vitem"},[_vm._v(_vm._s(item))])]},{"item":item,"index":index})]}}:{key:"default",fn:function(undefined){return _vm._t("default")}}],null,true)}),(_vm.showRightArrow)?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--right",on:{"click":_vm.scrollRight}},[_c('i',{staticClass:"el-icon-arrow-right"})]):_vm._e(),(_vm.showBottomArrow && _vm.direction === 'vertical')?_c('div',{staticClass:"xt-scroll-arrow__btn xt-scroll-arrow__btn--bottom",on:{"click":_vm.scrollBottom}},[_c('i',{staticClass:"el-icon-arrow-down"})]):_vm._e()],1)
137
144
  }
138
145
  var staticRenderFns = []
139
146
 
140
147
 
141
- // CONCATENATED MODULE: ./src/components/xt-scroll-arrow/index.vue?vue&type=template&id=3d6a271c
148
+ // CONCATENATED MODULE: ./src/components/xt-scroll-arrow/index.vue?vue&type=template&id=6d59f4e4
149
+
150
+ // EXTERNAL MODULE: ./src/components/xt-scroll/style/index.scss
151
+ var xt_scroll_style = __webpack_require__("f699");
152
+
153
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"1d9e3ae5-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/xt-scroll/index.vue?vue&type=template&id=74170e40
154
+ var xt_scrollvue_type_template_id_74170e40_render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"xt-scroll",class:[`xt-scroll--${_vm.scrollDirection}`],style:(_vm.containerStyle)},[_c('div',{ref:"scrollWrap",staticClass:"xt-scroll__wrap",class:[`xt-scroll__wrap--${_vm.scrollDirection}`],on:{"scroll":_vm.onScroll}},[(_vm.vScroll)?_c('div',{staticClass:"xt-scroll__virtual",style:(_vm.virtualWrapperStyle)},[(_vm.scrollDirection === 'vertical')?_c('div',{staticClass:"xt-scroll__phantom",style:({ height: _vm.totalSize + 'px' })},[_c('div',{style:({ transform: `translateY(${_vm.offsetStart}px)` })},[_vm._l((_vm.visibleData),function(item,index){return [_c('div',{key:_vm.getItemKey(item, index),staticClass:"xt-scroll__item",class:_vm.itemClassName,style:(_vm.itemStyle)},[_vm._t("item",function(){return [_vm._v(" "+_vm._s(item)+" ")]},{"item":item,"index":_vm.getOriginalIndex(index)})],2)]})],2)]):(_vm.scrollDirection === 'horizontal')?_c('div',{staticClass:"xt-scroll__phantom",style:({ width: _vm.totalSize + 'px' })},[_c('div',{style:({ transform: `translateX(${_vm.offsetStart}px)` })},[_vm._l((_vm.visibleData),function(item,index){return [_c('div',{key:_vm.getItemKey(item, index),staticClass:"xt-scroll__item",class:_vm.itemClassName,style:(_vm.itemStyle)},[_vm._t("item",function(){return [_vm._v(" "+_vm._s(item)+" ")]},{"item":item,"index":_vm.getOriginalIndex(index)})],2)]})],2)]):_vm._e()]):_vm._e(),(_vm.vScroll && (!_vm.data || !_vm.data.length) && !_vm.loading)?_c('div',{staticClass:"xt-scroll__empty"},[_vm._t("empty",function(){return [_c('span',[_vm._v(_vm._s(_vm.emptyText))])]})],2):_vm._e(),(_vm.loading)?_c('div',{staticClass:"xt-scroll__loading"},[_vm._t("loading",function(){return [_c('i',{staticClass:"el-icon-loading"}),_c('span',[_vm._v("加载中...")])]})],2):_vm._e(),(!_vm.vScroll)?_c('div',{staticClass:"xt-scroll__default"},[_vm._t("default")],2):_vm._e(),(_vm.vScroll && _vm.showLoadMore && !_vm.loading)?_c('div',{staticClass:"xt-scroll__loadmore",on:{"click":_vm.onLoadMore}},[_c('el-button',{attrs:{"type":"text","loading":_vm.loadMoreLoading}},[_vm._v(" "+_vm._s(_vm.loadMoreText)+" ")])],1):_vm._e()])])
155
+ }
156
+ var xt_scrollvue_type_template_id_74170e40_staticRenderFns = []
157
+
158
+
159
+ // CONCATENATED MODULE: ./src/components/xt-scroll/index.vue?vue&type=template&id=74170e40
160
+
161
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/xt-scroll/index.vue?vue&type=script&lang=js
162
+
163
+ /* harmony default export */ var xt_scrollvue_type_script_lang_js = ({
164
+ name: 'XtScroll',
165
+
166
+ props: {
167
+ // ========== 虚拟滚动配置 ==========
168
+ /** 是否启用虚拟滚动 */
169
+ vScroll: { type: Boolean, default: false },
170
+ /** 数据源 */
171
+ data: { type: Array, default: () => [] },
172
+ /** 每个 item 的固定尺寸(px),纵向为高度,横向为宽度 */
173
+ itemSize: { type: Number, default: 50 },
174
+ /** item 的唯一键字段名 */
175
+ keyField: { type: String, default: 'id' },
176
+ /** 预渲染缓冲区大小(item 数量) */
177
+ bufferSize: { type: Number, default: 5 },
178
+ /** 虚拟滚动方向 */
179
+ scrollDirection: {
180
+ type: String,
181
+ default: 'vertical',
182
+ validator: (v) => ['vertical', 'horizontal'].includes(v)
183
+ },
184
+ /** item 的自定义 CSS 类名 */
185
+ itemClassName: { type: String, default: '' },
186
+
187
+ // ========== 容器尺寸 ==========
188
+ height: { type: [Number, String], default: '' },
189
+ maxHeight: { type: [Number, String], default: '' },
190
+ width: { type: [Number, String], default: '' },
191
+ maxWidth: { type: [Number, String], default: '' },
192
+
193
+ // ========== 状态 ==========
194
+ loading: { type: Boolean, default: false },
195
+ emptyText: { type: String, default: '暂无数据' },
196
+
197
+ // ========== 加载更多 ==========
198
+ loadMore: { type: Boolean, default: false },
199
+ loadMoreText: { type: String, default: '加载更多' },
200
+ loadMoreLoading: { type: Boolean, default: false }
201
+ },
202
+
203
+ data() {
204
+ return {
205
+ scrollPos: 0,
206
+ containerSize: 0,
207
+ resizeObserver: null,
208
+ rafId: null
209
+ }
210
+ },
211
+
212
+ computed: {
213
+ containerStyle() {
214
+ const style = {}
215
+ if (this.height) {
216
+ style.height = typeof this.height === 'number' ? this.height + 'px' : this.height
217
+ }
218
+ if (this.maxHeight) {
219
+ style.maxHeight = typeof this.maxHeight === 'number' ? this.maxHeight + 'px' : this.maxHeight
220
+ }
221
+ if (this.width) {
222
+ style.width = typeof this.width === 'number' ? this.width + 'px' : this.width
223
+ }
224
+ if (this.maxWidth) {
225
+ style.maxWidth = typeof this.maxWidth === 'number' ? this.maxWidth + 'px' : this.maxWidth
226
+ }
227
+ return style
228
+ },
229
+
230
+ virtualWrapperStyle() {
231
+ const style = {}
232
+ if (this.scrollDirection === 'horizontal') {
233
+ style.height = '100%'
234
+ }
235
+ return style
236
+ },
237
+
238
+ itemStyle() {
239
+ return this.scrollDirection === 'vertical'
240
+ ? { height: this.itemSize + 'px' }
241
+ : { width: this.itemSize + 'px', display: 'inline-block' }
242
+ },
243
+
244
+ /** 数据总条数 */
245
+ dataCount() {
246
+ return (this.data && this.data.length) || 0
247
+ },
248
+
249
+ /** 总滚动尺寸 */
250
+ totalSize() {
251
+ return this.dataCount * this.itemSize
252
+ },
253
+
254
+ /** 虚拟滚动:可见范围起始索引 */
255
+ visibleStartIndex() {
256
+ if (!this.vScroll) return 0
257
+ const start = Math.floor(this.scrollPos / this.itemSize)
258
+ return Math.max(0, start - this.bufferSize)
259
+ },
260
+
261
+ /** 虚拟滚动:可见范围结束索引 */
262
+ visibleEndIndex() {
263
+ if (!this.vScroll) return this.dataCount
264
+ const visibleCount = Math.ceil(this.containerSize / this.itemSize)
265
+ const end = Math.ceil(this.scrollPos / this.itemSize) + visibleCount
266
+ return Math.min(this.dataCount, end + this.bufferSize)
267
+ },
268
+
269
+ /** 可见数据 */
270
+ visibleData() {
271
+ if (!this.vScroll || !this.data) return this.data || []
272
+ return this.data.slice(this.visibleStartIndex, this.visibleEndIndex)
273
+ },
274
+
275
+ /** 偏移量(px) */
276
+ offsetStart() {
277
+ return this.visibleStartIndex * this.itemSize
278
+ },
279
+
280
+ /** 是否显示加载更多 */
281
+ showLoadMore() {
282
+ if (!this.loadMore || !this.dataCount) return false
283
+ if (this.scrollDirection === 'vertical') {
284
+ return this.scrollPos + this.containerSize >= this.totalSize - this.itemSize * 2
285
+ }
286
+ return false
287
+ }
288
+ },
289
+
290
+ watch: {
291
+ data: {
292
+ handler() {
293
+ this.$nextTick(() => {
294
+ this.updateContainerSize()
295
+ })
296
+ }
297
+ },
298
+ vScroll(val) {
299
+ if (val) {
300
+ this.$nextTick(() => {
301
+ this.updateContainerSize()
302
+ this.bindResizeObserver()
303
+ })
304
+ }
305
+ }
306
+ },
307
+
308
+ mounted() {
309
+ this.$nextTick(() => {
310
+ this.updateContainerSize()
311
+ if (this.vScroll) {
312
+ this.bindResizeObserver()
313
+ }
314
+ })
315
+ },
316
+
317
+ beforeDestroy() {
318
+ this.unbindResizeObserver()
319
+ if (this.rafId && typeof cancelAnimationFrame !== 'undefined') {
320
+ cancelAnimationFrame(this.rafId)
321
+ this.rafId = null
322
+ }
323
+ },
324
+
325
+ methods: {
326
+ // ========== 虚拟滚动 ==========
327
+ onScroll() {
328
+ const wrap = this.$refs.scrollWrap
329
+ if (!wrap) return
330
+
331
+ const pos = this.scrollDirection === 'vertical' ? wrap.scrollTop : wrap.scrollLeft
332
+ this.scrollPos = pos
333
+
334
+ // 对外 emit scroll 事件,传递 scrollTop/scrollLeft 方便父组件使用
335
+ this.$emit('scroll', {
336
+ scrollTop: wrap.scrollTop,
337
+ scrollLeft: wrap.scrollLeft
338
+ })
339
+
340
+ if (this.rafId) return
341
+ if (typeof requestAnimationFrame === 'undefined') return
342
+ this.rafId = requestAnimationFrame(() => {
343
+ this.$forceUpdate()
344
+ this.rafId = null
345
+ })
346
+ },
347
+
348
+ getItemKey(item, index) {
349
+ if (item && typeof item === 'object' && this.keyField && item[this.keyField] != null) {
350
+ return item[this.keyField]
351
+ }
352
+ return `vitem_${this.visibleStartIndex + index}`
353
+ },
354
+
355
+ getOriginalIndex(index) {
356
+ return this.visibleStartIndex + index
357
+ },
358
+
359
+ // ========== 容器尺寸 ==========
360
+ updateContainerSize() {
361
+ const wrap = this.$refs.scrollWrap
362
+ if (!wrap) return
363
+
364
+ this.containerSize = this.scrollDirection === 'vertical'
365
+ ? wrap.clientHeight
366
+ : wrap.clientWidth
367
+ },
368
+
369
+ bindResizeObserver() {
370
+ if (typeof ResizeObserver === 'undefined') return
371
+ this.unbindResizeObserver()
372
+
373
+ const wrap = this.$refs.scrollWrap
374
+ if (!wrap) return
375
+
376
+ this.resizeObserver = new ResizeObserver(() => {
377
+ this.updateContainerSize()
378
+ })
379
+ this.resizeObserver.observe(wrap)
380
+ },
381
+
382
+ unbindResizeObserver() {
383
+ if (this.resizeObserver) {
384
+ this.resizeObserver.disconnect()
385
+ this.resizeObserver = null
386
+ }
387
+ },
388
+
389
+ // ========== 加载更多 ==========
390
+ onLoadMore() {
391
+ this.$emit('load-more')
392
+ },
393
+
394
+ // ========== 对外暴露方法 ==========
395
+ /** 获取原生滚动容器 DOM 元素 */
396
+ getScrollContainer() {
397
+ return this.$refs.scrollWrap
398
+ },
399
+
400
+ /** 滚动到指定位置 */
401
+ scrollTo(pos, behavior) {
402
+ const wrap = this.$refs.scrollWrap
403
+ if (!wrap) return
404
+
405
+ const opts = behavior && typeof behavior === 'string'
406
+ ? { behavior }
407
+ : { behavior: 'smooth' }
408
+
409
+ if (this.scrollDirection === 'vertical') {
410
+ wrap.scrollTo({ top: pos, ...opts })
411
+ } else {
412
+ wrap.scrollTo({ left: pos, ...opts })
413
+ }
414
+ },
415
+
416
+ /** 滚动到指定索引的 item */
417
+ scrollToItem(index) {
418
+ const pos = index * this.itemSize
419
+ this.scrollTo(pos)
420
+ },
421
+
422
+ /** 滚动到顶部/左侧 */
423
+ scrollToStart() {
424
+ this.scrollTo(0)
425
+ },
426
+
427
+ /** 滚动到底部/右侧 */
428
+ scrollToEnd() {
429
+ this.scrollTo(this.totalSize)
430
+ },
431
+
432
+ /** 获取当前滚动位置 */
433
+ getScrollPos() {
434
+ return this.scrollPos
435
+ }
436
+ }
437
+ });
438
+
439
+ // CONCATENATED MODULE: ./src/components/xt-scroll/index.vue?vue&type=script&lang=js
440
+ /* harmony default export */ var components_xt_scrollvue_type_script_lang_js = (xt_scrollvue_type_script_lang_js);
441
+ // CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
442
+ /* globals __VUE_SSR_CONTEXT__ */
443
+
444
+ // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
445
+ // This module is a runtime utility for cleaner component module output and will
446
+ // be included in the final webpack user bundle.
447
+
448
+ function normalizeComponent(
449
+ scriptExports,
450
+ render,
451
+ staticRenderFns,
452
+ functionalTemplate,
453
+ injectStyles,
454
+ scopeId,
455
+ moduleIdentifier /* server only */,
456
+ shadowMode /* vue-cli only */
457
+ ) {
458
+ // Vue.extend constructor export interop
459
+ var options =
460
+ typeof scriptExports === 'function' ? scriptExports.options : scriptExports
461
+
462
+ // render functions
463
+ if (render) {
464
+ options.render = render
465
+ options.staticRenderFns = staticRenderFns
466
+ options._compiled = true
467
+ }
468
+
469
+ // functional template
470
+ if (functionalTemplate) {
471
+ options.functional = true
472
+ }
473
+
474
+ // scopedId
475
+ if (scopeId) {
476
+ options._scopeId = 'data-v-' + scopeId
477
+ }
478
+
479
+ var hook
480
+ if (moduleIdentifier) {
481
+ // server build
482
+ hook = function (context) {
483
+ // 2.3 injection
484
+ context =
485
+ context || // cached call
486
+ (this.$vnode && this.$vnode.ssrContext) || // stateful
487
+ (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
488
+ // 2.2 with runInNewContext: true
489
+ if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
490
+ context = __VUE_SSR_CONTEXT__
491
+ }
492
+ // inject component styles
493
+ if (injectStyles) {
494
+ injectStyles.call(this, context)
495
+ }
496
+ // register component module identifier for async chunk inferrence
497
+ if (context && context._registeredComponents) {
498
+ context._registeredComponents.add(moduleIdentifier)
499
+ }
500
+ }
501
+ // used by ssr in case component is cached and beforeCreate
502
+ // never gets called
503
+ options._ssrRegister = hook
504
+ } else if (injectStyles) {
505
+ hook = shadowMode
506
+ ? function () {
507
+ injectStyles.call(
508
+ this,
509
+ (options.functional ? this.parent : this).$root.$options.shadowRoot
510
+ )
511
+ }
512
+ : injectStyles
513
+ }
514
+
515
+ if (hook) {
516
+ if (options.functional) {
517
+ // for template-only hot-reload because in that case the render fn doesn't
518
+ // go through the normalizer
519
+ options._injectStyles = hook
520
+ // register for functional component in vue file
521
+ var originalRender = options.render
522
+ options.render = function renderWithStyleInjection(h, context) {
523
+ hook.call(context)
524
+ return originalRender(h, context)
525
+ }
526
+ } else {
527
+ // inject component registration as beforeCreate hook
528
+ var existing = options.beforeCreate
529
+ options.beforeCreate = existing ? [].concat(existing, hook) : [hook]
530
+ }
531
+ }
532
+
533
+ return {
534
+ exports: scriptExports,
535
+ options: options
536
+ }
537
+ }
538
+
539
+ // CONCATENATED MODULE: ./src/components/xt-scroll/index.vue
540
+
541
+
542
+
543
+
544
+
545
+ /* normalize component */
546
+
547
+ var component = normalizeComponent(
548
+ components_xt_scrollvue_type_script_lang_js,
549
+ xt_scrollvue_type_template_id_74170e40_render,
550
+ xt_scrollvue_type_template_id_74170e40_staticRenderFns,
551
+ false,
552
+ null,
553
+ null,
554
+ null
555
+
556
+ )
557
+
558
+ /* harmony default export */ var xt_scroll = (component.exports);
559
+ // CONCATENATED MODULE: ./src/components/xt-scroll/index.js
560
+
561
+
562
+
563
+ xt_scroll.install = function (Vue) {
564
+ Vue.component(xt_scroll.name, xt_scroll)
565
+ }
566
+
567
+ /* harmony default export */ var components_xt_scroll = (xt_scroll);
142
568
 
143
569
  // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--1-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/xt-scroll-arrow/index.vue?vue&type=script&lang=js
144
570
 
571
+
572
+
145
573
  /* harmony default export */ var xt_scroll_arrowvue_type_script_lang_js = ({
146
574
  name: 'XtScrollArrow',
575
+
576
+ components: {
577
+ XtScroll: components_xt_scroll
578
+ },
579
+
147
580
  props: {
581
+ // ========== 滚动方向 ==========
148
582
  direction: {
149
583
  type: String,
150
584
  default: 'horizontal',
151
585
  validator: (val) => ['horizontal', 'vertical'].includes(val)
152
586
  },
587
+
588
+ // ========== 滚动步长 ==========
153
589
  scrollStep: {
154
590
  type: Number,
155
591
  default: 100
156
592
  },
593
+
594
+ // ========== 箭头显示 ==========
157
595
  autoHide: {
158
596
  type: Boolean,
159
597
  default: true
160
598
  },
599
+
600
+ // ========== 容器尺寸 ==========
161
601
  height: {
162
602
  type: [String, Number],
163
603
  default: ''
@@ -166,6 +606,8 @@ var staticRenderFns = []
166
606
  type: [String, Number],
167
607
  default: ''
168
608
  },
609
+
610
+ // ========== 模式 ==========
169
611
  appendMode: {
170
612
  type: Boolean,
171
613
  default: false
@@ -173,16 +615,65 @@ var staticRenderFns = []
173
615
  clickMode: {
174
616
  type: Boolean,
175
617
  default: false
618
+ },
619
+
620
+ // ========== 虚拟滚动配置 ==========
621
+ /** 是否启用虚拟滚动 */
622
+ vScroll: {
623
+ type: Boolean,
624
+ default: false
625
+ },
626
+ /** 虚拟滚动数据源 */
627
+ vScrollData: {
628
+ type: Array,
629
+ default: () => []
630
+ },
631
+ /** 每个 item 的固定尺寸(px) */
632
+ itemSize: {
633
+ type: Number,
634
+ default: 50
635
+ },
636
+ /** item 的唯一键字段名 */
637
+ keyField: {
638
+ type: String,
639
+ default: 'id'
640
+ },
641
+ /** 预渲染缓冲区大小 */
642
+ bufferSize: {
643
+ type: Number,
644
+ default: 5
645
+ },
646
+ /** 虚拟滚动加载状态 */
647
+ vScrollLoading: {
648
+ type: Boolean,
649
+ default: false
650
+ },
651
+
652
+ // ========== 加载更多 ==========
653
+ loadMore: {
654
+ type: Boolean,
655
+ default: false
656
+ },
657
+ loadMoreText: {
658
+ type: String,
659
+ default: '加载更多'
660
+ },
661
+ loadMoreLoading: {
662
+ type: Boolean,
663
+ default: false
176
664
  }
177
665
  },
666
+
178
667
  data() {
179
668
  return {
180
669
  showLeftArrow: false,
181
670
  showRightArrow: false,
182
671
  showTopArrow: false,
183
- showBottomArrow: false
672
+ showBottomArrow: false,
673
+ mutationObserver: null
184
674
  }
185
675
  },
676
+
186
677
  computed: {
187
678
  containerStyle() {
188
679
  const style = {}
@@ -193,27 +684,44 @@ var staticRenderFns = []
193
684
  style.width = typeof this.width === 'number' ? `${this.width}px` : this.width
194
685
  }
195
686
  return style
687
+ },
688
+ scrollClass() {
689
+ return this.vScroll ? 'xt-scroll-arrow__content--virtual' : ''
196
690
  }
197
691
  },
692
+
198
693
  watch: {
199
694
  direction() {
200
695
  this.$nextTick(() => {
201
696
  this.checkScroll()
202
697
  })
698
+ },
699
+ vScrollData: {
700
+ handler() {
701
+ this.$nextTick(() => {
702
+ this.checkScroll()
703
+ })
704
+ }
203
705
  }
204
706
  },
707
+
205
708
  mounted() {
206
709
  this.$nextTick(() => {
207
710
  this.checkScroll()
208
711
  })
209
712
  this.addResizeObserver()
210
- this.addMutationObserver()
713
+ if (!this.vScroll) {
714
+ this.addMutationObserver()
715
+ }
211
716
  },
717
+
212
718
  beforeDestroy() {
213
719
  this.removeResizeObserver()
214
720
  this.removeMutationObserver()
215
721
  },
722
+
216
723
  methods: {
724
+ // ========== 尺寸监听 ==========
217
725
  addResizeObserver() {
218
726
  if (typeof ResizeObserver !== 'undefined') {
219
727
  this.resizeObserver = new ResizeObserver(() => {
@@ -221,17 +729,19 @@ var staticRenderFns = []
221
729
  this.checkScroll()
222
730
  })
223
731
  })
224
- const container = this.$refs.scrollContainer
732
+ const container = this.$el
225
733
  if (container) {
226
734
  this.resizeObserver.observe(container)
227
735
  }
228
736
  }
229
737
  },
738
+
230
739
  removeResizeObserver() {
231
740
  if (this.resizeObserver) {
232
741
  this.resizeObserver.disconnect()
233
742
  }
234
743
  },
744
+
235
745
  addMutationObserver() {
236
746
  if (typeof MutationObserver !== 'undefined') {
237
747
  this.mutationObserver = new MutationObserver(() => {
@@ -242,28 +752,37 @@ var staticRenderFns = []
242
752
  }
243
753
  })
244
754
  })
245
- const container = this.$refs.scrollContainer
755
+ const container = this.$refs.scrollRef
246
756
  if (container) {
247
- this.mutationObserver.observe(container, {
248
- childList: true,
249
- subtree: true
250
- })
757
+ const wrap = container.getScrollContainer && container.getScrollContainer()
758
+ if (wrap) {
759
+ this.mutationObserver.observe(wrap, {
760
+ childList: true,
761
+ subtree: true
762
+ })
763
+ }
251
764
  }
252
765
  }
253
766
  },
767
+
254
768
  removeMutationObserver() {
255
769
  if (this.mutationObserver) {
256
770
  this.mutationObserver.disconnect()
257
771
  }
258
772
  },
773
+
774
+ // ========== 滚动检测 ==========
259
775
  checkScroll() {
260
- const container = this.$refs.scrollContainer
261
- if (!container) return
776
+ const scrollRef = this.$refs.scrollRef
777
+ if (!scrollRef) return
778
+
779
+ const wrap = scrollRef.getScrollContainer && scrollRef.getScrollContainer()
780
+ if (!wrap) return
262
781
 
263
782
  if (this.direction === 'horizontal') {
264
- const scrollLeft = container.scrollLeft
265
- const scrollWidth = container.scrollWidth
266
- const clientWidth = container.clientWidth
783
+ const scrollLeft = wrap.scrollLeft
784
+ const scrollWidth = wrap.scrollWidth
785
+ const clientWidth = wrap.clientWidth
267
786
  const maxScroll = scrollWidth - clientWidth
268
787
 
269
788
  this.showTopArrow = false
@@ -271,15 +790,15 @@ var staticRenderFns = []
271
790
 
272
791
  if (this.autoHide) {
273
792
  this.showLeftArrow = scrollLeft > 0
274
- this.showRightArrow = maxScroll > 0 && scrollLeft < maxScroll
793
+ this.showRightArrow = maxScroll > 0 && scrollLeft < maxScroll - 1
275
794
  } else {
276
795
  this.showLeftArrow = maxScroll > 0
277
796
  this.showRightArrow = maxScroll > 0
278
797
  }
279
798
  } else {
280
- const scrollTop = container.scrollTop
281
- const scrollHeight = container.scrollHeight
282
- const clientHeight = container.clientHeight
799
+ const scrollTop = wrap.scrollTop
800
+ const scrollHeight = wrap.scrollHeight
801
+ const clientHeight = wrap.clientHeight
283
802
  const maxScroll = scrollHeight - clientHeight
284
803
 
285
804
  this.showLeftArrow = false
@@ -287,181 +806,83 @@ var staticRenderFns = []
287
806
 
288
807
  if (this.autoHide) {
289
808
  this.showTopArrow = scrollTop > 0
290
- this.showBottomArrow = maxScroll > 0 && scrollTop < maxScroll
809
+ this.showBottomArrow = maxScroll > 0 && scrollTop < maxScroll - 1
291
810
  } else {
292
811
  this.showTopArrow = maxScroll > 0
293
812
  this.showBottomArrow = maxScroll > 0
294
813
  }
295
814
  }
296
815
  },
297
- handleScroll() {
816
+
817
+ onScroll() {
298
818
  this.checkScroll()
299
- this.$emit('scroll', this.$refs.scrollContainer)
300
- },
301
- handleClick(e) {
302
- if (!this.clickMode) return
303
- const container = this.$refs.scrollContainer
304
- if (!container) return
305
-
306
- const target = e.target
307
- if (!target || target === container) return
308
-
309
- const containerRect = container.getBoundingClientRect()
310
- const targetRect = target.getBoundingClientRect()
311
-
312
- const isVisible = (
313
- targetRect.left >= containerRect.left &&
314
- targetRect.right <= containerRect.right &&
315
- targetRect.top >= containerRect.top &&
316
- targetRect.bottom <= containerRect.bottom
317
- )
318
-
319
- if (!isVisible) {
320
- if (this.direction === 'horizontal') {
321
- const scrollLeft = target.offsetLeft - (container.clientWidth - targetRect.width) / 2
322
- container.scrollTo({ left: scrollLeft, behavior: 'smooth' })
323
- } else {
324
- const scrollTop = target.offsetTop - (container.clientHeight - targetRect.height) / 2
325
- container.scrollTo({ top: scrollTop, behavior: 'smooth' })
326
- }
327
- }
819
+ this.$emit('scroll', this.$refs.scrollRef)
328
820
  },
821
+
822
+ // ========== 滚动操作 ==========
329
823
  scrollLeft() {
330
- const container = this.$refs.scrollContainer
331
- if (container) {
332
- container.scrollBy({ left: -this.scrollStep, behavior: 'smooth' })
333
- }
824
+ this._scrollBy(-this.scrollStep)
334
825
  },
826
+
335
827
  scrollRight() {
336
- const container = this.$refs.scrollContainer
337
- if (container) {
338
- container.scrollBy({ left: this.scrollStep, behavior: 'smooth' })
339
- }
828
+ this._scrollBy(this.scrollStep)
340
829
  },
830
+
341
831
  scrollTop() {
342
- const container = this.$refs.scrollContainer
343
- if (container) {
344
- container.scrollBy({ top: -this.scrollStep, behavior: 'smooth' })
345
- }
832
+ this._scrollBy(-this.scrollStep, 'vertical')
346
833
  },
834
+
347
835
  scrollBottom() {
348
- const container = this.$refs.scrollContainer
349
- if (container) {
350
- container.scrollBy({ top: this.scrollStep, behavior: 'smooth' })
836
+ this._scrollBy(this.scrollStep, 'vertical')
837
+ },
838
+
839
+ _scrollBy(delta, dir) {
840
+ const direction = dir || this.direction
841
+ const scrollRef = this.$refs.scrollRef
842
+ if (!scrollRef) return
843
+
844
+ const wrap = scrollRef.getScrollContainer && scrollRef.getScrollContainer()
845
+ if (!wrap) return
846
+
847
+ if (direction === 'horizontal') {
848
+ wrap.scrollBy({ left: delta, behavior: 'smooth' })
849
+ } else {
850
+ wrap.scrollBy({ top: delta, behavior: 'smooth' })
351
851
  }
352
852
  },
853
+
353
854
  scrollToEnd() {
354
- const container = this.$refs.scrollContainer
355
- if (!container) return
855
+ const scrollRef = this.$refs.scrollRef
856
+ if (!scrollRef) return
857
+ const wrap = scrollRef.getScrollContainer && scrollRef.getScrollContainer()
858
+ if (!wrap) return
859
+
356
860
  if (this.direction === 'horizontal') {
357
- container.scrollTo({ left: container.scrollWidth, behavior: 'smooth' })
861
+ wrap.scrollTo({ left: wrap.scrollWidth, behavior: 'smooth' })
358
862
  } else {
359
- container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' })
863
+ wrap.scrollTo({ top: wrap.scrollHeight, behavior: 'smooth' })
360
864
  }
865
+ },
866
+
867
+ // ========== 对外暴露方法 ==========
868
+ /** 获取滚动位置 */
869
+ getScrollPos() {
870
+ const scrollRef = this.$refs.scrollRef
871
+ if (!scrollRef) return 0
872
+ const wrap = scrollRef.getScrollContainer && scrollRef.getScrollContainer()
873
+ if (!wrap) return 0
874
+ return this.direction === 'horizontal' ? wrap.scrollLeft : wrap.scrollTop
875
+ },
876
+
877
+ /** 获取 xt-scroll 组件实例 */
878
+ getScrollInstance() {
879
+ return this.$refs.scrollRef
361
880
  }
362
881
  }
363
882
  });
364
883
 
365
884
  // CONCATENATED MODULE: ./src/components/xt-scroll-arrow/index.vue?vue&type=script&lang=js
366
885
  /* harmony default export */ var components_xt_scroll_arrowvue_type_script_lang_js = (xt_scroll_arrowvue_type_script_lang_js);
367
- // CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
368
- /* globals __VUE_SSR_CONTEXT__ */
369
-
370
- // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
371
- // This module is a runtime utility for cleaner component module output and will
372
- // be included in the final webpack user bundle.
373
-
374
- function normalizeComponent(
375
- scriptExports,
376
- render,
377
- staticRenderFns,
378
- functionalTemplate,
379
- injectStyles,
380
- scopeId,
381
- moduleIdentifier /* server only */,
382
- shadowMode /* vue-cli only */
383
- ) {
384
- // Vue.extend constructor export interop
385
- var options =
386
- typeof scriptExports === 'function' ? scriptExports.options : scriptExports
387
-
388
- // render functions
389
- if (render) {
390
- options.render = render
391
- options.staticRenderFns = staticRenderFns
392
- options._compiled = true
393
- }
394
-
395
- // functional template
396
- if (functionalTemplate) {
397
- options.functional = true
398
- }
399
-
400
- // scopedId
401
- if (scopeId) {
402
- options._scopeId = 'data-v-' + scopeId
403
- }
404
-
405
- var hook
406
- if (moduleIdentifier) {
407
- // server build
408
- hook = function (context) {
409
- // 2.3 injection
410
- context =
411
- context || // cached call
412
- (this.$vnode && this.$vnode.ssrContext) || // stateful
413
- (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
414
- // 2.2 with runInNewContext: true
415
- if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
416
- context = __VUE_SSR_CONTEXT__
417
- }
418
- // inject component styles
419
- if (injectStyles) {
420
- injectStyles.call(this, context)
421
- }
422
- // register component module identifier for async chunk inferrence
423
- if (context && context._registeredComponents) {
424
- context._registeredComponents.add(moduleIdentifier)
425
- }
426
- }
427
- // used by ssr in case component is cached and beforeCreate
428
- // never gets called
429
- options._ssrRegister = hook
430
- } else if (injectStyles) {
431
- hook = shadowMode
432
- ? function () {
433
- injectStyles.call(
434
- this,
435
- (options.functional ? this.parent : this).$root.$options.shadowRoot
436
- )
437
- }
438
- : injectStyles
439
- }
440
-
441
- if (hook) {
442
- if (options.functional) {
443
- // for template-only hot-reload because in that case the render fn doesn't
444
- // go through the normalizer
445
- options._injectStyles = hook
446
- // register for functional component in vue file
447
- var originalRender = options.render
448
- options.render = function renderWithStyleInjection(h, context) {
449
- hook.call(context)
450
- return originalRender(h, context)
451
- }
452
- } else {
453
- // inject component registration as beforeCreate hook
454
- var existing = options.beforeCreate
455
- options.beforeCreate = existing ? [].concat(existing, hook) : [hook]
456
- }
457
- }
458
-
459
- return {
460
- exports: scriptExports,
461
- options: options
462
- }
463
- }
464
-
465
886
  // CONCATENATED MODULE: ./src/components/xt-scroll-arrow/index.vue
466
887
 
467
888
 
@@ -470,7 +891,7 @@ function normalizeComponent(
470
891
 
471
892
  /* normalize component */
472
893
 
473
- var component = normalizeComponent(
894
+ var xt_scroll_arrow_component = normalizeComponent(
474
895
  components_xt_scroll_arrowvue_type_script_lang_js,
475
896
  render,
476
897
  staticRenderFns,
@@ -481,7 +902,7 @@ var component = normalizeComponent(
481
902
 
482
903
  )
483
904
 
484
- /* harmony default export */ var xt_scroll_arrow = (component.exports);
905
+ /* harmony default export */ var xt_scroll_arrow = (xt_scroll_arrow_component.exports);
485
906
  // CONCATENATED MODULE: ./src/components/xt-scroll-arrow/index.js
486
907
 
487
908