paginar 0.1.7 → 0.2.1

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/style.css CHANGED
@@ -1,3 +1,20 @@
1
+
2
+ .width-enter-active[data-v-a72e8c45],
3
+ .width-leave-active[data-v-a72e8c45] {
4
+ transition: width .5s;
5
+ }
6
+ .width-enter-active>*[data-v-a72e8c45] {
7
+ transition-property: opacity;
8
+ transition-duration: .2s;
9
+ transition-delay: .3s;
10
+ }
11
+ .width-leave-active>*[data-v-a72e8c45] {
12
+ transition: opacity .25s;
13
+ }
14
+ .width-enter[data-v-a72e8c45],
15
+ .width-leave-to[data-v-a72e8c45] {
16
+ width: 0;
17
+ }
1
18
  /*
2
19
  ! tailwindcss v3.1.8 | MIT License | https://tailwindcss.com
3
20
  */
@@ -563,10 +580,22 @@ video {
563
580
  position: relative;
564
581
  }
565
582
 
583
+ .top-0 {
584
+ top: 0px;
585
+ }
586
+
566
587
  .right-0 {
567
588
  right: 0px;
568
589
  }
569
590
 
591
+ .top-4 {
592
+ top: 1rem;
593
+ }
594
+
595
+ .right-8 {
596
+ right: 2rem;
597
+ }
598
+
570
599
  .bottom-0 {
571
600
  bottom: 0px;
572
601
  }
@@ -583,6 +612,10 @@ video {
583
612
  left: -0.5rem;
584
613
  }
585
614
 
615
+ .z-20 {
616
+ z-index: 20;
617
+ }
618
+
586
619
  .z-40 {
587
620
  z-index: 40;
588
621
  }
@@ -614,6 +647,22 @@ video {
614
647
  margin-bottom: 2rem;
615
648
  }
616
649
 
650
+ .mb-6 {
651
+ margin-bottom: 1.5rem;
652
+ }
653
+
654
+ .ml-4 {
655
+ margin-left: 1rem;
656
+ }
657
+
658
+ .ml-10 {
659
+ margin-left: 2.5rem;
660
+ }
661
+
662
+ .mt-10 {
663
+ margin-top: 2.5rem;
664
+ }
665
+
617
666
  .mt-3 {
618
667
  margin-top: 0.75rem;
619
668
  }
@@ -626,10 +675,6 @@ video {
626
675
  margin-left: 0.75rem;
627
676
  }
628
677
 
629
- .ml-4 {
630
- margin-left: 1rem;
631
- }
632
-
633
678
  .mb-2 {
634
679
  margin-bottom: 0.5rem;
635
680
  }
@@ -650,14 +695,22 @@ video {
650
695
  display: none;
651
696
  }
652
697
 
