paginar 0.3.2 → 0.3.3
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/index.es.js +9639 -8894
- package/dist/index.es.js.map +1 -1
- package/dist/style.css +287 -284
- package/package.json +1 -1
- package/src/App.ce.vue +166 -148
- package/src/components/HeaderSlot/SummaryButton/SummaryButton.vue +74 -70
- package/src/tailwind.css +285 -282
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.3.
|
|
7
|
+
"version": "0.3.3",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/educkf/paginar.git"
|
package/src/App.ce.vue
CHANGED
|
@@ -1,148 +1,166 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { ref, onMounted, defineProps, watch } from 'vue'
|
|
3
|
-
import { useWindowSize, watchDebounced } from '@vueuse/core'
|
|
4
|
-
|
|
5
|
-
import HeaderSlot from './components/HeaderSlot/HeaderSlot.vue'
|
|
6
|
-
import BookContent from './components/BookContent.vue'
|
|
7
|
-
import EngineWrapper from './components/EngineWrapper.vue'
|
|
8
|
-
import ReaderWrapper from './components/ReaderWrapper.vue'
|
|
9
|
-
import ReferencePopup from './components/ReferencePopup.vue'
|
|
10
|
-
import FooterSlot from './components/FooterSlot.vue'
|
|
11
|
-
import FootnotesAside from './components/FootnotesAside.vue'
|
|
12
|
-
|
|
13
|
-
import usePagination from './composables/usePagination'
|
|
14
|
-
import useEstimatePages from './composables/useEstimatePages'
|
|
15
|
-
import useTextContent from './composables/useTextContent'
|
|
16
|
-
import useReaderSettings from './composables/useReaderSettings'
|
|
17
|
-
import useStyles from './composables/useStyles'
|
|
18
|
-
import useBrowser from './composables/useBrowser'
|
|
19
|
-
|
|
20
|
-
const props = defineProps({
|
|
21
|
-
bookTitle: String,
|
|
22
|
-
bookContent: String,
|
|
23
|
-
readerSettings: String,
|
|
24
|
-
readerBlocked: Boolean,
|
|
25
|
-
rootClass: {
|
|
26
|
-
type: String,
|
|
27
|
-
default: ''
|
|
28
|
-
},
|
|
29
|
-
cssFile: String,
|
|
30
|
-
cssString: String
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const { baseFont, bookTitle, chapterTitle, textFont, fontSize, columns, setColumns, mode } = useReaderSettings
|
|
34
|
-
const { currentPage, totalPages } = usePagination
|
|
35
|
-
const { content, listenToClicks } = useTextContent
|
|
36
|
-
const { width, height } = useWindowSize()
|
|
37
|
-
|
|
38
|
-
const readerComponent = ref(null)
|
|
39
|
-
const contentArea = ref(null)
|
|
40
|
-
const rootComponent = ref(null)
|
|
41
|
-
|
|
42
|
-
onMounted(async () => {
|
|
43
|
-
usePagination.init(readerComponent, contentArea)
|
|
44
|
-
useReaderSettings.initSettings(props.readerSettings)
|
|
45
|
-
useStyles.initStyles(props, rootComponent)
|
|
46
|
-
|
|
47
|
-
if (['string', 'boolean'].includes(typeof props.readerBlocked)) {
|
|
48
|
-
useReaderSettings.setBlocked(props.readerBlocked)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
watch(
|
|
54
|
-
props,
|
|
55
|
-
() => {
|
|
56
|
-
if (props.hasOwnProperty('readerBlocked')) {
|
|
57
|
-
const readerBlocked = typeof props.readerBlocked === 'boolean'
|
|
58
|
-
? props.readerBlocked
|
|
59
|
-
: props.readerBlocked === 'true'
|
|
60
|
-
useReaderSettings.setBlocked(readerBlocked)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
watchDebounced(
|
|
66
|
-
[width, height, content, columns, fontSize, textFont],
|
|
67
|
-
() => {
|
|
68
|
-
if (width.value < 1024 && columns.value === 'double')
|
|
69
|
-
setColumns('single')
|
|
70
|
-
else
|
|
71
|
-
useEstimatePages.estimate(readerComponent, contentArea)
|
|
72
|
-
},
|
|
73
|
-
{ debounce: 125, maxWait: 250 }
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
watchDebounced(content,
|
|
77
|
-
() => listenToClicks(contentArea),
|
|
78
|
-
{ debounce: 125, maxWait: 250 }
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
</script>
|
|
82
|
-
|
|
83
|
-
<template>
|
|
84
|
-
<div class="rootWrapper" :class="useBrowser.isSafari ? 'safari' : ''">
|
|
85
|
-
<main id="rootComponent"
|
|
86
|
-
ref="rootComponent"
|
|
87
|
-
:class="mode
|
|
88
|
-
.concat(` ${props.rootClass}`)
|
|
89
|
-
.concat(` currentPage-${currentPage}`)
|
|
90
|
-
.concat(` totalPages-${totalPages}`)
|
|
91
|
-
.concat(` columns-${columns}`)
|
|
92
|
-
"
|
|
93
|
-
:style="`font-family: ${baseFont}`"
|
|
94
|
-
>
|
|
95
|
-
<HeaderSlot>
|
|
96
|
-
<template #header>
|
|
97
|
-
<slot name="header">
|
|
98
|
-
<p class="text-white text-center text-sm">
|
|
99
|
-
{{ props.bookTitle || bookTitle }}
|
|
100
|
-
</p>
|
|
101
|
-
<p class="text-white text-center text-xs">
|
|
102
|
-
{{ props.chapterTitle || chapterTitle }}
|
|
103
|
-
</p>
|
|
104
|
-
</slot>
|
|
105
|
-
</template>
|
|
106
|
-
|
|
107
|
-
<template #summaryTop>
|
|
108
|
-
<slot name="summaryTop"></slot>
|
|
109
|
-
</template>
|
|
110
|
-
|
|
111
|
-
<template #summaryBottom>
|
|
112
|
-
<slot name="summaryBottom"></slot>
|
|
113
|
-
</template>
|
|
114
|
-
|
|
115
|
-
<template #optionsTop>
|
|
116
|
-
<slot name="optionsTop"></slot>
|
|
117
|
-
</template>
|
|
118
|
-
|
|
119
|
-
<template #optionsBottom>
|
|
120
|
-
<slot name="optionsBottom"></slot>
|
|
121
|
-
</template>
|
|
122
|
-
</HeaderSlot>
|
|
123
|
-
|
|
124
|
-
<EngineWrapper>
|
|
125
|
-
<div id="reader-component" ref="readerComponent" >
|
|
126
|
-
<ReaderWrapper>
|
|
127
|
-
<section id="content-area" ref="contentArea"
|
|
128
|
-
:style="`font-family: ${textFont}; font-size: ${fontSize}px`">
|
|
129
|
-
<BookContent :bookContent="bookContent">
|
|
130
|
-
<template #content>
|
|
131
|
-
<slot name="content"></slot>
|
|
132
|
-
</template>
|
|
133
|
-
</BookContent>
|
|
134
|
-
</section>
|
|
135
|
-
</ReaderWrapper>
|
|
136
|
-
</div>
|
|
137
|
-
</EngineWrapper>
|
|
138
|
-
|
|
139
|
-
<FooterSlot :currPage="currentPage" :totalPages="totalPages" />
|
|
140
|
-
|
|
141
|
-
<ReferencePopup />
|
|
142
|
-
</main>
|
|
143
|
-
|
|
144
|
-
<FootnotesAside />
|
|
145
|
-
</div>
|
|
146
|
-
</template>
|
|
147
|
-
|
|
148
|
-
<style lang="scss" src="./assets/main.css"></style>
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, onMounted, defineProps, watch } from 'vue'
|
|
3
|
+
import { useWindowSize, watchDebounced } from '@vueuse/core'
|
|
4
|
+
|
|
5
|
+
import HeaderSlot from './components/HeaderSlot/HeaderSlot.vue'
|
|
6
|
+
import BookContent from './components/BookContent.vue'
|
|
7
|
+
import EngineWrapper from './components/EngineWrapper.vue'
|
|
8
|
+
import ReaderWrapper from './components/ReaderWrapper.vue'
|
|
9
|
+
import ReferencePopup from './components/ReferencePopup.vue'
|
|
10
|
+
import FooterSlot from './components/FooterSlot.vue'
|
|
11
|
+
import FootnotesAside from './components/FootnotesAside.vue'
|
|
12
|
+
|
|
13
|
+
import usePagination from './composables/usePagination'
|
|
14
|
+
import useEstimatePages from './composables/useEstimatePages'
|
|
15
|
+
import useTextContent from './composables/useTextContent'
|
|
16
|
+
import useReaderSettings from './composables/useReaderSettings'
|
|
17
|
+
import useStyles from './composables/useStyles'
|
|
18
|
+
import useBrowser from './composables/useBrowser'
|
|
19
|
+
|
|
20
|
+
const props = defineProps({
|
|
21
|
+
bookTitle: String,
|
|
22
|
+
bookContent: String,
|
|
23
|
+
readerSettings: String,
|
|
24
|
+
readerBlocked: Boolean,
|
|
25
|
+
rootClass: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: ''
|
|
28
|
+
},
|
|
29
|
+
cssFile: String,
|
|
30
|
+
cssString: String
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const { baseFont, bookTitle, chapterTitle, textFont, fontSize, columns, setColumns, mode } = useReaderSettings
|
|
34
|
+
const { currentPage, totalPages } = usePagination
|
|
35
|
+
const { content, listenToClicks } = useTextContent
|
|
36
|
+
const { width, height } = useWindowSize()
|
|
37
|
+
|
|
38
|
+
const readerComponent = ref(null)
|
|
39
|
+
const contentArea = ref(null)
|
|
40
|
+
const rootComponent = ref(null)
|
|
41
|
+
|
|
42
|
+
onMounted(async () => {
|
|
43
|
+
usePagination.init(readerComponent, contentArea)
|
|
44
|
+
useReaderSettings.initSettings(props.readerSettings)
|
|
45
|
+
useStyles.initStyles(props, rootComponent)
|
|
46
|
+
|
|
47
|
+
if (['string', 'boolean'].includes(typeof props.readerBlocked)) {
|
|
48
|
+
useReaderSettings.setBlocked(props.readerBlocked)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
watch(
|
|
54
|
+
props,
|
|
55
|
+
() => {
|
|
56
|
+
if (props.hasOwnProperty('readerBlocked')) {
|
|
57
|
+
const readerBlocked = typeof props.readerBlocked === 'boolean'
|
|
58
|
+
? props.readerBlocked
|
|
59
|
+
: props.readerBlocked === 'true'
|
|
60
|
+
useReaderSettings.setBlocked(readerBlocked)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
watchDebounced(
|
|
66
|
+
[width, height, content, columns, fontSize, textFont],
|
|
67
|
+
() => {
|
|
68
|
+
if (width.value < 1024 && columns.value === 'double')
|
|
69
|
+
setColumns('single')
|
|
70
|
+
else
|
|
71
|
+
useEstimatePages.estimate(readerComponent, contentArea)
|
|
72
|
+
},
|
|
73
|
+
{ debounce: 125, maxWait: 250 }
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
watchDebounced(content,
|
|
77
|
+
() => listenToClicks(contentArea),
|
|
78
|
+
{ debounce: 125, maxWait: 250 }
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<template>
|
|
84
|
+
<div class="rootWrapper" :class="useBrowser.isSafari ? 'safari' : ''">
|
|
85
|
+
<main id="rootComponent"
|
|
86
|
+
ref="rootComponent"
|
|
87
|
+
:class="mode
|
|
88
|
+
.concat(` ${props.rootClass}`)
|
|
89
|
+
.concat(` currentPage-${currentPage}`)
|
|
90
|
+
.concat(` totalPages-${totalPages}`)
|
|
91
|
+
.concat(` columns-${columns}`)
|
|
92
|
+
"
|
|
93
|
+
:style="`font-family: ${baseFont}`"
|
|
94
|
+
>
|
|
95
|
+
<HeaderSlot>
|
|
96
|
+
<template #header>
|
|
97
|
+
<slot name="header">
|
|
98
|
+
<p class="text-white text-center text-sm">
|
|
99
|
+
{{ props.bookTitle || bookTitle }}
|
|
100
|
+
</p>
|
|
101
|
+
<p class="text-white text-center text-xs">
|
|
102
|
+
{{ props.chapterTitle || chapterTitle }}
|
|
103
|
+
</p>
|
|
104
|
+
</slot>
|
|
105
|
+
</template>
|
|
106
|
+
|
|
107
|
+
<template #summaryTop>
|
|
108
|
+
<slot name="summaryTop"></slot>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<template #summaryBottom>
|
|
112
|
+
<slot name="summaryBottom"></slot>
|
|
113
|
+
</template>
|
|
114
|
+
|
|
115
|
+
<template #optionsTop>
|
|
116
|
+
<slot name="optionsTop"></slot>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<template #optionsBottom>
|
|
120
|
+
<slot name="optionsBottom"></slot>
|
|
121
|
+
</template>
|
|
122
|
+
</HeaderSlot>
|
|
123
|
+
|
|
124
|
+
<EngineWrapper>
|
|
125
|
+
<div id="reader-component" ref="readerComponent" >
|
|
126
|
+
<ReaderWrapper>
|
|
127
|
+
<section id="content-area" ref="contentArea"
|
|
128
|
+
:style="`font-family: ${textFont}; font-size: ${fontSize}px`">
|
|
129
|
+
<BookContent :bookContent="bookContent">
|
|
130
|
+
<template #content>
|
|
131
|
+
<slot name="content"></slot>
|
|
132
|
+
</template>
|
|
133
|
+
</BookContent>
|
|
134
|
+
</section>
|
|
135
|
+
</ReaderWrapper>
|
|
136
|
+
</div>
|
|
137
|
+
</EngineWrapper>
|
|
138
|
+
|
|
139
|
+
<FooterSlot :currPage="currentPage" :totalPages="totalPages" />
|
|
140
|
+
|
|
141
|
+
<ReferencePopup />
|
|
142
|
+
</main>
|
|
143
|
+
|
|
144
|
+
<FootnotesAside />
|
|
145
|
+
</div>
|
|
146
|
+
</template>
|
|
147
|
+
|
|
148
|
+
<style lang="scss" src="./assets/main.css"></style>
|
|
149
|
+
|
|
150
|
+
<style lang="scss">
|
|
151
|
+
/* In your Vue component's <style> section */
|
|
152
|
+
:host {
|
|
153
|
+
user-select: text;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
::slotted(*) {
|
|
157
|
+
user-select: text;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Ensure paragraphs can be selected across boundaries */
|
|
161
|
+
::slotted(p) {
|
|
162
|
+
user-select: text;
|
|
163
|
+
display: block;
|
|
164
|
+
/* Avoid inline-block which can break selection */
|
|
165
|
+
}
|
|
166
|
+
</style>
|
|
@@ -1,70 +1,74 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { ref } from 'vue'
|
|
3
|
-
import { onClickOutside, onKeyStroke } from '@vueuse/core'
|
|
4
|
-
|
|
5
|
-
import useReaderSettings from '../../../composables/useReaderSettings'
|
|
6
|
-
|
|
7
|
-
import IconHome from '../../icons/Home.vue'
|
|
8
|
-
import IconClose from '../../icons/Close.vue'
|
|
9
|
-
import IconSummary from '../../icons/Summary.vue'
|
|
10
|
-
import SummaryDropdown from './SummaryDropdown.vue'
|
|
11
|
-
|
|
12
|
-
const show = ref(false)
|
|
13
|
-
const button = ref(null)
|
|
14
|
-
|
|
15
|
-
const { homeUrl } = useReaderSettings
|
|
16
|
-
|
|
17
|
-
const toggleSummary = () =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<template #
|
|
63
|
-
<slot name="
|
|
64
|
-
</template>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
</
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { onClickOutside, onKeyStroke } from '@vueuse/core'
|
|
4
|
+
|
|
5
|
+
import useReaderSettings from '../../../composables/useReaderSettings'
|
|
6
|
+
|
|
7
|
+
import IconHome from '../../icons/Home.vue'
|
|
8
|
+
import IconClose from '../../icons/Close.vue'
|
|
9
|
+
import IconSummary from '../../icons/Summary.vue'
|
|
10
|
+
import SummaryDropdown from './SummaryDropdown.vue'
|
|
11
|
+
|
|
12
|
+
const show = ref(false)
|
|
13
|
+
const button = ref(null)
|
|
14
|
+
|
|
15
|
+
const { homeUrl } = useReaderSettings
|
|
16
|
+
|
|
17
|
+
const toggleSummary = () => {
|
|
18
|
+
show.value = !show.value
|
|
19
|
+
useReaderSettings.setBlocked(show.value)
|
|
20
|
+
}
|
|
21
|
+
const hide = () => {
|
|
22
|
+
if (!useReaderSettings.blocked.value) {
|
|
23
|
+
show.value = false
|
|
24
|
+
useReaderSettings.setBlocked(false)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onClickOutside(button, () => hide())
|
|
29
|
+
onKeyStroke('Escape', () => hide())
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div id="summary-menu" ref="button" class="relative">
|
|
34
|
+
<div class="flex">
|
|
35
|
+
<a v-if="homeUrl"
|
|
36
|
+
:href="homeUrl" title="Homepage" id="home-button"
|
|
37
|
+
class="hidden md:flex items-center border p-3 shadow mr-3
|
|
38
|
+
border-white text-white"
|
|
39
|
+
>
|
|
40
|
+
<IconHome class="w-6 h-6"/>
|
|
41
|
+
</a>
|
|
42
|
+
|
|
43
|
+
<div class="position-relative">
|
|
44
|
+
<button
|
|
45
|
+
@click.prevent="toggleSummary()"
|
|
46
|
+
class="border p-3 shadow flex items-center border-white text-white"
|
|
47
|
+
id="summary-button" aria-haspopup="true" :aria-expanded="show"
|
|
48
|
+
>
|
|
49
|
+
<IconSummary v-if="!show" />
|
|
50
|
+
<IconClose v-else class="w-6 h-6" />
|
|
51
|
+
|
|
52
|
+
<span class="block ml-3">
|
|
53
|
+
Sumário
|
|
54
|
+
</span>
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
<transition enter-active-class="transition ease-out duration-100 transform" enter-class="opacity-0 scale-95"
|
|
58
|
+
enter-to-class="opacity-100 scale-100" leave-active-class="transition ease-in duration-75 transform"
|
|
59
|
+
leave-class="opacity-100 scale-100" leave-to-class="opacity-0 scale-95"
|
|
60
|
+
>
|
|
61
|
+
<SummaryDropdown v-if="show">
|
|
62
|
+
<template #summaryTop>
|
|
63
|
+
<slot name="summaryTop"/>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template #summaryBottom>
|
|
67
|
+
<slot name="summaryBottom"/>
|
|
68
|
+
</template>
|
|
69
|
+
</SummaryDropdown>
|
|
70
|
+
</transition>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|