[issue #9] refactor2022/imports #12
| @ -1,4 +1,4 @@ | |||||||
| var CSV = [ | [ | ||||||
| ["fspr","plnt","promo","terms","bill_cust_descr","ship_cust_descr","dsm","quota_rep_descr","director","billto_group","shipto_group","chan","chansub","chan_retail","part","part_descr","part_group","branding","majg_descr","ming_descr","majs_descr","mins_descr","segm","substance","fs_line","r_currency","r_rate","c_currency","c_rate","units","value_loc","value_usd","cost_loc","cost_usd","calc_status","flag","order_date","order_month","order_season","request_date","request_month","request_season","ship_date","ship_month","ship_season","version","iter","logid","tag","comment","module"], | ["fspr","plnt","promo","terms","bill_cust_descr","ship_cust_descr","dsm","quota_rep_descr","director","billto_group","shipto_group","chan","chansub","chan_retail","part","part_descr","part_group","branding","majg_descr","ming_descr","majs_descr","mins_descr","segm","substance","fs_line","r_currency","r_rate","c_currency","c_rate","units","value_loc","value_usd","cost_loc","cost_usd","calc_status","flag","order_date","order_month","order_season","request_date","request_month","request_season","ship_date","ship_month","ship_season","version","iter","logid","tag","comment","module"], | ||||||
| ["2102","152","NONE","1A","BWIC0001 - BWI COMPANIES INC","BLOO0017 - BLOOMING COLOR","10032","BRYAN HILL","Baggetta","BWI","BLOOMING COLOR","DRP","DRP","DRP","HZP3E100G18D050","HZP3E100G18D050 - E-10 3 STRAND HGR BLACK","HZP3E100","","110 - INJECTION","000 - NON BRANDED","108 - ACCESSORIES","A06 - HANGERS","Greenhouse","Plastic","41010","US","1.0000000000","US","1.0000000000","25600.0000000000","2099.2000000000","2099.20","1697.2800000000","1697.28","CLOSED","SHIPMENT","2020-06-08","01 - Jun","2021","2020-06-15","01 - Jun","2021","2020-07-09","02 - Jul","2021","15mo","actuals","1","Initial Build","don't undo","build_pool"], | ["2102","152","NONE","1A","BWIC0001 - BWI COMPANIES INC","BLOO0017 - BLOOMING COLOR","10032","BRYAN HILL","Baggetta","BWI","BLOOMING COLOR","DRP","DRP","DRP","HZP3E100G18D050","HZP3E100G18D050 - E-10 3 STRAND HGR BLACK","HZP3E100","","110 - INJECTION","000 - NON BRANDED","108 - ACCESSORIES","A06 - HANGERS","Greenhouse","Plastic","41010","US","1.0000000000","US","1.0000000000","25600.0000000000","2099.2000000000","2099.20","1697.2800000000","1697.28","CLOSED","SHIPMENT","2020-06-08","01 - Jun","2021","2020-06-15","01 - Jun","2021","2020-07-09","02 - Jul","2021","15mo","actuals","1","Initial Build","don't undo","build_pool"], | ||||||
| ["2211","152","NONE","1A","PAST0002 - PASTANCH LLC","PAST0002 - PASTANCH LLC","13028","RICHARD MEULE","Soltis","PASTANCH LLC","PASTANCH LLC","WHS","WHS","WHS","AMK12000G18D050","AMK12000G18D050 - 3G 1200 REG BM POT BLACK","AMK12000","","210 - BLOW MOLD","000 - NON BRANDED","104 - ROUND POTS AND TRAYS","A19 - NURSERY POTS >= 1 GAL","Nursery","Plastic","41010","US","1.0000000000","US","1.0000000000","24000.0000000000","9144.0000000000","9144.00","4936.0800000000","4936.08","OPEN","REMAINDER","2022-03-26","10 - Mar","2022","2022-04-28","11 - Apr","2022","2022-04-28","11 - Apr","2022","b22","copy","1","Initial Build","don't undo","build_pool"], | ["2211","152","NONE","1A","PAST0002 - PASTANCH LLC","PAST0002 - PASTANCH LLC","13028","RICHARD MEULE","Soltis","PASTANCH LLC","PASTANCH LLC","WHS","WHS","WHS","AMK12000G18D050","AMK12000G18D050 - 3G 1200 REG BM POT BLACK","AMK12000","","210 - BLOW MOLD","000 - NON BRANDED","104 - ROUND POTS AND TRAYS","A19 - NURSERY POTS >= 1 GAL","Nursery","Plastic","41010","US","1.0000000000","US","1.0000000000","24000.0000000000","9144.0000000000","9144.00","4936.0800000000","4936.08","OPEN","REMAINDER","2022-03-26","10 - Mar","2022","2022-04-28","11 - Apr","2022","2022-04-28","11 - Apr","2022","b22","copy","1","Initial Build","don't undo","build_pool"], | ||||||
							
								
								
									
										495
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										495
									
								
								index.html
									
									
									
									
									
								
							| @ -2,490 +2,21 @@ | |||||||
| <html> | <html> | ||||||
| <head> | <head> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"/> |     <meta name="viewport" content="width=device-width, initial-scale=1"/> | ||||||
|     <script src="./libraries/papaparse.min.js"></script> |     <script type="importmap"> | ||||||
|     <script src="./libraries/n.js"></script> |         { | ||||||
|     <script src="./libraries/pivot.js"></script> |             "imports": | ||||||
|     <script src="./data/csv.js"></script> |             { | ||||||
|  |            "react": "https://esm.sh/react@18", | ||||||
|  | "react-dom/client": "https://esm.sh/react-dom/client", | ||||||
|  |         "@emotion": "https://esm.sh/@emotion", | ||||||
|  |        "@emotion/": "https://esm.sh/@emotion/", | ||||||
|  |              "htm": "https://esm.sh/htm/react" | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|     <div id="app"></div> |     <div id="app"></div> | ||||||
|     <!-- initialize table --> |     <script type="module" src="./src/app.js"></script> | ||||||
|     <script> |  | ||||||
|     let columnNames = CSV.shift(); |  | ||||||
|     let columnTypes = ([...columnNames]).fill("hidden"); |  | ||||||
|     columnTypes[29] = "sum"; |  | ||||||
|     columnTypes[30] = "sum"; |  | ||||||
|     columnTypes[31] = "sum"; |  | ||||||
|     columnTypes[32] = "sum"; |  | ||||||
|     columnTypes[33] = "sum"; |  | ||||||
|     columnTypes[7 ] = "label"; |  | ||||||
|     columnTypes[8 ] = "label"; |  | ||||||
|     columnTypes[9 ] = "label"; |  | ||||||
|     columnTypes[10] = "label"; |  | ||||||
|     columnTypes[11] = "label"; |  | ||||||
|     Pivot.Init( |  | ||||||
|         columnTypes, |  | ||||||
|         columnNames, |  | ||||||
|         CSV |  | ||||||
|     ); |  | ||||||
|     </script> |  | ||||||
|     <!-- rendering --> |  | ||||||
|     <script type="module"> |  | ||||||
|     import { h, render, createContext, Fragment } from 'https://cdn.skypack.dev/preact'; |  | ||||||
|     import { useReducer, useState } from 'https://cdn.skypack.dev/preact/hooks'; |  | ||||||
|     import { css, cx } from 'https://cdn.skypack.dev/@emotion/css'; |  | ||||||
|     import htm from 'https://unpkg.com/htm?module'; |  | ||||||
|     const html = htm.bind(h); |  | ||||||
|      |  | ||||||
|     let PivotForm = props => |  | ||||||
|     { |  | ||||||
|         let styles = css` |  | ||||||
|             position:realtive; |  | ||||||
|             box-sizing: border-box; |  | ||||||
|             padding: 10px; |  | ||||||
|             color:black; |  | ||||||
|             font-family:sans-serif; |  | ||||||
|      |  | ||||||
|             .Title |  | ||||||
|             { |  | ||||||
|                 font-size:24px; |  | ||||||
|                 font-weight:100; |  | ||||||
|             } |  | ||||||
|      |  | ||||||
|             .Section |  | ||||||
|             { |  | ||||||
|                 padding:10px 0 10px 0; |  | ||||||
|      |  | ||||||
|                 .Heading |  | ||||||
|                 { |  | ||||||
|                     display:inline-block; |  | ||||||
|                     color:#666; |  | ||||||
|                     font-family:sans-serif; |  | ||||||
|                     font-size:12px; |  | ||||||
|                     font-weight:900; |  | ||||||
|                     text-transform:uppercase; |  | ||||||
|                 } |  | ||||||
|                 .Group |  | ||||||
|                 { |  | ||||||
|                     display:inline-block; |  | ||||||
|                     padding:5px; |  | ||||||
|                     border-radius:5px; |  | ||||||
|                     margin:3px; |  | ||||||
|                     background:rgba(0, 0, 0, 0.3) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|      |  | ||||||
|         let pivotColumns = N.Step(Pivot.Schema, "label")||[]; |  | ||||||
|         let pivotColumnsUsed = N.Step(Pivot.Proto, "used-pivot")||[]; |  | ||||||
|      |  | ||||||
|         let sumColumns = N.Step(Pivot.Schema, "sum")||[]; |  | ||||||
|         //let sumColumnsUsed = N.Step(Pivot.Proto, "used-sum")||[]; |  | ||||||
|      |  | ||||||
|         let indiciesPivot = pivotColumnsUsed.map(node=>node.Meta.Index); |  | ||||||
|         let indiciesSum = sumColumns.map(node=>node.Meta.Index); |  | ||||||
|         //let indiciesSum = sumColumnsUsed.map(node=>node.Meta.Index); |  | ||||||
|          |  | ||||||
|         let displayPivotsAll = html` |  | ||||||
|         <div class="Section"> |  | ||||||
|             <div class="Heading">Available Columns</div> |  | ||||||
|             <div class="Group"> |  | ||||||
|             ${pivotColumns.map( columnPivot => |  | ||||||
|             { |  | ||||||
|                 let attributes = {}; |  | ||||||
|                 if(N.Step(columnPivot, "used-pivot", false)) |  | ||||||
|                 { |  | ||||||
|                     attributes.disabled = true; |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     attributes.onClick = e=> |  | ||||||
|                     { |  | ||||||
|                         N.Connect(Pivot.Proto, columnPivot, "used-pivot"); |  | ||||||
|                         Render(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return html`<button ...${attributes}>${columnPivot.Meta.Label}</button>`; |  | ||||||
|             })} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|         let displayPivotsPending = null; |  | ||||||
|         if(pivotColumnsUsed.length) |  | ||||||
|         { |  | ||||||
|             displayPivotsPending  = html` |  | ||||||
|             <div class="Section"> |  | ||||||
|                 <div class="Heading">Pending Pivot</div> |  | ||||||
|                 <div class="Group"> |  | ||||||
|                     ${pivotColumnsUsed.map(columnPivot=>html` |  | ||||||
|                     <button onClick=${e=>{N.Disconnect(Pivot.Proto, columnPivot, "used-pivot");Render();}}> |  | ||||||
|                         ${columnPivot.Meta.Label} |  | ||||||
|                     </button> |  | ||||||
|                     `)} |  | ||||||
|                 </div> |  | ||||||
|      |  | ||||||
|                 <button onClick=${e=>{ |  | ||||||
|                     N.Disconnect(Pivot.Proto, null, "used-pivot"); |  | ||||||
|                     N.Disconnect(Pivot.Proto, null, "used-sum"); |  | ||||||
|                     Pivot.Create(pivotColumnsUsed.map(column=>column.Meta.Label).join("|"), indiciesPivot, indiciesSum); |  | ||||||
|                     Render(); |  | ||||||
|                 }}>Create</button> |  | ||||||
|             </div> |  | ||||||
|             `; |  | ||||||
|         } |  | ||||||
|      |  | ||||||
|         return html` |  | ||||||
|         <div class=${styles}> |  | ||||||
|             <div class="Title">Create New Pivot</div> |  | ||||||
|             ${displayPivotsAll} |  | ||||||
|             ${displayPivotsPending} |  | ||||||
|         </div> |  | ||||||
|         `; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     let Section = props => |  | ||||||
|     { |  | ||||||
|         let styles = css` |  | ||||||
|             .Heading |  | ||||||
|             { |  | ||||||
|                 padding:6px 0 6px 0; |  | ||||||
|                 color:#666; |  | ||||||
|                 font-weight:900; |  | ||||||
|                 font-size:12px; |  | ||||||
|                 text-transform:uppercase; |  | ||||||
|                 cursor:pointer; |  | ||||||
|      |  | ||||||
|                 span |  | ||||||
|                 { |  | ||||||
|                     display:inline-block; |  | ||||||
|                     width:20px; |  | ||||||
|                     height:20px; |  | ||||||
|                     margin-right:10px; |  | ||||||
|                     border-radius:20px; |  | ||||||
|                     background:black; |  | ||||||
|                     color:white; |  | ||||||
|                     text-align:center; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .Heading:hover |  | ||||||
|             { |  | ||||||
|                 color:black; |  | ||||||
|             } |  | ||||||
|             .Body |  | ||||||
|             { |  | ||||||
|                 position:relative; |  | ||||||
|                 padding:10px 0 20px 30px; |  | ||||||
|                 &::before |  | ||||||
|                 { |  | ||||||
|                     content: " "; |  | ||||||
|                     display:block; |  | ||||||
|                     position:absolute; |  | ||||||
|                     top:-8px; |  | ||||||
|                     left:8px; |  | ||||||
|                     width:3px; |  | ||||||
|                     height:100%; |  | ||||||
|                     background:black; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|         let [openGet, openSet] = useState(false); |  | ||||||
|         return html` |  | ||||||
|             <div class=${styles}> |  | ||||||
|                 <div class="Heading" onClick=${e=>openSet(!openGet)}> |  | ||||||
|                     <span>${openGet ? `−` : `+`}</span> |  | ||||||
|                     ${props.label} |  | ||||||
|                 </div> |  | ||||||
|                 ${ openGet ? html`<div class="Body">${props.children}</div>` : null } |  | ||||||
|             </div> |  | ||||||
|         `; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     let ModificationsIcon = ({node}) => |  | ||||||
|     { |  | ||||||
|         let modsUp   = N.Step(node, "ModifyUp",   false)||[]; |  | ||||||
|         let modsDown = N.Step(node, "ModifyDown", false)||[]; |  | ||||||
|         let modsAt   = N.Step(node, "ModifyAt",   false)||[]; |  | ||||||
|         let modsOut  = N.Step(node, "ModifyOut",  false)||[]; |  | ||||||
|      |  | ||||||
|         var button = null; |  | ||||||
|         if(modsAt.length) |  | ||||||
|         { |  | ||||||
|             button = html`<button onClick=${e=>{Pivot.Unmodify(modsAt[0]); Render();}}>-</button>`; |  | ||||||
|         }  |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             button = html`<button onClick=${e=>{Pivot.Modify(node); Render();}}>+</button>`; |  | ||||||
|         } |  | ||||||
|      |  | ||||||
|         let padding = 7; |  | ||||||
|         let icon = 0; |  | ||||||
|         let styles = css` |  | ||||||
|             position:relative; |  | ||||||
|             display:inline-block; |  | ||||||
|             vertical-align:middle; |  | ||||||
|             width:${padding*2 + icon}px; |  | ||||||
|             height:${padding*2 + icon}px; |  | ||||||
|             margin:${padding*2}; |  | ||||||
|             .Icon |  | ||||||
|             { |  | ||||||
|                 position:absolute; |  | ||||||
|                 display:inline-block; |  | ||||||
|                 width:${padding*2 + icon}px; |  | ||||||
|                 height:${padding*2 + icon}px; |  | ||||||
|                 text-align:center; |  | ||||||
|                 font-size:9px; |  | ||||||
|                 font-family:sans-serif; |  | ||||||
|                 font-weight:900; |  | ||||||
|                 line-height:${padding*2 + icon}px; |  | ||||||
|      |  | ||||||
|                 &::after |  | ||||||
|                 { |  | ||||||
|                     content:" "; |  | ||||||
|                     display:block; |  | ||||||
|                     position:absolute; |  | ||||||
|                     width:${icon}px; |  | ||||||
|                     height:${icon}px; |  | ||||||
|                     border:${padding}px solid transparent; |  | ||||||
|                 } |  | ||||||
|      |  | ||||||
|                 &.Down |  | ||||||
|                 { |  | ||||||
|                     left:0; |  | ||||||
|                     bottom:100%; |  | ||||||
|                     &::after |  | ||||||
|                     { |  | ||||||
|                         top:100%; |  | ||||||
|                         border-top-color:green; |  | ||||||
|                         border-bottom:0px solid transparent; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 &.At |  | ||||||
|                 { |  | ||||||
|                     top:0; |  | ||||||
|                     left:100%; |  | ||||||
|                     &::after |  | ||||||
|                     { |  | ||||||
|                         top:0; |  | ||||||
|                         right:100%; |  | ||||||
|                         border-right-color:red; |  | ||||||
|                         border-left:0px solid transparent; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 &.Up |  | ||||||
|                 { |  | ||||||
|                     left:0; |  | ||||||
|                     top:100%; |  | ||||||
|                     &::after |  | ||||||
|                     { |  | ||||||
|                         bottom:100%; |  | ||||||
|                         border-bottom-color:orange; |  | ||||||
|                         border-top:0px solid transparent; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|      |  | ||||||
|                 &.Out |  | ||||||
|                 { |  | ||||||
|                     top:0; |  | ||||||
|                     right:100%; |  | ||||||
|                     &::after |  | ||||||
|                     { |  | ||||||
|                         top:0; |  | ||||||
|                         left:100%; |  | ||||||
|                         border-left-color:grey; |  | ||||||
|                         border-right:0px solid transparent; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|      |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|         return html` |  | ||||||
|         <div class=${styles}> |  | ||||||
|             ${modsDown.length ? html`<div class="Icon Down">${modsDown.length}</div>` : null} |  | ||||||
|             ${modsAt.length ? html`<span class="Icon At">${modsAt.length}</span>` : null} |  | ||||||
|             ${modsUp.length ? html`<span class="Icon Up">${modsUp.length}</span>` : null} |  | ||||||
|             ${modsOut.length ? html`<span class="Icon Out">${modsOut.length}</span>` : null} |  | ||||||
|         </div> |  | ||||||
|         ${button} |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     let PivotBranch = props => |  | ||||||
|     { |  | ||||||
|         let row = props.node.Meta.Row; |  | ||||||
|         let displayCellsModify = row.map(column=>false); |  | ||||||
|         props.node.Meta.IndexSum.forEach(i=> |  | ||||||
|         { |  | ||||||
|             displayCellsModify[i] = html`<td><input type="number" value=${row[i]}/></td>`; |  | ||||||
|         }); |  | ||||||
|         displayCellsModify.forEach((cell, i)=> |  | ||||||
|         { |  | ||||||
|             if(!cell) |  | ||||||
|             { |  | ||||||
|                 displayCellsModify[i] = html`<td>${row[i]}</td>` |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|      |  | ||||||
|         let displayCells = (node, visible) => |  | ||||||
|         { |  | ||||||
|             let output = []; |  | ||||||
|             node.Meta.Row.forEach((column, i)=> |  | ||||||
|             { |  | ||||||
|                 if(visible[i]) |  | ||||||
|                 { |  | ||||||
|                     output.push( h("td", null, column) ); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             ); |  | ||||||
|             return output; |  | ||||||
|         } |  | ||||||
|      |  | ||||||
|         let stylesLeaf = css` |  | ||||||
|             background:#ddd; |  | ||||||
|             color:#333; |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|         return html` |  | ||||||
|         <tbody> |  | ||||||
|             <tr> |  | ||||||
|                 <td> |  | ||||||
|                     <strong class=${css`margin-left:${props.node.Meta.Depth*10}px;`}>${props.node.Meta.Label}</strong> |  | ||||||
|                 </td> |  | ||||||
|                 <td> |  | ||||||
|                     <${ModificationsIcon} node=${props.node}><//> |  | ||||||
|                 </td> |  | ||||||
|                 ${displayCells(props.node, props.visible)} |  | ||||||
|             </tr> |  | ||||||
|             ${(N.Step(props.node, "Leaf")||[]).map(leaf => |  | ||||||
|             { |  | ||||||
|                 return html` |  | ||||||
|                 <tr class=${stylesLeaf}> |  | ||||||
|                     <td></td> |  | ||||||
|                     <td><${ModificationsIcon} node=${leaf}><//></td> |  | ||||||
|                     ${displayCells(leaf, props.visible)} |  | ||||||
|                 </tr> |  | ||||||
|                 `; |  | ||||||
|             } |  | ||||||
|             )} |  | ||||||
|         </tbody> |  | ||||||
|         `; |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     let PivotRoot = ({pivot}) => |  | ||||||
|     { |  | ||||||
|         let labelsDefault = (N.Step(Pivot.Schema, "hidden")||[]).map(column => column.Meta.Index); |  | ||||||
|         let labelsSum = (N.Step(Pivot.Schema, "sum")||[]).map(column => column.Meta.Index); |  | ||||||
|         let labelsPivot = (N.Step(Pivot.Schema, "label")||[]).map(column => column.Meta.Index); |  | ||||||
|         let labelsAll = (N.Step(Pivot.Schema, "all")||[]).map(column => column.Meta.Label); |  | ||||||
|         let labelsAllState = useState(labelsAll.map(column=>true)); |  | ||||||
|      |  | ||||||
|         let headersDisplay = []; |  | ||||||
|         labelsAllState[0].forEach((visible, index)=> |  | ||||||
|         { |  | ||||||
|             if(visible) |  | ||||||
|             { |  | ||||||
|                 headersDisplay.push(html`<th>${labelsAll[index]}</th>`); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         ); |  | ||||||
|      |  | ||||||
|         let stylesRoot = css` |  | ||||||
|             display:block; |  | ||||||
|             box-sizing:border-box; |  | ||||||
|             padding:15px; |  | ||||||
|             font-family:sans-serif; |  | ||||||
|         `; |  | ||||||
|         let stylesHeading = css` |  | ||||||
|             margin: 0 0 15px 0; |  | ||||||
|             font-weight:0; |  | ||||||
|             font-size:24px; |  | ||||||
|         `; |  | ||||||
|      |  | ||||||
|         let modifiers = N.Step(pivot, "ModifyUp", false) || []; |  | ||||||
|         let handleDelete = ()=> |  | ||||||
|         { |  | ||||||
|             Pivot.Delete(pivot); |  | ||||||
|             Render(); |  | ||||||
|         }; |  | ||||||
|      |  | ||||||
|      |  | ||||||
|         let displayColumnGroup = (inAllLabels, inAllState, inIndicies) => |  | ||||||
|         { |  | ||||||
|             return html` |  | ||||||
|             <div> |  | ||||||
|                 <button onClick=${e=> |  | ||||||
|                 { |  | ||||||
|                     let clone = [...inAllState[0]]; |  | ||||||
|                     inIndicies.forEach(index =>clone[index] = true); |  | ||||||
|                     inAllState[1](clone); |  | ||||||
|                 } |  | ||||||
|                 }>Show All</button> |  | ||||||
|                 <button onClick=${e=> |  | ||||||
|                 { |  | ||||||
|                     let clone = [...inAllState[0]]; |  | ||||||
|                     inIndicies.forEach(index =>clone[index] = false); |  | ||||||
|                     inAllState[1](clone); |  | ||||||
|                 } |  | ||||||
|                 }>Hide All</button> |  | ||||||
|             </div> |  | ||||||
|             <div> |  | ||||||
|                 ${inIndicies.map((index) => html`<button onClick=${e=> |  | ||||||
|                 { |  | ||||||
|                     let clone = [...inAllState[0]]; |  | ||||||
|                     clone[index] = !clone[index]; |  | ||||||
|                     inAllState[1](clone); |  | ||||||
|                 } |  | ||||||
|                 }>${inAllLabels[index]} ${inAllState[0][index] ? `visible`:`hidden`}</button>`)} |  | ||||||
|             </div>`; |  | ||||||
|         }; |  | ||||||
|      |  | ||||||
|         let rows = []; |  | ||||||
|         N.ID.Walk++; |  | ||||||
|         N.Walk(n=>rows.push(h(PivotBranch, {node:n, visible:labelsAllState[0]}, null)), pivot, "Hierarchy"); |  | ||||||
|      |  | ||||||
|         return html` |  | ||||||
|         <div class=${stylesRoot}> |  | ||||||
|             <div key="heading" class=${stylesHeading}>${pivot.Meta.Label}</div> |  | ||||||
|             <${Section} key="settings" label=${`Settings`}> |  | ||||||
|                 <button onClick=${handleDelete}>Destroy Pivot</button> |  | ||||||
|             <//> |  | ||||||
|             <${Section} key="columns" label="Columns"> |  | ||||||
|                 <h3>Unused</h3> |  | ||||||
|                 ${displayColumnGroup(labelsAll, labelsAllState, labelsDefault)} |  | ||||||
|                 <h3>Summation</h3> |  | ||||||
|                 ${displayColumnGroup(labelsAll, labelsAllState, labelsSum)} |  | ||||||
|                 <h3>Pivot</h3> |  | ||||||
|                 ${displayColumnGroup(labelsAll, labelsAllState, labelsPivot)} |  | ||||||
|             <//> |  | ||||||
|             <${Section} key="tree" label=${"Tree"}> |  | ||||||
|                 <table> |  | ||||||
|                     <thead> |  | ||||||
|                         <tr> |  | ||||||
|                             <th>Label</th><th>Modifications</th>${headersDisplay} |  | ||||||
|                         </th> |  | ||||||
|                     </thead> |  | ||||||
|                     ${rows} |  | ||||||
|                 </table> |  | ||||||
|             <//> |  | ||||||
|         </div> |  | ||||||
|         `; |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     let ElRoot = props => |  | ||||||
|     { |  | ||||||
|         let pivots = N.Step(Pivot.Root, "Pivot")||[]; |  | ||||||
|         return h("div", null, [ |  | ||||||
|             h(PivotForm), |  | ||||||
|             pivots.map(pivot=>h(PivotRoot, {key:pivot.Meta.Label, pivot})) |  | ||||||
|         ]) |  | ||||||
|     }; |  | ||||||
|     const Render = () => render(h(ElRoot), document.querySelector("#app")); |  | ||||||
|     Render(); |  | ||||||
|      |  | ||||||
|     </script> |  | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
							
								
								
									
										172
									
								
								libraries/n.js
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								libraries/n.js
									
									
									
									
									
								
							| @ -1,172 +0,0 @@ | |||||||
| var N = |  | ||||||
| { |  | ||||||
| ID:{ |  | ||||||
|     Walk:0, |  | ||||||
|     Instance:0 |  | ||||||
| }, |  | ||||||
| Create(inMeta) |  | ||||||
| { |  | ||||||
|     return { |  | ||||||
|         ID:{ |  | ||||||
|             Walk:0, |  | ||||||
|             Instance:N.ID.Instance++ |  | ||||||
|         }, |  | ||||||
|         Meta:inMeta||{}, |  | ||||||
|         Link:{} |  | ||||||
|     }; |  | ||||||
| }, |  | ||||||
| Connect(inNodeMajor, inNodeMinor, inKey, inUnique) |  | ||||||
| { |  | ||||||
|     if(inUnique) // bail if the nodes are already connected
 |  | ||||||
|     { |  | ||||||
|         let check = N.Step(inNodeMajor, inKey, true); |  | ||||||
|         if(check) |  | ||||||
|         { |  | ||||||
|             if(check.indexOf(inNodeMinor) !== -1) |  | ||||||
|             { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); |  | ||||||
|     N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); |  | ||||||
| }, |  | ||||||
| Disconnect(inNodeMajor, inNodeMinor, inKey) |  | ||||||
| { |  | ||||||
|     let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); |  | ||||||
| 
 |  | ||||||
|     // if no specific child was passed
 |  | ||||||
|     if(inNodeMinor === null) |  | ||||||
|     { |  | ||||||
|         // get all the children
 |  | ||||||
|         let check = N.Step(inNodeMajor, inKey); |  | ||||||
|         if(!check){ return; } |  | ||||||
| 
 |  | ||||||
|         // go down to each child ...
 |  | ||||||
|         check.forEach( inNodeMinor =>  |  | ||||||
|         { |  | ||||||
|             let connections = inNodeMinor.Link[inKey]; |  | ||||||
|             remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent
 |  | ||||||
| 
 |  | ||||||
|             // if after the remove operation, this child has no connections on inKey, scrub the key
 |  | ||||||
|             if(!connections.Set.length && !connections.Get.length) |  | ||||||
|             { |  | ||||||
|                 delete inNodeMinor.Link[inKey]; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well
 |  | ||||||
|         if(inNodeMajor.Link[inKey].Get.length == 0) |  | ||||||
|         { |  | ||||||
|             delete inNodeMajor.Link[inKey]; |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // if no specific parent was passed
 |  | ||||||
|     if(inNodeMajor === null) |  | ||||||
|     { |  | ||||||
|         // get all the parents
 |  | ||||||
|         let check = N.Step(inNodeMinor, inKey, false); |  | ||||||
|         if(!check){ return; } |  | ||||||
|          |  | ||||||
|         // go up to each parent ...
 |  | ||||||
|         check.forEach( inNodeMajor =>  |  | ||||||
|         { |  | ||||||
|             let connections = inNodeMajor.Link[inKey]; |  | ||||||
|             remove( connections.Set, inNodeMinor); // ... and remove any reference to the child
 |  | ||||||
|              |  | ||||||
|             // if after the remove operation, this parent has no connections on inKey, scrub the key
 |  | ||||||
|             if( !connections.Set.length && !connections.Get.length ) |  | ||||||
|             { |  | ||||||
|                 delete inNodeMajor.Link[inKey]; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well
 |  | ||||||
|         if(inNodeMinor.Link[inKey].Set.length == 0) |  | ||||||
|         { |  | ||||||
|             delete inNodeMinor.Link[inKey]; |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // if a specific parent and child were passed
 |  | ||||||
|     if(inNodeMajor.Link[inKey].Set.length == 1) |  | ||||||
|     { |  | ||||||
|         delete inNodeMajor.Link[inKey]; |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         remove(inNodeMajor.Link[inKey].Set, inNodeMinor); |  | ||||||
|     } |  | ||||||
|     if(inNodeMinor.Link[inKey].Get.length == 1) |  | ||||||
|     { |  | ||||||
|         delete inNodeMinor.Link[inKey]; |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         remove(inNodeMinor.Link[inKey].Get, inNodeMajor); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| }, |  | ||||||
| Step(inNode, inKey, inForward, inForceCreate) |  | ||||||
| { |  | ||||||
|     let connectionGroup = inNode.Link[inKey]; |  | ||||||
|     if(!connectionGroup) |  | ||||||
|     { |  | ||||||
|         if(inForceCreate === true) |  | ||||||
|         { |  | ||||||
|             inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; |  | ||||||
|      |  | ||||||
| }, |  | ||||||
| Walk(inIterator, inNode, inKey, inForward, inTerminal) |  | ||||||
| { |  | ||||||
|     let array = N.Step(inNode, inKey, inForward); |  | ||||||
| 
 |  | ||||||
|     if(!array.length && inTerminal) |  | ||||||
|     { |  | ||||||
|         return inTerminal(inNode); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for(let i=0; i<array.length; i++) |  | ||||||
|     { |  | ||||||
|         let next = array[i]; |  | ||||||
|         if(next.ID.Walk !== N.ID.Walk) |  | ||||||
|         { |  | ||||||
|             next.ID.Walk = N.ID.Walk; |  | ||||||
|             //console.log("processing", next.Meta)
 |  | ||||||
|             let results = inIterator(next); |  | ||||||
|             if(results !== false) |  | ||||||
|             { |  | ||||||
|                 N.Walk(inIterator, next, inKey, inForward, inTerminal); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 //console.log("routine exited");
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             //console.log("id collision");
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| }, |  | ||||||
| Path(inArray, inNode, inKey, inForward) |  | ||||||
| { |  | ||||||
|     var current = inNode; |  | ||||||
|     var direction = inForward||true; |  | ||||||
|     for(let i=0; i<inArray.length; i++) |  | ||||||
|     { |  | ||||||
|         current = N.Step(current, inKey, direction)[inArray[i]]; |  | ||||||
|     } |  | ||||||
|     return current; |  | ||||||
| } |  | ||||||
| }; |  | ||||||
							
								
								
									
										7
									
								
								libraries/papaparse.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								libraries/papaparse.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										478
									
								
								src/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								src/app.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,478 @@ | |||||||
|  | import { createRoot } from "react-dom/client"; | ||||||
|  | import { createElement as h, createContext, Fragment, useReducer, useState } from 'react'; | ||||||
|  | import { css, cx } from '@emotion/css'; | ||||||
|  | import { html } from 'htm'; | ||||||
|  | 
 | ||||||
|  | import N from "./n.js"; | ||||||
|  | import Pivot from "./pivot.js"; | ||||||
|  | 
 | ||||||
|  | import CSV from "../data/csv.json" assert { type: "json" }; | ||||||
|  | let columnNames = CSV.shift(); | ||||||
|  | let columnTypes = ([...columnNames]).fill("hidden"); | ||||||
|  | columnTypes[29] = "sum"; | ||||||
|  | columnTypes[30] = "sum"; | ||||||
|  | columnTypes[31] = "sum"; | ||||||
|  | columnTypes[32] = "sum"; | ||||||
|  | columnTypes[33] = "sum"; | ||||||
|  | columnTypes[7 ] = "label"; | ||||||
|  | columnTypes[8 ] = "label"; | ||||||
|  | columnTypes[9 ] = "label"; | ||||||
|  | columnTypes[10] = "label"; | ||||||
|  | columnTypes[11] = "label"; | ||||||
|  | Pivot.Init( | ||||||
|  |     columnTypes, | ||||||
|  |     columnNames, | ||||||
|  |     CSV | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | let PivotForm = props => | ||||||
|  | { | ||||||
|  |     let styles = css` | ||||||
|  |         position:realtive; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         padding: 10px; | ||||||
|  |         color:black; | ||||||
|  |         font-family:sans-serif; | ||||||
|  | 
 | ||||||
|  |         .Title | ||||||
|  |         { | ||||||
|  |             font-size:24px; | ||||||
|  |             font-weight:100; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .Section | ||||||
|  |         { | ||||||
|  |             padding:10px 0 10px 0; | ||||||
|  | 
 | ||||||
|  |             .Heading | ||||||
|  |             { | ||||||
|  |                 display:inline-block; | ||||||
|  |                 color:#666; | ||||||
|  |                 font-family:sans-serif; | ||||||
|  |                 font-size:12px; | ||||||
|  |                 font-weight:900; | ||||||
|  |                 text-transform:uppercase; | ||||||
|  |             } | ||||||
|  |             .Group | ||||||
|  |             { | ||||||
|  |                 display:inline-block; | ||||||
|  |                 padding:5px; | ||||||
|  |                 border-radius:5px; | ||||||
|  |                 margin:3px; | ||||||
|  |                 background:rgba(0, 0, 0, 0.3) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     let pivotColumns = N.Step(Pivot.Schema, "label")||[]; | ||||||
|  |     let pivotColumnsUsed = N.Step(Pivot.Proto, "used-pivot")||[]; | ||||||
|  | 
 | ||||||
|  |     let sumColumns = N.Step(Pivot.Schema, "sum")||[]; | ||||||
|  |     //let sumColumnsUsed = N.Step(Pivot.Proto, "used-sum")||[];
 | ||||||
|  | 
 | ||||||
|  |     let indiciesPivot = pivotColumnsUsed.map(node=>node.Meta.Index); | ||||||
|  |     let indiciesSum = sumColumns.map(node=>node.Meta.Index); | ||||||
|  |     //let indiciesSum = sumColumnsUsed.map(node=>node.Meta.Index);
 | ||||||
|  |      | ||||||
|  |     let displayPivotsAll = html` | ||||||
|  |     <div class="Section"> | ||||||
|  |         <div class="Heading">Available Columns</div> | ||||||
|  |         <div class="Group"> | ||||||
|  |         ${pivotColumns.map( columnPivot => | ||||||
|  |         { | ||||||
|  |             let attributes = {}; | ||||||
|  |             if(N.Step(columnPivot, "used-pivot", false)) | ||||||
|  |             { | ||||||
|  |                 attributes.disabled = true; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 attributes.onClick = e=> | ||||||
|  |                 { | ||||||
|  |                     N.Connect(Pivot.Proto, columnPivot, "used-pivot"); | ||||||
|  |                     Render(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return html`<button ...${attributes}>${columnPivot.Meta.Label}</button>`; | ||||||
|  |         })} | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  |     let displayPivotsPending = null; | ||||||
|  |     if(pivotColumnsUsed.length) | ||||||
|  |     { | ||||||
|  |         displayPivotsPending  = html` | ||||||
|  |         <div class="Section"> | ||||||
|  |             <div class="Heading">Pending Pivot</div> | ||||||
|  |             <div class="Group"> | ||||||
|  |                 ${pivotColumnsUsed.map(columnPivot=>html` | ||||||
|  |                 <button onClick=${e=>{N.Disconnect(Pivot.Proto, columnPivot, "used-pivot");Render();}}> | ||||||
|  |                     ${columnPivot.Meta.Label} | ||||||
|  |                 </button> | ||||||
|  |                 `)}
 | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <button onClick=${e=>{ | ||||||
|  |                 N.Disconnect(Pivot.Proto, null, "used-pivot"); | ||||||
|  |                 N.Disconnect(Pivot.Proto, null, "used-sum"); | ||||||
|  |                 Pivot.Create(pivotColumnsUsed.map(column=>column.Meta.Label).join("|"), indiciesPivot, indiciesSum); | ||||||
|  |                 Render(); | ||||||
|  |             }}>Create</button> | ||||||
|  |         </div> | ||||||
|  |         `;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return html` | ||||||
|  |     <div class=${styles}> | ||||||
|  |         <div class="Title">Create New Pivot</div> | ||||||
|  |         ${displayPivotsAll} | ||||||
|  |         ${displayPivotsPending} | ||||||
|  |     </div> | ||||||
|  |     `;
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | let Section = props => | ||||||
|  | { | ||||||
|  |     let styles = css` | ||||||
|  |         .Heading | ||||||
|  |         { | ||||||
|  |             padding:6px 0 6px 0; | ||||||
|  |             color:#666; | ||||||
|  |             font-weight:900; | ||||||
|  |             font-size:12px; | ||||||
|  |             text-transform:uppercase; | ||||||
|  |             cursor:pointer; | ||||||
|  | 
 | ||||||
|  |             span | ||||||
|  |             { | ||||||
|  |                 display:inline-block; | ||||||
|  |                 width:20px; | ||||||
|  |                 height:20px; | ||||||
|  |                 margin-right:10px; | ||||||
|  |                 border-radius:20px; | ||||||
|  |                 background:black; | ||||||
|  |                 color:white; | ||||||
|  |                 text-align:center; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .Heading:hover | ||||||
|  |         { | ||||||
|  |             color:black; | ||||||
|  |         } | ||||||
|  |         .Body | ||||||
|  |         { | ||||||
|  |             position:relative; | ||||||
|  |             padding:10px 0 20px 30px; | ||||||
|  |             &::before | ||||||
|  |             { | ||||||
|  |                 content: " "; | ||||||
|  |                 display:block; | ||||||
|  |                 position:absolute; | ||||||
|  |                 top:-8px; | ||||||
|  |                 left:8px; | ||||||
|  |                 width:3px; | ||||||
|  |                 height:100%; | ||||||
|  |                 background:black; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  |     let [openGet, openSet] = useState(false); | ||||||
|  |     return html` | ||||||
|  |         <div class=${styles}> | ||||||
|  |             <div class="Heading" onClick=${e=>openSet(!openGet)}> | ||||||
|  |                 <span>${openGet ? `−` : `+`}</span> | ||||||
|  |                 ${props.label} | ||||||
|  |             </div> | ||||||
|  |             ${ openGet ? html`<div class="Body">${props.children}</div>` : null } | ||||||
|  |         </div> | ||||||
|  |     `;
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | let ModificationsIcon = ({node}) => | ||||||
|  | { | ||||||
|  |     let modsUp   = N.Step(node, "ModifyUp",   false)||[]; | ||||||
|  |     let modsDown = N.Step(node, "ModifyDown", false)||[]; | ||||||
|  |     let modsAt   = N.Step(node, "ModifyAt",   false)||[]; | ||||||
|  |     let modsOut  = N.Step(node, "ModifyOut",  false)||[]; | ||||||
|  | 
 | ||||||
|  |     var button = null; | ||||||
|  |     if(modsAt.length) | ||||||
|  |     { | ||||||
|  |         button = html`<button onClick=${e=>{Pivot.Unmodify(modsAt[0]); Render();}}>-</button>`; | ||||||
|  |     }  | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         button = html`<button onClick=${e=>{Pivot.Modify(node); Render();}}>+</button>`; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let padding = 7; | ||||||
|  |     let icon = 0; | ||||||
|  |     let styles = css` | ||||||
|  |         position:relative; | ||||||
|  |         display:inline-block; | ||||||
|  |         vertical-align:middle; | ||||||
|  |         width:${padding*2 + icon}px; | ||||||
|  |         height:${padding*2 + icon}px; | ||||||
|  |         margin:${padding*2}; | ||||||
|  |         .Icon | ||||||
|  |         { | ||||||
|  |             position:absolute; | ||||||
|  |             display:inline-block; | ||||||
|  |             width:${padding*2 + icon}px; | ||||||
|  |             height:${padding*2 + icon}px; | ||||||
|  |             text-align:center; | ||||||
|  |             font-size:9px; | ||||||
|  |             font-family:sans-serif; | ||||||
|  |             font-weight:900; | ||||||
|  |             line-height:${padding*2 + icon}px; | ||||||
|  | 
 | ||||||
|  |             &::after | ||||||
|  |             { | ||||||
|  |                 content:" "; | ||||||
|  |                 display:block; | ||||||
|  |                 position:absolute; | ||||||
|  |                 width:${icon}px; | ||||||
|  |                 height:${icon}px; | ||||||
|  |                 border:${padding}px solid transparent; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             &.Down | ||||||
|  |             { | ||||||
|  |                 left:0; | ||||||
|  |                 bottom:100%; | ||||||
|  |                 &::after | ||||||
|  |                 { | ||||||
|  |                     top:100%; | ||||||
|  |                     border-top-color:green; | ||||||
|  |                     border-bottom:0px solid transparent; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             &.At | ||||||
|  |             { | ||||||
|  |                 top:0; | ||||||
|  |                 left:100%; | ||||||
|  |                 &::after | ||||||
|  |                 { | ||||||
|  |                     top:0; | ||||||
|  |                     right:100%; | ||||||
|  |                     border-right-color:red; | ||||||
|  |                     border-left:0px solid transparent; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             &.Up | ||||||
|  |             { | ||||||
|  |                 left:0; | ||||||
|  |                 top:100%; | ||||||
|  |                 &::after | ||||||
|  |                 { | ||||||
|  |                     bottom:100%; | ||||||
|  |                     border-bottom-color:orange; | ||||||
|  |                     border-top:0px solid transparent; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             &.Out | ||||||
|  |             { | ||||||
|  |                 top:0; | ||||||
|  |                 right:100%; | ||||||
|  |                 &::after | ||||||
|  |                 { | ||||||
|  |                     top:0; | ||||||
|  |                     left:100%; | ||||||
|  |                     border-left-color:grey; | ||||||
|  |                     border-right:0px solid transparent; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  |     return html` | ||||||
|  |     <div class=${styles}> | ||||||
|  |         ${modsDown.length ? html`<div class="Icon Down">${modsDown.length}</div>` : null} | ||||||
|  |         ${modsAt.length ? html`<span class="Icon At">${modsAt.length}</span>` : null} | ||||||
|  |         ${modsUp.length ? html`<span class="Icon Up">${modsUp.length}</span>` : null} | ||||||
|  |         ${modsOut.length ? html`<span class="Icon Out">${modsOut.length}</span>` : null} | ||||||
|  |     </div> | ||||||
|  |     ${button} | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let PivotBranch = props => | ||||||
|  | { | ||||||
|  |     let row = props.node.Meta.Row; | ||||||
|  |     let displayCellsModify = row.map(column=>false); | ||||||
|  |     props.node.Meta.IndexSum.forEach(i=> | ||||||
|  |     { | ||||||
|  |         displayCellsModify[i] = html`<td><input type="number" value=${row[i]}/></td>`; | ||||||
|  |     }); | ||||||
|  |     displayCellsModify.forEach((cell, i)=> | ||||||
|  |     { | ||||||
|  |         if(!cell) | ||||||
|  |         { | ||||||
|  |             displayCellsModify[i] = html`<td>${row[i]}</td>` | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     let displayCells = (node, visible) => | ||||||
|  |     { | ||||||
|  |         let output = []; | ||||||
|  |         node.Meta.Row.forEach((column, i)=> | ||||||
|  |         { | ||||||
|  |             if(visible[i]) | ||||||
|  |             { | ||||||
|  |                 output.push( h("td", null, column) ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         ); | ||||||
|  |         return output; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let stylesLeaf = css` | ||||||
|  |         background:#ddd; | ||||||
|  |         color:#333; | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  |     return html` | ||||||
|  |     <tbody> | ||||||
|  |         <tr> | ||||||
|  |             <td> | ||||||
|  |                 <strong class=${css`margin-left:${props.node.Meta.Depth*10}px;`}>${props.node.Meta.Label}</strong> | ||||||
|  |             </td> | ||||||
|  |             <td> | ||||||
|  |                 <${ModificationsIcon} node=${props.node}><//>
 | ||||||
|  |             </td> | ||||||
|  |             ${displayCells(props.node, props.visible)} | ||||||
|  |         </tr> | ||||||
|  |         ${(N.Step(props.node, "Leaf")||[]).map(leaf => | ||||||
|  |         { | ||||||
|  |             return html` | ||||||
|  |             <tr class=${stylesLeaf}> | ||||||
|  |                 <td></td> | ||||||
|  |                 <td><${ModificationsIcon} node=${leaf}><//></td>
 | ||||||
|  |                 ${displayCells(leaf, props.visible)} | ||||||
|  |             </tr> | ||||||
|  |             `;
 | ||||||
|  |         } | ||||||
|  |         )} | ||||||
|  |     </tbody> | ||||||
|  |     `;
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let PivotRoot = ({pivot}) => | ||||||
|  | { | ||||||
|  |     let labelsDefault = (N.Step(Pivot.Schema, "hidden")||[]).map(column => column.Meta.Index); | ||||||
|  |     let labelsSum = (N.Step(Pivot.Schema, "sum")||[]).map(column => column.Meta.Index); | ||||||
|  |     let labelsPivot = (N.Step(Pivot.Schema, "label")||[]).map(column => column.Meta.Index); | ||||||
|  |     let labelsAll = (N.Step(Pivot.Schema, "all")||[]).map(column => column.Meta.Label); | ||||||
|  | 
 | ||||||
|  |     let labelsAllState = useState(labelsAll.map(column=>true)) | ||||||
|  | 
 | ||||||
|  |     let headersDisplay = []; | ||||||
|  |     labelsAllState[0].forEach((visible, index)=> | ||||||
|  |     { | ||||||
|  |         if(visible) | ||||||
|  |         { | ||||||
|  |             headersDisplay.push(html`<th>${labelsAll[index]}</th>`); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     let stylesRoot = css` | ||||||
|  |         display:block; | ||||||
|  |         box-sizing:border-box; | ||||||
|  |         padding:15px; | ||||||
|  |         font-family:sans-serif; | ||||||
|  |     `;
 | ||||||
|  |     let stylesHeading = css` | ||||||
|  |         margin: 0 0 15px 0; | ||||||
|  |         font-weight:0; | ||||||
|  |         font-size:24px; | ||||||
|  |     `;
 | ||||||
|  | 
 | ||||||
|  |     let modifiers = N.Step(pivot, "ModifyUp", false) || []; | ||||||
|  |     let handleDelete = ()=> | ||||||
|  |     { | ||||||
|  |         Pivot.Delete(pivot); | ||||||
|  |         Render(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     let displayColumnGroup = (inAllLabels, inAllState, inIndicies) => | ||||||
|  |     { | ||||||
|  |         return html` | ||||||
|  |         <div> | ||||||
|  |             <button onClick=${e=> | ||||||
|  |             { | ||||||
|  |                 let clone = [...inAllState[0]]; | ||||||
|  |                 inIndicies.forEach(index =>clone[index] = true); | ||||||
|  |                 inAllState[1](clone); | ||||||
|  |             } | ||||||
|  |             }>Show All</button> | ||||||
|  |             <button onClick=${e=> | ||||||
|  |             { | ||||||
|  |                 let clone = [...inAllState[0]]; | ||||||
|  |                 inIndicies.forEach(index =>clone[index] = false); | ||||||
|  |                 inAllState[1](clone); | ||||||
|  |             } | ||||||
|  |             }>Hide All</button> | ||||||
|  |         </div> | ||||||
|  |         <div> | ||||||
|  |             ${inIndicies.map((index) => html`<button onClick=${e=> | ||||||
|  |             { | ||||||
|  |                 let clone = [...inAllState[0]]; | ||||||
|  |                 clone[index] = !clone[index]; | ||||||
|  |                 inAllState[1](clone); | ||||||
|  |             } | ||||||
|  |             }>${inAllLabels[index]} ${inAllState[0][index] ? `visible`:`hidden`}</button>`)} | ||||||
|  |         </div>`; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let rows = []; | ||||||
|  |     N.ID.Walk++; | ||||||
|  |     N.Walk(n=>rows.push(h(PivotBranch, {node:n, visible:labelsAllState[0]}, null)), pivot, "Hierarchy"); | ||||||
|  | 
 | ||||||
|  |     return html` | ||||||
|  |     <div class=${stylesRoot}> | ||||||
|  |         <div key="heading" class=${stylesHeading}>${pivot.Meta.Label}</div> | ||||||
|  |         <${Section} key="settings" label=${`Settings`}> | ||||||
|  |             <button onClick=${handleDelete}>Destroy Pivot</button> | ||||||
|  |         <//>
 | ||||||
|  |         <${Section} key="columns" label="Columns"> | ||||||
|  |             <h3>Unused</h3> | ||||||
|  |             ${displayColumnGroup(labelsAll, labelsAllState, labelsDefault)} | ||||||
|  |             <h3>Summation</h3> | ||||||
|  |             ${displayColumnGroup(labelsAll, labelsAllState, labelsSum)} | ||||||
|  |             <h3>Pivot</h3> | ||||||
|  |             ${displayColumnGroup(labelsAll, labelsAllState, labelsPivot)} | ||||||
|  |         <//>
 | ||||||
|  |         <${Section} key="tree" label=${"Tree"}> | ||||||
|  |             <table> | ||||||
|  |                 <thead> | ||||||
|  |                     <tr> | ||||||
|  |                         <th>Label</th><th>Modifications</th>${headersDisplay} | ||||||
|  |                     </th> | ||||||
|  |                 </thead> | ||||||
|  |                 ${rows} | ||||||
|  |             </table> | ||||||
|  |         <//>
 | ||||||
|  |     </div> | ||||||
|  |     `;
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let ElRoot = props => | ||||||
|  | { | ||||||
|  |     let pivots = N.Step(Pivot.Root, "Pivot")||[]; | ||||||
|  |     return h("div", null, [ | ||||||
|  |         h(PivotForm), | ||||||
|  |         pivots.map(pivot=>h(PivotRoot, {key:pivot.Meta.Label, pivot})) | ||||||
|  |     ]) | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const Root = createRoot(document.querySelector("#app")); | ||||||
|  | const Render = () => Root.render(h(ElRoot)); | ||||||
|  | Render(); | ||||||
							
								
								
									
										174
									
								
								src/n.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/n.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | |||||||
|  | const N = | ||||||
|  | { | ||||||
|  |     ID:{ | ||||||
|  |         Walk:0, | ||||||
|  |         Instance:0 | ||||||
|  |     }, | ||||||
|  |     Create(inMeta) | ||||||
|  |     { | ||||||
|  |         return { | ||||||
|  |             ID:{ | ||||||
|  |                 Walk:0, | ||||||
|  |                 Instance:N.ID.Instance++ | ||||||
|  |             }, | ||||||
|  |             Meta:inMeta||{}, | ||||||
|  |             Link:{} | ||||||
|  |         }; | ||||||
|  |     }, | ||||||
|  |     Connect(inNodeMajor, inNodeMinor, inKey, inUnique) | ||||||
|  |     { | ||||||
|  |         if(inUnique) // bail if the nodes are already connected
 | ||||||
|  |         { | ||||||
|  |             let check = N.Step(inNodeMajor, inKey, true); | ||||||
|  |             if(check) | ||||||
|  |             { | ||||||
|  |                 if(check.indexOf(inNodeMinor) !== -1) | ||||||
|  |                 { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); | ||||||
|  |         N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); | ||||||
|  |     }, | ||||||
|  |     Disconnect(inNodeMajor, inNodeMinor, inKey) | ||||||
|  |     { | ||||||
|  |         let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); | ||||||
|  | 
 | ||||||
|  |         // if no specific child was passed
 | ||||||
|  |         if(inNodeMinor === null) | ||||||
|  |         { | ||||||
|  |             // get all the children
 | ||||||
|  |             let check = N.Step(inNodeMajor, inKey); | ||||||
|  |             if(!check){ return; } | ||||||
|  | 
 | ||||||
|  |             // go down to each child ...
 | ||||||
|  |             check.forEach( inNodeMinor =>  | ||||||
|  |             { | ||||||
|  |                 let connections = inNodeMinor.Link[inKey]; | ||||||
|  |                 remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent
 | ||||||
|  | 
 | ||||||
|  |                 // if after the remove operation, this child has no connections on inKey, scrub the key
 | ||||||
|  |                 if(!connections.Set.length && !connections.Get.length) | ||||||
|  |                 { | ||||||
|  |                     delete inNodeMinor.Link[inKey]; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well
 | ||||||
|  |             if(inNodeMajor.Link[inKey].Get.length == 0) | ||||||
|  |             { | ||||||
|  |                 delete inNodeMajor.Link[inKey]; | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // if no specific parent was passed
 | ||||||
|  |         if(inNodeMajor === null) | ||||||
|  |         { | ||||||
|  |             // get all the parents
 | ||||||
|  |             let check = N.Step(inNodeMinor, inKey, false); | ||||||
|  |             if(!check){ return; } | ||||||
|  |              | ||||||
|  |             // go up to each parent ...
 | ||||||
|  |             check.forEach( inNodeMajor =>  | ||||||
|  |             { | ||||||
|  |                 let connections = inNodeMajor.Link[inKey]; | ||||||
|  |                 remove( connections.Set, inNodeMinor); // ... and remove any reference to the child
 | ||||||
|  |                  | ||||||
|  |                 // if after the remove operation, this parent has no connections on inKey, scrub the key
 | ||||||
|  |                 if( !connections.Set.length && !connections.Get.length ) | ||||||
|  |                 { | ||||||
|  |                     delete inNodeMajor.Link[inKey]; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well
 | ||||||
|  |             if(inNodeMinor.Link[inKey].Set.length == 0) | ||||||
|  |             { | ||||||
|  |                 delete inNodeMinor.Link[inKey]; | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // if a specific parent and child were passed
 | ||||||
|  |         if(inNodeMajor.Link[inKey].Set.length == 1) | ||||||
|  |         { | ||||||
|  |             delete inNodeMajor.Link[inKey]; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             remove(inNodeMajor.Link[inKey].Set, inNodeMinor); | ||||||
|  |         } | ||||||
|  |         if(inNodeMinor.Link[inKey].Get.length == 1) | ||||||
|  |         { | ||||||
|  |             delete inNodeMinor.Link[inKey]; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             remove(inNodeMinor.Link[inKey].Get, inNodeMajor); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     }, | ||||||
|  |     Step(inNode, inKey, inForward, inForceCreate) | ||||||
|  |     { | ||||||
|  |         let connectionGroup = inNode.Link[inKey]; | ||||||
|  |         if(!connectionGroup) | ||||||
|  |         { | ||||||
|  |             if(inForceCreate === true) | ||||||
|  |             { | ||||||
|  |                 inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; | ||||||
|  |          | ||||||
|  |     }, | ||||||
|  |     Walk(inIterator, inNode, inKey, inForward, inTerminal) | ||||||
|  |     { | ||||||
|  |         let array = N.Step(inNode, inKey, inForward); | ||||||
|  | 
 | ||||||
|  |         if(!array.length && inTerminal) | ||||||
|  |         { | ||||||
|  |             return inTerminal(inNode); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(let i=0; i<array.length; i++) | ||||||
|  |         { | ||||||
|  |             let next = array[i]; | ||||||
|  |             if(next.ID.Walk !== N.ID.Walk) | ||||||
|  |             { | ||||||
|  |                 next.ID.Walk = N.ID.Walk; | ||||||
|  |                 //console.log("processing", next.Meta)
 | ||||||
|  |                 let results = inIterator(next); | ||||||
|  |                 if(results !== false) | ||||||
|  |                 { | ||||||
|  |                     N.Walk(inIterator, next, inKey, inForward, inTerminal); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     //console.log("routine exited");
 | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 //console.log("id collision");
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     Path(inArray, inNode, inKey, inForward) | ||||||
|  |     { | ||||||
|  |         var current = inNode; | ||||||
|  |         var direction = inForward||true; | ||||||
|  |         for(let i=0; i<inArray.length; i++) | ||||||
|  |         { | ||||||
|  |             current = N.Step(current, inKey, direction)[inArray[i]]; | ||||||
|  |         } | ||||||
|  |         return current; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default N; | ||||||
| @ -1,4 +1,6 @@ | |||||||
| var Pivot =  | import N from "./n.js"; | ||||||
|  | 
 | ||||||
|  | const Pivot =  | ||||||
| { | { | ||||||
| Leaves:{}, | Leaves:{}, | ||||||
| Root:N.Create({Label:"All Pivots"}), | Root:N.Create({Label:"All Pivots"}), | ||||||
| @ -196,3 +198,5 @@ Unmodify(inModifier) | |||||||
|     N.Disconnect(null, inModifier, "Modifier"); |     N.Disconnect(null, inModifier, "Modifier"); | ||||||
| } | } | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export default Pivot; | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user