@indielayer/ui 1.16.0 → 1.17.0

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 (125) hide show
  1. package/docs/components/menu/DocsMenu.vue +3 -0
  2. package/docs/pages/component/infiniteLoader/composable.vue +168 -0
  3. package/docs/pages/component/infiniteLoader/index.vue +36 -0
  4. package/docs/pages/component/infiniteLoader/usage.vue +161 -0
  5. package/docs/pages/component/virtualGrid/index.vue +29 -0
  6. package/docs/pages/component/virtualGrid/usage.vue +20 -0
  7. package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
  8. package/docs/pages/component/virtualList/index.vue +36 -0
  9. package/docs/pages/component/virtualList/usage.vue +17 -0
  10. package/docs/search/components.json +1 -1
  11. package/lib/components/select/Select.vue.js +35 -35
  12. package/lib/components/table/Table.vue.js +1 -1
  13. package/lib/composables/useVirtualList.d.ts +1 -1
  14. package/lib/index.d.ts +1 -0
  15. package/lib/index.js +88 -76
  16. package/lib/index.umd.js +4 -4
  17. package/lib/install.js +15 -7
  18. package/lib/version.d.ts +1 -1
  19. package/lib/version.js +1 -1
  20. package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
  21. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
  22. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
  23. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
  24. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
  25. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
  26. package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
  27. package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
  28. package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
  29. package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
  30. package/lib/virtual/components/virtualList/VirtualList.vue.js +157 -0
  31. package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
  32. package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
  33. package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
  34. package/lib/virtual/components/virtualList/types.d.ts +115 -0
  35. package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
  36. package/lib/virtual/components/virtualList/useDynamicRowHeight.js +69 -0
  37. package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
  38. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
  39. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
  40. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
  41. package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
  42. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
  43. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
  44. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
  45. package/lib/virtual/core/createCachedBounds.d.ts +6 -0
  46. package/lib/virtual/core/createCachedBounds.js +55 -0
  47. package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
  48. package/lib/virtual/core/getEstimatedSize.js +22 -0
  49. package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
  50. package/lib/virtual/core/getOffsetForIndex.js +40 -0
  51. package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
  52. package/lib/virtual/core/getStartStopIndices.js +31 -0
  53. package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
  54. package/lib/virtual/core/types.d.ts +11 -0
  55. package/lib/virtual/core/useCachedBounds.d.ts +7 -0
  56. package/lib/virtual/core/useCachedBounds.js +18 -0
  57. package/lib/virtual/core/useIsRtl.d.ts +2 -0
  58. package/lib/virtual/core/useIsRtl.js +15 -0
  59. package/lib/virtual/core/useItemSize.d.ts +5 -0
  60. package/lib/virtual/core/useItemSize.js +27 -0
  61. package/lib/virtual/core/useVirtualizer.d.ts +33 -0
  62. package/lib/virtual/core/useVirtualizer.js +171 -0
  63. package/lib/virtual/index.d.ts +9 -0
  64. package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
  65. package/lib/virtual/types.d.ts +2 -0
  66. package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
  67. package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
  68. package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
  69. package/lib/virtual/utils/assert.d.ts +1 -0
  70. package/lib/virtual/utils/assert.js +7 -0
  71. package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
  72. package/lib/virtual/utils/getRTLOffsetType.js +13 -0
  73. package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
  74. package/lib/virtual/utils/getScrollbarSize.js +11 -0
  75. package/lib/virtual/utils/isRtl.d.ts +1 -0
  76. package/lib/virtual/utils/isRtl.js +12 -0
  77. package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
  78. package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
  79. package/lib/virtual/utils/shallowCompare.d.ts +1 -0
  80. package/lib/virtual/utils/shallowCompare.js +14 -0
  81. package/package.json +1 -1
  82. package/src/components/select/Select.vue +3 -2
  83. package/src/components/table/Table.vue +1 -1
  84. package/src/composables/useVirtualList.ts +1 -1
  85. package/src/index.ts +1 -0
  86. package/src/install.ts +9 -3
  87. package/src/version.ts +1 -1
  88. package/src/virtual/README.md +285 -0
  89. package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
  90. package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
  91. package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
  92. package/src/virtual/components/virtualGrid/types.ts +160 -0
  93. package/src/virtual/components/virtualList/VirtualList.test.ts +47 -0
  94. package/src/virtual/components/virtualList/VirtualList.vue +233 -0
  95. package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
  96. package/src/virtual/components/virtualList/types.ts +127 -0
  97. package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +183 -0
  98. package/src/virtual/components/virtualList/useDynamicRowHeight.ts +147 -0
  99. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
  100. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
  101. package/src/virtual/composables/infinite-loader/types.ts +36 -0
  102. package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
  103. package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
  104. package/src/virtual/core/createCachedBounds.ts +72 -0
  105. package/src/virtual/core/getEstimatedSize.ts +29 -0
  106. package/src/virtual/core/getOffsetForIndex.ts +90 -0
  107. package/src/virtual/core/getStartStopIndices.test.ts +45 -0
  108. package/src/virtual/core/getStartStopIndices.ts +71 -0
  109. package/src/virtual/core/types.ts +17 -0
  110. package/src/virtual/core/useCachedBounds.ts +21 -0
  111. package/src/virtual/core/useIsRtl.ts +25 -0
  112. package/src/virtual/core/useItemSize.ts +34 -0
  113. package/src/virtual/core/useVirtualizer.ts +294 -0
  114. package/src/virtual/index.ts +25 -0
  115. package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
  116. package/src/virtual/types.ts +3 -0
  117. package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
  118. package/src/virtual/utils/areArraysEqual.ts +13 -0
  119. package/src/virtual/utils/assert.ts +10 -0
  120. package/src/virtual/utils/getRTLOffsetType.ts +51 -0
  121. package/src/virtual/utils/getScrollbarSize.ts +24 -0
  122. package/src/virtual/utils/isRtl.ts +13 -0
  123. package/src/virtual/utils/parseNumericStyleValue.ts +19 -0
  124. package/src/virtual/utils/shallowCompare.ts +29 -0
  125. package/volar.d.ts +3 -0
