1- import { group } from "d3-array" ;
1+ import { group , groups } from "d3-array" ;
22import { create } from "d3-selection" ;
33import { Mark } from "../mark.js" ;
44import { autoScaleRange } from "../scales.js" ;
55
6- // TODO facet-x
7- export class Facet extends Mark {
8- constructor ( data , { y, transform} = { } , marks = [ ] ) {
6+ class Facet extends Mark {
7+ constructor ( data , { x, y, transform} = { } , marks = [ ] ) {
98 super (
109 data ,
1110 [
12- { name : "fy" , value : y , scale : "fy" , type : "band" }
11+ { name : "fx" , value : x , scale : "fx" , type : "band" , optional : true } ,
12+ { name : "fy" , value : y , scale : "fy" , type : "band" , optional : true }
1313 ] ,
1414 transform
1515 ) ;
1616 this . marks = marks ;
1717 this . facets = undefined ; // set by initialize
1818 }
1919 initialize ( data ) {
20- const { index, channels : [ fy ] } = super . initialize ( data ) ;
21- const [ , { value : FY } ] = fy ;
20+ const { index, channels} = super . initialize ( data ) ;
2221 const subchannels = [ ] ;
2322 const facets = this . facets = new Map ( ) ;
24-
25- for ( const [ facetKey , facetIndex ] of group ( index , i => FY [ i ] ) ) {
23+ for ( const [ facetKey , facetIndex ] of facetGroups ( index , channels ) ) {
2624 const facetData = Array . from ( facetIndex , i => data [ i ] ) ;
2725 const markIndex = new Map ( ) ;
2826 const markChannels = new Map ( ) ;
@@ -40,30 +38,29 @@ export class Facet extends Mark {
4038 }
4139 facets . set ( facetKey , { markIndex, markChannels} ) ;
4240 }
43-
44- return { index, channels : [ fy , ...subchannels ] } ;
41+ return { index, channels : [ ...channels , ...subchannels ] } ;
4542 }
4643 render ( index , scales , channels , options ) {
4744 const { marks, facets} = this ;
48- const { fy} = scales ;
49- const { y, marginRight, marginLeft, width} = options ;
45+ const { fx , fy} = scales ;
46+ const { x , y, marginTop , marginRight, marginBottom , marginLeft, width, height } = options ;
5047
5148 const subdimensions = {
52- marginTop : 0 ,
53- marginRight ,
54- marginBottom : 0 ,
55- marginLeft ,
56- width ,
57- height : fy . bandwidth ( )
49+ ... fy
50+ ? { marginTop : 0 , marginBottom : 0 , height : fy . bandwidth ( ) }
51+ : { marginTop , marginBottom , height } ,
52+ ... fx
53+ ? { marginRight : 0 , marginLeft : 0 , width : fx . bandwidth ( ) }
54+ : { marginRight , marginLeft , width }
5855 } ;
5956
60- autoScaleRange ( { y} , subdimensions ) ;
57+ autoScaleRange ( { x , y} , subdimensions ) ;
6158
6259 return create ( "svg:g" )
6360 . call ( g => g . selectAll ( )
64- . data ( fy . domain ( ) )
61+ . data ( facets . keys ( ) )
6562 . join ( "g" )
66- . attr ( "transform" , ( key ) => `translate(0, ${ fy ( key ) } )` )
63+ . attr ( "transform" , facetTranslate ( fx , fy ) )
6764 . each ( function ( key ) {
6865 const { markIndex, markChannels} = facets . get ( key ) ;
6966 for ( const mark of marks ) {
@@ -79,3 +76,30 @@ export class Facet extends Mark {
7976 . node ( ) ;
8077 }
8178}
79+
80+ export function facets ( data , { x, y, ...options } , marks ) {
81+ return x === undefined && y === undefined
82+ ? marks // if no facets are specified, ignore!
83+ : [ new Facet ( data , { x, y, ...options } , marks ) ] ;
84+ }
85+
86+ function facetGroups ( index , channels ) {
87+ return ( channels . length > 1 ? facetGroup2 : facetGroup1 ) ( index , ...channels ) ;
88+ }
89+
90+ function facetGroup1 ( index , [ , { value : F } ] ) {
91+ return group ( index , i => F [ i ] ) ;
92+ }
93+
94+ function facetGroup2 ( index , [ , { value : FX } ] , [ , { value : FY } ] ) {
95+ return groups ( index , i => FX [ i ] , i => FY [ i ] )
96+ . flatMap ( ( [ x , xgroup ] ) => xgroup
97+ . map ( ( [ y , ygroup ] ) => [ [ x , y ] , ygroup ] ) ) ;
98+ }
99+
100+ // This must match the key structure returned by facetGroups.
101+ function facetTranslate ( fx , fy ) {
102+ return fx && fy ? ( [ kx , ky ] ) => `translate(${ fx ( kx ) } ,${ fy ( ky ) } )`
103+ : fx ? kx => `translate(${ fx ( kx ) } ,0)`
104+ : ky => `translate(0,${ fy ( ky ) } )` ;
105+ }
0 commit comments