@orbcharts/core 3.0.0-beta.1 → 3.0.0-beta.11
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/dist/orbcharts-core.es.js +3271 -2862
- package/dist/orbcharts-core.umd.js +4 -4
- package/dist/src/defaults.d.ts +24 -23
- package/dist/src/utils/d3Scale.d.ts +28 -0
- package/dist/src/utils/gridObservables.d.ts +29 -19
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/src/utils/multiGridObservables.d.ts +2 -2
- package/dist/src/utils/multiValueObservables.d.ts +73 -0
- package/dist/src/utils/orbchartsUtils.d.ts +24 -10
- package/dist/src/utils/relationshipObservables.d.ts +13 -0
- package/dist/src/utils/seriesObservables.d.ts +4 -4
- package/dist/src/utils/treeObservables.d.ts +2 -5
- package/package.json +2 -2
- package/src/GridChart.ts +2 -2
- package/src/MultiGridChart.ts +2 -2
- package/src/MultiValueChart.ts +2 -2
- package/src/RelationshipChart.ts +2 -2
- package/src/SeriesChart.ts +2 -2
- package/src/TreeChart.ts +2 -2
- package/src/base/createBaseChart.ts +13 -13
- package/src/base/validators/chartParamsValidator.ts +6 -6
- package/src/defaults.ts +87 -47
- package/src/grid/computedDataFn.ts +4 -4
- package/src/grid/contextObserverCallback.ts +58 -37
- package/src/grid/dataFormatterValidator.ts +14 -14
- package/src/multiGrid/computedDataFn.ts +2 -2
- package/src/multiValue/computedDataFn.ts +81 -147
- package/src/multiValue/contextObserverCallback.ts +150 -2
- package/src/relationship/computedDataFn.ts +94 -60
- package/src/relationship/contextObserverCallback.ts +68 -0
- package/src/tree/computedDataFn.ts +9 -10
- package/src/tree/contextObserverCallback.ts +6 -9
- package/src/utils/d3Scale.ts +198 -0
- package/src/utils/gridObservables.ts +320 -248
- package/src/utils/index.ts +1 -1
- package/src/utils/multiGridObservables.ts +75 -49
- package/src/utils/multiValueObservables.ts +662 -0
- package/src/utils/observables.ts +30 -12
- package/src/utils/orbchartsUtils.ts +90 -65
- package/src/utils/relationshipObservables.ts +85 -0
- package/src/utils/seriesObservables.ts +7 -7
- package/src/utils/treeObservables.ts +44 -33
- package/src/utils/validator.ts +5 -4
- package/dist/src/utils/d3Utils.d.ts +0 -19
- package/src/utils/d3Utils.ts +0 -108
    
        package/src/utils/observables.ts
    CHANGED
    
    | @@ -45,6 +45,15 @@ export function resizeObservable(elem: HTMLElement | Element): Observable<DOMRec | |
| 45 45 | 
             
              })
         | 
| 46 46 | 
             
            }
         | 
| 47 47 |  | 
| 48 | 
            +
            interface HighlightTargetValue {
         | 
| 49 | 
            +
              id: string | null
         | 
| 50 | 
            +
              label: string | null
         | 
| 51 | 
            +
              seriesLabel: string | null
         | 
| 52 | 
            +
              groupLabel: string | null
         | 
| 53 | 
            +
              categoryLabel: string | null
         | 
| 54 | 
            +
              highlightDefault: string | null
         | 
| 55 | 
            +
            }
         | 
| 56 | 
            +
             | 
| 48 57 | 
             
            // 通用 highlight Observable
         | 