@@ -0,0 +1,162 @@
1
+ import EventEmitter from 'node:events'
2
+
3
+ type GetDOMRect = (element: HTMLElement) => DOMRectReadOnly | undefined | void;
4
+
5
+ const emitter = new EventEmitter()
6
+
7
+ emitter.setMaxListeners(100)
8
+
9
+ const elementToDOMRect = new Map<HTMLElement, DOMRect>()
10
+
11
+ let defaultDomRect: DOMRectReadOnly = new DOMRect(0, 0, 0, 0)
12
+ let disabled: boolean = false
13
+ let getDOMRect: GetDOMRect | undefined = undefined
14
+
15
+ export function disableResizeObserverForCurrentTest() {
16
+ disabled = true
17
+ }
18
+
19
+ export function setDefaultElementSize({
20
+ height,
21
+ width,
22
+ }: {
23
+ height: number;
24
+ width: number;
25
+ }) {
26
+ defaultDomRect = new DOMRect(0, 0, width, height)
27
+
28
+ emitter.emit('change')
29
+ }
30
+
31
+ export function setElementSizeFunction(value: GetDOMRect) {
32
+ getDOMRect = value
33
+
34
+ emitter.emit('change')
35
+ }
36
+
37
+ export function setElementSize({
38
+ element,
39
+ height,
40
+ width,
41
+ }: {
42
+ element: HTMLElement;
43
+ height: number;
44
+ width: number;
45
+ }) {
46
+ elementToDOMRect.set(element, new DOMRect(0, 0, width, height))
47
+
48
+ emitter.emit('change', element)
49
+ }
50
+
51
+ export function simulateUnsupportedEnvironmentForTest() {
52
+ // @ts-expect-error Simulate API being unsupported
53
+ window.ResizeObserver = null
54
+ }
55
+
56
+ export function mockResizeObserver() {
57
+ disabled = false
58
+
59
+ const originalResizeObserver = window.ResizeObserver
60
+
61
+ window.ResizeObserver = MockResizeObserver
62
+
63
+ return function unmockResizeObserver() {
64
+ window.ResizeObserver = originalResizeObserver
65
+
66
+ defaultDomRect = new DOMRect(0, 0, 0, 0)
67
+ disabled = false
68
+ getDOMRect = undefined
69
+
70
+ elementToDOMRect.clear()
71
+ }
72
+ }
73
+
74
+ class MockResizeObserver implements ResizeObserver {
75
+ readonly #callback: ResizeObserverCallback
76
+ #disconnected: boolean = false
77
+ #elements: Set<HTMLElement> = new Set()
78
+
79
+ constructor(callback: ResizeObserverCallback) {
80
+ this.#callback = callback
81
+
82
+ emitter.addListener('change', this.#onChange)
83
+ }
84
+
85
+ observe(element: HTMLElement) {
86
+ if (this.#disconnected) {
87
+ return
88
+ }
89
+
90
+ this.#elements.add(element)
91
+ this.#notify([element])
92
+ }
93
+
94
+ unobserve(element: HTMLElement) {
95
+ this.#elements.delete(element)
96
+ }
97
+
98
+ disconnect() {
99
+ this.#disconnected = true
100
+ this.#elements.clear()
101
+
102
+ emitter.removeListener('change', this.#onChange)
103
+ }
104
+
105
+ #notify(elements: HTMLElement[]) {
106
+ if (disabled) {
107
+ return
108
+ }
109
+
110
+ const entries = elements.map((element) => {
111
+ const computedStyle = window.getComputedStyle(element)
112
+ const writingMode = computedStyle.writingMode
113
+
114
+ let contentRect: DOMRectReadOnly =
115
+ elementToDOMRect.get(element) ?? defaultDomRect
116
+
117
+ if (getDOMRect) {
118
+ const contentRectOverride = getDOMRect(element)
119
+
120
+ if (contentRectOverride) {
121
+ contentRect = contentRectOverride
122
+ }
123
+ }
124
+
125
+ let blockSize = 0
126
+ let inlineSize = 0
127
+
128
+ if (writingMode.includes('vertical')) {
129
+ blockSize = contentRect.width
130
+ inlineSize = contentRect.height
131
+ } else {
132
+ blockSize = contentRect.height
133
+ inlineSize = contentRect.width
134
+ }
135
+
136
+ return {
137
+ borderBoxSize: [
138
+ {
139
+ blockSize,
140
+ inlineSize,
141
+ },
142
+ ],
143
+ contentBoxSize: [],
144
+ contentRect,
145
+ devicePixelContentBoxSize: [],
146
+ target: element,
147
+ }
148
+ })
149
+
150
+ this.#callback(entries, this)
151
+ }
152
+
153
+ #onChange = (target?: HTMLElement) => {
154
+ if (target) {
155
+ if (this.#elements.has(target)) {
156
+ this.#notify([target])
157
+ }
158
+ } else {
159
+ this.#notify(Array.from(this.#elements))
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,3 @@
1
+ export type Align = 'auto' | 'center' | 'end' | 'smart' | 'start';
2
+
3
+ export type TagNames = keyof HTMLElementTagNameMap;
@@ -0,0 +1,37 @@
1
+ import type { Direction } from '../core/types'
2
+ import { getRTLOffsetType } from './getRTLOffsetType'
3
+
4
+ export function adjustScrollOffsetForRtl({
5
+ containerElement,
6
+ direction,
7
+ isRtl,
8
+ scrollOffset,
9
+ }: {
10
+ containerElement: HTMLElement | null;
11
+ direction: Direction;
12
+ isRtl: boolean;
13
+ scrollOffset: number;
14
+ }) {
15
+ // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
16
+ // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
17
+ // So we need to determine which browser behavior we're dealing with, and mimic it.
18
+ if (direction === 'horizontal') {
19
+ if (isRtl) {
20
+ switch (getRTLOffsetType()) {
21
+ case 'negative': {
22
+ return -scrollOffset
23
+ }
24
+ case 'positive-descending': {
25
+ if (containerElement) {
26
+ const { clientWidth, scrollLeft, scrollWidth } = containerElement
27
+
28
+ return scrollWidth - clientWidth - scrollLeft
29
+ }
30
+ break
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ return scrollOffset
37
+ }
@@ -0,0 +1,13 @@
1
+ export function areArraysEqual(a: unknown[], b: unknown[]) {
2
+ if (a.length !== b.length) {
3
+ return false
4
+ }
5
+
6
+ for (let index = 0; index < a.length; index++) {
7
+ if (!Object.is(a[index], b[index])) {
8
+ return false
9
+ }
10
+ }
11
+
12
+ return true
13
+ }
@@ -0,0 +1,10 @@
1
+ export function assert(
2
+ expectedCondition: unknown,
3
+ message: string = 'Assertion error',
4
+ ): asserts expectedCondition {
5
+ if (!expectedCondition) {
6
+ console.error(message)
7
+
8
+ throw Error(message)
9
+ }
10
+ }
@@ -0,0 +1,51 @@
1
+ export type RTLOffsetType =
2
+ | 'negative'
3
+ | 'positive-descending'
4
+ | 'positive-ascending';
5
+
6
+ let cachedRTLResult: RTLOffsetType | null = null
7
+
8
+ // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
9
+ // Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
10
+ // Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
11
+ // The safest way to check this is to intentionally set a negative offset,
12
+ // and then verify that the subsequent "scroll" event matches the negative offset.
13
+ // If it does not match, then we can assume a non-standard RTL scroll implementation.
14
+ export function getRTLOffsetType(recalculate: boolean = false): RTLOffsetType {
15
+ if (cachedRTLResult === null || recalculate) {
16
+ const outerDiv = document.createElement('div')
17
+ const outerStyle = outerDiv.style
18
+
19
+ outerStyle.width = '50px'
20
+ outerStyle.height = '50px'
21
+ outerStyle.overflow = 'scroll'
22
+ outerStyle.direction = 'rtl'
23
+
24
+ const innerDiv = document.createElement('div')
25
+ const innerStyle = innerDiv.style
26
+
27
+ innerStyle.width = '100px'
28
+ innerStyle.height = '100px'
29
+
30
+ outerDiv.appendChild(innerDiv)
31
+
32
+ document.body.appendChild(outerDiv)
33
+
34
+ if (outerDiv.scrollLeft > 0) {
35
+ cachedRTLResult = 'positive-descending'
36
+ } else {
37
+ outerDiv.scrollLeft = 1
38
+ if (outerDiv.scrollLeft === 0) {
39
+ cachedRTLResult = 'negative'
40
+ } else {
41
+ cachedRTLResult = 'positive-ascending'
42
+ }
43
+ }
44
+
45
+ document.body.removeChild(outerDiv)
46
+
47
+ return cachedRTLResult
48
+ }
49
+
50
+ return cachedRTLResult
51
+ }
@@ -0,0 +1,24 @@
1
+ let size: number = -1
2
+
3
+ export function getScrollbarSize(recalculate: boolean = false): number {
4
+ if (size === -1 || recalculate) {
5
+ const div = document.createElement('div')
6
+ const style = div.style
7
+
8
+ style.width = '50px'
9
+ style.height = '50px'
10
+ style.overflow = 'scroll'
11
+
12
+ document.body.appendChild(div)
13
+
14
+ size = div.offsetWidth - div.clientWidth
15
+
16
+ document.body.removeChild(div)
17
+ }
18
+
19
+ return size
20
+ }
21
+
22
+ export function setScrollbarSizeForTests(value: number) {
23
+ size = value
24
+ }
@@ -0,0 +1,13 @@
1
+ export function isRtl(element: HTMLElement) {
2
+ let currentElement: HTMLElement | null = element
3
+
4
+ while (currentElement) {
5
+ if (currentElement.dir) {
6
+ return currentElement.dir === 'rtl'
7
+ }
8
+
9
+ currentElement = currentElement.parentElement
10
+ }
11
+
12
+ return false
13
+ }
@@ -0,0 +1,19 @@
1
+ import type { CSSProperties } from 'vue'
2
+
3
+ export function parseNumericStyleValue(
4
+ value: CSSProperties['height'],
5
+ ): number | undefined {
6
+ if (value !== undefined) {
7
+ switch (typeof value) {
8
+ case 'number': {
9
+ return value
10
+ }
11
+ case 'string': {
12
+ if (value.endsWith('px')) {
13
+ return parseFloat(value)
14
+ }
15
+ break
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,29 @@
1
+ import { assert } from './assert'
2
+
3
+ export function shallowCompare<Type extends object>(
4
+ a: Type | undefined,
5
+ b: Type | undefined,
6
+ ) {
7
+ if (a === b) {
8
+ return true
9
+ }
10
+
11
+ if (!!a !== !!b) {
12
+ return false
13
+ }
14
+
15
+ assert(a !== undefined)
16
+ assert(b !== undefined)
17
+
18
+ if (Object.keys(a).length !== Object.keys(b).length) {
19
+ return false
20
+ }
21
+
22
+ for (const key in a) {
23
+ if (!Object.is(b[key], a[key])) {
24
+ return false
25
+ }
26
+ }
27
+
28
+ return true
29
+ }
package/volar.d.ts CHANGED
@@ -20,6 +20,7 @@ declare module 'vue' {
20
20
  XFormGroup: typeof import('@indielayer/ui')['XFormGroup']
21
21
  XIcon: typeof import('@indielayer/ui')['XIcon']
22
22
  XImage: typeof import('@indielayer/ui')['XImage']
23
+ XInfiniteLoader: typeof import('@indielayer/ui')['XInfiniteLoader']
23
24
  XInput: typeof import('@indielayer/ui')['XInput']
24
25
  XInputFooter: typeof import('@indielayer/ui')['XInputFooter']
25
26
  XLabel: typeof import('@indielayer/ui')['XLabel']
@@ -58,6 +59,8 @@ declare module 'vue' {
58
59
  XToggleTip: typeof import('@indielayer/ui')['XToggleTip']
59
60
  XTooltip: typeof import('@indielayer/ui')['XTooltip']
60
61
  XUpload: typeof import('@indielayer/ui')['XUpload']
62
+ XVirtualGrid: typeof import('@indielayer/ui')['XVirtualGrid']
63
+ XVirtualList: typeof import('@indielayer/ui')['XVirtualList']
61
64
  }
62
65
  }
63
66