@indielayer/ui 1.16.0 → 1.18.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.
- package/README.md +2 -2
- package/docs/assets/css/tailwind.css +6 -0
- package/docs/components/common/CodePreview.vue +14 -9
- package/docs/components/common/DocsFeatures.vue +41 -0
- package/docs/components/common/DocsHero.vue +216 -0
- package/docs/components/common/DocumentPage.vue +99 -112
- package/docs/components/common/ExampleBlocks.vue +157 -0
- package/docs/components/menu/DocsMenu.vue +3 -0
- package/docs/components/toolbar/Toolbar.vue +11 -2
- package/docs/components/toolbar/ToolbarColorToggle.vue +4 -4
- package/docs/components/toolbar/ToolbarSearch.vue +59 -62
- package/docs/composables/useDocMeta.ts +47 -0
- package/docs/icons.ts +28 -0
- package/docs/layouts/default.vue +1 -3
- package/docs/layouts/simple.vue +3 -1
- package/docs/main.ts +5 -0
- package/docs/pages/colors.vue +56 -47
- package/docs/pages/component/infiniteLoader/composable.vue +168 -0
- package/docs/pages/component/infiniteLoader/index.vue +36 -0
- package/docs/pages/component/infiniteLoader/usage.vue +161 -0
- package/docs/pages/component/select/size.vue +1 -1
- package/docs/pages/component/select/usage.vue +14 -7
- package/docs/pages/component/virtualGrid/index.vue +29 -0
- package/docs/pages/component/virtualGrid/usage.vue +20 -0
- package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
- package/docs/pages/component/virtualList/index.vue +36 -0
- package/docs/pages/component/virtualList/usage.vue +17 -0
- package/docs/pages/error.vue +5 -3
- package/docs/pages/icons.vue +64 -54
- package/docs/pages/index.vue +93 -82
- package/docs/pages/typography.vue +38 -28
- package/docs/router/index.ts +31 -3
- package/docs/search/components.json +1 -1
- package/docs/search/index.json +1 -0
- package/lib/components/container/theme/Container.base.theme.js +1 -1
- package/lib/components/divider/theme/Divider.base.theme.js +1 -1
- package/lib/components/input/Input.vue.js +23 -24
- package/lib/components/select/Select.vue.d.ts +16 -27
- package/lib/components/select/Select.vue.js +452 -345
- package/lib/components/table/Table.vue.js +1 -1
- package/lib/composables/useVirtualList.d.ts +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +88 -76
- package/lib/index.umd.js +4 -4
- package/lib/install.js +15 -7
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
- package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
- package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
- package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
- package/lib/virtual/components/virtualList/VirtualList.vue.js +159 -0
- package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
- package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
- package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
- package/lib/virtual/components/virtualList/types.d.ts +115 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.js +68 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
- package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
- package/lib/virtual/core/createCachedBounds.d.ts +6 -0
- package/lib/virtual/core/createCachedBounds.js +55 -0
- package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
- package/lib/virtual/core/getEstimatedSize.js +22 -0
- package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
- package/lib/virtual/core/getOffsetForIndex.js +40 -0
- package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
- package/lib/virtual/core/getStartStopIndices.js +31 -0
- package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
- package/lib/virtual/core/types.d.ts +11 -0
- package/lib/virtual/core/useCachedBounds.d.ts +7 -0
- package/lib/virtual/core/useCachedBounds.js +18 -0
- package/lib/virtual/core/useIsRtl.d.ts +2 -0
- package/lib/virtual/core/useIsRtl.js +15 -0
- package/lib/virtual/core/useItemSize.d.ts +5 -0
- package/lib/virtual/core/useItemSize.js +27 -0
- package/lib/virtual/core/useVirtualizer.d.ts +33 -0
- package/lib/virtual/core/useVirtualizer.js +171 -0
- package/lib/virtual/index.d.ts +9 -0
- package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
- package/lib/virtual/types.d.ts +2 -0
- package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
- package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
- package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
- package/lib/virtual/utils/assert.d.ts +1 -0
- package/lib/virtual/utils/assert.js +7 -0
- package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
- package/lib/virtual/utils/getRTLOffsetType.js +13 -0
- package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
- package/lib/virtual/utils/getScrollbarSize.js +11 -0
- package/lib/virtual/utils/isRtl.d.ts +1 -0
- package/lib/virtual/utils/isRtl.js +12 -0
- package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
- package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
- package/lib/virtual/utils/shallowCompare.d.ts +1 -0
- package/lib/virtual/utils/shallowCompare.js +14 -0
- package/package.json +8 -3
- package/src/components/container/theme/Container.base.theme.ts +1 -1
- package/src/components/divider/theme/Divider.base.theme.ts +1 -1
- package/src/components/input/Input.vue +1 -2
- package/src/components/select/Select.vue +97 -20
- package/src/components/table/Table.vue +1 -1
- package/src/composables/useVirtualList.ts +1 -1
- package/src/index.ts +1 -0
- package/src/install.ts +9 -3
- package/src/version.ts +1 -1
- package/src/virtual/README.md +285 -0
- package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
- package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
- package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
- package/src/virtual/components/virtualGrid/types.ts +160 -0
- package/src/virtual/components/virtualList/VirtualList.test.ts +164 -0
- package/src/virtual/components/virtualList/VirtualList.vue +227 -0
- package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
- package/src/virtual/components/virtualList/types.ts +127 -0
- package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +197 -0
- package/src/virtual/components/virtualList/useDynamicRowHeight.ts +149 -0
- package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
- package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
- package/src/virtual/composables/infinite-loader/types.ts +36 -0
- package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
- package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
- package/src/virtual/core/createCachedBounds.ts +72 -0
- package/src/virtual/core/getEstimatedSize.ts +29 -0
- package/src/virtual/core/getOffsetForIndex.ts +90 -0
- package/src/virtual/core/getStartStopIndices.test.ts +45 -0
- package/src/virtual/core/getStartStopIndices.ts +71 -0
- package/src/virtual/core/types.ts +17 -0
- package/src/virtual/core/useCachedBounds.ts +21 -0
- package/src/virtual/core/useIsRtl.ts +25 -0
- package/src/virtual/core/useItemSize.ts +34 -0
- package/src/virtual/core/useVirtualizer.ts +294 -0
- package/src/virtual/index.ts +25 -0
- package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
- package/src/virtual/types.ts +3 -0
- package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
- package/src/virtual/utils/areArraysEqual.ts +13 -0
- package/src/virtual/utils/assert.ts +10 -0
- package/src/virtual/utils/getRTLOffsetType.ts +51 -0
- package/src/virtual/utils/getScrollbarSize.ts +24 -0
- package/src/virtual/utils/isRtl.ts +13 -0
- package/src/virtual/utils/parseNumericStyleValue.ts +21 -0
- package/src/virtual/utils/shallowCompare.ts +29 -0
- package/volar.d.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indielayer/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Indielayer UI Components with Tailwind CSS build for Vue 3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "João Teixeira",
|
|
@@ -50,12 +50,13 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@indielayer/stylelint-config": "^1.0.0",
|
|
52
52
|
"@rushstack/eslint-patch": "^1.3.2",
|
|
53
|
+
"@unhead/vue": "^2.0.19",
|
|
53
54
|
"@tsconfig/node18": "^2.0.1",
|
|
54
55
|
"@types/jsdom": "^21.1.1",
|
|
55
56
|
"@types/node": "^18.16.18",
|
|
56
57
|
"@vitejs/plugin-vue": "^4.2.3",
|
|
57
58
|
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
|
58
|
-
"@vue/test-utils": "^2.4.
|
|
59
|
+
"@vue/test-utils": "^2.4.6",
|
|
59
60
|
"@vue/tsconfig": "^0.4.0",
|
|
60
61
|
"@vuepic/vue-datepicker": "^11.0.2",
|
|
61
62
|
"@vueuse/core": "^11.1.0",
|
|
@@ -107,9 +108,13 @@
|
|
|
107
108
|
"gen:types": "vue-tsc --declaration --emitDeclarationOnly -p tsconfig.vitest.json --composite false",
|
|
108
109
|
"gen:version": "node .scripts/gen-version.cjs",
|
|
109
110
|
"gen:search": "node .scripts/gen-search.cjs",
|
|
111
|
+
"gen:llms": "node .scripts/gen-llms.cjs",
|
|
112
|
+
"gen:sitemap": "node .scripts/gen-sitemap.cjs",
|
|
110
113
|
"test": "pnpm test:unit",
|
|
114
|
+
"test:ci": "vitest run --environment jsdom",
|
|
111
115
|
"test:unit": "vitest --environment jsdom",
|
|
112
116
|
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
|
|
113
|
-
"stylelint": "stylelint \"**/*.{css,vue,postcss,scss,sass}\" --ignore-path .gitignore"
|
|
117
|
+
"stylelint": "stylelint \"**/*.{css,vue,postcss,scss,sass}\" --ignore-path .gitignore",
|
|
118
|
+
"stylelint:fix": "stylelint \"**/*.{css,vue,postcss,scss,sass}\" --ignore-path .gitignore --fix"
|
|
114
119
|
}
|
|
115
120
|
}
|
|
@@ -4,7 +4,7 @@ const theme: DividerTheme = {
|
|
|
4
4
|
classes: {
|
|
5
5
|
wrapper: ({ props }) => `flex justify-center items-center ${props.vertical ? 'h-full flex-col' : 'w-full'}`,
|
|
6
6
|
|
|
7
|
-
label: 'font-medium text-
|
|
7
|
+
label: 'font-medium text-xs text-slate-400 dark:text-secondary-300',
|
|
8
8
|
|
|
9
9
|
line: 'bg-secondary-200 dark:bg-secondary-700 flex-grow',
|
|
10
10
|
},
|
|
@@ -152,14 +152,13 @@ defineExpose({ focus, blur, reset, validate, setError })
|
|
|
152
152
|
ref="elRef"
|
|
153
153
|
:class="[
|
|
154
154
|
classes.input,
|
|
155
|
-
type === 'password' ? 'pr-10' : '',
|
|
156
155
|
// error
|
|
157
156
|
errorInternal
|
|
158
157
|
? 'border-error-500 dark:border-error-400 focus:outline-error-500'
|
|
159
158
|
: 'focus:outline-[color:var(--x-input-border)]',
|
|
160
159
|
{
|
|
161
160
|
'!pl-10': iconLeft || icon,
|
|
162
|
-
'!pr-10': iconRight,
|
|
161
|
+
'!pr-10': iconRight || showPasswordToggle || showClearIcon,
|
|
163
162
|
},
|
|
164
163
|
]"
|
|
165
164
|
:disabled="disabled"
|
|
@@ -17,6 +17,9 @@ const selectProps = {
|
|
|
17
17
|
type: String,
|
|
18
18
|
default: 'Filter by...',
|
|
19
19
|
},
|
|
20
|
+
filterablePrefix: Boolean,
|
|
21
|
+
filterableSuffix: Boolean,
|
|
22
|
+
hideSelectedOptionSlots: Boolean,
|
|
20
23
|
virtualList: Boolean,
|
|
21
24
|
virtualListOffsetTop: Number,
|
|
22
25
|
virtualListOffsetBottom: Number,
|
|
@@ -26,7 +29,7 @@ const selectProps = {
|
|
|
26
29
|
},
|
|
27
30
|
virtualListOverscan: {
|
|
28
31
|
type: Number,
|
|
29
|
-
default:
|
|
32
|
+
default: 10,
|
|
30
33
|
},
|
|
31
34
|
placement: String as PropType<PopoverPlacement>,
|
|
32
35
|
}
|
|
@@ -78,7 +81,7 @@ import XInput from '../input/Input.vue'
|
|
|
78
81
|
|
|
79
82
|
const props = defineProps(selectProps)
|
|
80
83
|
|
|
81
|
-
const emit = defineEmits([...useInputtable.emits(), 'close'])
|
|
84
|
+
const emit = defineEmits([...useInputtable.emits(), 'close', 'open'])
|
|
82
85
|
|
|
83
86
|
const internalMultiple = computed(() => props.multiple || props.multipleCheckbox)
|
|
84
87
|
|
|
@@ -110,12 +113,35 @@ const selected = computed<any | any[]>({
|
|
|
110
113
|
},
|
|
111
114
|
})
|
|
112
115
|
|
|
113
|
-
const
|
|
114
|
-
if (!props.options) return new Map<
|
|
116
|
+
const optionsByValue = computed(() => {
|
|
117
|
+
if (!props.options) return new Map<string | number, SelectOption>()
|
|
115
118
|
|
|
116
|
-
return new Map(props.options.map((option) => [option, option
|
|
119
|
+
return new Map(props.options.map((option) => [option.value, option]))
|
|
117
120
|
})
|
|
118
121
|
|
|
122
|
+
const filterCache = computed(() => {
|
|
123
|
+
if (!props.options) return new Map<SelectOption, { label: string; prefix?: string; suffix?: string; }>()
|
|
124
|
+
|
|
125
|
+
return new Map(props.options.map((option) => [
|
|
126
|
+
option,
|
|
127
|
+
{
|
|
128
|
+
label: option.label.toLowerCase(),
|
|
129
|
+
prefix: props.filterablePrefix && option.prefix ? option.prefix.toLowerCase() : undefined,
|
|
130
|
+
suffix: props.filterableSuffix && option.suffix ? option.suffix.toLowerCase() : undefined,
|
|
131
|
+
},
|
|
132
|
+
]))
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
function matchesFilter(option: SelectOption, filterLower: string) {
|
|
136
|
+
const cached = filterCache.value.get(option)
|
|
137
|
+
|
|
138
|
+
if (!cached) return false
|
|
139
|
+
|
|
140
|
+
return cached.label.includes(filterLower)
|
|
141
|
+
|| cached.prefix?.includes(filterLower)
|
|
142
|
+
|| cached.suffix?.includes(filterLower)
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
const internalOptions = computed(() => {
|
|
120
146
|
if (!props.options || props.options.length === 0) return []
|
|
121
147
|
|
|
@@ -128,10 +154,9 @@ const internalOptions = computed(() => {
|
|
|
128
154
|
: [],
|
|
129
155
|
)
|
|
130
156
|
const singleSelectedValue = !internalMultiple.value ? selected.value : null
|
|
131
|
-
const cache = labelCache.value
|
|
132
157
|
|
|
133
158
|
return props.options
|
|
134
|
-
.filter((option) => !hasFilter ||
|
|
159
|
+
.filter((option) => !hasFilter || matchesFilter(option, filterLower))
|
|
135
160
|
.map((option) => {
|
|
136
161
|
const isActive = internalMultiple.value
|
|
137
162
|
? selectedSet.has(option.value)
|
|
@@ -189,6 +214,7 @@ watch(isOpen, (isOpenValue) => {
|
|
|
189
214
|
})
|
|
190
215
|
}, 50)
|
|
191
216
|
|
|
217
|
+
emit('open')
|
|
192
218
|
} else {
|
|
193
219
|
if (props.filterable) filter.value = ''
|
|
194
220
|
emit('close')
|
|
@@ -264,7 +290,7 @@ function findSelectableIndex(start: number | undefined, direction = 'down') {
|
|
|
264
290
|
}
|
|
265
291
|
|
|
266
292
|
function handleOptionClick(value: string | number) {
|
|
267
|
-
const option =
|
|
293
|
+
const option = getItem(value)
|
|
268
294
|
|
|
269
295
|
if (!option || option.disabled) return
|
|
270
296
|
|
|
@@ -325,8 +351,14 @@ function handleRemove(e: Event, value: string) {
|
|
|
325
351
|
}
|
|
326
352
|
}
|
|
327
353
|
|
|
354
|
+
function getItem(value: string | number | []) {
|
|
355
|
+
if (Array.isArray(value)) return undefined
|
|
356
|
+
|
|
357
|
+
return optionsByValue.value.get(value)
|
|
358
|
+
}
|
|
359
|
+
|
|
328
360
|
function getLabel(value: string | number | []) {
|
|
329
|
-
const option =
|
|
361
|
+
const option = getItem(value)
|
|
330
362
|
|
|
331
363
|
if (option) return option.label
|
|
332
364
|
|
|
@@ -541,15 +573,29 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
|
|
|
541
573
|
:key="value"
|
|
542
574
|
size="xs"
|
|
543
575
|
removable
|
|
544
|
-
:outlined="!(isDisabled ||
|
|
545
|
-
:disabled="isDisabled ||
|
|
576
|
+
:outlined="!(isDisabled || getItem(value)?.disabled)"
|
|
577
|
+
:disabled="isDisabled || getItem(value)?.disabled"
|
|
546
578
|
:style="{ 'max-width': valueIndex === 0 && hiddenTagsCounterRef ? `calc(100% - ${hiddenTagsCounterRef.offsetWidth + 6 + 'px'})` : undefined }"
|
|
547
579
|
@remove="(e: Event) => { handleRemove(e, value) }"
|
|
548
580
|
>
|
|
549
|
-
<template
|
|
550
|
-
<
|
|
581
|
+
<template v-if="!hideSelectedOptionSlots">
|
|
582
|
+
<div class="flex items-center">
|
|
583
|
+
<span v-if="$slots.prefix || getItem(value)?.prefix" class="mr-2 shrink-0">
|
|
584
|
+
<slot name="prefix" :item="getItem(value)">{{ getItem(value)?.prefix }}</slot>
|
|
585
|
+
</span>
|
|
586
|
+
|
|
587
|
+
<span class="flex-1 truncate">
|
|
588
|
+
{{ getLabel(value) }}
|
|
589
|
+
</span>
|
|
590
|
+
|
|
591
|
+
<span v-if="$slots.suffix || getItem(value)?.suffix" class="ml-1 shrink-0">
|
|
592
|
+
<slot name="suffix" :item="getItem(value)">{{ getItem(value)?.suffix }}</slot>
|
|
593
|
+
</span>
|
|
594
|
+
</div>
|
|
595
|
+
</template>
|
|
596
|
+
<template v-else>
|
|
597
|
+
{{ getLabel(value) }}
|
|
551
598
|
</template>
|
|
552
|
-
{{ getLabel(value) }}
|
|
553
599
|
</x-tag>
|
|
554
600
|
|
|
555
601
|
<div
|
|
@@ -561,7 +607,24 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
|
|
|
561
607
|
</div>
|
|
562
608
|
</template>
|
|
563
609
|
<template v-else-if="!internalMultiple && !isEmpty(selected) && getLabel(selected) !== ''">
|
|
564
|
-
|
|
610
|
+
<template v-if="!hideSelectedOptionSlots">
|
|
611
|
+
<div class="flex items-center">
|
|
612
|
+
<span v-if="$slots.prefix || getItem(selected)?.prefix" class="mr-2 shrink-0">
|
|
613
|
+
<slot name="prefix" :item="getItem(selected)">{{ getItem(selected)?.prefix }}</slot>
|
|
614
|
+
</span>
|
|
615
|
+
|
|
616
|
+
<span class="flex-1 truncate">
|
|
617
|
+
{{ getLabel(selected) }}
|
|
618
|
+
</span>
|
|
619
|
+
|
|
620
|
+
<span v-if="$slots.suffix || getItem(selected)?.suffix" class="ml-1 shrink-0">
|
|
621
|
+
<slot name="suffix" :item="getItem(selected)">{{ getItem(selected)?.suffix }}</slot>
|
|
622
|
+
</span>
|
|
623
|
+
</div>
|
|
624
|
+
</template>
|
|
625
|
+
<template v-else>
|
|
626
|
+
{{ getLabel(selected) }}
|
|
627
|
+
</template>
|
|
565
628
|
</template>
|
|
566
629
|
|
|
567
630
|
<template v-else>
|
|
@@ -635,14 +698,28 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
|
|
|
635
698
|
:key="value"
|
|
636
699
|
size="xs"
|
|
637
700
|
removable
|
|
638
|
-
:outlined="!(isDisabled ||
|
|
639
|
-
:disabled="isDisabled ||
|
|
701
|
+
:outlined="!(isDisabled || getItem(value)?.disabled)"
|
|
702
|
+
:disabled="isDisabled || getItem(value)?.disabled"
|
|
640
703
|
@remove="(e: Event) => { handleRemove(e, value) }"
|
|
641
704
|
>
|
|
642
|
-
<template
|
|
643
|
-
<
|
|
705
|
+
<template v-if="!hideSelectedOptionSlots">
|
|
706
|
+
<div class="flex items-center">
|
|
707
|
+
<span v-if="$slots.prefix || getItem(value)?.prefix" class="mr-2 shrink-0">
|
|
708
|
+
<slot name="prefix" :item="getItem(value)">{{ getItem(value)?.prefix }}</slot>
|
|
709
|
+
</span>
|
|
710
|
+
|
|
711
|
+
<span class="flex-1 truncate">
|
|
712
|
+
{{ getLabel(value) }}
|
|
713
|
+
</span>
|
|
714
|
+
|
|
715
|
+
<span v-if="$slots.suffix || getItem(value)?.suffix" class="ml-1 shrink-0">
|
|
716
|
+
<slot name="suffix" :item="getItem(value)">{{ getItem(value)?.suffix }}</slot>
|
|
717
|
+
</span>
|
|
718
|
+
</div>
|
|
719
|
+
</template>
|
|
720
|
+
<template v-else>
|
|
721
|
+
{{ getLabel(value) }}
|
|
644
722
|
</template>
|
|
645
|
-
{{ getLabel(value) }}
|
|
646
723
|
</x-tag>
|
|
647
724
|
</x-popover-container>
|
|
648
725
|
</template>
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { default as version } from './version'
|
|
|
3
3
|
export * from './components'
|
|
4
4
|
export * from './composables'
|
|
5
5
|
export * from './themes'
|
|
6
|
+
export * from './virtual'
|
|
6
7
|
export type { UITheme, ComponentThemes } from './theme'
|
|
7
8
|
|
|
8
9
|
export { default as createUI, type UIOptions } from './create'
|
package/src/install.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import * as components from './components'
|
|
2
|
+
import { XVirtualGrid, XVirtualList, XInfiniteLoader } from './virtual'
|
|
2
3
|
import create from './create'
|
|
3
4
|
|
|
4
5
|
export default create({
|
|
5
|
-
components:
|
|
6
|
-
(
|
|
7
|
-
|
|
6
|
+
components: [
|
|
7
|
+
...Object.keys(components).map(
|
|
8
|
+
(key) => components[key as keyof object],
|
|
9
|
+
),
|
|
10
|
+
XVirtualList,
|
|
11
|
+
XVirtualGrid,
|
|
12
|
+
XInfiniteLoader,
|
|
13
|
+
],
|
|
8
14
|
})
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.
|
|
1
|
+
export default '1.18.0'
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Indielayer - Virtual Vue
|
|
2
|
+
|
|
3
|
+
Vue 3 port of [react-window](https://github.com/bvaughn/react-window) - Efficient virtualized list and grid components using the Composition API.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
High-performance virtualized list and grid components for Vue 3 applications. It renders only the visible items in large datasets, dramatically improving performance when dealing with thousands of rows or cells.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### List Example
|
|
12
|
+
|
|
13
|
+
```vue
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { XVirtualList } from '@indielayer/ui'
|
|
16
|
+
|
|
17
|
+
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<XVirtualList
|
|
22
|
+
:row-count="items.length"
|
|
23
|
+
:row-height="50"
|
|
24
|
+
:style="{ height: '400px' }"
|
|
25
|
+
>
|
|
26
|
+
<template #row="{ index, style }">
|
|
27
|
+
<div :style="style" class="row">
|
|
28
|
+
{{ items[index] }}
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
</XVirtualList>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
.row {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
padding: 0 1rem;
|
|
39
|
+
border-bottom: 1px solid #eee;
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Grid Example
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { XVirtualGrid } from '@indielayer/ui'
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<XVirtualGrid
|
|
53
|
+
:row-count="1000"
|
|
54
|
+
:column-count="1000"
|
|
55
|
+
:row-height="50"
|
|
56
|
+
:column-width="100"
|
|
57
|
+
:style="{ height: '500px', width: '800px' }"
|
|
58
|
+
>
|
|
59
|
+
<template #cell="{ rowIndex, columnIndex, style }">
|
|
60
|
+
<div :style="style" class="cell">
|
|
61
|
+
Row {{ rowIndex }}, Col {{ columnIndex }}
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
</XVirtualGrid>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<style scoped>
|
|
68
|
+
.cell {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
border: 1px solid #ddd;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Features
|
|
78
|
+
|
|
79
|
+
- ⚡️ **Virtual scrolling** - Only renders visible items
|
|
80
|
+
- 📏 **Variable sizes** - Fixed, variable, or dynamic item sizes
|
|
81
|
+
- 📐 **Dynamic measurement** - Automatic height measurement with `useDynamicRowHeight`
|
|
82
|
+
- 🌐 **RTL support** - Right-to-left language support
|
|
83
|
+
- 🖥️ **SSR compatible** - Works with server-side rendering
|
|
84
|
+
- 📝 **TypeScript** - Full type definitions included
|
|
85
|
+
- 🎯 **Imperative API** - Programmatic scrolling via template refs
|
|
86
|
+
- ♿️ **ARIA support** - Accessibility attributes built-in
|
|
87
|
+
- 🔄 **Overscan** - Render extra items to reduce flicker
|
|
88
|
+
- ♾️ **Infinite loading** - Built-in support for infinite scroll/pagination
|
|
89
|
+
|
|
90
|
+
## Components & Composables
|
|
91
|
+
|
|
92
|
+
### List
|
|
93
|
+
|
|
94
|
+
Virtualized list component for rendering large datasets with many rows.
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { XVirtualList } from '@indielayer/ui'
|
|
99
|
+
|
|
100
|
+
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<template>
|
|
104
|
+
<XVirtualList
|
|
105
|
+
:row-count="items.length"
|
|
106
|
+
:row-height="50"
|
|
107
|
+
:style="{ height: '400px' }"
|
|
108
|
+
>
|
|
109
|
+
<template #row="{ index, style }">
|
|
110
|
+
<div :style="style">
|
|
111
|
+
{{ items[index] }}
|
|
112
|
+
</div>
|
|
113
|
+
</template>
|
|
114
|
+
</XVirtualList>
|
|
115
|
+
</template>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Grid
|
|
119
|
+
|
|
120
|
+
Virtualized grid component for rendering data with rows and columns.
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<script setup lang="ts">
|
|
124
|
+
import { XVirtualGrid } from '@indielayer/ui'
|
|
125
|
+
|
|
126
|
+
const rowCount = 1000
|
|
127
|
+
const columnCount = 1000
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<template>
|
|
131
|
+
<XVirtualGrid
|
|
132
|
+
:row-count="rowCount"
|
|
133
|
+
:column-count="columnCount"
|
|
134
|
+
:row-height="50"
|
|
135
|
+
:column-width="100"
|
|
136
|
+
:style="{ height: '400px', width: '600px' }"
|
|
137
|
+
>
|
|
138
|
+
<template #cell="{ rowIndex, columnIndex, style }">
|
|
139
|
+
<div :style="style">
|
|
140
|
+
Row {{ rowIndex }}, Col {{ columnIndex }}
|
|
141
|
+
</div>
|
|
142
|
+
</template>
|
|
143
|
+
</XVirtualGrid>
|
|
144
|
+
</template>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Infinite Loader
|
|
148
|
+
|
|
149
|
+
Utility for loading data on-demand as users scroll through lists.
|
|
150
|
+
|
|
151
|
+
**Using the composable (recommended):**
|
|
152
|
+
|
|
153
|
+
```vue
|
|
154
|
+
<script setup lang="ts">
|
|
155
|
+
import { ref } from 'vue'
|
|
156
|
+
import { XVirtualList, useInfiniteLoader } from '@indielayer/ui'
|
|
157
|
+
|
|
158
|
+
const items = ref<string[]>([])
|
|
159
|
+
const hasMoreData = ref(true)
|
|
160
|
+
|
|
161
|
+
const isRowLoaded = (index: number) => index < items.value.length
|
|
162
|
+
|
|
163
|
+
const loadMoreRows = async (startIndex: number, stopIndex: number) => {
|
|
164
|
+
// Simulate API call
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
166
|
+
|
|
167
|
+
for (let i = startIndex; i <= stopIndex; i++) {
|
|
168
|
+
items.value[i] = `Item ${i}`
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (items.value.length >= 1000) {
|
|
172
|
+
hasMoreData.value = false
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const { onRowsRendered } = useInfiniteLoader({
|
|
177
|
+
isRowLoaded,
|
|
178
|
+
loadMoreRows,
|
|
179
|
+
rowCount: hasMoreData.value ? items.value.length + 50 : items.value.length,
|
|
180
|
+
minimumBatchSize: 20,
|
|
181
|
+
threshold: 15,
|
|
182
|
+
})
|
|
183
|
+
</script>
|
|
184
|
+
|
|
185
|
+
<template>
|
|
186
|
+
<XVirtualList
|
|
187
|
+
:row-count="hasMoreData ? items.length + 50 : items.length"
|
|
188
|
+
:row-height="50"
|
|
189
|
+
:style="{ height: '400px' }"
|
|
190
|
+
:on-rows-rendered="onRowsRendered"
|
|
191
|
+
>
|
|
192
|
+
<template #row="{ index, style }">
|
|
193
|
+
<div :style="style">
|
|
194
|
+
{{ items[index] || 'Loading...' }}
|
|
195
|
+
</div>
|
|
196
|
+
</template>
|
|
197
|
+
</XVirtualList>
|
|
198
|
+
</template>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Using the component:**
|
|
202
|
+
|
|
203
|
+
```vue
|
|
204
|
+
<script setup lang="ts">
|
|
205
|
+
import { ref } from 'vue'
|
|
206
|
+
import { XVirtualList, XInfiniteLoader } from '@indielayer/ui'
|
|
207
|
+
|
|
208
|
+
const items = ref<string[]>([])
|
|
209
|
+
|
|
210
|
+
const isRowLoaded = (index: number) => index < items.value.length
|
|
211
|
+
|
|
212
|
+
const loadMoreRows = async (startIndex: number, stopIndex: number) => {
|
|
213
|
+
for (let i = startIndex; i <= stopIndex; i++) {
|
|
214
|
+
items.value[i] = `Item ${i}`
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<template>
|
|
220
|
+
<XInfiniteLoader
|
|
221
|
+
:is-row-loaded="isRowLoaded"
|
|
222
|
+
:load-more-rows="loadMoreRows"
|
|
223
|
+
:row-count="1000"
|
|
224
|
+
>
|
|
225
|
+
<template #default="{ onRowsRendered }">
|
|
226
|
+
<XVirtualList
|
|
227
|
+
:row-count="1000"
|
|
228
|
+
:row-height="50"
|
|
229
|
+
:style="{ height: '400px' }"
|
|
230
|
+
:on-rows-rendered="onRowsRendered"
|
|
231
|
+
>
|
|
232
|
+
<template #row="{ index, style }">
|
|
233
|
+
<div :style="style">
|
|
234
|
+
{{ items[index] || 'Loading...' }}
|
|
235
|
+
</div>
|
|
236
|
+
</template>
|
|
237
|
+
</XVirtualList>
|
|
238
|
+
</template>
|
|
239
|
+
</XInfiniteLoader>
|
|
240
|
+
</template>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## API
|
|
244
|
+
|
|
245
|
+
### XVirtualList Props
|
|
246
|
+
|
|
247
|
+
| Prop | Type | Required | Description |
|
|
248
|
+
|------|------|----------|-------------|
|
|
249
|
+
| `rowCount` | `number` | ✅ | Number of rows in the list |
|
|
250
|
+
| `rowHeight` | `number \| string \| function` | ✅ | Row height (px, %, or function) |
|
|
251
|
+
| `style` | `CSSProperties` | ❌ | Container styles (should include height) |
|
|
252
|
+
| `rowProps` | `object` | ❌ | Additional props passed to row slot |
|
|
253
|
+
| `overscanCount` | `number` | ❌ | Extra rows to render (default: 3) |
|
|
254
|
+
| `defaultHeight` | `number` | ❌ | Default height for SSR |
|
|
255
|
+
| `onRowsRendered` | `function` | ❌ | Callback when visible rows change |
|
|
256
|
+
| `onResize` | `function` | ❌ | Callback when container resizes |
|
|
257
|
+
|
|
258
|
+
### XVirtualGrid Props
|
|
259
|
+
|
|
260
|
+
| Prop | Type | Required | Description |
|
|
261
|
+
|------|------|----------|-------------|
|
|
262
|
+
| `rowCount` | `number` | ✅ | Number of rows |
|
|
263
|
+
| `columnCount` | `number` | ✅ | Number of columns |
|
|
264
|
+
| `rowHeight` | `number \| string \| function` | ✅ | Row height |
|
|
265
|
+
| `columnWidth` | `number \| string \| function` | ✅ | Column width |
|
|
266
|
+
| `style` | `CSSProperties` | ❌ | Container styles |
|
|
267
|
+
| `cellProps` | `object` | ❌ | Additional props passed to cell slot |
|
|
268
|
+
| `overscanCount` | `number` | ❌ | Extra rows/columns (default: 3) |
|
|
269
|
+
| `dir` | `'ltr' \| 'rtl' \| 'auto'` | ❌ | Text direction |
|
|
270
|
+
| `onCellsRendered` | `function` | ❌ | Callback when visible cells change |
|
|
271
|
+
| `onResize` | `function` | ❌ | Callback when container resizes |
|
|
272
|
+
|
|
273
|
+
### Infinite Loader Props
|
|
274
|
+
|
|
275
|
+
| Prop | Type | Required | Default | Description |
|
|
276
|
+
|------|------|----------|---------|-------------|
|
|
277
|
+
| `isRowLoaded` | `(index: number) => boolean` | ✅ | - | Function to check if a row is loaded |
|
|
278
|
+
| `loadMoreRows` | `(start: number, stop: number) => Promise<void>` | ✅ | - | Async function to load more rows |
|
|
279
|
+
| `rowCount` | `number` | ✅ | - | Total row count (can be estimated) |
|
|
280
|
+
| `minimumBatchSize` | `number` | ❌ | 10 | Min rows to load per request |
|
|
281
|
+
| `threshold` | `number` | ❌ | 15 | Pre-fetch distance in rows |
|
|
282
|
+
|
|
283
|
+
## Credits
|
|
284
|
+
|
|
285
|
+
This library is a Vue 3 port of [react-window](https://github.com/bvaughn/react-window) by Brian Vaughn. All credit for the original design and virtualization algorithms goes to the original author and contributors.
|