@datagouv/components-next 1.0.2-dev.68 → 1.0.2-dev.69

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.
Files changed (24) hide show
  1. package/dist/{Datafair.client-mCVEbSye.js → Datafair.client-B57v03ac.js} +1 -1
  2. package/dist/{JsonPreview.client-D2-QXKW9.js → JsonPreview.client-C7uJkiI6.js} +2 -2
  3. package/dist/{MapContainer.client-fG7SH8SY.js → MapContainer.client-D9AVgKoU.js} +2 -2
  4. package/dist/{PdfPreview.client-BD2LoeWd.js → PdfPreview.client-3_AtEyvC.js} +2 -2
  5. package/dist/{Pmtiles.client-BoISUK3N.js → Pmtiles.client-D00kMVdH.js} +1 -1
  6. package/dist/{PreviewWrapper.vue_vue_type_script_setup_true_lang-BRkoVZX6.js → PreviewWrapper.vue_vue_type_script_setup_true_lang-NHqgUJAK.js} +1 -1
  7. package/dist/{XmlPreview.client-D4beM-9H.js → XmlPreview.client-Bn2kpzCg.js} +3 -3
  8. package/dist/components-next.css +1 -1
  9. package/dist/components-next.js +166 -157
  10. package/dist/components.css +1 -1
  11. package/dist/{index-CHqTMS42.js → index-BPZOlCaf.js} +1 -1
  12. package/dist/{main-vXPsdhWv.js → main-By-J5lfU.js} +79299 -31033
  13. package/dist/{vue3-xml-viewer.common-S2fcy_RO.js → vue3-xml-viewer.common-D-uUQRA7.js} +1 -1
  14. package/package.json +2 -1
  15. package/src/components/Chart/ChartViewer.vue +226 -0
  16. package/src/components/Chart/ChartViewerWrapper.vue +170 -0
  17. package/src/components/Form/Listbox.vue +101 -0
  18. package/src/components/ResourceAccordion/Preview.vue +1 -1
  19. package/src/composables/useHasTabularData.ts +6 -0
  20. package/src/config.ts +1 -0
  21. package/src/functions/charts.ts +68 -0
  22. package/src/functions/tabularApi.ts +136 -7
  23. package/src/main.ts +27 -0
  24. package/src/types/visualizations.ts +89 -0
@@ -1,18 +1,147 @@
1
1
  import { ofetch } from 'ofetch'
2
2
  import { useComponentsConfig, type PluginConfig } from '../config'
3
+ import type { GenericFilter } from '../types/visualizations'
3
4
  import type { SortConfig } from '../components/TabularExplorer/types'
4
5
 
5
6
  export type { SortConfig }
6
7
 
8
+ export type TabularDataResponse = {
9
+ data: Array<Record<string, unknown>>
10
+ links: {
11
+ profile: string
12
+ swagger: string
13
+ next: string
14
+ }
15
+ meta: { total: number }
16
+ }
17
+
18
+ export type TabularAggregateType = 'avg' | 'sum' | 'count' | 'min' | 'max'
19
+
20
+ export type FetchTabularDataOptions = {
21
+ resourceId: string
22
+ page?: number
23
+ pageSize?: number
24
+ columns?: Array<string> | undefined
25
+ sort?: SortConfig
26
+ groupBy?: string | undefined
27
+ aggregation?: {
28
+ column: string
29
+ type: TabularAggregateType
30
+ } | undefined
31
+ filters?: GenericFilter | undefined
32
+ }
33
+
34
+ export type TabularProfileResponse = {
35
+ profile: {
36
+ header: Array<string>
37
+ columns: Record<string, {
38
+ score: number
39
+ format: string
40
+ python_type: string
41
+ }>
42
+ formats: Record<string, Array<string>>
43
+ profile: Record<string, {
44
+ tops: Array<{ count: number, value: string }>
45
+ nb_distinct: number
46
+ nb_missing_values: number
47
+ min?: number
48
+ max?: number
49
+ std?: number
50
+ mean?: number
51
+ }>
52
+ encoding: string
53
+ separator: string
54
+ categorical: Array<string>
55
+ total_lines: number
56
+ nb_duplicates: number
57
+ columns_fields: Record<string, {
58
+ score: number
59
+ format: string
60
+ python_type: string
61
+ }>
62
+ columns_labels: Record<string, {
63
+ score: number
64
+ format: string
65
+ python_type: string
66
+ }>
67
+ header_row_idx: number
68
+ heading_columns: number
69
+ trailing_columns: number
70
+ }
71
+ deleted_at: string | null
72
+ dataset_id: string
73
+ indexes: null
74
+ }
75
+
7
76
  /**
8
- * Call Tabular-api to get table content
77
+ * Call Tabular-api to get table content with options object
9
78
  */
