492 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			492 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
| <!DOCTYPE html>
 | ||
| <html>
 | ||
| <head>
 | ||
|     <meta name="viewport" content="width=device-width, initial-scale=1"/>
 | ||
|     <script src="./libraries/papaparse.min.js"></script>
 | ||
|     <script src="./libraries/n.js"></script>
 | ||
|     <script src="./libraries/pivot.js"></script>
 | ||
|     <script src="./data/csv.js"></script>
 | ||
| </head>
 | ||
| <body>
 | ||
|     <div id="app"></div>
 | ||
|     <!-- initialize table -->
 | ||
|     <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>
 | ||
| </html>
 |