@bagelink/vue 1.15.69 → 1.15.73
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/dist/components/ListItem.vue.d.ts +3 -4
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/Pagination.vue.d.ts +6 -2
- package/dist/components/Pagination.vue.d.ts.map +1 -1
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/index.cjs +22 -22
- package/dist/index.mjs +3799 -3773
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ListItem.vue +36 -6
- package/src/components/Pagination.vue +65 -41
- package/src/i18n/locales/en.json +2 -1
- package/src/i18n/locales/he.json +2 -1
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { IconType, ThemeType } from '@bagelink/vue'
|
|
3
3
|
import { Avatar, Icon } from '@bagelink/vue'
|
|
4
|
-
import { computed } from 'vue'
|
|
4
|
+
import { computed, useAttrs } from 'vue'
|
|
5
|
+
|
|
6
|
+
defineOptions({ inheritAttrs: false })
|
|
5
7
|
|
|
6
8
|
const props = withDefaults(
|
|
7
9
|
defineProps<{
|
|
@@ -31,9 +33,8 @@ const props = withDefaults(
|
|
|
31
33
|
/** Visually mark as selected (for non-router selection lists). */
|
|
32
34
|
active?: boolean
|
|
33
35
|
/** Render as an interactive row (cursor + hover) without needing a handler.
|
|
34
|
-
Implied when `to`, `href`, or `
|
|
36
|
+
Implied when `to`, `href`, or a `@click` listener is set. */
|
|
35
37
|
clickable?: boolean
|
|
36
|
-
onClick?: () => void
|
|
37
38
|
}>(),
|
|
38
39
|
{
|
|
39
40
|
ellipsis: true,
|
|
@@ -41,9 +42,34 @@ const props = withDefaults(
|
|
|
41
42
|
}
|
|
42
43
|
)
|
|
43
44
|
|
|
45
|
+
const attrs = useAttrs()
|
|
46
|
+
|
|
47
|
+
// Split inherited attrs: event listeners (onClick, onClickStop, …) belong on the
|
|
48
|
+
// clickable element so handlers + modifiers like `.stop` fire on the actual
|
|
49
|
+
// button/link row; everything else (class, style, data-*, …) stays on the root
|
|
50
|
+
// wrapper to preserve existing behavior.
|
|
51
|
+
const listenerAttrs = computed(() => {
|
|
52
|
+
const out: Record<string, any> = {}
|
|
53
|
+
for (const k of Object.keys(attrs)) {
|
|
54
|
+
if (/^on[A-Z]/.test(k)) { out[k] = (attrs as any)[k] }
|
|
55
|
+
}
|
|
56
|
+
return out
|
|
57
|
+
})
|
|
58
|
+
const rootAttrs = computed(() => {
|
|
59
|
+
const out: Record<string, any> = {}
|
|
60
|
+
for (const k of Object.keys(attrs)) {
|
|
61
|
+
if (!/^on[A-Z]/.test(k)) { out[k] = (attrs as any)[k] }
|
|
62
|
+
}
|
|
63
|
+
return out
|
|
64
|
+
})
|
|
65
|
+
|
|
44
66
|
const hasTo = computed(() => props.to !== undefined && props.to !== '')
|
|
45
67
|
const hasHref = computed(() => props.href !== undefined && props.href !== '')
|
|
46
|
-
|
|
68
|
+
// A `@click` listener (with or without modifiers like `.stop`) shows up in
|
|
69
|
+
// $attrs as `onClick` / `onClickStop` / etc — detect any of them so a clickable
|
|
70
|
+
// row is recognized even when the handler uses modifiers.
|
|
71
|
+
const hasClickListener = computed(() => Object.keys(attrs).some(k => /^onClick/i.test(k)))
|
|
72
|
+
const isClickable = computed(() => hasTo.value || hasHref.value || hasClickListener.value || props.clickable === true)
|
|
47
73
|
|
|
48
74
|
const isComponent = computed(() => {
|
|
49
75
|
if (hasTo.value) { return 'router-link' }
|
|
@@ -53,7 +79,10 @@ const isComponent = computed(() => {
|
|
|
53
79
|
})
|
|
54
80
|
|
|
55
81
|
const bind = computed(() => {
|
|
56
|
-
|
|
82
|
+
// Spread the inherited event listeners (including `@click`/`@click.stop`) onto
|
|
83
|
+
// the clickable element itself, so the handler fires on the actual button/link
|
|
84
|
+
// row — not the outer wrapper — and modifiers like `.stop` keep working.
|
|
85
|
+
const obj: { [key: string]: any } = { ...listenerAttrs.value }
|
|
57
86
|
if (props.to !== undefined && props.to !== '') { obj.to = props.to }
|
|
58
87
|
else if (props.href !== undefined && props.href !== '') { obj.href = props.href }
|
|
59
88
|
if (props.target !== undefined && props.target !== undefined && ((props.to !== undefined && props.to !== '') || (props.href !== undefined && props.href !== ''))) { obj.target = props.target }
|
|
@@ -69,6 +98,7 @@ const bind = computed(() => {
|
|
|
69
98
|
|
|
70
99
|
<template>
|
|
71
100
|
<div
|
|
101
|
+
v-bind="rootAttrs"
|
|
72
102
|
class="flex space-between list-item-row"
|
|
73
103
|
:class="{ 'no-border-list': props.flat || rounded, 'list-item-flush': props.fullWidth, 'list-item-fullrow': isClickable, 'list-item-rounded': rounded, 'list-item-active': active }"
|
|
74
104
|
:style="color ? { '--bgl-list-item-accent': `var(--bgl-${color})` } : undefined"
|
|
@@ -86,7 +116,7 @@ const bind = computed(() => {
|
|
|
86
116
|
'py-05': props.thin,
|
|
87
117
|
'px-1': !props.fullWidth,
|
|
88
118
|
'px-0': props.fullWidth,
|
|
89
|
-
}"
|
|
119
|
+
}"
|
|
90
120
|
>
|
|
91
121
|
<!-- Leading visual INSIDE the clickable area. Use #media for custom art
|
|
92
122
|
(covers, thumbnails); `src`/`showAvatar` and `icon` are shortcuts. -->
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { Option } from '@bagelink/vue'
|
|
3
|
+
import { Btn, SelectInput, useI18n } from '@bagelink/vue'
|
|
3
4
|
import { computed, ref, watch, nextTick } from 'vue'
|
|
5
|
+
import { getOptionValue } from '../utils/options'
|
|
4
6
|
|
|
5
7
|
interface Range {
|
|
6
8
|
start: number
|
|
@@ -9,25 +11,38 @@ interface Range {
|
|
|
9
11
|
|
|
10
12
|
interface PaginationProps {
|
|
11
13
|
totalItems: number
|
|
12
|
-
perPage?: number
|
|
13
14
|
totalPages?: number
|
|
14
15
|
variant?: 'default' | 'simple'
|
|
15
16
|
rtl?: boolean
|
|
16
17
|
maxVisiblePages?: number
|
|
18
|
+
/** When set, renders a "per page" selector with these choices. */
|
|
19
|
+
perPageOptions?: number[]
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
const props = withDefaults(defineProps<PaginationProps>(), {
|
|
20
23
|
totalItems: 0,
|
|
21
|
-
perPage: 25,
|
|
22
24
|
totalPages: undefined,
|
|
23
25
|
variant: 'default',
|
|
24
26
|
rtl: false,
|
|
25
|
-
maxVisiblePages: 3
|
|
27
|
+
maxVisiblePages: 3,
|
|
28
|
+
perPageOptions: undefined,
|
|
26
29
|
})
|
|
27
30
|
|
|
31
|
+
const { $t } = useI18n()
|
|
32
|
+
|
|
28
33
|
const page = defineModel<number>('page', { default: 1 })
|
|
34
|
+
const perPage = defineModel<number>('perPage', { default: 25 })
|
|
29
35
|
const range = defineModel<Range>('range')
|
|
30
36
|
|
|
37
|
+
// Update how many items show per page, then jump back to the first page.
|
|
38
|
+
function onPerPageChange(opt: Option | Option[] | undefined) {
|
|
39
|
+
const v = Number(getOptionValue(Array.isArray(opt) ? opt[0] : opt))
|
|
40
|
+
if (!Number.isNaN(v) && v > 0) {
|
|
41
|
+
perPage.value = v
|
|
42
|
+
page.value = 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
const paginationContainer = ref<HTMLElement>()
|
|
32
47
|
const indicatorPosition = ref(0)
|
|
33
48
|
const indicatorWidth = ref(0)
|
|
@@ -35,18 +50,16 @@ const indicatorWidth = ref(0)
|
|
|
35
50
|
// Calculate totalPages from totalItems and perPage if not provided directly
|
|
36
51
|
const computedTotalPages = computed(() => {
|
|
37
52
|
if (props.totalPages !== undefined) { return props.totalPages }
|
|
38
|
-
|
|
39
|
-
return Math.max(1, Math.ceil(props.totalItems / perPage))
|
|
53
|
+
return Math.max(1, Math.ceil(props.totalItems / perPage.value))
|
|
40
54
|
})
|
|
41
55
|
|
|
42
56
|
watch(
|
|
43
|
-
[() => page.value, () =>
|
|
57
|
+
[() => page.value, () => perPage.value, () => props.totalItems],
|
|
44
58
|
() => {
|
|
45
59
|
if (range.value) {
|
|
46
|
-
const { perPage } = props
|
|
47
60
|
// Calculate zero-based indices
|
|
48
|
-
const start = (page.value - 1) * perPage
|
|
49
|
-
const end = Math.min(start + perPage - 1, props.totalItems - 1)
|
|
61
|
+
const start = (page.value - 1) * perPage.value
|
|
62
|
+
const end = Math.min(start + perPage.value - 1, props.totalItems - 1)
|
|
50
63
|
range.value = { start, end }
|
|
51
64
|
}
|
|
52
65
|
// Update indicator position when page changes
|
|
@@ -187,41 +200,52 @@ const renderPageButtons = computed(() => {
|
|
|
187
200
|
</script>
|
|
188
201
|
|
|
189
202
|
<template>
|
|
190
|
-
<div v-if="computedTotalPages > 1
|
|
191
|
-
<!--
|
|
192
|
-
<
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
:
|
|
196
|
-
|
|
197
|
-
width: `${indicatorWidth}px`,
|
|
198
|
-
}"
|
|
203
|
+
<div v-if="computedTotalPages > 1 || perPageOptions" class="flex gap-1 align-items-center justify-content-between">
|
|
204
|
+
<!-- Per-page selector -->
|
|
205
|
+
<label v-if="perPageOptions" class="flex gap-05 align-items-center txt14 nowrap">
|
|
206
|
+
<span class="color-gray">{{ $t('pagination.perPage') }}</span>
|
|
207
|
+
<SelectInput
|
|
208
|
+
thin border :options="perPageOptions" :model-value="perPage"
|
|
209
|
+
@update:model-value="onPerPageChange"
|
|
199
210
|
/>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
</label>
|
|
212
|
+
|
|
213
|
+
<div v-if="computedTotalPages > 1" ref="paginationContainer" class="relative flex gap-1 justify-content">
|
|
214
|
+
<!-- Default pagination with page numbers -->
|
|
215
|
+
<template v-if="variant !== 'simple'">
|
|
216
|
+
<div
|
|
217
|
+
class="indicator radius-1"
|
|
218
|
+
:style="{
|
|
219
|
+
[rtl ? 'right' : 'left']: `${indicatorPosition}px`,
|
|
220
|
+
width: `${indicatorWidth}px`,
|
|
221
|
+
}"
|
|
208
222
|
/>
|
|
223
|
+
<!-- Render the page buttons and ellipses in order -->
|
|
224
|
+
<template v-for="item in renderPageButtons" :key="item.key">
|
|
225
|
+
<!-- Page button -->
|
|
226
|
+
<Btn
|
|
227
|
+
v-if="item.type === 'page'" flat thin
|
|
228
|
+
:class="{ selected: item.number === page }"
|
|
229
|
+
:value="item.number ? item.number.toString() : ''"
|
|
230
|
+
@click="item.number ? handleClick(item.number) : null"
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<!-- Ellipsis -->
|
|
234
|
+
<div v-else-if="item.type === 'ellipsis'" class="pagination-ellipsis">
|
|
235
|
+
...
|
|
236
|
+
</div>
|
|
237
|
+
</template>
|
|
238
|
+
</template>
|
|
209
239
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
240
|
+
<!-- Simple pagination with prev/next buttons -->
|
|
241
|
+
<template v-else>
|
|
242
|
+
<Btn flat thin :disabled="page <= 1" icon="chevron_left" @click="prev" />
|
|
243
|
+
<span class="pagination-info">
|
|
244
|
+
{{ displayIndex(range?.start) }}-{{ displayIndex(range?.end) }} / {{ props.totalItems }}
|
|
245
|
+
</span>
|
|
246
|
+
<Btn flat thin :disabled="page >= computedTotalPages" icon="chevron_right" @click="next" />
|
|
214
247
|
</template>
|
|
215
|
-
</
|
|
216
|
-
|
|
217
|
-
<!-- Simple pagination with prev/next buttons -->
|
|
218
|
-
<template v-else>
|
|
219
|
-
<Btn flat thin :disabled="page <= 1" icon="chevron_left" @click="prev" />
|
|
220
|
-
<span class="pagination-info">
|
|
221
|
-
{{ displayIndex(range?.start) }}-{{ displayIndex(range?.end) }} / {{ props.totalItems }}
|
|
222
|
-
</span>
|
|
223
|
-
<Btn flat thin :disabled="page >= computedTotalPages" icon="chevron_right" @click="next" />
|
|
224
|
-
</template>
|
|
248
|
+
</div>
|
|
225
249
|
</div>
|
|
226
250
|
</template>
|
|
227
251
|
|
package/src/i18n/locales/en.json
CHANGED