| 49 58 | 
             
            export const highlightObservable = <T extends ChartType, D>({ datumList$, fullChartParams$, event$ }: {
         | 
| 50 59 | 
             
              datumList$: Observable<D[]>
         | 
| @@ -54,11 +63,12 @@ export const highlightObservable = <T extends ChartType, D>({ datumList$, fullCh | |
| 54 63 | 
             
              const destroy$ = new Subject()
         | 
| 55 64 |  | 
| 56 65 | 
             
              // 預設的highlight
         | 
| 57 | 
            -
              const highlightDefault | 
| 66 | 
            +
              const highlightDefault$: Observable<HighlightTargetValue> = fullChartParams$.pipe(
         | 
| 58 67 | 
             
                takeUntil(destroy$),
         | 
| 59 68 | 
             
                map(d => {
         | 
| 60 69 | 
             
                  return {
         | 
| 61 70 | 
             
                    id: null,
         | 
| 71 | 
            +
                    label: null,
         | 
| 62 72 | 
             
                    seriesLabel: null,
         | 
| 63 73 | 
             
                    groupLabel: null,
         | 
| 64 74 | 
             
                    categoryLabel: null,
         | 
| @@ -69,22 +79,25 @@ export const highlightObservable = <T extends ChartType, D>({ datumList$, fullCh | |
| 69 79 | 
             
              )
         | 
| 70 80 |  | 
| 71 81 | 
             
              // 事件觸發的highlight
         | 
| 72 | 
            -
              const highlightMouseover | 
| 82 | 
            +
              const highlightMouseover$: Observable<HighlightTargetValue> = event$.pipe(
         | 
| 73 83 | 
             
                takeUntil(destroy$),
         | 
| 74 84 | 
             
                // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
         | 
| 75 85 | 
             
                filter(d => d.eventName === 'mouseover'),
         | 
| 76 86 | 
             
                // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
         | 
| 77 | 
            -
                map( | 
| 78 | 
            -
                   | 
| 87 | 
            +
                map(_d => {
         | 
| 88 | 
            +
                  const d = _d as any
         | 
| 89 | 
            +
                  return d.datum
         | 
| 79 90 | 
             
                    ? {
         | 
| 80 | 
            -
                      id:  | 
| 81 | 
            -
                       | 
| 82 | 
            -
                       | 
| 83 | 
            -
                       | 
| 91 | 
            +
                      id: d.datum.id,
         | 
| 92 | 
            +
                      label: d.datum.label,
         | 
| 93 | 
            +
                      seriesLabel: d.datum.seriesLabel,
         | 
| 94 | 
            +
                      groupLabel: d.datum.groupLabel,
         | 
| 95 | 
            +
                      categoryLabel: d.datum.categoryLabel,
         | 
| 84 96 | 
             
                      highlightDefault: null
         | 
| 85 97 | 
             
                    }
         | 
| 86 98 | 
             
                    : {
         | 
| 87 99 | 
             
                      id: null,
         | 
| 100 | 
            +
                      label: null,
         | 
| 88 101 | 
             
                      seriesLabel: null,
         | 
| 89 102 | 
             
                      groupLabel: null,
         | 
| 90 103 | 
             
                      categoryLabel: null,
         | 
| @@ -102,9 +115,14 @@ export const highlightObservable = <T extends ChartType, D>({ datumList$, fullCh | |
| 102 115 | 
             
                switchMap(d => highlightDefault$)
         | 
| 103 116 | 
             
              )
         | 
| 104 117 |  | 
| 105 | 
            -
              function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null) {
         | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 118 | 
            +
              // function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null) {
         | 
| 119 | 
            +
              //   const datum = datumList.find(d => (d as ComputedDatumBase).id === id)
         | 
| 120 | 
            +
              //   return datum ? [datum] : []
         | 
| 121 | 
            +
              // }
         | 
| 122 | 
            +
              function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null, label: string | null) {
         | 
| 123 | 
            +
                return id == null && label == null
         | 
| 124 | 
            +
                  ? []
         | 
| 125 | 
            +
                  : datumList.filter(d => (d as ComputedDatumBase).id === id || (d as ComputedDatumBase).label === label)
         | 
| 108 126 | 
             
              }
         | 
| 109 127 |  | 
| 110 128 | 
             
              function getSeriesIds (datumList: ComputedDatumTypeMap<T>[], seriesLabel: string | null) {
         | 
| @@ -136,7 +154,7 @@ export const highlightObservable = <T extends ChartType, D>({ datumList$, fullCh | |
| 136 154 | 
             
                ).subscribe(data => {
         | 
| 137 155 | 
             
                  let datumList: ComputedDatumTypeMap<T>[] = []
         | 
| 138 156 | 
             
                  if (data.fullChartParams.highlightTarget === 'datum') {
         | 
| 139 | 
            -
                    datumList = getDatumIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.id)
         | 
| 157 | 
            +
                    datumList = getDatumIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.id, data.target.label)
         | 
| 140 158 | 
             
                  } else if (data.fullChartParams.highlightTarget === 'series') {
         | 
| 141 159 | 
             
                    datumList = getSeriesIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.seriesLabel)
         | 
| 142 160 | 
             
                  } else if (data.fullChartParams.highlightTarget === 'group') {
         | 
| @@ -13,11 +13,12 @@ import type { | |
| 13 13 | 
             
              DataMultiValue,
         | 
| 14 14 | 
             
              DataMultiValueDatum,
         | 
| 15 15 | 
             
              DataMultiValueValue,
         | 
| 16 | 
            +
              ComputedLayoutDatumMultiValue,
         | 
| 16 17 | 
             
              DataFormatterContainer,
         | 
| 17 18 | 
             
              SeriesDirection,
         | 
| 18 19 | 
             
              DataFormatterGridGrid,
         | 
| 19 | 
            -
               | 
| 20 | 
            -
               | 
| 20 | 
            +
              ContainerPosition,
         | 
| 21 | 
            +
              ContainerPositionScaled,
         | 
| 21 22 | 
             
              Layout
         | 
| 22 23 | 
             
            } from '../../lib/core-types'
         | 
| 23 24 | 
             
            import { isPlainObject } from './commonUtils'
         | 
| @@ -29,8 +30,11 @@ export function formatValueToLabel (value: any, valueFormatter: string | ((text: | |
| 29 30 | 
             
              return d3.format(valueFormatter as string)!(value)
         | 
| 30 31 | 
             
            }
         | 
| 31 32 |  | 
| 32 | 
            -
            export function createDefaultDatumId (chartTypeOrPrefix: string, levelOneIndex: number, levelTwoIndex | 
| 33 | 
            -
              let text = `${chartTypeOrPrefix}_${levelOneIndex} | 
| 33 | 
            +
            export function createDefaultDatumId (chartTypeOrPrefix: string, levelOneIndex: number, levelTwoIndex?: number, levelThreeIndex?: number) {
         | 
| 34 | 
            +
              let text = `${chartTypeOrPrefix}_${levelOneIndex}`
         | 
| 35 | 
            +
              if (levelTwoIndex != null) {
         | 
| 36 | 
            +
                text += `_${levelTwoIndex}`
         | 
| 37 | 
            +
              }
         | 
| 34 38 | 
             
              if (levelThreeIndex != null) {
         | 
| 35 39 | 
             
                text += `_${levelThreeIndex}`
         | 
| 36 40 | 
             
              }
         | 
| @@ -45,6 +49,11 @@ export function createDefaultGroupLabel (chartTypeOrPrefix: string, groupIndex: | |
| 45 49 | 
             
              return `${chartTypeOrPrefix}_group${groupIndex}`
         | 
| 46 50 | 
             
            }
         | 
| 47 51 |  | 
| 52 | 
            +
            export function createDefaultCategoryLabel () {
         | 
| 53 | 
            +
              // return `${chartTypeOrPrefix}_category`
         | 
| 54 | 
            +
              return '' // 空值
         | 
| 55 | 
            +
            }
         | 
| 56 | 
            +
             | 
| 48 57 | 
             
            export function createGridSeriesLabels ({ transposedDataGrid, dataFormatterGrid, chartType = 'grid' }: {
         | 
| 49 58 | 
             
              transposedDataGrid: DataGridDatum[][],
         | 
| 50 59 | 
             
              dataFormatterGrid: DataFormatterGridGrid
         | 
| @@ -114,76 +123,125 @@ export function createMultiGridGroupLabels ({ transposedDataGrid, dataFormatterG | |
| 114 123 | 
             
            }
         | 
| 115 124 |  | 
| 116 125 | 
             
            // 取得最小及最大值 - 數字陣列
         | 
| 117 | 
            -
            export function  | 
| 118 | 
            -
              const  | 
| 126 | 
            +
            export function getMinMax (data: number[]): [number, number] {
         | 
| 127 | 
            +
              const defaultMinMax: [number, number] = [0, 0] // default
         | 
| 119 128 | 
             
              if (!data.length) {
         | 
| 120 | 
            -
                return  | 
| 129 | 
            +
                return defaultMinMax
         | 
| 121 130 | 
             
              }
         | 
| 122 | 
            -
              const  | 
| 131 | 
            +
              const minMax: [number, number] = data.reduce((prev, current) => {
         | 
| 123 132 | 
             
                // [min, max]
         | 
| 124 133 | 
             
                return [
         | 
| 125 134 | 
             
                  current < prev[0] ? current : prev[0],
         | 
| 126 135 | 
             
                  current > prev[1] ? current : prev[1]
         | 
| 127 136 | 
             
                ]
         | 
| 128 137 | 
             
              }, [data[0], data[0]])
         | 
| 129 | 
            -
              return  | 
| 138 | 
            +
              return minMax
         | 
| 130 139 | 
             
            }
         | 
| 131 140 |  | 
| 132 141 | 
             
            // 取得最小及最大值 - datum格式陣列資料
         | 
| 133 | 
            -
            export function  | 
| 142 | 
            +
            export function getMinMaxValue (data: DatumValue[]): [number, number] {
         | 
| 134 143 | 
             
              const arr = data
         | 
| 135 144 | 
             
                .filter(d => d != null && d.value != null)
         | 
| 136 145 | 
             
                .map(d => d.value )
         | 
| 137 | 
            -
              return  | 
| 146 | 
            +
              return getMinMax(arr)
         | 
| 138 147 | 
             
            }
         | 
| 139 148 |  | 
| 140 149 | 
             
            // 取得最小及最大值 - Series Data
         | 
| 141 | 
            -
            export function  | 
| 150 | 
            +
            export function getMinMaxSeries (data: DataSeries): [number, number] {
         | 
| 142 151 | 
             
              const flatData: (DataSeriesValue | DataSeriesDatum)[] = data[0] && Array.isArray((data as (DataSeriesValue | DataSeriesDatum)[][])[0])
         | 
| 143 152 | 
             
                ? data.flat()
         | 
| 144 153 | 
             
                : data as (DataSeriesValue | DataSeriesDatum)[]
         | 
| 145 154 | 
             
              const arr = flatData
         | 
| 146 155 | 
             
                .filter(d => (d == null || (isPlainObject(d) && (d as DataSeriesDatum).value == null)) === false) // 過濾掉null &
         | 
| 147 156 | 
             
                .map(d => typeof d === 'number' ? d : d.value )
         | 
| 148 | 
            -
              return  | 
| 157 | 
            +
              return getMinMax(arr)
         | 
| 149 158 | 
             
            }
         | 
| 150 159 |  | 
| 151 160 | 
             
            // 取得最小及最大值 - Grid Data
         | 
| 152 | 
            -
            export function  | 
| 161 | 
            +
            export function getMinMaxGrid (data: DataGrid): [number, number] {
         | 
| 153 162 | 
             
              const flatData: (DataGridValue | DataGridDatum)[] = data.flat()
         | 
| 154 163 | 
             
              const arr = flatData
         | 
| 155 164 | 
             
                .filter(d => (d == null || (isPlainObject(d) && (d as DataGridDatum).value == null)) === false) // 過濾掉null
         | 
| 156 165 | 
             
                .map(d => typeof d === 'number' ? d : d.value )
         | 
| 157 | 
            -
              return  | 
| 166 | 
            +
              return getMinMax(arr)
         | 
| 158 167 | 
             
            }
         | 
| 159 168 |  | 
| 160 169 | 
             
            // 取得最小及最大值 - MultiGrid Data
         | 
| 161 | 
            -
            export function  | 
| 170 | 
            +
            export function getMinMaxMultiGrid (data: DataMultiGrid): [number, number] {
         | 
| 162 171 | 
             
              const flatData: (DataGridValue | DataGridDatum)[] = data.flat().flat()
         | 
| 163 172 | 
             
              const arr = flatData
         | 
| 164 173 | 
             
                .filter(d => (d == null || (isPlainObject(d) && (d as DataGridDatum).value == null)) === false) // 過濾掉null
         | 
| 165 174 | 
             
                .map(d => typeof d === 'number' ? d : d.value )
         | 
| 166 | 
            -
              return  | 
| 175 | 
            +
              return getMinMax(arr)
         | 
| 167 176 | 
             
            }
         | 
| 168 177 |  | 
| 169 178 | 
             
            // 取得最小及最大值 - MultiValue Data
         | 
| 170 | 
            -
            export function  | 
| 171 | 
            -
              const  | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 179 | 
            +
            export function getMinMaxMultiValue (data: DataMultiValue, valueIndex: number): [number, number] {
         | 
| 180 | 
            +
              const arr: number[] = data
         | 
| 181 | 
            +
                .map(d => {
         | 
| 182 | 
            +
                  if (Array.isArray(d)) {
         | 
| 183 | 
            +
                    return d[valueIndex] ?? null
         | 
| 184 | 
            +
                  } else if (isPlainObject(d)) {
         | 
| 185 | 
            +
                    return (d as DataMultiValueDatum).value[valueIndex] ?? null
         | 
| 186 | 
            +
                  } else {
         | 
| 187 | 
            +
                    return null
         | 
| 188 | 
            +
                  }
         | 
| 189 | 
            +
                })
         | 
| 190 | 
            +
                .filter(d => d != null)
         | 
| 191 | 
            +
              return getMinMax(arr)
         | 
| 192 | 
            +
            }
         | 
| 193 | 
            +
             | 
| 194 | 
            +
            export function getMinMaxMultiValueXY ({ data, minX, maxX, minY, maxY }: {
         | 
| 195 | 
            +
              data: ComputedLayoutDatumMultiValue[][]
         | 
| 196 | 
            +
              minX: number
         | 
| 197 | 
            +
              maxX: number
         | 
| 198 | 
            +
              minY: number
         | 
| 199 | 
            +
              maxY: number
         | 
| 200 | 
            +
            }) {
         | 
| 201 | 
            +
              let filteredData: ComputedLayoutDatumMultiValue[][] = []
         | 
| 202 | 
            +
              let minXDatum: ComputedLayoutDatumMultiValue | null = null
         | 
| 203 | 
            +
              let maxXDatum: ComputedLayoutDatumMultiValue | null = null
         | 
| 204 | 
            +
              let minYDatum: ComputedLayoutDatumMultiValue | null = null
         | 
| 205 | 
            +
              let maxYDatum: ComputedLayoutDatumMultiValue | null = null
         | 
| 206 | 
            +
              
         | 
| 207 | 
            +
              for (let categoryData of data) {
         | 
| 208 | 
            +
                for (let datum of categoryData) {
         | 
| 209 | 
            +
                  if (datum.axisX >= minX && datum.axisX <= maxX && datum.axisY >= minY && datum.axisY <= maxY) {
         | 
| 210 | 
            +
                    filteredData.push(categoryData)
         | 
| 211 | 
            +
                    if (minXDatum == null || datum.axisX < minXDatum.axisX) {
         | 
| 212 | 
            +
                      minXDatum = datum
         | 
| 213 | 
            +
                    }
         | 
| 214 | 
            +
                    if (maxXDatum == null || datum.axisX > maxXDatum.axisX) {
         | 
| 215 | 
            +
                      maxXDatum = datum
         | 
| 216 | 
            +
                    }
         | 
| 217 | 
            +
                    if (minYDatum == null || datum.axisY < minYDatum.axisY) {
         | 
| 218 | 
            +
                      minYDatum = datum
         | 
| 219 | 
            +
                    }
         | 
| 220 | 
            +
                    if (maxYDatum == null || datum.axisY > maxYDatum.axisY) {
         | 
| 221 | 
            +
                      maxYDatum = datum
         | 
| 222 | 
            +
                    }
         | 
| 223 | 
            +
                  }
         | 
| 224 | 
            +
                }
         | 
| 225 | 
            +
              }
         | 
| 226 | 
            +
             | 
| 227 | 
            +
              return {
         | 
| 228 | 
            +
                minXDatum,
         | 
| 229 | 
            +
                maxXDatum,
         | 
| 230 | 
            +
                minYDatum,
         | 
| 231 | 
            +
                maxYDatum,
         | 
| 232 | 
            +
                filteredData
         | 
| 233 | 
            +
              }
         | 
| 176 234 | 
             
            }
         | 
| 177 235 |  | 
| 178 236 | 
             
            // @Q@ 待處理
         | 
| 179 237 | 
             
            // // 取得最小及最大值 - Relationship Data
         | 
| 180 | 
            -
            // export function  | 
| 238 | 
            +
            // export function getMinMaxRelationship (data: DataRelationship, target: 'nodes' | 'edges' = 'nodes'): [number, number] {
         | 
| 181 239 |  | 
| 182 240 | 
             
            // }
         | 
| 183 241 |  | 
| 184 242 | 
             
            // @Q@ 待處理
         | 
| 185 243 | 
             
            // // 取得最小及最大值 - Tree Data
         | 
| 186 | 
            -
            // export function  | 
| 244 | 
            +
            // export function getMinMaxTree (data: DataTree): [number, number] {
         | 
| 187 245 |  | 
| 188 246 | 
             
            // }
         | 
| 189 247 |  | 
| @@ -213,31 +271,13 @@ export function transposeData<T> (seriesDirection: SeriesDirection, data: T[][]) | |
| 213 271 |  | 
| 214 272 |  | 
| 215 273 | 
             
            export function seriesColorPredicate (seriesIndex: number, chartParams: ChartParams) {
         | 
| 216 | 
            -
              return seriesIndex < chartParams.colors[chartParams.colorScheme]. | 
| 217 | 
            -
                ? chartParams.colors[chartParams.colorScheme]. | 
| 218 | 
            -
                : chartParams.colors[chartParams.colorScheme]. | 
| 219 | 
            -
                  seriesIndex % chartParams.colors[chartParams.colorScheme]. | 
| 274 | 
            +
              return seriesIndex < chartParams.colors[chartParams.colorScheme].label.length
         | 
| 275 | 
            +
                ? chartParams.colors[chartParams.colorScheme].label[seriesIndex]
         | 
| 276 | 
            +
                : chartParams.colors[chartParams.colorScheme].label[
         | 
| 277 | 
            +
                  seriesIndex % chartParams.colors[chartParams.colorScheme].label.length
         | 
| 220 278 | 
             
                ]
         | 
| 221 279 | 
             
            }
         | 
| 222 280 |  | 
| 223 | 
            -
            // export function calcSeriesContainerPosition (layout: Layout, container: DataFormatterContainer, rowIndex: number, columnIndex: number) {
         | 
| 224 | 
            -
            //   const { gap, rowAmount, columnAmount } = container
         | 
| 225 | 
            -
            //   const width = (layout.width - (gap * (columnAmount - 1))) / columnAmount
         | 
| 226 | 
            -
            //   const height = (layout.height - (gap * (rowAmount - 1))) / rowAmount
         | 
| 227 | 
            -
            //   const x = columnIndex * width + (columnIndex * gap)
         | 
| 228 | 
            -
            //   const y = rowIndex * height + (rowIndex * gap)
         | 
| 229 | 
            -
            //   // const translate: [number, number] = [x, y]
         | 
| 230 | 
            -
              
         | 
| 231 | 
            -
            //   return {
         | 
| 232 | 
            -
            //     // translate,
         | 
| 233 | 
            -
            //     startX: x,
         | 
| 234 | 
            -
            //     startY: y,
         | 
| 235 | 
            -
            //     centerX: x + width / 2,
         | 
| 236 | 
            -
            //     centerY: y + height / 2,
         | 
| 237 | 
            -
            //     width,
         | 
| 238 | 
            -
            //     height
         | 
| 239 | 
            -
            //   }
         | 
| 240 | 
            -
            // }
         | 
| 241 281 |  | 
| 242 282 | 
             
            // 計算預設欄列數量
         | 
| 243 283 | 
             
            // 規則1.rowAmount*columnAmount要大於或等於amount,並且數字要盡可能小
         | 
| @@ -251,7 +291,7 @@ function calcGridDimensions (amount: number): { rowAmount: number; columnAmount: | |
| 251 291 | 
             
              return { rowAmount, columnAmount }
         | 
| 252 292 | 
             
            }
         | 
| 253 293 |  | 
| 254 | 
            -
            export function calcSeriesContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number):  | 
| 294 | 
            +
            export function calcSeriesContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): ContainerPosition[] {
         | 
| 255 295 | 
             
              const { gap } = container
         | 
| 256 296 | 
             
              const { rowAmount, columnAmount } = (container.rowAmount * container.columnAmount) >= amount
         | 
| 257 297 | 
             
                // 如果container設定的rowAmount和columnAmount的乘積大於或等於amount,則使用目前設定
         | 
| @@ -284,22 +324,7 @@ export function calcSeriesContainerLayout (layout: Layout, container: DataFormat | |
| 284 324 | 
             
              })
         | 
| 285 325 | 
             
            }
         | 
| 286 326 |  | 
| 287 | 
            -
             | 
| 288 | 
            -
            //   const { gap, rowAmount, columnAmount } = container
         | 
| 289 | 
            -
            //   const width = (layout.width - (gap * (columnAmount - 1))) / columnAmount
         | 
| 290 | 
            -
            //   const height = (layout.height - (gap * (rowAmount - 1))) / rowAmount
         | 
| 291 | 
            -
            //   const x = columnIndex * width + (columnIndex * gap)
         | 
| 292 | 
            -
            //   const y = rowIndex * height + (rowIndex * gap)
         | 
| 293 | 
            -
            //   const translate: [number, number] = [x, y]
         | 
| 294 | 
            -
            //   const scale: [number, number] = [width / layout.width, height / layout.height]
         | 
| 295 | 
            -
             | 
| 296 | 
            -
            //   return {
         | 
| 297 | 
            -
            //     translate,
         | 
| 298 | 
            -
            //     scale
         | 
| 299 | 
            -
            //   }
         | 
| 300 | 
            -
            // }
         | 
| 301 | 
            -
             | 
| 302 | 
            -
            export function calcGridContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): GridContainerPosition[] {
         | 
| 327 | 
            +
            export function calcGridContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): ContainerPositionScaled[] {
         | 
| 303 328 | 
             
              const { gap } = container
         | 
| 304 329 | 
             
              const { rowAmount, columnAmount } = (container.rowAmount * container.columnAmount) >= amount
         | 
| 305 330 | 
             
                // 如果container設定的rowAmount和columnAmount的乘積大於或等於amount,則使用目前設定
         | 
| @@ -318,7 +343,7 @@ export function calcGridContainerLayout (layout: Layout, container: DataFormatte | |
| 318 343 | 
             
                const translate: [number, number] = [x, y]
         | 
| 319 344 | 
             
                const scale: [number, number] = [width / layout.width, height / layout.height]
         | 
| 320 345 |  | 
| 321 | 
            -
                return {
         | 
| 346 | 
            +
                return {                      
         | 
| 322 347 | 
             
                  slotIndex: index,
         | 
| 323 348 | 
             
                  rowIndex,
         | 
| 324 349 | 
             
                  columnIndex,
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            import {
         | 
| 2 | 
            +
              combineLatest,
         | 
| 3 | 
            +
              distinctUntilChanged,
         | 
| 4 | 
            +
              filter,
         | 
| 5 | 
            +
              map,
         | 
| 6 | 
            +
              merge,
         | 
| 7 | 
            +
              takeUntil,
         | 
| 8 | 
            +
              shareReplay,
         | 
| 9 | 
            +
              switchMap,
         | 
| 10 | 
            +
              Subject,
         | 
| 11 | 
            +
              Observable } from 'rxjs'
         | 
| 12 | 
            +
            import type {
         | 
| 13 | 
            +
              ChartParams,
         | 
| 14 | 
            +
              ComputedDataRelationship,
         | 
| 15 | 
            +
              ComputedDataTypeMap,
         | 
| 16 | 
            +
              ComputedNode,
         | 
| 17 | 
            +
              ComputedEdge,
         | 
| 18 | 
            +
              DataFormatterTree } from '../../lib/core-types'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
            export const categoryLabelsObservable = (
         | 
| 22 | 
            +
              CategoryNodeMap$: Observable<Map<string, ComputedNode[]>>,
         | 
| 23 | 
            +
              CategoryEdgeMap$: Observable<Map<string, ComputedEdge[]>>
         | 
| 24 | 
            +
            ): Observable<string[]> => {
         | 
| 25 | 
            +
              return combineLatest({
         | 
| 26 | 
            +
                CategoryNodeMap: CategoryNodeMap$,
         | 
| 27 | 
            +
                CategoryEdgeMap: CategoryEdgeMap$
         | 
| 28 | 
            +
              }).pipe(
         | 
| 29 | 
            +
                switchMap(async d => d),
         | 
| 30 | 
            +
                map(data => {
         | 
| 31 | 
            +
                  return [...Array.from(data.CategoryNodeMap.keys()), ...Array.from(data.CategoryEdgeMap.keys())]
         | 
| 32 | 
            +
                }),
         | 
| 33 | 
            +
                distinctUntilChanged((a, b) => {
         | 
| 34 | 
            +
                  return JSON.stringify(a).length === JSON.stringify(b).length
         | 
| 35 | 
            +
                }),
         | 
| 36 | 
            +
              )
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            export const NodeMapObservable = (computedData$: Observable<ComputedDataRelationship>) => {
         | 
| 40 | 
            +
              return computedData$.pipe(
         | 
| 41 | 
            +
                map(data => {
         | 
| 42 | 
            +
                  const nodeMap = new Map<string, ComputedNode>()
         | 
| 43 | 
            +
                  data.nodes.forEach(node => {
         | 
| 44 | 
            +
                    nodeMap.set(node.id, node)
         | 
| 45 | 
            +
                  })
         | 
| 46 | 
            +
                  return nodeMap
         | 
| 47 | 
            +
                }),
         | 
| 48 | 
            +
              )
         | 
| 49 | 
            +
            }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            export const EdgeMapObservable = (computedData$: Observable<ComputedDataRelationship>) => {
         | 
| 52 | 
            +
              return computedData$.pipe(
         | 
| 53 | 
            +
                map(data => {
         | 
| 54 | 
            +
                  const edgeMap = new Map<string, ComputedEdge>()
         | 
| 55 | 
            +
                  data.edges.forEach(edge => {
         | 
| 56 | 
            +
                    edgeMap.set(edge.id, edge)
         | 
| 57 | 
            +
                  })
         | 
| 58 | 
            +
                  return edgeMap
         | 
| 59 | 
            +
                }),
         | 
| 60 | 
            +
              )
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            // 所有可見的節點
         | 
| 64 | 
            +
            export const relationshipVisibleComputedDataObservable = ({ computedData$, NodeMap$ }: {
         | 
| 65 | 
            +
              computedData$: Observable<ComputedDataRelationship>
         | 
| 66 | 
            +
              NodeMap$: Observable<Map<string, ComputedNode>>
         | 
| 67 | 
            +
            }) => {
         | 
| 68 | 
            +
              return combineLatest({
         | 
| 69 | 
            +
                computedData: computedData$,
         | 
| 70 | 
            +
                NodeMap: NodeMap$
         | 
| 71 | 
            +
              }).pipe(
         | 
| 72 | 
            +
                switchMap(async d => d),
         | 
| 73 | 
            +
                map(data => {
         | 
| 74 | 
            +
                  return {
         | 
| 75 | 
            +
                    nodes: data.computedData.nodes.filter(node => node.visible),
         | 
| 76 | 
            +
                    edges: data.computedData.edges
         | 
| 77 | 
            +
                      .filter(edge => edge.visible)
         | 
| 78 | 
            +
                      // 依照節點是否存在篩選
         | 
| 79 | 
            +
                      .filter(edge => {
         | 
| 80 | 
            +
                        return data.NodeMap.has(edge.startNode.id) && data.NodeMap.has(edge.endNode.id)
         | 
| 81 | 
            +
                      })
         | 
| 82 | 
            +
                  }
         | 
| 83 | 
            +
                })
         | 
| 84 | 
            +
              )
         | 
| 85 | 
            +
            }
         | 
| @@ -14,7 +14,7 @@ import type { | |
| 14 14 | 
             
              ComputedDatumSeries,
         | 
| 15 15 | 
             
              ComputedDataTypeMap,
         | 
| 16 16 | 
             
              DataFormatterTypeMap,
         | 
| 17 | 
            -
               | 
| 17 | 
            +
              ContainerPosition,
         | 
| 18 18 | 
             
              Layout } from '../../lib/core-types'
         | 
| 19 19 | 
             
            import { calcSeriesContainerLayout } from './orbchartsUtils'
         | 
| 20 20 |  | 
| @@ -97,7 +97,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma | |
| 97 97 | 
             
              computedData$: Observable<ComputedDataTypeMap<'series'>>
         | 
| 98 98 | 
             
              fullDataFormatter$: Observable<DataFormatterTypeMap<'series'>>
         | 
| 99 99 | 
             
              layout$: Observable<Layout>
         | 
| 100 | 
            -
            }): Observable< | 
| 100 | 
            +
            }): Observable<ContainerPosition[]> => {
         | 
| 101 101 |  | 
| 102 102 | 
             
              const gridContainerPosition$ = combineLatest({
         | 
| 103 103 | 
             
                computedData: computedData$,
         | 
| @@ -113,7 +113,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma | |
| 113 113 | 
             
                    // return data.computedData.map((seriesData, seriesIndex) => {
         | 
| 114 114 | 
             
                    //   const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
         | 
| 115 115 | 
             
                    //   const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
         | 
| 116 | 
            -
                    //   const { startX, startY, centerX, centerY, width, height } =  | 
| 116 | 
            +
                    //   const { startX, startY, centerX, centerY, width, height } = calcContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
         | 
| 117 117 | 
             
                    //   return {
         | 
| 118 118 | 
             
                    //     slotIndex: seriesIndex,
         | 
| 119 119 | 
             
                    //     rowIndex,
         | 
| @@ -132,7 +132,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma | |
| 132 132 | 
             
                    // const columnIndex = 0
         | 
| 133 133 | 
             
                    // const rowIndex = 0
         | 
| 134 134 | 
             
                    // return data.computedData.map((seriesData, seriesIndex) => {
         | 
| 135 | 
            -
                    //   const { startX, startY, centerX, centerY, width, height } =  | 
| 135 | 
            +
                    //   const { startX, startY, centerX, centerY, width, height } = calcContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
         | 
| 136 136 | 
             
                    //   return {
         | 
| 137 137 | 
             
                    //     slotIndex: 0,
         | 
| 138 138 | 
             
                    //     rowIndex,
         | 
| @@ -153,7 +153,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma | |
| 153 153 | 
             
            }
         | 
| 154 154 |  | 
| 155 155 | 
             
            export const seriesContainerPositionMapObservable = ({ seriesContainerPosition$, seriesLabels$, separateSeries$ }: {
         | 
| 156 | 
            -
              seriesContainerPosition$: Observable< | 
| 156 | 
            +
              seriesContainerPosition$: Observable<ContainerPosition[]>
         | 
| 157 157 | 
             
              seriesLabels$: Observable<string[]>
         | 
| 158 158 | 
             
              separateSeries$: Observable<boolean>
         | 
| 159 159 | 
             
            }) => {
         | 
| @@ -165,10 +165,10 @@ export const seriesContainerPositionMapObservable = ({ seriesContainerPosition$, | |
| 165 165 | 
             
                switchMap(async (d) => d),
         | 
| 166 166 | 
             
                map(data => {
         | 
| 167 167 | 
             
                  return data.separateSeries
         | 
| 168 | 
            -
                    ? new Map<string,  | 
| 168 | 
            +
                    ? new Map<string, ContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
         | 
| 169 169 | 
             
                      return [seriesLabel, data.seriesContainerPosition[seriesIndex] ?? data.seriesContainerPosition[0]]
         | 
| 170 170 | 
             
                    }))
         | 
| 171 | 
            -
                    : new Map<string,  | 
| 171 | 
            +
                    : new Map<string, ContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
         | 
| 172 172 | 
             
                      return [seriesLabel, data.seriesContainerPosition[0]]
         | 
| 173 173 | 
             
                    }))
         | 
| 174 174 | 
             
                })
         | 
| @@ -34,42 +34,53 @@ export const nodeListObservable = ({ computedData$ }: { computedData$: Observabl | |
| 34 34 | 
             
              )
         | 
| 35 35 | 
             
            }
         | 
| 36 36 |  | 
| 37 | 
            -
            export const  | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
            }) => {
         | 
| 37 | 
            +
            // export const categoryLabelsObservable = ({ nodeList$, fullDataFormatter$ }: {
         | 
| 38 | 
            +
            //   nodeList$: Observable<ComputedDataTree[]>
         | 
| 39 | 
            +
            //   fullDataFormatter$: Observable<DataFormatterTree>
         | 
| 40 | 
            +
            // }) => {
         | 
| 41 41 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 42 | 
            +
            //   const categoryLabels$ = fullDataFormatter$.pipe(
         | 
| 43 | 
            +
            //     map(d => d.categoryLabels),
         | 
| 44 | 
            +
            //     distinctUntilChanged((a, b) => {
         | 
| 45 | 
            +
            //       return JSON.stringify(a).length === JSON.stringify(b).length
         | 
| 46 | 
            +
            //     }),
         | 
| 47 | 
            +
            //   )
         | 
| 48 48 |  | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 49 | 
            +
            //   return combineLatest({
         | 
| 50 | 
            +
            //     nodeList: nodeList$,
         | 
| 51 | 
            +
            //     categoryLabels: categoryLabels$
         | 
| 52 | 
            +
            //   }).pipe(
         | 
| 53 | 
            +
            //     switchMap(async d => d),
         | 
| 54 | 
            +
            //     map(data => {
         | 
| 55 | 
            +
            //       const CurrentLabelSet = new Set(data.categoryLabels)
         | 
| 56 | 
            +
            //       const ExistLabelSet = new Set(
         | 
| 57 | 
            +
            //         data.nodeList.filter(node => node.visible).map(node => node.categoryLabel)
         | 
| 58 | 
            +
            //       )
         | 
| 59 | 
            +
            //       // 加入已存在的label(data.nodeList有,但是dataFormatter.categoryLabels沒有)
         | 
| 60 | 
            +
            //       Array.from(ExistLabelSet).forEach(label => {
         | 
| 61 | 
            +
            //         if (!CurrentLabelSet.has(label)) {
         | 
| 62 | 
            +
            //           CurrentLabelSet.add(label)
         | 
| 63 | 
            +
            //         }
         | 
| 64 | 
            +
            //       })
         | 
| 65 | 
            +
            //       // 移除不存在的label(dataFormatter.categoryLabels有,但是data.nodeList沒有)
         | 
| 66 | 
            +
            //       Array.from(CurrentLabelSet).forEach(label => {
         | 
| 67 | 
            +
            //         if (!ExistLabelSet.has(label)) {
         | 
| 68 | 
            +
            //           ExistLabelSet.delete(label)
         | 
| 69 | 
            +
            //         }
         | 
| 70 | 
            +
            //       })
         | 
| 71 71 |  | 
| 72 | 
            -
             | 
| 72 | 
            +
            //       return Array.from(CurrentLabelSet)
         | 
| 73 | 
            +
            //     }),
         | 
| 74 | 
            +
            //     distinctUntilChanged((a, b) => {
         | 
| 75 | 
            +
            //       return JSON.stringify(a).length === JSON.stringify(b).length
         | 
| 76 | 
            +
            //     }),
         | 
| 77 | 
            +
            //   )
         | 
| 78 | 
            +
            // }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            export const categoryLabelsObservable = (CategoryDataMap$: Observable<Map<string, ComputedDataTree[]>>) => {
         | 
| 81 | 
            +
              return CategoryDataMap$.pipe(
         | 
| 82 | 
            +
                map(data => {
         | 
| 83 | 
            +
                  return Array.from(data.keys())
         | 
| 73 84 | 
             
                }),
         | 
| 74 85 | 
             
                distinctUntilChanged((a, b) => {
         | 
| 75 86 | 
             
                  return JSON.stringify(a).length === JSON.stringify(b).length
         | 
    
        package/src/utils/validator.ts
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            import type {
         | 
| 2 | 
            +
              ColorType,
         | 
| 2 3 | 
             
              ValidatorResult,
         | 
| 3 4 | 
             
              ToBeTypes,
         | 
| 4 5 | 
             
              ToBeOption,
         | 
| @@ -40,14 +41,14 @@ function getInvalidColumn<T> (data: T, rules: Partial<ValidatorRule<T>>) { | |
| 40 41 | 
             
              }
         | 
| 41 42 | 
             
              // "toBeOption" 的測試
         | 
| 42 43 | 
             
              const testOption: {[key in ToBeOption]: (value: any) => boolean} = {
         | 
| 43 | 
            -
                ColorType: (value:  | 
| 44 | 
            +
                ColorType: (value: ColorType) => {
         | 
| 44 45 | 
             
                  return value === 'none'
         | 
| 45 | 
            -
                    || value === ' | 
| 46 | 
            +
                    || value === 'label'
         | 
| 46 47 | 
             
                    || value === 'primary'
         | 
| 47 48 | 
             
                    || value === 'secondary'
         | 
| 48 | 
            -
                    || value === ' | 
| 49 | 
            +
                    || value === 'labelContrast'
         | 
| 49 50 | 
             
                    || value === 'background'
         | 
| 50 | 
            -
                }
         | 
| 51 | 
            +
                },
         | 
| 51 52 | 
             
              }
         | 
| 52 53 |  | 
| 53 54 | 
             
              const failColumn = Object.keys(data).find((columnName: string) => {
         | 
| @@ -1,19 +0,0 @@ | |
| 1 | 
            -
            import * as d3 from 'd3';
         | 
| 2 | 
            -
            export declare const createAxisLinearScale: ({ maxValue, minValue, axisWidth, scaleDomain, scaleRange, }: {
         | 
| 3 | 
            -
                maxValue: number;
         | 
| 4 | 
            -
                minValue: number;
         | 
| 5 | 
            -
                axisWidth: number;
         | 
| 6 | 
            -
                scaleDomain: [number | "min" | "auto", number | "max" | "auto"];
         | 
| 7 | 
            -
                scaleRange: [number, number];
         | 
| 8 | 
            -
            }) => d3.ScaleLinear<number, number, never>;
         | 
| 9 | 
            -
            export declare const createAxisPointScale: ({ axisLabels, axisWidth, padding }: {
         | 
| 10 | 
            -
                axisLabels: string[];
         | 
| 11 | 
            -
                axisWidth: number;
         | 
| 12 | 
            -
                padding?: number;
         | 
| 13 | 
            -
            }) => d3.ScalePoint<string>;
         | 
| 14 | 
            -
            export declare const createAxisQuantizeScale: ({ axisLabels, axisWidth, padding, reverse }: {
         | 
| 15 | 
            -
                axisLabels: string[] | Date[];
         | 
| 16 | 
            -
                axisWidth: number;
         | 
| 17 | 
            -
                padding?: number;
         | 
| 18 | 
            -
                reverse?: boolean;
         | 
| 19 | 
            -
            }) => d3.ScaleQuantize<number, never>;
         |