@@ -33,6 +33,19 @@ class TreeList extends BaseTreeList {
3333 * @member {Object} currentPageRecord=null
3434 */
3535 currentPageRecord_ : null ,
36+ /**
37+ * @member {Boolean} lazyChildLoad=false
38+ */
39+ lazyChildLoad : false ,
40+ /**
41+ * Optional URL prefix for relative child chunk URLs.
42+ * @member {String|null} lazyChildUrlPrefix=null
43+ */
44+ lazyChildUrlPrefix : null ,
45+ /**
46+ * @member {String} lazyChildUrlField='childrenUrl'
47+ */
48+ lazyChildUrlField : 'childrenUrl' ,
3649 /**
3750 * Optional prefix for the route (e.g. '/learn' or '/releases')
3851 * @member {String|null} routePrefix=null
@@ -90,6 +103,109 @@ class TreeList extends BaseTreeList {
90103 }
91104 }
92105
106+ /**
107+ * @summary Resolves the URL used to fetch a folder node's deferred children.
108+ * @param {Object } record
109+ * @returns {String|null }
110+ */
111+ getLazyChildUrl ( record ) {
112+ let me = this ,
113+ url = record ?. [ me . lazyChildUrlField ] ;
114+
115+ if ( ! url ) {
116+ return null
117+ }
118+
119+ if ( / ^ (?: [ a - z ] + : ) ? \/ \/ / i. test ( url ) || url . startsWith ( '/' ) || url . startsWith ( './' ) || url . startsWith ( '../' ) ) {
120+ return url
121+ }
122+
123+ return ( me . lazyChildUrlPrefix || '' ) + url
124+ }
125+
126+ /**
127+ * @summary Adds content paths to chunk leaves whose compact index payload omits them.
128+ * @param {Object[] } records
129+ * @param {Object } parentRecord
130+ * @returns {Object[] }
131+ */
132+ normalizeLazyChildRecords ( records , parentRecord ) {
133+ let { contentDir, filePrefix} = parentRecord ;
134+
135+ if ( ! contentDir ) {
136+ return records
137+ }
138+
139+ filePrefix ??= '' ;
140+
141+ return records . map ( record => {
142+ if ( ! record . path && record . id ) {
143+ return {
144+ ...record ,
145+ path : `${ contentDir } /${ filePrefix } ${ record . id } .md`
146+ }
147+ }
148+
149+ return record
150+ } )
151+ }
152+
153+ /**
154+ * @summary Loads a folder node's child chunk on first expand when `lazyChildLoad` is enabled.
155+ * @param {Object } record
156+ * @returns {Promise<Boolean|void> } false cancels the folder toggle.
157+ */
158+ async onFolderItemClick ( record ) {
159+ let me = this ;
160+
161+ if ( ! me . lazyChildLoad || record . isLeaf || record . isChildrenLoaded ) {
162+ return
163+ }
164+
165+ if ( me . store . find ( 'parentId' , record . id ) . length > 0 ) {
166+ record . isChildrenLoaded = true ;
167+ return
168+ }
169+
170+ if ( record . isLoading ) {
171+ return false
172+ }
173+
174+ let url = me . getLazyChildUrl ( record ) ;
175+
176+ if ( ! url ) {
177+ return
178+ }
179+
180+ record . isLoading = true ;
181+
182+ try {
183+ let response = await fetch ( url ) ;
184+
185+ if ( ! response . ok ) {
186+ throw new Error ( `Failed to load tree children from ${ url } : ${ response . status } ` )
187+ }
188+
189+ let records = await response . json ( ) ;
190+
191+ if ( ! Array . isArray ( records ) ) {
192+ records = records ?. data || [ ]
193+ }
194+
195+ if ( records . length > 0 ) {
196+ me . store . add ( me . normalizeLazyChildRecords ( records , record ) )
197+ }
198+
199+ record . isChildrenLoaded = true
200+ } catch ( error ) {
201+ record . hasError = true ;
202+ console . error ( 'TreeList lazy child load failed' , { error, record, url} ) ;
203+ return false
204+ } finally {
205+ record . isLoading = false
206+ }
207+ }
208+
93209 /**
94210 * @param {Object } record
95211 */
@@ -110,7 +226,11 @@ class TreeList extends BaseTreeList {
110226 onStoreLoad ( ) {
111227 super . onStoreLoad ( ) ;
112228
113- this . getStateProvider ( ) . data . countPages = this . store . getCount ( )
229+ let stateProvider = this . getStateProvider ( ) ;
230+
231+ if ( stateProvider ) {
232+ stateProvider . data . countPages = this . store . getCount ( )
233+ }
114234 }
115235}
116236
0 commit comments