10
- export async function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig | null) {
11
- let url = `${config.tabularApiUrl}/api/resources/${id}/data/?page=${page}&page_size=${config.tabularApiPageSize || 15}`
12
- if (sortConfig) {
13
- url = url + `&${sortConfig.column}__sort=${sortConfig.direction}`
79
+ export async function fetchTabularData(config: PluginConfig, options: FetchTabularDataOptions): Promise<TabularDataResponse> {
80
+ const page = options.page ?? 1
81
+ const pageSize = options.pageSize ?? config.tabularApiPageSize ?? 15
82
+ let url = `${config.tabularApiUrl}/api/resources/${options.resourceId}/data/?page=${page}&page_size=${pageSize}`
83
+ if (options.columns) {
84
+ url += `&columns=${options.columns.map(col => encodeURIComponent(col)).join(',')}`
85
+ }
86
+ if (options.sort) {
87
+ url += `&${encodeURIComponent(options.sort.column)}__sort=${encodeURIComponent(options.sort.direction)}`
14
88
  }
15
- return await ofetch(url)
89
+ if (options.groupBy && options.aggregation?.type) {
90
+ url += `&${encodeURIComponent(options.groupBy)}__groupby&${encodeURIComponent(options.aggregation.column)}__${encodeURIComponent(options.aggregation.type)}`
91
+ }
92
+ if (options.filters) {
93
+ const filterQuery = buildFilterQuery(options.filters)
94
+ if (filterQuery) {
95
+ url += `&${filterQuery}`
96
+ }
97
+ }
98
+ return await ofetch<TabularDataResponse>(url)
99
+ }
100
+
101
+ function buildFilterQuery(filters: GenericFilter): string {
102
+ const params: Array<string> = []
103
+ if ('filters' in filters) {
104
+ for (const filter of filters.filters) {
105
+ if ('filters' in filter) {
106
+ params.push(buildFilterQuery(filter))
107
+ }
108
+ else {
109
+ if (filter.condition === 'is_null') {
110
+ params.push(`${encodeURIComponent(filter.column)}__isnull`)
111
+ }
112
+ else if (filter.condition === 'is_not_null') {
113
+ params.push(`${encodeURIComponent(filter.column)}__isnotnull`)
114
+ }
115
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
116
+ params.push(`${encodeURIComponent(filter.column)}__${encodeURIComponent(filter.condition)}=${encodeURIComponent(filter.value)}`)
117
+ }
118
+ }
119
+ }
120
+ }
121
+ else {
122
+ const filter = filters
123
+ if (filter.condition === 'is_null') {
124
+ params.push(`${encodeURIComponent(filter.column)}__isnull`)
125
+ }
126
+ else if (filter.condition === 'is_not_null') {
127
+ params.push(`${encodeURIComponent(filter.column)}__isnotnull`)
128
+ }
129
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
130
+ params.push(`${encodeURIComponent(filter.column)}__${encodeURIComponent(filter.condition)}=${encodeURIComponent(filter.value)}`)
131
+ }
132
+ }
133
+ return params.join('&')
134
+ }
135
+
136
+ /**
137
+ * Call Tabular-api to get table content
138
+ */
139
+ export async function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig | null): Promise<TabularDataResponse> {
140
+ return fetchTabularData(config, {
141
+ resourceId: id,
142
+ page,
143
+ sort: sortConfig ?? undefined,
144
+ })
16
145
  }
17
146
 
