@dolanske/vui 0.3.4 → 0.5.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/LICENSE +673 -673
- package/README.md +41 -40
- package/dist/components/Dropdown/DropdownItem.vue.d.ts +1 -0
- package/dist/components/Flex/Flex.vue.d.ts +3 -1
- package/dist/components/Grid/Grid.vue.d.ts +3 -1
- package/dist/components/Tabs/Tabs.vue.d.ts +4 -0
- package/dist/style.css +1 -1
- package/dist/vui.js +1547 -1534
- package/package.json +68 -68
- package/src/App.vue +176 -175
- package/src/components/Accordion/Accordion.vue +91 -91
- package/src/components/Accordion/AccordionGroup.vue +43 -43
- package/src/components/Accordion/accordion.scss +81 -80
- package/src/components/Alert/Alert.vue +53 -53
- package/src/components/Alert/alert.scss +80 -80
- package/src/components/Avatar/Avatar.vue +50 -50
- package/src/components/Avatar/avatar.scss +52 -52
- package/src/components/Badge/Badge.vue +21 -21
- package/src/components/Badge/badge.scss +89 -89
- package/src/components/Breadcrumbs/BreadcrumbItem.vue +26 -26
- package/src/components/Breadcrumbs/Breadcrumbs.vue +33 -33
- package/src/components/Breadcrumbs/breadcrumbs.scss +30 -30
- package/src/components/Button/Button.vue +90 -90
- package/src/components/Button/button.scss +178 -176
- package/src/components/ButtonGroup/ButtonGroup.vue +25 -25
- package/src/components/ButtonGroup/button-group.scss +51 -51
- package/src/components/Calendar/Calendar.vue +63 -60
- package/src/components/Calendar/calendar.scss +60 -56
- package/src/components/Card/Card.vue +48 -48
- package/src/components/Card/card.scss +53 -53
- package/src/components/Checkbox/Checkbox.vue +51 -52
- package/src/components/Checkbox/checkbox.scss +75 -66
- package/src/components/CopyClipboard/CopyClipboard.vue +82 -82
- package/src/components/CopyClipboard/copy-clipboard.scss +17 -17
- package/src/components/Divider/Divider.vue +44 -44
- package/src/components/Divider/divider.scss +35 -35
- package/src/components/Drawer/Drawer.vue +97 -97
- package/src/components/Drawer/drawer.scss +36 -36
- package/src/components/Dropdown/Dropdown.vue +111 -111
- package/src/components/Dropdown/DropdownItem.vue +33 -29
- package/src/components/Dropdown/DropdownTitle.vue +8 -8
- package/src/components/Dropdown/dropdown-item.scss +77 -0
- package/src/components/Dropdown/dropdown.scss +39 -117
- package/src/components/Flex/Flex.vue +113 -106
- package/src/components/Grid/Grid.vue +60 -54
- package/src/components/Input/Counter.vue +70 -70
- package/src/components/Input/Dropzone.vue +65 -65
- package/src/components/Input/File.vue +15 -15
- package/src/components/Input/Input.vue +121 -121
- package/src/components/Input/Password.vue +47 -47
- package/src/components/Input/Textarea.vue +76 -76
- package/src/components/Input/input.scss +208 -208
- package/src/components/Kbd/Kbd.vue +48 -48
- package/src/components/Kbd/KbdGroup.vue +31 -31
- package/src/components/Kbd/kbd.scss +18 -18
- package/src/components/Modal/Confirm.vue +56 -56
- package/src/components/Modal/Modal.vue +91 -91
- package/src/components/Modal/modal.scss +49 -49
- package/src/components/OTP/OTP.vue +133 -133
- package/src/components/OTP/OTPItem.vue +37 -37
- package/src/components/OTP/otp.scss +83 -83
- package/src/components/Pagination/Pagination.vue +74 -74
- package/src/components/Pagination/pagination.ts +78 -78
- package/src/components/Popout/Popout.vue +42 -42
- package/src/components/Popout/popout.scss +8 -8
- package/src/components/Progress/Progress.vue +90 -90
- package/src/components/Progress/progress.scss +41 -41
- package/src/components/Radio/Radio.vue +36 -36
- package/src/components/Radio/RadioGroup.vue +40 -40
- package/src/components/Radio/radio.scss +68 -59
- package/src/components/Select/Select.vue +180 -180
- package/src/components/Select/select.scss +44 -44
- package/src/components/Sheet/Sheet.vue +92 -92
- package/src/components/Sheet/sheet.scss +60 -60
- package/src/components/Sidebar/Sidebar.vue +102 -0
- package/src/components/Sidebar/sidebar.scss +123 -0
- package/src/components/Skeleton/Skeleton.vue +43 -43
- package/src/components/Skeleton/skeleton.scss +14 -14
- package/src/components/Spinner/Spinner.vue +42 -42
- package/src/components/Spinner/spinner.scss +46 -46
- package/src/components/Switch/Switch.vue +30 -30
- package/src/components/Switch/switch.scss +60 -52
- package/src/components/Table/Cell.vue +23 -23
- package/src/components/Table/Header.vue +59 -59
- package/src/components/Table/Row.vue +9 -9
- package/src/components/Table/SelectAll.vue +23 -23
- package/src/components/Table/SelectRow.vue +29 -29
- package/src/components/Table/Table.vue +66 -66
- package/src/components/Table/table.scss +134 -134
- package/src/components/Table/table.ts +244 -244
- package/src/components/Tabs/Tab.vue +27 -27
- package/src/components/Tabs/Tabs.vue +89 -82
- package/src/components/Tabs/tabs.scss +80 -79
- package/src/components/Toast/Toasts.vue +47 -47
- package/src/components/Toast/toast.scss +41 -41
- package/src/components/Toast/toast.ts +68 -68
- package/src/components/Tooltip/Tooltip.vue +86 -86
- package/src/components/Tooltip/tooltip.scss +4 -4
- package/src/index.scss +1 -1
- package/src/index.ts +119 -119
- package/src/internal/Backdrop/Backdrop.vue +22 -22
- package/src/internal/Backdrop/backdrop.scss +28 -28
- package/src/main.ts +5 -5
- package/src/shared/helpers.ts +74 -74
- package/src/shared/types.ts +29 -29
- package/src/style/animation.scss +21 -21
- package/src/style/core.scss +150 -148
- package/src/style/layout.scss +168 -136
- package/src/style/media-query.scss +29 -29
- package/src/style/reset.scss +135 -135
- package/src/style/{fonts.scss → text.scss} +74 -53
- package/src/style/tooltip.scss +128 -128
- package/src/style/typography.scss +338 -338
- package/src/style/utils.scss +36 -36
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
<script setup lang='ts'>
|
|
2
|
-
import type { OtpContext } from './OTP.vue'
|
|
3
|
-
import { Icon } from '@iconify/vue'
|
|
4
|
-
import { inject } from 'vue'
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
i: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const props = defineProps<Props>()
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
otpValue,
|
|
14
|
-
cursorIndex,
|
|
15
|
-
redacted,
|
|
16
|
-
register,
|
|
17
|
-
} = inject('otp-context') as OtpContext
|
|
18
|
-
|
|
19
|
-
register()
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<template>
|
|
23
|
-
<div
|
|
24
|
-
class="vui-otp-item" :class="{
|
|
25
|
-
'active': props.i === cursorIndex,
|
|
26
|
-
'has-value': otpValue.trim().at(props.i),
|
|
27
|
-
}"
|
|
28
|
-
>
|
|
29
|
-
<div class="blinker" />
|
|
30
|
-
<template v-if="otpValue.trim().at(props.i)">
|
|
31
|
-
<Icon v-if="redacted" icon="ph:asterisk" />
|
|
32
|
-
<template v-else>
|
|
33
|
-
{{ otpValue.at(props.i) }}
|
|
34
|
-
</template>
|
|
35
|
-
</template>
|
|
36
|
-
</div>
|
|
37
|
-
</template>
|
|
1
|
+
<script setup lang='ts'>
|
|
2
|
+
import type { OtpContext } from './OTP.vue'
|
|
3
|
+
import { Icon } from '@iconify/vue'
|
|
4
|
+
import { inject } from 'vue'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
i: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const props = defineProps<Props>()
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
otpValue,
|
|
14
|
+
cursorIndex,
|
|
15
|
+
redacted,
|
|
16
|
+
register,
|
|
17
|
+
} = inject('otp-context') as OtpContext
|
|
18
|
+
|
|
19
|
+
register()
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div
|
|
24
|
+
class="vui-otp-item" :class="{
|
|
25
|
+
'active': props.i === cursorIndex,
|
|
26
|
+
'has-value': otpValue.trim().at(props.i),
|
|
27
|
+
}"
|
|
28
|
+
>
|
|
29
|
+
<div class="blinker" />
|
|
30
|
+
<template v-if="otpValue.trim().at(props.i)">
|
|
31
|
+
<Icon v-if="redacted" icon="ph:asterisk" />
|
|
32
|
+
<template v-else>
|
|
33
|
+
{{ otpValue.at(props.i) }}
|
|
34
|
+
</template>
|
|
35
|
+
</template>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
.vui-otp {
|
|
2
|
-
display: inline-block;
|
|
3
|
-
position: relative;
|
|
4
|
-
|
|
5
|
-
.vui-otp-items {
|
|
6
|
-
display: inline-flex;
|
|
7
|
-
gap: 0;
|
|
8
|
-
|
|
9
|
-
.vui-otp-item {
|
|
10
|
-
display: flex;
|
|
11
|
-
align-items: center;
|
|
12
|
-
justify-content: center;
|
|
13
|
-
width: 42px;
|
|
14
|
-
height: 42px;
|
|
15
|
-
border: 1px solid var(--color-border-strong);
|
|
16
|
-
color: var(--color-text);
|
|
17
|
-
z-index: 1;
|
|
18
|
-
font-size: var(--font-size-m);
|
|
19
|
-
outline: 0 solid var(--color-text-light);
|
|
20
|
-
transition: var(--transition);
|
|
21
|
-
|
|
22
|
-
.blinker {
|
|
23
|
-
display: none;
|
|
24
|
-
height: 16px;
|
|
25
|
-
width: 1px;
|
|
26
|
-
background-color: var(--color-text);
|
|
27
|
-
animation: blink 1.25s ease-out infinite;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@keyframes blink {
|
|
31
|
-
0%,
|
|
32
|
-
70%,
|
|
33
|
-
100% {
|
|
34
|
-
opacity: 1;
|
|
35
|
-
}
|
|
36
|
-
20%,
|
|
37
|
-
50% {
|
|
38
|
-
opacity: 0;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
&.has-value {
|
|
43
|
-
background-color: var(--color-bg-raised);
|
|
44
|
-
|
|
45
|
-
.blinker {
|
|
46
|
-
display: none !important;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
&.active {
|
|
51
|
-
z-index: 2;
|
|
52
|
-
outline-width: 2px;
|
|
53
|
-
|
|
54
|
-
.blinker {
|
|
55
|
-
display: block;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
&:not(:first-child) {
|
|
60
|
-
margin-left: -1px;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
&:first-child {
|
|
64
|
-
border-top-left-radius: var(--border-radius-m);
|
|
65
|
-
border-bottom-left-radius: var(--border-radius-m);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
&:last-child {
|
|
69
|
-
border-top-right-radius: var(--border-radius-m);
|
|
70
|
-
border-bottom-right-radius: var(--border-radius-m);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.vui-otp-input {
|
|
76
|
-
position: absolute;
|
|
77
|
-
inset: 0;
|
|
78
|
-
outline-width: 0px;
|
|
79
|
-
opacity: 0;
|
|
80
|
-
background: transparent;
|
|
81
|
-
z-index: 5;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
.vui-otp {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
position: relative;
|
|
4
|
+
|
|
5
|
+
.vui-otp-items {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
gap: 0;
|
|
8
|
+
|
|
9
|
+
.vui-otp-item {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
width: 42px;
|
|
14
|
+
height: 42px;
|
|
15
|
+
border: 1px solid var(--color-border-strong);
|
|
16
|
+
color: var(--color-text);
|
|
17
|
+
z-index: 1;
|
|
18
|
+
font-size: var(--font-size-m);
|
|
19
|
+
outline: 0 solid var(--color-text-light);
|
|
20
|
+
transition: var(--transition);
|
|
21
|
+
|
|
22
|
+
.blinker {
|
|
23
|
+
display: none;
|
|
24
|
+
height: 16px;
|
|
25
|
+
width: 1px;
|
|
26
|
+
background-color: var(--color-text);
|
|
27
|
+
animation: blink 1.25s ease-out infinite;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@keyframes blink {
|
|
31
|
+
0%,
|
|
32
|
+
70%,
|
|
33
|
+
100% {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
}
|
|
36
|
+
20%,
|
|
37
|
+
50% {
|
|
38
|
+
opacity: 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&.has-value {
|
|
43
|
+
background-color: var(--color-bg-raised);
|
|
44
|
+
|
|
45
|
+
.blinker {
|
|
46
|
+
display: none !important;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.active {
|
|
51
|
+
z-index: 2;
|
|
52
|
+
outline-width: 2px;
|
|
53
|
+
|
|
54
|
+
.blinker {
|
|
55
|
+
display: block;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&:not(:first-child) {
|
|
60
|
+
margin-left: -1px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:first-child {
|
|
64
|
+
border-top-left-radius: var(--border-radius-m);
|
|
65
|
+
border-bottom-left-radius: var(--border-radius-m);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&:last-child {
|
|
69
|
+
border-top-right-radius: var(--border-radius-m);
|
|
70
|
+
border-bottom-right-radius: var(--border-radius-m);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.vui-otp-input {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
outline-width: 0px;
|
|
79
|
+
opacity: 0;
|
|
80
|
+
background: transparent;
|
|
81
|
+
z-index: 5;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
<script setup lang='ts'>
|
|
2
|
-
import type { Pagination } from './pagination'
|
|
3
|
-
import { computed } from 'vue'
|
|
4
|
-
import Button from '../Button/Button.vue'
|
|
5
|
-
import Flex from '../Flex/Flex.vue'
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
numbers?: boolean
|
|
9
|
-
pagination: Pagination
|
|
10
|
-
prevNext?: boolean
|
|
11
|
-
firstLast?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
-
numbers: true,
|
|
16
|
-
prevNext: true,
|
|
17
|
-
firstLast: true,
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const emit = defineEmits<{
|
|
21
|
-
change: [page: number]
|
|
22
|
-
}>()
|
|
23
|
-
|
|
24
|
-
const canNextPage = computed(() => props.pagination.currentPage < props.pagination.endPage)
|
|
25
|
-
const canPrevPage = computed(() => props.pagination.currentPage > props.pagination.startPage)
|
|
26
|
-
|
|
27
|
-
function setNext() {
|
|
28
|
-
emit('change', props.pagination.currentPage + 1)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function setPrev() {
|
|
32
|
-
emit('change', props.pagination.currentPage - 1)
|
|
33
|
-
}
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<template>
|
|
37
|
-
<Flex inline class="vui-pagination" gap="s">
|
|
38
|
-
<slot name="start">
|
|
39
|
-
<Button v-if="props.firstLast" data-title-top="First page" plain :disabled="props.pagination.startPage === props.pagination.currentPage" square icon="ph:caret-double-left" @click="emit('change', props.pagination.startPage)" />
|
|
40
|
-
</slot>
|
|
41
|
-
|
|
42
|
-
<slot name="prev" :disabled="canPrevPage" :set-page="setPrev">
|
|
43
|
-
<Button v-if="props.prevNext" data-title-top="Previous page" plain :disabled="!canPrevPage" square icon="ph:caret-left" @click="setPrev" />
|
|
44
|
-
</slot>
|
|
45
|
-
|
|
46
|
-
<template v-if="props.numbers">
|
|
47
|
-
<Flex gap="s">
|
|
48
|
-
<Button
|
|
49
|
-
v-for="page in props.pagination.pages"
|
|
50
|
-
:key="page"
|
|
51
|
-
square
|
|
52
|
-
:plain="props.pagination.currentPage !== page"
|
|
53
|
-
:variant="props.pagination.currentPage === page ? 'blue' : 'default'"
|
|
54
|
-
@click="emit('change', page)"
|
|
55
|
-
>
|
|
56
|
-
{{ page }}
|
|
57
|
-
</Button>
|
|
58
|
-
</Flex>
|
|
59
|
-
</template>
|
|
60
|
-
<slot name="next" :disabled="canNextPage" :set-page="setNext">
|
|
61
|
-
<Button v-if="props.prevNext" data-title-top="Next page" plain :disabled="!canNextPage" square icon="ph:caret-right" @click="setNext" />
|
|
62
|
-
</slot>
|
|
63
|
-
|
|
64
|
-
<slot name="end">
|
|
65
|
-
<Button v-if="props.firstLast" data-title-top="Last page" plain :disabled="props.pagination.endPage === props.pagination.currentPage" square icon="ph:caret-double-right" @click="emit('change', props.pagination.endPage)" />
|
|
66
|
-
</slot>
|
|
67
|
-
</Flex>
|
|
68
|
-
</template>
|
|
69
|
-
|
|
70
|
-
<style scoped>
|
|
71
|
-
[data-title-top]:before {
|
|
72
|
-
white-space: nowrap;
|
|
73
|
-
}
|
|
74
|
-
</style>
|
|
1
|
+
<script setup lang='ts'>
|
|
2
|
+
import type { Pagination } from './pagination'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import Button from '../Button/Button.vue'
|
|
5
|
+
import Flex from '../Flex/Flex.vue'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
numbers?: boolean
|
|
9
|
+
pagination: Pagination
|
|
10
|
+
prevNext?: boolean
|
|
11
|
+
firstLast?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
numbers: true,
|
|
16
|
+
prevNext: true,
|
|
17
|
+
firstLast: true,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
change: [page: number]
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
const canNextPage = computed(() => props.pagination.currentPage < props.pagination.endPage)
|
|
25
|
+
const canPrevPage = computed(() => props.pagination.currentPage > props.pagination.startPage)
|
|
26
|
+
|
|
27
|
+
function setNext() {
|
|
28
|
+
emit('change', props.pagination.currentPage + 1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function setPrev() {
|
|
32
|
+
emit('change', props.pagination.currentPage - 1)
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<Flex inline class="vui-pagination" gap="s">
|
|
38
|
+
<slot name="start">
|
|
39
|
+
<Button v-if="props.firstLast" data-title-top="First page" plain :disabled="props.pagination.startPage === props.pagination.currentPage" square icon="ph:caret-double-left" @click="emit('change', props.pagination.startPage)" />
|
|
40
|
+
</slot>
|
|
41
|
+
|
|
42
|
+
<slot name="prev" :disabled="canPrevPage" :set-page="setPrev">
|
|
43
|
+
<Button v-if="props.prevNext" data-title-top="Previous page" plain :disabled="!canPrevPage" square icon="ph:caret-left" @click="setPrev" />
|
|
44
|
+
</slot>
|
|
45
|
+
|
|
46
|
+
<template v-if="props.numbers">
|
|
47
|
+
<Flex gap="s">
|
|
48
|
+
<Button
|
|
49
|
+
v-for="page in props.pagination.pages"
|
|
50
|
+
:key="page"
|
|
51
|
+
square
|
|
52
|
+
:plain="props.pagination.currentPage !== page"
|
|
53
|
+
:variant="props.pagination.currentPage === page ? 'blue' : 'default'"
|
|
54
|
+
@click="emit('change', page)"
|
|
55
|
+
>
|
|
56
|
+
{{ page }}
|
|
57
|
+
</Button>
|
|
58
|
+
</Flex>
|
|
59
|
+
</template>
|
|
60
|
+
<slot name="next" :disabled="canNextPage" :set-page="setNext">
|
|
61
|
+
<Button v-if="props.prevNext" data-title-top="Next page" plain :disabled="!canNextPage" square icon="ph:caret-right" @click="setNext" />
|
|
62
|
+
</slot>
|
|
63
|
+
|
|
64
|
+
<slot name="end">
|
|
65
|
+
<Button v-if="props.firstLast" data-title-top="Last page" plain :disabled="props.pagination.endPage === props.pagination.currentPage" square icon="ph:caret-double-right" @click="emit('change', props.pagination.endPage)" />
|
|
66
|
+
</slot>
|
|
67
|
+
</Flex>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
[data-title-top]:before {
|
|
72
|
+
white-space: nowrap;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { createArray } from '../../shared/helpers'
|
|
2
|
-
|
|
3
|
-
export interface Pagination {
|
|
4
|
-
totalItems: number
|
|
5
|
-
currentPage: number
|
|
6
|
-
perPage: number
|
|
7
|
-
totalPages: number
|
|
8
|
-
startPage: number
|
|
9
|
-
endPage: number
|
|
10
|
-
startIndex: number
|
|
11
|
-
endIndex: number
|
|
12
|
-
pages: number[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function paginate(
|
|
16
|
-
totalItems: number,
|
|
17
|
-
currentPage: number = 1,
|
|
18
|
-
perPage: number = 15,
|
|
19
|
-
maxPages: number = 5,
|
|
20
|
-
): Pagination {
|
|
21
|
-
// calculate total pages
|
|
22
|
-
const totalPages = Math.ceil(totalItems / perPage)
|
|
23
|
-
|
|
24
|
-
// ensure current page isn't out of range
|
|
25
|
-
if (currentPage < 1) {
|
|
26
|
-
currentPage = 1
|
|
27
|
-
}
|
|
28
|
-
else if (currentPage > totalPages) {
|
|
29
|
-
currentPage = totalPages
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let startPage: number, endPage: number
|
|
33
|
-
if (totalPages <= maxPages) {
|
|
34
|
-
// total pages less than max so show all pages
|
|
35
|
-
startPage = 1
|
|
36
|
-
endPage = totalPages
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
// total pages more than max so calculate start and end pages
|
|
40
|
-
const maxPagesBeforeCurrentPage = Math.floor(maxPages / 2)
|
|
41
|
-
const maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1
|
|
42
|
-
if (currentPage <= maxPagesBeforeCurrentPage) {
|
|
43
|
-
// current page near the start
|
|
44
|
-
startPage = 1
|
|
45
|
-
endPage = maxPages
|
|
46
|
-
}
|
|
47
|
-
else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
|
|
48
|
-
// current page near the end
|
|
49
|
-
startPage = totalPages - maxPages + 1
|
|
50
|
-
endPage = totalPages
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// current page somewhere in the middle
|
|
54
|
-
startPage = currentPage - maxPagesBeforeCurrentPage
|
|
55
|
-
endPage = currentPage + maxPagesAfterCurrentPage
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// calculate start and end item indexes
|
|
60
|
-
const startIndex = (currentPage - 1) * perPage
|
|
61
|
-
const endIndex = Math.min(startIndex + perPage - 1, totalItems - 1)
|
|
62
|
-
|
|
63
|
-
// create an array of pages to ng-repeat in the pager control
|
|
64
|
-
const pages = createArray((endPage + 1) - startPage).map(i => startPage + i)
|
|
65
|
-
|
|
66
|
-
// return object with all pager properties required by the view
|
|
67
|
-
return {
|
|
68
|
-
totalItems,
|
|
69
|
-
currentPage,
|
|
70
|
-
perPage,
|
|
71
|
-
totalPages,
|
|
72
|
-
startPage,
|
|
73
|
-
endPage,
|
|
74
|
-
startIndex,
|
|
75
|
-
endIndex,
|
|
76
|
-
pages,
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import { createArray } from '../../shared/helpers'
|
|
2
|
+
|
|
3
|
+
export interface Pagination {
|
|
4
|
+
totalItems: number
|
|
5
|
+
currentPage: number
|
|
6
|
+
perPage: number
|
|
7
|
+
totalPages: number
|
|
8
|
+
startPage: number
|
|
9
|
+
endPage: number
|
|
10
|
+
startIndex: number
|
|
11
|
+
endIndex: number
|
|
12
|
+
pages: number[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function paginate(
|
|
16
|
+
totalItems: number,
|
|
17
|
+
currentPage: number = 1,
|
|
18
|
+
perPage: number = 15,
|
|
19
|
+
maxPages: number = 5,
|
|
20
|
+
): Pagination {
|
|
21
|
+
// calculate total pages
|
|
22
|
+
const totalPages = Math.ceil(totalItems / perPage)
|
|
23
|
+
|
|
24
|
+
// ensure current page isn't out of range
|
|
25
|
+
if (currentPage < 1) {
|
|
26
|
+
currentPage = 1
|
|
27
|
+
}
|
|
28
|
+
else if (currentPage > totalPages) {
|
|
29
|
+
currentPage = totalPages
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let startPage: number, endPage: number
|
|
33
|
+
if (totalPages <= maxPages) {
|
|
34
|
+
// total pages less than max so show all pages
|
|
35
|
+
startPage = 1
|
|
36
|
+
endPage = totalPages
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// total pages more than max so calculate start and end pages
|
|
40
|
+
const maxPagesBeforeCurrentPage = Math.floor(maxPages / 2)
|
|
41
|
+
const maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1
|
|
42
|
+
if (currentPage <= maxPagesBeforeCurrentPage) {
|
|
43
|
+
// current page near the start
|
|
44
|
+
startPage = 1
|
|
45
|
+
endPage = maxPages
|
|
46
|
+
}
|
|
47
|
+
else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
|
|
48
|
+
// current page near the end
|
|
49
|
+
startPage = totalPages - maxPages + 1
|
|
50
|
+
endPage = totalPages
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// current page somewhere in the middle
|
|
54
|
+
startPage = currentPage - maxPagesBeforeCurrentPage
|
|
55
|
+
endPage = currentPage + maxPagesAfterCurrentPage
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// calculate start and end item indexes
|
|
60
|
+
const startIndex = (currentPage - 1) * perPage
|
|
61
|
+
const endIndex = Math.min(startIndex + perPage - 1, totalItems - 1)
|
|
62
|
+
|
|
63
|
+
// create an array of pages to ng-repeat in the pager control
|
|
64
|
+
const pages = createArray((endPage + 1) - startPage).map(i => startPage + i)
|
|
65
|
+
|
|
66
|
+
// return object with all pager properties required by the view
|
|
67
|
+
return {
|
|
68
|
+
totalItems,
|
|
69
|
+
currentPage,
|
|
70
|
+
perPage,
|
|
71
|
+
totalPages,
|
|
72
|
+
startPage,
|
|
73
|
+
endPage,
|
|
74
|
+
startIndex,
|
|
75
|
+
endIndex,
|
|
76
|
+
pages,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
<script setup lang='ts'>
|
|
2
|
-
import type { Placement, PopoutMaybeElement } from '../../shared/types'
|
|
3
|
-
import { autoPlacement, offset, shift, useFloating } from '@floating-ui/vue'
|
|
4
|
-
import { toRef, useTemplateRef } from 'vue'
|
|
5
|
-
import './popout.scss'
|
|
6
|
-
|
|
7
|
-
export interface Props {
|
|
8
|
-
anchor: PopoutMaybeElement<HTMLElement>
|
|
9
|
-
/**
|
|
10
|
-
* Override the autoPlacement option
|
|
11
|
-
*/
|
|
12
|
-
placement?: Placement
|
|
13
|
-
/**
|
|
14
|
-
* Distance between the anchor and the rendered tooltip
|
|
15
|
-
*/
|
|
16
|
-
offset?: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
20
|
-
offset: 8,
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const popoutRef = useTemplateRef('popout')
|
|
24
|
-
const anchorRef = toRef(props.anchor)
|
|
25
|
-
|
|
26
|
-
const { floatingStyles } = useFloating(anchorRef, popoutRef, {
|
|
27
|
-
placement: props.placement,
|
|
28
|
-
middleware: [
|
|
29
|
-
...(props.placement ? [] : [autoPlacement()]),
|
|
30
|
-
offset(props.offset),
|
|
31
|
-
shift({
|
|
32
|
-
padding: 8,
|
|
33
|
-
}),
|
|
34
|
-
],
|
|
35
|
-
})
|
|
36
|
-
</script>
|
|
37
|
-
|
|
38
|
-
<template>
|
|
39
|
-
<div ref="popout" :style="floatingStyles" class="vui-popout">
|
|
40
|
-
<slot />
|
|
41
|
-
</div>
|
|
42
|
-
</template>
|
|
1
|
+
<script setup lang='ts'>
|
|
2
|
+
import type { Placement, PopoutMaybeElement } from '../../shared/types'
|
|
3
|
+
import { autoPlacement, offset, shift, useFloating } from '@floating-ui/vue'
|
|
4
|
+
import { toRef, useTemplateRef } from 'vue'
|
|
5
|
+
import './popout.scss'
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
anchor: PopoutMaybeElement<HTMLElement>
|
|
9
|
+
/**
|
|
10
|
+
* Override the autoPlacement option
|
|
11
|
+
*/
|
|
12
|
+
placement?: Placement
|
|
13
|
+
/**
|
|
14
|
+
* Distance between the anchor and the rendered tooltip
|
|
15
|
+
*/
|
|
16
|
+
offset?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
20
|
+
offset: 8,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const popoutRef = useTemplateRef('popout')
|
|
24
|
+
const anchorRef = toRef(props.anchor)
|
|
25
|
+
|
|
26
|
+
const { floatingStyles } = useFloating(anchorRef, popoutRef, {
|
|
27
|
+
placement: props.placement,
|
|
28
|
+
middleware: [
|
|
29
|
+
...(props.placement ? [] : [autoPlacement()]),
|
|
30
|
+
offset(props.offset),
|
|
31
|
+
shift({
|
|
32
|
+
padding: 8,
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
})
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div ref="popout" :style="floatingStyles" class="vui-popout">
|
|
40
|
+
<slot />
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
.vui-popout {
|
|
2
|
-
border-radius: var(--border-radius);
|
|
3
|
-
box-shadow: var(--box-shadow);
|
|
4
|
-
min-width: 80px;
|
|
5
|
-
background-color: var(--color-bg-raised);
|
|
6
|
-
border-radius: var(--border-radius-m);
|
|
7
|
-
z-index: 1000;
|
|
8
|
-
}
|
|
1
|
+
.vui-popout {
|
|
2
|
+
border-radius: var(--border-radius);
|
|
3
|
+
box-shadow: var(--box-shadow);
|
|
4
|
+
min-width: 80px;
|
|
5
|
+
background-color: var(--color-bg-raised);
|
|
6
|
+
border-radius: var(--border-radius-m);
|
|
7
|
+
z-index: 1000;
|
|
8
|
+
}
|