@@ -2,6 +2,7 @@ import * as React from 'react';
22import type { JSXElement } from '@fluentui/react-components' ;
33import { DeclarativeChart , IDeclarativeChart , Schema } from '@fluentui/react-charts' ;
44import {
5+ Button ,
56 Dropdown ,
67 Field ,
78 Input ,
@@ -158,18 +159,7 @@ const DEFAULT_SCHEMAS = [
158159const dropdownStyles = { width : 200 } ;
159160const inputStyles = { maxWidth : 300 } ;
160161
161- const cachedFetch = ( url : string ) => {
162- const cachedData = localStorage . getItem ( url ) ;
163- if ( cachedData ) {
164- return Promise . resolve ( JSON . parse ( cachedData ) ) ;
165- }
166- return fetch ( url )
167- . then ( response => response . json ( ) )
168- . then ( data => {
169- localStorage . setItem ( url , JSON . stringify ( data ) ) ;
170- return data ;
171- } ) ;
172- } ;
162+ type LoadingState = 'initial' | 'loading' | 'partially_loaded' | 'loaded' ;
173163
174164export const DeclarativeChartBasicExample = ( ) : JSXElement => {
175165 const declarativeChartRef = React . useRef < IDeclarativeChart > ( null ) ;
@@ -185,37 +175,42 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
185175 const [ fluentDataVizColorPalette , setFluentDataVizColorPalette ] =
186176 React . useState < FluentDataVizColorPaletteTypes > ( 'default' ) ;
187177 const [ showMore , setShowMore ] = React . useState ( false ) ;
188- const [ isLoading , setLoading ] = React . useState ( false ) ;
178+ const [ loadingState , setLoadingState ] = React . useState < LoadingState > ( 'initial' ) ;
189179
190180 React . useEffect ( ( ) => {
191181 doc ?. addEventListener ( 'contextmenu' , e => {
192182 e . preventDefault ( ) ;
193183 } ) ;
194184 } , [ doc ] ) ;
195185
196- React . useEffect ( ( ) => {
197- const loadSchemas = async ( ) => {
198- setLoading ( true ) ;
199- /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
200- const _schemas : { key : string ; schema : any } [ ] = [ ] ;
201- for ( let i = 1 ; i <= 80 ; i ++ ) {
202- try {
203- const filename = `data_${ ( '00' + i ) . slice ( - 3 ) } ` ;
204- const schema = await cachedFetch (
205- `https://raw.githubusercontent.com/microsoft/fluentui-charting-contrib/refs/heads/main/apps/plotly_examples/src/data/${ filename } .json` ,
206- ) ;
207- _schemas . push ( { key : filename , schema } ) ;
208- } catch ( error ) {
209- // Nothing to do here
210- }
211- }
212- loadedSchemas . current = _schemas ;
213- setLoading ( false ) ;
214- } ;
215-
216- loadSchemas ( ) ;
186+ const loadSchemas = React . useCallback ( async ( startLoadingState : LoadingState = 'loading' ) => {
187+ setLoadingState ( startLoadingState ) ;
188+ let disableLoadMore = false ;
189+ const offset = loadedSchemas . current . length ;
190+ const promises = Array . from ( { length : 100 } , ( _ , index ) => {
191+ const id = offset + index + 1 ;
192+ const filename = `data_${ id < 100 ? ( '00' + id ) . slice ( - 3 ) : id } ` ;
193+ return fetch (
194+ `https://raw.githubusercontent.com/microsoft/fluentui-charting-contrib/refs/heads/main/apps/plotly_examples/src/data/${ filename } .json` ,
195+ )
196+ . then ( response => {
197+ if ( response . status === 404 ) {
198+ disableLoadMore = true ;
199+ return null ;
200+ }
201+ return response . json ( ) ;
202+ } )
203+ . then ( schema => ( { key : filename , schema } ) )
204+ . catch ( ( ) => ( { key : filename , schema : { } } ) ) ;
205+ } ) ;
206+ loadedSchemas . current . push ( ...( await Promise . all ( promises ) ) . filter ( item => item . schema !== null ) ) ;
207+ setLoadingState ( disableLoadMore ? 'loaded' : 'partially_loaded' ) ;
217208 } , [ ] ) ;
218209
210+ React . useEffect ( ( ) => {
211+ loadSchemas ( 'initial' ) ;
212+ } , [ loadSchemas ] ) ;
213+
219214 const getSchemaByKey = React . useCallback (
220215 /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
221216 ( key : string ) : any => {
@@ -225,38 +220,57 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
225220 [ showMore ] ,
226221 ) ;
227222
223+ const applySelection = React . useCallback (
224+ ( option : { key : string ; text : string } | undefined ) => {
225+ if ( ! option ) {
226+ return ;
227+ }
228+ setSelectedOptions ( [ option . key ] ) ;
229+ setDropdownValue ( option . text ) ;
230+ const schema = getSchemaByKey ( option . key ) ;
231+ setSelectedLegends ( schema . selectedLegends ? JSON . stringify ( schema . selectedLegends ) : '' ) ;
232+ } ,
233+ [ getSchemaByKey ] ,
234+ ) ;
235+
228236 React . useEffect ( ( ) => {
229- if ( showMore && ( isLoading || loadedSchemas . current . length === 0 ) ) {
237+ if ( ! showMore ) {
238+ setOptions ( DEFAULT_OPTIONS ) ;
239+ applySelection ( DEFAULT_OPTIONS [ 0 ] ) ;
240+ return ;
241+ }
242+ // showMore === true
243+ if ( loadingState === 'initial' || loadedSchemas . current . length === 0 ) {
230244 setOptions ( [ ] ) ;
231245 setSelectedOptions ( [ ] ) ;
232246 setDropdownValue ( '' ) ;
233247 setSelectedLegends ( '' ) ;
234- } else {
235- const _options = showMore
236- ? loadedSchemas . current . map ( schema => ( { key : schema . key , text : schema . key } ) )
237- : DEFAULT_OPTIONS ;
248+ return ;
249+ }
250+ const _options = loadedSchemas . current . map ( schema => ( { key : schema . key , text : schema . key } ) ) ;
251+ if ( loadingState . includes ( 'loaded' ) ) {
238252 setOptions ( _options ) ;
239- setSelectedOptions ( [ _options [ 0 ] . key ] ) ;
240- setDropdownValue ( _options [ 0 ] . text ) ;
241- const selectedPlotlySchema = getSchemaByKey ( _options [ 0 ] . key ) ;
242- const { selectedLegends : _selectedLegends } = selectedPlotlySchema ;
243- setSelectedLegends ( _selectedLegends ? JSON . stringify ( _selectedLegends ) : '' ) ;
244253 }
245- } , [ showMore , isLoading , getSchemaByKey ] ) ;
254+ setSelectedOptions ( prevSelectedOptions => {
255+ if ( prevSelectedOptions [ 0 ] ?. includes ( 'data_' ) ) {
256+ return prevSelectedOptions ;
257+ }
258+ setDropdownValue ( _options [ 0 ] . text ) ;
259+ const schema = getSchemaByKey ( _options [ 0 ] . key ) ;
260+ setSelectedLegends ( schema . selectedLegends ? JSON . stringify ( schema . selectedLegends ) : '' ) ;
261+ return [ _options [ 0 ] . key ] ;
262+ } ) ;
263+ } , [ showMore , loadingState , applySelection , getSchemaByKey ] ) ;
246264
247265 const onSwitchDataChange = React . useCallback ( ( ev : React . ChangeEvent < HTMLInputElement > ) => {
248266 setShowMore ( ev . currentTarget . checked ) ;
249267 } , [ ] ) ;
250268
251269 const onOptionSelect = React . useCallback (
252270 ( ev : SelectionEvents , data : OptionOnSelectData ) => {
253- setSelectedOptions ( data . selectedOptions ) ;
254- setDropdownValue ( data . optionText ?? '' ) ;
255- const selectedPlotlySchema = getSchemaByKey ( data . selectedOptions [ 0 ] ) ;
256- const { selectedLegends : _selectedLegends } = selectedPlotlySchema ;
257- setSelectedLegends ( _selectedLegends ? JSON . stringify ( _selectedLegends ) : '' ) ;
271+ applySelection ( { key : data . selectedOptions [ 0 ] , text : data . optionText ?? '' } ) ;
258272 } ,
259- [ getSchemaByKey ] ,
273+ [ applySelection ] ,
260274 ) ;
261275
262276 const onSelectedLegendsChange = React . useCallback (
@@ -293,7 +307,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
293307
294308 const renderDeclarativeChart = React . useCallback ( ( ) => {
295309 if ( showMore ) {
296- if ( isLoading ) {
310+ if ( loadingState === 'initial' ) {
297311 return < Spinner label = "Loading..." /> ;
298312 } else if ( loadedSchemas . current . length === 0 ) {
299313 return < div > More examples could not be loaded.</ div > ;
@@ -325,7 +339,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
325339 ) ;
326340 } , [
327341 showMore ,
328- isLoading ,
342+ loadingState ,
329343 selectedOptions ,
330344 selectedLegends ,
331345 getSchemaByKey ,
@@ -336,7 +350,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
336350 return (
337351 < div >
338352 < Switch checked = { showMore } onChange = { onSwitchDataChange } label = { showMore ? 'Show more' : 'Show few' } />
339- < div style = { { display : 'flex' , flexDirection : 'row' , gap : '10px' } } >
353+ < div style = { { display : 'flex' , flexDirection : 'row' , gap : '10px' , alignItems : 'end' } } >
340354 < Field label = "Select a schema" >
341355 < Dropdown
342356 value = { dropdownValue }
@@ -351,6 +365,17 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
351365 ) ) }
352366 </ Dropdown >
353367 </ Field >
368+ { showMore && (
369+ < div >
370+ < Button
371+ icon = { loadingState . includes ( 'loaded' ) ? undefined : < Spinner size = "tiny" /> }
372+ onClick = { ( ) => loadSchemas ( ) }
373+ disabledFocusable = { loadingState !== 'partially_loaded' }
374+ >
375+ { loadingState . includes ( 'loaded' ) ? 'Load more' : 'Loading' }
376+ </ Button >
377+ </ div >
378+ ) }
354379 < Field label = "Select a color palette" >
355380 < Dropdown
356381 value = { fluentDataVizColorPalette }
0 commit comments