18
147
  /**
@@ -20,5 +149,5 @@ export async function getData(config: PluginConfig, id: string, page: number, so
20
149
  */
21
150
  export function useGetProfile() {
22
151
  const config = useComponentsConfig()
23
- return (id: string) => ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
152
+ return (id: string) => ofetch<TabularProfileResponse>(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
24
153
  }
package/src/main.ts CHANGED
@@ -23,6 +23,7 @@ import type { Site } from './types/site'
23
23
  import type { Weight, WellType } from './types/ui'
24
24
  import type { User, UserReference } from './types/users'
25
25
  import type { Report, ReportSubject, ReportReason } from './types/reports'
26
+ import type { Chart, ChartForm, ChartForApi, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, XAxisForm, UnitPosition, YAxis, DataSeriesType, DataSeries, DataSeriesForm } from './types/visualizations'
26
27
  import type { GlobalSearchConfig, SearchType, SearchTypeConfig, SortOption, HiddenFilter, BuiltInFilterKey, DatasetSearchConfig, DatasetSearchFilters, DataserviceSearchConfig, DataserviceSearchFilters, ReuseSearchConfig, ReuseSearchFilters, OrganizationSearchConfig, OrganizationSearchFilters, TopicSearchConfig, TopicSearchFilters } from './types/search'
27
28
  import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
28
29
  import { useSearchFilter } from './composables/useSearchFilter'
@@ -82,6 +83,8 @@ import ReuseDetails from './components/ReuseDetails.vue'
82
83
  import SchemaCard from './components/SchemaCard.vue'
83
84
  import SimpleBanner from './components/SimpleBanner.vue'
84
85
  import SmallChart from './components/SmallChart.vue'
86
+ import ChartViewer from './components/Chart/ChartViewer.vue'
87
+ import ChartViewerWrapper from './components/Chart/ChartViewerWrapper.vue'
85
88
  import StatBox from './components/StatBox.vue'
86
89
  import Tab from './components/Tabs/Tab.vue'
87
90
  import TabGroup from './components/Tabs/TabGroup.vue'
@@ -96,6 +99,7 @@ import GlobalSearch from './components/Search/GlobalSearch.vue'
96
99
  import SearchInput from './components/Search/SearchInput.vue'
97
100
  import SearchableSelect from './components/Form/SearchableSelect.vue'
98
101
  import SelectGroup from './components/Form/SelectGroup.vue'
102
+ import Listbox from './components/Form/Listbox.vue'
99
103
  import InfiniteLoader from './components/InfiniteLoader.vue'
100
104
  import TabularExplorer from './components/TabularExplorer/TabularExplorer.vue'
101
105
  import type { UseFetchFunction } from './functions/api.types'
@@ -104,6 +108,7 @@ import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
104
108
  export { Toaster, toast } from 'vue-sonner'
105
109
 
106
110
  export * from './composables/useActiveDescendant'
111
+ export * from './composables/useDebouncedRef'
107
112
  export * from './composables/useMetrics'
108
113
  export * from './composables/useReuseType'
109
114
  export * from './composables/useTranslation'
@@ -125,6 +130,8 @@ export * from './functions/reuses'
125
130
  export * from './functions/schemas'
126
131
  export * from './functions/tabular'
127
132
  export * from './functions/users'
133
+ export * from './functions/tabularApi'
134
+ export * from './functions/charts'
128
135
  export * from './types/access_types'
129
136
 
130
137
  export type {
@@ -235,6 +242,23 @@ export type {
235
242
  ValidataError,
236
243
  Weight,
237
244
  WellType,
245
+ Chart,
246
+ ChartForm,
247
+ ChartForApi,
248
+ FilterCondition,
249
+ Filter,
250
+ AndFilters,
251
+ GenericFilter,
252
+ XAxisType,
253
+ XAxisSortBy,
254
+ SortDirection,
255
+ XAxis,
256
+ XAxisForm,
257
+ UnitPosition,
258
+ YAxis,
259
+ DataSeriesType,
260
+ DataSeries,
261
+ DataSeriesForm,
238
262
  }
239
263
 
240
264
  export {
@@ -321,6 +345,8 @@ export {
321
345
  SchemaCard,
322
346
  SimpleBanner,
323
347
  SmallChart,
348
+ ChartViewer,
349
+ ChartViewerWrapper,
324
350
  StatBox,
325
351
  OpenApiViewer,
326
352
  Tab,
@@ -337,6 +363,7 @@ export {
337
363
  SearchInput,
338
364
  SearchableSelect,
339
365
  SelectGroup,
366
+ Listbox,
340
367
  InfiniteLoader,
341
368
  TabularExplorer,
342
369
  }
@@ -0,0 +1,89 @@
1
+ import type { TabularAggregateType } from '../functions/tabularApi'
2
+ import type { Owned, OwnedWithId } from './owned'
3
+
4
+ export type FilterCondition = 'exact' | 'differs' | 'is_null' | 'is_not_null' | 'greater' | 'less' | 'strictly_greater' | 'strictly_less'
5
+ export type Filter = {
6
+ _cls: 'Filter'
7
+ column: string
8
+ condition: FilterCondition
9
+ value: string | null
10
+ }
11
+
12
+ export type AndFilters = {
13
+ _cls: 'AndFilters'
14
+ filters: Array<Filter | AndFilters>
15
+ }
16
+
17
+ export type GenericFilter = Filter | AndFilters
18
+
19
+ export type XAxisType = 'discrete' | 'continuous'
20
+
21
+ export type XAxisSortBy = 'axis_x' | 'axis_y'
22
+
23
+ export type SortDirection = 'asc' | 'desc'
24
+
25
+ export type XAxis = {
26
+ column_x: string
27
+ sort_x_by: XAxisSortBy | null
28
+ sort_x_direction: SortDirection | null
29
+ type: XAxisType
30
+ }
31
+
32
+ export type CombinedSort = '' | 'axis_x-asc' | 'axis_x-desc' | 'axis_y-asc' | 'axis_y-desc'
33
+
34
+ export type XAxisForm = Omit<XAxis, 'sort_x_by' | 'sort_x_direction'> & { sort_combined: CombinedSort }
35
+
36
+ export type UnitPosition = 'prefix' | 'suffix'
37
+
38
+ export type YAxis = {
39
+ min: number | null
40
+ max: number | null
41
+ label: string | null
42
+ unit: string | null
43
+ unit_position: UnitPosition | null
44
+ }
45
+
46
+ export type DataSeriesType = 'line' | 'histogram'
47
+
48
+ export type DataSeries = {
49
+ type: DataSeriesType
50
+ column_y: string
51
+ aggregate_y: TabularAggregateType | null
52
+ resource_id: string
53
+ column_x_name_override: string | null
54
+ filters: GenericFilter | null
55
+ }
56
+
57
+ // Type for form where aggregate_y can be empty string for select binding
58
+ export type DataSeriesForm = Omit<DataSeries, 'aggregate_y'> & { aggregate_y: TabularAggregateType | '' | null }
59
+
60
+ export type Chart = Owned & {
61
+ id: string
62
+ title: string
63
+ slug: string
64
+ description: string
65
+ private: boolean
66
+ created_at: string
67
+ last_modified: string
68
+ deleted_at: string | null
69
+ uri: string
70
+ page: string
71
+ x_axis: XAxis
72
+ y_axis: YAxis
73
+ series: Array<DataSeries>
74
+ extras: Record<string, unknown>
75
+ permissions: { delete: boolean, edit: boolean, read: boolean }
76
+ metrics: {
77
+ views: number
78
+ }
79
+ }
80
+
81
+ export type ChartForApi = OwnedWithId & Pick<Chart, 'title' | 'description' | 'private' | 'x_axis' | 'y_axis' | 'series' | 'extras'>
82
+
83
+ export type ChartForm = Omit<ChartForApi, 'x_axis' | 'series' | 'owner' | 'organization'> & {
84
+ owned: OwnedWithId
85
+ x_axis: XAxisForm
86
+ series: Array<DataSeriesForm>
87
+ chart_type?: DataSeriesType | null
88
+ filter: GenericFilter | null
89
+ }