coralite-plugin-aggregation 0.9.0 → 0.11.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 +39 -23
- package/lib/components/coralite-pagination.html +67 -72
- package/lib/index.js +206 -210
- package/package.json +2 -2
- package/types/index.js +2 -2
package/README.md
CHANGED
|
@@ -33,10 +33,24 @@ Create a template for individual items (e.g., `templates/coralite-post.html`):
|
|
|
33
33
|
```html
|
|
34
34
|
<template id="coralite-post">
|
|
35
35
|
<article class="post">
|
|
36
|
-
<h2><a href="{{
|
|
37
|
-
<p>{{
|
|
36
|
+
<h2><a href="{{ url }}">{{ title }}</a></h2>
|
|
37
|
+
<p>{{ description }}</p>
|
|
38
38
|
</article>
|
|
39
39
|
</template>
|
|
40
|
+
|
|
41
|
+
<script type="module">
|
|
42
|
+
import { defineComponent } from 'coralite'
|
|
43
|
+
|
|
44
|
+
export default defineComponent({
|
|
45
|
+
data: ({ page }) => {
|
|
46
|
+
return {
|
|
47
|
+
url: page.url.pathname,
|
|
48
|
+
title: page.meta.title,
|
|
49
|
+
description: page.meta.description
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
</script>
|
|
40
54
|
```
|
|
41
55
|
|
|
42
56
|
Create a component to list them (e.g., `templates/blog-list.html`):
|
|
@@ -50,28 +64,30 @@ Create a component to list them (e.g., `templates/blog-list.html`):
|
|
|
50
64
|
|
|
51
65
|
<script type="module">
|
|
52
66
|
import { defineComponent } from 'coralite'
|
|
53
|
-
import {
|
|
67
|
+
import { aggregate } from 'aggregation'
|
|
54
68
|
|
|
55
69
|
export default defineComponent({
|
|
56
|
-
|
|
57
|
-
posts
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
data: async () => {
|
|
71
|
+
const posts = await aggregate({
|
|
72
|
+
// Path to aggregate pages from (relative to pages directory)
|
|
73
|
+
path: ['blog'],
|
|
74
|
+
// Template ID to render for each item
|
|
75
|
+
template: 'coralite-post',
|
|
76
|
+
// Sort by date descending (assuming meta.date exists)
|
|
77
|
+
sort: (a, b) => new Date(b.meta.date) - new Date(a.meta.date),
|
|
78
|
+
// Limit items per page
|
|
79
|
+
limit: 10,
|
|
80
|
+
// Enable pagination
|
|
81
|
+
pagination: {
|
|
82
|
+
segment: 'page', // URL segment: /blog/page/1
|
|
83
|
+
maxVisible: 5, // Max pagination links to show
|
|
84
|
+
ariaLabel: 'Blog Pagination',
|
|
85
|
+
ellipsis: '...'
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
posts
|
|
75
91
|
}
|
|
76
92
|
}
|
|
77
93
|
})
|
|
@@ -114,4 +130,4 @@ When `pagination` is enabled and `limit` is set:
|
|
|
114
130
|
|
|
115
131
|
## License
|
|
116
132
|
|
|
117
|
-
|
|
133
|
+
MPL-2.0
|
|
@@ -7,98 +7,93 @@
|
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script type="module">
|
|
10
|
-
import { defineComponent } from 'coralite
|
|
10
|
+
import { defineComponent } from 'coralite'
|
|
11
11
|
|
|
12
12
|
export default defineComponent({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
attributes: {
|
|
14
|
+
ariaLabel: { type: String, default: 'Page navigation' },
|
|
15
|
+
currentPage: { type: Number, default: 1 },
|
|
16
|
+
totalPages: { type: Number, default: 1 },
|
|
17
|
+
maxVisible: { type: Number, default: 5 },
|
|
18
|
+
baseUrl: { type: String, default: '' },
|
|
19
|
+
urlPrefix: { type: String, default: '' },
|
|
20
|
+
segment: { type: String, default: 'page' },
|
|
21
|
+
ellipsis: { type: String, default: '...' }
|
|
22
|
+
},
|
|
23
|
+
getters: {
|
|
24
|
+
paginationLinks: (state) => {
|
|
25
|
+
const getPageUrl = (page) => {
|
|
26
|
+
if (page === 1) {
|
|
27
|
+
return state.baseUrl;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const cleanPrefix = state.urlPrefix.endsWith('/') ? state.urlPrefix : `${state.urlPrefix}/`;
|
|
31
|
+
return `${cleanPrefix}${state.segment}/${page}.html`;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
const cleanPrefix = urlPrefix.endsWith('/') ? urlPrefix : `${urlPrefix}/`
|
|
34
|
-
return `${cleanPrefix}${segment}/${page}.html`
|
|
35
|
-
}
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const createItem = (page, text, isActive, isDisabled) => {
|
|
35
|
+
let className = 'page-item';
|
|
36
|
+
if (isActive) className += ' active';
|
|
37
|
+
if (isDisabled) className += ' disabled';
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
let attr = '';
|
|
40
|
+
if (isActive) attr += ' aria-current="page"';
|
|
41
|
+
if (isDisabled) attr += ' tabindex="-1" aria-disabled="true"';
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
const href = isDisabled ? '#' : getPageUrl(page);
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
return `<li class="${className}"><a class="page-link" href="${href}"${attr}>${text}</a></li>`;
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
let links = '';
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// Previous Link
|
|
51
|
+
links += createItem(state.currentPage - 1, 'Previous', false, state.currentPage <= 1);
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
// Calculate Window (Start/End)
|
|
54
|
+
const half = Math.floor(state.maxVisible / 2);
|
|
55
|
+
let start = state.currentPage - half;
|
|
56
|
+
let end = state.currentPage + half;
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
if (start < 1) {
|
|
59
|
+
start = 1;
|
|
60
|
+
end = Math.min(state.totalPages, state.maxVisible);
|
|
61
|
+
}
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
if (end > state.totalPages) {
|
|
64
|
+
end = state.totalPages;
|
|
65
|
+
start = Math.max(1, state.totalPages - state.maxVisible + 1);
|
|
66
|
+
}
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const showFirst = start > 1;
|
|
69
|
+
const showLast = end < state.totalPages;
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
// First page + ellipsis
|
|
72
|
+
if (showFirst) {
|
|
73
|
+
links += createItem(1, '1', false, false);
|
|
74
|
+
if (start > 2) {
|
|
75
|
+
links += `<li class="page-item disabled"><span class="page-link">${state.ellipsis}</span></li>`;
|
|
76
|
+
}
|
|
79
77
|
}
|
|
80
|
-
}
|
|
81
78
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
// Main window loop
|
|
80
|
+
for (let i = start; i <= end; i++) {
|
|
81
|
+
links += createItem(i, i, i === state.currentPage, false);
|
|
82
|
+
}
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
// Last page + ellipsis
|
|
85
|
+
if (showLast) {
|
|
86
|
+
if (end < state.totalPages - 1) {
|
|
87
|
+
links += `<li class="page-item disabled"><span class="page-link">${state.ellipsis}</span></li>`;
|
|
88
|
+
}
|
|
89
|
+
links += createItem(state.totalPages, state.totalPages, false, false);
|
|
91
90
|
}
|
|
92
|
-
links += createItem(totalPages, totalPages, false, false)
|
|
93
|
-
}
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
// Next Link
|
|
93
|
+
links += createItem(state.currentPage + 1, 'Next', false, state.currentPage >= state.totalPages);
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
ariaLabel,
|
|
100
|
-
paginationLinks: links
|
|
95
|
+
return links;
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
})
|
|
104
|
-
</script>
|
|
99
|
+
</script>
|
package/lib/index.js
CHANGED
|
@@ -1,248 +1,244 @@
|
|
|
1
1
|
import { definePlugin } from 'coralite'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
4
|
+
export const aggregation = definePlugin({
|
|
5
|
+
name: 'aggregation',
|
|
6
|
+
exports: {
|
|
7
|
+
aggregate: (context) => async (options) => {
|
|
8
|
+
const { state = {}, page: currentPageContext, app, renderContext: currentRenderContext } = context;
|
|
9
|
+
const {
|
|
10
|
+
path: paths = [],
|
|
11
|
+
template: component,
|
|
12
|
+
pagination,
|
|
13
|
+
filter,
|
|
14
|
+
sort,
|
|
15
|
+
limit,
|
|
16
|
+
offset = 0,
|
|
17
|
+
recursive = false,
|
|
18
|
+
transformState
|
|
19
|
+
} = options
|
|
20
|
+
const pagesRoot = app.options.pages
|
|
21
|
+
|
|
22
|
+
// Collect pages
|
|
23
|
+
let allPages = []
|
|
24
|
+
const uniquePaths = new Set()
|
|
25
|
+
|
|
26
|
+
for (const relativePath of paths) {
|
|
27
|
+
const targetPath = path.join(pagesRoot, relativePath)
|
|
28
|
+
|
|
29
|
+
if (!recursive) {
|
|
30
|
+
const pagesInDir = app.pages.getListByPath(targetPath)
|
|
31
|
+
|
|
32
|
+
if (pagesInDir) {
|
|
33
|
+
for (const item of pagesInDir) {
|
|
34
|
+
const itemPath = item.path.pathname
|
|
35
|
+
|
|
36
|
+
if (!uniquePaths.has(itemPath)) {
|
|
37
|
+
uniquePaths.add(itemPath)
|
|
38
|
+
allPages.push(item)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
41
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
} else {
|
|
43
|
+
// Recursive search
|
|
44
|
+
for (const item of app.pages.list) {
|
|
45
|
+
const dirname = item.path.dirname
|
|
46
|
+
|
|
47
|
+
if (dirname === targetPath || dirname.startsWith(targetPath + path.sep)) {
|
|
48
|
+
const itemPath = item.path.pathname
|
|
49
|
+
|
|
50
|
+
if (!uniquePaths.has(itemPath)) {
|
|
51
|
+
uniquePaths.add(itemPath)
|
|
52
|
+
allPages.push(item)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return filter(pageProps)
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Sort
|
|
68
|
-
if (typeof sort === 'function') {
|
|
69
|
-
allPages.sort((a, b) => {
|
|
70
|
-
const propsA = (a.result && a.result.properties) ? a.result.properties : a.properties
|
|
71
|
-
const propsB = (b.result && b.result.properties) ? b.result.properties : b.properties
|
|
72
|
-
return sort(propsA, propsB)
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Pagination
|
|
77
|
-
let startIndex = offset
|
|
78
|
-
let endIndex = allPages.length
|
|
79
|
-
|
|
80
|
-
let currentPage = 1
|
|
81
|
-
let totalPages = 1
|
|
82
|
-
|
|
83
|
-
const currentRenderContext = context.renderContext
|
|
84
|
-
const buildId = currentRenderContext && currentRenderContext.buildId
|
|
85
|
-
|
|
86
|
-
if (limit) {
|
|
87
|
-
if (pagination) {
|
|
88
|
-
const segment = pagination.segment || 'page'
|
|
89
|
-
const urlPathname = context.page.url.pathname
|
|
90
|
-
|
|
91
|
-
const escapedSegment = segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
92
|
-
const segmentRegex = new RegExp(`/${escapedSegment}/(\\d+)`)
|
|
93
|
-
const match = urlPathname.match(segmentRegex)
|
|
94
|
-
|
|
95
|
-
if (match) {
|
|
96
|
-
currentPage = parseInt(match[1], 10)
|
|
58
|
+
|
|
59
|
+
// Filter
|
|
60
|
+
if (typeof filter === 'function') {
|
|
61
|
+
allPages = allPages.filter(item => {
|
|
62
|
+
const itemState = (item.result && item.result.state) ? item.result.state : item.state
|
|
63
|
+
return filter(itemState)
|
|
64
|
+
})
|
|
97
65
|
}
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
67
|
+
// Sort
|
|
68
|
+
if (typeof sort === 'function') {
|
|
69
|
+
allPages.sort((a, b) => {
|
|
70
|
+
const propsA = (a.result && a.result.state) ? a.result.state : a.state
|
|
71
|
+
const propsB = (b.result && b.result.state) ? b.result.state : b.state
|
|
72
|
+
return sort(propsA, propsB)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
102
75
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const currentDirname = context.page.file.dirname
|
|
76
|
+
// Pagination
|
|
77
|
+
let startIndex = offset
|
|
78
|
+
let endIndex = allPages.length
|
|
107
79
|
|
|
108
|
-
|
|
109
|
-
|
|
80
|
+
let currentPage = 1
|
|
81
|
+
let totalPages = 1
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
targetDir = currentDirname
|
|
113
|
-
urlPrefixBase = path.dirname(urlPathname)
|
|
114
|
-
} else {
|
|
115
|
-
const basename = path.basename(currentFilename, path.extname(currentFilename))
|
|
116
|
-
targetDir = path.join(currentDirname, basename)
|
|
117
|
-
urlPrefixBase = urlPathname.replace(path.extname(currentFilename), '')
|
|
118
|
-
}
|
|
83
|
+
const buildId = currentRenderContext && currentRenderContext.buildId
|
|
119
84
|
|
|
120
|
-
|
|
85
|
+
if (limit) {
|
|
86
|
+
if (pagination) {
|
|
87
|
+
const segment = pagination.segment || 'page'
|
|
88
|
+
const urlPathname = currentPageContext.url.pathname
|
|
121
89
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
90
|
+
const escapedSegment = segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
91
|
+
const segmentRegex = new RegExp(`/${escapedSegment}/(\\d+)`)
|
|
92
|
+
const match = urlPathname.match(segmentRegex)
|
|
126
93
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (let i = 2; i <= totalPages; i++) {
|
|
130
|
-
const newPathname = path.join(targetDir, segment, `${i}.html`)
|
|
131
|
-
|
|
132
|
-
const virtualItem = {
|
|
133
|
-
content: currentItem ? currentItem.content : '',
|
|
134
|
-
path: {
|
|
135
|
-
pathname: newPathname,
|
|
136
|
-
dirname: path.dirname(newPathname),
|
|
137
|
-
filename: path.basename(newPathname)
|
|
138
|
-
},
|
|
139
|
-
properties: {
|
|
140
|
-
paginationBaseUrl: urlPathname,
|
|
141
|
-
paginationUrlPrefix: urlPrefixBase
|
|
142
|
-
},
|
|
143
|
-
type: 'page'
|
|
94
|
+
if (match) {
|
|
95
|
+
currentPage = parseInt(match[1], 10)
|
|
144
96
|
}
|
|
145
97
|
|
|
146
|
-
|
|
98
|
+
startIndex = offset + (currentPage - 1) * limit
|
|
99
|
+
endIndex = startIndex + limit
|
|
100
|
+
totalPages = Math.ceil(allPages.length / limit)
|
|
101
|
+
|
|
102
|
+
if (!match && currentPage === 1 && totalPages > 1 && buildId) {
|
|
103
|
+
const currentPathname = currentPageContext.file.pathname
|
|
104
|
+
const currentFilename = currentPageContext.file.filename
|
|
105
|
+
const currentDirname = currentPageContext.file.dirname
|
|
106
|
+
|
|
107
|
+
let targetDir = currentDirname
|
|
108
|
+
let urlPrefixBase = ''
|
|
109
|
+
|
|
110
|
+
if (currentFilename === 'index.html') {
|
|
111
|
+
targetDir = currentDirname
|
|
112
|
+
urlPrefixBase = path.dirname(urlPathname)
|
|
113
|
+
} else {
|
|
114
|
+
const basename = path.basename(currentFilename, path.extname(currentFilename))
|
|
115
|
+
targetDir = path.join(currentDirname, basename)
|
|
116
|
+
urlPrefixBase = urlPathname.replace(path.extname(currentFilename), '')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!urlPrefixBase.endsWith('/')) urlPrefixBase += '/'
|
|
120
|
+
|
|
121
|
+
if (currentFilename === 'index.html') {
|
|
122
|
+
urlPrefixBase = path.dirname(urlPathname)
|
|
123
|
+
if (!urlPrefixBase.endsWith('/')) urlPrefixBase += '/'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const currentItem = app.pages.getItem(currentPathname)
|
|
127
|
+
|
|
128
|
+
for (let i = 2; i <= totalPages; i++) {
|
|
129
|
+
const newPathname = path.join(targetDir, segment, `${i}.html`)
|
|
130
|
+
|
|
131
|
+
const virtualItem = {
|
|
132
|
+
content: currentItem ? currentItem.content : '',
|
|
133
|
+
path: {
|
|
134
|
+
pathname: newPathname,
|
|
135
|
+
dirname: path.dirname(newPathname),
|
|
136
|
+
filename: path.basename(newPathname)
|
|
137
|
+
},
|
|
138
|
+
state: {
|
|
139
|
+
paginationBaseUrl: urlPathname,
|
|
140
|
+
paginationUrlPrefix: urlPrefixBase
|
|
141
|
+
},
|
|
142
|
+
type: 'page'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await app.addRenderQueue(virtualItem, buildId)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
endIndex = Math.min(startIndex + limit, allPages.length)
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} else if (typeof transform === 'function') {
|
|
169
|
-
itemProps[key] = transform(pageProps)
|
|
152
|
+
|
|
153
|
+
const paginatedPages = allPages.slice(startIndex, endIndex)
|
|
154
|
+
const resultNodes = []
|
|
155
|
+
|
|
156
|
+
for (const item of paginatedPages) {
|
|
157
|
+
const itemState = (item.result && item.result.state) ? item.result.state : item.state
|
|
158
|
+
let itemProps = { ...itemState }
|
|
159
|
+
|
|
160
|
+
// Apply properties transformations
|
|
161
|
+
if (transformState && typeof transformState === 'object') {
|
|
162
|
+
for (const key in transformState) {
|
|
163
|
+
if (Object.prototype.hasOwnProperty.call(transformState, key)) {
|
|
164
|
+
const transform = transformState[key]
|
|
165
|
+
if (typeof transform === 'string') {
|
|
166
|
+
itemProps[key] = itemState[transform]
|
|
167
|
+
} else if (typeof transform === 'function') {
|
|
168
|
+
itemProps[key] = transform(itemState)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
173
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
if (component) {
|
|
175
|
+
const componentElement = await app.createComponentElement({
|
|
176
|
+
id: component,
|
|
177
|
+
state: itemProps,
|
|
178
|
+
page: item.result?.page || item.state?.page,
|
|
179
|
+
renderContext: currentRenderContext
|
|
180
|
+
})
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
if (componentElement && componentElement.children) {
|
|
183
|
+
resultNodes.push(...componentElement.children)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
185
186
|
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
187
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let baseUrl = urlPathname
|
|
194
|
-
let urlPrefix = ''
|
|
188
|
+
if (pagination) {
|
|
189
|
+
const paginationComponentId = pagination.component || 'coralite-pagination'
|
|
190
|
+
const urlPathname = currentPageContext.url.pathname
|
|
195
191
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
192
|
+
let baseUrl = urlPathname
|
|
193
|
+
let urlPrefix = ''
|
|
199
194
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (baseUrl.endsWith('/index.html') || baseUrl.endsWith('/')) {
|
|
204
|
-
urlPrefix = path.dirname(baseUrl)
|
|
205
|
-
} else {
|
|
206
|
-
const basename = path.basename(baseUrl, '.html')
|
|
207
|
-
urlPrefix = path.join(path.dirname(baseUrl), basename)
|
|
208
|
-
}
|
|
209
|
-
}
|
|
195
|
+
if (state.paginationBaseUrl) {
|
|
196
|
+
baseUrl = state.paginationBaseUrl
|
|
197
|
+
}
|
|
210
198
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
ellipsis: pagination.ellipsis || '...'
|
|
222
|
-
}
|
|
199
|
+
if (state.paginationUrlPrefix) {
|
|
200
|
+
urlPrefix = state.paginationUrlPrefix
|
|
201
|
+
} else {
|
|
202
|
+
if (baseUrl.endsWith('/index.html') || baseUrl.endsWith('/')) {
|
|
203
|
+
urlPrefix = path.dirname(baseUrl)
|
|
204
|
+
} else {
|
|
205
|
+
const basename = path.basename(baseUrl, '.html')
|
|
206
|
+
urlPrefix = path.join(path.dirname(baseUrl), basename)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
223
209
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
210
|
+
if (!urlPrefix.endsWith('/')) urlPrefix += '/'
|
|
211
|
+
|
|
212
|
+
const paginationProps = {
|
|
213
|
+
'current-page': String(currentPage),
|
|
214
|
+
'total-pages': String(totalPages),
|
|
215
|
+
'base-url': baseUrl,
|
|
216
|
+
'url-prefix': urlPrefix,
|
|
217
|
+
segment: pagination.segment || 'page',
|
|
218
|
+
'max-visible': String(pagination.maxVisible || 5),
|
|
219
|
+
'aria-label': pagination.ariaLabel || 'Pagination',
|
|
220
|
+
ellipsis: pagination.ellipsis || '...'
|
|
221
|
+
}
|
|
230
222
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
223
|
+
const componentElement = await app.createComponentElement({
|
|
224
|
+
id: paginationComponentId,
|
|
225
|
+
state: paginationProps,
|
|
226
|
+
page: currentPageContext,
|
|
227
|
+
renderContext: currentRenderContext
|
|
228
|
+
})
|
|
235
229
|
|
|
236
|
-
|
|
237
|
-
|
|
230
|
+
if (componentElement && componentElement.children) {
|
|
231
|
+
resultNodes.push(...componentElement.children)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
238
234
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
return app.transform(resultNodes)
|
|
236
|
+
}
|
|
237
|
+
},
|
|
242
238
|
components: [
|
|
243
239
|
// @ts-ignore
|
|
244
240
|
path.join(import.meta.dirname, 'components/coralite-pagination.html')
|
|
245
241
|
]
|
|
246
242
|
})
|
|
247
243
|
|
|
248
|
-
export default aggregation
|
|
244
|
+
export default aggregation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coralite-plugin-aggregation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "A Coralite plugin for dynamically collecting, filtering, sorting, and displaying content across multiple sources. Build database-free Coralite websites with automated content aggregation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@stylistic/eslint-plugin-js": "^4.2.0",
|
|
45
45
|
"@stylistic/eslint-plugin-plus": "^4.2.0",
|
|
46
|
-
"coralite": "^0.
|
|
46
|
+
"coralite": "^0.35.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"test": "node --test ./tests/index.spec.js"
|
package/types/index.js
CHANGED
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {Object} AggregationOptions
|
|
12
12
|
* @property {string[]} [path=[]] - An array of relative paths to search for pages within `pagesRoot`.
|
|
13
|
-
* @property {string} [
|
|
13
|
+
* @property {string} [component] - The component ID to use for rendering each item found.
|
|
14
14
|
* @property {AggregationPaginationOptions} [pagination] - Configuration for pagination logic and controls. If present, pagination logic is enabled.
|
|
15
15
|
* @property {function(Object): boolean} [filter] - A callback function to filter pages. It receives the page values object and should return `true` to keep the item.
|
|
16
16
|
* @property {function(Object, Object): number} [sort] - A comparison function for sorting pages. It receives two page value objects (a, b) and should return a number.
|
|
17
17
|
* @property {number} [limit] - The maximum number of items to return (or items per page if pagination is used).
|
|
18
18
|
* @property {number} [offset=0] - The starting index for fetching items.
|
|
19
19
|
* @property {boolean} [recursive=false] - If true, searches subdirectories of the specified paths.
|
|
20
|
-
* @property {Object.<string, (string|function(Object): *)>} [
|
|
20
|
+
* @property {Object.<string, (string|function(Object): *)>} [transformState] - A map of key transformations. Keys are the new property names. Values can be a string (source property name) or a function (receiving page values and returning the new value).
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
export default {}
|