653
- .h-full {
654
- height: 100%;
698
+ .h-screen {
699
+ height: 100vh;
655
700
  }
656
701
 
657
702
  .h-6 {
658
703
  height: 1.5rem;
659
704
  }
660
705
 
706
+ .h-8 {
707
+ height: 2rem;
708
+ }
709
+
710
+ .h-full {
711
+ height: 100%;
712
+ }
713
+
661
714
  .h-10 {
662
715
  height: 2.5rem;
663
716
  }
@@ -670,14 +723,22 @@ video {
670
723
  width: 100%;
671
724
  }
672
725
 
673
- .w-4\/5 {
674
- width: 80%;
726
+ .w-96 {
727
+ width: 24rem;
675
728
  }
676
729
 
677
730
  .w-6 {
678
731
  width: 1.5rem;
679
732
  }
680
733
 
734
+ .w-8 {
735
+ width: 2rem;
736
+ }
737
+
738
+ .w-4\/5 {
739
+ width: 80%;
740
+ }
741
+
681
742
  .w-5 {
682
743
  width: 1.25rem;
683
744
  }
@@ -686,6 +747,10 @@ video {
686
747
  width: 15rem;
687
748
  }
688
749
 
750
+ .flex-shrink-0 {
751
+ flex-shrink: 0;
752
+ }
753
+
689
754
  .scale-95 {
690
755
  --tw-scale-x: .95;
691
756
  --tw-scale-y: .95;
@@ -738,10 +803,18 @@ video {
738
803
  align-self: flex-end;
739
804
  }
740
805
 
806
+ .overflow-auto {
807
+ overflow: auto;
808
+ }
809
+
741
810
  .overflow-hidden {
742
811
  overflow: hidden;
743
812
  }
744
813
 
814
+ .overflow-x-hidden {
815
+ overflow-x: hidden;
816
+ }
817
+
745
818
  .rounded {
746
819
  border-radius: 0.25rem;
747
820
  }
@@ -772,6 +845,10 @@ video {
772
845
  border-color: rgb(209 213 219 / var(--tw-border-opacity));
773
846
  }
774
847
 
848
+ .border-b-white\/25 {
849
+ border-bottom-color: rgb(255 255 255 / 0.25);
850
+ }
851
+
775
852
  .bg-gray-800 {
776
853
  --tw-bg-opacity: 1;
777
854
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
@@ -800,6 +877,11 @@ video {
800
877
  padding-right: 2.5rem;
801
878
  }
802
879
 
880
+ .py-4 {
881
+ padding-top: 1rem;
882
+ padding-bottom: 1rem;
883
+ }
884
+
803
885
  .px-2 {
804
886
  padding-left: 0.5rem;
805
887
  padding-right: 0.5rem;
@@ -810,11 +892,6 @@ video {
810
892
  padding-bottom: 0.75rem;
811
893
  }
812
894
 
813
- .py-4 {
814
- padding-top: 1rem;
815
- padding-bottom: 1rem;
816
- }
817
-
818
895
  .px-3 {
819
896
  padding-left: 0.75rem;
820
897
  padding-right: 0.75rem;
@@ -824,6 +901,14 @@ video {
824
901
  padding-top: 2.5rem;
825
902
  }
826
903
 
904
+ .pl-4 {
905
+ padding-left: 1rem;
906
+ }
907
+
908
+ .pb-10 {
909
+ padding-bottom: 2.5rem;
910
+ }
911
+
827
912
  .text-left {
828
913
  text-align: left;
829
914
  }
@@ -851,6 +936,10 @@ video {
851
936
  font-weight: 300;
852
937
  }
853
938
 
939
+ .lowercase {
940
+ text-transform: lowercase;
941
+ }
942
+
854
943
  .tracking-wide {
855
944
  letter-spacing: 0.025em;
856
945
  }
@@ -875,6 +964,10 @@ video {
875
964
  color: rgb(0 0 0 / var(--tw-text-opacity));
876
965
  }
877
966
 
967
+ .opacity-50 {
968
+ opacity: 0.5;
969
+ }
970
+
878
971
  .opacity-100 {
879
972
  opacity: 1;
880
973
  }
@@ -891,6 +984,12 @@ video {
891
984
  opacity: 0.3;
892
985
  }
893
986
 
987
+ .shadow-2xl {
988
+ --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
989
+ --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
990
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
991
+ }
992
+
894
993
  .shadow {
895
994
  --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
896
995
  --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -915,12 +1014,6 @@ video {
915
1014
  transition-duration: 150ms;
916
1015
  }
917
1016
 
918
- .transition-opacity {
919
- transition-property: opacity;
920
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
921
- transition-duration: 150ms;
922
- }
923
-
924
1017
  .transition {
925
1018
  transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
926
1019
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
@@ -929,6 +1022,12 @@ video {
929
1022
  transition-duration: 150ms;
930
1023
  }
931
1024
 
1025
+ .transition-opacity {
1026
+ transition-property: opacity;
1027
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1028
+ transition-duration: 150ms;
1029
+ }
1030
+
932
1031
  .duration-100 {
933
1032
  transition-duration: 100ms;
934
1033
  }
@@ -949,6 +1048,10 @@ video {
949
1048
  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
950
1049
  }
951
1050
 
1051
+ .last\:border-transparent:last-child {
1052
+ border-color: transparent;
1053
+ }
1054
+
952
1055
  .hover\:bg-gray-100:hover {
953
1056
  --tw-bg-opacity: 1;
954
1057
  background-color: rgb(243 244 246 / var(--tw-bg-opacity));
@@ -986,6 +1089,10 @@ video {
986
1089
  display: grid;
987
1090
  }
988
1091
 
1092
+ .md\:w-\[40em\] {
1093
+ width: 40em;
1094
+ }
1095
+
989
1096
  .md\:w-2\/5 {
990
1097
  width: 40%;
991
1098
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "description": "A flipping page web component for reading long texts on the brower",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
7
- "version": "0.1.7",
7
+ "version": "0.2.1",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/educkf/paginar.git"
package/src/App.ce.vue CHANGED
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { ref, onMounted, defineProps, onBeforeUnmount } from 'vue'
2
+ import { ref, onMounted, defineProps, watch } from 'vue'
3
3
  import { useWindowSize, watchDebounced } from '@vueuse/core'
4
4
 
5
5
  import HeaderSlot from './components/HeaderSlot/HeaderSlot.vue'
@@ -8,18 +8,19 @@ import EngineWrapper from './components/EngineWrapper.vue'
8
8
  import ReaderWrapper from './components/ReaderWrapper.vue'
9
9
  import ReferencePopup from './components/ReferencePopup.vue'
10
10
  import FooterSlot from './components/FooterSlot.vue'
11
+ import FootnotesAside from './components/FootnotesAside.vue'
11
12
 
12
13
  import usePagination from './composables/usePagination'
13
14
  import useEstimatePages from './composables/useEstimatePages'
14
15
  import useTextContent from './composables/useTextContent'
15
16
  import useReaderSettings from './composables/useReaderSettings'
16
- import useReferences from './composables/useReferences'
17
17
  import useStyles from './composables/useStyles'
18
18
 
19
19
  const props = defineProps({
20
20
  bookTitle: String,
21
21
  bookContent: String,
22
22
  readerSettings: String,
23
+ readerBlocked: Boolean,
23
24
  rootClass: {
24
25
  type: String,
25
26
  default: ''
@@ -30,8 +31,7 @@ const props = defineProps({
30
31
 
31
32
  const { initSettings, baseFont, textFont, fontSize, columns, setColumns, mode } = useReaderSettings
32
33
  const { currentPage, totalPages, init } = usePagination
33
- const { content } = useTextContent
34
- const { listenToReferencesClick } = useReferences
34
+ const { content, listenToClicks } = useTextContent
35
35
  const { width, height } = useWindowSize()
36
36
 
37
37
  const readerComponent = ref(null)
@@ -42,6 +42,10 @@ onMounted(async () => {
42
42
  init(readerComponent, contentArea)
43
43
  initSettings(props.readerSettings)
44
44
 
45
+ if (['string', 'boolean'].includes(typeof props.readerBlocked)) {
46
+ useReaderSettings.setBlocked(props.readerBlocked)
47
+ }
48
+
45
49
  if (props.cssFile) {
46
50
  useStyles.stylesheetLoader(props.cssFile, rootComponent)
47
51
  }
@@ -51,6 +55,20 @@ onMounted(async () => {
51
55
  }
52
56
  })
53
57
 
58
+ watch(
59
+ props,
60
+ () => {
61
+ console.log(props)
62
+ if (props.hasOwnProperty('readerBlocked')) {
63
+ const readerBlocked = typeof props.readerBlocked === 'boolean'
64
+ ? props.readerBlocked
65
+ : props.readerBlocked === 'true'
66
+ console.log(readerBlocked)
67
+ useReaderSettings.setBlocked(readerBlocked)
68
+ }
69
+ }
70
+ )
71
+
54
72
  watchDebounced(
55
73
  [width, height, content, columns, fontSize, textFont],
56
74
  () => {
@@ -63,7 +81,7 @@ watchDebounced(
63
81
  )
64
82
 
65
83
  watchDebounced(content,
66
- () => listenToReferencesClick(contentArea),
84
+ () => listenToClicks(contentArea),
67
85
  { debounce: 125, maxWait: 250 }
68
86
  )
69
87
 
@@ -126,6 +144,8 @@ watchDebounced(content,
126
144
 
127
145
  <ReferencePopup />
128
146
  </main>
147
+
148
+ <FootnotesAside />
129
149
  </div>
130
150
  </template>
131
151
 
@@ -132,6 +132,11 @@ header.wrapper {
132
132
  }
133
133
  }
134
134
 
135
+ #asidebar {
136
+ background: var(--sidebar-backgorund, #690000);
137
+ color: var(--sidebar-text, #f3f4f6);
138
+ }
139
+
135
140
  /* slider */
136
141
 
137
142
  .slider-target,.slider-target *{
@@ -0,0 +1,123 @@
1
+ <script setup>
2
+ import { computed, onMounted, watch, ref } from 'vue'
3
+ import { onKeyStroke } from '@vueuse/core'
4
+
5
+ import useFootnotes from '../composables/useFootnotes'
6
+ import useReaderSettings from '../composables/useReaderSettings'
7
+
8
+ import IconClose from '../components/icons/Close.vue'
9
+ import IconNotes from '../components/icons/Notes.vue'
10
+
11
+ const show = computed(() => useFootnotes.footnote.value)
12
+ const footnote = computed(() => useFootnotes.footnote.value)
13
+ const showFootnotes = computed(() => useFootnotes.showFootnotes.value)
14
+ const footnotes = computed(() => useFootnotes.footnotes.value)
15
+
16
+ const oldBlockedValue = ref(null)
17
+ const footnoteList = ref(null)
18
+
19
+ function hideAside() {
20
+ useFootnotes.setHighlightedFootnote(null)
21
+ useFootnotes.setShowFootnotes(false)
22
+ }
23
+
24
+ onKeyStroke('Escape', () => {
25
+ if (footnote.value || showFootnotes.value) {
26
+ hideAside()
27
+ }
28
+ })
29
+
30
+ watch(
31
+ [footnote, showFootnotes],
32
+ () => {
33
+ if (footnote.value || showFootnotes.value) {
34
+ if (useReaderSettings.blocked.value === false) {
35
+ oldBlockedValue.value = useReaderSettings.blocked.value
36
+ }
37
+ useReaderSettings.setBlocked(true)
38
+
39
+ } else {
40
+ if (oldBlockedValue.value === false) {
41
+ useReaderSettings.setBlocked(oldBlockedValue.value)
42
+ }
43
+ oldBlockedValue.value = null
44
+ }
45
+
46
+ if (footnote.value) {
47
+ setTimeout(() => {
48
+ const element = footnoteList.value.querySelector(`#${footnote.value.id}`)
49
+ element.scrollIntoView()
50
+ element.focus()
51
+ }, 50)
52
+ }
53
+ }
54
+ )
55
+
56
+ </script>
57
+
58
+ <template>
59
+ <transition name="width">
60
+ <div v-if="show || showFootnotes" id="asidebar"
61
+ class="absolute top-0 right-0 w-96 md:w-[40em] flex-shrink-0 h-screen overflow-auto overflow-x-hidden z-20 shadow-2xl"
62
+ >
63
+ <button @click.prevent="hideAside()" class="fixed top-4 right-8 z-20">
64
+ <IconClose class="w-6 h-6 text-white" />
65
+ </button>
66
+
67
+ <div class="w-full p-4">
68
+ <header class="text-areia py-4 pl-4">
69
+ <div class="flex items-center mb-6">
70
+ <IconNotes class="w-8 h-8" />
71
+ <span class="ml-4">
72
+
73
+ Notas de rodapé
74
+ <span class="ml-10">
75
+ {{ footnotes.length }}
76
+ </span>
77
+ </span>
78
+ </div>
79
+ </header>
80
+
81
+ <section>
82
+ <ul ref="footnoteList">
83
+ <li v-for="(footnoteItem, index) in footnotes"
84
+ :key="footnoteItem.id"
85
+ :id="footnoteItem.id"
86
+ v-html="`<span class='footnote-index'>${index + 1}.</span> ${footnoteItem.text}`"
87
+ class="text-left border-b border-b-white/25 last:border-transparent
88
+ pb-10 mt-10 px-10 relative
89
+ "
90
+ :class="footnote && footnote.id !== footnoteItem.id
91
+ ? 'opacity-50'
92
+ : 'opacity-100'
93
+ "></li>
94
+ </ul>
95
+ </section>
96
+ </div>
97
+ </div>
98
+ </transition>
99
+ </template>
100
+
101
+
102
+
103
+ <style scoped>
104
+ .width-enter-active,
105
+ .width-leave-active {
106
+ transition: width .5s;
107
+ }
108
+
109
+ .width-enter-active>* {
110
+ transition-property: opacity;
111
+ transition-duration: .2s;
112
+ transition-delay: .3s;
113
+ }
114
+
115
+ .width-leave-active>* {
116
+ transition: opacity .25s;
117
+ }
118
+
119
+ .width-enter,
120
+ .width-leave-to {
121
+ width: 0;
122
+ }
123
+ </style>
@@ -2,6 +2,8 @@
2
2
  import { ref } from 'vue'
3
3
  import { onClickOutside, onKeyStroke, useFullscreen } from '@vueuse/core'
4
4
 
5
+ import useReaderSettings from '../../../composables/useReaderSettings'
6
+
5
7
  import IconThreeDots from '../../icons/ThreeDots.vue'
6
8
  import IconClose from '../../icons/Close.vue'
7
9
  import IconExpand from '../../icons/Expand.vue'
@@ -11,7 +13,11 @@ const show = ref(false)
11
13
  const button = ref(null)
12
14
 
13
15
  const toggleSummary = () => show.value = !show.value
14
- const hide = () => show.value = false
16
+ const hide = () => {
17
+ if (!useReaderSettings.blocked.value) {
18
+ show.value = false
19
+ }
20
+ }
15
21
 
16
22
  onClickOutside(button, () => hide())
17
23
  onKeyStroke('Escape', () => hide())
@@ -1,9 +1,14 @@
1
1
  <script setup>
2
2
  import IconEye from '../../icons/Eye.vue'
3
+ import IconFootnote from '../../icons/Footnote.vue'
4
+
3
5
  import OptionsColumns from './OptionsColumns.vue';
4
6
  import OptionsFontFamily from './OptionsFontFamily.vue';
5
7
  import OptionsFontSize from './OptionsFontSize.vue';
6
8
  import OptionsColor from './OptionsColor.vue'
9
+
10
+ import useFootnotes from '../../../composables/useFootnotes'
11
+
7
12
  </script>
8
13
 
9
14
  <template>
@@ -18,7 +23,17 @@ import OptionsColor from './OptionsColor.vue'
18
23
 
19
24
  <slot name="optionsTop" />
20
25
 
21
- <div class="w-full flex justify-center items-center py-3">
26
+ <button @click.prevent="useFootnotes.setShowFootnotes(true)"
27
+ class="w-full flex justify-center items-center py-3 lowercase"
28
+ >
29
+ <IconFootnote class="w-6 h-6" />
30
+ <span class="ml-4">Notas de rodapé</span>
31
+ </button>
32
+
33
+ <div class="w-full border-b border-areia my-3"></div>
34
+
35
+
36
+ <div class="w-full flex justify-center items-center py-3 lowercase">
22
37
  <IconEye class="w-6 h-6" />
23
38
  <span class="ml-4">Visualização</span>
24
39
  </div>
@@ -2,6 +2,8 @@
2
2
  import { ref } from 'vue'
3
3
  import { onClickOutside, onKeyStroke } from '@vueuse/core'
4
4
 
5
+ import useReaderSettings from '../../../composables/useReaderSettings'
6
+
5
7
  import IconHome from '../../icons/Home.vue'
6
8
  import IconClose from '../../icons/Close.vue'
7
9
  import IconSummary from '../../icons/Summary.vue'
@@ -11,7 +13,11 @@ const show = ref(false)
11
13
  const button = ref(null)
12
14
 
13
15
  const toggleSummary = () => show.value = !show.value
14
- const hide = () => show.value = false
16
+ const hide = () => {
17
+ if (!useReaderSettings.blocked.value) {
18
+ show.value = false
19
+ }
20
+ }
15
21
 
16
22
  onClickOutside(button, () => hide())
17
23
  onKeyStroke('Escape', () => hide())
@@ -1,6 +1,9 @@
1
1
  <script setup>
2
2
  import { onKeyStroke } from '@vueuse/core'
3
+
4
+ import useReaderSettings from '../composables/useReaderSettings'
3
5
  import useReferences from '../composables/useReferences'
6
+
4
7
  const { reference, setHighlightedReference } = useReferences
5
8
 
6
9
  function closeRef() {
@@ -8,7 +11,9 @@ function closeRef() {
8
11
  }
9
12
 
10
13
  onKeyStroke('Escape', () => {
11
- setHighlightedReference(null)
14
+ if (!useReaderSettings.blocked.value) {
15
+ setHighlightedReference(null)
16
+ }
12
17
  })
13
18
 
14
19
  </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
4
+ </svg>
5
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
3
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
4
+ </svg>
5
+ </template>
@@ -1,28 +1,28 @@
1
- import { computed, reactive } from 'vue'
2
-
3
- const state = reactive({
4
- totalPages: 1
5
- })
6
-
7
- function estimate(viewport, content) {
8
- if (viewport && content) {
9
- const { width: viewportWidth } = viewport.value.getBoundingClientRect()
10
- const { width: contentWidth } = content.value.getBoundingClientRect()
11
-
12
- if (contentWidth > viewportWidth) {
13
- state.totalPages = Math.ceil(contentWidth / viewportWidth)
14
-
15
- } else {
16
- state.totalPages = 1
17
- }
18
- } else {
19
- state.totalPages = 1
20
- }
21
- }
22
-
23
- const totalPages = computed(() => state.totalPages)
24
-
25
- export default {
26
- totalPages,
27
- estimate
28
- }
1
+ import { computed, reactive } from 'vue'
2
+
3
+ const state = reactive({
4
+ totalPages: 1
5
+ })
6
+
7
+ function estimate(viewport, content) {
8
+ if (viewport && content) {
9
+ const { width: viewportWidth } = viewport.value.getBoundingClientRect()
10
+ const { width: contentWidth } = content.value.getBoundingClientRect()
11
+
12
+ if (contentWidth > viewportWidth) {
13
+ state.totalPages = Math.ceil(contentWidth / viewportWidth)
14
+
15
+ } else {
16
+ state.totalPages = 1
17
+ }
18
+ } else {
19
+ state.totalPages = 1
20
+ }
21
+ }
22
+
23
+ const totalPages = computed(() => state.totalPages)
24
+
25
+ export default {
26
+ totalPages,
27
+ estimate
28
+ }