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 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="{{ $urlPathname }}">{{ meta_title }}</a></h2>
37
- <p>{{ meta_description }}</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 { aggregation } from 'coralite/plugins'
67
+ import { aggregate } from 'aggregation'
54
68
 
55
69
  export default defineComponent({
56
- tokens: {
57
- posts: async () => {
58
- return await aggregation({
59
- // Path to aggregate pages from (relative to pages directory)
60
- path: ['blog'],
61
- // Template ID to render for each item
62
- template: 'coralite-post',
63
- // Sort by date descending (assuming meta_date exists)
64
- sort: (a, b) => new Date(b.meta_date) - new Date(a.meta_date),
65
- // Limit items per page
66
- limit: 10,
67
- // Enable pagination
68
- pagination: {
69
- segment: 'page', // URL segment: /blog/page/1
70
- maxVisible: 5, // Max pagination links to show
71
- ariaLabel: 'Blog Pagination',
72
- ellipsis: '...'
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
- AGPL-3.0-or-later
133
+ MPL-2.0
@@ -7,98 +7,93 @@
7
7
  </template>
8
8
 
9
9
  <script type="module">
10
- import { defineComponent } from 'coralite/plugins'
10
+ import { defineComponent } from 'coralite'
11
11
 
12
12
  export default defineComponent({
13
- properties: (context) => {
14
- const props = context.properties
15
-
16
- const ariaLabel = props['aria-label'] || 'Page navigation'
17
-
18
- const currentPage = parseInt(props['current-page'] || '1', 10)
19
- const totalPages = parseInt(props['total-pages'] || '1', 10)
20
- const maxVisible = parseInt(props['max-visible'] || '5', 10)
21
-
22
- const baseUrl = props['base-url'] || ''
23
- const urlPrefix = props['url-prefix'] || ''
24
- const segment = props['segment'] || 'page'
25
- const ellipsis = props['ellipsis'] || '...'
26
-
27
- // Compute pagination synchronously
28
- const getPageUrl = (page) => {
29
- if (page === 1) {
30
- return baseUrl
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
- const createItem = (page, text, isActive, isDisabled) => {
38
- let className = 'page-item'
39
- if (isActive) className += ' active'
40
- if (isDisabled) className += ' disabled'
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
- let attr = ''
43
- if (isActive) attr += ' aria-current="page"'
44
- if (isDisabled) attr += ' tabindex="-1" aria-disabled="true"'
39
+ let attr = '';
40
+ if (isActive) attr += ' aria-current="page"';
41
+ if (isDisabled) attr += ' tabindex="-1" aria-disabled="true"';
45
42
 
46
- const href = isDisabled ? '#' : getPageUrl(page)
43
+ const href = isDisabled ? '#' : getPageUrl(page);
47
44
 
48
- return `<li class="${className}"><a class="page-link" href="${href}"${attr}>${text}</a></li>`
49
- }
45
+ return `<li class="${className}"><a class="page-link" href="${href}"${attr}>${text}</a></li>`;
46
+ }
50
47
 
51
- let links = ''
48
+ let links = '';
52
49
 
53
- // Previous Link
54
- links += createItem(currentPage - 1, 'Previous', false, currentPage <= 1)
50
+ // Previous Link
51
+ links += createItem(state.currentPage - 1, 'Previous', false, state.currentPage <= 1);
55
52
 
56
- // Calculate Window (Start/End)
57
- const half = Math.floor(maxVisible / 2)
58
- let start = currentPage - half
59
- let end = currentPage + half
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
- if (start < 1) {
62
- start = 1
63
- end = Math.min(totalPages, maxVisible)
64
- }
58
+ if (start < 1) {
59
+ start = 1;
60
+ end = Math.min(state.totalPages, state.maxVisible);
61
+ }
65
62
 
66
- if (end > totalPages) {
67
- end = totalPages
68
- start = Math.max(1, totalPages - maxVisible + 1)
69
- }
63
+ if (end > state.totalPages) {
64
+ end = state.totalPages;
65
+ start = Math.max(1, state.totalPages - state.maxVisible + 1);
66
+ }
70
67
 
71
- const showFirst = start > 1
72
- const showLast = end < totalPages
68
+ const showFirst = start > 1;
69
+ const showLast = end < state.totalPages;
73
70
 
74
- // First page + ellipsis
75
- if (showFirst) {
76
- links += createItem(1, '1', false, false)
77
- if (start > 2) {
78
- links += `<li class="page-item disabled"><span class="page-link">${ellipsis}</span></li>`
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
- // Main window loop
83
- for (let i = start; i <= end; i++) {
84
- links += createItem(i, i, i === currentPage, false)
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
- // Last page + ellipsis
88
- if (showLast) {
89
- if (end < totalPages - 1) {
90
- links += `<li class="page-item disabled"><span class="page-link">${ellipsis}</span></li>`
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
- // Next Link
96
- links += createItem(currentPage + 1, 'Next', false, currentPage >= totalPages)
92
+ // Next Link
93
+ links += createItem(state.currentPage + 1, 'Next', false, state.currentPage >= state.totalPages);
97
94
 
98
- return {
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
- * Aggregates content based on configuration
6
- * @param {import('../types/index.js').AggregationOptions} options
7
- * @param {Object} context
8
- * @returns {Promise<any[]>}
9
- */
10
- async function aggregationMethod (options, context) {
11
- const {
12
- path: paths = [],
13
- template,
14
- pagination,
15
- filter,
16
- sort,
17
- limit,
18
- offset = 0,
19
- recursive = false,
20
- transformProperties
21
- } = options
22
-
23
- const contextProperties = context.properties || {}
24
- const pagesRoot = this.options.pages
25
-
26
- // Collect pages
27
- let allPages = []
28
- const uniquePaths = new Set()
29
-
30
- for (const relativePath of paths) {
31
- const targetPath = path.join(pagesRoot, relativePath)
32
-
33
- if (!recursive) {
34
- const pagesInDir = this.pages.getListByPath(targetPath)
35
- if (pagesInDir) {
36
- for (const page of pagesInDir) {
37
- const pagePath = page.url ? page.url.pathname : page.path.pathname
38
- if (!uniquePaths.has(pagePath)) {
39
- uniquePaths.add(pagePath)
40
- allPages.push(page)
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
- } else {
45
- // Recursive search
46
- for (const page of this.pages.list) {
47
- const dirname = page.file ? page.file.dirname : page.path.dirname
48
- if (dirname === targetPath || dirname.startsWith(targetPath + path.sep)) {
49
- const pagePath = page.url ? page.url.pathname : page.path.pathname
50
- if (!uniquePaths.has(pagePath)) {
51
- uniquePaths.add(pagePath)
52
- allPages.push(page)
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
- // Filter
60
- if (typeof filter === 'function') {
61
- allPages = allPages.filter(page => {
62
- const pageProps = (page.result && page.result.properties) ? page.result.properties : page.properties
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
- startIndex = offset + (currentPage - 1) * limit
100
- endIndex = startIndex + limit
101
- totalPages = Math.ceil(allPages.length / limit)
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
- if (!match && currentPage === 1 && totalPages > 1 && buildId) {
104
- const currentPathname = context.page.file.pathname
105
- const currentFilename = context.page.file.filename
106
- const currentDirname = context.page.file.dirname
76
+ // Pagination
77
+ let startIndex = offset
78
+ let endIndex = allPages.length
107
79
 
108
- let targetDir = currentDirname
109
- let urlPrefixBase = ''
80
+ let currentPage = 1
81
+ let totalPages = 1
110
82
 
111
- if (currentFilename === 'index.html') {
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
- if (!urlPrefixBase.endsWith('/')) urlPrefixBase += '/'
85
+ if (limit) {
86
+ if (pagination) {
87
+ const segment = pagination.segment || 'page'
88
+ const urlPathname = currentPageContext.url.pathname
121
89
 
122
- if (currentFilename === 'index.html') {
123
- urlPrefixBase = path.dirname(urlPathname)
124
- if (!urlPrefixBase.endsWith('/')) urlPrefixBase += '/'
125
- }
90
+ const escapedSegment = segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
91
+ const segmentRegex = new RegExp(`/${escapedSegment}/(\\d+)`)
92
+ const match = urlPathname.match(segmentRegex)
126
93
 
127
- const currentItem = this.pages.getItem(currentPathname)
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
- await this.addRenderQueue(virtualItem, buildId)
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
- } else {
150
- endIndex = Math.min(startIndex + limit, allPages.length)
151
- }
152
- }
153
-
154
- const paginatedPages = allPages.slice(startIndex, endIndex)
155
- const resultNodes = []
156
-
157
- for (const page of paginatedPages) {
158
- const pageProps = (page.result && page.result.properties) ? page.result.properties : page.properties
159
- let itemProps = { ...pageProps }
160
-
161
- // Apply properties transformations
162
- if (transformProperties && typeof transformProperties === 'object') {
163
- for (const key in transformProperties) {
164
- if (Object.prototype.hasOwnProperty.call(transformProperties, key)) {
165
- const transform = transformProperties[key]
166
- if (typeof transform === 'string') {
167
- itemProps[key] = pageProps[transform]
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
- if (template) {
176
- const component = await this.createComponentElement({
177
- id: template,
178
- properties: itemProps,
179
- page: context.page,
180
- renderContext: currentRenderContext
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
- if (component && component.children) {
184
- resultNodes.push(...component.children)
182
+ if (componentElement && componentElement.children) {
183
+ resultNodes.push(...componentElement.children)
184
+ }
185
+ }
185
186
  }
186
- }
187
- }
188
187
 
189
- if (pagination) {
190
- const paginationTemplateId = pagination.template || 'coralite-pagination'
191
- const urlPathname = context.page.url.pathname
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
- if (contextProperties.paginationBaseUrl) {
197
- baseUrl = contextProperties.paginationBaseUrl
198
- }
192
+ let baseUrl = urlPathname
193
+ let urlPrefix = ''
199
194
 
200
- if (contextProperties.paginationUrlPrefix) {
201
- urlPrefix = contextProperties.paginationUrlPrefix
202
- } else {
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
- if (!urlPrefix.endsWith('/')) urlPrefix += '/'
212
-
213
- const paginationProps = {
214
- 'current-page': String(currentPage),
215
- 'total-pages': String(totalPages),
216
- 'base-url': baseUrl,
217
- 'url-prefix': urlPrefix,
218
- segment: pagination.segment || 'page',
219
- 'max-visible': String(pagination.maxVisible || 5),
220
- 'aria-label': pagination.ariaLabel || 'Pagination',
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
- const component = await this.createComponentElement({
225
- id: paginationTemplateId,
226
- properties: paginationProps,
227
- page: context.page,
228
- renderContext: currentRenderContext
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
- if (component && component.children) {
232
- resultNodes.push(...component.children)
233
- }
234
- }
223
+ const componentElement = await app.createComponentElement({
224
+ id: paginationComponentId,
225
+ state: paginationProps,
226
+ page: currentPageContext,
227
+ renderContext: currentRenderContext
228
+ })
235
229
 
236
- return resultNodes
237
- }
230
+ if (componentElement && componentElement.children) {
231
+ resultNodes.push(...componentElement.children)
232
+ }
233
+ }
238
234
 
239
- export const aggregation = definePlugin({
240
- name: 'aggregation',
241
- method: aggregationMethod,
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.9.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.33.1"
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} [template] - The component ID to use for rendering each item found.
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): *)>} [transformProperties] - 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).
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 {}