prographs 0.12.0 → 0.13.0
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/CHANGELOG.md +9 -1
- package/README.md +6 -4
- package/dist/AutoGraph-PDPEAIXL.cjs +145 -0
- package/dist/GraphType.js +19 -0
- package/dist/autograph/chartIndex.js +521 -0
- package/dist/autograph/chartModules.js +2 -1
- package/dist/autograph/index.cjs +98 -93
- package/dist/catalog-DI4XZ46L.cjs +142 -0
- package/dist/catalog.js +2 -1
- package/dist/charts/biologycharts/BiologySamples.js +59 -0
- package/dist/charts/biologycharts/PunnettSquare.js +342 -0
- package/dist/charts/biologycharts/biologyEnums.js +22 -1
- package/dist/charts/biologycharts/index.js +5 -3
- package/dist/charts/doecharts/DoeSamples.js +78 -0
- package/dist/charts/doecharts/SipocDiagram.js +453 -0
- package/dist/charts/doecharts/index.js +4 -2
- package/dist/charts/fieldcharts/SpeciesAccumulationCurve.js +350 -0
- package/dist/charts/fieldcharts/fieldEnums.js +8 -1
- package/dist/charts/fieldcharts/index.js +5 -3
- package/dist/charts/signals/AntennaPatternChart.js +492 -0
- package/dist/charts/signals/SignalSamples.js +17 -0
- package/dist/charts/signals/index.js +5 -3
- package/dist/charts/softwarediagrams/ComponentDiagram.js +504 -0
- package/dist/charts/softwarediagrams/DeploymentDiagram.js +419 -0
- package/dist/charts/softwarediagrams/SoftwareSamples.js +283 -1
- package/dist/charts/softwarediagrams/index.js +7 -3
- package/dist/charts/softwarediagrams/softwareEnums.js +45 -1
- package/dist/charts/sportscharts/PowerDurationCurve.js +301 -0
- package/dist/charts/sportscharts/SportsSamples.js +102 -0
- package/dist/charts/sportscharts/TrainingLoadChart.js +325 -0
- package/dist/charts/sportscharts/index.js +6 -2
- package/dist/charts/strategycharts/BassDiffusionChart.js +362 -0
- package/dist/charts/strategycharts/BassModel.js +96 -0
- package/dist/charts/strategycharts/BurnRunwayChart.js +284 -0
- package/dist/charts/strategycharts/CapTableChart.js +366 -0
- package/dist/charts/strategycharts/KanbanBoard.js +324 -0
- package/dist/charts/strategycharts/OkrTree.js +339 -0
- package/dist/charts/strategycharts/StrategyPane.js +27 -0
- package/dist/charts/strategycharts/StrategySamples.js +251 -0
- package/dist/charts/strategycharts/index.js +15 -3
- package/dist/charts/threefields/BsfcMap3DChart.js +231 -0
- package/dist/charts/threefields/CompressorMap3DChart.js +217 -0
- package/dist/charts/threefields/FieldGrid.js +50 -0
- package/dist/charts/threefields/LossLandscape3DChart.js +201 -0
- package/dist/charts/threefields/MapSheet.js +77 -0
- package/dist/charts/threefields/TerrainRoute3DChart.js +201 -0
- package/dist/charts/threefields/TerrainRouteMath.js +40 -0
- package/dist/charts/threefields/ThreeFieldBase.js +117 -0
- package/dist/charts/threefields/ThreeFieldsSamples.js +80 -0
- package/dist/charts/threefields/WaferMap3DChart.js +218 -0
- package/dist/charts/threefields/index.js +19 -0
- package/dist/charts/threesurfaces/index.js +2 -2
- package/dist/charts/tradingcharts/KeltnerChannelChart.js +171 -0
- package/dist/charts/tradingcharts/TradingMath.js +20 -0
- package/dist/charts/tradingcharts/index.js +4 -2
- package/dist/{chunk-ZL6XDXMK.cjs → chunk-26PSLEP4.cjs} +8 -8
- package/dist/{chunk-W3US6EFP.cjs → chunk-2BO5DZUP.cjs} +12 -12
- package/dist/{chunk-5DP7OUSE.cjs → chunk-2GG5YYSC.cjs} +6 -6
- package/dist/{chunk-7FG4JSVO.cjs → chunk-2GL2VNJU.cjs} +253 -33
- package/dist/{chunk-LCWMRCLF.cjs → chunk-2PYOI7OY.cjs} +21 -21
- package/dist/{chunk-JFWJAEBD.cjs → chunk-3U4MCSKI.cjs} +4 -4
- package/dist/{chunk-CUPLHZRR.cjs → chunk-3ZMM3FSK.cjs} +4 -4
- package/dist/{chunk-L2AV62DP.cjs → chunk-4767WQJ7.cjs} +6 -6
- package/dist/{chunk-DA2EYIIP.cjs → chunk-4E7FSSBF.cjs} +1210 -168
- package/dist/{chunk-VS5LQEZB.cjs → chunk-526STI2S.cjs} +5 -5
- package/dist/{chunk-RXIMEK6C.cjs → chunk-55767YPI.cjs} +20 -20
- package/dist/{chunk-JXLSKIUV.cjs → chunk-5FBQOMMT.cjs} +26 -26
- package/dist/{chunk-27PZV5BR.cjs → chunk-5N4Z7MTS.cjs} +4 -4
- package/dist/{chunk-KEWCUIQA.cjs → chunk-5WEV6YWA.cjs} +22 -22
- package/dist/chunk-5XOVVC5X.cjs +3281 -0
- package/dist/{chunk-HK5J7Q2W.cjs → chunk-6HLXVWXU.cjs} +9 -9
- package/dist/{chunk-7JG47RW4.cjs → chunk-6LQT4WJ5.cjs} +25 -25
- package/dist/{chunk-OCVTI6X4.cjs → chunk-6YTWOUFD.cjs} +329 -36
- package/dist/{chunk-W7IQCAVN.cjs → chunk-7F6W6HKL.cjs} +11 -11
- package/dist/{chunk-F2DCJXDM.cjs → chunk-7L6KYU5A.cjs} +21 -21
- package/dist/{chunk-GTGR6LPR.cjs → chunk-A4GIN4FE.cjs} +17 -17
- package/dist/{chunk-B2CETSR6.cjs → chunk-A4US7SA7.cjs} +5 -5
- package/dist/{chunk-3DIF2YYW.cjs → chunk-AOXPBLPW.cjs} +10 -10
- package/dist/{chunk-7TTR4UF5.cjs → chunk-ATI45Z4M.cjs} +2 -2
- package/dist/{chunk-5BFCIX2V.cjs → chunk-CPPMO4IQ.cjs} +434 -148
- package/dist/{chunk-AYZZQ6AC.cjs → chunk-CZ5KGF6Q.cjs} +9 -9
- package/dist/{chunk-7THJ3ZBK.cjs → chunk-D4I35MCE.cjs} +2 -2
- package/dist/{chunk-MCPPFRLN.cjs → chunk-DCGAAN3Z.cjs} +2 -2
- package/dist/{chunk-VABL6CVX.cjs → chunk-DEY36N37.cjs} +21 -21
- package/dist/{chunk-RNCKGJCC.cjs → chunk-DXHXD3DK.cjs} +3 -3
- package/dist/{chunk-A56ZGMVZ.cjs → chunk-E7B45DBZ.cjs} +11 -11
- package/dist/{chunk-DOR6L4MT.cjs → chunk-EJC6EAC5.cjs} +18 -18
- package/dist/{chunk-UPGNM7MA.cjs → chunk-EULJOOMA.cjs} +11 -11
- package/dist/{chunk-G4AZL4GO.cjs → chunk-EYM6LEIF.cjs} +474 -70
- package/dist/{chunk-NJ4X6XGV.cjs → chunk-EZTYX2GH.cjs} +5 -5
- package/dist/chunk-FDD4UP73.cjs +521 -0
- package/dist/{chunk-WJ3VH2Q4.cjs → chunk-GEAIGM3J.cjs} +110 -615
- package/dist/{chunk-P3SBHF43.cjs → chunk-GRUYVXTS.cjs} +2 -2
- package/dist/{chunk-NX4Z5LMS.cjs → chunk-H3SZXND5.cjs} +5 -5
- package/dist/{chunk-EE4S4TOE.cjs → chunk-HS45TOFJ.cjs} +21 -21
- package/dist/chunk-INGOG6OV.cjs +899 -0
- package/dist/{chunk-UDKA6DGT.cjs → chunk-J3CDXZHF.cjs} +4 -4
- package/dist/{chunk-YO6G7TKI.cjs → chunk-JAU5ADEU.cjs} +11 -11
- package/dist/{chunk-22LBK4GA.cjs → chunk-JQEBSHX3.cjs} +4 -4
- package/dist/{chunk-RXX3U7VQ.cjs → chunk-KLXGLMFU.cjs} +10 -10
- package/dist/chunk-KVOX36J3.cjs +327 -0
- package/dist/chunk-L6BGNHFL.cjs +139 -0
- package/dist/{chunk-GMER35L3.cjs → chunk-L7TB7PU5.cjs} +20 -20
- package/dist/{chunk-AFXYNBL2.cjs → chunk-LBTRYEM2.cjs} +4 -4
- package/dist/{chunk-WYYCKIOD.cjs → chunk-LLULNJSP.cjs} +11 -11
- package/dist/{chunk-J27XOSLP.cjs → chunk-LSO46AFV.cjs} +8 -8
- package/dist/{chunk-5XJO45ES.cjs → chunk-LZ2KJ7ZX.cjs} +11 -11
- package/dist/chunk-M4IQ5LIZ.cjs +422 -0
- package/dist/{chunk-Y2KIUKJF.cjs → chunk-MPDHDKYZ.cjs} +11 -11
- package/dist/{chunk-4EMPKRZU.cjs → chunk-MRQ42ZJE.cjs} +3 -3
- package/dist/{chunk-EU5D2MF4.cjs → chunk-MYV4KZWP.cjs} +9 -9
- package/dist/{chunk-XD7KN4UN.cjs → chunk-MZXVLC24.cjs} +6 -6
- package/dist/{chunk-PPSONARU.cjs → chunk-NLBVDFIR.cjs} +41 -2
- package/dist/{chunk-L3PEGMGM.cjs → chunk-NY3TBZY4.cjs} +33 -33
- package/dist/{chunk-6YNYXXB6.cjs → chunk-OGVVSFAR.cjs} +3 -3
- package/dist/{chunk-MLV7QLB6.cjs → chunk-OMTQIQMN.cjs} +6 -6
- package/dist/{chunk-BEEOSNK7.cjs → chunk-PEJJ6TXJ.cjs} +15 -144
- package/dist/{chunk-SOKOWUF5.cjs → chunk-PPGWHIU7.cjs} +2 -2
- package/dist/{chunk-IELWOM2J.cjs → chunk-PPO6SDAN.cjs} +6 -6
- package/dist/{chunk-6JTZZCSL.cjs → chunk-PUMKX5F2.cjs} +3 -3
- package/dist/{chunk-RFOXFY2U.cjs → chunk-PXBYWCNB.cjs} +190 -799
- package/dist/{chunk-3BYXOISI.cjs → chunk-Q3HB3IX6.cjs} +4 -4
- package/dist/{chunk-RXCOVONX.cjs → chunk-RBMUOIXE.cjs} +289 -34
- package/dist/{chunk-MSBNHCSE.cjs → chunk-RJHGCUUD.cjs} +6 -6
- package/dist/{chunk-I6VG7F6Z.cjs → chunk-RSONDMYW.cjs} +708 -141
- package/dist/{chunk-GZDNPHMT.cjs → chunk-SHMPFCUE.cjs} +19 -19
- package/dist/{chunk-ZIBVCOW6.cjs → chunk-SN4FCXFC.cjs} +19 -19
- package/dist/{chunk-7KM52NMN.cjs → chunk-SN5T7WYO.cjs} +2 -2
- package/dist/{chunk-7MSZR65Z.cjs → chunk-SQJZGVDR.cjs} +7 -7
- package/dist/chunk-SYABFMD7.cjs +633 -0
- package/dist/{chunk-2RRFEYFH.cjs → chunk-TICB3MAC.cjs} +16 -16
- package/dist/{chunk-AOOKHUV5.cjs → chunk-U7Z7XYR2.cjs} +5 -5
- package/dist/{chunk-JEWQAXVG.cjs → chunk-UA2THQFV.cjs} +6 -6
- package/dist/{chunk-LHZPAHU3.cjs → chunk-VIK4L2YF.cjs} +112 -521
- package/dist/{chunk-4YWMAQ4J.cjs → chunk-VM2Y6BDY.cjs} +4 -4
- package/dist/{chunk-IRRKE2ZE.cjs → chunk-VMS3KWYI.cjs} +21 -21
- package/dist/{chunk-4SLARW6F.cjs → chunk-VU5NUD46.cjs} +17 -17
- package/dist/{chunk-XHY6TVYG.cjs → chunk-VV5CVOF7.cjs} +13 -13
- package/dist/{chunk-CCIBPGPZ.cjs → chunk-VYSAWPSS.cjs} +57 -57
- package/dist/{chunk-TTNEMQ2A.cjs → chunk-W7YRWEWQ.cjs} +22 -22
- package/dist/{chunk-C7MUOGEW.cjs → chunk-X7R5SY62.cjs} +6 -6
- package/dist/{chunk-ILR5KBMB.cjs → chunk-XOK3KTSS.cjs} +6 -6
- package/dist/{chunk-2CZM2I3T.cjs → chunk-XUIX45VU.cjs} +19 -19
- package/dist/{chunk-TGRMPS3A.cjs → chunk-XWH46PVJ.cjs} +165 -43
- package/dist/{chunk-WQYXXECR.cjs → chunk-Y5XVMK3K.cjs} +6 -6
- package/dist/{chunk-AK4TX2WG.cjs → chunk-YCNKEDTG.cjs} +13 -13
- package/dist/{chunk-PJJB24DU.cjs → chunk-YEF7SE3X.cjs} +20 -20
- package/dist/{chunk-RVLV4XW6.cjs → chunk-YMTVPMU3.cjs} +22 -22
- package/dist/data/index.cjs +2 -2
- package/dist/entries/autograph-lazy.cjs +2 -2
- package/dist/entries/charts/acoustictransport.cjs +3 -3
- package/dist/entries/charts/analysischarts.cjs +3 -3
- package/dist/entries/charts/astrocharts.cjs +3 -3
- package/dist/entries/charts/bars.cjs +3 -3
- package/dist/entries/charts/bayesiancharts.cjs +3 -3
- package/dist/entries/charts/biocharts.cjs +3 -3
- package/dist/entries/charts/biologycharts.cjs +11 -3
- package/dist/entries/charts/biologycharts.d.ts +1 -1
- package/dist/entries/charts/biologycharts.js +3 -2
- package/dist/entries/charts/business.cjs +3 -3
- package/dist/entries/charts/carecharts.cjs +3 -3
- package/dist/entries/charts/chemistrycharts.cjs +3 -3
- package/dist/entries/charts/civicscharts.cjs +3 -3
- package/dist/entries/charts/clinicalcharts.cjs +3 -3
- package/dist/entries/charts/comparison.cjs +3 -3
- package/dist/entries/charts/demography.cjs +3 -3
- package/dist/entries/charts/density.cjs +3 -3
- package/dist/entries/charts/diagrams.cjs +3 -3
- package/dist/entries/charts/distribution.cjs +3 -3
- package/dist/entries/charts/doecharts.cjs +13 -3
- package/dist/entries/charts/doecharts.d.ts +1 -1
- package/dist/entries/charts/doecharts.js +2 -1
- package/dist/entries/charts/earthscience.cjs +3 -3
- package/dist/entries/charts/economicscharts.cjs +3 -3
- package/dist/entries/charts/energycharts.cjs +3 -3
- package/dist/entries/charts/engineering.cjs +3 -3
- package/dist/entries/charts/fieldcharts.cjs +7 -3
- package/dist/entries/charts/fieldcharts.d.ts +1 -1
- package/dist/entries/charts/fieldcharts.js +3 -2
- package/dist/entries/charts/financial.cjs +3 -3
- package/dist/entries/charts/flow.cjs +3 -3
- package/dist/entries/charts/frameworks.cjs +3 -3
- package/dist/entries/charts/genomics.cjs +3 -3
- package/dist/entries/charts/geoflows.cjs +3 -3
- package/dist/entries/charts/geomaps.cjs +3 -3
- package/dist/entries/charts/geosciencecharts.cjs +3 -3
- package/dist/entries/charts/hierarchy.cjs +3 -3
- package/dist/entries/charts/indicators.cjs +3 -3
- package/dist/entries/charts/inferencecharts.cjs +3 -3
- package/dist/entries/charts/layoutcharts.cjs +3 -3
- package/dist/entries/charts/lifescience.cjs +4 -4
- package/dist/entries/charts/linesareas.cjs +3 -3
- package/dist/entries/charts/logisticscharts.cjs +3 -3
- package/dist/entries/charts/machinecharts.cjs +7 -3
- package/dist/entries/charts/machinecharts.d.ts +1 -1
- package/dist/entries/charts/machinecharts.js +2 -1
- package/dist/entries/charts/massive.cjs +3 -3
- package/dist/entries/charts/mathematical.cjs +3 -3
- package/dist/entries/charts/matrix.cjs +3 -3
- package/dist/entries/charts/medicalcharts.cjs +4 -4
- package/dist/entries/charts/mlcharts.cjs +4 -4
- package/dist/entries/charts/modelcharts.cjs +6 -5
- package/dist/entries/charts/multivariate.cjs +3 -3
- package/dist/entries/charts/musiccharts.cjs +3 -3
- package/dist/entries/charts/network.cjs +3 -3
- package/dist/entries/charts/physicscharts.cjs +3 -3
- package/dist/entries/charts/pictorial.cjs +4 -4
- package/dist/entries/charts/portfoliocharts.cjs +3 -3
- package/dist/entries/charts/productcharts.cjs +3 -3
- package/dist/entries/charts/proportion.cjs +3 -3
- package/dist/entries/charts/qualitycharts.cjs +3 -3
- package/dist/entries/charts/radial.cjs +3 -3
- package/dist/entries/charts/reliabilitycharts.cjs +5 -3
- package/dist/entries/charts/scatter.cjs +3 -3
- package/dist/entries/charts/scientific.cjs +3 -3
- package/dist/entries/charts/seriescharts.cjs +3 -3
- package/dist/entries/charts/signals.cjs +7 -3
- package/dist/entries/charts/signals.d.ts +1 -1
- package/dist/entries/charts/signals.js +2 -1
- package/dist/entries/charts/softwarediagrams.cjs +21 -3
- package/dist/entries/charts/softwarediagrams.d.ts +1 -1
- package/dist/entries/charts/softwarediagrams.js +4 -2
- package/dist/entries/charts/sportscharts.cjs +13 -3
- package/dist/entries/charts/sportscharts.d.ts +1 -1
- package/dist/entries/charts/sportscharts.js +3 -1
- package/dist/entries/charts/statistics.cjs +6 -6
- package/dist/entries/charts/strategycharts.cjs +30 -3
- package/dist/entries/charts/strategycharts.d.ts +1 -1
- package/dist/entries/charts/strategycharts.js +6 -1
- package/dist/entries/charts/structure.cjs +3 -3
- package/dist/entries/charts/surveycharts.cjs +3 -3
- package/dist/entries/charts/systemscharts.cjs +3 -3
- package/dist/entries/charts/textcharts.cjs +5 -5
- package/dist/entries/charts/thematicmaps.cjs +4 -4
- package/dist/entries/charts/threebars.cjs +3 -3
- package/dist/entries/charts/threefields.cjs +53 -0
- package/dist/entries/charts/threefields.d.ts +1 -0
- package/dist/entries/charts/threefields.js +6 -0
- package/dist/entries/charts/threeglobe.cjs +3 -3
- package/dist/entries/charts/threenetwork.cjs +3 -3
- package/dist/entries/charts/threestrata.cjs +3 -3
- package/dist/entries/charts/threesurfaces.cjs +7 -3
- package/dist/entries/charts/threesurfaces.d.ts +1 -1
- package/dist/entries/charts/threesurfaces.js +2 -1
- package/dist/entries/charts/threevolume.cjs +4 -4
- package/dist/entries/charts/timeline.cjs +3 -3
- package/dist/entries/charts/tracecharts.cjs +3 -3
- package/dist/entries/charts/trackscharts.cjs +3 -3
- package/dist/entries/charts/tradingcharts.cjs +6 -4
- package/dist/entries/charts/tradingcharts.d.ts +1 -1
- package/dist/entries/charts/tradingcharts.js +2 -1
- package/dist/entries/three.cjs +60 -20
- package/dist/entries/three.d.ts +1 -1
- package/dist/entries/three.js +7 -1
- package/dist/index.cjs +258 -97
- package/dist/index.d.cts +1953 -10
- package/dist/index.d.ts +1953 -10
- package/dist/index.js +26 -5
- package/dist/page/index.cjs +2 -2
- package/dist/react/components.js +58 -1
- package/dist/react/index.cjs +135 -92
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/package.json +1 -1
- package/dist/AutoGraph-XLBJQDRB.cjs +0 -140
- package/dist/catalog-O7XGDD2H.cjs +0 -137
- package/dist/chunk-77K2HTRC.cjs +0 -3220
- package/dist/chunk-XEK47TVD.cjs +0 -323
- /package/dist/{chunk-UMEKIIHJ.cjs → chunk-LNWTC72Y.cjs} +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11; var _class12; var _class13; var _class14; var _class15; var _class16;
|
|
2
2
|
|
|
3
3
|
var _chunkQ5A2TY2Acjs = require('./chunk-Q5A2TY2A.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
var _chunkNEXADWQScjs = require('./chunk-NEXADWQS.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
var _chunkXX2YTCORcjs = require('./chunk-XX2YTCOR.cjs');
|
|
7
10
|
|
|
8
11
|
|
|
@@ -12,7 +15,7 @@ var _chunkEEF5WRRZcjs = require('./chunk-EEF5WRRZ.cjs');
|
|
|
12
15
|
var _chunkPY4R3IFFcjs = require('./chunk-PY4R3IFF.cjs');
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
var
|
|
18
|
+
var _chunkLNWTC72Ycjs = require('./chunk-LNWTC72Y.cjs');
|
|
16
19
|
|
|
17
20
|
// src/charts/strategycharts/KanoCurve.ts
|
|
18
21
|
var KanoCategory = /* @__PURE__ */ ((KanoCategory2) => {
|
|
@@ -56,69 +59,87 @@ var KanoCurve = class {
|
|
|
56
59
|
}
|
|
57
60
|
};
|
|
58
61
|
|
|
59
|
-
// src/charts/strategycharts/
|
|
60
|
-
var
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
var
|
|
64
|
-
var
|
|
65
|
-
var
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
static #fill(box, size, style) {
|
|
76
|
-
const { notes } = box, lead = Math.round(size * 1.25), pad = style === "card" ? CARD_PAD : 0, gap = style === "bullet" ? 2 : 3;
|
|
77
|
-
const indent = style === "bullet" ? INDENT : pad, width = box.width - indent - pad;
|
|
78
|
-
if (width < size * 2 || box.height < lead) return { blocks: [], whole: !notes.length };
|
|
79
|
-
const wrapped = notes.map((note) => _chunkQ5A2TY2Acjs.FrameworkText.wrap(note.text, width, size));
|
|
80
|
-
const tall = (lines) => lines.length * lead + pad;
|
|
81
|
-
const cost = (count) => wrapped.slice(0, count).reduce((sum, lines) => sum + tall(lines) + gap, 0);
|
|
82
|
-
let shown = notes.length;
|
|
83
|
-
if (cost(shown) - gap > box.height) {
|
|
84
|
-
shown--;
|
|
85
|
-
while (shown > 0 && cost(shown) + lead > box.height) shown--;
|
|
62
|
+
// src/charts/strategycharts/BassDiffusionChart.ts
|
|
63
|
+
var _d3 = require('d3');
|
|
64
|
+
|
|
65
|
+
// src/charts/strategycharts/BassModel.ts
|
|
66
|
+
var LEAST = 3;
|
|
67
|
+
var FLAT = 1e-6;
|
|
68
|
+
var positive = (value) => Number.isFinite(value) && value > 0;
|
|
69
|
+
var BassModel = class _BassModel {
|
|
70
|
+
/** Periods 1 to `count` of the model. */
|
|
71
|
+
static run({ p, q, m }, count2) {
|
|
72
|
+
const periods = [];
|
|
73
|
+
let adopted = 0;
|
|
74
|
+
for (let t = 1; t <= count2; t++) {
|
|
75
|
+
const left = Math.max(0, m - adopted), innovators = Math.min(left, p * left), imitators = Math.min(left - innovators, m > 0 ? q * (adopted / m) * left : 0);
|
|
76
|
+
adopted += innovators + imitators;
|
|
77
|
+
periods.push({ t, innovators, imitators, adopters: innovators + imitators, cumulative: adopted });
|
|
86
78
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
return periods;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Bass's own estimate: least squares of each period's adopters on the total adopted before it and that total squared,
|
|
83
|
+
* solved for p, q and m. Null with fewer than three periods, or when the fit is not a diffusion: no positive
|
|
84
|
+
* coefficients, or a market smaller than what has already adopted.
|
|
85
|
+
*/
|
|
86
|
+
static fit(adopters) {
|
|
87
|
+
if (adopters.length < LEAST) return null;
|
|
88
|
+
const total = adopters.reduce((sum5, value) => sum5 + value, 0);
|
|
89
|
+
if (!positive(total)) return null;
|
|
90
|
+
let before = 0;
|
|
91
|
+
const rows = adopters.map((value) => {
|
|
92
|
+
const x = before / total;
|
|
93
|
+
before += value;
|
|
94
|
+
return [1, x, x * x, value];
|
|
95
|
+
});
|
|
96
|
+
const normal = [0, 1, 2].map((i) => [...[0, 1, 2].map((j) => rows.reduce((sum5, row) => sum5 + row[i] * row[j], 0)), rows.reduce((sum5, row) => sum5 + row[i] * row[3], 0)]);
|
|
97
|
+
const solved = _BassModel.#solve(normal);
|
|
98
|
+
if (!solved) return null;
|
|
99
|
+
const a = solved[0], b = solved[1] / total, c = solved[2] / (total * total), root = b * b - 4 * a * c;
|
|
100
|
+
if (!(solved[2] < -FLAT * Math.abs(a)) || !(a > 0) || root < 0) return null;
|
|
101
|
+
const m = (-b - Math.sqrt(root)) / (2 * c), p = a / m, q = -c * m;
|
|
102
|
+
return positive(m) && positive(p) && positive(q) && p < 1 && m >= total ? { p, q, m } : null;
|
|
103
|
+
}
|
|
104
|
+
/** Gaussian elimination with partial pivoting on an augmented matrix. Null when the rows are not independent. */
|
|
105
|
+
static #solve(matrix) {
|
|
106
|
+
const size = matrix.length;
|
|
107
|
+
for (let column = 0; column < size; column++) {
|
|
108
|
+
let pivot = column;
|
|
109
|
+
for (let row = column + 1; row < size; row++) if (Math.abs(matrix[row][column]) > Math.abs(matrix[pivot][column])) pivot = row;
|
|
110
|
+
if (Math.abs(matrix[pivot][column]) < 1e-12) return null;
|
|
111
|
+
[matrix[column], matrix[pivot]] = [matrix[pivot], matrix[column]];
|
|
112
|
+
for (let row = 0; row < size; row++) {
|
|
113
|
+
if (row === column) continue;
|
|
114
|
+
const factor = matrix[row][column] / matrix[column][column];
|
|
115
|
+
for (let k = column; k <= size; k++) matrix[row][k] -= factor * matrix[column][k];
|
|
94
116
|
}
|
|
95
117
|
}
|
|
96
|
-
|
|
97
|
-
const place = (note, hidden2, lines, key) => {
|
|
98
|
-
const height = note ? tall(lines) : lead, inset = note ? pad / 2 : 0;
|
|
99
|
-
const block = { key, box, note, hidden: hidden2, lines, x: box.x + (note ? indent : 0), y: top + inset + size - 1, lead, left: box.x, top, width: box.width, height, size };
|
|
100
|
-
top += height + gap;
|
|
101
|
-
return block;
|
|
102
|
-
};
|
|
103
|
-
const blocks = notes.slice(0, shown).map((note, i) => place(note, [], wrapped[i], `${box.key}:${i}`));
|
|
104
|
-
const hidden = notes.slice(shown);
|
|
105
|
-
if (hidden.length && top + lead <= box.y + box.height + 0.5) blocks.push(place(null, hidden, [`+${hidden.length} more`], `${box.key}:more`));
|
|
106
|
-
return { blocks, whole: !hidden.length && !cut };
|
|
118
|
+
return matrix.map((row, i) => row[size] / row[i]);
|
|
107
119
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
/** The market that makes the model with `p` and `q` reach `total` adopters by the end of period `count`. */
|
|
121
|
+
static market(p, q, total, count2) {
|
|
122
|
+
const share = _nullishCoalesce(_optionalChain([_BassModel, 'access', _5 => _5.run, 'call', _6 => _6({ p, q, m: 1 }, count2), 'access', _7 => _7.at, 'call', _8 => _8(-1), 'optionalAccess', _9 => _9.cumulative]), () => ( 0));
|
|
123
|
+
return share > 0 ? total / share : total;
|
|
124
|
+
}
|
|
125
|
+
/** The period in which the model's new adopters peak, or null when it has no periods. */
|
|
126
|
+
static peak(periods) {
|
|
127
|
+
return periods.reduce((best, period) => best && best.adopters >= period.adopters ? best : period, null);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/charts/strategycharts/StrategyPane.ts
|
|
132
|
+
var StrategyPane = class {
|
|
133
|
+
static axis(ctx, name, scale, count2, format) {
|
|
134
|
+
const ticks = scale.ticks(count2);
|
|
135
|
+
const grid = ctx.marks(`axis-${name}-grid`, "line", ticks, (tick) => tick).attr("stroke", (tick) => tick === 0 ? ctx.theme.axis : ctx.theme.grid).attr("stroke-width", 1);
|
|
136
|
+
ctx.set(grid, { x1: 0, x2: ctx.plot.width, y1: (tick) => scale(tick), y2: (tick) => scale(tick) });
|
|
137
|
+
const labels = ctx.text(ctx.marks(`axis-${name}-ticks`, "text", ticks, (tick) => tick)).attr("dy", "0.32em").attr("text-anchor", "end").text(format);
|
|
138
|
+
ctx.set(labels, { x: -3, y: (tick) => scale(tick) });
|
|
139
|
+
}
|
|
140
|
+
/** Short figures for pane ticks and notes: 261K, 2.1M. */
|
|
141
|
+
static compact(value) {
|
|
142
|
+
return Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(value);
|
|
122
143
|
}
|
|
123
144
|
};
|
|
124
145
|
|
|
@@ -262,20 +283,51 @@ var METHODS = [
|
|
|
262
283
|
];
|
|
263
284
|
var PEOPLE = ["Mira V.", "Tobias R.", "Ines H.", "Kofi A.", "Lena B.", "Arun S.", "Yuki M.", "Dara Q.", "Paulo R.", "Greta O.", "Sami N.", "Noor F.", "Elan W.", "Cora L."];
|
|
264
285
|
var TEAMS = ["Sales", "Product", "Operations"];
|
|
286
|
+
var ROUNDS = [
|
|
287
|
+
["Founding", []],
|
|
288
|
+
["Seed", [["Angel investors", 0.05], ["Halden Seed Fund", 0.14]]],
|
|
289
|
+
["Series A", [["Northquill Ventures", 0.2], ["Halden Seed Fund", 0.03], ["Employee pool", 0.06]]],
|
|
290
|
+
["Series B", [["Pellmoor Capital", 0.15], ["Northquill Ventures", 0.03], ["Employee pool", 0.03]]]
|
|
291
|
+
];
|
|
292
|
+
var STAGES = [["Backlog", null], ["Ready", 4], ["In progress", 3], ["Review", 2], ["Done", null]];
|
|
293
|
+
var CARDS = [
|
|
294
|
+
["Gift card balance", 0, "Checkout", "Feature"],
|
|
295
|
+
["Search by ingredient", 0, "Search", "Feature"],
|
|
296
|
+
["Archive old orders", 0, "Platform", "Chore"],
|
|
297
|
+
["Split shipments", 0, "Checkout", "Feature"],
|
|
298
|
+
["Retry failed payments", 1, "Checkout", "Bug"],
|
|
299
|
+
["Typo-tolerant search", 1, "Search", "Feature"],
|
|
300
|
+
["Rotate signing keys", 1, "Platform", "Chore"],
|
|
301
|
+
["Saved carts sync", 2, "Checkout", "Feature"],
|
|
302
|
+
["Slow results on mobile", 2, "Search", "Bug"],
|
|
303
|
+
["Move queues to new cluster", 2, "Platform", "Chore"],
|
|
304
|
+
["Address lookup", 2, "Checkout", "Feature"],
|
|
305
|
+
["Tax rounding error", 3, "Checkout", "Bug"],
|
|
306
|
+
["Filter by dietary need", 3, "Search", "Feature"],
|
|
307
|
+
["One-page checkout", 4, "Checkout", "Feature"],
|
|
308
|
+
["Synonym dictionary", 4, "Search", "Feature"],
|
|
309
|
+
["Upgrade database", 4, "Platform", "Chore"],
|
|
310
|
+
["Empty results page", 4, "Search", "Bug"]
|
|
311
|
+
];
|
|
312
|
+
var OBJECTIVES = [
|
|
313
|
+
["Make setup quick for new teams", [["Median setup time under 10 minutes", 72, 0.8], ["Activation rate from 41% to 55%", 48, 0.6], ["Halve setup support tickets", 85, 0.9]]],
|
|
314
|
+
["Earn the trust of larger customers", [["Pass the security audit", 90, 0.9], ["Uptime of 99.95% for the quarter", 64, 0.7], ["Sign 6 customers over 500 seats", 33, 0.3], ["Launch single sign-on", 55, 0.5]]],
|
|
315
|
+
["Grow revenue from existing accounts", [["Net revenue retention of 112%", 40, 0.5], ["Launch usage-based pricing", 20, 0.3], ["Expansion pipeline of 1.8M", 68, 0.7]]]
|
|
316
|
+
];
|
|
265
317
|
var StrategySamples = class {
|
|
266
318
|
/** `{ technology, position, years }`: place on the cycle from 0 to 100, and years to the plateau or the word Obsolete. */
|
|
267
319
|
static hypeCycle(seed = 1) {
|
|
268
|
-
const { u } =
|
|
320
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
269
321
|
return TECHNOLOGIES.map(([technology, position, years]) => ({ technology, position: round(clamp(position + (u() - 0.5) * 6, 1, 99)), years }));
|
|
270
322
|
}
|
|
271
323
|
/** `{ technology, adoption }`: share of the market that has adopted each product, in percent. */
|
|
272
324
|
static adoption(seed = 1) {
|
|
273
|
-
const { u } =
|
|
325
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
274
326
|
return PRODUCTS.map(([technology, adoption]) => ({ technology, adoption: round(clamp(adoption + (u() - 0.5) * 6, 1, 99)) }));
|
|
275
327
|
}
|
|
276
328
|
/** `{ feature, category, implementation, satisfaction }` for an invented notes app: implementation 0 to 100, satisfaction -100 to 100. */
|
|
277
329
|
static kano(seed = 1) {
|
|
278
|
-
const { n } =
|
|
330
|
+
const { n } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
279
331
|
return FEATURES.map(([feature, category, level]) => {
|
|
280
332
|
const implementation = clamp(Math.round(level + n() * 4), 0, 100);
|
|
281
333
|
return { feature, category, implementation, satisfaction: clamp(Math.round(KanoCurve.satisfaction(category, implementation / 100) * 100 + n() * 7), -100, 100) };
|
|
@@ -283,7 +335,7 @@ var StrategySamples = class {
|
|
|
283
335
|
}
|
|
284
336
|
/** An invented tea shop's value chain: nodes `{ id, visibility, evolution, evolveTo }` on 0 to 1, links from each component to what it needs. */
|
|
285
337
|
static wardley(seed = 1) {
|
|
286
|
-
const { u } =
|
|
338
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
287
339
|
const nodes = COMPONENTS.map(([id, visibility, evolution, evolveTo]) => {
|
|
288
340
|
const shift = (u() - 0.5) * 0.05;
|
|
289
341
|
return { id, visibility, evolution: round(clamp(evolution + shift, 0.02, 0.98), 3), evolveTo: evolveTo === void 0 ? null : round(clamp(evolveTo + shift, 0.02, 0.98), 3) };
|
|
@@ -292,12 +344,12 @@ var StrategySamples = class {
|
|
|
292
344
|
}
|
|
293
345
|
/** `{ factor, offering, score }`: how much each invented hotel offering gives on each competing factor, 0 to 10. */
|
|
294
346
|
static strategyCanvas(seed = 1) {
|
|
295
|
-
const { u } =
|
|
347
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
296
348
|
return OFFERINGS.flatMap(([offering, scores]) => FACTORS.map((factor, i) => ({ factor, offering, score: round(clamp(scores[i] + (u() - 0.5) * 1.2, 0, 10)) })));
|
|
297
349
|
}
|
|
298
350
|
/** `{ force, driver, strength }` for an invented grocery delivery firm: each driver's strength from 1 to 5. */
|
|
299
351
|
static fiveForces(seed = 1) {
|
|
300
|
-
const { u } =
|
|
352
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
301
353
|
return FORCES.flatMap(([force, drivers]) => drivers.map(([driver, strength]) => ({ force, driver, strength: clamp(strength + Math.round((u() - 0.5) * 1.6), 1, 5) })));
|
|
302
354
|
}
|
|
303
355
|
/** `{ block, note }` for an invented bike-share scheme, covering the blocks of the business model canvas and of the lean canvas. */
|
|
@@ -306,7 +358,7 @@ var StrategySamples = class {
|
|
|
306
358
|
}
|
|
307
359
|
/** `{ stage, lane, note, emotion }` for an invented mattress brand: emotion runs from -2 to 2 and repeats on each row of a stage. */
|
|
308
360
|
static journey(seed = 1) {
|
|
309
|
-
const { u } =
|
|
361
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
310
362
|
return JOURNEY.flatMap(([stage, mood, actions, touchpoints, pains]) => {
|
|
311
363
|
const emotion = clamp(Math.round((mood + (u() - 0.5)) * 2) / 2, -2, 2);
|
|
312
364
|
const lane = (name, notes) => notes.map((note) => ({ stage, lane: name, note, emotion }));
|
|
@@ -319,15 +371,57 @@ var StrategySamples = class {
|
|
|
319
371
|
}
|
|
320
372
|
/** `{ method, basis, low, high, current }`: an invented share's value range by method, with today's price on every row. */
|
|
321
373
|
static footballField(seed = 1) {
|
|
322
|
-
const { u, n } =
|
|
374
|
+
const { u, n } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
323
375
|
const level = 0.9 + u() * 0.2, current = round(42 * level + n(), 2);
|
|
324
376
|
return METHODS.map(([method, basis, low, high]) => ({ method, basis, low: round(low * level + n() * 1.5, 2), high: round(high * level + n() * 1.5, 2), current }));
|
|
325
377
|
}
|
|
326
378
|
/** `{ name, team, performance, potential, tenure }`: invented people rated 0 to 100 on two scales, with years in role. */
|
|
327
379
|
static nineBox(seed = 1) {
|
|
328
|
-
const { u } =
|
|
380
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
329
381
|
return PEOPLE.map((name, i) => ({ name, team: TEAMS[i % TEAMS.length], performance: Math.round(8 + u() * 88), potential: Math.round(8 + u() * 88), tenure: round(0.5 + u() * 9) }));
|
|
330
382
|
}
|
|
383
|
+
/** `{ round, holder, shares }`: the fully diluted shares each holder of an invented company has after each funding round. */
|
|
384
|
+
static capTable(seed = 1) {
|
|
385
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
386
|
+
const held = /* @__PURE__ */ new Map([["Founders", 8e6], ["Employee pool", 1e6]]);
|
|
387
|
+
return ROUNDS.flatMap(([round2, buyers]) => {
|
|
388
|
+
const stakes = buyers.map(([holder, stake]) => [holder, stake * (0.85 + u() * 0.3)]);
|
|
389
|
+
const before = [...held.values()].reduce((sum5, shares) => sum5 + shares, 0), sold = stakes.reduce((sum5, [, stake]) => sum5 + stake, 0);
|
|
390
|
+
for (const [holder, stake] of stakes) held.set(holder, (_nullishCoalesce(held.get(holder), () => ( 0))) + Math.round(before * stake / (1 - sold) / 1e3) * 1e3);
|
|
391
|
+
return [...held].map(([holder, shares]) => ({ round: round2, holder, shares }));
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
/** `{ month, balance, burn }`: an invented company's month-end cash and net burn over a year, with one month of net inflow. */
|
|
395
|
+
static burn(seed = 1) {
|
|
396
|
+
const { u, n } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
397
|
+
let balance = Math.round(4600 + u() * 600) * 1e3;
|
|
398
|
+
return Array.from({ length: 12 }, (_, i) => {
|
|
399
|
+
const burn = i === 4 ? -Math.round(40 + u() * 60) * 1e3 : Math.round(175 + 11 * i + n() * 22) * 1e3;
|
|
400
|
+
balance -= burn;
|
|
401
|
+
return { month: `${i < 2 ? 2025 : 2026}-${String((i + 10) % 12 + 1).padStart(2, "0")}`, balance, burn };
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/** `{ task, stage, lane, kind, owner, limit }`: an invented shop team's board, each card carrying its stage's work-in-progress limit. A few cards move on a stage by seed. */
|
|
405
|
+
static kanban(seed = 1) {
|
|
406
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
407
|
+
return CARDS.map(([task, stage, lane, kind], i) => ({ task, at: Math.min(STAGES.length - 1, stage + (u() < 0.2 ? 1 : 0)), lane, kind, owner: PEOPLE[i % PEOPLE.length] })).sort((a, b) => a.at - b.at).map(({ at, ...card }) => ({ ...card, stage: STAGES[at][0], limit: STAGES[at][1] }));
|
|
408
|
+
}
|
|
409
|
+
/** `{ objective, keyResult, progress, confidence }` for an invented software firm's quarter: progress 0 to 100, confidence 0 to 1. */
|
|
410
|
+
static okr(seed = 1) {
|
|
411
|
+
const { u } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
412
|
+
return OBJECTIVES.flatMap(([objective, results]) => results.map(([keyResult, progress, confidence]) => ({
|
|
413
|
+
objective,
|
|
414
|
+
keyResult,
|
|
415
|
+
progress: clamp(Math.round(progress + (u() - 0.5) * 16), 0, 100),
|
|
416
|
+
confidence: round(clamp(confidence + (u() - 0.5) * 0.2, 0, 1))
|
|
417
|
+
})));
|
|
418
|
+
}
|
|
419
|
+
/** `{ period, adopters }`: households taking up an invented product in each of its first eight years, drawn from a Bass curve with noise. */
|
|
420
|
+
static bass(seed = 1) {
|
|
421
|
+
const { u, n } = _chunkLNWTC72Ycjs.SampleData.rng(seed);
|
|
422
|
+
const model = BassModel.run({ p: 0.02 + u() * 0.012, q: 0.38 + u() * 0.1, m: Math.round(110 + u() * 40) * 1e3 }, 8);
|
|
423
|
+
return model.map((period) => ({ period: 2018 + period.t, adopters: Math.max(0, Math.round(period.adopters * (1 + n() * 0.035))) }));
|
|
424
|
+
}
|
|
331
425
|
};
|
|
332
426
|
|
|
333
427
|
// src/charts/strategycharts/StrategyWords.ts
|
|
@@ -341,6 +435,427 @@ var StrategyWords = class {
|
|
|
341
435
|
}
|
|
342
436
|
};
|
|
343
437
|
|
|
438
|
+
// src/charts/strategycharts/BassDiffusionChart.ts
|
|
439
|
+
var BassDiffusionView = /* @__PURE__ */ ((BassDiffusionView2) => {
|
|
440
|
+
BassDiffusionView2["Both"] = "both";
|
|
441
|
+
BassDiffusionView2["Adopters"] = "adopters";
|
|
442
|
+
BassDiffusionView2["Cumulative"] = "cumulative";
|
|
443
|
+
return BassDiffusionView2;
|
|
444
|
+
})(BassDiffusionView || {});
|
|
445
|
+
var OBSERVED = "Observed";
|
|
446
|
+
var MODEL = "Bass model";
|
|
447
|
+
var SOURCES = ["Innovators", "Imitators"];
|
|
448
|
+
var TITLES = { ["adopters" /* Adopters */]: "New adopters per period", ["cumulative" /* Cumulative */]: "Cumulative adopters" };
|
|
449
|
+
var LIMIT = 60;
|
|
450
|
+
var NEARLY = 0.985;
|
|
451
|
+
var PANE_GAP = 20;
|
|
452
|
+
var SIZE = 10.5;
|
|
453
|
+
var figure = (value) => value.toLocaleString("en", { maximumFractionDigits: Math.abs(value) < 100 ? 1 : 0 });
|
|
454
|
+
var BassDiffusionChart = (_class = class extends _chunkXX2YTCORcjs.CartesianChart {
|
|
455
|
+
static __initStatic() {this.meta = {
|
|
456
|
+
id: "bass-diffusion",
|
|
457
|
+
name: "Bass diffusion curve",
|
|
458
|
+
family: "trend" /* Trend */,
|
|
459
|
+
dimension: 2 /* Two */,
|
|
460
|
+
shape: "table" /* Table */,
|
|
461
|
+
description: "Fits the Bass model to observed adoptions and draws new adopters per period and the cumulative S-curve, dashed where they run past the data.",
|
|
462
|
+
tags: ["diffusion of innovations", "adoption", "s-curve", "forecast", "innovation", "imitation", "marketing"],
|
|
463
|
+
sample: (seed) => StrategySamples.bass(seed),
|
|
464
|
+
variants: {
|
|
465
|
+
view: ["both" /* Both */, "adopters" /* Adopters */, "cumulative" /* Cumulative */],
|
|
466
|
+
split: [false, true],
|
|
467
|
+
peak: [false, true]
|
|
468
|
+
}
|
|
469
|
+
}}
|
|
470
|
+
#x = null;
|
|
471
|
+
#ticks = 6;
|
|
472
|
+
defaults() {
|
|
473
|
+
return { period: "period", adopters: "adopters", view: "both" /* Both */, split: false, peak: false };
|
|
474
|
+
}
|
|
475
|
+
yTicks() {
|
|
476
|
+
return _nullishCoalesce(this.options.yTicks, () => ( this.#ticks));
|
|
477
|
+
}
|
|
478
|
+
/** The panes have a scale each, so with both drawn only the periods can zoom. */
|
|
479
|
+
zoomAxes() {
|
|
480
|
+
return this.options.view === "both" /* Both */ ? "x" /* X */ : "xy" /* XY */;
|
|
481
|
+
}
|
|
482
|
+
#rows() {
|
|
483
|
+
const { period, adopters } = this.options;
|
|
484
|
+
return StrategyRows.of(this.options.data).flatMap((row) => {
|
|
485
|
+
const seen = StrategyRows.number(row[adopters]);
|
|
486
|
+
return seen === null ? [] : [{ period: row[period], adopters: Math.max(0, seen) }];
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
/** The coefficients in use, and the model run from the first period until nearly the whole market has adopted. */
|
|
490
|
+
get model() {
|
|
491
|
+
const { innovation, imitation, market } = this.options, observed = this.#rows().map((row) => row.adopters);
|
|
492
|
+
const given = (value, most = Infinity) => value !== void 0 && Number.isFinite(value) && value > 0 && value <= most ? value : void 0;
|
|
493
|
+
const fit = BassModel.fit(observed), total = _d3.sum.call(void 0, observed);
|
|
494
|
+
const p = _nullishCoalesce(_nullishCoalesce(given(innovation, 1), () => ( _optionalChain([fit, 'optionalAccess', _10 => _10.p]))), () => ( 0.03)), q = _nullishCoalesce(_nullishCoalesce(given(imitation), () => ( _optionalChain([fit, 'optionalAccess', _11 => _11.q]))), () => ( 0.38));
|
|
495
|
+
const m = _nullishCoalesce(_nullishCoalesce(given(market), () => ( _optionalChain([fit, 'optionalAccess', _12 => _12.m]))), () => ( (total > 0 ? BassModel.market(p, q, total, observed.length) : 100)));
|
|
496
|
+
const coefficients = { p, q, m }, done = BassModel.run(coefficients, LIMIT).findIndex((period) => period.cumulative >= NEARLY * m);
|
|
497
|
+
const count2 = Math.max(observed.length + 2, Math.min(LIMIT, Math.max(6, done < 0 ? LIMIT : done + 2)));
|
|
498
|
+
return { coefficients, fitted: !!fit && [innovation, imitation, market].every((value) => given(value) === void 0), observed, periods: BassModel.run(coefficients, count2) };
|
|
499
|
+
}
|
|
500
|
+
/** Names period `t`, counted from 1: the observed names, carried on where they are evenly spaced numbers and as +1, +2 where they are not. */
|
|
501
|
+
#namer() {
|
|
502
|
+
const cells = this.#rows().map((row) => row.period), numbers = cells.map((cell) => StrategyRows.number(cell)), step = cells.length > 1 ? numbers[1] - numbers[0] : 1;
|
|
503
|
+
if (!cells.length) return (t) => `${t}`;
|
|
504
|
+
if (step && numbers.every((value, i) => value !== null && Math.abs(value - (numbers[0] + i * step)) < 1e-9)) return (t) => `${+(numbers[0] + (t - 1) * step).toFixed(6)}`;
|
|
505
|
+
return (t) => t <= cells.length ? _chunkQ5A2TY2Acjs.FrameworkText.clean(cells[t - 1]) || `${t}` : `+${t - cells.length}`;
|
|
506
|
+
}
|
|
507
|
+
tickFormat(scale) {
|
|
508
|
+
if (scale !== this.#x) return super.tickFormat(scale);
|
|
509
|
+
const name = this.#namer(), last = this.model.periods.length;
|
|
510
|
+
return (value) => Number.isInteger(+value) && +value >= 1 && +value <= last ? _chunkQ5A2TY2Acjs.FrameworkText.fit(name(+value), 64, 11) : "";
|
|
511
|
+
}
|
|
512
|
+
#panes(height, { coefficients, observed, periods }) {
|
|
513
|
+
const { view, peak } = this.options;
|
|
514
|
+
const kinds = view === "both" /* Both */ ? ["adopters" /* Adopters */, "cumulative" /* Cumulative */] : [view === "cumulative" /* Cumulative */ ? "cumulative" /* Cumulative */ : "adopters" /* Adopters */];
|
|
515
|
+
const upper = kinds.length > 1 ? Math.round((height - PANE_GAP) * 0.54) : height;
|
|
516
|
+
return kinds.map((kind, i) => {
|
|
517
|
+
const top = i ? upper + PANE_GAP : 0, bottom = i ? height : upper, bell = kind === "adopters" /* Adopters */;
|
|
518
|
+
const head = bell ? peak ? 36 : 18 : i ? 18 : 32;
|
|
519
|
+
const high = bell ? _d3.max.call(void 0, [...periods.map((period) => period.adopters), ...observed]) : Math.max(coefficients.m, _d3.sum.call(void 0, observed));
|
|
520
|
+
const scale = _d3.scaleLinear.call(void 0, [0, high || 1], [bottom, Math.min(bottom - 1, top + head)]);
|
|
521
|
+
return { kind, first: !i, top, bottom, scale: bell ? scale.nice(4) : scale };
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
scales(ctx) {
|
|
525
|
+
const fit = this.model;
|
|
526
|
+
this.#x = _d3.scaleLinear.call(void 0, [0.5, fit.periods.length + 0.5], [0, ctx.plot.width]);
|
|
527
|
+
const [first] = this.#panes(ctx.plot.height, fit);
|
|
528
|
+
const [low, high] = first.scale.range();
|
|
529
|
+
this.#ticks = Math.max(2, Math.min(6, Math.floor((low - high) / 24)));
|
|
530
|
+
return { x: this.#x, y: first.scale };
|
|
531
|
+
}
|
|
532
|
+
plotMarks(ctx, { x, y }) {
|
|
533
|
+
const { split, peak } = this.options, { palette, theme, plot } = ctx;
|
|
534
|
+
const fit = this.model, { observed, periods, coefficients } = fit, name = this.#namer(), seen = observed.length;
|
|
535
|
+
const colors = { observed: seen ? palette.color(OBSERVED) : theme.inkMuted, model: palette.color(MODEL), sources: split ? SOURCES.map((source) => palette.color(source)) : [] };
|
|
536
|
+
const panes = this.#panes(plot.height, fit).map((pane) => pane.first ? { ...pane, scale: y } : pane);
|
|
537
|
+
const top = BassModel.peak(periods);
|
|
538
|
+
const columns = ctx.marks("periods", "rect", periods, (period) => period.t).attr("fill", "transparent");
|
|
539
|
+
ctx.set(columns, { x: (period) => x(period.t - 0.5), y: 0, width: (period) => Math.max(0, x(period.t + 0.5) - x(period.t - 0.5)), height: plot.height });
|
|
540
|
+
ctx.hover(columns, (period) => ({
|
|
541
|
+
title: name(period.t),
|
|
542
|
+
color: colors.model,
|
|
543
|
+
rows: [
|
|
544
|
+
...period.t <= seen ? [[OBSERVED, figure(observed[period.t - 1])]] : [],
|
|
545
|
+
["Model adopters", figure(period.adopters)],
|
|
546
|
+
[SOURCES[0], figure(period.innovators)],
|
|
547
|
+
[SOURCES[1], figure(period.imitators)],
|
|
548
|
+
["Cumulative", figure(period.cumulative)],
|
|
549
|
+
["Share of market", `${figure(period.cumulative / coefficients.m * 100)}%`]
|
|
550
|
+
]
|
|
551
|
+
}));
|
|
552
|
+
const words = [];
|
|
553
|
+
let innovators = 0, counted = 0;
|
|
554
|
+
const running = periods.map((period) => {
|
|
555
|
+
innovators += period.innovators;
|
|
556
|
+
if (period.t <= seen) counted += observed[period.t - 1];
|
|
557
|
+
return { innovators, counted };
|
|
558
|
+
});
|
|
559
|
+
for (const pane of panes) {
|
|
560
|
+
const bell = pane.kind === "adopters" /* Adopters */;
|
|
561
|
+
const levels = periods.map((period, i) => bell ? { t: period.t, total: period.adopters, innovators: period.innovators, seen: period.t <= seen ? observed[i] : null } : { t: period.t, total: period.cumulative, innovators: running[i].innovators, seen: period.t <= seen ? running[i].counted : null });
|
|
562
|
+
if (!pane.first) StrategyPane.axis(ctx, pane.kind, pane.scale, Math.max(2, Math.min(4, Math.floor((pane.bottom - pane.top) / 28))), StrategyPane.compact);
|
|
563
|
+
words.push(...this.#drawPane(ctx, pane, x, levels, colors, fit, top, name));
|
|
564
|
+
}
|
|
565
|
+
words.push(...this.#heading(panes[0], fit, plot.width));
|
|
566
|
+
const rules = ctx.marks("peak-rule", "line", peak && top ? panes : [], (pane) => pane.kind).attr("stroke", theme.axis).attr("stroke-width", 1);
|
|
567
|
+
ctx.set(rules, { x1: x(_nullishCoalesce(_optionalChain([top, 'optionalAccess', _13 => _13.t]), () => ( 0))), x2: x(_nullishCoalesce(_optionalChain([top, 'optionalAccess', _14 => _14.t]), () => ( 0))), y1: (pane) => pane.scale.range()[1] - 6, y2: (pane) => pane.bottom });
|
|
568
|
+
StrategyWords.draw(ctx, "words", words);
|
|
569
|
+
}
|
|
570
|
+
/** The pane's title at the left and, on the first pane, the coefficients at the right where they fit beside it. */
|
|
571
|
+
#heading(pane, { coefficients: { p, q, m }, fitted, observed }, width) {
|
|
572
|
+
const figures = `p ${p.toFixed(3)}, q ${q.toFixed(3)}, m ${StrategyPane.compact(m)}`, whole = fitted ? `Fitted to ${observed.length} periods: ${figures}` : figures;
|
|
573
|
+
const room = width - _chunkQ5A2TY2Acjs.FrameworkText.width(TITLES[pane.kind], SIZE) - 24, text = [whole, figures].find((option) => _chunkQ5A2TY2Acjs.FrameworkText.width(option, SIZE) <= room);
|
|
574
|
+
return text ? [{ key: "coefficients", text, x: width - 2, y: pane.top + 7, anchor: "end", size: SIZE, halo: true }] : [];
|
|
575
|
+
}
|
|
576
|
+
#drawPane(ctx, pane, x, levels, colors, { observed, coefficients }, top, name) {
|
|
577
|
+
const { peak } = this.options, { theme, plot } = ctx, { kind, scale } = pane, bell = kind === "adopters" /* Adopters */;
|
|
578
|
+
const seen = observed.length, ahead = seen > 0 && seen < levels.length;
|
|
579
|
+
const spans = ahead ? [{ key: "fit", from: 1, to: seen, faded: false }, { key: "ahead", from: seen, to: levels.length, faded: true }] : [{ key: "fit", from: 1, to: levels.length, faded: false }];
|
|
580
|
+
const within = (span) => levels.filter((level) => level.t >= span.from && level.t <= span.to);
|
|
581
|
+
const curve = _chunkPY4R3IFFcjs.Scales.curve("monotone" /* Monotone */);
|
|
582
|
+
const market = bell ? [] : [coefficients.m];
|
|
583
|
+
const ceiling = ctx.marks(`axis-${kind}-market`, "line", market).attr("stroke", theme.axis).attr("stroke-dasharray", "4 3");
|
|
584
|
+
ctx.set(ceiling, { x1: 0, x2: plot.width, y1: (level) => scale(level), y2: (level) => scale(level) });
|
|
585
|
+
const layers = colors.sources.length ? spans.flatMap((span) => [0, 1].map((source) => ({ key: `${SOURCES[source]}/${span.key}`, source, faded: span.faded, levels: within(span) }))) : [];
|
|
586
|
+
const stack = _d3.area.call(void 0, ).x((level) => x(level.t)).curve(curve);
|
|
587
|
+
const fills = ctx.marks(`${kind}-sources`, "path", layers.filter((layer) => layer.levels.length > 1), (layer) => layer.key).attr("fill", (layer) => colors.sources[layer.source]).attr("fill-opacity", (layer) => layer.faded ? 0.28 : 0.72);
|
|
588
|
+
ctx.set(fills, { d: (layer) => _nullishCoalesce(stack.y0((level) => scale(layer.source ? level.innovators : 0)).y1((level) => scale(layer.source ? level.total : level.innovators))(layer.levels), () => ( "")) });
|
|
589
|
+
const whole = levels.at(-1);
|
|
590
|
+
ctx.hover(fills, (layer) => ({
|
|
591
|
+
title: SOURCES[layer.source],
|
|
592
|
+
color: colors.sources[layer.source],
|
|
593
|
+
rows: [["Span", layer.faded ? "Projected" : ahead ? "Fitted to the observed periods" : "Model"], ...whole && !bell ? [["Share of all adopters", `${figure((layer.source ? whole.total - whole.innovators : whole.innovators) / (whole.total || 1) * 100)}%`]] : []]
|
|
594
|
+
}));
|
|
595
|
+
const path = _d3.line.call(void 0, ).x((level) => x(level.t)).y((level) => scale(level.total)).curve(curve);
|
|
596
|
+
const drawn = ctx.marks(`${kind}-model`, "path", spans.filter((span) => !span.faded).map(within).filter((span) => span.length > 1)).attr("fill", "none").attr("stroke", colors.model).attr("stroke-width", 2).attr("stroke-linecap", "round");
|
|
597
|
+
ctx.set(drawn, { d: (span) => _nullishCoalesce(path(span), () => ( "")) });
|
|
598
|
+
ctx.drawOn(drawn);
|
|
599
|
+
const projected = ctx.marks(`${kind}-projection`, "path", spans.filter((span) => span.faded).map(within).filter((span) => span.length > 1)).attr("fill", "none").attr("stroke", colors.model).attr("stroke-width", 2).attr("stroke-dasharray", "6 4");
|
|
600
|
+
ctx.set(projected, { d: (span) => _nullishCoalesce(path(span), () => ( "")) });
|
|
601
|
+
const dots = ctx.marks(`${kind}-observed`, "circle", levels.filter((level) => level.seen !== null), (level) => level.t).attr("fill", colors.observed).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
602
|
+
ctx.set(dots, { cx: (level) => x(level.t), cy: (level) => scale(level.seen), r: 4 }, { r: 0 }, 12);
|
|
603
|
+
ctx.hover(dots, (level) => ({ title: name(level.t), color: colors.observed, rows: [[OBSERVED, figure(level.seen)], ["Model", figure(level.total)]] }));
|
|
604
|
+
const crest = peak && top ? levels.filter((level) => level.t === top.t) : [];
|
|
605
|
+
const turn = ctx.marks(`${kind}-peak`, "circle", crest, (level) => level.t).attr("fill", colors.model).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
606
|
+
ctx.set(turn, { cx: (level) => x(level.t), cy: (level) => scale(level.total), r: 5 }, { r: 0 });
|
|
607
|
+
ctx.hover(turn, (level) => ({ title: `Peak in ${name(level.t)}`, color: colors.model, rows: [["New adopters", figure(top.adopters)], ["Adopted by then", figure(top.cumulative)]] }));
|
|
608
|
+
const title = { key: `title/${kind}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(TITLES[kind], plot.width - 4, SIZE), x: 2, y: pane.top + 7, size: SIZE, weight: 600, tone: "ink", halo: true };
|
|
609
|
+
const words = [title];
|
|
610
|
+
if (!bell) {
|
|
611
|
+
const text = `Market potential ${StrategyPane.compact(coefficients.m)}`;
|
|
612
|
+
if (_chunkQ5A2TY2Acjs.FrameworkText.width(text, SIZE) + _chunkQ5A2TY2Acjs.FrameworkText.width(TITLES[kind], SIZE) + 24 <= plot.width || pane.first) words.push({ key: `market/${kind}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, plot.width - 4, SIZE), x: plot.width - 2, y: scale(coefficients.m) - 8, anchor: "end", size: SIZE, halo: true });
|
|
613
|
+
}
|
|
614
|
+
for (const level of crest) {
|
|
615
|
+
const text = `Peak ${name(level.t)}`, at = x(level.t), half = _chunkQ5A2TY2Acjs.FrameworkText.width(text, SIZE) / 2;
|
|
616
|
+
if (bell) words.push({ key: `peak/${kind}`, text, x: StrategyRows.clamp(at, half, Math.max(half, plot.width - half)), y: scale.range()[1] - 13, anchor: "middle", size: SIZE, weight: 600, tone: "ink", halo: true });
|
|
617
|
+
else if (pane.first && at + 10 + half * 2 <= plot.width) words.push({ key: `peak/${kind}`, text, x: at + 10, y: scale(level.total) + 9, size: SIZE, weight: 600, tone: "ink", halo: true });
|
|
618
|
+
}
|
|
619
|
+
return words;
|
|
620
|
+
}
|
|
621
|
+
}, _class.__initStatic(), _class);
|
|
622
|
+
|
|
623
|
+
// src/charts/strategycharts/BurnRunwayChart.ts
|
|
624
|
+
|
|
625
|
+
var BurnRunwayBalance = /* @__PURE__ */ ((BurnRunwayBalance2) => {
|
|
626
|
+
BurnRunwayBalance2["Area"] = "area";
|
|
627
|
+
BurnRunwayBalance2["Line"] = "line";
|
|
628
|
+
return BurnRunwayBalance2;
|
|
629
|
+
})(BurnRunwayBalance || {});
|
|
630
|
+
var BurnRunwayLayout = /* @__PURE__ */ ((BurnRunwayLayout2) => {
|
|
631
|
+
BurnRunwayLayout2["Panels"] = "panels";
|
|
632
|
+
BurnRunwayLayout2["Overlay"] = "overlay";
|
|
633
|
+
return BurnRunwayLayout2;
|
|
634
|
+
})(BurnRunwayLayout || {});
|
|
635
|
+
var CASH = "Cash balance";
|
|
636
|
+
var BURN = "Net burn";
|
|
637
|
+
var SHARE = 0.64;
|
|
638
|
+
var PANE_GAP2 = 18;
|
|
639
|
+
var THICK = 28;
|
|
640
|
+
var TICK = 42;
|
|
641
|
+
var MONTH = new Intl.DateTimeFormat("en", { month: "short", year: "2-digit" });
|
|
642
|
+
var figure2 = (value) => value.toLocaleString("en", { maximumFractionDigits: 1 });
|
|
643
|
+
var lasting = (value) => `${figure2(value)} ${figure2(value) === "1" ? "month" : "months"}`;
|
|
644
|
+
var BurnRunwayChart = (_class2 = class _BurnRunwayChart extends _chunkXX2YTCORcjs.CartesianChart {
|
|
645
|
+
static __initStatic2() {this.meta = {
|
|
646
|
+
id: "burn-runway",
|
|
647
|
+
name: "Burn and runway chart",
|
|
648
|
+
family: "financial" /* Financial */,
|
|
649
|
+
dimension: 2 /* Two */,
|
|
650
|
+
shape: "table" /* Table */,
|
|
651
|
+
description: "Plots month-end cash and monthly net burn, then extends the balance as a dashed line at the mean burn of the latest months to where it reaches zero.",
|
|
652
|
+
tags: ["cash", "burn rate", "runway", "start-up", "projection", "finance"],
|
|
653
|
+
sample: (seed) => StrategySamples.burn(seed),
|
|
654
|
+
variants: {
|
|
655
|
+
balanceMark: ["area" /* Area */, "line" /* Line */],
|
|
656
|
+
layout: ["panels" /* Panels */, "overlay" /* Overlay */],
|
|
657
|
+
window: [3, 6]
|
|
658
|
+
}
|
|
659
|
+
}}
|
|
660
|
+
/** Months until `balance` reaches zero at `rate` a month: 0 when it already has, null when the rate is not a burn. */
|
|
661
|
+
static runway(balance, rate) {
|
|
662
|
+
if (balance <= 0) return 0;
|
|
663
|
+
return rate !== null && rate > 0 ? balance / rate : null;
|
|
664
|
+
}
|
|
665
|
+
static #date(cell) {
|
|
666
|
+
if (cell instanceof Date) return Number.isNaN(+cell) ? null : cell;
|
|
667
|
+
const parts = /^(\d{4})-(\d{1,2})(?:-\d{1,2})?$/.exec(String(_nullishCoalesce(cell, () => ( ""))).trim());
|
|
668
|
+
return parts && +parts[2] >= 1 && +parts[2] <= 12 ? new Date(+parts[1], +parts[2] - 1, 1) : null;
|
|
669
|
+
}
|
|
670
|
+
/** `high` rounded up to a tick, leaving the low end of the axis where the data puts it: a month of net inflow should not open a whole step under zero. */
|
|
671
|
+
static #rounded(high, ticks) {
|
|
672
|
+
return high > 0 ? _d3.scaleLinear.call(void 0, ).domain([0, high]).nice(ticks).domain()[1] : high;
|
|
673
|
+
}
|
|
674
|
+
#every = 1;
|
|
675
|
+
#ticks = 6;
|
|
676
|
+
defaults() {
|
|
677
|
+
return { month: "month", balance: "balance", burn: "burn", balanceMark: "area" /* Area */, layout: "panels" /* Panels */, window: 3 };
|
|
678
|
+
}
|
|
679
|
+
yTicks() {
|
|
680
|
+
return _nullishCoalesce(this.options.yTicks, () => ( this.#ticks));
|
|
681
|
+
}
|
|
682
|
+
/** The pane under the balance has its own scale, so only the months can zoom. */
|
|
683
|
+
zoomAxes() {
|
|
684
|
+
return "x" /* X */;
|
|
685
|
+
}
|
|
686
|
+
/** The recorded months in order, each with its net burn where one is stated or can be worked out. */
|
|
687
|
+
get months() {
|
|
688
|
+
const { month, balance, burn } = this.options;
|
|
689
|
+
let before = null;
|
|
690
|
+
return StrategyRows.of(this.options.data).flatMap((row, i) => {
|
|
691
|
+
const cash = StrategyRows.number(row[balance]);
|
|
692
|
+
if (cash === null) return [];
|
|
693
|
+
const date = _BurnRunwayChart.#date(row[month]), fall = before === null ? null : before - cash;
|
|
694
|
+
before = cash;
|
|
695
|
+
return [{ label: date ? MONTH.format(date) : _chunkQ5A2TY2Acjs.FrameworkText.clean(row[month]) || `${i + 1}`, date, balance: cash, burn: _nullishCoalesce(StrategyRows.number(row[burn]), () => ( fall)) }];
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
get outlook() {
|
|
699
|
+
const recorded = this.months, last = recorded.at(-1), { window } = this.options;
|
|
700
|
+
const recent = recorded.slice(-(Number.isFinite(window) ? Math.max(1, Math.round(window)) : 3)).flatMap((month) => _nullishCoalesce(month.burn, () => ( []))), rate = _nullishCoalesce(_d3.mean.call(void 0, recent), () => ( null));
|
|
701
|
+
const runway = last ? _BurnRunwayChart.runway(last.balance, rate) : null;
|
|
702
|
+
const reach = runway === null || rate === null ? 0 : Math.min(runway, Math.max(6, recorded.length * 2));
|
|
703
|
+
const ahead = reach > 0 ? [..._d3.range.call(void 0, 0, Math.floor(reach) + 1), ...Number.isInteger(reach) ? [] : [reach]] : [];
|
|
704
|
+
return { rate, averaged: recent.length, months: runway, path: ahead.map((step) => ({ ahead: step, balance: step === runway ? 0 : last.balance - rate * step })) };
|
|
705
|
+
}
|
|
706
|
+
/** A label per band: the recorded months, then the months of the projection. */
|
|
707
|
+
#labels(recorded, { path }) {
|
|
708
|
+
const from = _optionalChain([recorded, 'access', _15 => _15.at, 'call', _16 => _16(-1), 'optionalAccess', _17 => _17.date]);
|
|
709
|
+
return [...recorded.map((month) => month.label), ..._d3.range.call(void 0, 1, Math.ceil(_nullishCoalesce(_optionalChain([path, 'access', _18 => _18.at, 'call', _19 => _19(-1), 'optionalAccess', _20 => _20.ahead]), () => ( 0))) + 1).map((step) => from ? MONTH.format(new Date(from.getFullYear(), from.getMonth() + step, 1)) : `+${step}`)];
|
|
710
|
+
}
|
|
711
|
+
tickFormat(scale) {
|
|
712
|
+
if (!("bandwidth" in scale)) return super.tickFormat(scale);
|
|
713
|
+
const labels = this.#labels(this.months, this.outlook);
|
|
714
|
+
return (key) => +key % this.#every ? "" : _chunkQ5A2TY2Acjs.FrameworkText.fit(_nullishCoalesce(labels[+key], () => ( "")), TICK * this.#every, 11);
|
|
715
|
+
}
|
|
716
|
+
#pane(height, recorded, rate) {
|
|
717
|
+
const burns = recorded.flatMap((month) => _nullishCoalesce(month.burn, () => ( []))), low = Math.min(0, _nullishCoalesce(_d3.min.call(void 0, burns), () => ( 0))), high = Math.max(0, _nullishCoalesce(_d3.max.call(void 0, burns), () => ( 0)), _nullishCoalesce(rate, () => ( 0)));
|
|
718
|
+
return _d3.scaleLinear.call(void 0, [low, _BurnRunwayChart.#rounded(high > low ? high : low + 1, 4)], [height, Math.round(height * SHARE) + PANE_GAP2]);
|
|
719
|
+
}
|
|
720
|
+
scales(ctx) {
|
|
721
|
+
const recorded = this.months, outlook = this.outlook, overlay = this.options.layout === "overlay" /* Overlay */;
|
|
722
|
+
const x = _d3.scaleBand.call(void 0, this.#labels(recorded, outlook).map((_, i) => `${i}`), [0, ctx.plot.width]).paddingInner(0.3).paddingOuter(0.2);
|
|
723
|
+
this.#every = Math.max(1, Math.ceil(TICK / Math.max(1, x.step())));
|
|
724
|
+
const values = [...recorded.map((month) => month.balance), ...overlay ? recorded.flatMap((month) => _nullishCoalesce(month.burn, () => ( []))) : []];
|
|
725
|
+
const low = Math.min(0, _nullishCoalesce(_d3.min.call(void 0, values), () => ( 0))), high = _nullishCoalesce(_d3.max.call(void 0, values), () => ( 1)), base = overlay ? ctx.plot.height : Math.round(ctx.plot.height * SHARE);
|
|
726
|
+
this.#ticks = Math.max(2, Math.min(6, Math.floor(base / 24)));
|
|
727
|
+
return { x, y: _d3.scaleLinear.call(void 0, [low, _BurnRunwayChart.#rounded(high > low ? high : low + 1, this.#ticks)], [base, 0]) };
|
|
728
|
+
}
|
|
729
|
+
plotMarks(ctx, { x, y }) {
|
|
730
|
+
const { balanceMark, layout } = this.options, { palette, theme, plot } = ctx;
|
|
731
|
+
const recorded = this.months, outlook = this.outlook, { rate, path } = outlook, labels = this.#labels(recorded, outlook);
|
|
732
|
+
const cash = palette.color(CASH), spend = palette.color(BURN), latest = recorded.length - 1;
|
|
733
|
+
const pane = layout === "overlay" /* Overlay */ ? y : this.#pane(plot.height, recorded, rate);
|
|
734
|
+
const centre = (index) => (_nullishCoalesce(x(`${Math.floor(index)}`), () => ( 0))) + x.bandwidth() / 2 + (index - Math.floor(index)) * x.step();
|
|
735
|
+
const told = (month) => ({ title: month.label, rows: [[CASH, StrategyPane.compact(month.balance)], ...month.burn === null ? [] : [[BURN, StrategyPane.compact(month.burn)]]] });
|
|
736
|
+
if (pane !== y) StrategyPane.axis(ctx, "burn", pane, 2, StrategyPane.compact);
|
|
737
|
+
const edge = path.length ? [centre(latest) + x.step() / 2] : [];
|
|
738
|
+
const divider = ctx.marks("axis-now", "line", edge).attr("stroke", theme.axis).attr("stroke-dasharray", "2 3");
|
|
739
|
+
ctx.set(divider, { x1: (at) => at, x2: (at) => at, y1: 0, y2: plot.height });
|
|
740
|
+
const frame = new (0, _chunkNEXADWQScjs.BarFrame)(true, pane, x), thick = Math.min(x.bandwidth(), THICK);
|
|
741
|
+
const spent = recorded.flatMap((month, i) => month.burn === null ? [] : [{ key: `${i}`, month, burn: month.burn }]);
|
|
742
|
+
const bars = ctx.marks("burn", "path", spent, (bar) => bar.key).attr("fill", spend);
|
|
743
|
+
ctx.set(bars, { d: (bar) => frame.bar(centre(+bar.key) - thick / 2, thick, 0, bar.burn, 3) }, { d: (bar) => frame.bar(centre(+bar.key) - thick / 2, thick, 0, 0, 0) }, 10);
|
|
744
|
+
ctx.hover(bars, (bar) => ({ ...told(bar.month), color: spend }));
|
|
745
|
+
const averaged = rate === null ? [] : [{ from: centre(Math.max(0, recorded.length - outlook.averaged)) - x.step() / 2, to: path.length ? centre(latest + path.at(-1).ahead) : centre(latest) + x.step() / 2 }];
|
|
746
|
+
const level = ctx.marks("burn-rate", "line", averaged).attr("stroke", spend).attr("stroke-width", 1.5).attr("stroke-dasharray", "5 4");
|
|
747
|
+
ctx.set(level, { x1: (span) => span.from, x2: (span) => span.to, y1: pane(_nullishCoalesce(rate, () => ( 0))), y2: pane(_nullishCoalesce(rate, () => ( 0))) });
|
|
748
|
+
ctx.hover(level, () => ({ title: "Recent burn rate", color: spend, rows: [["Mean net burn", `${StrategyPane.compact(_nullishCoalesce(rate, () => ( 0)))} a month`], ["Months averaged", outlook.averaged]] }));
|
|
749
|
+
const past = recorded.map((month, i) => [centre(i), y(month.balance)]), ahead = path.map((step) => [centre(latest + step.ahead), y(step.balance)]);
|
|
750
|
+
const under = _d3.area.call(void 0, ).x((point) => point[0]).y0(y(0)).y1((point) => point[1]);
|
|
751
|
+
const filled = balanceMark === "area" /* Area */ ? [{ key: "recorded", points: past, opacity: 0.2 }, { key: "projected", points: ahead, opacity: 0.08 }].filter((part) => part.points.length > 1) : [];
|
|
752
|
+
const fills = ctx.marks("balance-area", "path", filled, (part) => part.key).attr("fill", cash).attr("fill-opacity", (part) => part.opacity);
|
|
753
|
+
ctx.set(fills, { d: (part) => _nullishCoalesce(under(part.points), () => ( "")) });
|
|
754
|
+
const traced = ctx.marks("balance", "path", past.length > 1 ? [past] : []).attr("fill", "none").attr("stroke", cash).attr("stroke-width", 2).attr("stroke-linejoin", "round");
|
|
755
|
+
ctx.set(traced, { d: (points2) => _nullishCoalesce(_d3.line.call(void 0, )(points2), () => ( "")) });
|
|
756
|
+
ctx.drawOn(traced);
|
|
757
|
+
ctx.hover(traced, () => ({ title: CASH, color: cash, rows: [["Latest", StrategyPane.compact(recorded[latest].balance)], ["Months recorded", recorded.length]] }));
|
|
758
|
+
const runway = outlook.months === null ? "None at this rate" : lasting(outlook.months);
|
|
759
|
+
const dashed = ctx.marks("projection", "path", ahead.length > 1 ? [ahead] : []).attr("fill", "none").attr("stroke", cash).attr("stroke-width", 2).attr("stroke-dasharray", "6 4");
|
|
760
|
+
ctx.set(dashed, { d: (points2) => _nullishCoalesce(_d3.line.call(void 0, )(points2), () => ( "")) });
|
|
761
|
+
ctx.hover(dashed, () => ({ title: "Balance at the recent burn rate", color: cash, rows: [["Burn rate", `${StrategyPane.compact(_nullishCoalesce(rate, () => ( 0)))} a month`], ["Runway", runway]] }));
|
|
762
|
+
const dotted = recorded.map((month, i) => ({ key: `${i}`, month, at: past[i] })).filter((dot, i) => i === latest || balanceMark === "line" /* Line */ && x.step() >= 12);
|
|
763
|
+
const dots = ctx.marks("months", "circle", dotted, (dot) => dot.key).attr("fill", cash).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
764
|
+
ctx.set(dots, { cx: (dot) => dot.at[0], cy: (dot) => dot.at[1], r: 4 }, { r: 0 }, 10);
|
|
765
|
+
ctx.hover(dots, (dot) => ({ ...told(dot.month), color: cash }));
|
|
766
|
+
const spentOut = path.length && path.at(-1).ahead === outlook.months ? [ahead.at(-1)] : [];
|
|
767
|
+
const end = ctx.marks("cash-out", "circle", spentOut).attr("fill", theme.surface).attr("stroke", cash).attr("stroke-width", 2);
|
|
768
|
+
ctx.set(end, { cx: (point) => point[0], cy: (point) => point[1], r: 4.5 }, { r: 0 });
|
|
769
|
+
ctx.hover(end, () => ({ title: `Reaches zero in ${labels.at(-1)}`, color: cash, rows: [["Runway", runway], ["Burn rate", `${StrategyPane.compact(_nullishCoalesce(rate, () => ( 0)))} a month`]] }));
|
|
770
|
+
StrategyWords.draw(ctx, "notes", this.#notes(recorded, outlook, plot.width));
|
|
771
|
+
}
|
|
772
|
+
/** The runway in words: at the right over the projection, or at the left when there is none to draw. */
|
|
773
|
+
#notes(recorded, { rate, averaged, months: runway, path }, width) {
|
|
774
|
+
if (!recorded.length) return [];
|
|
775
|
+
const span = averaged === 1 ? "month" : `${averaged} months`, room = Math.min(width - 8, width * 0.6);
|
|
776
|
+
const rated = (lead, burn) => [`, the mean of the last ${span}`, ` (${averaged}-month mean)`, ""].map((tail) => `${lead} ${StrategyPane.compact(burn)} a month${tail}`).filter((text) => _chunkQ5A2TY2Acjs.FrameworkText.width(text, 10.5) <= room).slice(0, 1);
|
|
777
|
+
const lines = path.length ? [`Runway ${lasting(runway)}`, ...rated("at", rate)] : runway === 0 ? ["Balance is at or below zero"] : rate === null ? ["No net burn recorded to project from"] : ["Cash is not falling at the recent rate", ...rated("net burn", rate)];
|
|
778
|
+
return lines.map((text, row) => ({
|
|
779
|
+
key: `note/${row}`,
|
|
780
|
+
text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, width - 8, 11),
|
|
781
|
+
full: text,
|
|
782
|
+
x: path.length ? width - 2 : 4,
|
|
783
|
+
y: 7 + row * 14,
|
|
784
|
+
anchor: path.length ? "end" : "start",
|
|
785
|
+
size: row ? 10.5 : 11.5,
|
|
786
|
+
weight: row ? 400 : 600,
|
|
787
|
+
tone: row ? "secondary" : "ink",
|
|
788
|
+
halo: true
|
|
789
|
+
}));
|
|
790
|
+
}
|
|
791
|
+
}, _class2.__initStatic2(), _class2);
|
|
792
|
+
|
|
793
|
+
// src/charts/strategycharts/StrategyNotes.ts
|
|
794
|
+
var SIZES = [11, 10, 9];
|
|
795
|
+
var DOT = 3;
|
|
796
|
+
var INDENT = 10;
|
|
797
|
+
var CARD_PAD = 4;
|
|
798
|
+
var TIP_WIDTH = 320;
|
|
799
|
+
var StrategyNotes = class _StrategyNotes {
|
|
800
|
+
static plan(boxes, style) {
|
|
801
|
+
let blocks = [];
|
|
802
|
+
for (const size of SIZES) {
|
|
803
|
+
const filled = boxes.map((box) => _StrategyNotes.#fill(box, size, style));
|
|
804
|
+
blocks = filled.flatMap((fill) => fill.blocks);
|
|
805
|
+
if (filled.every((fill) => fill.whole)) break;
|
|
806
|
+
}
|
|
807
|
+
return blocks;
|
|
808
|
+
}
|
|
809
|
+
static #fill(box, size, style) {
|
|
810
|
+
const { notes } = box, lead = Math.round(size * 1.25), pad = style === "card" ? CARD_PAD : 0, gap = style === "bullet" ? 2 : 3;
|
|
811
|
+
const indent = style === "bullet" ? INDENT : pad, width = box.width - indent - pad;
|
|
812
|
+
if (width < size * 2 || box.height < lead) return { blocks: [], whole: !notes.length };
|
|
813
|
+
const wrapped = notes.map((note) => _chunkQ5A2TY2Acjs.FrameworkText.wrap(note.text, width, size));
|
|
814
|
+
const tall = (lines) => lines.length * lead + pad;
|
|
815
|
+
const cost = (count2) => wrapped.slice(0, count2).reduce((sum5, lines) => sum5 + tall(lines) + gap, 0);
|
|
816
|
+
let shown = notes.length;
|
|
817
|
+
if (cost(shown) - gap > box.height) {
|
|
818
|
+
shown--;
|
|
819
|
+
while (shown > 0 && cost(shown) + lead > box.height) shown--;
|
|
820
|
+
}
|
|
821
|
+
let cut = false;
|
|
822
|
+
if (shown === 0 && notes.length) {
|
|
823
|
+
const rows = Math.floor((box.height - pad - (notes.length > 1 ? lead + gap : 0)) / lead);
|
|
824
|
+
if (rows >= 1) {
|
|
825
|
+
cut = wrapped[0].length > rows;
|
|
826
|
+
wrapped[0] = _chunkQ5A2TY2Acjs.FrameworkText.clamp(wrapped[0], rows, width, size);
|
|
827
|
+
shown = 1;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
let top = box.y;
|
|
831
|
+
const place = (note, hidden2, lines, key) => {
|
|
832
|
+
const height = note ? tall(lines) : lead, inset = note ? pad / 2 : 0;
|
|
833
|
+
const block = { key, box, note, hidden: hidden2, lines, x: box.x + (note ? indent : 0), y: top + inset + size - 1, lead, left: box.x, top, width: box.width, height, size };
|
|
834
|
+
top += height + gap;
|
|
835
|
+
return block;
|
|
836
|
+
};
|
|
837
|
+
const blocks = notes.slice(0, shown).map((note, i) => place(note, [], wrapped[i], `${box.key}:${i}`));
|
|
838
|
+
const hidden = notes.slice(shown);
|
|
839
|
+
if (hidden.length && top + lead <= box.y + box.height + 0.5) blocks.push(place(null, hidden, [`+${hidden.length} more`], `${box.key}:more`));
|
|
840
|
+
return { blocks, whole: !hidden.length && !cut };
|
|
841
|
+
}
|
|
842
|
+
static draw(ctx, name, blocks, style) {
|
|
843
|
+
const items = ctx.marks(name, "g", blocks, (block) => block.key), tint = (block) => _nullishCoalesce(_optionalChain([block, 'access', _21 => _21.note, 'optionalAccess', _22 => _22.color]), () => ( block.box.color));
|
|
844
|
+
ctx.hover(items, (block) => {
|
|
845
|
+
if (block.note) return _chunkQ5A2TY2Acjs.FrameworkText.tip(block.note.text, _nullishCoalesce(block.note.rows, () => ( [])), tint(block));
|
|
846
|
+
return { title: `${block.hidden.length} more`, color: block.box.color, rows: block.hidden.flatMap((note) => _chunkQ5A2TY2Acjs.FrameworkText.wrap(note.text, TIP_WIDTH, 12).map((line8) => [line8, ""])) };
|
|
847
|
+
});
|
|
848
|
+
const carded = (block) => style === "card" && !!block.note;
|
|
849
|
+
const backs = items.selectAll(":scope > rect").data((block) => [block]).join("rect").attr("rx", 3).attr("fill", (block) => carded(block) ? tint(block) : "transparent").attr("fill-opacity", (block) => carded(block) ? 0.22 : null);
|
|
850
|
+
ctx.set(backs, { x: (block) => block.left, y: (block) => block.top, width: (block) => block.width, height: (block) => block.height });
|
|
851
|
+
const dots = items.selectAll("circle").data((block) => style === "bullet" && block.note ? [block] : []).join("circle").attr("r", DOT).attr("fill", tint).attr("stroke", ctx.theme.surface).attr("stroke-width", 2);
|
|
852
|
+
ctx.set(dots, { cx: (block) => block.left + DOT + 1, cy: (block) => block.top + block.lead / 2 });
|
|
853
|
+
const body = ctx.text(items.selectAll("text").data((block) => [block]).join("text"), 11, "ink").attr("font-size", (block) => block.size).attr("fill", (block) => block.note ? ctx.theme.ink : ctx.theme.inkSecondary).attr("pointer-events", "none");
|
|
854
|
+
_chunkQ5A2TY2Acjs.FrameworkText.write(ctx, body);
|
|
855
|
+
return items;
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
|
|
344
859
|
// src/charts/strategycharts/BusinessModelCanvas.ts
|
|
345
860
|
var BusinessModelCanvasKind = /* @__PURE__ */ ((BusinessModelCanvasKind2) => {
|
|
346
861
|
BusinessModelCanvasKind2["BusinessModel"] = "business-model";
|
|
@@ -352,7 +867,7 @@ var BusinessModelCanvasNotes = /* @__PURE__ */ ((BusinessModelCanvasNotes2) => {
|
|
|
352
867
|
BusinessModelCanvasNotes2["Sticky"] = "sticky";
|
|
353
868
|
return BusinessModelCanvasNotes2;
|
|
354
869
|
})(BusinessModelCanvasNotes || {});
|
|
355
|
-
var slot = (name, short,
|
|
870
|
+
var slot = (name, short, area5, step, column, row, columns, rows, pattern) => ({ name, short, area: area5, step, column, row, columns, rows, pattern });
|
|
356
871
|
var finances = (cost, revenue) => [slot("Cost Structure", "Costs", "Finances", cost, 0, 2, 5, 1, /cost/), slot("Revenue Streams", "Revenue", "Finances", revenue, 5, 2, 5, 1, /revenue/)];
|
|
357
872
|
var SLOTS = {
|
|
358
873
|
["business-model" /* BusinessModel */]: [
|
|
@@ -382,8 +897,8 @@ var GAP = 4;
|
|
|
382
897
|
var PAD = 4;
|
|
383
898
|
var HEAD_SIZE = 10;
|
|
384
899
|
var BADGE = 7;
|
|
385
|
-
var BusinessModelCanvas = (
|
|
386
|
-
static
|
|
900
|
+
var BusinessModelCanvas = (_class3 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
901
|
+
static __initStatic3() {this.meta = {
|
|
387
902
|
id: "business-model-canvas",
|
|
388
903
|
name: "Business model canvas",
|
|
389
904
|
family: "matrix" /* Matrix */,
|
|
@@ -459,10 +974,211 @@ var BusinessModelCanvas = (_class = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
459
974
|
const style = noteStyle === "sticky" /* Sticky */ ? "card" : "bullet";
|
|
460
975
|
StrategyNotes.draw(ctx, "notes", StrategyNotes.plan(lists, style), style);
|
|
461
976
|
}
|
|
462
|
-
},
|
|
977
|
+
}, _class3.__initStatic3(), _class3);
|
|
978
|
+
|
|
979
|
+
// src/charts/strategycharts/CapTableChart.ts
|
|
980
|
+
|
|
981
|
+
var CapTableView = /* @__PURE__ */ ((CapTableView2) => {
|
|
982
|
+
CapTableView2["Rounds"] = "rounds";
|
|
983
|
+
CapTableView2["Single"] = "single-round";
|
|
984
|
+
CapTableView2["Dilution"] = "dilution";
|
|
985
|
+
return CapTableView2;
|
|
986
|
+
})(CapTableView || {});
|
|
987
|
+
var CapTableBasis = /* @__PURE__ */ ((CapTableBasis2) => {
|
|
988
|
+
CapTableBasis2["Percent"] = "percent";
|
|
989
|
+
CapTableBasis2["Shares"] = "shares";
|
|
990
|
+
return CapTableBasis2;
|
|
991
|
+
})(CapTableBasis || {});
|
|
992
|
+
var LATEST = "Latest";
|
|
993
|
+
var SINGLE = 84;
|
|
994
|
+
var SIZE2 = 10.5;
|
|
995
|
+
var HEAD = 14;
|
|
996
|
+
var figure3 = (value, digits = 1) => value.toLocaleString("en", { maximumFractionDigits: digits });
|
|
997
|
+
var percent = (share, digits = 1) => `${figure3(share * 100, digits)}%`;
|
|
998
|
+
var points = (change) => `${change > 0 ? "+" : change < 0 ? "\u2212" : ""}${figure3(Math.abs(change) * 100)} pts`;
|
|
999
|
+
var count = (shares) => Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(shares);
|
|
1000
|
+
var CapTableChart = (_class4 = class _CapTableChart extends _chunkXX2YTCORcjs.CartesianChart {
|
|
1001
|
+
static __initStatic4() {this.meta = {
|
|
1002
|
+
id: "cap-table",
|
|
1003
|
+
name: "Cap table chart",
|
|
1004
|
+
family: "composition" /* Composition */,
|
|
1005
|
+
dimension: 2 /* Two */,
|
|
1006
|
+
shape: "table" /* Table */,
|
|
1007
|
+
description: "Stacks each holder's share of the fully diluted total for every funding round, with ribbons that show dilution from one round to the next.",
|
|
1008
|
+
tags: ["capitalisation table", "ownership", "dilution", "funding rounds", "equity", "start-up"],
|
|
1009
|
+
sample: (seed) => StrategySamples.capTable(seed),
|
|
1010
|
+
variants: {
|
|
1011
|
+
view: ["rounds" /* Rounds */, "single-round" /* Single */, "dilution" /* Dilution */],
|
|
1012
|
+
orientation: ["vertical" /* Vertical */, "horizontal" /* Horizontal */],
|
|
1013
|
+
basis: ["percent" /* Percent */, "shares" /* Shares */],
|
|
1014
|
+
ribbons: [true, false]
|
|
1015
|
+
},
|
|
1016
|
+
/** One bar has no neighbour to join and the same proportions on either basis, and the waterfall is in points of ownership. */
|
|
1017
|
+
valid: (values) => values.view === "rounds" /* Rounds */ || values.basis === "percent" /* Percent */ && values.ribbons === true
|
|
1018
|
+
}}
|
|
1019
|
+
/** The waterfall of `holder`'s ownership: its share after the first round it holds in, the change at each later round, and where it stands. */
|
|
1020
|
+
static dilution(rounds, holder) {
|
|
1021
|
+
const shares = rounds.map((round2) => _nullishCoalesce(_optionalChain([round2, 'access', _23 => _23.stakes, 'access', _24 => _24.find, 'call', _25 => _25((stake) => stake.holder === holder), 'optionalAccess', _26 => _26.share]), () => ( 0))), first = shares.findIndex((share) => share > 0);
|
|
1022
|
+
if (first < 0) return [];
|
|
1023
|
+
const steps = shares.slice(first).map((share, i) => ({ label: rounds[first + i].round, from: i ? shares[first + i - 1] : 0, to: share, total: !i }));
|
|
1024
|
+
return steps.length > 1 ? [...steps, { label: LATEST, from: 0, to: shares.at(-1), total: true }] : steps;
|
|
1025
|
+
}
|
|
1026
|
+
#room = 84;
|
|
1027
|
+
defaults() {
|
|
1028
|
+
return { round: "round", holder: "holder", shares: "shares", view: "rounds" /* Rounds */, orientation: "vertical" /* Vertical */, basis: "percent" /* Percent */, ribbons: true };
|
|
1029
|
+
}
|
|
1030
|
+
get #vertical() {
|
|
1031
|
+
return this.options.orientation !== "horizontal" /* Horizontal */;
|
|
1032
|
+
}
|
|
1033
|
+
#ledger() {
|
|
1034
|
+
const { round: round2, holder, shares } = this.options;
|
|
1035
|
+
const held = /* @__PURE__ */ new Map(), holders = /* @__PURE__ */ new Set();
|
|
1036
|
+
for (const row of StrategyRows.of(this.options.data)) {
|
|
1037
|
+
const name = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[round2]), who = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[holder]), amount = StrategyRows.number(row[shares]);
|
|
1038
|
+
if (!name || !who || amount === null || amount < 0) continue;
|
|
1039
|
+
holders.add(who);
|
|
1040
|
+
const stakes = _nullishCoalesce(held.get(name), () => ( /* @__PURE__ */ new Map()));
|
|
1041
|
+
held.set(name, stakes.set(who, (_nullishCoalesce(stakes.get(who), () => ( 0))) + amount));
|
|
1042
|
+
}
|
|
1043
|
+
let earlier = null;
|
|
1044
|
+
const rounds = [...held].map(([name, stakes]) => {
|
|
1045
|
+
const total = _d3.sum.call(void 0, stakes.values()), before = earlier;
|
|
1046
|
+
const listed = [...holders].filter((who) => stakes.get(who)).map((who) => ({ round: name, holder: who, shares: stakes.get(who), share: stakes.get(who) / total, before: before ? _nullishCoalesce(before.get(who), () => ( 0)) : null }));
|
|
1047
|
+
earlier = new Map(listed.map((stake) => [stake.holder, stake.share]));
|
|
1048
|
+
return { round: name, total, stakes: listed };
|
|
1049
|
+
});
|
|
1050
|
+
return { rounds, holders: [...holders] };
|
|
1051
|
+
}
|
|
1052
|
+
/** Each round in order, with every holder's stake in it. */
|
|
1053
|
+
get rounds() {
|
|
1054
|
+
return this.#ledger().rounds;
|
|
1055
|
+
}
|
|
1056
|
+
/** What the view draws: the stacked slices of the rounds shown, or the steps of the followed holder's waterfall. */
|
|
1057
|
+
#plan() {
|
|
1058
|
+
const { view, basis, focusRound, focusHolder } = this.options, { rounds, holders } = this.#ledger();
|
|
1059
|
+
const holder = holders.includes(_nullishCoalesce(focusHolder, () => ( ""))) ? focusHolder : _nullishCoalesce(holders[0], () => ( ""));
|
|
1060
|
+
if (view === "dilution" /* Dilution */) return { rounds, holders, holder, shown: [], slices: [], steps: _CapTableChart.dilution(rounds, holder).map((step, i) => ({ ...step, key: `${i}` })) };
|
|
1061
|
+
const shown = view === "single-round" /* Single */ ? rounds.filter((round2, i) => rounds.some((other) => other.round === focusRound) ? round2.round === focusRound : i === rounds.length - 1) : rounds;
|
|
1062
|
+
const slices = shown.flatMap((round2) => {
|
|
1063
|
+
let at = 0;
|
|
1064
|
+
return round2.stakes.map((stake, i) => {
|
|
1065
|
+
const from = at;
|
|
1066
|
+
at += basis === "shares" /* Shares */ ? stake.shares : stake.share * 100;
|
|
1067
|
+
return { key: `${stake.round}/${stake.holder}`, stake, from, to: at, outer: i === round2.stakes.length - 1 };
|
|
1068
|
+
});
|
|
1069
|
+
});
|
|
1070
|
+
return { rounds, holders, holder, shown, slices, steps: [] };
|
|
1071
|
+
}
|
|
1072
|
+
get #stepped() {
|
|
1073
|
+
return this.options.view === "dilution" /* Dilution */;
|
|
1074
|
+
}
|
|
1075
|
+
/** The followed holder is named over the waterfall, where a legend of one entry would not show. */
|
|
1076
|
+
plotMargin() {
|
|
1077
|
+
const base = super.plotMargin();
|
|
1078
|
+
return { ...base, top: base.top + (this.#stepped ? HEAD : 0) };
|
|
1079
|
+
}
|
|
1080
|
+
legendItems() {
|
|
1081
|
+
return this.#stepped ? [] : super.legendItems();
|
|
1082
|
+
}
|
|
1083
|
+
tickFormat(scale) {
|
|
1084
|
+
if ("bandwidth" in scale) {
|
|
1085
|
+
const steps = this.#stepped ? this.#plan().steps : null;
|
|
1086
|
+
return (name) => _chunkQ5A2TY2Acjs.FrameworkText.fit(steps ? _nullishCoalesce(_optionalChain([steps, 'access', _27 => _27[+name], 'optionalAccess', _28 => _28.label]), () => ( "")) : String(name), this.#room, 11);
|
|
1087
|
+
}
|
|
1088
|
+
return this.#stepped || this.options.basis !== "shares" /* Shares */ ? (value) => `${figure3(+value)}%` : super.tickFormat(scale);
|
|
1089
|
+
}
|
|
1090
|
+
scales(ctx) {
|
|
1091
|
+
const { view, basis, ribbons } = this.options, vertical = this.#vertical, { width, height } = ctx.plot;
|
|
1092
|
+
const { steps, shown } = this.#plan();
|
|
1093
|
+
const names = this.#stepped ? steps.map((step) => step.key) : shown.map((round2) => round2.round);
|
|
1094
|
+
const top = this.#stepped ? (_nullishCoalesce(_d3.max.call(void 0, steps, (step) => Math.max(step.from, step.to)), () => ( 1))) * 125 : basis === "shares" /* Shares */ ? _d3.max.call(void 0, shown, (round2) => round2.total) || 1 : 100;
|
|
1095
|
+
const band = _d3.scaleBand.call(void 0, names, [0, vertical ? width : height]).padding(view === "rounds" /* Rounds */ && ribbons ? 0.45 : 0.3);
|
|
1096
|
+
const value = _d3.scaleLinear.call(void 0, [0, top], vertical ? [height, 0] : [0, width]);
|
|
1097
|
+
this.#room = vertical ? Math.max(12, band.step() - 6) : 84;
|
|
1098
|
+
return vertical ? { x: band, y: value } : { x: value, y: band };
|
|
1099
|
+
}
|
|
1100
|
+
plotMarks(ctx, { x, y }) {
|
|
1101
|
+
const { view, basis, ribbons } = this.options, { palette, theme, plot } = ctx;
|
|
1102
|
+
const vertical = this.#vertical, frame = new (0, _chunkNEXADWQScjs.BarFrame)(vertical, vertical ? y : x, vertical ? x : y);
|
|
1103
|
+
const { rounds, holders, holder, slices, steps } = this.#plan();
|
|
1104
|
+
for (const name of holders) palette.color(name);
|
|
1105
|
+
const thick = view === "single-round" /* Single */ ? Math.min(frame.thick, SINGLE) : frame.thick;
|
|
1106
|
+
const at = (name) => (_nullishCoalesce(frame.band(name), () => ( 0))) + (frame.thick - thick) / 2;
|
|
1107
|
+
const joins = view === "rounds" /* Rounds */ && ribbons ? slices.flatMap((left) => {
|
|
1108
|
+
const next = rounds[rounds.findIndex((round2) => round2.round === left.stake.round) + 1];
|
|
1109
|
+
const right = next && slices.find((slice) => slice.stake.round === next.round && slice.stake.holder === left.stake.holder);
|
|
1110
|
+
return right ? [{ key: left.key, holder: left.stake.holder, left, right }] : [];
|
|
1111
|
+
}) : [];
|
|
1112
|
+
const ribbon = ({ left, right }) => {
|
|
1113
|
+
const near = at(left.stake.round) + thick, far = at(right.stake.round);
|
|
1114
|
+
return `M${frame.point(near, left.from)}L${frame.point(near, left.to)}L${frame.point(far, right.to)}L${frame.point(far, right.from)}Z`;
|
|
1115
|
+
};
|
|
1116
|
+
const bands = ctx.marks("ribbons", "path", joins, (join) => join.key).attr("fill", (join) => palette.color(join.holder)).attr("fill-opacity", 0.22);
|
|
1117
|
+
ctx.set(bands, { d: ribbon });
|
|
1118
|
+
ctx.hover(bands, ({ left, right }) => ({
|
|
1119
|
+
title: left.stake.holder,
|
|
1120
|
+
color: palette.color(left.stake.holder),
|
|
1121
|
+
rows: [[left.stake.round, percent(left.stake.share)], [right.stake.round, percent(right.stake.share)], ["Change", points(right.stake.share - left.stake.share)]]
|
|
1122
|
+
}));
|
|
1123
|
+
const bars = ctx.marks("stakes", "path", slices, (slice) => slice.key).attr("fill", (slice) => palette.color(slice.stake.holder));
|
|
1124
|
+
const shape = (slice, to) => frame.bar(at(slice.stake.round), thick, slice.from, to, slice.outer ? 4 : 0, slice.from ? 2 : 0);
|
|
1125
|
+
ctx.set(bars, { d: (slice) => shape(slice, slice.to) }, { d: (slice) => shape(slice, slice.from) }, 12);
|
|
1126
|
+
ctx.hover(bars, ({ stake }) => ({
|
|
1127
|
+
title: stake.holder,
|
|
1128
|
+
color: palette.color(stake.holder),
|
|
1129
|
+
rows: [["Round", stake.round], ["Shares", stake.shares.toLocaleString("en")], ["Ownership", percent(stake.share, 2)], ...stake.before === null ? [] : [["Since last round", points(stake.share - stake.before)]]]
|
|
1130
|
+
}));
|
|
1131
|
+
const words = slices.flatMap((slice) => {
|
|
1132
|
+
const { stake } = slice, across = Math.abs(frame.value(slice.to) - frame.value(slice.from)), start = at(stake.round);
|
|
1133
|
+
if (view === "single-round" /* Single */) return _CapTableChart.#named(slice, frame, start, thick, across, plot);
|
|
1134
|
+
const text = basis === "shares" /* Shares */ ? count(stake.shares) : percent(stake.share, 0), wide = _chunkQ5A2TY2Acjs.FrameworkText.width(text, SIZE2) + 6;
|
|
1135
|
+
if (vertical ? across < 14 || thick < wide : across < wide || thick < 14) return [];
|
|
1136
|
+
const [px, py] = frame.point(start + thick / 2, (slice.from + slice.to) / 2);
|
|
1137
|
+
return [{ key: slice.key, text, x: px, y: py, anchor: "middle", size: SIZE2, weight: 600, fill: palette.inkOn(palette.color(stake.holder)) }];
|
|
1138
|
+
});
|
|
1139
|
+
const fill = (step) => step.total ? palette.color(holder) : palette.diverging(step.to < step.from ? 0.9 : -0.9);
|
|
1140
|
+
const rules = steps.slice(0, -1).map((step, i) => ({ key: step.key, ...frame.rule(step.to * 100, at(step.key) + thick, at(steps[i + 1].key)) }));
|
|
1141
|
+
const links = ctx.marks("connectors", "line", rules, (rule) => rule.key).attr("stroke", theme.axis).attr("stroke-width", 1);
|
|
1142
|
+
ctx.set(links, { x1: (rule) => rule.x1, x2: (rule) => rule.x2, y1: (rule) => rule.y1, y2: (rule) => rule.y2 });
|
|
1143
|
+
const falls = ctx.marks("steps", "path", steps, (step) => step.key).attr("fill", fill);
|
|
1144
|
+
ctx.set(falls, { d: (step) => frame.bar(at(step.key), thick, step.from * 100, step.to * 100, 4) }, { d: (step) => frame.bar(at(step.key), thick, step.from * 100, step.from * 100, 0) }, 30);
|
|
1145
|
+
ctx.hover(falls, (step) => ({
|
|
1146
|
+
title: step.label,
|
|
1147
|
+
color: fill(step),
|
|
1148
|
+
rows: step.total ? [[holder, percent(step.to, 2)]] : [["Before", percent(step.from, 2)], ["After", percent(step.to, 2)], ["Change", points(step.to - step.from)]]
|
|
1149
|
+
}));
|
|
1150
|
+
words.push(...steps.flatMap((step) => {
|
|
1151
|
+
const end = Math.max(step.from, step.to) * 100, room = vertical ? frame.band.step() - 4 : plot.width - frame.value(end) - 5;
|
|
1152
|
+
const text = (step.total ? [percent(step.to), percent(step.to, 0)] : [points(step.to - step.from), points(step.to - step.from).replace(" pts", "")]).find((option) => _chunkQ5A2TY2Acjs.FrameworkText.width(option, SIZE2) <= room);
|
|
1153
|
+
if (!text) return [];
|
|
1154
|
+
const [px, py] = frame.point(at(step.key) + thick / 2, end, vertical ? -9 : 5);
|
|
1155
|
+
return [{ key: `step/${step.key}`, text, x: px, y: py, anchor: vertical ? "middle" : "start", size: SIZE2, weight: step.total ? 600 : 400, tone: "ink" }];
|
|
1156
|
+
}));
|
|
1157
|
+
if (steps.length) words.push({ key: "followed", text: _chunkQ5A2TY2Acjs.FrameworkText.fit(`${holder}: ownership after each round`, plot.width, 11), x: 0, y: 3 - HEAD, weight: 600, tone: "ink" });
|
|
1158
|
+
StrategyWords.draw(ctx, "labels", words);
|
|
1159
|
+
}
|
|
1160
|
+
/** The single bar names each slice outside it: its share before a column and its holder after, or both under a bar. Slices too thin for a line of text go unnamed. */
|
|
1161
|
+
static #named(slice, frame, start, thick, across, plot) {
|
|
1162
|
+
const { stake } = slice, [px, py] = frame.point(start + thick, (slice.from + slice.to) / 2), share = percent(stake.share);
|
|
1163
|
+
if (frame.vertical) {
|
|
1164
|
+
const name2 = _chunkQ5A2TY2Acjs.FrameworkText.fit(stake.holder, plot.width - px - 8, SIZE2);
|
|
1165
|
+
if (across < 13) return [];
|
|
1166
|
+
return [
|
|
1167
|
+
...start - 8 >= _chunkQ5A2TY2Acjs.FrameworkText.width(share, SIZE2) ? [{ key: slice.key, text: share, x: start - 8, y: py, anchor: "end", size: SIZE2 }] : [],
|
|
1168
|
+
...name2.length >= 4 ? [{ key: `${slice.key}/name`, text: name2, full: stake.holder, x: px + 8, y: py, size: SIZE2, tone: "ink" }] : []
|
|
1169
|
+
];
|
|
1170
|
+
}
|
|
1171
|
+
const name = _chunkQ5A2TY2Acjs.FrameworkText.fit(stake.holder, across - 4, SIZE2), named = name.length >= 4;
|
|
1172
|
+
if (py + 28 > plot.height || _chunkQ5A2TY2Acjs.FrameworkText.width(share, SIZE2) > across - 4) return [];
|
|
1173
|
+
return [
|
|
1174
|
+
...named ? [{ key: `${slice.key}/name`, text: name, full: stake.holder, x: px, y: py + 11, anchor: "middle", size: SIZE2, tone: "ink" }] : [],
|
|
1175
|
+
{ key: slice.key, text: share, x: px, y: py + (named ? 24 : 11), anchor: "middle", size: SIZE2 }
|
|
1176
|
+
];
|
|
1177
|
+
}
|
|
1178
|
+
}, _class4.__initStatic4(), _class4);
|
|
463
1179
|
|
|
464
1180
|
// src/charts/strategycharts/CustomerJourneyMap.ts
|
|
465
|
-
|
|
1181
|
+
|
|
466
1182
|
var CustomerJourneyStageMark = /* @__PURE__ */ ((CustomerJourneyStageMark2) => {
|
|
467
1183
|
CustomerJourneyStageMark2["Chevron"] = "chevron";
|
|
468
1184
|
CustomerJourneyStageMark2["Box"] = "box";
|
|
@@ -479,12 +1195,12 @@ var CustomerJourneyEmotionPlace = /* @__PURE__ */ ((CustomerJourneyEmotionPlace2
|
|
|
479
1195
|
return CustomerJourneyEmotionPlace2;
|
|
480
1196
|
})(CustomerJourneyEmotionPlace || {});
|
|
481
1197
|
var GAP2 = 3;
|
|
482
|
-
var
|
|
1198
|
+
var HEAD2 = 22;
|
|
483
1199
|
var POINT = 5;
|
|
484
1200
|
var PAD2 = 4;
|
|
485
1201
|
var LANE_NAME = 10.5;
|
|
486
|
-
var CustomerJourneyMap = (
|
|
487
|
-
static
|
|
1202
|
+
var CustomerJourneyMap = (_class5 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
1203
|
+
static __initStatic5() {this.meta = {
|
|
488
1204
|
id: "customer-journey-map",
|
|
489
1205
|
name: "Customer journey map",
|
|
490
1206
|
family: "flow" /* Flow */,
|
|
@@ -532,21 +1248,21 @@ var CustomerJourneyMap = (_class2 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
532
1248
|
const felt = stages.some((stage) => stage.emotion !== null);
|
|
533
1249
|
const gutter = Math.min(72, plot.width * 0.22), across = Math.max(0, plot.width - gutter - GAP2 * Math.max(0, stages.length - 1)) / Math.max(1, stages.length);
|
|
534
1250
|
const columns = stages.map((stage, i) => ({ ...stage, x: gutter + i * (across + GAP2), width: across }));
|
|
535
|
-
const body = Math.max(0, plot.height -
|
|
1251
|
+
const body = Math.max(0, plot.height - HEAD2 - GAP2);
|
|
536
1252
|
const moodRoom = felt ? lanes.length ? Math.min(72, Math.max(34, body * 0.28)) : body : 0;
|
|
537
1253
|
const laneRoom = lanes.length ? Math.max(0, body - moodRoom - GAP2 * (lanes.length - (felt ? 0 : 1))) / lanes.length : 0;
|
|
538
1254
|
const top = emotionPlace === "top" /* Top */ && felt;
|
|
539
|
-
const moodTop = top ?
|
|
540
|
-
const laneTop = (i) =>
|
|
1255
|
+
const moodTop = top ? HEAD2 + GAP2 : HEAD2 + GAP2 + lanes.length * (laneRoom + GAP2);
|
|
1256
|
+
const laneTop = (i) => HEAD2 + GAP2 + (top ? moodRoom + GAP2 : 0) + i * (laneRoom + GAP2);
|
|
541
1257
|
const header = (column, i) => {
|
|
542
1258
|
const { x, width } = column, point = Math.min(POINT, width / 3), end = x + width;
|
|
543
|
-
if (stageMark === "box" /* Box */) return `M${x + 3},0H${end - 3}Q${end},0 ${end},3V${
|
|
544
|
-
const tip = i < columns.length - 1 ? `L${end + point},${
|
|
545
|
-
return `M${x},0H${end}${tip}H${x}${i ? `L${x + point},${
|
|
1259
|
+
if (stageMark === "box" /* Box */) return `M${x + 3},0H${end - 3}Q${end},0 ${end},3V${HEAD2 - 3}Q${end},${HEAD2} ${end - 3},${HEAD2}H${x + 3}Q${x},${HEAD2} ${x},${HEAD2 - 3}V3Q${x},0 ${x + 3},0Z`;
|
|
1260
|
+
const tip = i < columns.length - 1 ? `L${end + point},${HEAD2 / 2}L${end},${HEAD2}` : `V${HEAD2}`;
|
|
1261
|
+
return `M${x},0H${end}${tip}H${x}${i ? `L${x + point},${HEAD2 / 2}` : ""}Z`;
|
|
546
1262
|
};
|
|
547
1263
|
const heads = ctx.marks("stages", "path", columns, (column) => column.name).attr("fill", theme.axis);
|
|
548
1264
|
ctx.set(heads, { d: header });
|
|
549
|
-
ctx.hover(heads, (column) => ({ title: column.name, rows: [["Notes", [...column.notes.values()].reduce((
|
|
1265
|
+
ctx.hover(heads, (column) => ({ title: column.name, rows: [["Notes", [...column.notes.values()].reduce((sum5, notes) => sum5 + notes.length, 0)], ...column.emotion === null ? [] : [["Emotion", +column.emotion.toFixed(2)]]] }));
|
|
550
1266
|
const cells = lanes.flatMap((lane, row) => columns.map((column) => ({ key: `${lane}/${column.name}`, lane, column, x: column.x, y: laneTop(row), width: column.width, height: laneRoom, color: palette.color(lane) })));
|
|
551
1267
|
const panels = ctx.marks("cells", "rect", cells, (cell) => cell.key).attr("rx", 3).attr("fill", (cell) => cell.color).attr("fill-opacity", 0.12);
|
|
552
1268
|
ctx.set(panels, { x: (cell) => cell.x, y: (cell) => cell.y, width: (cell) => cell.width, height: (cell) => cell.height });
|
|
@@ -562,7 +1278,7 @@ var CustomerJourneyMap = (_class2 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
562
1278
|
const lined = emotionMark === "line" /* Line */;
|
|
563
1279
|
const path = _d3.line.call(void 0, ).x((mood) => mood.x).y((mood) => mood.y).curve(_chunkPY4R3IFFcjs.Scales.curve("monotone" /* Monotone */));
|
|
564
1280
|
const trace = ctx.marks("emotion-line", "path", lined && moods.length > 1 ? [moods] : []).attr("fill", "none").attr("stroke", theme.inkSecondary).attr("stroke-width", 2);
|
|
565
|
-
ctx.set(trace, { d: (
|
|
1281
|
+
ctx.set(trace, { d: (points2) => _nullishCoalesce(path(points2), () => ( "")) });
|
|
566
1282
|
ctx.drawOn(trace);
|
|
567
1283
|
const dots = ctx.marks("emotion-dots", "circle", lined ? moods : [], (mood) => mood.column.name).attr("fill", (mood) => mood.fill).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
568
1284
|
ctx.set(dots, { cx: (mood) => mood.x, cy: (mood) => mood.y, r: 5 }, { r: 0 }, 30);
|
|
@@ -579,7 +1295,7 @@ var CustomerJourneyMap = (_class2 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
579
1295
|
const words = [
|
|
580
1296
|
...columns.map((column, i) => {
|
|
581
1297
|
const lead = stageMark === "chevron" /* Chevron */ && i ? Math.min(POINT, column.width / 3) : 0;
|
|
582
|
-
return { key: `stage/${column.name}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(column.name, column.width - lead - 2, 10.5), full: column.name, x: column.x + lead + (column.width - lead) / 2, y:
|
|
1298
|
+
return { key: `stage/${column.name}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(column.name, column.width - lead - 2, 10.5), full: column.name, x: column.x + lead + (column.width - lead) / 2, y: HEAD2 / 2, anchor: "middle", size: 10.5, weight: 600, fill: palette.inkOn(theme.axis) };
|
|
583
1299
|
}),
|
|
584
1300
|
...lanes.flatMap((lane, row) => laneWords(lane, laneTop(row), laneRoom, `lane/${lane}`)),
|
|
585
1301
|
...felt ? laneWords("Emotion", moodTop, moodRoom, "lane-emotion") : [],
|
|
@@ -597,7 +1313,7 @@ var CustomerJourneyMap = (_class2 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
597
1313
|
}));
|
|
598
1314
|
StrategyNotes.draw(ctx, "notes", StrategyNotes.plan(lists, "plain"), "plain");
|
|
599
1315
|
}
|
|
600
|
-
},
|
|
1316
|
+
}, _class5.__initStatic5(), _class5);
|
|
601
1317
|
|
|
602
1318
|
// src/charts/strategycharts/EisenhowerMatrix.ts
|
|
603
1319
|
var EisenhowerQuadrant = /* @__PURE__ */ ((EisenhowerQuadrant2) => {
|
|
@@ -625,12 +1341,12 @@ var KINDS = [
|
|
|
625
1341
|
];
|
|
626
1342
|
var GAP3 = 6;
|
|
627
1343
|
var RAIL = 18;
|
|
628
|
-
var
|
|
1344
|
+
var HEAD3 = 22;
|
|
629
1345
|
var SIDE = 128;
|
|
630
1346
|
var PAD3 = 6;
|
|
631
1347
|
var NOTE = 16;
|
|
632
|
-
var EisenhowerMatrix = (
|
|
633
|
-
static
|
|
1348
|
+
var EisenhowerMatrix = (_class6 = class _EisenhowerMatrix extends _chunkEEF5WRRZcjs.Chart2D {
|
|
1349
|
+
static __initStatic6() {this.meta = {
|
|
634
1350
|
id: "eisenhower-matrix",
|
|
635
1351
|
name: "Eisenhower matrix",
|
|
636
1352
|
family: "matrix" /* Matrix */,
|
|
@@ -695,16 +1411,16 @@ var EisenhowerMatrix = (_class3 = class _EisenhowerMatrix extends _chunkEEF5WRRZ
|
|
|
695
1411
|
const panels = ctx.marks("panels", "rect", boxes, (box) => box.kind.quadrant).attr("rx", 4).attr("fill", (box) => box.color).attr("fill-opacity", 0.1);
|
|
696
1412
|
ctx.set(panels, { x: (box) => box.x, y: (box) => box.y, width: (box) => box.width, height: (box) => box.height });
|
|
697
1413
|
const bands = ctx.marks("bands", "rect", boxes, (box) => box.kind.quadrant).attr("rx", 4).attr("fill", (box) => box.color);
|
|
698
|
-
ctx.set(bands, { x: (box) => box.x, y: (box) => box.y, width: (box) => box.side || box.width, height: (box) => box.side ? box.height : Math.min(
|
|
1414
|
+
ctx.set(bands, { x: (box) => box.x, y: (box) => box.y, width: (box) => box.side || box.width, height: (box) => box.side ? box.height : Math.min(HEAD3, box.height) });
|
|
699
1415
|
ctx.hover(bands, (box) => ({ title: box.kind.quadrant, color: box.color, rows: [["Tasks", box.tasks.length], [box.kind.why, ""]] }));
|
|
700
1416
|
const headed = (box) => box.side || box.width;
|
|
701
1417
|
const words = boxes.filter((box) => box.height >= 16).flatMap((box) => {
|
|
702
1418
|
const ink = palette.inkOn(box.color), room = headed(box) - PAD3 * 2 - 20, name = _chunkQ5A2TY2Acjs.FrameworkText.fit(box.kind.quadrant, room, 12), left = box.x + PAD3;
|
|
703
1419
|
const used = _chunkQ5A2TY2Acjs.FrameworkText.width(name, 12) + 8, why = _chunkQ5A2TY2Acjs.FrameworkText.fit(box.kind.why, box.side ? room + 20 : room - used, 10.5);
|
|
704
1420
|
const title = { key: `title/${box.kind.quadrant}`, text: name, x: left, y: box.y + 11, size: 12, weight: 600, fill: ink };
|
|
705
|
-
const
|
|
1421
|
+
const count2 = { key: `count/${box.kind.quadrant}`, text: `${box.tasks.length}`, x: box.x + headed(box) - PAD3, y: box.y + 11, anchor: "end", size: 12, fill: ink };
|
|
706
1422
|
const reason = box.side && box.height >= 38 ? [{ key: `why/${box.kind.quadrant}`, text: why, full: box.kind.why, x: left, y: box.y + 27, size: 10.5, fill: ink }] : !box.side && !framing && why === box.kind.why ? [{ key: `why/${box.kind.quadrant}`, text: why, x: left + used, y: box.y + 11, size: 10.5, fill: ink }] : [];
|
|
707
|
-
return [title, ...headed(box) >= 70 ? [
|
|
1423
|
+
return [title, ...headed(box) >= 70 ? [count2] : [], ...reason];
|
|
708
1424
|
});
|
|
709
1425
|
if (rail) {
|
|
710
1426
|
const columns = (frame.width - GAP3) / 2, rows = (frame.height - GAP3) / 2;
|
|
@@ -714,7 +1430,7 @@ var EisenhowerMatrix = (_class3 = class _EisenhowerMatrix extends _chunkEEF5WRRZ
|
|
|
714
1430
|
if (skipped) words.push({ key: "note", text: _chunkQ5A2TY2Acjs.FrameworkText.fit(`${skipped} ${skipped === 1 ? "row" : "rows"} left out: no task, or urgent or important not stated`, plot.width, 10.5), x: 0, y: plot.height - 6, size: 10.5, tone: "muted" });
|
|
715
1431
|
StrategyWords.draw(ctx, "words", words);
|
|
716
1432
|
const lists = boxes.map((box) => {
|
|
717
|
-
const top = box.side ? PAD3 :
|
|
1433
|
+
const top = box.side ? PAD3 : HEAD3 + PAD3 - 1;
|
|
718
1434
|
return {
|
|
719
1435
|
key: box.kind.quadrant,
|
|
720
1436
|
color: box.color,
|
|
@@ -728,7 +1444,7 @@ var EisenhowerMatrix = (_class3 = class _EisenhowerMatrix extends _chunkEEF5WRRZ
|
|
|
728
1444
|
const style = taskMark === "card" /* Card */ ? "card" : "bullet";
|
|
729
1445
|
StrategyNotes.draw(ctx, "tasks", StrategyNotes.plan(lists, style), style);
|
|
730
1446
|
}
|
|
731
|
-
},
|
|
1447
|
+
}, _class6.__initStatic6(), _class6);
|
|
732
1448
|
|
|
733
1449
|
// src/charts/strategycharts/FiveForcesDiagram.ts
|
|
734
1450
|
|
|
@@ -759,12 +1475,12 @@ var KINDS2 = [
|
|
|
759
1475
|
];
|
|
760
1476
|
var GAP4 = 20;
|
|
761
1477
|
var PAD4 = 6;
|
|
762
|
-
var
|
|
1478
|
+
var HEAD4 = 18;
|
|
763
1479
|
var PIP = 6;
|
|
764
1480
|
var METER = 32;
|
|
765
1481
|
var WIDE = 1.7;
|
|
766
|
-
var FiveForcesDiagram = (
|
|
767
|
-
static
|
|
1482
|
+
var FiveForcesDiagram = (_class7 = class _FiveForcesDiagram extends _chunkEEF5WRRZcjs.Chart2D {
|
|
1483
|
+
static __initStatic7() {this.meta = {
|
|
768
1484
|
id: "five-forces",
|
|
769
1485
|
name: "Five forces diagram",
|
|
770
1486
|
family: "matrix" /* Matrix */,
|
|
@@ -856,7 +1572,7 @@ var FiveForcesDiagram = (_class4 = class _FiveForcesDiagram extends _chunkEEF5WR
|
|
|
856
1572
|
const box = by.get(arrow.key);
|
|
857
1573
|
return { title: `${box.rated.name} on rivalry`, color: box.color, rows: [["Strength", score(box)], ["Level", box.rated.level]] };
|
|
858
1574
|
});
|
|
859
|
-
const metered = (box) => box.width >= METER + 60 && box.height >=
|
|
1575
|
+
const metered = (box) => box.width >= METER + 60 && box.height >= HEAD4;
|
|
860
1576
|
const pips = top <= 10 && Number.isInteger(top) ? top : 5, step = (METER + 2) / pips;
|
|
861
1577
|
const marks = boxes.filter(metered).flatMap((box) => {
|
|
862
1578
|
const left = box.x + box.width - PAD4 - METER, y = box.y + 6;
|
|
@@ -868,8 +1584,8 @@ var FiveForcesDiagram = (_class4 = class _FiveForcesDiagram extends _chunkEEF5WR
|
|
|
868
1584
|
const words = boxes.flatMap((box) => {
|
|
869
1585
|
const room = box.width - PAD4 * 2 - (metered(box) ? METER + 6 : 0), whole = _chunkQ5A2TY2Acjs.FrameworkText.fit(box.rated.name, room, 11);
|
|
870
1586
|
const head2 = { key: `name/${box.rated.force}`, text: whole === box.rated.name ? whole : _chunkQ5A2TY2Acjs.FrameworkText.fit(box.kind.short, room, 11), full: box.rated.name, x: box.x + PAD4, y: box.y + 9, size: 11, weight: 600, tone: "ink" };
|
|
871
|
-
if (drivers || box.height <
|
|
872
|
-
const middle = box.y +
|
|
1587
|
+
if (drivers || box.height < HEAD4 + 30) return box.height < 16 ? [] : [head2];
|
|
1588
|
+
const middle = box.y + HEAD4 + (box.height - HEAD4) / 2;
|
|
873
1589
|
return [
|
|
874
1590
|
head2,
|
|
875
1591
|
{ key: `score/${box.rated.force}`, text: box.rated.strength === null ? "n/a" : `${+box.rated.strength.toFixed(1)}`, x: box.x + box.width / 2, y: middle - 7, anchor: "middle", size: 18, weight: 600, tone: "ink" },
|
|
@@ -881,14 +1597,14 @@ var FiveForcesDiagram = (_class4 = class _FiveForcesDiagram extends _chunkEEF5WR
|
|
|
881
1597
|
key: box.rated.force,
|
|
882
1598
|
color: box.color,
|
|
883
1599
|
x: box.x + PAD4,
|
|
884
|
-
y: box.y +
|
|
1600
|
+
y: box.y + HEAD4,
|
|
885
1601
|
width: Math.max(0, box.width - PAD4 * 2),
|
|
886
|
-
height: Math.max(0, box.height -
|
|
1602
|
+
height: Math.max(0, box.height - HEAD4 - 2),
|
|
887
1603
|
notes: box.rated.drivers.map((driver) => ({ text: driver.text, rows: [["Force", box.rated.name], ["Strength", driver.strength === null ? "Not rated" : `${driver.strength} of ${top}`]] }))
|
|
888
1604
|
}));
|
|
889
1605
|
StrategyNotes.draw(ctx, "drivers", StrategyNotes.plan(lists, "bullet"), "bullet");
|
|
890
1606
|
}
|
|
891
|
-
},
|
|
1607
|
+
}, _class7.__initStatic7(), _class7);
|
|
892
1608
|
|
|
893
1609
|
// src/charts/strategycharts/FootballFieldChart.ts
|
|
894
1610
|
|
|
@@ -902,11 +1618,11 @@ var FootballFieldOrder = /* @__PURE__ */ ((FootballFieldOrder2) => {
|
|
|
902
1618
|
FootballFieldOrder2["Midpoint"] = "by-midpoint";
|
|
903
1619
|
return FootballFieldOrder2;
|
|
904
1620
|
})(FootballFieldOrder || {});
|
|
905
|
-
var
|
|
1621
|
+
var THICK2 = 22;
|
|
906
1622
|
var END = 5;
|
|
907
1623
|
var NOTE2 = 13;
|
|
908
|
-
var FootballFieldChart = (
|
|
909
|
-
static
|
|
1624
|
+
var FootballFieldChart = (_class8 = class _FootballFieldChart extends _chunkXX2YTCORcjs.CartesianChart {
|
|
1625
|
+
static __initStatic8() {this.meta = {
|
|
910
1626
|
id: "football-field",
|
|
911
1627
|
name: "Football field valuation chart",
|
|
912
1628
|
family: "financial" /* Financial */,
|
|
@@ -934,8 +1650,8 @@ var FootballFieldChart = (_class5 = class _FootballFieldChart extends _chunkXX2Y
|
|
|
934
1650
|
price ??= StrategyRows.number(row[current]);
|
|
935
1651
|
const a = StrategyRows.number(row[low]), b = StrategyRows.number(row[high]), name = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[method]);
|
|
936
1652
|
if (a === null || b === null || !name) continue;
|
|
937
|
-
const held = spans.get(name), from = Math.min(a, b, _nullishCoalesce(_optionalChain([held, 'optionalAccess',
|
|
938
|
-
spans.set(name, { method: name, group: _nullishCoalesce(_optionalChain([held, 'optionalAccess',
|
|
1653
|
+
const held = spans.get(name), from = Math.min(a, b, _nullishCoalesce(_optionalChain([held, 'optionalAccess', _29 => _29.low]), () => ( Infinity))), to = Math.max(a, b, _nullishCoalesce(_optionalChain([held, 'optionalAccess', _30 => _30.high]), () => ( -Infinity)));
|
|
1654
|
+
spans.set(name, { method: name, group: _nullishCoalesce(_optionalChain([held, 'optionalAccess', _31 => _31.group]), () => ( (_chunkQ5A2TY2Acjs.FrameworkText.clean(row[group]) || "Range"))), low: from, high: to, middle: (from + to) / 2 });
|
|
939
1655
|
}
|
|
940
1656
|
const listed = [...spans.values()];
|
|
941
1657
|
if (order === "by-midpoint" /* Midpoint */) listed.sort((a, b) => _d3.descending.call(void 0, a.middle, b.middle));
|
|
@@ -969,7 +1685,7 @@ var FootballFieldChart = (_class5 = class _FootballFieldChart extends _chunkXX2Y
|
|
|
969
1685
|
plotMarks(ctx, { x, y }) {
|
|
970
1686
|
const { mark, midpoint, consensus } = this.options, { palette, theme, plot } = ctx;
|
|
971
1687
|
const { spans, current } = this.model;
|
|
972
|
-
const thick = Math.min(
|
|
1688
|
+
const thick = Math.min(THICK2, y.bandwidth()), centre = (span) => (_nullishCoalesce(y(span.method), () => ( 0))) + y.bandwidth() / 2;
|
|
973
1689
|
const reach = _nullishCoalesce(_d3.max.call(void 0, spans, (span) => Math.max(Math.abs(span.low), Math.abs(span.high))), () => ( 0)), text = this.#format(Math.max(reach, Math.abs(_nullishCoalesce(current, () => ( 0)))));
|
|
974
1690
|
for (const span of spans) palette.color(span.group);
|
|
975
1691
|
const tell = (span) => ({
|
|
@@ -1009,7 +1725,7 @@ var FootballFieldChart = (_class5 = class _FootballFieldChart extends _chunkXX2Y
|
|
|
1009
1725
|
if (agreed) words.push({ ...headed(`Median range ${text(agreed[0])} to ${text(agreed[1])}`, (x(agreed[0]) + x(agreed[1])) / 2, current === null ? 0 : 1, "consensus"), weight: 400, tone: "secondary" });
|
|
1010
1726
|
StrategyWords.draw(ctx, "labels", words);
|
|
1011
1727
|
}
|
|
1012
|
-
},
|
|
1728
|
+
}, _class8.__initStatic8(), _class8);
|
|
1013
1729
|
|
|
1014
1730
|
// src/charts/strategycharts/HypeCycleChart.ts
|
|
1015
1731
|
|
|
@@ -1017,10 +1733,10 @@ var FootballFieldChart = (_class5 = class _FootballFieldChart extends _chunkXX2Y
|
|
|
1017
1733
|
// src/charts/strategycharts/StrategyLabels.ts
|
|
1018
1734
|
var SIDES = ["right", "left", "above", "below"];
|
|
1019
1735
|
var GAP5 = 4;
|
|
1020
|
-
var
|
|
1736
|
+
var LEAST2 = 5;
|
|
1021
1737
|
var hits = (a, b) => a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;
|
|
1022
1738
|
var StrategyLabels = class {
|
|
1023
|
-
static place(anchors,
|
|
1739
|
+
static place(anchors, area5, size = 10.5, widest = 130, blocked = [], soft = []) {
|
|
1024
1740
|
const marks = anchors.map((anchor) => ({ x0: anchor.x - anchor.r, y0: anchor.y - anchor.r, x1: anchor.x + anchor.r, y1: anchor.y + anchor.r }));
|
|
1025
1741
|
const boxed = (rect) => ({ x0: rect.x, y0: rect.y, x1: rect.x + rect.width, y1: rect.y + rect.height });
|
|
1026
1742
|
const taken = blocked.map(boxed), lines = soft.map(boxed);
|
|
@@ -1030,15 +1746,15 @@ var StrategyLabels = class {
|
|
|
1030
1746
|
const full = _chunkQ5A2TY2Acjs.FrameworkText.clean(anchor.text);
|
|
1031
1747
|
if (!full || named.has(i)) return;
|
|
1032
1748
|
for (const side of SIDES) {
|
|
1033
|
-
const across = side === "right" ?
|
|
1749
|
+
const across = side === "right" ? area5.x + area5.width - anchor.x - anchor.r - GAP5 : side === "left" ? anchor.x - anchor.r - GAP5 - area5.x : Math.min(anchor.x - area5.x, area5.x + area5.width - anchor.x) * 2;
|
|
1034
1750
|
const text = _chunkQ5A2TY2Acjs.FrameworkText.fit(full, Math.min(widest, across), size);
|
|
1035
|
-
if (whole ? text !== full : text.length <=
|
|
1751
|
+
if (whole ? text !== full : text.length <= LEAST2) continue;
|
|
1036
1752
|
const width = _chunkQ5A2TY2Acjs.FrameworkText.width(text, size), reach = anchor.r + GAP5;
|
|
1037
1753
|
const x = side === "right" ? anchor.x + reach : side === "left" ? anchor.x - reach : anchor.x;
|
|
1038
1754
|
const y = side === "above" ? anchor.y - reach - size / 2 : side === "below" ? anchor.y + reach + size / 2 : anchor.y;
|
|
1039
1755
|
const x0 = side === "right" ? x : side === "left" ? x - width : x - width / 2;
|
|
1040
1756
|
const box = { x0, y0: y - size / 2 - 1, x1: x0 + width, y1: y + size / 2 + 1 };
|
|
1041
|
-
if (box.y0 <
|
|
1757
|
+
if (box.y0 < area5.y || box.y1 > area5.y + area5.height || box.x0 < area5.x || box.x1 > area5.x + area5.width) continue;
|
|
1042
1758
|
if (taken.some((other) => hits(box, other)) || marks.some((other, j) => j !== i && hits(box, other)) || clear && lines.some((other) => hits(box, other))) continue;
|
|
1043
1759
|
taken.push(box);
|
|
1044
1760
|
named.add(i);
|
|
@@ -1091,8 +1807,8 @@ var LIST_ROW = 13;
|
|
|
1091
1807
|
var SYMBOL_AREA = 78;
|
|
1092
1808
|
var SMALLEST = 4;
|
|
1093
1809
|
var LARGEST = 11;
|
|
1094
|
-
var HypeCycleChart = (
|
|
1095
|
-
static
|
|
1810
|
+
var HypeCycleChart = (_class9 = class _HypeCycleChart extends _chunkEEF5WRRZcjs.Chart2D {
|
|
1811
|
+
static __initStatic9() {this.meta = {
|
|
1096
1812
|
id: "hype-cycle",
|
|
1097
1813
|
name: "Hype cycle",
|
|
1098
1814
|
family: "temporal" /* Temporal */,
|
|
@@ -1243,7 +1959,150 @@ var HypeCycleChart = (_class6 = class _HypeCycleChart extends _chunkEEF5WRRZcjs.
|
|
|
1243
1959
|
if (over && rows) words.push({ key: "list-more", text: `+${placed.length - shown} more`, x: left + 20, y: frame.y + shown * LIST_ROW + LIST_ROW / 2, size: 10.5 });
|
|
1244
1960
|
return words;
|
|
1245
1961
|
}
|
|
1246
|
-
},
|
|
1962
|
+
}, _class9.__initStatic9(), _class9);
|
|
1963
|
+
|
|
1964
|
+
// src/charts/strategycharts/KanbanBoard.ts
|
|
1965
|
+
|
|
1966
|
+
var KanbanCardDetail = /* @__PURE__ */ ((KanbanCardDetail2) => {
|
|
1967
|
+
KanbanCardDetail2["Title"] = "title";
|
|
1968
|
+
KanbanCardDetail2["Full"] = "full";
|
|
1969
|
+
return KanbanCardDetail2;
|
|
1970
|
+
})(KanbanCardDetail || {});
|
|
1971
|
+
var GAP6 = 6;
|
|
1972
|
+
var HEAD5 = 20;
|
|
1973
|
+
var HEAD_LIMITS = 42;
|
|
1974
|
+
var METER2 = 21;
|
|
1975
|
+
var OVER = 33;
|
|
1976
|
+
var RAIL2 = 18;
|
|
1977
|
+
var PAD5 = 4;
|
|
1978
|
+
var CARD_GAP = 3;
|
|
1979
|
+
var INSET = 5;
|
|
1980
|
+
var EDGE = 4;
|
|
1981
|
+
var LEAD = 12;
|
|
1982
|
+
var SIZE3 = 10.5;
|
|
1983
|
+
var MORE = 11;
|
|
1984
|
+
var NARROWEST = 28;
|
|
1985
|
+
var KanbanBoard = (_class10 = class _KanbanBoard extends _chunkEEF5WRRZcjs.Chart2D {
|
|
1986
|
+
static __initStatic10() {this.meta = {
|
|
1987
|
+
id: "kanban-board",
|
|
1988
|
+
name: "Kanban board",
|
|
1989
|
+
family: "project" /* Project */,
|
|
1990
|
+
dimension: 2 /* Two */,
|
|
1991
|
+
shape: "table" /* Table */,
|
|
1992
|
+
description: "Sets task cards in a column per workflow stage, with each stage's work-in-progress limit on a meter and breached stages marked.",
|
|
1993
|
+
tags: ["kanban", "workflow", "wip limit", "task board", "swimlanes", "agile"],
|
|
1994
|
+
sample: (seed) => StrategySamples.kanban(seed),
|
|
1995
|
+
variants: {
|
|
1996
|
+
swimlanes: [false, true],
|
|
1997
|
+
cardDetail: ["title" /* Title */, "full" /* Full */],
|
|
1998
|
+
limits: [true, false]
|
|
1999
|
+
}
|
|
2000
|
+
}}
|
|
2001
|
+
/** How many of `count` cards, each `card` px tall, stack in `room` px. */
|
|
2002
|
+
static capacity(room, card, count2) {
|
|
2003
|
+
return Math.max(0, Math.min(count2, Math.floor((room + CARD_GAP) / (card + CARD_GAP))));
|
|
2004
|
+
}
|
|
2005
|
+
defaults() {
|
|
2006
|
+
return { task: "task", stage: "stage", limit: "limit", lane: "lane", group: "kind", owner: "owner", swimlanes: false, cardDetail: "title" /* Title */, limits: true };
|
|
2007
|
+
}
|
|
2008
|
+
plotMargin() {
|
|
2009
|
+
return { top: 8, right: 12, bottom: 8, left: 12 };
|
|
2010
|
+
}
|
|
2011
|
+
/** The stages in workflow order with their cards and limits, and the lanes in the order first seen. */
|
|
2012
|
+
get board() {
|
|
2013
|
+
const { task, stage, limit, lane, group, owner } = this.options;
|
|
2014
|
+
const stages = /* @__PURE__ */ new Map(), lanes = /* @__PURE__ */ new Set();
|
|
2015
|
+
for (const row of StrategyRows.of(this.options.data)) {
|
|
2016
|
+
const name = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[stage]), title = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[task]), cap = StrategyRows.number(row[limit]);
|
|
2017
|
+
if (!name) continue;
|
|
2018
|
+
const held = _nullishCoalesce(stages.get(name), () => ( { name, limit: null, cards: [], over: 0 }));
|
|
2019
|
+
stages.set(name, held);
|
|
2020
|
+
if (held.limit === null && cap !== null && cap >= 0) held.limit = cap;
|
|
2021
|
+
if (!title) continue;
|
|
2022
|
+
const card = { task: title, stage: name, lane: _chunkQ5A2TY2Acjs.FrameworkText.clean(row[lane]) || "No lane", group: _chunkQ5A2TY2Acjs.FrameworkText.clean(row[group]) || "Card", owner: _chunkQ5A2TY2Acjs.FrameworkText.clean(row[owner]) };
|
|
2023
|
+
lanes.add(card.lane);
|
|
2024
|
+
held.cards.push(card);
|
|
2025
|
+
}
|
|
2026
|
+
for (const held of stages.values()) held.over = held.limit === null ? 0 : Math.max(0, held.cards.length - held.limit);
|
|
2027
|
+
return { stages: [...stages.values()], lanes: [...lanes] };
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* The card height in use and the height of each lane. Cards take the tallest of `sizes` at which every lane holds its
|
|
2031
|
+
* fullest stack, and lanes grow with their stacks. When no size fits, each lane is first given room for one card.
|
|
2032
|
+
*/
|
|
2033
|
+
static lanes(stacks, sizes, free) {
|
|
2034
|
+
const need = (card2) => stacks.map((stack) => Math.max(1, stack) * (card2 + CARD_GAP) - CARD_GAP + PAD5 * 2);
|
|
2035
|
+
const card = _nullishCoalesce(_nullishCoalesce(sizes.find((size) => _d3.sum.call(void 0, need(size)) <= free), () => ( sizes.at(-1))), () => ( 0)), wanted = need(card), all = _d3.sum.call(void 0, wanted);
|
|
2036
|
+
if (all <= free) return { card, heights: wanted.map((height) => free * height / all) };
|
|
2037
|
+
const least = Math.min(card + PAD5 * 2, free / Math.max(1, stacks.length)), spare = free - least * stacks.length, short = all - least * stacks.length;
|
|
2038
|
+
return { card, heights: wanted.map((height) => least + (short > 0 ? spare * (height - least) / short : 0)) };
|
|
2039
|
+
}
|
|
2040
|
+
draw(ctx) {
|
|
2041
|
+
const { swimlanes, cardDetail, limits } = this.options, { plot, theme, palette } = ctx;
|
|
2042
|
+
const { stages, lanes } = this.board;
|
|
2043
|
+
const rows = swimlanes && lanes.length ? lanes : [null];
|
|
2044
|
+
const rail = rows[0] === null ? 0 : RAIL2, head = limits ? HEAD_LIMITS : HEAD5, full = cardDetail === "full" /* Full */;
|
|
2045
|
+
const wide = Math.max(0, plot.width - rail - GAP6 * (stages.length - 1)) / Math.max(1, stages.length);
|
|
2046
|
+
const held = (stage, lane) => stage.cards.filter((card) => lane === null || card.lane === lane);
|
|
2047
|
+
const size = (lines2) => INSET * 2 + lines2 * LEAD + (full ? LEAD : 0);
|
|
2048
|
+
const { card: height, heights } = _KanbanBoard.lanes(rows.map((lane) => _nullishCoalesce(_d3.max.call(void 0, stages, (stage) => held(stage, lane).length), () => ( 0))), [size(2), size(1)], Math.max(0, plot.height - head - GAP6 * (rows.length - 1)));
|
|
2049
|
+
const tops = heights.map((_, j) => head + _d3.sum.call(void 0, heights.slice(0, j)) + GAP6 * j), lines = height === size(2) ? 2 : 1;
|
|
2050
|
+
const cells = stages.flatMap((stage, i) => rows.map((lane, j) => ({ key: `${i}/${j}`, stage, lane, x: rail + i * (wide + GAP6), y: tops[j], width: wide, height: heights[j], cards: held(stage, lane) })));
|
|
2051
|
+
for (const stage of stages) for (const card of stage.cards) palette.color(card.group);
|
|
2052
|
+
const breached = (stage) => !!limits && stage.over > 0;
|
|
2053
|
+
const panels = ctx.marks("columns", "rect", cells, (cell) => cell.key).attr("rx", 4).attr("fill", (cell) => breached(cell.stage) ? theme.critical : theme.grid).attr("fill-opacity", (cell) => breached(cell.stage) ? 0.12 : 0.6);
|
|
2054
|
+
ctx.set(panels, { x: (cell) => cell.x, y: (cell) => cell.y, width: (cell) => cell.width, height: (cell) => cell.height });
|
|
2055
|
+
ctx.hover(panels, ({ stage, lane, cards: cards2 }) => ({
|
|
2056
|
+
title: lane === null ? stage.name : `${stage.name}, ${lane}`,
|
|
2057
|
+
rows: [["Cards here", cards2.length], ["Cards in stage", stage.cards.length], ["Limit", _nullishCoalesce(stage.limit, () => ( "None"))], ...stage.over ? [["Over the limit by", stage.over]] : []]
|
|
2058
|
+
}));
|
|
2059
|
+
const tally = (stage) => limits && stage.limit !== null ? `${stage.cards.length} / ${stage.limit}` : `${stage.cards.length}`;
|
|
2060
|
+
const metered = limits ? stages.map((stage, i) => ({ stage, x: rail + i * (wide + GAP6) + 4, width: Math.max(0, wide - 14 - _chunkQ5A2TY2Acjs.FrameworkText.width(tally(stage), SIZE3)) })).filter((meter) => meter.stage.limit !== null && meter.width >= 12) : [];
|
|
2061
|
+
const tracks = ctx.marks("axis-limits", "rect", metered, (meter) => meter.stage.name).attr("rx", 2.5).attr("fill", theme.grid);
|
|
2062
|
+
ctx.set(tracks, { x: (meter) => meter.x, y: METER2 - 2.5, width: (meter) => meter.width, height: 5 });
|
|
2063
|
+
const filled = ctx.marks("load", "rect", metered, (meter) => meter.stage.name).attr("rx", 2.5).attr("fill", (meter) => meter.stage.over ? theme.critical : theme.inkSecondary);
|
|
2064
|
+
const load = ({ stage, width }) => width * (stage.limit ? Math.min(1, stage.cards.length / stage.limit) : stage.cards.length ? 1 : 0);
|
|
2065
|
+
ctx.set(filled, { x: (meter) => meter.x, y: METER2 - 2.5, width: load, height: 5 }, { width: 0 }, 30);
|
|
2066
|
+
const flagged = limits ? stages.map((stage, i) => ({ stage, x: rail + i * (wide + GAP6) + 8 })).filter((flag) => flag.stage.over && wide >= 16) : [];
|
|
2067
|
+
const flags = ctx.marks("breaches", "path", flagged, (flag) => flag.stage.name).attr("fill", theme.critical);
|
|
2068
|
+
ctx.set(flags, { d: _nullishCoalesce(_d3.symbol.call(void 0, _d3.symbolTriangle, 40)(), () => ( "")), transform: (flag) => `translate(${flag.x},${OVER})` });
|
|
2069
|
+
const across = wide - PAD5 * 2, room = across - EDGE - 9;
|
|
2070
|
+
const words = [], more = [], hidden = /* @__PURE__ */ new Map();
|
|
2071
|
+
const placed = across < NARROWEST ? [] : cells.flatMap((cell) => {
|
|
2072
|
+
const inner = cell.height - PAD5 * 2, shown = _KanbanBoard.capacity(inner, height, cell.cards.length), left = cell.x + PAD5, rest = cell.cards.slice(shown);
|
|
2073
|
+
const tag = rest.length ? `+${rest.length}` : "", taken = tag ? _chunkQ5A2TY2Acjs.FrameworkText.width(tag, 9.5) + 5 : 0;
|
|
2074
|
+
if (rest.length) hidden.set(cell.key, rest);
|
|
2075
|
+
if (rest.length && !shown && inner >= MORE) more.push({ key: cell.key, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(`${tag} more`, across, 9.5), x: left + 2, y: cell.y + cell.height / 2, size: 9.5 });
|
|
2076
|
+
return cell.cards.slice(0, shown).map((card, k) => {
|
|
2077
|
+
const top = cell.y + PAD5 + k * (height + CARD_GAP), last = k === shown - 1 && !!tag, span = room - (last && !full ? taken : 0);
|
|
2078
|
+
const text = lines > 1 ? _chunkQ5A2TY2Acjs.FrameworkText.clamp(_chunkQ5A2TY2Acjs.FrameworkText.wrap(card.task, span, SIZE3), lines, span, SIZE3) : [_chunkQ5A2TY2Acjs.FrameworkText.fit(card.task, span, SIZE3)];
|
|
2079
|
+
if (full && card.owner) words.push({ key: `owner/${cell.key}/${k}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(card.owner, room - (last ? taken : 0), 9.5), full: card.owner, x: left + EDGE + 5, y: top + height - INSET - 5, size: 9.5 });
|
|
2080
|
+
if (last) more.push({ key: cell.key, text: tag, x: left + across - 4, y: full ? top + height - INSET - 5 : top + INSET + (lines - 1) * LEAD + 5.5, anchor: "end", size: 9.5, weight: 600 });
|
|
2081
|
+
return { key: `${cell.key}/${k}`, card, left, top, width: across, height, color: palette.color(card.group), lines: text, x: left + EDGE + 5, y: top + INSET + 9, lead: LEAD };
|
|
2082
|
+
});
|
|
2083
|
+
});
|
|
2084
|
+
const cards = ctx.marks("cards", "g", placed, (card) => card.key);
|
|
2085
|
+
ctx.hover(cards, ({ card }) => _chunkQ5A2TY2Acjs.FrameworkText.tip(card.task, [["Stage", card.stage], ["Lane", card.lane], ["Kind", card.group], ...card.owner ? [["Owner", card.owner]] : []], palette.color(card.group)));
|
|
2086
|
+
const faces = cards.selectAll("rect.face").data((card) => [card]).join("rect").attr("class", "face").attr("rx", 3).attr("fill", theme.surface);
|
|
2087
|
+
ctx.set(faces, { x: (card) => card.left, y: (card) => card.top, width: (card) => card.width, height: (card) => card.height });
|
|
2088
|
+
const edges = cards.selectAll("rect.kind").data((card) => [card]).join("rect").attr("class", "kind").attr("rx", 2).attr("fill", (card) => card.color);
|
|
2089
|
+
ctx.set(edges, { x: (card) => card.left, y: (card) => card.top, width: EDGE, height: (card) => card.height });
|
|
2090
|
+
const titles = ctx.text(cards.selectAll("text").data((card) => [card]).join("text"), SIZE3, "ink").attr("pointer-events", "none");
|
|
2091
|
+
_chunkQ5A2TY2Acjs.FrameworkText.write(ctx, titles);
|
|
2092
|
+
const beside = (stage) => _chunkQ5A2TY2Acjs.FrameworkText.width(tally(stage), SIZE3) + 8;
|
|
2093
|
+
const counted = stages.every((stage) => limits ? wide >= beside(stage) : _chunkQ5A2TY2Acjs.FrameworkText.width(stage.name, 11) + beside(stage) + 8 <= wide);
|
|
2094
|
+
for (const [i, stage] of stages.entries()) {
|
|
2095
|
+
const left = rail + i * (wide + GAP6), count2 = tally(stage);
|
|
2096
|
+
if (counted) words.push({ key: `count/${i}`, text: count2, x: left + wide - 4, y: limits ? METER2 : 8, anchor: "end", size: SIZE3, weight: breached(stage) ? 600 : 400, tone: breached(stage) ? "ink" : "secondary" });
|
|
2097
|
+
words.push({ key: `stage/${i}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(stage.name, wide - 8, 11), full: stage.name, x: left + 4, y: 8, size: 11, weight: 600, tone: "ink" });
|
|
2098
|
+
const over = [`Over by ${stage.over}`, `+${stage.over}`].find((text) => _chunkQ5A2TY2Acjs.FrameworkText.width(text, 10) <= wide - 18);
|
|
2099
|
+
if (breached(stage) && over) words.push({ key: `over/${i}`, text: over, x: left + 15, y: OVER, size: 10, weight: 600, tone: "ink" });
|
|
2100
|
+
}
|
|
2101
|
+
words.push(...rows.flatMap((lane, j) => lane === null ? [] : [{ key: `lane/${j}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(lane, heights[j], SIZE3), full: lane, x: RAIL2 / 2 - 3, y: tops[j] + heights[j] / 2, anchor: "middle", size: SIZE3, weight: 600, turned: true }]));
|
|
2102
|
+
StrategyWords.draw(ctx, "words", words);
|
|
2103
|
+
ctx.hover(StrategyWords.draw(ctx, "more", more), (word) => ({ title: `${_nullishCoalesce(_optionalChain([hidden, 'access', _32 => _32.get, 'call', _33 => _33(word.key), 'optionalAccess', _34 => _34.length]), () => ( 0))} more cards`, rows: (_nullishCoalesce(hidden.get(word.key), () => ( []))).map((card) => [_chunkQ5A2TY2Acjs.FrameworkText.fit(card.task, 260, 12), card.owner]) }));
|
|
2104
|
+
}
|
|
2105
|
+
}, _class10.__initStatic10(), _class10);
|
|
1247
2106
|
|
|
1248
2107
|
// src/charts/strategycharts/KanoModelChart.ts
|
|
1249
2108
|
|
|
@@ -1277,8 +2136,8 @@ var SHAPES = {
|
|
|
1277
2136
|
};
|
|
1278
2137
|
var GUTTER2 = 16;
|
|
1279
2138
|
var AREA = 80;
|
|
1280
|
-
var KanoModelChart = (
|
|
1281
|
-
static
|
|
2139
|
+
var KanoModelChart = (_class11 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
2140
|
+
static __initStatic11() {this.meta = {
|
|
1282
2141
|
id: "kano-model",
|
|
1283
2142
|
name: "Kano model",
|
|
1284
2143
|
family: "relationship" /* Relationship */,
|
|
@@ -1350,7 +2209,7 @@ var KanoModelChart = (_class7 = class extends _chunkEEF5WRRZcjs.Chart2D {
|
|
|
1350
2209
|
words.push(...StrategyLabels.place(anchors, frame, 10.5, 130, [], traced));
|
|
1351
2210
|
StrategyWords.draw(ctx, "words", words);
|
|
1352
2211
|
}
|
|
1353
|
-
},
|
|
2212
|
+
}, _class11.__initStatic11(), _class11);
|
|
1354
2213
|
|
|
1355
2214
|
// src/charts/strategycharts/NineBoxGrid.ts
|
|
1356
2215
|
|
|
@@ -1364,13 +2223,13 @@ var TALENT = [["Potential gem", "High potential", "Star"], ["Inconsistent player
|
|
|
1364
2223
|
var TIERS = ["Low", "Medium", "High"];
|
|
1365
2224
|
var WORDS = [[/^(l|low)$/, 0], [/^(m|med|medium|moderate)$/, 1], [/^(h|high)$/, 2]];
|
|
1366
2225
|
var GUTTER3 = 28;
|
|
1367
|
-
var
|
|
1368
|
-
var
|
|
2226
|
+
var GAP7 = 3;
|
|
2227
|
+
var PAD6 = 5;
|
|
1369
2228
|
var R = 5;
|
|
1370
2229
|
var LARGEST2 = 13;
|
|
1371
2230
|
var titled = (column) => column.charAt(0).toUpperCase() + column.slice(1);
|
|
1372
|
-
var NineBoxGrid = (
|
|
1373
|
-
static
|
|
2231
|
+
var NineBoxGrid = (_class12 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2D {
|
|
2232
|
+
static __initStatic12() {this.meta = {
|
|
1374
2233
|
id: "nine-box-grid",
|
|
1375
2234
|
name: "Nine-box grid",
|
|
1376
2235
|
family: "matrix" /* Matrix */,
|
|
@@ -1406,10 +2265,10 @@ var NineBoxGrid = (_class8 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2
|
|
|
1406
2265
|
}
|
|
1407
2266
|
/** Every row with both ratings, each with its cell and its ratings on the scale. A worded rating sits mid-cell. */
|
|
1408
2267
|
get entries() {
|
|
1409
|
-
const { item, performance, potential, group, size } = this.options,
|
|
2268
|
+
const { item, performance, potential, group, size } = this.options, range6 = this.#range(), [low, high] = range6;
|
|
1410
2269
|
const rating = (cell, third) => _nullishCoalesce(StrategyRows.number(cell), () => ( low + (third + 0.5) * (high - low) / 3));
|
|
1411
2270
|
return StrategyRows.of(this.options.data).flatMap((row) => {
|
|
1412
|
-
const column = _NineBoxGrid.third(row[performance],
|
|
2271
|
+
const column = _NineBoxGrid.third(row[performance], range6), tier = _NineBoxGrid.third(row[potential], range6);
|
|
1413
2272
|
if (column === null || tier === null) return [];
|
|
1414
2273
|
return [{
|
|
1415
2274
|
name: _chunkQ5A2TY2Acjs.FrameworkText.clean(row[item]) || "Unnamed",
|
|
@@ -1427,15 +2286,15 @@ var NineBoxGrid = (_class8 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2
|
|
|
1427
2286
|
const { performance, potential, placement, sized, cellLabels, cellNames } = this.options, { plot, theme, palette } = ctx;
|
|
1428
2287
|
const entries = this.entries, [low, high] = this.#range(), third = (high - low) / 3;
|
|
1429
2288
|
const frame = { x: GUTTER3, y: 0, width: Math.max(1, plot.width - GUTTER3), height: Math.max(1, plot.height - GUTTER3) };
|
|
1430
|
-
const wide = Math.max(0, frame.width -
|
|
2289
|
+
const wide = Math.max(0, frame.width - GAP7 * 2) / 3, tall = Math.max(0, frame.height - GAP7 * 2) / 3;
|
|
1431
2290
|
const names = _nullishCoalesce(cellNames, () => ( TALENT));
|
|
1432
2291
|
const cells = [2, 1, 0].flatMap((tier) => [0, 1, 2].map((column) => ({
|
|
1433
2292
|
key: `${column}/${tier}`,
|
|
1434
2293
|
column,
|
|
1435
2294
|
tier,
|
|
1436
|
-
name: _chunkQ5A2TY2Acjs.FrameworkText.clean(_optionalChain([names, 'access',
|
|
1437
|
-
x: frame.x + column * (wide +
|
|
1438
|
-
y: frame.y + (2 - tier) * (tall +
|
|
2295
|
+
name: _chunkQ5A2TY2Acjs.FrameworkText.clean(_optionalChain([names, 'access', _35 => _35[2 - tier], 'optionalAccess', _36 => _36[column]])),
|
|
2296
|
+
x: frame.x + column * (wide + GAP7),
|
|
2297
|
+
y: frame.y + (2 - tier) * (tall + GAP7),
|
|
1439
2298
|
width: wide,
|
|
1440
2299
|
height: tall,
|
|
1441
2300
|
entries: entries.filter((entry) => entry.column === column && entry.tier === tier)
|
|
@@ -1451,10 +2310,10 @@ var NineBoxGrid = (_class8 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2
|
|
|
1451
2310
|
{ key: "axis-x", text: _chunkQ5A2TY2Acjs.FrameworkText.fit(titled(performance), frame.width, 11), x: frame.x + frame.width / 2, y: plot.height - 6, anchor: "middle", weight: 600 },
|
|
1452
2311
|
{ key: "axis-y", text: _chunkQ5A2TY2Acjs.FrameworkText.fit(titled(potential), frame.height, 11), x: 6, y: frame.y + frame.height / 2, anchor: "middle", weight: 600, turned: true },
|
|
1453
2312
|
...TIERS.flatMap((text, i) => [
|
|
1454
|
-
{ key: `tier-x/${text}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, wide, 10), x: frame.x + i * (wide +
|
|
1455
|
-
{ key: `tier-y/${text}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, tall, 10), x: 20, y: frame.y + (2 - i) * (tall +
|
|
2313
|
+
{ key: `tier-x/${text}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, wide, 10), x: frame.x + i * (wide + GAP7) + wide / 2, y: frame.y + frame.height + 8, anchor: "middle", size: 10 },
|
|
2314
|
+
{ key: `tier-y/${text}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(text, tall, 10), x: 20, y: frame.y + (2 - i) * (tall + GAP7) + tall / 2, anchor: "middle", size: 10, turned: true }
|
|
1456
2315
|
]),
|
|
1457
|
-
...labelled ? cells.map((cell) => ({ key: `cell/${cell.key}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(cell.name, cell.width -
|
|
2316
|
+
...labelled ? cells.map((cell) => ({ key: `cell/${cell.key}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(cell.name, cell.width - PAD6 * 2, 10), full: cell.name, x: cell.x + PAD6, y: cell.y + 9, size: 10, weight: 600 })) : []
|
|
1458
2317
|
];
|
|
1459
2318
|
const largest = _d3.max.call(void 0, entries, (entry) => Math.max(0, _nullishCoalesce(entry.size, () => ( 0)))) || 1;
|
|
1460
2319
|
const dots = placement !== "scatter" /* Scatter */ ? [] : cells.flatMap((cell) => cell.entries.map((entry, i) => {
|
|
@@ -1471,7 +2330,7 @@ var NineBoxGrid = (_class8 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2
|
|
|
1471
2330
|
const marks = ctx.marks("placed", "circle", [...dots].sort((a, b) => b.r - a.r), (dot) => dot.key).attr("fill", (dot) => dot.fill).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
1472
2331
|
ctx.set(marks, { cx: (dot) => dot.x, cy: (dot) => dot.y, r: (dot) => dot.r }, { r: 0 }, 15);
|
|
1473
2332
|
ctx.hover(marks, (dot) => tell(dot.entry, dot.cell));
|
|
1474
|
-
const headers = labelled ? cells.map((cell) => ({ x: cell.x, y: cell.y, width: Math.min(cell.width, _chunkQ5A2TY2Acjs.FrameworkText.width(cell.name, 10) +
|
|
2333
|
+
const headers = labelled ? cells.map((cell) => ({ x: cell.x, y: cell.y, width: Math.min(cell.width, _chunkQ5A2TY2Acjs.FrameworkText.width(cell.name, 10) + PAD6 * 2), height: head })) : [];
|
|
1475
2334
|
words.push(...StrategyLabels.place(dots.map((dot) => ({ key: `name/${dot.key}`, text: dot.entry.name, x: dot.x, y: dot.y, r: dot.r + 1 })), frame, 10, 90, headers));
|
|
1476
2335
|
const counted = placement === "count" /* Count */ ? cells : [];
|
|
1477
2336
|
const most = _d3.max.call(void 0, cells, (cell) => cell.entries.length) || 1, reach = Math.max(0, Math.min(wide, tall - head) / 2 - 3);
|
|
@@ -1494,15 +2353,180 @@ var NineBoxGrid = (_class8 = class _NineBoxGrid extends _chunkEEF5WRRZcjs.Chart2
|
|
|
1494
2353
|
const lists = placement !== "list" /* List */ ? [] : cells.map((cell) => ({
|
|
1495
2354
|
key: cell.key,
|
|
1496
2355
|
color: theme.inkMuted,
|
|
1497
|
-
x: cell.x +
|
|
2356
|
+
x: cell.x + PAD6,
|
|
1498
2357
|
y: cell.y + head + 3,
|
|
1499
|
-
width: Math.max(0, cell.width -
|
|
2358
|
+
width: Math.max(0, cell.width - PAD6 * 2),
|
|
1500
2359
|
height: Math.max(0, cell.height - head - 6),
|
|
1501
2360
|
notes: cell.entries.map((entry) => ({ text: entry.name, color: color(entry), rows: tell(entry, cell).rows }))
|
|
1502
2361
|
}));
|
|
1503
2362
|
StrategyNotes.draw(ctx, "names", StrategyNotes.plan(lists, "bullet"), "bullet");
|
|
1504
2363
|
}
|
|
1505
|
-
},
|
|
2364
|
+
}, _class12.__initStatic12(), _class12);
|
|
2365
|
+
|
|
2366
|
+
// src/charts/strategycharts/OkrTree.ts
|
|
2367
|
+
|
|
2368
|
+
var OkrTreeLayout = /* @__PURE__ */ ((OkrTreeLayout2) => {
|
|
2369
|
+
OkrTreeLayout2["Tree"] = "tree";
|
|
2370
|
+
OkrTreeLayout2["List"] = "list";
|
|
2371
|
+
return OkrTreeLayout2;
|
|
2372
|
+
})(OkrTreeLayout || {});
|
|
2373
|
+
var OkrTreeRollup = /* @__PURE__ */ ((OkrTreeRollup2) => {
|
|
2374
|
+
OkrTreeRollup2["Mean"] = "mean";
|
|
2375
|
+
OkrTreeRollup2["Weakest"] = "weakest";
|
|
2376
|
+
return OkrTreeRollup2;
|
|
2377
|
+
})(OkrTreeRollup || {});
|
|
2378
|
+
var BAR = 6;
|
|
2379
|
+
var GROUP = 10;
|
|
2380
|
+
var INDENT2 = 18;
|
|
2381
|
+
var SIZE4 = 10.5;
|
|
2382
|
+
var FIGURE = 30;
|
|
2383
|
+
var PIP2 = 34;
|
|
2384
|
+
var LEVELS = [[0.7, "On track", "good"], [0.4, "At risk", "warning"], [-Infinity, "Off track", "critical"]];
|
|
2385
|
+
var OkrTree = (_class13 = class _OkrTree extends _chunkEEF5WRRZcjs.Chart2D {
|
|
2386
|
+
static __initStatic13() {this.meta = {
|
|
2387
|
+
id: "okr-tree",
|
|
2388
|
+
name: "OKR tree",
|
|
2389
|
+
family: "hierarchy" /* Hierarchy */,
|
|
2390
|
+
dimension: 2 /* Two */,
|
|
2391
|
+
shape: "table" /* Table */,
|
|
2392
|
+
description: "Lists objectives over their key results with a progress bar and confidence on each, and scores every objective from its key results.",
|
|
2393
|
+
tags: ["okr", "objectives", "key results", "goals", "progress", "planning"],
|
|
2394
|
+
sample: (seed) => StrategySamples.okr(seed),
|
|
2395
|
+
variants: {
|
|
2396
|
+
layout: ["tree" /* Tree */, "list" /* List */],
|
|
2397
|
+
rollup: ["mean" /* Mean */, "weakest" /* Weakest */],
|
|
2398
|
+
confidenceMarks: [true, false]
|
|
2399
|
+
}
|
|
2400
|
+
}}
|
|
2401
|
+
/** One score for several: their mean, or the lowest. Null when there are none. */
|
|
2402
|
+
static rollup(values, how = "mean" /* Mean */) {
|
|
2403
|
+
return _nullishCoalesce((how === "weakest" /* Weakest */ ? _d3.min.call(void 0, values) : _d3.mean.call(void 0, values)), () => ( null));
|
|
2404
|
+
}
|
|
2405
|
+
/** What a confidence from 0 to 1 is called: on track from 0.7, at risk from 0.4, off track below. */
|
|
2406
|
+
static status(confidence) {
|
|
2407
|
+
return LEVELS.find(([floor]) => confidence >= floor)[1];
|
|
2408
|
+
}
|
|
2409
|
+
defaults() {
|
|
2410
|
+
return { objective: "objective", keyResult: "keyResult", progress: "progress", confidence: "confidence", layout: "tree" /* Tree */, rollup: "mean" /* Mean */, confidenceMarks: true };
|
|
2411
|
+
}
|
|
2412
|
+
plotMargin() {
|
|
2413
|
+
return { top: 8, right: 12, bottom: 8, left: 12 };
|
|
2414
|
+
}
|
|
2415
|
+
/** Every objective is named where it is drawn. */
|
|
2416
|
+
legendItems() {
|
|
2417
|
+
return [];
|
|
2418
|
+
}
|
|
2419
|
+
/** The objectives in order, each scored from its key results. */
|
|
2420
|
+
get objectives() {
|
|
2421
|
+
const { objective, keyResult, progress, confidence, rollup } = this.options, rows = StrategyRows.of(this.options.data);
|
|
2422
|
+
const most = _nullishCoalesce(_d3.max.call(void 0, rows, (row) => _nullishCoalesce(StrategyRows.number(row[confidence]), () => ( 0))), () => ( 0)), unit = most <= 1 ? 1 : most <= 10 ? 10 : 100;
|
|
2423
|
+
const held = /* @__PURE__ */ new Map();
|
|
2424
|
+
for (const row of rows) {
|
|
2425
|
+
const name = _chunkQ5A2TY2Acjs.FrameworkText.clean(row[objective]), done = StrategyRows.number(row[progress]), sure = StrategyRows.number(row[confidence]);
|
|
2426
|
+
if (!name || done === null) continue;
|
|
2427
|
+
held.set(name, [..._nullishCoalesce(held.get(name), () => ( [])), { name: _chunkQ5A2TY2Acjs.FrameworkText.clean(row[keyResult]) || "Unnamed", progress: StrategyRows.clamp(done, 0, 100), confidence: sure === null ? null : StrategyRows.clamp(sure / unit, 0, 1) }]);
|
|
2428
|
+
}
|
|
2429
|
+
return [...held].map(([name, keyResults]) => ({
|
|
2430
|
+
name,
|
|
2431
|
+
keyResults,
|
|
2432
|
+
progress: _nullishCoalesce(_OkrTree.rollup(keyResults.map((result) => result.progress), rollup), () => ( 0)),
|
|
2433
|
+
confidence: _OkrTree.rollup(keyResults.flatMap((result) => _nullishCoalesce(result.confidence, () => ( []))), rollup)
|
|
2434
|
+
}));
|
|
2435
|
+
}
|
|
2436
|
+
draw(ctx) {
|
|
2437
|
+
const { theme } = ctx, parts = this.options.layout === "list" /* List */ ? this.#list(ctx) : this.#tree(ctx);
|
|
2438
|
+
const cards = ctx.marks("objectives", "rect", parts.cards, (card) => card.key).attr("rx", 4).attr("fill", (card) => card.color).attr("fill-opacity", 0.14);
|
|
2439
|
+
ctx.set(cards, { x: (card) => card.x, y: (card) => card.y, width: (card) => card.width, height: (card) => card.height });
|
|
2440
|
+
const links = ctx.marks("links", "path", parts.links, (link) => link.key).attr("fill", "none").attr("stroke", theme.axis).attr("stroke-width", 1.5);
|
|
2441
|
+
ctx.set(links, { d: (link) => link.d });
|
|
2442
|
+
const guides = ctx.marks("axis-guides", "path", parts.guides, (guide) => guide.key).attr("fill", "none").attr("stroke", theme.axis);
|
|
2443
|
+
ctx.set(guides, { d: (guide) => guide.d });
|
|
2444
|
+
const meters = ctx.marks("meters", "g", parts.meters, (meter) => meter.key);
|
|
2445
|
+
ctx.hover(meters, (meter) => meter.tip);
|
|
2446
|
+
const tracks = meters.selectAll("rect.track").data((meter) => [meter]).join("rect").attr("class", "track").attr("rx", BAR / 2).attr("fill", theme.grid);
|
|
2447
|
+
ctx.set(tracks, { x: (meter) => meter.x, y: (meter) => meter.y, width: (meter) => meter.width, height: BAR });
|
|
2448
|
+
const fills = meters.selectAll("rect.done").data((meter) => [meter]).join("rect").attr("class", "done").attr("rx", BAR / 2).attr("fill", (meter) => meter.color);
|
|
2449
|
+
ctx.set(fills, { x: (meter) => meter.x, y: (meter) => meter.y, width: (meter) => meter.width * meter.share, height: BAR }, { width: 0 }, 25);
|
|
2450
|
+
const pips = ctx.marks("confidence", "circle", parts.pips, (pip) => pip.key).attr("fill", (pip) => pip.color).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
2451
|
+
ctx.set(pips, { cx: (pip) => pip.x, cy: (pip) => pip.y, r: 4.5 }, { r: 0 }, 15);
|
|
2452
|
+
StrategyWords.draw(ctx, "words", parts.words);
|
|
2453
|
+
}
|
|
2454
|
+
/** Room after a bar for its percentage and, with confidence shown, its dot and figure. */
|
|
2455
|
+
get #trail() {
|
|
2456
|
+
return FIGURE + (this.options.confidenceMarks ? PIP2 : 0);
|
|
2457
|
+
}
|
|
2458
|
+
/** A bar from `x` across `width`, centred on `y`, with what follows it. */
|
|
2459
|
+
#meter(ctx, parts, key, item, color, x, y, width, tip, bold) {
|
|
2460
|
+
const { theme } = ctx, after = x + width + 6;
|
|
2461
|
+
parts.meters.push({ key, x, y: y - BAR / 2, width, height: BAR, share: item.progress / 100, color, tip });
|
|
2462
|
+
parts.words.push({ key: `done/${key}`, text: `${Math.round(item.progress)}%`, x: after, y, size: SIZE4, weight: bold ? 600 : 400, tone: "ink" });
|
|
2463
|
+
if (!this.options.confidenceMarks || item.confidence === null) return;
|
|
2464
|
+
const level = LEVELS.find(([floor]) => item.confidence >= floor);
|
|
2465
|
+
parts.pips.push({ key, x: after + FIGURE + 3, y, color: theme[level[2]] });
|
|
2466
|
+
parts.words.push({ key: `sure/${key}`, text: item.confidence.toFixed(1), x: after + FIGURE + 11, y, size: SIZE4 });
|
|
2467
|
+
}
|
|
2468
|
+
#tips(objective, color) {
|
|
2469
|
+
const sure = (item) => item.confidence === null ? [] : [["Confidence", `${item.confidence.toFixed(2)}, ${_OkrTree.status(item.confidence).toLowerCase()}`]];
|
|
2470
|
+
const how = this.options.rollup === "weakest" /* Weakest */ ? "weakest key result" : "mean of key results";
|
|
2471
|
+
return {
|
|
2472
|
+
own: _chunkQ5A2TY2Acjs.FrameworkText.tip(objective.name, [["Key results", objective.keyResults.length], [`Progress, ${how}`, `${Math.round(objective.progress)}%`], ...sure(objective)], color),
|
|
2473
|
+
of: (result) => _chunkQ5A2TY2Acjs.FrameworkText.tip(result.name, [["Progress", `${Math.round(result.progress)}%`], ...sure(result)], color)
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
/** The objectives and the height of a row: with their key results while a row gets `least` px, and alone, as a list, in a plot too short for that. */
|
|
2477
|
+
#fitted(least, height) {
|
|
2478
|
+
const all = this.objectives, bare = all.map((objective) => ({ ...objective, keyResults: [] }));
|
|
2479
|
+
const rowOf = (list, units2) => Math.max(0, height - GROUP * (list.length - 1)) / Math.max(1, _d3.sum.call(void 0, list, units2));
|
|
2480
|
+
const units = this.options.layout === "list" /* List */ ? (objective) => objective.keyResults.length + 1 : (objective) => Math.max(1.4, objective.keyResults.length);
|
|
2481
|
+
const row = rowOf(all, units);
|
|
2482
|
+
return row >= least ? { shown: all, row } : { shown: bare, row: rowOf(bare, units) };
|
|
2483
|
+
}
|
|
2484
|
+
#tree(ctx) {
|
|
2485
|
+
const { plot, palette } = ctx, parts = { cards: [], links: [], guides: [], meters: [], pips: [], words: [] };
|
|
2486
|
+
const { shown, row: free } = this.#fitted(12, plot.height), row = Math.min(46, free), trail = this.#trail;
|
|
2487
|
+
if (!shown.some((objective) => objective.keyResults.length)) return this.#list(ctx);
|
|
2488
|
+
const left = Math.min(250, plot.width * 0.37), reach = Math.max(18, plot.width * 0.06), start = left + reach, across = Math.max(0, plot.width - start - trail - 6);
|
|
2489
|
+
let top = 0;
|
|
2490
|
+
shown.forEach((objective, i) => {
|
|
2491
|
+
const color = palette.color(objective.name), tips = this.#tips(objective, color), tall = Math.max(1.4, objective.keyResults.length) * row;
|
|
2492
|
+
const card = { key: `${i}`, color, x: 0, y: top + 2, width: left, height: Math.max(0, tall - 4) }, middle = top + tall / 2, room = left - 16;
|
|
2493
|
+
parts.cards.push(card);
|
|
2494
|
+
const barred = card.height >= 20 && room - trail >= 24, lines = Math.floor((card.height - 8 - (barred ? 14 : 0)) / 13);
|
|
2495
|
+
const name = _chunkQ5A2TY2Acjs.FrameworkText.clamp(_chunkQ5A2TY2Acjs.FrameworkText.wrap(objective.name, room, 11), Math.max(0, lines), room, 11), from = middle - (name.length * 13 + (barred ? 14 : 0)) / 2 + 6;
|
|
2496
|
+
parts.words.push(...name.map((text, line8) => ({ key: `objective/${i}/${line8}`, text, full: objective.name, x: 8, y: from + line8 * 13, size: 11, weight: 600, tone: "ink" })));
|
|
2497
|
+
if (barred) this.#meter(ctx, parts, `${i}`, objective, color, 8, from + name.length * 13 + 2, room - trail, tips.own, true);
|
|
2498
|
+
const first = top + (tall - objective.keyResults.length * row) / 2;
|
|
2499
|
+
objective.keyResults.forEach((result, k) => {
|
|
2500
|
+
const centre = first + (k + 0.5) * row, over = row >= 25, beside = over ? 0 : Math.round(across * 0.5), key = `${i}/${k}`;
|
|
2501
|
+
parts.links.push({ key, d: `M${left},${middle}C${left + reach * 0.55},${middle} ${left + reach * 0.45},${centre} ${start - 4},${centre}` });
|
|
2502
|
+
parts.words.push({ key: `result/${key}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(result.name, over ? plot.width - start : beside - 6, SIZE4), full: result.name, x: start, y: centre - (over ? 7 : 0), size: SIZE4, tone: "ink" });
|
|
2503
|
+
this.#meter(ctx, parts, key, result, color, start + beside, centre + (over ? 7 : 0), across - beside, tips.of(result), false);
|
|
2504
|
+
});
|
|
2505
|
+
top += tall + GROUP;
|
|
2506
|
+
});
|
|
2507
|
+
return parts;
|
|
2508
|
+
}
|
|
2509
|
+
#list(ctx) {
|
|
2510
|
+
const { plot, palette } = ctx, parts = { cards: [], links: [], guides: [], meters: [], pips: [], words: [] };
|
|
2511
|
+
const { shown, row: free } = this.#fitted(13, plot.height), row = Math.min(32, free), trail = this.#trail;
|
|
2512
|
+
const bar = Math.max(40, Math.min(220, plot.width * 0.3)), at = Math.max(0, plot.width - trail - 6 - bar), named = row >= 12;
|
|
2513
|
+
let top = 0;
|
|
2514
|
+
shown.forEach((objective, i) => {
|
|
2515
|
+
const color = palette.color(objective.name), tips = this.#tips(objective, color), head = top + row / 2;
|
|
2516
|
+
if (named) parts.words.push({ key: `objective/${i}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(objective.name, at - 10, 11.5), full: objective.name, x: 0, y: head, size: 11.5, weight: 600, tone: "ink" });
|
|
2517
|
+
this.#meter(ctx, parts, `${i}`, objective, color, at, head, bar, tips.own, true);
|
|
2518
|
+
const centres = objective.keyResults.map((_, k) => top + (k + 1.5) * row);
|
|
2519
|
+
if (centres.length) parts.guides.push({ key: `${i}`, d: `M6,${head + Math.min(9, row / 2)}V${centres.at(-1)}${centres.map((centre) => `M6,${centre}H${INDENT2 - 5}`).join("")}` });
|
|
2520
|
+
objective.keyResults.forEach((result, k) => {
|
|
2521
|
+
const key = `${i}/${k}`;
|
|
2522
|
+
parts.words.push({ key: `result/${key}`, text: _chunkQ5A2TY2Acjs.FrameworkText.fit(result.name, at - INDENT2 - 10, SIZE4), full: result.name, x: INDENT2, y: centres[k], size: SIZE4, tone: "ink" });
|
|
2523
|
+
this.#meter(ctx, parts, key, result, color, at, centres[k], bar, tips.of(result), false);
|
|
2524
|
+
});
|
|
2525
|
+
top += (objective.keyResults.length + 1) * row + GROUP;
|
|
2526
|
+
});
|
|
2527
|
+
return parts;
|
|
2528
|
+
}
|
|
2529
|
+
}, _class13.__initStatic13(), _class13);
|
|
1506
2530
|
|
|
1507
2531
|
// src/charts/strategycharts/StrategyCanvasChart.ts
|
|
1508
2532
|
|
|
@@ -1514,8 +2538,8 @@ var StrategyCanvasOrder = /* @__PURE__ */ ((StrategyCanvasOrder2) => {
|
|
|
1514
2538
|
})(StrategyCanvasOrder || {});
|
|
1515
2539
|
var LABEL = 10;
|
|
1516
2540
|
var ROWS = 2;
|
|
1517
|
-
var StrategyCanvasChart = (
|
|
1518
|
-
static
|
|
2541
|
+
var StrategyCanvasChart = (_class14 = class _StrategyCanvasChart extends _chunkXX2YTCORcjs.CartesianChart {
|
|
2542
|
+
static __initStatic14() {this.meta = {
|
|
1519
2543
|
id: "strategy-canvas",
|
|
1520
2544
|
name: "Strategy canvas",
|
|
1521
2545
|
family: "comparison" /* Comparison */,
|
|
@@ -1596,7 +2620,7 @@ var StrategyCanvasChart = (_class9 = class _StrategyCanvasChart extends _chunkXX
|
|
|
1596
2620
|
});
|
|
1597
2621
|
const band = _d3.area.call(void 0, ).x((point) => at(point.factor)).y0((point) => y(point.others)).y1((point) => y(point.value)).curve(_chunkPY4R3IFFcjs.Scales.curve(curve));
|
|
1598
2622
|
const shade = ctx.marks("gap", "path", apart.length > 1 ? [apart] : []).attr("fill", focal ? palette.color(focal.offering) : "none").attr("fill-opacity", 0.16);
|
|
1599
|
-
ctx.set(shade, { d: (
|
|
2623
|
+
ctx.set(shade, { d: (points2) => _nullishCoalesce(band(points2), () => ( "")) });
|
|
1600
2624
|
const path = _d3.line.call(void 0, ).x((point) => at(point.factor)).y((point) => y(point.value)).curve(_chunkPY4R3IFFcjs.Scales.curve(curve));
|
|
1601
2625
|
const lines = ctx.marks("curves", "path", curves, (item) => item.offering).attr("fill", "none").attr("stroke", (item) => palette.color(item.offering)).attr("stroke-width", 2).attr("stroke-linejoin", "round");
|
|
1602
2626
|
ctx.set(lines, { d: (item) => _nullishCoalesce(path(item.points), () => ( "")) });
|
|
@@ -1608,7 +2632,7 @@ var StrategyCanvasChart = (_class9 = class _StrategyCanvasChart extends _chunkXX
|
|
|
1608
2632
|
const names = _StrategyCanvasChart.#names(factors, x.step()).flatMap((lines2, i) => lines2.flatMap((text, row) => text ? [{ key: `${factors[i]}/${row}`, text, full: factors[i], x: at(factors[i]), y: ctx.plot.height + 13 + row * 11, anchor: "middle", size: LABEL }] : []));
|
|
1609
2633
|
StrategyWords.draw(ctx, "axis-factors", names);
|
|
1610
2634
|
}
|
|
1611
|
-
},
|
|
2635
|
+
}, _class14.__initStatic14(), _class14);
|
|
1612
2636
|
|
|
1613
2637
|
// src/charts/strategycharts/TechnologyAdoptionCurve.ts
|
|
1614
2638
|
|
|
@@ -1619,13 +2643,13 @@ var TechnologyAdoptionSegments = /* @__PURE__ */ ((TechnologyAdoptionSegments2)
|
|
|
1619
2643
|
})(TechnologyAdoptionSegments || {});
|
|
1620
2644
|
var SHARES = [["Innovators", 2.5], ["Early adopters", 13.5], ["Early majority", 34], ["Late majority", 34], ["Laggards", 16]];
|
|
1621
2645
|
var REACH = 3.1;
|
|
1622
|
-
var
|
|
2646
|
+
var GAP8 = 18;
|
|
1623
2647
|
var BELL = 0.8;
|
|
1624
2648
|
var NAME_SIZE2 = 10;
|
|
1625
2649
|
var SERIES = "Cumulative adoption";
|
|
1626
2650
|
var density = (z) => Math.exp(-(z * z) / 2);
|
|
1627
|
-
var TechnologyAdoptionCurve = (
|
|
1628
|
-
static
|
|
2651
|
+
var TechnologyAdoptionCurve = (_class15 = class _TechnologyAdoptionCurve extends _chunkEEF5WRRZcjs.Chart2D {
|
|
2652
|
+
static __initStatic15() {this.meta = {
|
|
1629
2653
|
id: "technology-adoption-curve",
|
|
1630
2654
|
name: "Technology adoption curve",
|
|
1631
2655
|
family: "distribution" /* Distribution */,
|
|
@@ -1682,7 +2706,7 @@ var TechnologyAdoptionCurve = (_class10 = class _TechnologyAdoptionCurve extends
|
|
|
1682
2706
|
}
|
|
1683
2707
|
draw(ctx) {
|
|
1684
2708
|
const { item, adoption, segments, cumulative, chasm } = this.options, { plot, theme, palette } = ctx;
|
|
1685
|
-
const groups = _TechnologyAdoptionCurve.groups(), gap = chasm ?
|
|
2709
|
+
const groups = _TechnologyAdoptionCurve.groups(), gap = chasm ? GAP8 : 0, split = groups[1].to;
|
|
1686
2710
|
const right = cumulative ? 30 : 0;
|
|
1687
2711
|
const scale = Math.max(1, plot.width - right - gap) / (REACH * 2);
|
|
1688
2712
|
const names = groups.map((group) => _chunkQ5A2TY2Acjs.FrameworkText.clamp(_chunkQ5A2TY2Acjs.FrameworkText.wrap(group.name, (group.to - group.from) * scale - 4, NAME_SIZE2), 2, (group.to - group.from) * scale - 4, NAME_SIZE2));
|
|
@@ -1713,7 +2737,7 @@ var TechnologyAdoptionCurve = (_class10 = class _TechnologyAdoptionCurve extends
|
|
|
1713
2737
|
ctx.set(baseline, { x1: 0, x2: frame.width, y1: bottom, y2: bottom });
|
|
1714
2738
|
const climb = [...steps(-REACH, split).map((z) => [x(z, false), rise2(z)]), ...steps(split, REACH).map((z) => [x(z, true), rise2(z)])];
|
|
1715
2739
|
const curve = ctx.marks("cumulative", "path", cumulative ? [climb] : []).attr("fill", "none").attr("stroke", stroke).attr("stroke-width", 2).attr("stroke-linecap", "round");
|
|
1716
|
-
ctx.set(curve, { d: (
|
|
2740
|
+
ctx.set(curve, { d: (points2) => _nullishCoalesce(_d3.line.call(void 0, )(points2), () => ( "")) });
|
|
1717
2741
|
ctx.drawOn(curve);
|
|
1718
2742
|
const marked = StrategyRows.of(this.options.data).flatMap((row, i) => {
|
|
1719
2743
|
const share = StrategyRows.number(row[adoption]);
|
|
@@ -1747,7 +2771,7 @@ var TechnologyAdoptionCurve = (_class10 = class _TechnologyAdoptionCurve extends
|
|
|
1747
2771
|
words.push(...StrategyLabels.place(anchors, frame, 10.5, 140, [...under, ...titled2], traced));
|
|
1748
2772
|
StrategyWords.draw(ctx, "words", words);
|
|
1749
2773
|
}
|
|
1750
|
-
},
|
|
2774
|
+
}, _class15.__initStatic15(), _class15);
|
|
1751
2775
|
|
|
1752
2776
|
// src/charts/strategycharts/WardleyMap.ts
|
|
1753
2777
|
var WardleyStages = /* @__PURE__ */ ((WardleyStages2) => {
|
|
@@ -1760,14 +2784,14 @@ var WardleyAxis = /* @__PURE__ */ ((WardleyAxis2) => {
|
|
|
1760
2784
|
WardleyAxis2["Even"] = "even";
|
|
1761
2785
|
return WardleyAxis2;
|
|
1762
2786
|
})(WardleyAxis || {});
|
|
1763
|
-
var
|
|
2787
|
+
var STAGES2 = [{ name: "Genesis", to: 0.175 }, { name: "Custom built", to: 0.4 }, { name: "Product", to: 0.7 }, { name: "Commodity", to: 1 }];
|
|
1764
2788
|
var GUTTER4 = 16;
|
|
1765
2789
|
var R2 = 5;
|
|
1766
|
-
var
|
|
2790
|
+
var HEAD6 = 7;
|
|
1767
2791
|
var COMPONENT = "Component";
|
|
1768
2792
|
var MOVEMENT = "Evolving";
|
|
1769
|
-
var WardleyMap = (
|
|
1770
|
-
static
|
|
2793
|
+
var WardleyMap = (_class16 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D {
|
|
2794
|
+
static __initStatic16() {this.meta = {
|
|
1771
2795
|
id: "wardley-map",
|
|
1772
2796
|
name: "Wardley map",
|
|
1773
2797
|
family: "network" /* Network */,
|
|
@@ -1784,12 +2808,12 @@ var WardleyMap = (_class11 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D
|
|
|
1784
2808
|
}}
|
|
1785
2809
|
/** The stage of evolution that holds `evolution`, 0 to 1. */
|
|
1786
2810
|
static stage(evolution) {
|
|
1787
|
-
return (_nullishCoalesce(
|
|
2811
|
+
return (_nullishCoalesce(STAGES2.find((stage) => evolution < stage.to), () => ( STAGES2[STAGES2.length - 1]))).name;
|
|
1788
2812
|
}
|
|
1789
2813
|
/** Where `evolution` falls when the four stages are drawn equally wide, 0 to 1. */
|
|
1790
2814
|
static even(evolution) {
|
|
1791
|
-
const at = StrategyRows.clamp(evolution, 0, 1), index = Math.max(0,
|
|
1792
|
-
return (index + (at - from) / (
|
|
2815
|
+
const at = StrategyRows.clamp(evolution, 0, 1), index = Math.max(0, STAGES2.findIndex((stage) => at <= stage.to)), from = index ? STAGES2[index - 1].to : 0;
|
|
2816
|
+
return (index + (at - from) / (STAGES2[index].to - from)) / STAGES2.length;
|
|
1793
2817
|
}
|
|
1794
2818
|
defaults() {
|
|
1795
2819
|
return { name: "name", visibility: "visibility", evolution: "evolution", evolveTo: "evolveTo", stages: "bands" /* Bands */, axis: "standard" /* Standard */, movement: true };
|
|
@@ -1801,15 +2825,15 @@ var WardleyMap = (_class11 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D
|
|
|
1801
2825
|
get model() {
|
|
1802
2826
|
const { data, name, visibility, evolution, evolveTo } = this.options;
|
|
1803
2827
|
const byId = /* @__PURE__ */ new Map();
|
|
1804
|
-
for (const node of StrategyRows.of(_optionalChain([data, 'optionalAccess',
|
|
2828
|
+
for (const node of StrategyRows.of(_optionalChain([data, 'optionalAccess', _37 => _37.nodes]))) {
|
|
1805
2829
|
const id = _chunkQ5A2TY2Acjs.FrameworkText.clean(node.id), up = StrategyRows.number(node[visibility]), along = StrategyRows.number(node[evolution]), next = StrategyRows.number(node[evolveTo]);
|
|
1806
2830
|
if (!id || byId.has(id) || up === null || along === null) continue;
|
|
1807
2831
|
const at = StrategyRows.clamp(along, 0, 1);
|
|
1808
2832
|
byId.set(id, { id, name: _chunkQ5A2TY2Acjs.FrameworkText.clean(node[name]) || id, visibility: StrategyRows.clamp(up, 0, 1), evolution: at, evolveTo: next === null ? null : StrategyRows.clamp(next, 0, 1), stage: _WardleyMap.stage(at), node });
|
|
1809
2833
|
}
|
|
1810
2834
|
const seen = /* @__PURE__ */ new Set();
|
|
1811
|
-
const needs = StrategyRows.of(_optionalChain([data, 'optionalAccess',
|
|
1812
|
-
const from = byId.get(_chunkQ5A2TY2Acjs.FrameworkText.clean(link.source)), to = byId.get(_chunkQ5A2TY2Acjs.FrameworkText.clean(link.target)), key = `${_optionalChain([from, 'optionalAccess',
|
|
2835
|
+
const needs = StrategyRows.of(_optionalChain([data, 'optionalAccess', _38 => _38.links])).flatMap((link) => {
|
|
2836
|
+
const from = byId.get(_chunkQ5A2TY2Acjs.FrameworkText.clean(link.source)), to = byId.get(_chunkQ5A2TY2Acjs.FrameworkText.clean(link.target)), key = `${_optionalChain([from, 'optionalAccess', _39 => _39.id])}>${_optionalChain([to, 'optionalAccess', _40 => _40.id])}`;
|
|
1813
2837
|
if (!from || !to || from === to || seen.has(key)) return [];
|
|
1814
2838
|
seen.add(key);
|
|
1815
2839
|
return [[from, to]];
|
|
@@ -1830,7 +2854,7 @@ var WardleyMap = (_class11 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D
|
|
|
1830
2854
|
const bottom = frame.y + frame.height, inset = R2 + 3;
|
|
1831
2855
|
const { components, needs } = this.model;
|
|
1832
2856
|
const spots = new Map(components.map((item) => [item, { ...item, x: along(item.evolution), y: frame.y + inset + (1 - item.visibility) * (frame.height - inset * 2) }]));
|
|
1833
|
-
const spans =
|
|
2857
|
+
const spans = STAGES2.map((stage, i) => ({ name: stage.name, from: along(i ? STAGES2[i - 1].to : 0), to: along(stage.to) }));
|
|
1834
2858
|
const bands = ctx.marks("stage-bands", "rect", stages === "bands" /* Bands */ ? spans.filter((_, i) => i % 2 === 0) : [], (span) => span.name).attr("fill", theme.grid).attr("fill-opacity", 0.55);
|
|
1835
2859
|
ctx.set(bands, { x: (span) => span.from, y: frame.y, width: (span) => span.to - span.from, height: frame.height });
|
|
1836
2860
|
const rules = ctx.marks("stage-rules", "line", stages === "rules" /* Rules */ ? spans.slice(1) : [], (span) => span.name).attr("stroke", theme.axis).attr("stroke-dasharray", "3 3");
|
|
@@ -1845,13 +2869,13 @@ var WardleyMap = (_class11 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D
|
|
|
1845
2869
|
const moves = !movement ? [] : [...spots.values()].flatMap((spot) => {
|
|
1846
2870
|
if (spot.evolveTo === null) return [];
|
|
1847
2871
|
const target = along(spot.evolveTo), way = Math.sign(target - spot.x);
|
|
1848
|
-
return Math.abs(target - spot.x) > R2 +
|
|
2872
|
+
return Math.abs(target - spot.x) > R2 + HEAD6 + 2 ? [{ key: spot.id, spot, x1: spot.x + way * (R2 + 2), x2: target }] : [];
|
|
1849
2873
|
});
|
|
1850
2874
|
const heading = moves.length ? palette.color(MOVEMENT) : fill;
|
|
1851
2875
|
const arrows = ctx.marks("movement", "line", moves, (move) => move.key).attr("stroke", heading).attr("stroke-width", 2).attr("stroke-dasharray", "4 3");
|
|
1852
|
-
ctx.set(arrows, { x1: (move) => move.x1, x2: (move) => move.x2 - Math.sign(move.x2 - move.x1) *
|
|
2876
|
+
ctx.set(arrows, { x1: (move) => move.x1, x2: (move) => move.x2 - Math.sign(move.x2 - move.x1) * HEAD6, y1: (move) => move.spot.y, y2: (move) => move.spot.y });
|
|
1853
2877
|
const heads = ctx.marks("movement-heads", "path", moves, (move) => move.key).attr("fill", heading);
|
|
1854
|
-
ctx.set(heads, { d: `M0,0L${-
|
|
2878
|
+
ctx.set(heads, { d: `M0,0L${-HEAD6},${-HEAD6 / 2}V${HEAD6 / 2}Z`, transform: (move) => `translate(${move.x2},${move.spot.y})${move.x2 < move.x1 ? " rotate(180)" : ""}` });
|
|
1855
2879
|
const moved = (move) => ({ title: move.spot.name, color: heading, rows: [["Now", move.spot.stage], ["Heading for", _WardleyMap.stage(move.spot.evolveTo)]] });
|
|
1856
2880
|
ctx.hover(arrows, moved);
|
|
1857
2881
|
ctx.hover(heads, moved);
|
|
@@ -1871,12 +2895,12 @@ var WardleyMap = (_class11 = class _WardleyMap extends _chunkEEF5WRRZcjs.Chart2D
|
|
|
1871
2895
|
];
|
|
1872
2896
|
const traced = [
|
|
1873
2897
|
...links.flatMap((link) => Array.from({ length: 9 }, (_, i) => ({ x: link.from.x + (link.to.x - link.from.x) * (i + 1) / 10 - 2, y: link.from.y + (link.to.y - link.from.y) * (i + 1) / 10 - 2, width: 4, height: 4 }))),
|
|
1874
|
-
...moves.map((move) => ({ x: Math.min(move.x1, move.x2), y: move.spot.y -
|
|
2898
|
+
...moves.map((move) => ({ x: Math.min(move.x1, move.x2), y: move.spot.y - HEAD6 / 2, width: Math.abs(move.x2 - move.x1), height: HEAD6 }))
|
|
1875
2899
|
];
|
|
1876
2900
|
words.push(...StrategyLabels.place(placed.map((spot) => ({ key: `name/${spot.id}`, text: spot.name, x: spot.x, y: spot.y, r: R2 + 1 })), frame, 10.5, 120, [], traced));
|
|
1877
2901
|
StrategyWords.draw(ctx, "words", words);
|
|
1878
2902
|
}
|
|
1879
|
-
},
|
|
2903
|
+
}, _class16.__initStatic16(), _class16);
|
|
1880
2904
|
|
|
1881
2905
|
// src/charts/strategycharts/index.ts
|
|
1882
2906
|
var strategychartsCatalog = [
|
|
@@ -1890,7 +2914,12 @@ var strategychartsCatalog = [
|
|
|
1890
2914
|
CustomerJourneyMap,
|
|
1891
2915
|
EisenhowerMatrix,
|
|
1892
2916
|
FootballFieldChart,
|
|
1893
|
-
NineBoxGrid
|
|
2917
|
+
NineBoxGrid,
|
|
2918
|
+
CapTableChart,
|
|
2919
|
+
BurnRunwayChart,
|
|
2920
|
+
KanbanBoard,
|
|
2921
|
+
OkrTree,
|
|
2922
|
+
BassDiffusionChart
|
|
1894
2923
|
];
|
|
1895
2924
|
|
|
1896
2925
|
|
|
@@ -1932,4 +2961,17 @@ var strategychartsCatalog = [
|
|
|
1932
2961
|
|
|
1933
2962
|
|
|
1934
2963
|
|
|
1935
|
-
|
|
2964
|
+
|
|
2965
|
+
|
|
2966
|
+
|
|
2967
|
+
|
|
2968
|
+
|
|
2969
|
+
|
|
2970
|
+
|
|
2971
|
+
|
|
2972
|
+
|
|
2973
|
+
|
|
2974
|
+
|
|
2975
|
+
|
|
2976
|
+
|
|
2977
|
+
exports.KanoCategory = KanoCategory; exports.BassDiffusionView = BassDiffusionView; exports.BassDiffusionChart = BassDiffusionChart; exports.BurnRunwayBalance = BurnRunwayBalance; exports.BurnRunwayLayout = BurnRunwayLayout; exports.BurnRunwayChart = BurnRunwayChart; exports.BusinessModelCanvasKind = BusinessModelCanvasKind; exports.BusinessModelCanvasNotes = BusinessModelCanvasNotes; exports.BusinessModelCanvas = BusinessModelCanvas; exports.CapTableView = CapTableView; exports.CapTableBasis = CapTableBasis; exports.CapTableChart = CapTableChart; exports.CustomerJourneyStageMark = CustomerJourneyStageMark; exports.CustomerJourneyEmotionMark = CustomerJourneyEmotionMark; exports.CustomerJourneyEmotionPlace = CustomerJourneyEmotionPlace; exports.CustomerJourneyMap = CustomerJourneyMap; exports.EisenhowerQuadrant = EisenhowerQuadrant; exports.EisenhowerLayout = EisenhowerLayout; exports.EisenhowerTaskMark = EisenhowerTaskMark; exports.EisenhowerMatrix = EisenhowerMatrix; exports.FiveForcesForce = FiveForcesForce; exports.FiveForcesRating = FiveForcesRating; exports.FiveForcesArrows = FiveForcesArrows; exports.FiveForcesDiagram = FiveForcesDiagram; exports.FootballFieldMark = FootballFieldMark; exports.FootballFieldOrder = FootballFieldOrder; exports.FootballFieldChart = FootballFieldChart; exports.HypeCycleMarker = HypeCycleMarker; exports.HypeCyclePhases = HypeCyclePhases; exports.HypeCycleLabels = HypeCycleLabels; exports.HypeCycleChart = HypeCycleChart; exports.KanbanCardDetail = KanbanCardDetail; exports.KanbanBoard = KanbanBoard; exports.KanoCurveSet = KanoCurveSet; exports.KanoAxes = KanoAxes; exports.KanoMarker = KanoMarker; exports.KanoPlacement = KanoPlacement; exports.KanoModelChart = KanoModelChart; exports.NineBoxPlacement = NineBoxPlacement; exports.NineBoxGrid = NineBoxGrid; exports.OkrTreeLayout = OkrTreeLayout; exports.OkrTreeRollup = OkrTreeRollup; exports.OkrTree = OkrTree; exports.StrategyCanvasOrder = StrategyCanvasOrder; exports.StrategyCanvasChart = StrategyCanvasChart; exports.TechnologyAdoptionSegments = TechnologyAdoptionSegments; exports.TechnologyAdoptionCurve = TechnologyAdoptionCurve; exports.WardleyStages = WardleyStages; exports.WardleyAxis = WardleyAxis; exports.WardleyMap = WardleyMap; exports.strategychartsCatalog = strategychartsCatalog;
|