From b9900397cc92fc527d269a00d5d31674b20b21e3 Mon Sep 17 00:00:00 2001 From: heswell Date: Wed, 1 Nov 2023 14:46:57 +0000 Subject: [PATCH 1/4] remove sample app --- .../config/localhost.config.json | 1 - vuu-ui/sample-apps/app-vuu-example/demo.tsx | 30 -- vuu-ui/sample-apps/app-vuu-example/index.tsx | 24 -- .../app-vuu-example/layout-loadind.md | 215 ---------- vuu-ui/sample-apps/app-vuu-example/login.css | 16 - vuu-ui/sample-apps/app-vuu-example/login.tsx | 31 -- .../sample-apps/app-vuu-example/package.json | 38 -- .../app-vuu-example/public/.DS_Store | Bin 6148 -> 0 bytes .../app-vuu-example/public/about.txt | 6 - .../public/apple-touch-icon.png | Bin 8943 -> 0 bytes .../app-vuu-example/public/demo.html | 20 - .../app-vuu-example/public/favicon-16x16.png | Bin 479 -> 0 bytes .../app-vuu-example/public/favicon-32x32.png | Bin 1060 -> 0 bytes .../app-vuu-example/public/favicon.ico | Bin 15406 -> 0 bytes .../app-vuu-example/public/index.html | 23 - .../app-vuu-example/public/login.html | 21 - .../app-vuu-example/public/manifest.json | 7 - .../app-vuu-example/scripts/.eslintrc.json | 15 - .../app-vuu-example/scripts/build.mjs | 188 --------- .../sample-apps/app-vuu-example/src/App.css | 73 ---- .../sample-apps/app-vuu-example/src/App.tsx | 119 ------ .../app-vuu-example/src/Layouts.jsx | 9 - .../src/app-sidepanel/AppSidePanel.css | 5 - .../src/app-sidepanel/AppSidePanel.tsx | 155 ------- .../src/app-sidepanel/index.ts | 1 - .../app-vuu-example/src/columnMetaData.ts | 399 ------------------ .../app-vuu-example/src/createPlaceholder.tsx | 13 - .../src/session-editing/index.ts | 1 - .../session-editing/session-table-config.ts | 106 ----- .../sample-apps/app-vuu-example/tsconfig.json | 6 - 30 files changed, 1522 deletions(-) delete mode 100644 vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json delete mode 100644 vuu-ui/sample-apps/app-vuu-example/demo.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/index.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/layout-loadind.md delete mode 100644 vuu-ui/sample-apps/app-vuu-example/login.css delete mode 100644 vuu-ui/sample-apps/app-vuu-example/login.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/package.json delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/.DS_Store delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/about.txt delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/apple-touch-icon.png delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/demo.html delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/favicon-32x32.png delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/favicon.ico delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/index.html delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/login.html delete mode 100644 vuu-ui/sample-apps/app-vuu-example/public/manifest.json delete mode 100644 vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json delete mode 100644 vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/App.css delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/App.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts delete mode 100644 vuu-ui/sample-apps/app-vuu-example/tsconfig.json diff --git a/vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json b/vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json deleted file mode 100644 index 0967ef424..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/vuu-ui/sample-apps/app-vuu-example/demo.tsx b/vuu-ui/sample-apps/app-vuu-example/demo.tsx deleted file mode 100644 index b732a2f03..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/demo.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { LoginPanel } from "@finos/vuu-shell"; -import { SaltProvider } from "@salt-ds/core"; -import { uuid } from "@finos/vuu-utils"; - -import "./login.css"; - -async function login(username: string) { - try { - const authToken = uuid(); - const date = new Date(); - const days = 1; - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); - document.cookie = `vuu-username=${username};expires=${date.toUTCString()};path=/`; - document.cookie = `vuu-auth-token=${authToken};expires=${date.toUTCString()};path=/`; - document.cookie = `vuu-auth-mode=demo`; - - window.location.href = "index.html"; - } catch (err) { - console.error(err); - } -} - -ReactDOM.render( - - - , - document.getElementById("root") -); diff --git a/vuu-ui/sample-apps/app-vuu-example/index.tsx b/vuu-ui/sample-apps/app-vuu-example/index.tsx deleted file mode 100644 index 770db3202..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { App } from "./src/App"; -import { - getAuthDetailsFromCookies, - LayoutManagementProvider, - redirectToLogin, -} from "@finos/vuu-shell"; - -import "@salt-ds/theme/index.css"; -import "@finos/vuu-icons/index.css"; - -const [username, token] = getAuthDetailsFromCookies(); -if (!username || !token) { - // This won't be needed with serverside protection - redirectToLogin(); -} else { - ReactDOM.render( - - - , - document.getElementById("root") - ); -} diff --git a/vuu-ui/sample-apps/app-vuu-example/layout-loadind.md b/vuu-ui/sample-apps/app-vuu-example/layout-loadind.md deleted file mode 100644 index 17298ff83..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/layout-loadind.md +++ /dev/null @@ -1,215 +0,0 @@ -1. client logs in for first time - /api/vui/{username} - -1A) existing service responds with 404, so we display defaultLayout, with placeholder - -**defaultLayout JSON is hardcoded in the app** - -```JSON -{ - -type: "Stack", -props: { - style: { - width: "100%", - height: "100%", - }, - enableAddTab: true, - enableRemoveTab: true, - preserve: true, - active: 0, - TabstripProps: { - allowAddTab: true, - allowCloseTab: true, - allowRenameTab: true, - }, -}, -children: [ - { - type: "Placeholder", - title: "Page 1", - }, -] -} -``` - -1B) new service returns defaultLayout, with placeholder "vuu.layout.version": 2 - -Note: we wrap the layout structure with an enclosing envelope that carries the version. We might -eventually want to record additional metadata here or user settings etc. Maybe application id - -Note also: the 'placeholder' is defined inline. I think the new system should equally support -inline layouts and dynamically loaded layouts. The former should be considered readonly - -```JSON -{ -"vuu.layout.version": 2 -layout: - { - - type: "Stack", - props: { - style: { - width: "100%", - height: "100%", - }, - enableAddTab: true, - enableRemoveTab: true, - preserve: true, - active: 0, - TabstripProps: { - allowAddTab: true, - allowCloseTab: true, - allowRenameTab: true, - }, - }, - children: [ - { - type: "Placeholder", - title: "Page 1", - }, - ] - } -} -``` - -2. UI renders defaultLayout, with placeholder. In either of the scenarios above we reach this point. - -3. User drags content onto layout to replace or displace placeholder - -3A) existing service POSTS to /api/vui/{username} the full persisted JSON structure, as above but with new content in open tab - -```JSON -{ - -type: "Stack", -props: { - style: { - width: "100%", - height: "100%", - }, - enableAddTab: true, - enableRemoveTab: true, - preserve: true, - active: 0, - TabstripProps: { - allowAddTab: true, - allowCloseTab: true, - allowRenameTab: true, - }, -}, -children: [ - { - type: "View", - id: 'xzy', - title: "Page 1", - props: { - closeable: true, - header: true, - label: "SIMUL Instruments", - resize: "defer" - }, - state: { - "table-config": { - columns: [ - {name: "ric", label: "RIC", pin: "left" }, - ... - ] - } - }, - children: [ - { - type: "Feature", - props: { - url: "./feature-vuu-table/index.js" - css: "./feature-vuu-table/index.css" - } - - } - ] - }, -] -} -``` - -3B) new service makes 2 POST requests - -Save the application-level JSON ... - -- /api/vui/{username} - -```JSON -{ -"vuu.layout.version": 2 -layout: - { - - type: "Stack", - props: { - style: { - width: "100%", - height: "100%", - }, - enableAddTab: true, - enableRemoveTab: true, - preserve: true, - active: 0, - TabstripProps: { - allowAddTab: true, - allowCloseTab: true, - allowRenameTab: true, - }, - }, - children: [ - { - type: "layout", - title: "Page 1", - url: "/api/vui/{username}/xyz" - }, - ] - } -} -``` - -save the new layout ... - -- /api/vui/layouts/xyz - -```JSON -{ - id: "xyz" - createdBy: "", - createdTime: "", - lastUpdatedTime: "", - layout: { - type: "View", - id: 'xzy', - title: "Page 1", - props: { - closeable: true, - header: true, - label: "SIMUL Instruments", - resize: "defer" - }, - state: { - "table-config": { - columns: [ - {name: "ric", label: "RIC", pin: "left" }, - ... - ] - } - }, - children: [ - { - type: "Feature", - props: { - url: "./feature-vuu-table/index.js" - css: "./feature-vuu-table/index.css" - } - - } - ] - } -} - -``` diff --git a/vuu-ui/sample-apps/app-vuu-example/login.css b/vuu-ui/sample-apps/app-vuu-example/login.css deleted file mode 100644 index 56812898f..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/login.css +++ /dev/null @@ -1,16 +0,0 @@ -body { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin: 0; -} - -#root { - width: 100vw; - height: 100vh; - display: flex; - align-items: center; - justify-content: center; - -} diff --git a/vuu-ui/sample-apps/app-vuu-example/login.tsx b/vuu-ui/sample-apps/app-vuu-example/login.tsx deleted file mode 100644 index 878194ee0..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/login.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { LoginPanel } from "@finos/vuu-shell"; -import { authenticate } from "@finos/vuu-data"; -import { SaltProvider } from "@salt-ds/core"; - -import "@finos/vuu-theme/index.css"; -import "./login.css"; - -async function login(username: string, password: string) { - try { - const { authUrl } = await vuuConfig; - const authToken = await authenticate(username, password, authUrl); - const date = new Date(); - const days = 1; - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); - document.cookie = `vuu-username=${username};expires=${date.toUTCString()};path=/`; - document.cookie = `vuu-auth-token=${authToken};expires=${date.toUTCString()};path=/`; - document.cookie = `vuu-auth-mode=login`; - window.location.href = "index.html"; - } catch (err) { - console.error(err); - } -} - -ReactDOM.render( - - - , - document.getElementById("root") -); diff --git a/vuu-ui/sample-apps/app-vuu-example/package.json b/vuu-ui/sample-apps/app-vuu-example/package.json deleted file mode 100644 index c6a74eb97..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "app-vuu-example", - "version": "0.0.26", - "description": "", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "node ./scripts/build.mjs", - "build:with-ag-grid": "node ./scripts/build.mjs --features feature-vuu-blotter,feature-ag-grid", - "start": "serve -p 5002 ../../deployed_apps/app-vuu-example" - }, - "keywords": [], - "author": "heswell", - "license": "Apache-2.0", - "sideEffects": [ - "**/*.css" - ], - "devDependencies": {}, - "dependencies": { - "@fontsource/open-sans": "^4.5.13", - "@salt-ds/core": "1.8.0", - "@salt-ds/icons": "1.5.1", - "@salt-ds/lab": "1.0.0-alpha.15", - "@salt-ds/theme": "1.7.1", - "@finos/vuu-data": "0.0.26", - "@finos/vuu-datagrid-types": "0.0.26", - "@finos/vuu-data-react": "0.0.26", - "@finos/vuu-layout": "0.0.26", - "@finos/vuu-popups": "0.0.26", - "@finos/vuu-shell": "0.0.26", - "@finos/vuu-utils": "0.0.26", - "classnames": "^2.3.1", - "react": "^17.0.2", - "react-dom": "^17.0.2" - }, - "engines": { - "node": ">=16.0.0" - } -} diff --git a/vuu-ui/sample-apps/app-vuu-example/public/.DS_Store b/vuu-ui/sample-apps/app-vuu-example/public/.DS_Store deleted file mode 100644 index f636ba78de789570470d886801d3950c0a112170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!A`D!UkI8lM9gBR=0nykRYjm;37VBZQldT

npw2PwSm~)`b6fpI{ z&7jXm@@wnP!^v73({Jfwl2 diff --git a/vuu-ui/sample-apps/app-vuu-example/public/about.txt b/vuu-ui/sample-apps/app-vuu-example/public/about.txt deleted file mode 100644 index dc353a4a5..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/public/about.txt +++ /dev/null @@ -1,6 +0,0 @@ -This favicon was generated using the following font: - -- Font Title: Roboto Condensed -- Font Author: Copyright 2011 Google Inc. All Rights Reserved. -- Font Source: http://fonts.gstatic.com/s/robotocondensed/v19/ieVl2ZhZI2eCN5jzbjEETS9weq8-59WxDCs5cvI.ttf -- Font License: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)) diff --git a/vuu-ui/sample-apps/app-vuu-example/public/apple-touch-icon.png b/vuu-ui/sample-apps/app-vuu-example/public/apple-touch-icon.png deleted file mode 100644 index 74bdb1c5c9e1f2c79d2275f8d9ec14a08b0d0fec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8943 zcmVPyA07*naRCr$PeR-Tz#kK$U+}p$KTMvseFw>yXxZnoH1yMv)Ai)SIxCMg@juCn4 z&tJ9|O-7&5m^`CCz&K7L!MHIB_&~u06mf}0+`uIoWSRjaFig+dvv%L}KGiTfK;Q1$ zx9{!gZmRy{L)EEM-}+8<-E&TzLy*l70wXIqtuCi2$I+_g1{5t@Qykf<#>HT`n6#lp zbRRu~RT1>HJ3Y<8{c{6a z*H|WX3Rs&6pddl_w-KbQA;NMM>KlIA+v3T7zZ=u12NG1@xT$s65_)U}J3L3$AFV0M zD1~?wgGLcy1Q>a7L*I&t2+hpcOyG?g^F~F(M$fLIEr;s~e%KLuz`{K_IT>kFnYj$W z2>@jSoNfiT`d$Wg0_+3u8Ik60?a-Fj%^ld<_j<)ogTsQ*!z;fjVKw7q4Ri@HE&=ht z_(9xov8p@%Q_j?e;_3+8j7kPD2=xS*P7lL*U- z#vTxD@T*w3W0rGsqN!+=J}F1&;g#NeR{bYyly-~4JedK9RbZup9Afx2g;psVKR_Bb zZ7p{;rNX*;){ruU9^!6tIyf+ch^H}d=DxeJZ2AbWkAQcXX_=qYr8~;E3bn-cdm5O!*$+)|s9zXH=KsZD=LOK5Q-D;$`l5as|l2m8HoaVJCS zOsD~POJh`Nq^?fZ6FVshUE=nRR)G5foB|+4Ol}GDm(dB}V-2{iw#@lTN~`KbvJeKI5|2z~Suoab+E{C^;x3BUy) zDfI4>9kq5+2H*kHfCpMbMv+8PT{0TG9Cm^R!Rxc;D6afZWiD;fDx8QGmb+7IdLc10> z6tJIvPT*TXba=)nTS}z_62{!lr2d&y{%_B$!p3-O7JotqPv(XN;9yXZbYI7N$Kx|! z0E{Y6e&J2=KB?nM=%I`120JwMkF4M-uykL?hg*F#3xP?~p7JaFi|w-qd=oD%;zsD8 z=Px764dBvvEl$pp5C}Z)hdMi6;iz#Tv>>x1BlA%LO-%@hr4n&PfQgm`Sh+qPq_&;V z;+mndx=^0Xaph1VPWF6&0(0%Ye%MRsYF39eH7sGm)rp9()G59Q5aY?3{K9MP{7|%w z(84qajqe3eEF6a|uhYzEUi}jQh6b z7yj0cliGSh3wQR=#+lHxho#e`uQ-$7^D?(XQEv2XFL>N)H@W46c2(8SU?R74XD2wR z`dB^TCRa$AT{F9&+DcU{CA5f|Q-BpR(8$UyN?lnZph*L!SQ({f8KFfyv(nzUT!w(A zE&=UX8Lihc3#M8XPi+mM#YbRE<5FUtX3wIf)Ko@5I9|5p7R<2XBheBAyTX3+^_X z%*HgKOCH&Eyy8gTAd|pIGFpj%NM@sP;M{F<`hQ_Yt|>x`Tyx#+4Wg`=OwVNobvY(1 z0wV2LTBdWNnOu6N2<@tBr~vWrQ>948SlfxvrysFRGoM>I_6k}B^t3PHFgWla+k zyCR2}Mxaw;l-160K4CQ1+7u*o^=auh=%b zNH<5K?g>47v3CRq@F4>ZyOoskB?3kfAcnu&fzx)(a&8VaMwieoxA&1?8ZX%-0*Qw} zrKik!gJD7sul%M&Q_^1o(Q%2imeM>0rMcmg{(oHOM{}1*p%%Y(Vt*CRbVYYU4UE5k z;|~TVglhdW3CBD2e+YN9YS`Kouw_9xX@n8|Rir7Q&$c!{W8-eWEpwX|%KU|*bxzn? zKDhQ^86j6O%?K1W(K}IH4n!W>i?l_ zX$)Y(@@8E^PjIE-FBj<7O)X3A`_)!kHb2zbhU6%CV|qSx z4!Gcu|M{o`4}93J3yman^sHf!ds|tdsMB*mJLIezuy9XKcE7Z*K|C<}Y3v;#ejcBi zl!Fod!~8tAXqf(D3wHV9Q!4Mak(s#ZlnmX4i=XVDtZBuEd-Q*{C8165hG~zltNWi zpVtR)-7Brw-KPHsCJAkhIz)7B&FsR*bqPJTf*qbA4X*(hV{QujN1aflV&#wXbbsPC z2JU;m9glAAuw5B(|6CPD7oouNEnWE4n?7`h`N5EcHoaAEdUh0!>z(r8fD_tPx%c}} zmA3&nO;2I-KyfOrnG*gZ?3HcZC|`TPFOe-;U2|MI?m9awY<;?4yy?TU5k68Sp-pd( z7XfXwr@a3^dn6n{=zT%_73J4}^QxQhO@#ZrB(&MxpiYegWwmqqe;fS&9igkMRaf%`_XBjZS@H22cgg?< zo}8G2tTcW5-A~rE;H_`$yGIvh5FWoc7s3Xu*WTLMjhQdC1`CUXYm(5hFoz%Vkc5s1k!}YD-X^tayl2+1;78N9yK(emEd%`Ay#yNE%}h(j;?UON7Cu0ElJS8e7?_FX$xOwPqA1NH6UFMGN$@AdH3jh;nH zLPy;0upLNIF0a}DJnOqFhuix!fGc7X9ly~>X7BURiH|eA_U8}UvEbv5h+4I?!ZC)NqO2cyWpztPH2%DcSvI$zy&dij@xkYm-yFIy1>yB`y{NvxoK1wB_qr*Q$??%s#!U4h_ z9Xz2+{#t*6;-^nz78_>)7nSzIBjdAm?ZBSq0A{?{ialYxbkV9T0;3j;%faEowXn1c&S^nVc^{-gtc+8Et0f+VyBv|+rVWhql=c%`>QQ?UlX z@fL`Uy9f~@{o<57l;rB$??iCD2(pj0i9ecmugr&-GgK4SlpEIhuwh4J?_lrolF+g4 zOmO_?s**8*N~`uBN2Ix8=N}GB!-8@8 zPo-*_0{H36t*C8@?tUW)ZEyp^cB9?Qf%9qTqQ+4w_-!!j+o7zO&qf)-;;{Ns{TOLs zzMJ)OD?H875v{k%;!GAMLSZV@Yp-qZ#`UkZ>b@P+8z%{E0Xc}e0-(|4s%rQ#h@ZDW zWS@x;WLrHY58u}@r3J47Ykk)h|+dnYi&}{cFdAA9moOkD~in>kY^`gB^Hey#B|haCeV7i!Gq1CfNbjCoH{sk?qHr9iq|5hMM`1>tSPcmBN%fBiiAJFYnO z*It%~!8-S65i2t7g%$`Wj+rJ2Z3eXvCp5Hm zqiLrf;lR>Kxged#DZ=h7UdOZ=3`RP5Zb|59b|Xw*=8we*T|*$3MI!rDj5CAq-jxLq zr*)`S->zZGsupaI@+AG^m~31g<}s{@xxW7c^B_))vzS1e~9>A=&Ia&_gkbu&(RvJu@ego;Q) zTSE_KZlhASH$Xs`hfLN?7B4A7a}_){ITr(U5;}_eu1Mx+B>GNVndhCFfm=`0&jI2V znL%R=PnCqWh8+xO!fY4iCz3Uj+a*N=4n2H9HpUc(zIHTqGR}IY5$y(U2*SGY(AaET zSQ=WE__#d(*`}ytH{?g5UAqsY9HGUjDl&r34U-Y{hPADD*}y$onCYIFsBhaAi5MsU zZ*$aE6G># zlvfUG2+j;GzlrA)G3qny8y{=F?3O7 zlnEaV1z&{iu!}C=<^F#eIa8G>Ei96nDJ7lPc1z9*QJEWCF{2_=k6VLt2Trif$M37=?dPvNDBZ9kqG^mNH}Rzt#47M z=J7DKzD1q#3lpu@cSvCOCz?t2RA5F(BZ}VtVNB>e(!zWvUTc5V5C}R^mQBvZsXBLV zQEqwcvrXu-?)n^5e|$+AsxH*e$h741PTciwnJ7c>fSF(t@1xUT+P)Vi9IK;f%@E+1!J#Cr0Kr(nsX{ z_*3Ox zz>*2M(D}r`8#}s#R-J@zJ?V6XqRE=@JQ|RHcV-rTsuQfVd2aweUfGPFkIcl+bh0mf zUhfaaZkWl86iH9v$PqOU^^)X#xZ95_R=3zv8CP7#?i-{3WNqn}o%q#ThuZ2Rt*H$Hedldm9;Y`Z5a52*2Io3tXn{d}zSvW`MrOPv4?sLKzdAb!Xh~go^Ge6Y# zOzv&hFs8Z*9R^<6L_7B#XJ+EM6ZBK4iCotr=XJuj-X$rLwP;W@AdB)9?_5y;k#sRs z`^zUC5H{=l^h5t~Q0BrCmxy>$>*qDL>x!Rb1-Z(;GC@j?sU-)%jk*Ikwe;ZY6M zM9lZC>wQ>bpc-y8dtpc41BwPA3Cp^f0p&MmWTH$bkF=;*rB?+&Y^KS;Tc*evJN4`@yn>RYPPgZL|LRdcag-BPz#-KQTr8_3z(n!_u!}i}Vxr zc9D$dCU>te>8zdzTmbb_Ey@ebj+RH+Pt_xCkfHV;AS>CgSPAq~h;w5FW}zZw$Om zYSVbntYJY@%y-*bU9GyBFSs9|n-hHzvFImkzH6q4V5P`Xgmpu>oa42zVlzYmq@SLU z5%v#Gm<~k-sOqme@r5@4;l2-752+}F;L2AqZfF{29GedD?{IBXogZ`7`YbIe9T{!2 zJ@_F{-n!c`eR}XBcEHL?s=U`IL~ekM|0bK&qmRaklX=DDT$~i<=e9?+hE5I^0TLmj z@oJ@c3Z9;riz9O)XJ-@Emt8)_ZeIXjH)#+x##_5MNN$m2azGa0sC*U0ISK}4DJYCU zk8$G56#QOd6J^`|8H*9Ku;6s&`dx zYR89rEHG}HRm99Vraiu{?xX7F4m`jF74mbpw5r}sM6=DzlPqJ-IKmOM`-Y0$5Ni-W zy?>0d2)$iv&rk`&3l#kN2bnng$TY(y$8h?H2t$!*^3%Nmh&&_7))?g8R#rIYV7)^U zdYIcgnF&k5&`%wdYzQ2rulVBs#i&dS2odxzwpSFqxAJAT6>y1|<>QYv3V$7&1bFMN(=zY(! z_$7djizr-n=o18lSx!U_ia*S32cal)AF(sUeVYhfeh!|zg2c^TS}=~=+rdGzUh^uA0aTQrflE_Q-mH~>CI=AmvIWz0*@Q!aw6LhAn-a0(6p`P z&O;Lt?w>Vc5T&ci`!f(8woOYZSR!B)0mhAmHHNL)=YH2L(2PQEYTx*-s*1 zIRb>8epXM~QI_wGiO^zjiM#%C1@sJ3sN$^UfJ=dPBA{uCGNbmog6ASl67hr%3aDn) z)!g_Rz~@F1DF0s~U;_dhJb8uVFr6X>oERgtIHiNZ|8N6H+2DU0HbRPw9f3}baX=7% z%vTN}VEIg&G7BeexrUk}^*#9iO`Xs}NXa9+j#nJ%B1oht z+DT=9i9i?x>Y24M+vfED!VF5L2^~zqTikd%i0_adL^IIKF(DAp!1Q09odtKJS89T2 zkTOqb0L4q1i_*IMYe6_Ann`6}i9kpM7$0}{bBy14ZC<@ugkmDJm~_aJwqi$j`v(9G zGOL6fClLU6w?8d&tiUlY%41ep%pPT zO9(B3mr9$x)dbRg8WWS2a&Sr`z(mWo-PTCZgmOs&0|8mvIYGC~VwhPu5cD!>W?jKJU|@?V9kV@3Z(ul7EG4v< z$W>K8g9(dBKCDe`HpaL*23m=^%rmEGX^f%{7;ZVCQNfC9XyZ(1+QT49*;HayZo*+F zi@6<&a-(N^!Q-ePON+QzPiV0_;v=!C;VvTl5;D~Eu;Xil1qYaM-?se1-&*kzc<@~I z5L#S47B>_?<9h*=2Ll0RlL$~StzIP*`n6|PVWS;@*h{o(R);k;EMdacb|^1pCKm$4 zc(NwH@LD@g<{l*MCA6@7EUFu8tF!%t?omXLIxQnhxHjcUZFF~1qu=x0 z9c^XtAhUogZiE(-iR*_$Q~$^cu96>$slis=RS=joJmpsczp(q7(OZYO5;{1sNLtg< zuwef~TlsoCHF+HfK5)$~yvfcFz);Y|pV0eFdhF+)6Zlr?d`U4zGq*G70dmlv>@v-T zL#$5-{oP63_!DMrfpn9n2B{hBBT{bi1qnmwo_lp^bJHwB;BNUwG1jfMdpz%EsJCy+ z%U|4A_vM7Kazuij;w!UBU~E$D0I`Wp=>4jSTzZ;9w}N=4@XN&8WoC#Q;{X5xok>JNR3DFx zNYFV%{IUm3k2^98{%j`KT)fwn6ol>pC78;WFpn9tWzuzfsUnh%6Je3cm>+FQTYEJN zFFmOU-Ln)?NkAdo4-TdvlQmzgWl$%8k2T=7Xfo$ptAcrHNl9q2Tw%9XI50^8t_NYX zw1$``!TymNKyPWxRT`QWizWuenRhGb+Aeo2WAlQGzQLu%nK5M526q}-esm`eo~hv>xq2uB2$LYJqr^S z5mxDBjc|*?JedK9nZNW@9YcU$BU+_!_k$F0d=jn@d@^E?a)j=^3BwnAM`(V&34}2q za)EJpCcYuGLt_t!HYh3=hrU(l-EC}5jg8>Wo59mx&x8WD6z+g-Svj1Zrd@r|njE z=L8fjTT>j_s>a12xR|w}M06yB2N6)jKml0E0kkX-We}ifj@B|dvsc3$mswuuM)z>;GFr?*!a_Tmy>S5jO{{XrXU5MPYy7T}5002ov JPDHLkV1f>NBn|)o diff --git a/vuu-ui/sample-apps/app-vuu-example/public/demo.html b/vuu-ui/sample-apps/app-vuu-example/public/demo.html deleted file mode 100644 index 8054d3bf6..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/public/demo.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - VUU App - - -

- - - diff --git a/vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png b/vuu-ui/sample-apps/app-vuu-example/public/favicon-16x16.png deleted file mode 100644 index f85891bbab87376496b2b9846ad9c2854fe6dbbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 479 zcmV<50U-W~P)Px$nMp)JR5(wilesK^Xd@!ns4OU=e}rrmMQs#|g{>E~ z5kU|Y6aN4gwp_$wcYT1JXklY4)YVPzp=f*yRJC(Wq&>jE zZ-df+#-~~@tN&62J;Jw`$(KeS-?x**;x_;!W&x6*JPx3%#>=6RAr#bz#V_%@9%2x5*bFUjZ^5 V$c=mbo!$Td002ovPDHLkV1oE-*Px&-bqA3R9HvNmu+lQWfaH%=eh0HuHEj+m;{`3Y9NY>VN+tjH(-XveR4((8zv@F zWe_1I5cLc31Mve!iNUBF7K~{mWP<(xRfWbY0=qBb4Vc^K6 zP4j}8xTbVQH}AW;)F#Bs#Ha_6Go8JB&OFA1)`(&3zWjXY`BZz70+fBqPGuypix}Gg zwr3Q0nh|MC*sB*7@9cYu#%A&{BS2ZxKT8zF)<%T8W*0bd7c+j<^+;V`<4qT)`kMye zZN6FsMfrk&`EwCG=7WKLfY$Z;Jl$5m2>|lVZMIW$9o!0`MktlKN`#LaF&fD0A9|m_zGUfq z?fwE(FScW4YY=?h=+1cxj?@)lb9)HKdbC*sFvw3{|G*gn-IEMpb(tL>)VNXGHjLi} zb!pakw*xO$IZ)LS#At-G0N}G!?jP(2P?8KFPXW3%6yxn5#?k!KxHLOhTZoXx@O>GQ zzh_+nV88@06{4h4^gC-^@Z<|L936q%PUw1C#mEP)w7%_E%IpkhmV7;A2^V~Iz>lNQBi0WgqOgt5}J zJnBYI(2xKho}0kFpQCy(*Pe%6xK$OX`+Twu7rF#atyi(7BZQ-W%-n_yKAW};V?mx- zmX96xI;95I915am*oXsct8~Cu;lRp+L4>tvS!%p?G(F*a}^FO^(YwE87KNQd~|LC*CrE{xFI%*&3|+HF){&i{yCOe$mM~= z1jzZ3OhV-HP#Tlye|jv}^_t#IrCru``2Sr@VstsZnpsnnv&-q!Nhc?!9kpzvxtr diff --git a/vuu-ui/sample-apps/app-vuu-example/public/favicon.ico b/vuu-ui/sample-apps/app-vuu-example/public/favicon.ico deleted file mode 100644 index cc26fb39dc9892043efd0897da0e93f96a244a77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 zcmeHNX^b4j6>bhG;uq$Jf5mdoT4rOBg!?d8z~n>`2$3HFDd7+@H^L}#K;p8*5tKNN z5+UO~jTfJbZM=xZYm7M-AAk+Ei5+7=G0w8K@p@-^?z>;!SKZaqJv}`;J+sX)f|h!^ zr}`aVy?R~s>b+K!lay1GDN_`_hLpQcRg{YrMG1wB|Fh0elwNK-_gwRNyP|BLswlIV zhFO@yXy;2!$MJ6Uyl_|Og-CbkAfH+DJcu?t^Aa+$XxeGvZnZPq9n$$EkCUINouIYL z!T<5D(69%OOBQJDGEBWLy6W=OPGAuIt&Z7Ct}bm=t|z(eF^a!&hr!p|PML#i`0S_D z?&qDdOgoqJXAiwD=+Pb@x|NdeKPC7wpROWp_2rffIYxGVZRKoA?_WmQ{|-{P_e_ev zc{e47|15aYpLCG6?pJnxF}`+wi5)s)8~<1f|3N!{?(k-cym%Hx*ZzVs|LL2EU-*^9 zzjPL5K3{L)gB{TS_?G)bTWaTDNqc#Y^<4TS^H=k^61~$e#rVMo8u1_S`}JSO`>x%8 zrHp#tAMndV`lR%YTHjyE@Ary%CdB8}d}4lT^ayv%I6d4Ox;fIVc8zzdyVzDI_$-*` z3AEuEXh7SjKFL4U`NMXW-NSQBigkJUjH4a2-~q3c;zSM~w9j(O66F4A`n>BT(9j&mtSt8qhD-Z%dYxX}=<7&5v#uG?8UL5m*w#4_iD9 z>W2+1S+4t~?d*36_Upe`H2EX%Q||Da*8c|di@3$5(}pKLberMF>#w!+;T_Wp84*u| zw_g45KM^z8^p#`D99Tic%*F5#NvF@*}Jx>W6+K*B8^K}jB z=Y4{R6}{$PZOzXpcVq{}H{N3PU1CV>`a5M0Z4~XPT?@tDN%e z(0~>^;DrpA;D8%g=c$;Fw4S#4jB|j`Q9diKr&Zd)GoW>~&4(<=3>dZ$pX!(~o##8= zSE?hdzwR84tzRrd9f3^94x$f4%1K9iXHFgOQvbwyV>aD>Z9Wlw3_73-Iwz|ykxr;>J=dWVy6YVWdVS^(#=Aog@tFwF zU%c>(cIXBMVDU2+!tTt+xW}7?-KKY3^ufpbfN^W4-EG)QC#Z~4BE72gkNYp$%q@4`1A_a2PB zp7@>f&Oa4;k#p4ZDN35(EmX1v@}-~w=F~_3py;|^QY7i#|d=?U`+4pWg2Jw1~r~y{40o{=!Wau{2o|J zdp_6L)FdOm`A&f;^VvFK+x*zS2{oT``$tw>M8)WrqE3@|>jC%kzS20_^XcVsT>s7e zs;k|jzvXnukHe<1*KVRb*T$%rrRcy7E_|pf!M>@f`}+UvQ-N^|<)U^_Koy zdu<;)=2-DJGpKtNqld&dKFMv58~XPycFEHE=P>?J($it~_dmFv!{<;V6!`ZqbwBs8 zC&xrap1|XIsh@8D!Z$iNP{YA{jJXH3x&rs7d>#!7KjSlU9ChH&9U5ZaDNt(X-zePE zChA_OwP8Mty?Ud=hJoQP*?86dtmsjyqMd+J^R5iKzh$&;U}UF z&b}jQqZ_UlbuB%8f@6YGeJCjW#{0`TJsbS@w$Y51%}_urTVdda{Chk zA8O5&_+qdAPCPHhM)*CyB2fG?56n1fjNjV$JCqLuzT^($8&y{yIMyj9j|&=n*MR2% z*}&mFXakLR>F$93RIY`NF{yi7i@ z%KZ!WV6D7=H207#y?;j^S}A|RHOGo|j#kcJVGLS5f7t5zTZ{pYR?VLq=sBl_y3x0& zei7*j&8wC-m$>cMZ%h41VE0)+lKl|l(4zG_*#}%(wSFr5fN{XwYOW7K)(8TrU(0^r zigh}u?+3irRlEMlxJT^(F&|<;js+2W*SLRh#HmgBe#72>nSZzPz%BCqjJ*%v-+#9) F@PAdixg!7o diff --git a/vuu-ui/sample-apps/app-vuu-example/public/index.html b/vuu-ui/sample-apps/app-vuu-example/public/index.html deleted file mode 100644 index 69b362da4..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - VUU App - - -
- - - diff --git a/vuu-ui/sample-apps/app-vuu-example/public/login.html b/vuu-ui/sample-apps/app-vuu-example/public/login.html deleted file mode 100644 index ac64d5b78..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/public/login.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - VUU App - - -
- - - diff --git a/vuu-ui/sample-apps/app-vuu-example/public/manifest.json b/vuu-ui/sample-apps/app-vuu-example/public/manifest.json deleted file mode 100644 index 17ddafb82..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/public/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "", - "short_name": "", - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json b/vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json deleted file mode 100644 index 17388657e..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "env": { - "node": true, - "es2021": true - }, - "extends": ["eslint:recommended", "plugin:react/recommended"], - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "rules": {} -} diff --git a/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs b/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs deleted file mode 100644 index b91fb1bbf..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs +++ /dev/null @@ -1,188 +0,0 @@ -import { - assertFileExists, - byFileName, - copyFolderSync, - formatBytes, - formatDuration, - getCommandLineArg, - padRight, - readJson, - readPackageJson, - writeMetaFile, -} from "../../../scripts/utils.mjs"; -import { build } from "../../../scripts/esbuild.mjs"; -import fs from "fs"; -import path from "path"; - -const entryPoints = ["index.tsx", "login.tsx", "demo.tsx"]; - -const outdir = "../../deployed_apps/app-vuu-example"; -let configFile = "./config/localhost.config.json"; - -const websocketUrl = getCommandLineArg("--url", true); -console.log(`websocket URL ${websocketUrl} type ${typeof websocketUrl}`); -const watch = getCommandLineArg("--watch"); -const development = watch || getCommandLineArg("--dev"); -const configPath = getCommandLineArg("--config", true); -const features = getCommandLineArg("--features", true, "feature-vuu-table"); -console.log({ features }); -if (configPath) { - configFile = configPath; -} - -const featureEntryPoints = features - .split(",") - .map((featureName) => `../${featureName}/index.ts`); - -assertFileExists(configFile, true); - -const { name: projectName } = readPackageJson(); - -const esbuildConfig = { - entryPoints: entryPoints.concat(featureEntryPoints), - env: development ? "development" : "production", - name: "app-vuu-example", - outdir, - splitting: true, - target: "esnext", -}; - -async function writeFeatureEntriesToConfigJson(featureBundles) { - return new Promise((resolve, reject) => { - console.log("[DEPLOY config]"); - const configJson = readJson(configFile); - if (websocketUrl) { - configJson.websocketUrl = websocketUrl; - } - let { features } = configJson; - if (features === undefined) { - features = configJson.features = {}; - } - - const featureFilePath = (featureName, files, matchPattern) => { - const file = files.find(({ fileName }) => - fileName.endsWith(matchPattern) - ); - if (file) { - return `./feature-${featureName}/${file.fileName}`; - } - }; - - featureBundles.forEach(({ name, files }) => { - const { description = name } = readJson( - path.resolve(`../feature-${name}/package.json`) - ); - features[name] = { - title: description, - name, - url: featureFilePath(name, files, ".js"), - css: featureFilePath(name, files, ".css"), - }; - }); - - fs.writeFile( - path.resolve(outdir, "config.json"), - JSON.stringify(configJson, null, 2), - (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - } - ); - }); -} - -async function main() { - function createDeployFolder() { - fs.rmSync(outdir, { recursive: true, force: true }); - fs.mkdirSync(outdir, { recursive: true }); - } - - console.log("[CLEAN]"); - createDeployFolder(); - - console.log("[BUILD]"); - const [ - { - result: { metafile }, - duration, - }, - ] = await Promise.all([build(esbuildConfig)]).catch((e) => { - console.error(e); - process.exit(1); - }); - - await writeMetaFile(metafile, outdir); - - console.log("[DEPLOY public assets]"); - const publicContent = fs.readdirSync(`./public`); - publicContent.forEach((file) => { - if (file !== ".DS_Store") { - if (typeof fs.cp === "function") { - // node v16.7 + - fs.cp( - path.resolve("public", file), - path.resolve(outdir, file), - { recursive: true }, - (err) => { - if (err) throw err; - } - ); - } else { - // delete once we no longer need to support node16 < .7 - copyFolderSync( - path.resolve("public", file), - path.resolve(outdir, file) - ); - } - } - }); - - const outputs = { - core: [], - common: [], - features: [], - }; - for (const [file, { bytes }] of Object.entries(metafile.outputs)) { - if (file.endsWith("js") || file.endsWith("css")) { - const fileName = file.replace(`${outdir}/`, ""); - if (fileName.startsWith(projectName)) { - outputs.core.push({ fileName, bytes }); - } else if (fileName.startsWith("feature")) { - const [name, featureFileName] = fileName.split("/"); - const featureName = name.replace("feature-", ""); - let feature = outputs.features.find((f) => f.name === featureName); - if (feature === undefined) { - feature = { name: featureName, files: [] }; - outputs.features.push(feature); - } - feature.files.push({ fileName: featureFileName, bytes }); - } else { - outputs.common.push({ fileName, bytes }); - } - } - } - - console.log("\ncore"); - outputs.core.sort(byFileName).forEach(({ fileName, bytes }) => { - console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`); - }); - console.log("\ncommon"); - outputs.common.forEach(({ fileName, bytes }) => { - console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`); - }); - outputs.features.forEach(({ name, files }) => { - console.log(`\nfeature: ${name}`); - files.forEach(({ fileName, bytes }) => { - console.log(`${padRight(fileName, 30)} ${formatBytes(bytes)}`); - }); - }); - - console.log(`\nbuild took ${formatDuration(duration)}`); - - await writeFeatureEntriesToConfigJson(outputs.features); -} - -main(); diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.css b/vuu-ui/sample-apps/app-vuu-example/src/App.css deleted file mode 100644 index 048d3429c..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/App.css +++ /dev/null @@ -1,73 +0,0 @@ -body { - padding: 0; - margin: 0; - height: 100vh; - width: 100vw; - overflow: hidden; -} - -.App { - - --vuuView-borderStyle: none solid solid none; - -} - -.ToolbarField .Input { - border: solid 1px #ccc; - height: 28px; - padding: 0 8px; -} - -.DragIcon.dragging { - background-color: orange !important; -} - -.vuuApp { - background-color: green; -} - -.ToolbarField > svg { - fill: var(--spectrum-global-color-static-gray-600); - width: 18px; - height: 18px; -} - -.vuHeaderCell { - text-transform: uppercase; -} - -.vuDialog { - margin-top: 20%; -} - -.vuDialog { - --vuuView-flex-direction: row; - --vuuView-flex-wrap: wrap; -} - -.vuDialog .Grid { - --hw-grid-flex-size: 1 1 auto; -} - -/** Temp, until we create Toolbar */ -.vuuToolbarProxy { - align-items: center; - display: flex; - gap: 12px; - height: 36px; -} - -.vuuToolbarProxy > [data-align="end"]{ - margin-left: auto; -} - -.vuuToolbarProxy-vertical { - flex-direction: column; -} - -.vuuShell-mainTabs { - height: 100%; - position: relative; - width: 100%; -} - diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx deleted file mode 100644 index 91562ab06..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { hasAction, MenuRpcResponse, TableSchema } from "@finos/vuu-data"; -import { RpcResponseHandler, useVuuTables } from "@finos/vuu-data-react"; -import { Dialog } from "@finos/vuu-popups"; -import { - Feature, - SessionEditingForm, - Shell, - ShellContextProvider, - ShellProps, - ThemeProvider, - VuuUser, -} from "@finos/vuu-shell"; -import { ReactElement, useCallback, useRef, useState } from "react"; -import { AppSidePanel } from "./app-sidepanel"; -import { getDefaultColumnConfig } from "./columnMetaData"; -import { getFormConfig } from "./session-editing"; -import { createPlaceholder } from "./createPlaceholder"; - -import "./App.css"; -// Because we do not render the AppSidePanel directly, the css will not be included in bundle. -import "./app-sidepanel/AppSidePanel.css"; -import { VuuTable } from "@finos/vuu-protocol-types"; - -const defaultWebsocketUrl = `wss://${location.hostname}:8090/websocket`; -const { websocketUrl: serverUrl = defaultWebsocketUrl, features } = - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await vuuConfig; - -//TODO how do we separate this from the feature -const vuuBlotterUrl = "./feature-vuu-table/index.js"; -// const vuuBlotterUrl = "./feature-vuu-table/index.js"; - -// createNewChild is used when we add a new Tab to Stack -const layoutProps: ShellProps["LayoutProps"] = { - createNewChild: createPlaceholder, - pathToDropTarget: "#main-tabs.ACTIVE_CHILD", -}; - -const withTable = (action: unknown): action is { table: VuuTable } => - action !== null && typeof action === "object" && "table" in action; - -export const App = ({ user }: { user: VuuUser }) => { - const dialogTitleRef = useRef(""); - const [dialogContent, setDialogContent] = useState(); - const handleClose = () => { - setDialogContent(undefined); - }; - - const tables = useVuuTables(); - - const handleRpcResponse: RpcResponseHandler = useCallback( - (response) => { - if ( - hasAction(response) && - typeof response.action === "object" && - response.action !== null && - "type" in response.action && - response.action?.type === "OPEN_DIALOG_ACTION" - ) { - const { tableSchema } = response.action as unknown as { - tableSchema: TableSchema; - }; - if (tableSchema) { - const formConfig = getFormConfig(response as MenuRpcResponse); - dialogTitleRef.current = formConfig.config.title; - setDialogContent( - - ); - } else if ( - withTable(response.action) && - tables && - response.action.table - ) { - const schema = tables.get(response.action.table.table); - if (schema) { - // If we already have this table open in this viewport, ignore - setDialogContent( - - ); - } - } - } else { - console.warn(`App, handleServiceRequest ${JSON.stringify(response)}`); - } - }, - [tables] - ); - - // TODO get Context from Shell - return ( - - - } - serverUrl={serverUrl} - user={user} - > - - {dialogContent} - - - - - ); -}; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx b/vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx deleted file mode 100644 index 4e95fc3f7..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import { FlexboxLayout as Flexbox, Placeholder } from "@finos/vuu-layout"; - -export const twoColumns = ( - - - - -); diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css deleted file mode 100644 index c6028d563..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.css +++ /dev/null @@ -1,5 +0,0 @@ - -.vuuFeatureDropdown { - border-bottom: solid 1px var(--salt-container-primary-borderColor); - margin-bottom: var(--salt-space-unit); -} \ No newline at end of file diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx deleted file mode 100644 index 32e884ba2..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/AppSidePanel.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { byModule, TableSchema } from "@finos/vuu-data"; -import { Palette, PaletteItem, ViewProps } from "@finos/vuu-layout"; -import { Feature, Features } from "@finos/vuu-shell"; -import { - Accordion, - AccordionGroup, - AccordionHeader, - AccordionPanel, -} from "@salt-ds/core"; -import { Dropdown, SelectionChangeHandler } from "@salt-ds/lab"; -import cx from "classnames"; -import { ReactElement, useMemo, useState } from "react"; - -import "./AppSidePanel.css"; - -const NO_FEATURES: Features = {}; -const NULL_FEATURE = {}; -export interface AppSidePanelProps { - features?: Features; - tables?: Map; - ViewProps?: Partial; -} - -type FeatureDescriptor = { - className: string; - css: string; - js: string; - name: string; - title: string; -}; - -const capitalize = (text: string) => - text.length === 0 ? "" : text[0].toUpperCase() + text.slice(1); - -const regexp_worfify = /(? { - const [firstWord, ...rest] = text.split(regexp_worfify); - return `${capitalize(firstWord)} ${rest.join(" ")}`; -}; - -const classBase = "vuuAppSidePanel"; - -export const AppSidePanel = ({ - features = NO_FEATURES, - tables, - ViewProps, -}: AppSidePanelProps) => { - const gridFeatures = useMemo( - () => - Object.entries(features).map(([featureName, { title, url, css }]) => { - return { - className: featureName, - css, - js: url, - name: featureName, - title, - } as FeatureDescriptor; - }), - [features] - ); - - const [selectedFeature, setSelectedFeature] = useState( - gridFeatures[0] ?? NULL_FEATURE - ); - const handleSelectFeature: SelectionChangeHandler = (event, item) => { - const feature = gridFeatures.find((f) => f.title === item); - if (feature) { - setSelectedFeature(feature); - } - }; - - const paletteItems = useMemo(() => { - return tables === undefined - ? [] - : Array.from(tables.values()) - .sort(byModule) - .map((schema) => { - const { className, css, js } = selectedFeature; - return { - component: ( - - ), - id: schema.table.table, - label: `${schema.table.module} ${wordify(schema.table.table)}`, - }; - }); - }, [selectedFeature, tables]); - - const featureSelection = (): ReactElement => { - const featureNames = gridFeatures.map((f) => f.title); - if (featureNames.length === 1) { - return
{featureNames[0]}
; - } else { - return ( - - className="vuuFeatureDropdown" - fullWidth - onSelectionChange={handleSelectFeature} - selected={selectedFeature?.title} - source={featureNames} - /> - ); - } - }; - - return ( -
- - - My Layouts - - - - Vuu Tables - - <> - {featureSelection()} - - {paletteItems.map((spec) => ( - - {spec.component} - - ))} - - - - - - Layout Templates - - - -
- ); -}; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts b/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts deleted file mode 100644 index fa3d1a25a..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/app-sidepanel/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./AppSidePanel"; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts b/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts deleted file mode 100644 index 7a8016780..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts +++ /dev/null @@ -1,399 +0,0 @@ -import { ColumnDescriptor } from "@finos/vuu-datagrid-types"; - -const Average = 2; - -const ccy: Partial = { - name: "ccy", - label: "CCY", - width: 60, -}; - -const filledQuantity: Partial = { - label: "Filled Qty", - name: "filledQuantity", - minWidth: 150, - type: { - name: "number", - renderer: { name: "progress", associatedField: "quantity" }, - formatting: { decimals: 0 }, - }, -}; - -const ric: Partial = { - name: "ric", - label: "RIC", - type: { - name: "string", - }, - width: 60, -}; - -const side: Partial = { - label: "Side", - name: "side", - type: { - name: "string", - }, - width: 60, -}; - -const columnMetaData: { [key: string]: Partial } = { - account: { - label: "Account", - name: "account", - type: { - name: "string", - }, - }, - algo: { - label: "Algo", - name: "algo", - type: { - name: "string", - }, - }, - ask: { - name: "ask", - label: "Ask", - type: { - name: "number", - renderer: { name: "background", flashStyle: "arrow-bg" }, - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - askSize: { - name: "askSize", - label: "Ask Size", - type: { - name: "number", - }, - aggregate: Average, - }, - averagePrice: { - label: "Average Price", - name: "averagePrice", - type: { - name: "number", - }, - aggregate: Average, - }, - bbg: { - name: "bbg", - label: "BBG", - type: { - name: "string", - }, - }, - bid: { - label: "Bid", - name: "bid", - type: { - name: "number", - renderer: { name: "background", flashStyle: "arrow-bg" }, - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - bidSize: { - label: "Bid Size", - name: "bidSize", - type: { - name: "number", - }, - aggregate: Average, - }, - childCount: { - label: "Child Count", - name: "childCount", - type: { - name: "number", - }, - aggregate: Average, - }, - - close: { - label: "Close", - name: "close", - type: { - name: "number", - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - clOrderId: { - label: "Child Order ID", - name: "clOrderId", - width: 60, - }, - created: { - label: "Created", - name: "created", - type: { - name: "time", - }, - }, - currency: { - name: "currency", - label: "CCY", - width: 60, - }, - description: { - name: "description", - label: "Description", - type: { - name: "string", - }, - }, - exchange: { - name: "exchange", - label: "Exchange", - type: { - name: "string", - }, - }, - filledQty: { - label: "Filled Qty", - name: "filledQty", - width: 150, - type: { - name: "number", - }, - }, - id: { - name: "id", - label: "ID", - type: { - name: "string", - }, - }, - idAsInt: { - label: "ID (int)", - name: "idAsInt", - type: { - name: "string", - }, - }, - isin: { - name: "isin", - label: "ISIN", - type: { - name: "string", - }, - }, - last: { - label: "Last", - name: "last", - type: { - name: "number", - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - lastUpdate: { - label: "Last Update", - name: "lastUpdate", - type: { - name: "time", - }, - }, - lotSize: { - label: "Lot Size", - name: "lotSize", - width: 80, - type: { - name: "number", - }, - }, - max: { - label: "Max", - name: "max", - width: 80, - type: { - name: "number", - }, - }, - mean: { - label: "Mean", - name: "mean", - width: 80, - type: { - name: "number", - }, - }, - open: { - label: "Open", - name: "open", - type: { - name: "number", - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - openQty: { - label: "Open Qty", - name: "openQty", - width: 80, - type: { - name: "number", - formatting: { decimals: 0 }, - }, - }, - orderId: { - label: "Order ID", - name: "orderId", - width: 60, - }, - - phase: { - label: "Phase", - name: "phase", - type: { - name: "string", - }, - }, - parentOrderId: { - label: "Parent Order Id", - name: "parentOrderId", - width: 80, - type: { - name: "number", - }, - }, - orderType: { - label: "Order Type", - name: "orderType", - type: { - name: "string", - }, - }, - price: { - label: "Price", - name: "price", - type: { - name: "number", - formatting: { decimals: 2, zeroPad: true }, - }, - aggregate: Average, - }, - priceLevel: { - label: "Price Level", - name: "priceLevel", - type: { - name: "string", - }, - }, - quantity: { - label: "Quantity", - name: "quantity", - width: 80, - type: { - name: "number", - }, - }, - scenario: { - label: "Scenario", - name: "scenario", - type: { - name: "string", - }, - }, - size: { - label: "Size", - name: "size", - width: 80, - type: { - name: "number", - }, - }, - status: { - label: "Status", - name: "status", - type: { - name: "string", - }, - }, - strategy: { - label: "Strategy", - name: "strategy", - type: { - name: "string", - }, - }, - table: { - label: "Table", - name: "table", - type: { - name: "string", - }, - }, - trader: { - label: "Trader", - name: "trader", - type: { - name: "string", - }, - }, - uniqueId: { - label: "Unique ID", - name: "uniqueId", - type: { - name: "string", - }, - }, - updateCount: { - label: "Update Count", - name: "updateCount", - width: 80, - type: { - name: "number", - }, - }, - updatesPerSecond: { - label: "Updates Per Second", - name: "updatesPerSecond", - width: 80, - type: { - name: "number", - }, - }, - user: { - label: "User", - name: "user", - type: { - name: "string", - }, - }, - volLimit: { - label: "Vol Limit", - name: "volLimit", - width: 80, - type: { - name: "number", - }, - }, -}; - -type TableColDefs = { [key: string]: Partial }; - -const tables: { [key: string]: TableColDefs } = { - orders: { - ccy, - filledQuantity, - ric, - side, - }, - ordersPrices: { - ccy, - filledQuantity, - ric, - side, - }, -}; - -export const getDefaultColumnConfig = ( - tableName: string, - columnName: string -) => { - return tables[tableName]?.[columnName] ?? columnMetaData[columnName]; -}; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx b/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx deleted file mode 100644 index 0bfc5cb22..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Placeholder, View } from "@finos/vuu-layout"; - -export const createPlaceholder = (index?: number) => ( - // Note make this width 100% and height 100% and we get a weird error where view continually resizes - growing - - - -); diff --git a/vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts b/vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts deleted file mode 100644 index 8e92f0ebb..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./session-table-config"; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts b/vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts deleted file mode 100644 index 91dc3cb01..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { - MenuRpcResponse, - OpenDialogAction, - TableSchema, -} from "@finos/vuu-data"; -import { FormConfig, FormFieldDescriptor } from "@finos/vuu-shell"; - -const static_config: { [key: string]: Partial } = { - OPEN_EDIT_RESET_FIX: { - title: "Reset the Sequence Number", - fields: [ - { - label: "Process Id", - description: "Process Id", - name: "process-id", - type: "string", - }, - { - description: "Sequence Number", - label: "Sequence Number", - name: "sequenceNumber", - type: "long", - }, - ], - }, -}; - -const mergeFields = ( - fields: FormFieldDescriptor[], - staticFields?: FormFieldDescriptor[] -) => { - if (Array.isArray(staticFields)) { - return fields.map((field) => { - const { name } = field; - const staticField = staticFields.find((f) => f.name === name); - if (staticField) { - return { - ...field, - ...staticField, - }; - } else { - return field; - } - }); - } else { - return fields; - } -}; - -const getStaticConfig = (rpcName: string, formConfig: FormConfig) => { - const staticConfig = static_config[rpcName]; - if (staticConfig) { - return { - ...formConfig, - ...staticConfig, - fields: mergeFields(formConfig.fields, staticConfig.fields), - }; - } else { - return formConfig; - } -}; - -const defaultFormConfig = { - fields: [], - key: "", - title: "", -}; - -const keyFirst = (c1: FormFieldDescriptor, c2: FormFieldDescriptor) => - c1.isKeyField ? -1 : c2.isKeyField ? 1 : 0; - -const configFromSchema = (schema?: TableSchema): FormConfig | undefined => { - if (schema) { - const { columns, key } = schema; - return { - key, - title: `Parameters for command`, - fields: columns - .map((col) => ({ - description: col.name, - label: col.name, - name: col.name, - type: col.serverDataType, - isKeyField: col.name === key, - })) - .sort(keyFirst), - }; - } -}; - -export const getFormConfig = ({ action, rpcName }: MenuRpcResponse) => { - const { tableSchema: schema } = action as OpenDialogAction; - const config = configFromSchema(schema) ?? defaultFormConfig; - - if (rpcName !== undefined && rpcName in static_config) { - return { - config: getStaticConfig(rpcName, config), - schema, - }; - } - - return { - config, - schema, - }; -}; diff --git a/vuu-ui/sample-apps/app-vuu-example/tsconfig.json b/vuu-ui/sample-apps/app-vuu-example/tsconfig.json deleted file mode 100644 index 409f9488e..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "target": "esnext", - } -} From 6d246cfe69f77773c47eed090300a69973a45aa2 Mon Sep 17 00:00:00 2001 From: heswell Date: Wed, 1 Nov 2023 15:13:35 +0000 Subject: [PATCH 2/4] rename basket trading app to sample-app --- vuu-ui/package.json | 2 - .../packages/vuu-data/src/inlined-worker.js | 2566 ++++++++++++++++- .../vuu-shell/src/login/LoginPanel.tsx | 6 +- .../config/localhost.config.json | 0 .../demo.tsx | 0 .../index.tsx | 0 .../layout-loadind.md | 0 .../login.css | 0 .../login.tsx | 6 +- .../package.json | 2 +- .../public/about.txt | 0 .../public/apple-touch-icon.png | Bin .../public/demo.html | 4 +- .../public/favicon.ico | Bin .../public/favicon.png | Bin .../public/favicon.svg | 0 .../public/index.html | 4 +- .../public/login.html | 4 +- .../public/manifest.json | 0 .../public/vuu-icon.svg | 0 .../scripts/.eslintrc.json | 0 .../scripts/build.mjs | 4 +- .../src/App.css | 0 .../src/App.tsx | 0 .../src/Layouts.jsx | 0 .../src/columnMetaData.ts | 0 .../src/createPlaceholder.tsx | 0 .../src/session-editing/index.ts | 0 .../session-editing/session-table-config.ts | 0 .../src/useFeatures.ts | 0 .../src/useRpcResponseHandler.tsx | 0 .../tsconfig.json | 0 .../feature-basket-trading/package.json | 2 +- .../feature-filter-table/package.json | 2 +- .../feature-instrument-tiles/package.json | 2 +- .../sample-apps/feature-template/package.json | 2 +- 36 files changed, 2585 insertions(+), 21 deletions(-) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/config/localhost.config.json (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/demo.tsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/index.tsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/layout-loadind.md (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/login.css (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/login.tsx (86%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/package.json (96%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/about.txt (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/apple-touch-icon.png (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/demo.html (80%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/favicon.ico (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/favicon.png (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/favicon.svg (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/index.html (82%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/login.html (83%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/manifest.json (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/public/vuu-icon.svg (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/scripts/.eslintrc.json (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/scripts/build.mjs (98%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/App.css (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/App.tsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/Layouts.jsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/columnMetaData.ts (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/createPlaceholder.tsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/session-editing/index.ts (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/session-editing/session-table-config.ts (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/useFeatures.ts (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/src/useRpcResponseHandler.tsx (100%) rename vuu-ui/sample-apps/{app-vuu-basket-trader => app-vuu-example}/tsconfig.json (100%) diff --git a/vuu-ui/package.json b/vuu-ui/package.json index c471237d9..6511b5ef5 100644 --- a/vuu-ui/package.json +++ b/vuu-ui/package.json @@ -19,12 +19,10 @@ "format": "prettier --write './**/*.{js,,mjs,jsx,css,md,json}' --config ./.prettierrc", "build": "node ./scripts/build-all.mjs", "build:app": "cd sample-apps/app-vuu-example && node scripts/build.mjs", - "build:app:basket": "cd sample-apps/app-vuu-basket-trader && node scripts/build.mjs", "build:packages": "npm run build -- --cjs --license && npm run type-defs", "build:packages:debug": "npm run build -- --cjs --debug && npm run type-defs -- --debug", "build:salt": "node ./scripts/build-salt.mjs", "launch:demo": "node ./scripts/launch-app.mjs", - "launch:demo:basket": "node ./scripts/launch-app.mjs -- --basket", "launch:demo:electron": "cd tools/electron && node ./node_modules/.bin/electron .", "launch:showcase": " cd showcase && node scripts/launch.mjs", "deploy:websocket-test": "node ./tools/deploy-websocket-test.mjs", diff --git a/vuu-ui/packages/vuu-data/src/inlined-worker.js b/vuu-ui/packages/vuu-data/src/inlined-worker.js index 4522f1be9..52abdfe2d 100644 --- a/vuu-ui/packages/vuu-data/src/inlined-worker.js +++ b/vuu-ui/packages/vuu-data/src/inlined-worker.js @@ -1,8 +1,2568 @@ export const workerSourceCode = ` -var fe=(s,e,t)=>{if(!e.has(s))throw TypeError("Cannot "+t)};var p=(s,e,t)=>(fe(s,e,"read from private field"),t?t.call(s):e.get(s)),U=(s,e,t)=>{if(e.has(s))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(s):e.set(s,t)},me=(s,e,t,n)=>(fe(s,e,"write to private field"),n?n.call(s,t):e.set(s,t),t);function he(s,e,t=[],n=[]){for(let r=0,o=s.length;r{var e,t;if(((e=globalThis.document)==null?void 0:e.cookie)!==void 0)return(t=globalThis.document.cookie.split("; ").find(n=>n.startsWith(\`\${s}=\`)))==null?void 0:t.split("=")[1]};function Y({from:s,to:e},t=0,n=Number.MAX_SAFE_INTEGER){if(t===0)return ns>=e&&s=this.to||ttypeof s=="string"&&ct.includes(s),dt="error",F=()=>{},gt="error",{loggingLevel:N=gt}=ft(),E=s=>{let e=N==="debug",t=e||N==="info",n=t||N==="warn",r=n||N==="error",o=t?g=>console.info(\`[\${s}] \${g}\`):F,a=n?g=>console.warn(\`[\${s}] \${g}\`):F,u=e?g=>console.debug(\`[\${s}] \${g}\`):F;return{errorEnabled:r,error:r?g=>console.error(\`[\${s}] \${g}\`):F}};function ft(){return typeof loggingSettings<"u"?loggingSettings:{loggingLevel:mt()}}function mt(){let s=be("vuu-logging-level");return pt(s)?s:dt}var{debug:ht,debugEnabled:bt}=E("range-monitor"),W=class{constructor(e){this.source=e;this.range={from:0,to:0};this.timestamp=0}isSet(){return this.timestamp!==0}set({from:e,to:t}){let{timestamp:n}=this;if(this.range.from=e,this.range.to=t,this.timestamp=performance.now(),n)bt&&ht(\`<\${this.source}> [\${e}-\${t}], \${(this.timestamp-n).toFixed(0)} ms elapsed\`);else return 0}};function Ce(s){return Array.isArray(s)}function Ct(s){return!Array.isArray(s)}var y,ye=class{constructor(){U(this,y,new Map)}addListener(e,t){let n=p(this,y).get(e);n?Ce(n)?n.push(t):Ct(n)&&p(this,y).set(e,[n,t]):p(this,y).set(e,t)}removeListener(e,t){if(!p(this,y).has(e))return;let n=p(this,y).get(e),r=-1;if(n===t)p(this,y).delete(e);else if(Array.isArray(n)){for(let o=length;o-- >0;)if(n[o]===t){r=o;break}if(r<0)return;n.length===1?(n.length=0,p(this,y).delete(e)):n.splice(r,1)}}removeAllListeners(e){e&&p(this,y).has(e)?p(this,y).delete(e):e===void 0&&p(this,y).clear()}emit(e,...t){if(p(this,y)){let n=p(this,y).get(e);n&&this.invokeHandler(n,t)}}once(e,t){let n=(...r)=>{this.removeListener(e,n),t(...r)};this.on(e,n)}on(e,t){this.addListener(e,t)}hasListener(e,t){let n=p(this,y).get(e);return Array.isArray(n)?n.includes(t):n===t}invokeHandler(e,t){if(Ce(e))e.slice().forEach(n=>this.invokeHandler(n,t));else switch(t.length){case 0:e();break;case 1:e(t[0]);break;case 2:e(t[0],t[1]);break;default:e.call(null,...t)}}};y=new WeakMap;var \$=String.fromCharCode(8200),m=String.fromCharCode(8199);var En={DIGIT:m,TWO_DIGITS:m+m,THREE_DIGITS:m+m+m,FULL_PADDING:[null,\$+m,\$+m+m,\$+m+m+m,\$+m+m+m+m]};var wn=m+m+m+m+m+m+m+m+m;var{COUNT:Bn}=M;var q=class{constructor(e){this.keys=new Map,this.free=[],this.nextKeyValue=0,this.reset(e)}next(){return this.free.length>0?this.free.pop():this.nextKeyValue++}reset({from:e,to:t}){this.keys.forEach((r,o)=>{(o=t)&&(this.free.push(r),this.keys.delete(o))});let n=t-e;this.keys.size+this.free.length>n&&(this.free.length=Math.max(0,n-this.keys.size));for(let r=e;rthis.keys.size&&(this.nextKeyValue=this.keys.size)}keyFor(e){let t=this.keys.get(e);if(t===void 0)throw console.log(\`key not found +var __accessCheck = (obj, member, msg) => { + if (!member.has(obj)) + throw TypeError("Cannot " + msg); +}; +var __privateGet = (obj, member, getter) => { + __accessCheck(obj, member, "read from private field"); + return getter ? getter.call(obj) : member.get(obj); +}; +var __privateAdd = (obj, member, value) => { + if (member.has(obj)) + throw TypeError("Cannot add the same private member more than once"); + member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +}; +var __privateSet = (obj, member, value, setter) => { + __accessCheck(obj, member, "write to private field"); + setter ? setter.call(obj, value) : member.set(obj, value); + return value; +}; + +// ../vuu-utils/src/array-utils.ts +function partition(array, test, pass = [], fail = []) { + for (let i = 0, len = array.length; i < len; i++) { + (test(array[i], i) ? pass : fail).push(array[i]); + } + return [pass, fail]; +} + +// ../vuu-utils/src/column-utils.ts +var metadataKeys = { + IDX: 0, + RENDER_IDX: 1, + IS_LEAF: 2, + IS_EXPANDED: 3, + DEPTH: 4, + COUNT: 5, + KEY: 6, + SELECTED: 7, + count: 8, + // TODO following only used in datamodel + PARENT_IDX: "parent_idx", + IDX_POINTER: "idx_pointer", + FILTER_COUNT: "filter_count", + NEXT_FILTER_IDX: "next_filter_idx" +}; +var { DEPTH, IS_LEAF } = metadataKeys; + +// ../vuu-utils/src/cookie-utils.ts +var getCookieValue = (name) => { + var _a, _b; + if (((_a = globalThis.document) == null ? void 0 : _a.cookie) !== void 0) { + return (_b = globalThis.document.cookie.split("; ").find((row) => row.startsWith(\`\${name}=\`))) == null ? void 0 : _b.split("=")[1]; + } +}; + +// ../vuu-utils/src/range-utils.ts +function getFullRange({ from, to }, bufferSize = 0, rowCount = Number.MAX_SAFE_INTEGER) { + if (bufferSize === 0) { + if (rowCount < from) { + return { from: 0, to: 0 }; + } else { + return { from, to: Math.min(to, rowCount) }; + } + } else if (from === 0) { + return { from, to: Math.min(to + bufferSize, rowCount) }; + } else { + const rangeSize = to - from; + const buff = Math.round(bufferSize / 2); + const shortfallBefore = from - buff < 0; + const shortFallAfter = rowCount - (to + buff) < 0; + if (shortfallBefore && shortFallAfter) { + return { from: 0, to: rowCount }; + } else if (shortfallBefore) { + return { from: 0, to: rangeSize + bufferSize }; + } else if (shortFallAfter) { + return { + from: Math.max(0, rowCount - (rangeSize + bufferSize)), + to: rowCount + }; + } else { + return { from: from - buff, to: to + buff }; + } + } +} +var withinRange = (value, { from, to }) => value >= from && value < to; +var WindowRange = class { + constructor(from, to) { + this.from = from; + this.to = to; + } + isWithin(index) { + return withinRange(index, this); + } + //find the overlap of this range and a new one + overlap(from, to) { + return from >= this.to || to < this.from ? [0, 0] : [Math.max(from, this.from), Math.min(to, this.to)]; + } + copy() { + return new WindowRange(this.from, this.to); + } +}; + +// ../vuu-utils/src/DataWindow.ts +var { KEY } = metadataKeys; + +// ../vuu-utils/src/logging-utils.ts +var logLevels = ["error", "warn", "info", "debug"]; +var isValidLogLevel = (value) => typeof value === "string" && logLevels.includes(value); +var DEFAULT_LOG_LEVEL = "error"; +var NO_OP = () => void 0; +var DEFAULT_DEBUG_LEVEL = false ? "error" : "info"; +var { loggingLevel = DEFAULT_DEBUG_LEVEL } = getLoggingSettings(); +var logger = (category) => { + const debugEnabled5 = loggingLevel === "debug"; + const infoEnabled5 = debugEnabled5 || loggingLevel === "info"; + const warnEnabled = infoEnabled5 || loggingLevel === "warn"; + const errorEnabled = warnEnabled || loggingLevel === "error"; + const info5 = infoEnabled5 ? (message) => console.info(\`[\${category}] \${message}\`) : NO_OP; + const warn4 = warnEnabled ? (message) => console.warn(\`[\${category}] \${message}\`) : NO_OP; + const debug5 = debugEnabled5 ? (message) => console.debug(\`[\${category}] \${message}\`) : NO_OP; + const error4 = errorEnabled ? (message) => console.error(\`[\${category}] \${message}\`) : NO_OP; + if (false) { + return { + errorEnabled, + error: error4 + }; + } else { + return { + debugEnabled: debugEnabled5, + infoEnabled: infoEnabled5, + warnEnabled, + errorEnabled, + info: info5, + warn: warn4, + debug: debug5, + error: error4 + }; + } +}; +function getLoggingSettings() { + if (typeof loggingSettings !== "undefined") { + return loggingSettings; + } else { + return { + loggingLevel: getLoggingLevelFromCookie() + }; + } +} +function getLoggingLevelFromCookie() { + const value = getCookieValue("vuu-logging-level"); + if (isValidLogLevel(value)) { + return value; + } else { + return DEFAULT_LOG_LEVEL; + } +} + +// ../vuu-utils/src/debug-utils.ts +var { debug, debugEnabled } = logger("range-monitor"); +var RangeMonitor = class { + constructor(source) { + this.source = source; + this.range = { from: 0, to: 0 }; + this.timestamp = 0; + } + isSet() { + return this.timestamp !== 0; + } + set({ from, to }) { + const { timestamp } = this; + this.range.from = from; + this.range.to = to; + this.timestamp = performance.now(); + if (timestamp) { + debugEnabled && debug( + \`<\${this.source}> [\${from}-\${to}], \${(this.timestamp - timestamp).toFixed(0)} ms elapsed\` + ); + } else { + return 0; + } + } +}; + +// ../vuu-utils/src/event-emitter.ts +function isArrayOfListeners(listeners) { + return Array.isArray(listeners); +} +function isOnlyListener(listeners) { + return !Array.isArray(listeners); +} +var _events; +var EventEmitter = class { + constructor() { + __privateAdd(this, _events, /* @__PURE__ */ new Map()); + } + addListener(event, listener) { + const listeners = __privateGet(this, _events).get(event); + if (!listeners) { + __privateGet(this, _events).set(event, listener); + } else if (isArrayOfListeners(listeners)) { + listeners.push(listener); + } else if (isOnlyListener(listeners)) { + __privateGet(this, _events).set(event, [listeners, listener]); + } + } + removeListener(event, listener) { + if (!__privateGet(this, _events).has(event)) { + return; + } + const listenerOrListeners = __privateGet(this, _events).get(event); + let position = -1; + if (listenerOrListeners === listener) { + __privateGet(this, _events).delete(event); + } else if (Array.isArray(listenerOrListeners)) { + for (let i = length; i-- > 0; ) { + if (listenerOrListeners[i] === listener) { + position = i; + break; + } + } + if (position < 0) { + return; + } + if (listenerOrListeners.length === 1) { + listenerOrListeners.length = 0; + __privateGet(this, _events).delete(event); + } else { + listenerOrListeners.splice(position, 1); + } + } + } + removeAllListeners(event) { + if (event && __privateGet(this, _events).has(event)) { + __privateGet(this, _events).delete(event); + } else if (event === void 0) { + __privateGet(this, _events).clear(); + } + } + emit(event, ...args) { + if (__privateGet(this, _events)) { + const handler = __privateGet(this, _events).get(event); + if (handler) { + this.invokeHandler(handler, args); + } + } + } + once(event, listener) { + const handler = (...args) => { + this.removeListener(event, handler); + listener(...args); + }; + this.on(event, handler); + } + on(event, listener) { + this.addListener(event, listener); + } + hasListener(event, listener) { + const listeners = __privateGet(this, _events).get(event); + if (Array.isArray(listeners)) { + return listeners.includes(listener); + } else { + return listeners === listener; + } + } + invokeHandler(handler, args) { + if (isArrayOfListeners(handler)) { + handler.slice().forEach((listener) => this.invokeHandler(listener, args)); + } else { + switch (args.length) { + case 0: + handler(); + break; + case 1: + handler(args[0]); + break; + case 2: + handler(args[0], args[1]); + break; + default: + handler.call(null, ...args); + } + } + } +}; +_events = new WeakMap(); + +// ../vuu-utils/src/round-decimal.ts +var PUNCTUATION_STR = String.fromCharCode(8200); +var DIGIT_STR = String.fromCharCode(8199); +var Space = { + DIGIT: DIGIT_STR, + TWO_DIGITS: DIGIT_STR + DIGIT_STR, + THREE_DIGITS: DIGIT_STR + DIGIT_STR + DIGIT_STR, + FULL_PADDING: [ + null, + PUNCTUATION_STR + DIGIT_STR, + PUNCTUATION_STR + DIGIT_STR + DIGIT_STR, + PUNCTUATION_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR, + PUNCTUATION_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + ] +}; +var LEADING_FILL = DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR + DIGIT_STR; + +// ../vuu-utils/src/json-utils.ts +var { COUNT } = metadataKeys; + +// ../vuu-utils/src/keyset.ts +var KeySet = class { + constructor(range) { + this.keys = /* @__PURE__ */ new Map(); + this.free = []; + this.nextKeyValue = 0; + this.reset(range); + } + next() { + if (this.free.length > 0) { + return this.free.pop(); + } else { + return this.nextKeyValue++; + } + } + reset({ from, to }) { + this.keys.forEach((keyValue, rowIndex) => { + if (rowIndex < from || rowIndex >= to) { + this.free.push(keyValue); + this.keys.delete(rowIndex); + } + }); + const size = to - from; + if (this.keys.size + this.free.length > size) { + this.free.length = Math.max(0, size - this.keys.size); + } + for (let rowIndex = from; rowIndex < to; rowIndex++) { + if (!this.keys.has(rowIndex)) { + const nextKeyValue = this.next(); + this.keys.set(rowIndex, nextKeyValue); + } + } + if (this.nextKeyValue > this.keys.size) { + this.nextKeyValue = this.keys.size; + } + } + keyFor(rowIndex) { + const key = this.keys.get(rowIndex); + if (key === void 0) { + console.log(\`key not found keys: \${this.toDebugString()} free : \${this.free.join(",")} - \`),Error(\`KeySet, no key found for rowIndex \${e}\`);return t}toDebugString(){return Array.from(this.keys.entries()).map((e,t)=>\`\${e}=>\${t}\`).join(",")}};var{IDX:Zn}=M;var{SELECTED:er}=M,x={False:0,True:1,First:2,Last:4};var yt=(s,e)=>e>=s[0]&&e<=s[1],St=x.True+x.First+x.Last,Tt=x.True+x.First,Rt=x.True+x.Last,Z=(s,e)=>{for(let t of s)if(typeof t=="number"){if(t===e)return St}else if(yt(t,e))return e===t[0]?Tt:e===t[1]?Rt:x.True;return x.False};var Se=s=>{if(s.every(t=>typeof t=="number"))return s;let e=[];for(let t of s)if(typeof t=="number")e.push(t);else for(let n=t[0];n<=t[1];n++)e.push(n);return e};var Et=(()=>{let s=0,e=()=>\`0000\${(Math.random()*36**4<<0).toString(36)}\`.slice(-4);return()=>(s+=1,\`u\${e()}\${s}\`)})();var{debug:ks,debugEnabled:As,error:Ee,info:w,infoEnabled:It,warn:_}=E("websocket-connection"),we="ws",vt=s=>s.startsWith(we+"://")||s.startsWith(we+"s://"),xe={},ee=Symbol("setWebsocket"),B=Symbol("connectionCallback");async function Ie(s,e,t,n=10,r=5){return xe[s]={status:"connecting",connect:{allowed:r,remaining:r},reconnect:{allowed:n,remaining:n}},ve(s,e,t)}async function Q(s){throw Error("connection broken")}async function ve(s,e,t,n){let{status:r,connect:o,reconnect:a}=xe[s],u=r==="connecting"?o:a;try{t({type:"connection-status",status:"connecting"});let c=typeof n<"u",g=await _t(s,e);console.info("%c\u26A1 %cconnected","font-size: 24px;color: green;font-weight: bold;","color:green; font-size: 14px;"),n!==void 0&&n[ee](g);let i=n!=null?n:new te(g,s,e,t),l=c?"reconnected":"connection-open-awaiting-session";return t({type:"connection-status",status:l}),i.status=l,u.remaining=u.allowed,i}catch{let g=--u.remaining>0;if(t({type:"connection-status",status:"disconnected",reason:"failed to connect",retry:g}),g)return Dt(s,e,t,n,2e3);throw Error("Failed to establish connection")}}var Dt=(s,e,t,n,r)=>new Promise(o=>{setTimeout(()=>{o(ve(s,e,t,n))},r)}),_t=(s,e)=>new Promise((t,n)=>{let r=vt(s)?s:\`wss://\${s}\`;It&&e!==void 0&&w(\`WebSocket Protocol \${e==null?void 0:e.toString()}\`);let o=new WebSocket(r,e);o.onopen=()=>t(o),o.onerror=a=>n(a)}),Ve=()=>{_==null||_("Connection cannot be closed, socket not yet opened")},Me=s=>{_==null||_(\`Message cannot be sent, socket closed \${s.body.type}\`)},Pt=s=>{try{return JSON.parse(s)}catch{throw Error(\`Error parsing JSON response from server \${s}\`)}},te=class{constructor(e,t,n,r){this.close=Ve;this.requiresLogin=!0;this.send=Me;this.status="ready";this.messagesCount=0;this.connectionMetricsInterval=null;this.handleWebsocketMessage=e=>{let t=Pt(e.data);this.messagesCount+=1,this[B](t)};this.url=t,this.protocol=n,this[B]=r,this[ee](e)}reconnect(){Q(this)}[(B,ee)](e){let t=this[B];e.onmessage=o=>{this.status="connected",e.onmessage=this.handleWebsocketMessage,this.handleWebsocketMessage(o)},this.connectionMetricsInterval=setInterval(()=>{t({type:"connection-metrics",messagesLength:this.messagesCount}),this.messagesCount=0},2e3),e.onerror=()=>{Ee("\u26A1 connection error"),t({type:"connection-status",status:"disconnected",reason:"error"}),this.connectionMetricsInterval&&(clearInterval(this.connectionMetricsInterval),this.connectionMetricsInterval=null),this.status==="connection-open-awaiting-session"?Ee("Websocket connection lost before Vuu session established, check websocket configuration"):this.status!=="closed"&&(Q(this),this.send=r)},e.onclose=()=>{w==null||w("\u26A1 connection close"),t({type:"connection-status",status:"disconnected",reason:"close"}),this.connectionMetricsInterval&&(clearInterval(this.connectionMetricsInterval),this.connectionMetricsInterval=null),this.status!=="closed"&&(Q(this),this.send=r)};let n=o=>{e.send(JSON.stringify(o))},r=o=>{w==null||w(\`TODO queue message until websocket reconnected \${o.body.type}\`)};this.send=n,this.close=()=>{this.status="closed",e.close(),this.close=Ve,this.send=Me,w==null||w("close websocket")}}};var Lt=["VIEW_PORT_MENUS_SELECT_RPC","VIEW_PORT_MENU_TABLE_RPC","VIEW_PORT_MENU_ROW_RPC","VIEW_PORT_MENU_CELL_RPC","VP_EDIT_CELL_RPC","VP_EDIT_ROW_RPC","VP_EDIT_ADD_ROW_RPC","VP_EDIT_DELETE_CELL_RPC","VP_EDIT_DELETE_ROW_RPC","VP_EDIT_SUBMIT_FORM_RPC"],De=s=>Lt.includes(s.type),ne=({requestId:s,...e})=>[s,e],_e=s=>{let e=s.at(0);if(e.updateType==="SIZE"){if(s.length===1)return s;e=s.at(1)}let t=s.at(-1);return[e,t]},Pe=s=>{let e={};for(let t of s)(e[t.viewPortId]||(e[t.viewPortId]=[])).push(t);return e};var re=({columns:s,dataTypes:e,key:t,table:n})=>({table:n,columns:s.map((r,o)=>({name:r,serverDataType:e[o]})),key:t});var Le=s=>s.type==="connection-status",Oe=s=>s.type==="connection-metrics";var ke=s=>"viewport"in s,Ae=s=>s.type==="VIEW_PORT_MENU_RESP"&&s.action!==null&&G(s.action.table),G=s=>s!==null&&typeof s=="object"&&"table"in s&&"module"in s?s.table.startsWith("session"):!1;var Ue="CHANGE_VP_SUCCESS",Fe="CHANGE_VP_RANGE_SUCCESS",Ne="CLOSE_TREE_NODE",We="CLOSE_TREE_SUCCESS";var \$e="CREATE_VP",qe="CREATE_VP_SUCCESS",Be="DISABLE_VP",Ge="DISABLE_VP_SUCCESS";var Ke="ENABLE_VP",He="ENABLE_VP_SUCCESS";var se="GET_VP_VISUAL_LINKS",je="GET_VIEW_PORT_MENUS";var ze="HB",Je="HB_RESP",Ye="LOGIN",Ze="LOGIN_SUCCESS",Xe="OPEN_TREE_NODE",Qe="OPEN_TREE_SUCCESS";var et="REMOVE_VP";var oe="RPC_RESP";var tt="SET_SELECTION_SUCCESS",ie="TABLE_META_RESP",ae="TABLE_LIST_RESP",nt="TABLE_ROW";var st=s=>{switch(s){case"TypeAheadRpcHandler":return"TYPEAHEAD";default:return"SIMUL"}};var ot=[],T=E("array-backed-moving-window");function Ot(s,e){if(!e||e.data.length!==s.data.length||e.sel!==s.sel)return!1;for(let t=0;t{var t;if((t=T.info)==null||t.call(T,\`setRowCount \${e}\`),e{let n=this.bufferSize*.25;return p(this,h).to-t0&&e-p(this,h).from0&&this.clientRange.from+this.rowsWithinRange===this.rowCount}outOfRange(e,t){let{from:n,to:r}=this.range;if(t=r)return!0}setAtIndex(e){let{rowIndex:t}=e,n=t-p(this,h).from;if(Ot(e,this.internalData[n]))return!1;let r=this.isWithinClientRange(t);return(r||this.isWithinRange(t))&&(!this.internalData[n]&&r&&(this.rowsWithinRange+=1),this.internalData[n]=e),r}getAtIndex(e){return p(this,h).isWithin(e)&&this.internalData[e-p(this,h).from]!=null?this.internalData[e-p(this,h).from]:void 0}isWithinRange(e){return p(this,h).isWithin(e)}isWithinClientRange(e){return this.clientRange.isWithin(e)}setClientRange(e,t){var g;(g=T.debug)==null||g.call(T,\`setClientRange \${e} - \${t}\`);let n=this.clientRange.from,r=Math.min(this.clientRange.to,this.rowCount);if(e===n&&t===r)return[!1,ot];let o=this.clientRange.copy();this.clientRange.from=e,this.clientRange.to=t,this.rowsWithinRange=0;for(let i=e;io.to){let i=Math.max(e,o.to);a=this.internalData.slice(i-u,t-u)}else{let i=Math.min(o.from,t);a=this.internalData.slice(e-u,i-u)}return[this.bufferBreakout(e,t),a]}setRange(e,t){var n,r;if(e!==p(this,h).from||t!==p(this,h).to){(n=T.debug)==null||n.call(T,\`setRange \${e} - \${t}\`);let[o,a]=p(this,h).overlap(e,t),u=new Array(t-e);this.rowsWithinRange=0;for(let c=o;c=0;o--)if(e[o]!==void 0){r=e[o];break}return n&&r?[n.rowIndex,r.rowIndex]:[-1,-1]}};h=new WeakMap;var kt=[],{debug:b,debugEnabled:H,error:At,info:d,infoEnabled:Ut,warn:P}=E("viewport"),Ft=({rowKey:s,updateType:e})=>e==="U"&&!s.startsWith("\$root"),j=[void 0,void 0],Nt={count:0,mode:void 0,size:0,ts:0},z=class{constructor({aggregations:e,bufferSize:t=50,columns:n,filter:r,groupBy:o=[],table:a,range:u,sort:c,title:g,viewport:i,visualLink:l},f){this.batchMode=!0;this.hasUpdates=!1;this.pendingUpdates=[];this.pendingOperations=new Map;this.pendingRangeRequests=[];this.rowCountChanged=!1;this.selectedRows=[];this.tableSchema=null;this.useBatchMode=!0;this.lastUpdateStatus=Nt;this.updateThrottleTimer=void 0;this.rangeMonitor=new W("ViewPort");this.disabled=!1;this.isTree=!1;this.status="";this.suspended=!1;this.suspendTimer=null;this.setLastSizeOnlyUpdateSize=e=>{this.lastUpdateStatus.size=e};this.setLastUpdate=e=>{let{ts:t,mode:n}=this.lastUpdateStatus,r=0;if(n===e){let o=Date.now();this.lastUpdateStatus.count+=1,this.lastUpdateStatus.ts=o,r=t===0?0:o-t}else this.lastUpdateStatus.count=1,this.lastUpdateStatus.ts=0,r=0;return this.lastUpdateStatus.mode=e,r};this.rangeRequestAlreadyPending=e=>{let{bufferSize:t}=this,n=t*.25,{from:r}=e;for(let{from:o,to:a}of this.pendingRangeRequests)if(r>=o&&r{this.updateThrottleTimer=void 0,this.lastUpdateStatus.count=3,this.postMessageToClient({clientViewportId:this.clientViewportId,mode:"size-only",size:this.lastUpdateStatus.size,type:"viewport-update"})};this.shouldThrottleMessage=e=>{let t=this.setLastUpdate(e);return e==="size-only"&&t>0&&t<500&&this.lastUpdateStatus.count>3};this.throttleMessage=e=>this.shouldThrottleMessage(e)?(d==null||d("throttling updates setTimeout to 2000"),this.updateThrottleTimer===void 0&&(this.updateThrottleTimer=setTimeout(this.sendThrottledSizeMessage,2e3)),!0):(this.updateThrottleTimer!==void 0&&(clearTimeout(this.updateThrottleTimer),this.updateThrottleTimer=void 0),!1);this.getNewRowCount=()=>{if(this.rowCountChanged&&this.dataWindow)return this.rowCountChanged=!1,this.dataWindow.rowCount};this.aggregations=e,this.bufferSize=t,this.clientRange=u,this.clientViewportId=i,this.columns=n,this.filter=r,this.groupBy=o,this.keys=new q(u),this.pendingLinkedParent=l,this.table=a,this.sort=c,this.title=g,Ut&&(d==null||d(\`constructor #\${i} \${a.table} bufferSize=\${t}\`)),this.dataWindow=new K(this.clientRange,u,this.bufferSize),this.postMessageToClient=f}get hasUpdatesToProcess(){return this.suspended?!1:this.rowCountChanged||this.hasUpdates}get size(){var e;return(e=this.dataWindow.rowCount)!=null?e:0}subscribe(){let{filter:e}=this.filter;return this.status=this.status==="subscribed"?"resubscribing":"subscribing",{type:\$e,table:this.table,range:Y(this.clientRange,this.bufferSize),aggregations:this.aggregations,columns:this.columns,sort:this.sort,groupBy:this.groupBy,filterSpec:{filter:e}}}handleSubscribed({viewPortId:e,aggregations:t,columns:n,filterSpec:r,range:o,sort:a,groupBy:u}){return this.serverViewportId=e,this.status="subscribed",this.aggregations=t,this.columns=n,this.groupBy=u,this.isTree=u&&u.length>0,this.dataWindow.setRange(o.from,o.to),{aggregations:t,type:"subscribed",clientViewportId:this.clientViewportId,columns:n,filter:r,groupBy:u,range:o,sort:a,tableSchema:this.tableSchema}}awaitOperation(e,t){this.pendingOperations.set(e,t)}completeOperation(e,...t){var u;let{clientViewportId:n,pendingOperations:r}=this,o=r.get(e);if(!o){At("no matching operation found to complete");return}let{type:a}=o;if(d==null||d(\`completeOperation \${a}\`),r.delete(e),a==="CHANGE_VP_RANGE"){let[c,g]=t;(u=this.dataWindow)==null||u.setRange(c,g);for(let i=this.pendingRangeRequests.length-1;i>=0;i--){let l=this.pendingRangeRequests[i];if(l.requestId===e){l.acked=!0;break}else P==null||P("range requests sent faster than they are being ACKed")}}else if(a==="config"){let{aggregations:c,columns:g,filter:i,groupBy:l,sort:f}=o.data;return this.aggregations=c,this.columns=g,this.filter=i,this.groupBy=l,this.sort=f,l.length>0?this.isTree=!0:this.isTree&&(this.isTree=!1),b==null||b(\`config change confirmed, isTree : \${this.isTree}\`),{clientViewportId:n,type:a,config:o.data}}else{if(a==="groupBy")return this.isTree=o.data.length>0,this.groupBy=o.data,b==null||b(\`groupBy change confirmed, isTree : \${this.isTree}\`),{clientViewportId:n,type:a,groupBy:o.data};if(a==="columns")return this.columns=o.data,{clientViewportId:n,type:a,columns:o.data};if(a==="filter")return this.filter=o.data,{clientViewportId:n,type:a,filter:o.data};if(a==="aggregate")return this.aggregations=o.data,{clientViewportId:n,type:"aggregate",aggregations:this.aggregations};if(a==="sort")return this.sort=o.data,{clientViewportId:n,type:a,sort:this.sort};if(a!=="selection"){if(a==="disable")return this.disabled=!0,{type:"disabled",clientViewportId:n};if(a==="enable")return this.disabled=!1,{type:"enabled",clientViewportId:n};if(a==="CREATE_VISUAL_LINK"){let[c,g,i]=t;return this.linkedParent={colName:c,parentViewportId:g,parentColName:i},this.pendingLinkedParent=void 0,{type:"vuu-link-created",clientViewportId:n,colName:c,parentViewportId:g,parentColName:i}}else if(a==="REMOVE_VISUAL_LINK")return this.linkedParent=void 0,{type:"vuu-link-removed",clientViewportId:n}}}}rangeRequest(e,t){H&&this.rangeMonitor.set(t);let n="CHANGE_VP_RANGE";if(this.dataWindow){let[r,o]=this.dataWindow.setClientRange(t.from,t.to),a,u=this.dataWindow.rowCount||void 0,c=r&&!this.rangeRequestAlreadyPending(t)?{type:n,viewPortId:this.serverViewportId,...Y(t,this.bufferSize,u)}:null;if(c){H&&(b==null||b(\`create CHANGE_VP_RANGE: [\${c.from} - \${c.to}]\`)),this.awaitOperation(e,{type:n});let i=this.pendingRangeRequests.at(-1);if(i)if(i.acked)console.warn("Range Request before previous request is filled");else{let{from:l,to:f}=i;this.dataWindow.outOfRange(l,f)?a={clientViewportId:this.clientViewportId,type:"debounce-begin"}:P==null||P("Range Request before previous request is acked")}this.pendingRangeRequests.push({...c,requestId:e}),this.useBatchMode&&(this.batchMode=!0)}else o.length>0&&(this.batchMode=!1);this.keys.reset(this.dataWindow.clientRange);let g=this.isTree?le:ue;return o.length?[c,o.map(i=>g(i,this.keys,this.selectedRows))]:a?[c,void 0,a]:[c]}else return[null]}setLinks(e){return this.links=e,[{type:"vuu-links",links:e,clientViewportId:this.clientViewportId},this.pendingLinkedParent]}setMenu(e){return{type:"vuu-menu",menu:e,clientViewportId:this.clientViewportId}}setTableSchema(e){this.tableSchema=e}openTreeNode(e,t){return this.useBatchMode&&(this.batchMode=!0),{type:Xe,vpId:this.serverViewportId,treeKey:t.key}}closeTreeNode(e,t){return this.useBatchMode&&(this.batchMode=!0),{type:Ne,vpId:this.serverViewportId,treeKey:t.key}}createLink(e,t,n,r){let o={type:"CREATE_VISUAL_LINK",parentVpId:n,childVpId:this.serverViewportId,parentColumnName:r,childColumnName:t};return this.awaitOperation(e,o),this.useBatchMode&&(this.batchMode=!0),o}removeLink(e){let t={type:"REMOVE_VISUAL_LINK",childVpId:this.serverViewportId};return this.awaitOperation(e,t),t}suspend(){this.suspended=!0,d==null||d("suspend")}resume(){return this.suspended=!1,H&&(b==null||b(\`resume: \${this.currentData()}\`)),this.currentData()}currentData(){let e=[];if(this.dataWindow){let t=this.dataWindow.getData(),{keys:n}=this,r=this.isTree?le:ue;for(let o of t)o&&e.push(r(o,n,this.selectedRows))}return e}enable(e){return this.awaitOperation(e,{type:"enable"}),d==null||d(\`enable: \${this.serverViewportId}\`),{type:Ke,viewPortId:this.serverViewportId}}disable(e){return this.awaitOperation(e,{type:"disable"}),d==null||d(\`disable: \${this.serverViewportId}\`),this.suspended=!1,{type:Be,viewPortId:this.serverViewportId}}columnRequest(e,t){return this.awaitOperation(e,{type:"columns",data:t}),b==null||b(\`columnRequest: \${t}\`),this.createRequest({columns:t})}filterRequest(e,t){this.awaitOperation(e,{type:"filter",data:t}),this.useBatchMode&&(this.batchMode=!0);let{filter:n}=t;return d==null||d(\`filterRequest: \${n}\`),this.createRequest({filterSpec:{filter:n}})}setConfig(e,t){this.awaitOperation(e,{type:"config",data:t});let{filter:n,...r}=t;return this.useBatchMode&&(this.batchMode=!0),H?b==null||b(\`setConfig \${JSON.stringify(t)}\`):d==null||d("setConfig"),this.createRequest({...r,filterSpec:typeof(n==null?void 0:n.filter)=="string"?{filter:n.filter}:{filter:""}},!0)}aggregateRequest(e,t){return this.awaitOperation(e,{type:"aggregate",data:t}),d==null||d(\`aggregateRequest: \${t}\`),this.createRequest({aggregations:t})}sortRequest(e,t){return this.awaitOperation(e,{type:"sort",data:t}),d==null||d(\`sortRequest: \${JSON.stringify(t.sortDefs)}\`),this.createRequest({sort:t})}groupByRequest(e,t=kt){var n;return this.awaitOperation(e,{type:"groupBy",data:t}),this.useBatchMode&&(this.batchMode=!0),this.isTree||(n=this.dataWindow)==null||n.clear(),this.createRequest({groupBy:t})}selectRequest(e,t){return this.selectedRows=t,this.awaitOperation(e,{type:"selection",data:t}),d==null||d(\`selectRequest: \${t}\`),{type:"SET_SELECTION",vpId:this.serverViewportId,selection:Se(t)}}removePendingRangeRequest(e,t){for(let n=this.pendingRangeRequests.length-1;n>=0;n--){let{from:r,to:o}=this.pendingRangeRequests[n],a=!0;if(e>=r&&er&&t0){e=[],t="update";for(let a of this.pendingUpdates)e.push(o(a,n,r));this.pendingUpdates.length=0}else{let a=this.dataWindow.getData();if(this.dataWindow.hasAllRowsWithinRange){e=[],t="batch";for(let u of a)e.push(o(u,n,r));this.batchMode=!1}}this.hasUpdates=!1}return this.throttleMessage(t)?j:[e,t]}createRequest(e,t=!1){return t?{type:"CHANGE_VP",viewPortId:this.serverViewportId,...e}:{type:"CHANGE_VP",viewPortId:this.serverViewportId,aggregations:this.aggregations,columns:this.columns,sort:this.sort,groupBy:this.groupBy,filterSpec:{filter:this.filter.filter},...e}}},ue=({rowIndex:s,rowKey:e,sel:t,data:n},r,o)=>[s,r.keyFor(s),!0,!1,0,0,e,t?Z(o,s):0].concat(n),le=({rowIndex:s,rowKey:e,sel:t,data:n},r,o)=>{let[a,u,,c,,g,...i]=n;return[s,r.keyFor(s),c,u,a,g,e,t?Z(o,s):0].concat(i)};var it=1;var{debug:I,debugEnabled:L,error:O,info:S,infoEnabled:Wt,warn:k}=E("server-proxy"),C=()=>\`\${it++}\`,\$t={},qt=s=>s.disabled!==!0&&s.suspended!==!0,Bt={type:"NO_ACTION"},Gt=(s,e,t)=>s.map(n=>n.parentVpId===e?{...n,label:t}:n);function Kt(s,e){return s.map(t=>{let{parentVpId:n}=t,r=e.get(n);if(r)return{...t,parentClientVpId:r.clientViewportId,label:r.title};throw Error("addLabelsToLinks viewport not found")})}var J=class{constructor(e,t){this.authToken="";this.user="user";this.pendingTableMetaRequests=new Map;this.pendingRequests=new Map;this.queuedRequests=[];this.cachedTableSchemas=new Map;this.connection=e,this.postMessageToClient=t,this.viewports=new Map,this.mapClientToServerViewport=new Map}async reconnect(){await this.login(this.authToken);let[e,t]=he(Array.from(this.viewports.values()),qt);this.viewports.clear(),this.mapClientToServerViewport.clear();let n=r=>{r.forEach(o=>{let{clientViewportId:a}=o;this.viewports.set(a,o),this.sendMessageToServer(o.subscribe(),a)})};n(e),setTimeout(()=>{n(t)},2e3)}async login(e,t="user"){if(e)return this.authToken=e,this.user=t,new Promise((n,r)=>{this.sendMessageToServer({type:Ye,token:this.authToken,user:t},""),this.pendingLogin={resolve:n,reject:r}});this.authToken===""&&O("login, cannot login until auth token has been obtained")}subscribe(e){if(this.mapClientToServerViewport.has(e.viewport))O(\`spurious subscribe call \${e.viewport}\`);else{if(!this.hasSchemaForTable(e.table)&&!G(e.table)){S==null||S(\`subscribe to \${e.table.table}, no metadata yet, request metadata\`);let n=C();this.sendMessageToServer({type:"GET_TABLE_META",table:e.table},n),this.pendingTableMetaRequests.set(n,e.viewport)}let t=new z(e,this.postMessageToClient);this.viewports.set(e.viewport,t),this.sendIfReady(t.subscribe(),e.viewport,this.sessionId!=="")}}unsubscribe(e){let t=this.mapClientToServerViewport.get(e);t?(S==null||S(\`Unsubscribe Message (Client to Server): - \${t}\`),this.sendMessageToServer({type:et,viewPortId:t})):O(\`failed to unsubscribe client viewport \${e}, viewport not found\`)}getViewportForClient(e,t=!0){let n=this.mapClientToServerViewport.get(e);if(n){let r=this.viewports.get(n);if(r)return r;if(t)throw Error(\`Viewport not found for client viewport \${e}\`);return null}else{if(this.viewports.has(e))return this.viewports.get(e);if(t)throw Error(\`Viewport server id not found for client viewport \${e}\`);return null}}setViewRange(e,t){let n=C(),[r,o,a]=e.rangeRequest(n,t.range);S==null||S(\`setViewRange \${t.range.from} - \${t.range.to}\`),r&&this.sendIfReady(r,n,e.status==="subscribed"),o?(S==null||S(\`setViewRange \${o.length} rows returned from cache\`),this.postMessageToClient({mode:"batch",type:"viewport-update",clientViewportId:e.clientViewportId,rows:o})):a&&this.postMessageToClient(a)}setConfig(e,t){let n=C(),r=e.setConfig(n,t.config);this.sendIfReady(r,n,e.status==="subscribed")}aggregate(e,t){let n=C(),r=e.aggregateRequest(n,t.aggregations);this.sendIfReady(r,n,e.status==="subscribed")}sort(e,t){let n=C(),r=e.sortRequest(n,t.sort);this.sendIfReady(r,n,e.status==="subscribed")}groupBy(e,t){let n=C(),r=e.groupByRequest(n,t.groupBy);this.sendIfReady(r,n,e.status==="subscribed")}filter(e,t){let n=C(),{filter:r}=t,o=e.filterRequest(n,r);this.sendIfReady(o,n,e.status==="subscribed")}setColumns(e,t){let n=C(),{columns:r}=t,o=e.columnRequest(n,r);this.sendIfReady(o,n,e.status==="subscribed")}setTitle(e,t){e&&(e.title=t.title,this.updateTitleOnVisualLinks(e))}select(e,t){let n=C(),{selected:r}=t,o=e.selectRequest(n,r);this.sendIfReady(o,n,e.status==="subscribed")}disableViewport(e){let t=C(),n=e.disable(t);this.sendIfReady(n,t,e.status==="subscribed")}enableViewport(e){if(e.disabled){let t=C(),n=e.enable(t);this.sendIfReady(n,t,e.status==="subscribed")}}suspendViewport(e){e.suspend(),e.suspendTimer=setTimeout(()=>{S==null||S("suspendTimer expired, escalate suspend to disable"),this.disableViewport(e)},3e3)}resumeViewport(e){e.suspendTimer&&(I==null||I("clear suspend timer"),clearTimeout(e.suspendTimer),e.suspendTimer=null);let t=e.resume();this.postMessageToClient({clientViewportId:e.clientViewportId,mode:"batch",rows:t,type:"viewport-update"})}openTreeNode(e,t){if(e.serverViewportId){let n=C();this.sendIfReady(e.openTreeNode(n,t),n,e.status==="subscribed")}}closeTreeNode(e,t){if(e.serverViewportId){let n=C();this.sendIfReady(e.closeTreeNode(n,t),n,e.status==="subscribed")}}createLink(e,t){let{parentClientVpId:n,parentColumnName:r,childColumnName:o}=t,a=C(),u=this.mapClientToServerViewport.get(n);if(u){let c=e.createLink(a,o,u,r);this.sendMessageToServer(c,a)}else O("ServerProxy unable to create link, viewport not found")}removeLink(e){let t=C(),n=e.removeLink(t);this.sendMessageToServer(n,t)}updateTitleOnVisualLinks(e){var r;let{serverViewportId:t,title:n}=e;for(let o of this.viewports.values())if(o!==e&&o.links&&t&&n&&(r=o.links)!=null&&r.some(a=>a.parentVpId===t)){let[a]=o.setLinks(Gt(o.links,t,n));this.postMessageToClient(a)}}removeViewportFromVisualLinks(e){var t;for(let n of this.viewports.values())if((t=n.links)!=null&&t.some(({parentVpId:r})=>r===e)){let[r]=n.setLinks(n.links.filter(({parentVpId:o})=>o!==e));this.postMessageToClient(r)}}menuRpcCall(e){let t=this.getViewportForClient(e.vpId,!1);if(t!=null&&t.serverViewportId){let[n,r]=ne(e);this.sendMessageToServer({...r,vpId:t.serverViewportId},n)}}rpcCall(e){let[t,n]=ne(e),r=st(n.service);this.sendMessageToServer(n,t,{module:r})}handleMessageFromClient(e){if(ke(e))if(e.type==="disable"){let t=this.getViewportForClient(e.viewport,!1);return t!==null?this.disableViewport(t):void 0}else{let t=this.getViewportForClient(e.viewport);switch(e.type){case"setViewRange":return this.setViewRange(t,e);case"config":return this.setConfig(t,e);case"aggregate":return this.aggregate(t,e);case"sort":return this.sort(t,e);case"groupBy":return this.groupBy(t,e);case"filter":return this.filter(t,e);case"select":return this.select(t,e);case"suspend":return this.suspendViewport(t);case"resume":return this.resumeViewport(t);case"enable":return this.enableViewport(t);case"openTreeNode":return this.openTreeNode(t,e);case"closeTreeNode":return this.closeTreeNode(t,e);case"createLink":return this.createLink(t,e);case"removeLink":return this.removeLink(t);case"setColumns":return this.setColumns(t,e);case"setTitle":return this.setTitle(t,e);default:}}else{if(De(e))return this.menuRpcCall(e);{let{type:t,requestId:n}=e;switch(t){case"GET_TABLE_LIST":return this.sendMessageToServer({type:t},n);case"GET_TABLE_META":return this.sendMessageToServer({type:t,table:e.table},n);case"RPC_CALL":return this.rpcCall(e);default:}}}O(\`Vuu ServerProxy Unexpected message from client \${JSON.stringify(e)}\`)}awaitResponseToMessage(e){return new Promise((t,n)=>{let r=C();this.sendMessageToServer(e,r),this.pendingRequests.set(r,{reject:n,resolve:t})})}sendIfReady(e,t,n=!0){return n?this.sendMessageToServer(e,t):this.queuedRequests.push(e),n}sendMessageToServer(e,t=\`\${it++}\`,n=\$t){let{module:r="CORE"}=n;this.authToken&&this.connection.send({requestId:t,sessionId:this.sessionId,token:this.authToken,user:this.user,module:r,body:e})}handleMessageFromServer(e){var u;let{body:t,requestId:n,sessionId:r}=e,o=this.pendingRequests.get(n);if(o){let{resolve:i}=o;this.pendingRequests.delete(n),i(t);return}let{viewports:a}=this;switch(t.type){case ze:this.sendMessageToServer({type:Je,ts:+new Date},"NA");break;case Ze:if(r)this.sessionId=r,(u=this.pendingLogin)==null||u.resolve(r),this.pendingLogin=void 0;else throw Error("LOGIN_SUCCESS did not provide sessionId");break;case qe:{let i=a.get(n);if(i){let{status:l}=i,{viewPortId:f}=t;n!==f&&(a.delete(n),a.set(f,i)),this.mapClientToServerViewport.set(n,f);let R=i.handleSubscribed(t);R&&(this.postMessageToClient(R),L&&I(\`post DataSourceSubscribedMessage to client: \${JSON.stringify(R)}\`)),i.disabled&&this.disableViewport(i),l==="subscribing"&&!G(i.table)&&(this.sendMessageToServer({type:se,vpId:f}),this.sendMessageToServer({type:je,vpId:f}),Array.from(a.entries()).filter(([V,{disabled:A}])=>V!==f&&!A).forEach(([V])=>{this.sendMessageToServer({type:se,vpId:V})}))}}break;case"REMOVE_VP_SUCCESS":{let i=a.get(t.viewPortId);i&&(this.mapClientToServerViewport.delete(i.clientViewportId),a.delete(t.viewPortId),this.removeViewportFromVisualLinks(t.viewPortId))}break;case tt:{let i=this.viewports.get(t.vpId);i&&i.completeOperation(n)}break;case Ue:case Ge:if(a.has(t.viewPortId)){let i=this.viewports.get(t.viewPortId);if(i){let l=i.completeOperation(n);l!==void 0&&(this.postMessageToClient(l),L&&I(\`postMessageToClient \${JSON.stringify(l)}\`))}}break;case He:{let i=this.viewports.get(t.viewPortId);if(i){let l=i.completeOperation(n);if(l){this.postMessageToClient(l);let f=i.currentData();L&&I(\`Enable Response (ServerProxy to Client): \${JSON.stringify(l)}\`),i.size===0?L&&I("Viewport Enabled but size 0, resend to server"):(this.postMessageToClient({clientViewportId:i.clientViewportId,mode:"batch",rows:f,size:i.size,type:"viewport-update"}),L&&I(\`Enable Response (ServerProxy to Client): send size \${i.size} \${f.length} rows from cache\`))}}}break;case nt:{let i=Pe(t.rows);for(let[l,f]of Object.entries(i)){let R=a.get(l);R?R.updateRows(f):k==null||k(\`TABLE_ROW message received for non registered viewport \${l}\`)}this.processUpdates()}break;case Fe:{let i=this.viewports.get(t.viewPortId);if(i){let{from:l,to:f}=t;i.completeOperation(n,l,f)}}break;case Qe:case We:break;case"CREATE_VISUAL_LINK_SUCCESS":{let i=this.viewports.get(t.childVpId),l=this.viewports.get(t.parentVpId);if(i&&l){let{childColumnName:f,parentColumnName:R}=t,V=i.completeOperation(n,f,l.clientViewportId,R);V&&this.postMessageToClient(V)}}break;case"REMOVE_VISUAL_LINK_SUCCESS":{let i=this.viewports.get(t.childVpId);if(i){let l=i.completeOperation(n);l&&this.postMessageToClient(l)}}break;case ae:this.postMessageToClient({type:ae,tables:t.tables,requestId:n});break;case ie:{let i=this.cacheTableMeta(t),l=this.pendingTableMetaRequests.get(n);if(l){this.pendingTableMetaRequests.delete(n);let f=this.viewports.get(l);f?f.setTableSchema(i):k==null||k("Message has come back AFTER CREATE_VP_SUCCESS, what do we do now")}else this.postMessageToClient({type:ie,tableSchema:i,requestId:n})}break;case"VP_VISUAL_LINKS_RESP":{let i=this.getActiveLinks(t.links),l=this.viewports.get(t.vpId);if(i.length&&l){let f=Kt(i,this.viewports),[R,V]=l.setLinks(f);if(this.postMessageToClient(R),V){let{link:A,parentClientVpId:at}=V,de=C(),ge=this.mapClientToServerViewport.get(at);if(ge){let ut=l.createLink(de,A.fromColumn,ge,A.toColumn);this.sendMessageToServer(ut,de)}}}}break;case"VIEW_PORT_MENUS_RESP":if(t.menu.name){let i=this.viewports.get(t.vpId);if(i){let l=i.setMenu(t.menu);this.postMessageToClient(l)}}break;case"VP_EDIT_RPC_RESPONSE":this.postMessageToClient({action:t.action,requestId:n,rpcName:t.rpcName,type:"VP_EDIT_RPC_RESPONSE"});break;case"VP_EDIT_RPC_REJECT":this.viewports.get(t.vpId)&&this.postMessageToClient({requestId:n,type:"VP_EDIT_RPC_REJECT",error:t.error});break;case"VIEW_PORT_MENU_REJ":{console.log("send menu error back to client");let{error:i,rpcName:l}=t;this.postMessageToClient({error:i,rpcName:l,type:"VIEW_PORT_MENU_REJ",requestId:n});break}case"VIEW_PORT_MENU_RESP":if(Ae(t)){let{action:i,rpcName:l}=t;this.awaitResponseToMessage({type:"GET_TABLE_META",table:i.table}).then(f=>{let R=re(f);this.postMessageToClient({rpcName:l,type:"VIEW_PORT_MENU_RESP",action:{...i,tableSchema:R},tableAlreadyOpen:this.isTableOpen(i.table),requestId:n})})}else{let{action:i}=t;this.postMessageToClient({type:"VIEW_PORT_MENU_RESP",action:i||Bt,tableAlreadyOpen:i!==null&&this.isTableOpen(i.table),requestId:n})}break;case oe:{let{method:i,result:l}=t;this.postMessageToClient({type:oe,method:i,result:l,requestId:n})}break;case"ERROR":O(t.msg);break;default:Wt&&S(\`handleMessageFromServer \${t.type}.\`)}}hasSchemaForTable(e){return this.cachedTableSchemas.has(\`\${e.module}:\${e.table}\`)}cacheTableMeta(e){let{module:t,table:n}=e.table,r=\`\${t}:\${n}\`,o=this.cachedTableSchemas.get(r);return o||(o=re(e),this.cachedTableSchemas.set(r,o)),o}isTableOpen(e){if(e){let t=e.table;for(let n of this.viewports.values())if(!n.suspended&&n.table.table===t)return!0}}getActiveLinks(e){return e.filter(t=>{let n=this.viewports.get(t.parentVpId);return n&&!n.suspended})}processUpdates(){this.viewports.forEach(e=>{var t;if(e.hasUpdatesToProcess){let n=e.getClientRows();if(n!==j){let[r,o]=n,a=e.getNewRowCount();(a!==void 0||r&&r.length>0)&&(L&&I(\`postMessageToClient #\${e.clientViewportId} viewport-update \${o}, \${(t=r==null?void 0:r.length)!=null?t:"no"} rows, size \${a}\`),o&&this.postMessageToClient({clientViewportId:e.clientViewportId,mode:o,rows:r,size:a,type:"viewport-update"}))}}})}};var D,{info:ce,infoEnabled:pe}=E("worker");async function Ht(s,e,t,n,r,o,a){let u=await Ie(s,e,c=>{Oe(c)?(console.log("post connection metrics"),postMessage({type:"connection-metrics",messages:c})):Le(c)?(r(c),c.status==="reconnected"&&D.reconnect()):D.handleMessageFromServer(c)},o,a);D=new J(u,c=>jt(c)),u.requiresLogin&&await D.login(t,n)}function jt(s){postMessage(s)}var zt=async({data:s})=>{switch(s.type){case"connect":await Ht(s.url,s.protocol,s.token,s.username,postMessage,s.retryLimitDisconnect,s.retryLimitStartup),postMessage({type:"connected"});break;case"subscribe":pe&&ce(\`client subscribe: \${JSON.stringify(s)}\`),D.subscribe(s);break;case"unsubscribe":pe&&ce(\`client unsubscribe: \${JSON.stringify(s)}\`),D.unsubscribe(s.viewport);break;default:pe&&ce(\`client message: \${JSON.stringify(s)}\`),D.handleMessageFromClient(s)}};self.addEventListener("message",zt);postMessage({type:"ready"}); + \`); + throw Error(\`KeySet, no key found for rowIndex \${rowIndex}\`); + } + return key; + } + toDebugString() { + return Array.from(this.keys.entries()).map((k, v) => \`\${k}=>\${v}\`).join(","); + } +}; + +// ../vuu-utils/src/row-utils.ts +var { IDX } = metadataKeys; + +// ../vuu-utils/src/selection-utils.ts +var { SELECTED } = metadataKeys; +var RowSelected = { + False: 0, + True: 1, + First: 2, + Last: 4 +}; +var rangeIncludes = (range, index) => index >= range[0] && index <= range[1]; +var SINGLE_SELECTED_ROW = RowSelected.True + RowSelected.First + RowSelected.Last; +var FIRST_SELECTED_ROW_OF_BLOCK = RowSelected.True + RowSelected.First; +var LAST_SELECTED_ROW_OF_BLOCK = RowSelected.True + RowSelected.Last; +var getSelectionStatus = (selected, itemIndex) => { + for (const item of selected) { + if (typeof item === "number") { + if (item === itemIndex) { + return SINGLE_SELECTED_ROW; + } + } else if (rangeIncludes(item, itemIndex)) { + if (itemIndex === item[0]) { + return FIRST_SELECTED_ROW_OF_BLOCK; + } else if (itemIndex === item[1]) { + return LAST_SELECTED_ROW_OF_BLOCK; + } else { + return RowSelected.True; + } + } + } + return RowSelected.False; +}; +var expandSelection = (selected) => { + if (selected.every((selectedItem) => typeof selectedItem === "number")) { + return selected; + } + const expandedSelected = []; + for (const selectedItem of selected) { + if (typeof selectedItem === "number") { + expandedSelected.push(selectedItem); + } else { + for (let i = selectedItem[0]; i <= selectedItem[1]; i++) { + expandedSelected.push(i); + } + } + } + return expandedSelected; +}; + +// ../../node_modules/html-to-image/es/util.js +var uuid = (() => { + let counter = 0; + const random = () => ( + // eslint-disable-next-line no-bitwise + \`0000\${(Math.random() * 36 ** 4 << 0).toString(36)}\`.slice(-4) + ); + return () => { + counter += 1; + return \`u\${random()}\${counter}\`; + }; +})(); + +// src/websocket-connection.ts +var { debug: debug2, debugEnabled: debugEnabled2, error, info, infoEnabled, warn } = logger( + "websocket-connection" +); +var WS = "ws"; +var isWebsocketUrl = (url) => url.startsWith(WS + "://") || url.startsWith(WS + "s://"); +var connectionAttemptStatus = {}; +var setWebsocket = Symbol("setWebsocket"); +var connectionCallback = Symbol("connectionCallback"); +async function connect(connectionString, protocol, callback, retryLimitDisconnect = 10, retryLimitStartup = 5) { + connectionAttemptStatus[connectionString] = { + status: "connecting", + connect: { + allowed: retryLimitStartup, + remaining: retryLimitStartup + }, + reconnect: { + allowed: retryLimitDisconnect, + remaining: retryLimitDisconnect + } + }; + return makeConnection(connectionString, protocol, callback); +} +async function reconnect(connection) { + throw Error("connection broken"); +} +async function makeConnection(url, protocol, callback, connection) { + const { + status: currentStatus, + connect: connectStatus, + reconnect: reconnectStatus + } = connectionAttemptStatus[url]; + const trackedStatus = currentStatus === "connecting" ? connectStatus : reconnectStatus; + try { + callback({ type: "connection-status", status: "connecting" }); + const reconnecting = typeof connection !== "undefined"; + const ws = await createWebsocket(url, protocol); + console.info( + "%c\u26A1 %cconnected", + "font-size: 24px;color: green;font-weight: bold;", + "color:green; font-size: 14px;" + ); + if (connection !== void 0) { + connection[setWebsocket](ws); + } + const websocketConnection = connection != null ? connection : new WebsocketConnection(ws, url, protocol, callback); + const status = reconnecting ? "reconnected" : "connection-open-awaiting-session"; + callback({ type: "connection-status", status }); + websocketConnection.status = status; + trackedStatus.remaining = trackedStatus.allowed; + return websocketConnection; + } catch (err) { + const retry = --trackedStatus.remaining > 0; + callback({ + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry + }); + if (retry) { + return makeConnectionIn(url, protocol, callback, connection, 2e3); + } else { + throw Error("Failed to establish connection"); + } + } +} +var makeConnectionIn = (url, protocol, callback, connection, delay) => new Promise((resolve) => { + setTimeout(() => { + resolve(makeConnection(url, protocol, callback, connection)); + }, delay); +}); +var createWebsocket = (connectionString, protocol) => new Promise((resolve, reject) => { + const websocketUrl = isWebsocketUrl(connectionString) ? connectionString : \`wss://\${connectionString}\`; + if (infoEnabled && protocol !== void 0) { + info(\`WebSocket Protocol \${protocol == null ? void 0 : protocol.toString()}\`); + } + const ws = new WebSocket(websocketUrl, protocol); + ws.onopen = () => resolve(ws); + ws.onerror = (evt) => reject(evt); +}); +var closeWarn = () => { + warn == null ? void 0 : warn(\`Connection cannot be closed, socket not yet opened\`); +}; +var sendWarn = (msg) => { + warn == null ? void 0 : warn(\`Message cannot be sent, socket closed \${msg.body.type}\`); +}; +var parseMessage = (message) => { + try { + return JSON.parse(message); + } catch (e) { + throw Error(\`Error parsing JSON response from server \${message}\`); + } +}; +var WebsocketConnection = class { + constructor(ws, url, protocol, callback) { + this.close = closeWarn; + this.requiresLogin = true; + this.send = sendWarn; + this.status = "ready"; + this.messagesCount = 0; + this.connectionMetricsInterval = null; + this.handleWebsocketMessage = (evt) => { + const vuuMessageFromServer = parseMessage(evt.data); + this.messagesCount += 1; + if (true) { + if (debugEnabled2 && vuuMessageFromServer.body.type !== "HB") { + debug2 == null ? void 0 : debug2(\`<<< \${vuuMessageFromServer.body.type}\`); + } + } + this[connectionCallback](vuuMessageFromServer); + }; + this.url = url; + this.protocol = protocol; + this[connectionCallback] = callback; + this[setWebsocket](ws); + } + reconnect() { + reconnect(this); + } + [(connectionCallback, setWebsocket)](ws) { + const callback = this[connectionCallback]; + ws.onmessage = (evt) => { + this.status = "connected"; + ws.onmessage = this.handleWebsocketMessage; + this.handleWebsocketMessage(evt); + }; + this.connectionMetricsInterval = setInterval(() => { + callback({ + type: "connection-metrics", + messagesLength: this.messagesCount + }); + this.messagesCount = 0; + }, 2e3); + ws.onerror = () => { + error(\`\u26A1 connection error\`); + callback({ + type: "connection-status", + status: "disconnected", + reason: "error" + }); + if (this.connectionMetricsInterval) { + clearInterval(this.connectionMetricsInterval); + this.connectionMetricsInterval = null; + } + if (this.status === "connection-open-awaiting-session") { + error( + \`Websocket connection lost before Vuu session established, check websocket configuration\` + ); + } else if (this.status !== "closed") { + reconnect(this); + this.send = queue; + } + }; + ws.onclose = () => { + info == null ? void 0 : info(\`\u26A1 connection close\`); + callback({ + type: "connection-status", + status: "disconnected", + reason: "close" + }); + if (this.connectionMetricsInterval) { + clearInterval(this.connectionMetricsInterval); + this.connectionMetricsInterval = null; + } + if (this.status !== "closed") { + reconnect(this); + this.send = queue; + } + }; + const send = (msg) => { + if (true) { + if (debugEnabled2 && msg.body.type !== "HB_RESP") { + debug2 == null ? void 0 : debug2(\`>>> \${msg.body.type}\`); + } + } + ws.send(JSON.stringify(msg)); + }; + const queue = (msg) => { + info == null ? void 0 : info(\`TODO queue message until websocket reconnected \${msg.body.type}\`); + }; + this.send = send; + this.close = () => { + this.status = "closed"; + ws.close(); + this.close = closeWarn; + this.send = sendWarn; + info == null ? void 0 : info("close websocket"); + }; + } +}; + +// src/message-utils.ts +var MENU_RPC_TYPES = [ + "VIEW_PORT_MENUS_SELECT_RPC", + "VIEW_PORT_MENU_TABLE_RPC", + "VIEW_PORT_MENU_ROW_RPC", + "VIEW_PORT_MENU_CELL_RPC", + "VP_EDIT_CELL_RPC", + "VP_EDIT_ROW_RPC", + "VP_EDIT_ADD_ROW_RPC", + "VP_EDIT_DELETE_CELL_RPC", + "VP_EDIT_DELETE_ROW_RPC", + "VP_EDIT_SUBMIT_FORM_RPC" +]; +var isVuuMenuRpcRequest = (message) => MENU_RPC_TYPES.includes(message["type"]); +var stripRequestId = ({ + requestId, + ...rest +}) => [requestId, rest]; +var getFirstAndLastRows = (rows) => { + let firstRow = rows.at(0); + if (firstRow.updateType === "SIZE") { + if (rows.length === 1) { + return rows; + } else { + firstRow = rows.at(1); + } + } + const lastRow = rows.at(-1); + return [firstRow, lastRow]; +}; +var groupRowsByViewport = (rows) => { + const result = {}; + for (const row of rows) { + const rowsForViewport = result[row.viewPortId] || (result[row.viewPortId] = []); + rowsForViewport.push(row); + } + return result; +}; +var createSchemaFromTableMetadata = ({ + columns, + dataTypes, + key, + table +}) => { + return { + table, + columns: columns.map((col, idx) => ({ + name: col, + serverDataType: dataTypes[idx] + })), + key + }; +}; + +// src/vuuUIMessageTypes.ts +var isConnectionStatusMessage = (msg) => msg.type === "connection-status"; +var isConnectionQualityMetrics = (msg) => msg.type === "connection-metrics"; +var isViewporttMessage = (msg) => "viewport" in msg; +var isSessionTableActionMessage = (messageBody) => messageBody.type === "VIEW_PORT_MENU_RESP" && messageBody.action !== null && isSessionTable(messageBody.action.table); +var isSessionTable = (table) => { + if (table !== null && typeof table === "object" && "table" in table && "module" in table) { + return table.table.startsWith("session"); + } + return false; +}; + +// src/server-proxy/messages.ts +var CHANGE_VP_SUCCESS = "CHANGE_VP_SUCCESS"; +var CHANGE_VP_RANGE_SUCCESS = "CHANGE_VP_RANGE_SUCCESS"; +var CLOSE_TREE_NODE = "CLOSE_TREE_NODE"; +var CLOSE_TREE_SUCCESS = "CLOSE_TREE_SUCCESS"; +var CREATE_VP = "CREATE_VP"; +var CREATE_VP_SUCCESS = "CREATE_VP_SUCCESS"; +var DISABLE_VP = "DISABLE_VP"; +var DISABLE_VP_SUCCESS = "DISABLE_VP_SUCCESS"; +var ENABLE_VP = "ENABLE_VP"; +var ENABLE_VP_SUCCESS = "ENABLE_VP_SUCCESS"; +var GET_VP_VISUAL_LINKS = "GET_VP_VISUAL_LINKS"; +var GET_VIEW_PORT_MENUS = "GET_VIEW_PORT_MENUS"; +var HB = "HB"; +var HB_RESP = "HB_RESP"; +var LOGIN = "LOGIN"; +var LOGIN_SUCCESS = "LOGIN_SUCCESS"; +var OPEN_TREE_NODE = "OPEN_TREE_NODE"; +var OPEN_TREE_SUCCESS = "OPEN_TREE_SUCCESS"; +var REMOVE_VP = "REMOVE_VP"; +var RPC_RESP = "RPC_RESP"; +var SET_SELECTION_SUCCESS = "SET_SELECTION_SUCCESS"; +var TABLE_META_RESP = "TABLE_META_RESP"; +var TABLE_LIST_RESP = "TABLE_LIST_RESP"; +var TABLE_ROW = "TABLE_ROW"; + +// src/server-proxy/rpc-services.ts +var getRpcServiceModule = (service) => { + switch (service) { + case "TypeAheadRpcHandler": + return "TYPEAHEAD"; + default: + return "SIMUL"; + } +}; + +// src/server-proxy/array-backed-moving-window.ts +var EMPTY_ARRAY = []; +var log = logger("array-backed-moving-window"); +function dataIsUnchanged(newRow, existingRow) { + if (!existingRow) { + return false; + } + if (existingRow.data.length !== newRow.data.length) { + return false; + } + if (existingRow.sel !== newRow.sel) { + return false; + } + for (let i = 0; i < existingRow.data.length; i++) { + if (existingRow.data[i] !== newRow.data[i]) { + return false; + } + } + return true; +} +var _range; +var ArrayBackedMovingWindow = class { + // Note, the buffer is already accounted for in the range passed in here + constructor({ from: clientFrom, to: clientTo }, { from, to }, bufferSize) { + __privateAdd(this, _range, void 0); + this.setRowCount = (rowCount) => { + var _a; + (_a = log.info) == null ? void 0 : _a.call(log, \`setRowCount \${rowCount}\`); + if (rowCount < this.internalData.length) { + this.internalData.length = rowCount; + } + if (rowCount < this.rowCount) { + this.rowsWithinRange = 0; + const end = Math.min(rowCount, this.clientRange.to); + for (let i = this.clientRange.from; i < end; i++) { + const rowIndex = i - __privateGet(this, _range).from; + if (this.internalData[rowIndex] !== void 0) { + this.rowsWithinRange += 1; + } + } + } + this.rowCount = rowCount; + }; + this.bufferBreakout = (from, to) => { + const bufferPerimeter = this.bufferSize * 0.25; + if (__privateGet(this, _range).to - to < bufferPerimeter) { + return true; + } else if (__privateGet(this, _range).from > 0 && from - __privateGet(this, _range).from < bufferPerimeter) { + return true; + } else { + return false; + } + }; + this.bufferSize = bufferSize; + this.clientRange = new WindowRange(clientFrom, clientTo); + __privateSet(this, _range, new WindowRange(from, to)); + this.internalData = new Array(bufferSize); + this.rowsWithinRange = 0; + this.rowCount = 0; + } + get range() { + return __privateGet(this, _range); + } + // TODO we shpuld probably have a hasAllClientRowsWithinRange + get hasAllRowsWithinRange() { + return this.rowsWithinRange === this.clientRange.to - this.clientRange.from || // this.rowsWithinRange === this.range.to - this.range.from || + this.rowCount > 0 && this.clientRange.from + this.rowsWithinRange === this.rowCount; + } + // Check to see if set of rows is outside the current viewport range, indicating + // that veiwport is being scrolled quickly and server is not able to keep up. + outOfRange(firstIndex, lastIndex) { + const { from, to } = this.range; + if (lastIndex < from) { + return true; + } + if (firstIndex >= to) { + return true; + } + } + setAtIndex(row) { + const { rowIndex: index } = row; + const internalIndex = index - __privateGet(this, _range).from; + if (dataIsUnchanged(row, this.internalData[internalIndex])) { + return false; + } + const isWithinClientRange = this.isWithinClientRange(index); + if (isWithinClientRange || this.isWithinRange(index)) { + if (!this.internalData[internalIndex] && isWithinClientRange) { + this.rowsWithinRange += 1; + } + this.internalData[internalIndex] = row; + } + return isWithinClientRange; + } + getAtIndex(index) { + return __privateGet(this, _range).isWithin(index) && this.internalData[index - __privateGet(this, _range).from] != null ? this.internalData[index - __privateGet(this, _range).from] : void 0; + } + isWithinRange(index) { + return __privateGet(this, _range).isWithin(index); + } + isWithinClientRange(index) { + return this.clientRange.isWithin(index); + } + // Returns [false] or [serverDataRequired, clientRows, holdingRows] + setClientRange(from, to) { + var _a; + (_a = log.debug) == null ? void 0 : _a.call(log, \`setClientRange \${from} - \${to}\`); + const currentFrom = this.clientRange.from; + const currentTo = Math.min(this.clientRange.to, this.rowCount); + if (from === currentFrom && to === currentTo) { + return [ + false, + EMPTY_ARRAY + /*, EMPTY_ARRAY*/ + ]; + } + const originalRange = this.clientRange.copy(); + this.clientRange.from = from; + this.clientRange.to = to; + this.rowsWithinRange = 0; + for (let i = from; i < to; i++) { + const internalIndex = i - __privateGet(this, _range).from; + if (this.internalData[internalIndex]) { + this.rowsWithinRange += 1; + } + } + let clientRows = EMPTY_ARRAY; + const offset = __privateGet(this, _range).from; + if (this.hasAllRowsWithinRange) { + if (to > originalRange.to) { + const start = Math.max(from, originalRange.to); + clientRows = this.internalData.slice(start - offset, to - offset); + } else { + const end = Math.min(originalRange.from, to); + clientRows = this.internalData.slice(from - offset, end - offset); + } + } + const serverDataRequired = this.bufferBreakout(from, to); + return [serverDataRequired, clientRows]; + } + setRange(from, to) { + var _a, _b; + if (from !== __privateGet(this, _range).from || to !== __privateGet(this, _range).to) { + (_a = log.debug) == null ? void 0 : _a.call(log, \`setRange \${from} - \${to}\`); + const [overlapFrom, overlapTo] = __privateGet(this, _range).overlap(from, to); + const newData = new Array(to - from); + this.rowsWithinRange = 0; + for (let i = overlapFrom; i < overlapTo; i++) { + const data = this.getAtIndex(i); + if (data) { + const index = i - from; + newData[index] = data; + if (this.isWithinClientRange(i)) { + this.rowsWithinRange += 1; + } + } + } + this.internalData = newData; + __privateGet(this, _range).from = from; + __privateGet(this, _range).to = to; + } else { + (_b = log.debug) == null ? void 0 : _b.call(log, \`setRange \${from} - \${to} IGNORED because not changed\`); + } + } + //TODO temp + get data() { + return this.internalData; + } + getData() { + var _a; + const { from, to } = __privateGet(this, _range); + const { from: clientFrom, to: clientTo } = this.clientRange; + const startOffset = Math.max(0, clientFrom - from); + const endOffset = Math.min( + to - from, + to, + clientTo - from, + (_a = this.rowCount) != null ? _a : to + ); + return this.internalData.slice(startOffset, endOffset); + } + clear() { + var _a; + (_a = log.debug) == null ? void 0 : _a.call(log, "clear"); + this.internalData.length = 0; + this.rowsWithinRange = 0; + this.setRowCount(0); + } + // used only for debugging + getCurrentDataRange() { + const rows = this.internalData; + const len = rows.length; + let [firstRow] = this.internalData; + let lastRow = this.internalData[len - 1]; + if (firstRow && lastRow) { + return [firstRow.rowIndex, lastRow.rowIndex]; + } else { + for (let i = 0; i < len; i++) { + if (rows[i] !== void 0) { + firstRow = rows[i]; + break; + } + } + for (let i = len - 1; i >= 0; i--) { + if (rows[i] !== void 0) { + lastRow = rows[i]; + break; + } + } + if (firstRow && lastRow) { + return [firstRow.rowIndex, lastRow.rowIndex]; + } else { + return [-1, -1]; + } + } + } +}; +_range = new WeakMap(); + +// src/server-proxy/viewport.ts +var EMPTY_GROUPBY = []; +var { debug: debug3, debugEnabled: debugEnabled3, error: error2, info: info2, infoEnabled: infoEnabled2, warn: warn2 } = logger("viewport"); +var isLeafUpdate = ({ rowKey, updateType }) => updateType === "U" && !rowKey.startsWith("\$root"); +var NO_DATA_UPDATE = [ + void 0, + void 0 +]; +var NO_UPDATE_STATUS = { + count: 0, + mode: void 0, + size: 0, + ts: 0 +}; +var Viewport = class { + constructor({ + aggregations, + bufferSize = 50, + columns, + filter, + groupBy = [], + table, + range, + sort, + title, + viewport, + visualLink + }, postMessageToClient) { + /** batchMode is irrelevant for Vuu Table, it was introduced to try and improve rendering performance of AgGrid */ + this.batchMode = true; + this.hasUpdates = false; + this.pendingUpdates = []; + this.pendingOperations = /* @__PURE__ */ new Map(); + this.pendingRangeRequests = []; + this.rowCountChanged = false; + this.selectedRows = []; + this.tableSchema = null; + this.useBatchMode = true; + this.lastUpdateStatus = NO_UPDATE_STATUS; + this.updateThrottleTimer = void 0; + this.rangeMonitor = new RangeMonitor("ViewPort"); + this.disabled = false; + this.isTree = false; + // TODO roll disabled/suspended into status + this.status = ""; + this.suspended = false; + this.suspendTimer = null; + // Records SIZE only updates + this.setLastSizeOnlyUpdateSize = (size) => { + this.lastUpdateStatus.size = size; + }; + this.setLastUpdate = (mode) => { + const { ts: lastTS, mode: lastMode } = this.lastUpdateStatus; + let elapsedTime = 0; + if (lastMode === mode) { + const ts = Date.now(); + this.lastUpdateStatus.count += 1; + this.lastUpdateStatus.ts = ts; + elapsedTime = lastTS === 0 ? 0 : ts - lastTS; + } else { + this.lastUpdateStatus.count = 1; + this.lastUpdateStatus.ts = 0; + elapsedTime = 0; + } + this.lastUpdateStatus.mode = mode; + return elapsedTime; + }; + this.rangeRequestAlreadyPending = (range) => { + const { bufferSize } = this; + const bufferThreshold = bufferSize * 0.25; + let { from: stillPendingFrom } = range; + for (const { from, to } of this.pendingRangeRequests) { + if (stillPendingFrom >= from && stillPendingFrom < to) { + if (range.to + bufferThreshold <= to) { + return true; + } else { + stillPendingFrom = to; + } + } + } + return false; + }; + this.sendThrottledSizeMessage = () => { + this.updateThrottleTimer = void 0; + this.lastUpdateStatus.count = 3; + this.postMessageToClient({ + clientViewportId: this.clientViewportId, + mode: "size-only", + size: this.lastUpdateStatus.size, + type: "viewport-update" + }); + }; + // If we are receiving multiple SIZE updates but no data, table is loading rows + // outside of our viewport. We can safely throttle these requests. Doing so will + // alleviate pressure on UI DataTable. + this.shouldThrottleMessage = (mode) => { + const elapsedTime = this.setLastUpdate(mode); + return mode === "size-only" && elapsedTime > 0 && elapsedTime < 500 && this.lastUpdateStatus.count > 3; + }; + this.throttleMessage = (mode) => { + if (this.shouldThrottleMessage(mode)) { + info2 == null ? void 0 : info2("throttling updates setTimeout to 2000"); + if (this.updateThrottleTimer === void 0) { + this.updateThrottleTimer = setTimeout( + this.sendThrottledSizeMessage, + 2e3 + ); + } + return true; + } else if (this.updateThrottleTimer !== void 0) { + clearTimeout(this.updateThrottleTimer); + this.updateThrottleTimer = void 0; + } + return false; + }; + this.getNewRowCount = () => { + if (this.rowCountChanged && this.dataWindow) { + this.rowCountChanged = false; + return this.dataWindow.rowCount; + } + }; + this.aggregations = aggregations; + this.bufferSize = bufferSize; + this.clientRange = range; + this.clientViewportId = viewport; + this.columns = columns; + this.filter = filter; + this.groupBy = groupBy; + this.keys = new KeySet(range); + this.pendingLinkedParent = visualLink; + this.table = table; + this.sort = sort; + this.title = title; + infoEnabled2 && (info2 == null ? void 0 : info2( + \`constructor #\${viewport} \${table.table} bufferSize=\${bufferSize}\` + )); + this.dataWindow = new ArrayBackedMovingWindow( + this.clientRange, + range, + this.bufferSize + ); + this.postMessageToClient = postMessageToClient; + } + get hasUpdatesToProcess() { + if (this.suspended) { + return false; + } + return this.rowCountChanged || this.hasUpdates; + } + get size() { + var _a; + return (_a = this.dataWindow.rowCount) != null ? _a : 0; + } + subscribe() { + const { filter } = this.filter; + this.status = this.status === "subscribed" ? "resubscribing" : "subscribing"; + return { + type: CREATE_VP, + table: this.table, + range: getFullRange(this.clientRange, this.bufferSize), + aggregations: this.aggregations, + columns: this.columns, + sort: this.sort, + groupBy: this.groupBy, + filterSpec: { filter } + }; + } + handleSubscribed({ + viewPortId, + aggregations, + columns, + filterSpec: filter, + range, + sort, + groupBy + }) { + this.serverViewportId = viewPortId; + this.status = "subscribed"; + this.aggregations = aggregations; + this.columns = columns; + this.groupBy = groupBy; + this.isTree = groupBy && groupBy.length > 0; + this.dataWindow.setRange(range.from, range.to); + return { + aggregations, + type: "subscribed", + clientViewportId: this.clientViewportId, + columns, + filter, + groupBy, + range, + sort, + tableSchema: this.tableSchema + }; + } + awaitOperation(requestId, msg) { + this.pendingOperations.set(requestId, msg); + } + // Return a message if we need to communicate this to client UI + completeOperation(requestId, ...params) { + var _a; + const { clientViewportId, pendingOperations } = this; + const pendingOperation = pendingOperations.get(requestId); + if (!pendingOperation) { + error2("no matching operation found to complete"); + return; + } + const { type } = pendingOperation; + info2 == null ? void 0 : info2(\`completeOperation \${type}\`); + pendingOperations.delete(requestId); + if (type === "CHANGE_VP_RANGE") { + const [from, to] = params; + (_a = this.dataWindow) == null ? void 0 : _a.setRange(from, to); + for (let i = this.pendingRangeRequests.length - 1; i >= 0; i--) { + const pendingRangeRequest = this.pendingRangeRequests[i]; + if (pendingRangeRequest.requestId === requestId) { + pendingRangeRequest.acked = true; + break; + } else { + warn2 == null ? void 0 : warn2("range requests sent faster than they are being ACKed"); + } + } + } else if (type === "config") { + const { aggregations, columns, filter, groupBy, sort } = pendingOperation.data; + this.aggregations = aggregations; + this.columns = columns; + this.filter = filter; + this.groupBy = groupBy; + this.sort = sort; + if (groupBy.length > 0) { + this.isTree = true; + } else if (this.isTree) { + this.isTree = false; + } + debug3 == null ? void 0 : debug3(\`config change confirmed, isTree : \${this.isTree}\`); + return { + clientViewportId, + type, + config: pendingOperation.data + }; + } else if (type === "groupBy") { + this.isTree = pendingOperation.data.length > 0; + this.groupBy = pendingOperation.data; + debug3 == null ? void 0 : debug3(\`groupBy change confirmed, isTree : \${this.isTree}\`); + return { + clientViewportId, + type, + groupBy: pendingOperation.data + }; + } else if (type === "columns") { + this.columns = pendingOperation.data; + return { + clientViewportId, + type, + columns: pendingOperation.data + }; + } else if (type === "filter") { + this.filter = pendingOperation.data; + return { + clientViewportId, + type, + filter: pendingOperation.data + }; + } else if (type === "aggregate") { + this.aggregations = pendingOperation.data; + return { + clientViewportId, + type: "aggregate", + aggregations: this.aggregations + }; + } else if (type === "sort") { + this.sort = pendingOperation.data; + return { + clientViewportId, + type, + sort: this.sort + }; + } else if (type === "selection") { + } else if (type === "disable") { + this.disabled = true; + return { + type: "disabled", + clientViewportId + }; + } else if (type === "enable") { + this.disabled = false; + return { + type: "enabled", + clientViewportId + }; + } else if (type === "CREATE_VISUAL_LINK") { + const [colName, parentViewportId, parentColName] = params; + this.linkedParent = { + colName, + parentViewportId, + parentColName + }; + this.pendingLinkedParent = void 0; + return { + type: "vuu-link-created", + clientViewportId, + colName, + parentViewportId, + parentColName + }; + } else if (type === "REMOVE_VISUAL_LINK") { + this.linkedParent = void 0; + return { + type: "vuu-link-removed", + clientViewportId + }; + } + } + // TODO when a range request arrives, consider the viewport to be scrolling + // until data arrives and we have the full range. + // When not scrolling, any server data is an update + // When scrolling, we are in batch mode + rangeRequest(requestId, range) { + if (debugEnabled3) { + this.rangeMonitor.set(range); + } + const type = "CHANGE_VP_RANGE"; + if (this.dataWindow) { + const [serverDataRequired, clientRows] = this.dataWindow.setClientRange( + range.from, + range.to + ); + let debounceRequest; + const maxRange = this.dataWindow.rowCount || void 0; + const serverRequest = serverDataRequired && !this.rangeRequestAlreadyPending(range) ? { + type, + viewPortId: this.serverViewportId, + ...getFullRange(range, this.bufferSize, maxRange) + } : null; + if (serverRequest) { + debugEnabled3 && (debug3 == null ? void 0 : debug3( + \`create CHANGE_VP_RANGE: [\${serverRequest.from} - \${serverRequest.to}]\` + )); + this.awaitOperation(requestId, { type }); + const pendingRequest = this.pendingRangeRequests.at(-1); + if (pendingRequest) { + if (pendingRequest.acked) { + console.warn("Range Request before previous request is filled"); + } else { + const { from, to } = pendingRequest; + if (this.dataWindow.outOfRange(from, to)) { + debounceRequest = { + clientViewportId: this.clientViewportId, + type: "debounce-begin" + }; + } else { + warn2 == null ? void 0 : warn2("Range Request before previous request is acked"); + } + } + } + this.pendingRangeRequests.push({ ...serverRequest, requestId }); + if (this.useBatchMode) { + this.batchMode = true; + } + } else if (clientRows.length > 0) { + this.batchMode = false; + } + this.keys.reset(this.dataWindow.clientRange); + const toClient = this.isTree ? toClientRowTree : toClientRow; + if (clientRows.length) { + return [ + serverRequest, + clientRows.map((row) => { + return toClient(row, this.keys, this.selectedRows); + }) + ]; + } else if (debounceRequest) { + return [serverRequest, void 0, debounceRequest]; + } else { + return [serverRequest]; + } + } else { + return [null]; + } + } + setLinks(links) { + this.links = links; + return [ + { + type: "vuu-links", + links, + clientViewportId: this.clientViewportId + }, + this.pendingLinkedParent + ]; + } + setMenu(menu) { + return { + type: "vuu-menu", + menu, + clientViewportId: this.clientViewportId + }; + } + setTableSchema(tableSchema) { + this.tableSchema = tableSchema; + } + openTreeNode(requestId, message) { + if (this.useBatchMode) { + this.batchMode = true; + } + return { + type: OPEN_TREE_NODE, + vpId: this.serverViewportId, + treeKey: message.key + }; + } + closeTreeNode(requestId, message) { + if (this.useBatchMode) { + this.batchMode = true; + } + return { + type: CLOSE_TREE_NODE, + vpId: this.serverViewportId, + treeKey: message.key + }; + } + createLink(requestId, colName, parentVpId, parentColumnName) { + const message = { + type: "CREATE_VISUAL_LINK", + parentVpId, + childVpId: this.serverViewportId, + parentColumnName, + childColumnName: colName + }; + this.awaitOperation(requestId, message); + if (this.useBatchMode) { + this.batchMode = true; + } + return message; + } + removeLink(requestId) { + const message = { + type: "REMOVE_VISUAL_LINK", + childVpId: this.serverViewportId + }; + this.awaitOperation(requestId, message); + return message; + } + suspend() { + this.suspended = true; + info2 == null ? void 0 : info2("suspend"); + } + resume() { + this.suspended = false; + if (debugEnabled3) { + debug3 == null ? void 0 : debug3(\`resume: \${this.currentData()}\`); + } + return this.currentData(); + } + currentData() { + const out = []; + if (this.dataWindow) { + const records = this.dataWindow.getData(); + const { keys } = this; + const toClient = this.isTree ? toClientRowTree : toClientRow; + for (const row of records) { + if (row) { + out.push(toClient(row, keys, this.selectedRows)); + } + } + } + return out; + } + enable(requestId) { + this.awaitOperation(requestId, { type: "enable" }); + info2 == null ? void 0 : info2(\`enable: \${this.serverViewportId}\`); + return { + type: ENABLE_VP, + viewPortId: this.serverViewportId + }; + } + disable(requestId) { + this.awaitOperation(requestId, { type: "disable" }); + info2 == null ? void 0 : info2(\`disable: \${this.serverViewportId}\`); + this.suspended = false; + return { + type: DISABLE_VP, + viewPortId: this.serverViewportId + }; + } + columnRequest(requestId, columns) { + this.awaitOperation(requestId, { + type: "columns", + data: columns + }); + debug3 == null ? void 0 : debug3(\`columnRequest: \${columns}\`); + return this.createRequest({ columns }); + } + filterRequest(requestId, dataSourceFilter) { + this.awaitOperation(requestId, { + type: "filter", + data: dataSourceFilter + }); + if (this.useBatchMode) { + this.batchMode = true; + } + const { filter } = dataSourceFilter; + info2 == null ? void 0 : info2(\`filterRequest: \${filter}\`); + return this.createRequest({ filterSpec: { filter } }); + } + setConfig(requestId, config) { + this.awaitOperation(requestId, { type: "config", data: config }); + const { filter, ...remainingConfig } = config; + if (this.useBatchMode) { + this.batchMode = true; + } + debugEnabled3 ? debug3 == null ? void 0 : debug3(\`setConfig \${JSON.stringify(config)}\`) : info2 == null ? void 0 : info2(\`setConfig\`); + return this.createRequest( + { + ...remainingConfig, + filterSpec: typeof (filter == null ? void 0 : filter.filter) === "string" ? { + filter: filter.filter + } : { + filter: "" + } + }, + true + ); + } + aggregateRequest(requestId, aggregations) { + this.awaitOperation(requestId, { type: "aggregate", data: aggregations }); + info2 == null ? void 0 : info2(\`aggregateRequest: \${aggregations}\`); + return this.createRequest({ aggregations }); + } + sortRequest(requestId, sort) { + this.awaitOperation(requestId, { type: "sort", data: sort }); + info2 == null ? void 0 : info2(\`sortRequest: \${JSON.stringify(sort.sortDefs)}\`); + return this.createRequest({ sort }); + } + groupByRequest(requestId, groupBy = EMPTY_GROUPBY) { + var _a; + this.awaitOperation(requestId, { type: "groupBy", data: groupBy }); + if (this.useBatchMode) { + this.batchMode = true; + } + if (!this.isTree) { + (_a = this.dataWindow) == null ? void 0 : _a.clear(); + } + return this.createRequest({ groupBy }); + } + selectRequest(requestId, selected) { + this.selectedRows = selected; + this.awaitOperation(requestId, { type: "selection", data: selected }); + info2 == null ? void 0 : info2(\`selectRequest: \${selected}\`); + return { + type: "SET_SELECTION", + vpId: this.serverViewportId, + selection: expandSelection(selected) + }; + } + removePendingRangeRequest(firstIndex, lastIndex) { + for (let i = this.pendingRangeRequests.length - 1; i >= 0; i--) { + const { from, to } = this.pendingRangeRequests[i]; + let isLast = true; + if (firstIndex >= from && firstIndex < to || lastIndex > from && lastIndex < to) { + if (!isLast) { + console.warn( + "removePendingRangeRequest TABLE_ROWS are not for latest request" + ); + } + this.pendingRangeRequests.splice(i, 1); + break; + } else { + isLast = false; + } + } + } + updateRows(rows) { + var _a, _b, _c; + const [firstRow, lastRow] = getFirstAndLastRows(rows); + if (firstRow && lastRow) { + this.removePendingRangeRequest(firstRow.rowIndex, lastRow.rowIndex); + } + if (rows.length === 1) { + if (firstRow.vpSize === 0 && this.disabled) { + debug3 == null ? void 0 : debug3( + \`ignore a SIZE=0 message on disabled viewport (\${rows.length} rows)\` + ); + return; + } else if (firstRow.updateType === "SIZE") { + this.setLastSizeOnlyUpdateSize(firstRow.vpSize); + } + } + for (const row of rows) { + if (this.isTree && isLeafUpdate(row)) { + continue; + } else { + if (row.updateType === "SIZE" || ((_a = this.dataWindow) == null ? void 0 : _a.rowCount) !== row.vpSize) { + (_b = this.dataWindow) == null ? void 0 : _b.setRowCount(row.vpSize); + this.rowCountChanged = true; + } + if (row.updateType === "U") { + if ((_c = this.dataWindow) == null ? void 0 : _c.setAtIndex(row)) { + this.hasUpdates = true; + if (!this.batchMode) { + this.pendingUpdates.push(row); + } + } + } + } + } + } + // This is called only after new data has been received from server - data + // returned direcly from buffer does not use this. + getClientRows() { + let out = void 0; + let mode = "size-only"; + if (!this.hasUpdates && !this.rowCountChanged) { + return NO_DATA_UPDATE; + } + if (this.hasUpdates) { + const { keys, selectedRows } = this; + const toClient = this.isTree ? toClientRowTree : toClientRow; + if (this.updateThrottleTimer) { + self.clearTimeout(this.updateThrottleTimer); + this.updateThrottleTimer = void 0; + } + if (this.pendingUpdates.length > 0) { + out = []; + mode = "update"; + for (const row of this.pendingUpdates) { + out.push(toClient(row, keys, selectedRows)); + } + this.pendingUpdates.length = 0; + } else { + const records = this.dataWindow.getData(); + if (this.dataWindow.hasAllRowsWithinRange) { + out = []; + mode = "batch"; + for (const row of records) { + out.push(toClient(row, keys, selectedRows)); + } + this.batchMode = false; + } + } + this.hasUpdates = false; + } + if (this.throttleMessage(mode)) { + return NO_DATA_UPDATE; + } else { + return [out, mode]; + } + } + createRequest(params, overWrite = false) { + if (overWrite) { + return { + type: "CHANGE_VP", + viewPortId: this.serverViewportId, + ...params + }; + } else { + return { + type: "CHANGE_VP", + viewPortId: this.serverViewportId, + aggregations: this.aggregations, + columns: this.columns, + sort: this.sort, + groupBy: this.groupBy, + filterSpec: { + filter: this.filter.filter + }, + ...params + }; + } + } +}; +var toClientRow = ({ rowIndex, rowKey, sel: isSelected, data }, keys, selectedRows) => { + return [ + rowIndex, + keys.keyFor(rowIndex), + true, + false, + 0, + 0, + rowKey, + isSelected ? getSelectionStatus(selectedRows, rowIndex) : 0 + ].concat(data); +}; +var toClientRowTree = ({ rowIndex, rowKey, sel: isSelected, data }, keys, selectedRows) => { + const [depth, isExpanded, , isLeaf, , count, ...rest] = data; + return [ + rowIndex, + keys.keyFor(rowIndex), + isLeaf, + isExpanded, + depth, + count, + rowKey, + isSelected ? getSelectionStatus(selectedRows, rowIndex) : 0 + ].concat(rest); +}; + +// src/server-proxy/server-proxy.ts +var _requestId = 1; +var { debug: debug4, debugEnabled: debugEnabled4, error: error3, info: info3, infoEnabled: infoEnabled3, warn: warn3 } = logger("server-proxy"); +var nextRequestId = () => \`\${_requestId++}\`; +var DEFAULT_OPTIONS = {}; +var isActiveViewport = (viewPort) => viewPort.disabled !== true && viewPort.suspended !== true; +var NO_ACTION = { + type: "NO_ACTION" +}; +var addTitleToLinks = (links, serverViewportId, label) => links.map( + (link) => link.parentVpId === serverViewportId ? { ...link, label } : link +); +function addLabelsToLinks(links, viewports) { + return links.map((linkDescriptor) => { + const { parentVpId } = linkDescriptor; + const viewport = viewports.get(parentVpId); + if (viewport) { + return { + ...linkDescriptor, + parentClientVpId: viewport.clientViewportId, + label: viewport.title + }; + } else { + throw Error("addLabelsToLinks viewport not found"); + } + }); +} +var ServerProxy = class { + constructor(connection, callback) { + this.authToken = ""; + this.user = "user"; + this.pendingTableMetaRequests = /* @__PURE__ */ new Map(); + this.pendingRequests = /* @__PURE__ */ new Map(); + this.queuedRequests = []; + this.cachedTableSchemas = /* @__PURE__ */ new Map(); + this.connection = connection; + this.postMessageToClient = callback; + this.viewports = /* @__PURE__ */ new Map(); + this.mapClientToServerViewport = /* @__PURE__ */ new Map(); + } + async reconnect() { + await this.login(this.authToken); + const [activeViewports, inactiveViewports] = partition( + Array.from(this.viewports.values()), + isActiveViewport + ); + this.viewports.clear(); + this.mapClientToServerViewport.clear(); + const reconnectViewports = (viewports) => { + viewports.forEach((viewport) => { + const { clientViewportId } = viewport; + this.viewports.set(clientViewportId, viewport); + this.sendMessageToServer(viewport.subscribe(), clientViewportId); + }); + }; + reconnectViewports(activeViewports); + setTimeout(() => { + reconnectViewports(inactiveViewports); + }, 2e3); + } + async login(authToken, user = "user") { + if (authToken) { + this.authToken = authToken; + this.user = user; + return new Promise((resolve, reject) => { + this.sendMessageToServer( + { type: LOGIN, token: this.authToken, user }, + "" + ); + this.pendingLogin = { resolve, reject }; + }); + } else if (this.authToken === "") { + error3("login, cannot login until auth token has been obtained"); + } + } + subscribe(message) { + if (!this.mapClientToServerViewport.has(message.viewport)) { + if (!this.hasSchemaForTable(message.table) && // A Session table is never cached - it is limited to a single workflow interaction + // The metadata for a session table is requested even before the subscribe call. + !isSessionTable(message.table)) { + info3 == null ? void 0 : info3( + \`subscribe to \${message.table.table}, no metadata yet, request metadata\` + ); + const requestId = nextRequestId(); + this.sendMessageToServer( + { type: "GET_TABLE_META", table: message.table }, + requestId + ); + this.pendingTableMetaRequests.set(requestId, message.viewport); + } + const viewport = new Viewport(message, this.postMessageToClient); + this.viewports.set(message.viewport, viewport); + this.sendIfReady( + viewport.subscribe(), + message.viewport, + this.sessionId !== "" + ); + } else { + error3(\`spurious subscribe call \${message.viewport}\`); + } + } + unsubscribe(clientViewportId) { + const serverViewportId = this.mapClientToServerViewport.get(clientViewportId); + if (serverViewportId) { + info3 == null ? void 0 : info3( + \`Unsubscribe Message (Client to Server): + \${serverViewportId}\` + ); + this.sendMessageToServer({ + type: REMOVE_VP, + viewPortId: serverViewportId + }); + } else { + error3( + \`failed to unsubscribe client viewport \${clientViewportId}, viewport not found\` + ); + } + } + getViewportForClient(clientViewportId, throws = true) { + const serverViewportId = this.mapClientToServerViewport.get(clientViewportId); + if (serverViewportId) { + const viewport = this.viewports.get(serverViewportId); + if (viewport) { + return viewport; + } else if (throws) { + throw Error( + \`Viewport not found for client viewport \${clientViewportId}\` + ); + } else { + return null; + } + } else if (this.viewports.has(clientViewportId)) { + return this.viewports.get(clientViewportId); + } else if (throws) { + throw Error( + \`Viewport server id not found for client viewport \${clientViewportId}\` + ); + } else { + return null; + } + } + /**********************************************************************/ + /* Handle messages from client */ + /**********************************************************************/ + setViewRange(viewport, message) { + const requestId = nextRequestId(); + const [serverRequest, rows, debounceRequest] = viewport.rangeRequest( + requestId, + message.range + ); + info3 == null ? void 0 : info3(\`setViewRange \${message.range.from} - \${message.range.to}\`); + if (serverRequest) { + if (true) { + info3 == null ? void 0 : info3( + \`CHANGE_VP_RANGE [\${message.range.from}-\${message.range.to}] => [\${serverRequest.from}-\${serverRequest.to}]\` + ); + } + this.sendIfReady( + serverRequest, + requestId, + viewport.status === "subscribed" + ); + } + if (rows) { + info3 == null ? void 0 : info3(\`setViewRange \${rows.length} rows returned from cache\`); + this.postMessageToClient({ + mode: "batch", + type: "viewport-update", + clientViewportId: viewport.clientViewportId, + rows + }); + } else if (debounceRequest) { + this.postMessageToClient(debounceRequest); + } + } + setConfig(viewport, message) { + const requestId = nextRequestId(); + const request = viewport.setConfig(requestId, message.config); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + aggregate(viewport, message) { + const requestId = nextRequestId(); + const request = viewport.aggregateRequest(requestId, message.aggregations); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + sort(viewport, message) { + const requestId = nextRequestId(); + const request = viewport.sortRequest(requestId, message.sort); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + groupBy(viewport, message) { + const requestId = nextRequestId(); + const request = viewport.groupByRequest(requestId, message.groupBy); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + filter(viewport, message) { + const requestId = nextRequestId(); + const { filter } = message; + const request = viewport.filterRequest(requestId, filter); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + setColumns(viewport, message) { + const requestId = nextRequestId(); + const { columns } = message; + const request = viewport.columnRequest(requestId, columns); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + setTitle(viewport, message) { + if (viewport) { + viewport.title = message.title; + this.updateTitleOnVisualLinks(viewport); + } + } + select(viewport, message) { + const requestId = nextRequestId(); + const { selected } = message; + const request = viewport.selectRequest(requestId, selected); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + disableViewport(viewport) { + const requestId = nextRequestId(); + const request = viewport.disable(requestId); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + enableViewport(viewport) { + if (viewport.disabled) { + const requestId = nextRequestId(); + const request = viewport.enable(requestId); + this.sendIfReady(request, requestId, viewport.status === "subscribed"); + } + } + suspendViewport(viewport) { + viewport.suspend(); + viewport.suspendTimer = setTimeout(() => { + info3 == null ? void 0 : info3("suspendTimer expired, escalate suspend to disable"); + this.disableViewport(viewport); + }, 3e3); + } + resumeViewport(viewport) { + if (viewport.suspendTimer) { + debug4 == null ? void 0 : debug4("clear suspend timer"); + clearTimeout(viewport.suspendTimer); + viewport.suspendTimer = null; + } + const rows = viewport.resume(); + this.postMessageToClient({ + clientViewportId: viewport.clientViewportId, + mode: "batch", + rows, + type: "viewport-update" + }); + } + openTreeNode(viewport, message) { + if (viewport.serverViewportId) { + const requestId = nextRequestId(); + this.sendIfReady( + viewport.openTreeNode(requestId, message), + requestId, + viewport.status === "subscribed" + ); + } + } + closeTreeNode(viewport, message) { + if (viewport.serverViewportId) { + const requestId = nextRequestId(); + this.sendIfReady( + viewport.closeTreeNode(requestId, message), + requestId, + viewport.status === "subscribed" + ); + } + } + createLink(viewport, message) { + const { parentClientVpId, parentColumnName, childColumnName } = message; + const requestId = nextRequestId(); + const parentVpId = this.mapClientToServerViewport.get(parentClientVpId); + if (parentVpId) { + const request = viewport.createLink( + requestId, + childColumnName, + parentVpId, + parentColumnName + ); + this.sendMessageToServer(request, requestId); + } else { + error3("ServerProxy unable to create link, viewport not found"); + } + } + removeLink(viewport) { + const requestId = nextRequestId(); + const request = viewport.removeLink(requestId); + this.sendMessageToServer(request, requestId); + } + updateTitleOnVisualLinks(viewport) { + var _a; + const { serverViewportId, title } = viewport; + for (const vp of this.viewports.values()) { + if (vp !== viewport && vp.links && serverViewportId && title) { + if ((_a = vp.links) == null ? void 0 : _a.some((link) => link.parentVpId === serverViewportId)) { + const [messageToClient] = vp.setLinks( + addTitleToLinks(vp.links, serverViewportId, title) + ); + this.postMessageToClient(messageToClient); + } + } + } + } + removeViewportFromVisualLinks(serverViewportId) { + var _a; + for (const vp of this.viewports.values()) { + if ((_a = vp.links) == null ? void 0 : _a.some(({ parentVpId }) => parentVpId === serverViewportId)) { + const [messageToClient] = vp.setLinks( + vp.links.filter(({ parentVpId }) => parentVpId !== serverViewportId) + ); + this.postMessageToClient(messageToClient); + } + } + } + menuRpcCall(message) { + const viewport = this.getViewportForClient(message.vpId, false); + if (viewport == null ? void 0 : viewport.serverViewportId) { + const [requestId, rpcRequest] = stripRequestId(message); + this.sendMessageToServer( + { + ...rpcRequest, + vpId: viewport.serverViewportId + }, + requestId + ); + } + } + rpcCall(message) { + const [requestId, rpcRequest] = stripRequestId(message); + const module = getRpcServiceModule(rpcRequest.service); + this.sendMessageToServer(rpcRequest, requestId, { module }); + } + handleMessageFromClient(message) { + if (isViewporttMessage(message)) { + if (message.type === "disable") { + const viewport = this.getViewportForClient(message.viewport, false); + if (viewport !== null) { + return this.disableViewport(viewport); + } else { + return; + } + } else { + const viewport = this.getViewportForClient(message.viewport); + switch (message.type) { + case "setViewRange": + return this.setViewRange(viewport, message); + case "config": + return this.setConfig(viewport, message); + case "aggregate": + return this.aggregate(viewport, message); + case "sort": + return this.sort(viewport, message); + case "groupBy": + return this.groupBy(viewport, message); + case "filter": + return this.filter(viewport, message); + case "select": + return this.select(viewport, message); + case "suspend": + return this.suspendViewport(viewport); + case "resume": + return this.resumeViewport(viewport); + case "enable": + return this.enableViewport(viewport); + case "openTreeNode": + return this.openTreeNode(viewport, message); + case "closeTreeNode": + return this.closeTreeNode(viewport, message); + case "createLink": + return this.createLink(viewport, message); + case "removeLink": + return this.removeLink(viewport); + case "setColumns": + return this.setColumns(viewport, message); + case "setTitle": + return this.setTitle(viewport, message); + default: + } + } + } else if (isVuuMenuRpcRequest(message)) { + return this.menuRpcCall(message); + } else { + const { type, requestId } = message; + switch (type) { + case "GET_TABLE_LIST": + return this.sendMessageToServer({ type }, requestId); + case "GET_TABLE_META": + return this.sendMessageToServer( + { type, table: message.table }, + requestId + ); + case "RPC_CALL": + return this.rpcCall(message); + default: + } + } + error3( + \`Vuu ServerProxy Unexpected message from client \${JSON.stringify( + message + )}\` + ); + } + awaitResponseToMessage(message) { + return new Promise((resolve, reject) => { + const requestId = nextRequestId(); + this.sendMessageToServer(message, requestId); + this.pendingRequests.set(requestId, { reject, resolve }); + }); + } + sendIfReady(message, requestId, isReady = true) { + if (isReady) { + this.sendMessageToServer(message, requestId); + } else { + this.queuedRequests.push(message); + } + return isReady; + } + sendMessageToServer(body, requestId = \`\${_requestId++}\`, options = DEFAULT_OPTIONS) { + const { module = "CORE" } = options; + if (this.authToken) { + this.connection.send({ + requestId, + sessionId: this.sessionId, + token: this.authToken, + user: this.user, + module, + body + }); + } + } + handleMessageFromServer(message) { + var _a, _b, _c; + const { body, requestId, sessionId } = message; + const pendingRequest = this.pendingRequests.get(requestId); + if (pendingRequest) { + const { resolve } = pendingRequest; + this.pendingRequests.delete(requestId); + resolve(body); + return; + } + const { viewports } = this; + switch (body.type) { + case HB: + this.sendMessageToServer( + { type: HB_RESP, ts: +/* @__PURE__ */ new Date() }, + "NA" + ); + break; + case LOGIN_SUCCESS: + if (sessionId) { + this.sessionId = sessionId; + (_a = this.pendingLogin) == null ? void 0 : _a.resolve(sessionId); + this.pendingLogin = void 0; + } else { + throw Error("LOGIN_SUCCESS did not provide sessionId"); + } + break; + case CREATE_VP_SUCCESS: + { + const viewport = viewports.get(requestId); + if (viewport) { + const { status: viewportStatus } = viewport; + const { viewPortId: serverViewportId } = body; + if (requestId !== serverViewportId) { + viewports.delete(requestId); + viewports.set(serverViewportId, viewport); + } + this.mapClientToServerViewport.set(requestId, serverViewportId); + const response = viewport.handleSubscribed(body); + if (response) { + this.postMessageToClient(response); + if (debugEnabled4) { + debug4( + \`post DataSourceSubscribedMessage to client: \${JSON.stringify( + response + )}\` + ); + } + } + if (viewport.disabled) { + this.disableViewport(viewport); + } + if (viewportStatus === "subscribing" && // A session table will never have Visual Links, nor Context Menus + !isSessionTable(viewport.table)) { + this.sendMessageToServer({ + type: GET_VP_VISUAL_LINKS, + vpId: serverViewportId + }); + this.sendMessageToServer({ + type: GET_VIEW_PORT_MENUS, + vpId: serverViewportId + }); + Array.from(viewports.entries()).filter( + ([id, { disabled }]) => id !== serverViewportId && !disabled + ).forEach(([vpId]) => { + this.sendMessageToServer({ + type: GET_VP_VISUAL_LINKS, + vpId + }); + }); + } + } + } + break; + case "REMOVE_VP_SUCCESS": + { + const viewport = viewports.get(body.viewPortId); + if (viewport) { + this.mapClientToServerViewport.delete(viewport.clientViewportId); + viewports.delete(body.viewPortId); + this.removeViewportFromVisualLinks(body.viewPortId); + } + } + break; + case SET_SELECTION_SUCCESS: + { + const viewport = this.viewports.get(body.vpId); + if (viewport) { + viewport.completeOperation(requestId); + } + } + break; + case CHANGE_VP_SUCCESS: + case DISABLE_VP_SUCCESS: + if (viewports.has(body.viewPortId)) { + const viewport = this.viewports.get(body.viewPortId); + if (viewport) { + const response = viewport.completeOperation(requestId); + if (response !== void 0) { + this.postMessageToClient(response); + if (debugEnabled4) { + debug4(\`postMessageToClient \${JSON.stringify(response)}\`); + } + } + } + } + break; + case ENABLE_VP_SUCCESS: + { + const viewport = this.viewports.get(body.viewPortId); + if (viewport) { + const response = viewport.completeOperation(requestId); + if (response) { + this.postMessageToClient(response); + const rows = viewport.currentData(); + debugEnabled4 && debug4( + \`Enable Response (ServerProxy to Client): \${JSON.stringify( + response + )}\` + ); + if (viewport.size === 0) { + debugEnabled4 && debug4(\`Viewport Enabled but size 0, resend to server\`); + } else { + this.postMessageToClient({ + clientViewportId: viewport.clientViewportId, + mode: "batch", + rows, + size: viewport.size, + type: "viewport-update" + }); + debugEnabled4 && debug4( + \`Enable Response (ServerProxy to Client): send size \${viewport.size} \${rows.length} rows from cache\` + ); + } + } + } + } + break; + case TABLE_ROW: + { + const viewportRowMap = groupRowsByViewport(body.rows); + if (debugEnabled4) { + const [firstRow, secondRow] = body.rows; + if (body.rows.length === 0) { + debug4("handleMessageFromServer TABLE_ROW 0 rows"); + } else if ((firstRow == null ? void 0 : firstRow.rowIndex) === -1) { + if (body.rows.length === 1) { + if (firstRow.updateType === "SIZE") { + debug4( + \`handleMessageFromServer [\${firstRow.viewPortId}] TABLE_ROW SIZE ONLY \${firstRow.vpSize}\` + ); + } else { + debug4( + \`handleMessageFromServer [\${firstRow.viewPortId}] TABLE_ROW SIZE \${firstRow.vpSize} rowIdx \${firstRow.rowIndex}\` + ); + } + } else { + debug4( + \`handleMessageFromServer TABLE_ROW \${body.rows.length} rows, SIZE \${firstRow.vpSize}, [\${secondRow == null ? void 0 : secondRow.rowIndex}] - [\${(_b = body.rows[body.rows.length - 1]) == null ? void 0 : _b.rowIndex}]\` + ); + } + } else { + debug4( + \`handleMessageFromServer TABLE_ROW \${body.rows.length} rows [\${firstRow == null ? void 0 : firstRow.rowIndex}] - [\${(_c = body.rows[body.rows.length - 1]) == null ? void 0 : _c.rowIndex}]\` + ); + } + } + for (const [viewportId, rows] of Object.entries(viewportRowMap)) { + const viewport = viewports.get(viewportId); + if (viewport) { + viewport.updateRows(rows); + } else { + warn3 == null ? void 0 : warn3( + \`TABLE_ROW message received for non registered viewport \${viewportId}\` + ); + } + } + this.processUpdates(); + } + break; + case CHANGE_VP_RANGE_SUCCESS: + { + const viewport = this.viewports.get(body.viewPortId); + if (viewport) { + const { from, to } = body; + if (true) { + info3 == null ? void 0 : info3(\`CHANGE_VP_RANGE_SUCCESS \${from} - \${to}\`); + } + viewport.completeOperation(requestId, from, to); + } + } + break; + case OPEN_TREE_SUCCESS: + case CLOSE_TREE_SUCCESS: + break; + case "CREATE_VISUAL_LINK_SUCCESS": + { + const viewport = this.viewports.get(body.childVpId); + const parentViewport = this.viewports.get(body.parentVpId); + if (viewport && parentViewport) { + const { childColumnName, parentColumnName } = body; + const response = viewport.completeOperation( + requestId, + childColumnName, + parentViewport.clientViewportId, + parentColumnName + ); + if (response) { + this.postMessageToClient(response); + } + } + } + break; + case "REMOVE_VISUAL_LINK_SUCCESS": + { + const viewport = this.viewports.get(body.childVpId); + if (viewport) { + const response = viewport.completeOperation( + requestId + ); + if (response) { + this.postMessageToClient(response); + } + } + } + break; + case TABLE_LIST_RESP: + this.postMessageToClient({ + type: TABLE_LIST_RESP, + tables: body.tables, + requestId + }); + break; + case TABLE_META_RESP: + { + const tableSchema = this.cacheTableMeta(body); + const clientViewportId = this.pendingTableMetaRequests.get(requestId); + if (clientViewportId) { + this.pendingTableMetaRequests.delete(requestId); + const viewport = this.viewports.get(clientViewportId); + if (viewport) { + viewport.setTableSchema(tableSchema); + } else { + warn3 == null ? void 0 : warn3( + "Message has come back AFTER CREATE_VP_SUCCESS, what do we do now" + ); + } + } else { + this.postMessageToClient({ + type: TABLE_META_RESP, + tableSchema, + requestId + }); + } + } + break; + case "VP_VISUAL_LINKS_RESP": + { + const activeLinkDescriptors = this.getActiveLinks(body.links); + const viewport = this.viewports.get(body.vpId); + if (activeLinkDescriptors.length && viewport) { + const linkDescriptorsWithLabels = addLabelsToLinks( + activeLinkDescriptors, + this.viewports + ); + const [clientMessage, pendingLink] = viewport.setLinks( + linkDescriptorsWithLabels + ); + this.postMessageToClient(clientMessage); + if (pendingLink) { + const { link, parentClientVpId } = pendingLink; + const requestId2 = nextRequestId(); + const serverViewportId = this.mapClientToServerViewport.get(parentClientVpId); + if (serverViewportId) { + const message2 = viewport.createLink( + requestId2, + link.fromColumn, + serverViewportId, + link.toColumn + ); + this.sendMessageToServer(message2, requestId2); + } + } + } + } + break; + case "VIEW_PORT_MENUS_RESP": + if (body.menu.name) { + const viewport = this.viewports.get(body.vpId); + if (viewport) { + const clientMessage = viewport.setMenu(body.menu); + this.postMessageToClient(clientMessage); + } + } + break; + case "VP_EDIT_RPC_RESPONSE": + { + this.postMessageToClient({ + action: body.action, + requestId, + rpcName: body.rpcName, + type: "VP_EDIT_RPC_RESPONSE" + }); + } + break; + case "VP_EDIT_RPC_REJECT": + { + const viewport = this.viewports.get(body.vpId); + if (viewport) { + this.postMessageToClient({ + requestId, + type: "VP_EDIT_RPC_REJECT", + error: body.error + }); + } + } + break; + case "VIEW_PORT_MENU_REJ": { + console.log(\`send menu error back to client\`); + const { error: error4, rpcName } = body; + this.postMessageToClient({ + error: error4, + rpcName, + type: "VIEW_PORT_MENU_REJ", + requestId + }); + break; + } + case "VIEW_PORT_MENU_RESP": + { + if (isSessionTableActionMessage(body)) { + const { action, rpcName } = body; + this.awaitResponseToMessage({ + type: "GET_TABLE_META", + table: action.table + }).then((response) => { + const tableSchema = createSchemaFromTableMetadata( + response + ); + this.postMessageToClient({ + rpcName, + type: "VIEW_PORT_MENU_RESP", + action: { + ...action, + tableSchema + }, + tableAlreadyOpen: this.isTableOpen(action.table), + requestId + }); + }); + } else { + const { action } = body; + this.postMessageToClient({ + type: "VIEW_PORT_MENU_RESP", + action: action || NO_ACTION, + tableAlreadyOpen: action !== null && this.isTableOpen(action.table), + requestId + }); + } + } + break; + case RPC_RESP: + { + const { method, result } = body; + this.postMessageToClient({ + type: RPC_RESP, + method, + result, + requestId + }); + } + break; + case "ERROR": + error3(body.msg); + break; + default: + infoEnabled3 && info3(\`handleMessageFromServer \${body["type"]}.\`); + } + } + hasSchemaForTable(table) { + return this.cachedTableSchemas.has(\`\${table.module}:\${table.table}\`); + } + cacheTableMeta(messageBody) { + const { module, table } = messageBody.table; + const key = \`\${module}:\${table}\`; + let tableSchema = this.cachedTableSchemas.get(key); + if (!tableSchema) { + tableSchema = createSchemaFromTableMetadata(messageBody); + this.cachedTableSchemas.set(key, tableSchema); + } + return tableSchema; + } + isTableOpen(table) { + if (table) { + const tableName = table.table; + for (const viewport of this.viewports.values()) { + if (!viewport.suspended && viewport.table.table === tableName) { + return true; + } + } + } + } + // Eliminate links to suspended viewports + getActiveLinks(linkDescriptors) { + return linkDescriptors.filter((linkDescriptor) => { + const viewport = this.viewports.get(linkDescriptor.parentVpId); + return viewport && !viewport.suspended; + }); + } + processUpdates() { + this.viewports.forEach((viewport) => { + var _a; + if (viewport.hasUpdatesToProcess) { + const result = viewport.getClientRows(); + if (result !== NO_DATA_UPDATE) { + const [rows, mode] = result; + const size = viewport.getNewRowCount(); + if (size !== void 0 || rows && rows.length > 0) { + debugEnabled4 && debug4( + \`postMessageToClient #\${viewport.clientViewportId} viewport-update \${mode}, \${(_a = rows == null ? void 0 : rows.length) != null ? _a : "no"} rows, size \${size}\` + ); + if (mode) { + this.postMessageToClient({ + clientViewportId: viewport.clientViewportId, + mode, + rows, + size, + type: "viewport-update" + }); + } + } + } + } + }); + } +}; + +// src/worker.ts +var server; +var { info: info4, infoEnabled: infoEnabled4 } = logger("worker"); +async function connectToServer(url, protocol, token, username, onConnectionStatusChange, retryLimitDisconnect, retryLimitStartup) { + const connection = await connect( + url, + protocol, + // if this was called during connect, we would get a ReferenceError, but it will + // never be called until subscriptions have been made, so this is safe. + //TODO do we need to listen in to the connection messages here so we can lock back in, in the event of a reconnenct ? + (msg) => { + if (isConnectionQualityMetrics(msg)) { + console.log("post connection metrics"); + postMessage({ type: "connection-metrics", messages: msg }); + } else if (isConnectionStatusMessage(msg)) { + onConnectionStatusChange(msg); + if (msg.status === "reconnected") { + server.reconnect(); + } + } else { + server.handleMessageFromServer(msg); + } + }, + retryLimitDisconnect, + retryLimitStartup + ); + server = new ServerProxy(connection, (msg) => sendMessageToClient(msg)); + if (connection.requiresLogin) { + await server.login(token, username); + } +} +function sendMessageToClient(message) { + postMessage(message); +} +var handleMessageFromClient = async ({ + data: message +}) => { + switch (message.type) { + case "connect": + await connectToServer( + message.url, + message.protocol, + message.token, + message.username, + postMessage, + message.retryLimitDisconnect, + message.retryLimitStartup + ); + postMessage({ type: "connected" }); + break; + case "subscribe": + infoEnabled4 && info4(\`client subscribe: \${JSON.stringify(message)}\`); + server.subscribe(message); + break; + case "unsubscribe": + infoEnabled4 && info4(\`client unsubscribe: \${JSON.stringify(message)}\`); + server.unsubscribe(message.viewport); + break; + default: + infoEnabled4 && info4(\`client message: \${JSON.stringify(message)}\`); + server.handleMessageFromClient(message); + } +}; +self.addEventListener("message", handleMessageFromClient); +postMessage({ type: "ready" }); `; \ No newline at end of file diff --git a/vuu-ui/packages/vuu-shell/src/login/LoginPanel.tsx b/vuu-ui/packages/vuu-shell/src/login/LoginPanel.tsx index 7f4159ca5..b01ffacfa 100644 --- a/vuu-ui/packages/vuu-shell/src/login/LoginPanel.tsx +++ b/vuu-ui/packages/vuu-shell/src/login/LoginPanel.tsx @@ -51,7 +51,11 @@ export const LoginPanel = ({ } }, [onSubmit, requirePassword, username]); - const handleCommitPassword = useCallback(() => {}, []); + const handleCommitPassword = useCallback(() => { + if (username) { + onSubmit(username, password); + } + }, [onSubmit, password, username]); const dataIsValid = username.trim() !== "" && diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/config/localhost.config.json b/vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/config/localhost.config.json rename to vuu-ui/sample-apps/app-vuu-example/config/localhost.config.json diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/demo.tsx b/vuu-ui/sample-apps/app-vuu-example/demo.tsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/demo.tsx rename to vuu-ui/sample-apps/app-vuu-example/demo.tsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx b/vuu-ui/sample-apps/app-vuu-example/index.tsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx rename to vuu-ui/sample-apps/app-vuu-example/index.tsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/layout-loadind.md b/vuu-ui/sample-apps/app-vuu-example/layout-loadind.md similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/layout-loadind.md rename to vuu-ui/sample-apps/app-vuu-example/layout-loadind.md diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/login.css b/vuu-ui/sample-apps/app-vuu-example/login.css similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/login.css rename to vuu-ui/sample-apps/app-vuu-example/login.css diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx b/vuu-ui/sample-apps/app-vuu-example/login.tsx similarity index 86% rename from vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx rename to vuu-ui/sample-apps/app-vuu-example/login.tsx index f2e0e3cee..925da5457 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/login.tsx +++ b/vuu-ui/sample-apps/app-vuu-example/login.tsx @@ -3,10 +3,12 @@ import ReactDOM from "react-dom"; import { LoginPanel, ThemeProvider } from "@finos/vuu-shell"; import { authenticate } from "@finos/vuu-data"; +import "@finos/vuu-icons/index.css"; import "@finos/vuu-theme/index.css"; + import "./login.css"; -async function login(username: string, password: string) { +async function login(username: string, password = "password") { try { const { authUrl } = await vuuConfig; const authToken = await authenticate(username, password, authUrl); @@ -23,7 +25,7 @@ async function login(username: string, password: string) { } ReactDOM.render( - + , document.getElementById("root") diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/package.json b/vuu-ui/sample-apps/app-vuu-example/package.json similarity index 96% rename from vuu-ui/sample-apps/app-vuu-basket-trader/package.json rename to vuu-ui/sample-apps/app-vuu-example/package.json index 8baea4e7b..4367ff480 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/package.json +++ b/vuu-ui/sample-apps/app-vuu-example/package.json @@ -1,5 +1,5 @@ { - "name": "app-vuu-basket-trader", + "name": "app-vuu-example", "version": "0.0.26", "description": "", "scripts": { diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/about.txt b/vuu-ui/sample-apps/app-vuu-example/public/about.txt similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/about.txt rename to vuu-ui/sample-apps/app-vuu-example/public/about.txt diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/apple-touch-icon.png b/vuu-ui/sample-apps/app-vuu-example/public/apple-touch-icon.png similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/apple-touch-icon.png rename to vuu-ui/sample-apps/app-vuu-example/public/apple-touch-icon.png diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html b/vuu-ui/sample-apps/app-vuu-example/public/demo.html similarity index 80% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html rename to vuu-ui/sample-apps/app-vuu-example/public/demo.html index 5e53d03ff..9abfbcef2 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/demo.html +++ b/vuu-ui/sample-apps/app-vuu-example/public/demo.html @@ -9,11 +9,11 @@ - + VUU App
- + diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.ico b/vuu-ui/sample-apps/app-vuu-example/public/favicon.ico similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.ico rename to vuu-ui/sample-apps/app-vuu-example/public/favicon.ico diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.png b/vuu-ui/sample-apps/app-vuu-example/public/favicon.png similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.png rename to vuu-ui/sample-apps/app-vuu-example/public/favicon.png diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.svg b/vuu-ui/sample-apps/app-vuu-example/public/favicon.svg similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/favicon.svg rename to vuu-ui/sample-apps/app-vuu-example/public/favicon.svg diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html b/vuu-ui/sample-apps/app-vuu-example/public/index.html similarity index 82% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html rename to vuu-ui/sample-apps/app-vuu-example/public/index.html index 155bead1d..a6cd74925 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/index.html +++ b/vuu-ui/sample-apps/app-vuu-example/public/index.html @@ -10,11 +10,11 @@ - + VUU App
- + diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html b/vuu-ui/sample-apps/app-vuu-example/public/login.html similarity index 83% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html rename to vuu-ui/sample-apps/app-vuu-example/public/login.html index d9fcdf50c..ac64d5b78 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/public/login.html +++ b/vuu-ui/sample-apps/app-vuu-example/public/login.html @@ -7,7 +7,7 @@ - + @@ -16,6 +16,6 @@
- + diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/manifest.json b/vuu-ui/sample-apps/app-vuu-example/public/manifest.json similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/manifest.json rename to vuu-ui/sample-apps/app-vuu-example/public/manifest.json diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/public/vuu-icon.svg b/vuu-ui/sample-apps/app-vuu-example/public/vuu-icon.svg similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/public/vuu-icon.svg rename to vuu-ui/sample-apps/app-vuu-example/public/vuu-icon.svg diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/.eslintrc.json b/vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/scripts/.eslintrc.json rename to vuu-ui/sample-apps/app-vuu-example/scripts/.eslintrc.json diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs b/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs similarity index 98% rename from vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs rename to vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs index 3e83b8134..4a4bec219 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/scripts/build.mjs +++ b/vuu-ui/sample-apps/app-vuu-example/scripts/build.mjs @@ -16,7 +16,7 @@ import path from "path"; const entryPoints = ["index.tsx", "login.tsx", "demo.tsx"]; -const outdir = "../../deployed_apps/app-vuu-basket-trader"; +const outdir = "../../deployed_apps/app-vuu-example"; let configFile = "./config/localhost.config.json"; const websocketUrl = getCommandLineArg("--url", true); @@ -43,7 +43,7 @@ const { name: projectName } = readPackageJson(); const esbuildConfig = { entryPoints: entryPoints.concat(featureEntryPoints), env: development ? "development" : "production", - name: "app-vuu-basket-trader", + name: "app-vuu-example", outdir, splitting: true, target: "esnext", diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.css b/vuu-ui/sample-apps/app-vuu-example/src/App.css similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/App.css rename to vuu-ui/sample-apps/app-vuu-example/src/App.css diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx rename to vuu-ui/sample-apps/app-vuu-example/src/App.tsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/Layouts.jsx b/vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/Layouts.jsx rename to vuu-ui/sample-apps/app-vuu-example/src/Layouts.jsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/columnMetaData.ts b/vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/columnMetaData.ts rename to vuu-ui/sample-apps/app-vuu-example/src/columnMetaData.ts diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/createPlaceholder.tsx b/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/createPlaceholder.tsx rename to vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/index.ts b/vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/index.ts rename to vuu-ui/sample-apps/app-vuu-example/src/session-editing/index.ts diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/session-table-config.ts b/vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/session-editing/session-table-config.ts rename to vuu-ui/sample-apps/app-vuu-example/src/session-editing/session-table-config.ts diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/useFeatures.ts b/vuu-ui/sample-apps/app-vuu-example/src/useFeatures.ts similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/useFeatures.ts rename to vuu-ui/sample-apps/app-vuu-example/src/useFeatures.ts diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/useRpcResponseHandler.tsx b/vuu-ui/sample-apps/app-vuu-example/src/useRpcResponseHandler.tsx similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/src/useRpcResponseHandler.tsx rename to vuu-ui/sample-apps/app-vuu-example/src/useRpcResponseHandler.tsx diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/tsconfig.json b/vuu-ui/sample-apps/app-vuu-example/tsconfig.json similarity index 100% rename from vuu-ui/sample-apps/app-vuu-basket-trader/tsconfig.json rename to vuu-ui/sample-apps/app-vuu-example/tsconfig.json diff --git a/vuu-ui/sample-apps/feature-basket-trading/package.json b/vuu-ui/sample-apps/feature-basket-trading/package.json index e8c343ac7..ed0a34e3e 100644 --- a/vuu-ui/sample-apps/feature-basket-trading/package.json +++ b/vuu-ui/sample-apps/feature-basket-trading/package.json @@ -5,7 +5,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "node ../../scripts/build-feature.mjs", - "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader" + "start": "serve -p 5002 ../../deployed_apps/app-vuu-example" }, "private": true, "keywords": [], diff --git a/vuu-ui/sample-apps/feature-filter-table/package.json b/vuu-ui/sample-apps/feature-filter-table/package.json index b60496af6..fc3226622 100644 --- a/vuu-ui/sample-apps/feature-filter-table/package.json +++ b/vuu-ui/sample-apps/feature-filter-table/package.json @@ -5,7 +5,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "node ../../scripts/build-feature.mjs", - "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader" + "start": "serve -p 5002 ../../deployed_apps/app-vuu-example" }, "private": true, "keywords": [], diff --git a/vuu-ui/sample-apps/feature-instrument-tiles/package.json b/vuu-ui/sample-apps/feature-instrument-tiles/package.json index 8e095ee79..6227baecb 100644 --- a/vuu-ui/sample-apps/feature-instrument-tiles/package.json +++ b/vuu-ui/sample-apps/feature-instrument-tiles/package.json @@ -5,7 +5,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "node ../../scripts/build-feature.mjs", - "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader" + "start": "serve -p 5002 ../../deployed_apps/app-vuu-example" }, "private": true, "keywords": [], diff --git a/vuu-ui/sample-apps/feature-template/package.json b/vuu-ui/sample-apps/feature-template/package.json index db724dca9..456fd94de 100644 --- a/vuu-ui/sample-apps/feature-template/package.json +++ b/vuu-ui/sample-apps/feature-template/package.json @@ -5,7 +5,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "node ../../scripts/build-feature.mjs", - "start": "serve -p 5002 ../../deployed_apps/app-vuu-basket-trader" + "start": "serve -p 5002 ../../deployed_apps/app-vuu-example" }, "private": true, "keywords": [], From 3ba64ad63b4eb324faf8842d6cbdf37a3d72f68c Mon Sep 17 00:00:00 2001 From: heswell Date: Wed, 1 Nov 2023 15:20:17 +0000 Subject: [PATCH 3/4] fix broken inmports --- .../src/useBasketTrading.tsx | 4 +- .../examples/Shell/AppSidePanel.examples.tsx | 59 ------------------- .../src/examples/Shell/Shell.examples.tsx | 50 +--------------- vuu-ui/showcase/src/examples/Shell/index.ts | 1 - 4 files changed, 3 insertions(+), 111 deletions(-) delete mode 100644 vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx diff --git a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx index 36a9dd33c..c65d328f9 100644 --- a/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx +++ b/vuu-ui/sample-apps/feature-basket-trading/src/useBasketTrading.tsx @@ -1,6 +1,6 @@ import { useViewContext } from "@finos/vuu-layout"; -import { VuuDataRow } from "packages/vuu-protocol-types"; -import { buildColumnMap, ColumnMap } from "packages/vuu-utils/src"; +import { VuuDataRow } from "@finos/vuu-protocol-types"; +import { buildColumnMap, ColumnMap } from "@finos/vuu-utils"; import { useCallback, useEffect, useMemo, useState } from "react"; import { BasketSelectorProps } from "./basket-selector"; import { NewBasketPanel } from "./new-basket-panel"; diff --git a/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx b/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx deleted file mode 100644 index c4fb71f37..000000000 --- a/vuu-ui/showcase/src/examples/Shell/AppSidePanel.examples.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useVuuTables } from "@finos/vuu-data-react"; -import { - DraggableLayout, - Flexbox, - LayoutProvider, - Placeholder, - View, -} from "@finos/vuu-layout"; -import { SaltProvider } from "@salt-ds/core"; -import { AppSidePanel } from "app-vuu-example/src/app-sidepanel"; -import { useMockFeatureData } from "../utils/mock-data"; -import { useAutoLoginToVuuServer } from "../utils/useAutoLoginToVuuServer"; - -let displaySequence = 1; - -export const DefaultAppSidePanel = () => { - const { features, schemas } = useMockFeatureData(); - return ( - - - - - - - - - - - - - ); -}; -DefaultAppSidePanel.displaySequence = displaySequence++; - -export const VuuConnectedAppSidePanel = () => { - const error = useAutoLoginToVuuServer(); - const tables = useVuuTables(); - const { features } = useMockFeatureData(); - - if (error) { - return {error}; - } - - return ( - - - - - - - - - - - - - ); -}; -VuuConnectedAppSidePanel.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx b/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx index 16dbca06a..fdf36c135 100644 --- a/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/Shell.examples.tsx @@ -1,8 +1,5 @@ import { Shell } from "@finos/vuu-shell"; -import { AppSidePanel } from "app-vuu-example/src/app-sidepanel"; -import { CSSProperties, useMemo } from "react"; -import { useMockFeatureData } from "../utils/mock-data"; -import { useAutoLoginToVuuServer } from "../utils"; +import { CSSProperties } from "react"; import { AutoVuuTable } from "../html/HtmlTable.examples"; import { registerComponent } from "@finos/vuu-layout"; @@ -46,48 +43,3 @@ export const ShellWithDefaultLayout = () => { }; ShellWithDefaultLayout.displaySequence = displaySequence++; - -export const ShellWithLeftPanel = () => { - const { features, schemas } = useMockFeatureData(); - return ( - } - loginUrl={window.location.toString()} - user={user} - style={ - { - "--vuuShell-height": "100%", - "--vuuShell-width": "100%", - } as CSSProperties - } - /> - ); -}; - -ShellWithLeftPanel.displaySequence = displaySequence++; - -export const ShellWithDefaultLayoutAndLeftPanel = () => { - const error = useAutoLoginToVuuServer(); - - const { features, schemas } = useMockFeatureData(); - - if (error) { - return error; - } - - return ( - } - loginUrl={window.location.toString()} - user={user} - style={ - { - "--vuuShell-height": "100%", - "--vuuShell-width": "100%", - } as CSSProperties - } - /> - ); -}; - -ShellWithDefaultLayoutAndLeftPanel.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/Shell/index.ts b/vuu-ui/showcase/src/examples/Shell/index.ts index 37d1ed0c4..a0e1bb7d4 100644 --- a/vuu-ui/showcase/src/examples/Shell/index.ts +++ b/vuu-ui/showcase/src/examples/Shell/index.ts @@ -1,5 +1,4 @@ export * as AppHeader from "./AppHeader.examples"; -export * as AppSidePanel from "./AppSidePanel.examples"; export * as ConnectionStatus from "./ConnectionStatus.examples"; export * as ConnectionMetrics from "./ConnectionMetrics.examples"; export * as Feature from "./Feature.examples"; From 8bab2830a6dbef01bd5f236a69459403b87b54f0 Mon Sep 17 00:00:00 2001 From: heswell Date: Sat, 4 Nov 2023 19:47:37 +0000 Subject: [PATCH 4/4] fix bugs persistning table settings --- vuu-ui/package-lock.json | 29 +---- .../packages/vuu-data/src/json-data-source.ts | 17 ++- .../src/filter-clause/ExpandoCombobox.tsx | 6 +- .../packages/vuu-popups/src/popup/Popup.tsx | 2 +- .../layout-management/useLayoutManager.tsx | 1 + .../vuu-shell/src/left-nav/LeftNav.css | 24 ++-- .../vuu-shell/src/left-nav/LeftNav.tsx | 43 ++++--- .../src/table-next/context-menu/index.ts | 2 +- ...xtMenu.ts => useHandleTableContextMenu.ts} | 2 +- .../src/table-next/header-cell/HeaderCell.css | 1 + .../src/table-next/useTableContextMenu.ts | 2 +- .../vuu-table/src/table-next/useTableModel.ts | 19 +-- .../vuu-table/src/table-next/useTableNext.ts | 116 +++++++++++++----- .../table/context-menu/useTableContextMenu.ts | 5 + .../src/tabstrip/useAnimatedSelectionThumb.ts | 1 - vuu-ui/packages/vuu-utils/src/column-utils.ts | 25 +++- vuu-ui/packages/vuu-utils/src/json-utils.ts | 6 + .../src/useFilterTable.tsx | 2 - .../src/examples/Shell/LeftNav.examples.tsx | 35 ++++++ .../FilterTableFeature.examples.tsx | 50 +++++--- 20 files changed, 260 insertions(+), 128 deletions(-) rename vuu-ui/packages/vuu-table/src/table-next/context-menu/{useTableContextMenu.ts => useHandleTableContextMenu.ts} (99%) diff --git a/vuu-ui/package-lock.json b/vuu-ui/package-lock.json index a5edb95de..15c18686b 100644 --- a/vuu-ui/package-lock.json +++ b/vuu-ui/package-lock.json @@ -2986,10 +2986,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/app-vuu-basket-trader": { - "resolved": "sample-apps/app-vuu-basket-trader", - "link": true - }, "node_modules/app-vuu-example": { "resolved": "sample-apps/app-vuu-example", "link": true @@ -11929,6 +11925,7 @@ "version": "0.0.26", "license": "Apache-2.0", "dependencies": { + "@finos/vuu-filters": "0.0.26", "@finos/vuu-popups": "0.0.26", "@finos/vuu-table": "0.0.26", "@finos/vuu-table-extras": "0.0.26", @@ -12089,6 +12086,7 @@ }, "sample-apps/app-vuu-basket-trader": { "version": "0.0.26", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@finos/vuu-data": "0.0.26", @@ -12123,9 +12121,7 @@ "@finos/vuu-utils": "0.0.26", "@fontsource/open-sans": "^4.5.13", "@salt-ds/core": "1.8.0", - "@salt-ds/icons": "1.5.1", "@salt-ds/lab": "1.0.0-alpha.15", - "@salt-ds/theme": "1.7.1", "classnames": "^2.3.1", "react": "^17.0.2", "react-dom": "^17.0.2" @@ -13219,6 +13215,7 @@ "@finos/vuu-layout": { "version": "file:packages/vuu-layout", "requires": { + "@finos/vuu-filters": "0.0.26", "@finos/vuu-popups": "0.0.26", "@finos/vuu-table": "0.0.26", "@finos/vuu-table-extras": "0.0.26", @@ -14605,24 +14602,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true }, - "app-vuu-basket-trader": { - "version": "file:sample-apps/app-vuu-basket-trader", - "requires": { - "@finos/vuu-data": "0.0.26", - "@finos/vuu-data-react": "0.0.26", - "@finos/vuu-datagrid-types": "0.0.26", - "@finos/vuu-layout": "0.0.26", - "@finos/vuu-popups": "0.0.26", - "@finos/vuu-shell": "0.0.26", - "@finos/vuu-utils": "0.0.26", - "@fontsource/open-sans": "^4.5.13", - "@salt-ds/core": "1.8.0", - "@salt-ds/lab": "1.0.0-alpha.15", - "classnames": "^2.3.1", - "react": "^17.0.2", - "react-dom": "^17.0.2" - } - }, "app-vuu-example": { "version": "file:sample-apps/app-vuu-example", "requires": { @@ -14635,9 +14614,7 @@ "@finos/vuu-utils": "0.0.26", "@fontsource/open-sans": "^4.5.13", "@salt-ds/core": "1.8.0", - "@salt-ds/icons": "1.5.1", "@salt-ds/lab": "1.0.0-alpha.15", - "@salt-ds/theme": "1.7.1", "classnames": "^2.3.1", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/vuu-ui/packages/vuu-data/src/json-data-source.ts b/vuu-ui/packages/vuu-data/src/json-data-source.ts index 23472a741..8bc2e8be8 100644 --- a/vuu-ui/packages/vuu-data/src/json-data-source.ts +++ b/vuu-ui/packages/vuu-data/src/json-data-source.ts @@ -209,6 +209,19 @@ export class JsonDataSource console.log("noop"); return this; } + set data(data: JsonData) { + console.log(`set JsonDataSource data`); + [this.columnDescriptors, this.#data] = jsonToDataSourceRows(data); + this.visibleRows = this.#data + .filter((row) => row[DEPTH] === 0) + .map((row, index) => + ([index, index] as Partial).concat(row.slice(2)) + ) as DataSourceRow[]; + + requestAnimationFrame(() => { + this.sendRowsToClient(); + }); + } select(selected: Selection) { const updatedRows: DataSourceRow[] = []; @@ -324,10 +337,6 @@ export class JsonDataSource this.#aggregations = aggregations; } - set data(data: JsonData) { - console.log(`set JsonDataSource data`); - } - get sort() { return this.#sort; } diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx b/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx index 1b1aa3933..03f201514 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/ExpandoCombobox.tsx @@ -103,8 +103,8 @@ export const ExpandoCombobox = forwardRef(function ExpandoCombobox< displayedItemCount: 10, itemHeight: 22, maxWidth: 300, - minWidth: 100, - width: "auto", + minWidth: 80, + width: "content-width", }, ]; }, [InputPropsProp, handleInputChange, ListPropsProp]); @@ -128,7 +128,7 @@ export const ExpandoCombobox = forwardRef(function ExpandoCombobox< ); const popupProps = { - minWidth: 102, + minWidth: "fit-content", }; return props.source?.length === 0 ? null : ( diff --git a/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx b/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx index 893b7273e..88192586d 100644 --- a/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx +++ b/vuu-ui/packages/vuu-popups/src/popup/Popup.tsx @@ -14,7 +14,7 @@ export type PopupPlacement = export interface PopupComponentProps extends HTMLAttributes { anchorElement: RefObject; - minWidth?: number; + minWidth?: number | string; offsetLeft?: number; offsetTop?: number; placement: PopupPlacement; diff --git a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx index 9785c7c6a..11a9f71f6 100644 --- a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx +++ b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx @@ -72,6 +72,7 @@ export const LayoutManagementProvider = ( const saveApplicationLayout = useCallback( (layout: LayoutJSON) => { + console.log(`save application layout ${JSON.stringify(layout, null, 2)}`); const persistenceManager = getPersistenceManager(); setApplicationLayout(layout, false); persistenceManager.saveApplicationLayout(layout); diff --git a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css index 7cef09867..e05b72301 100644 --- a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css +++ b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.css @@ -14,11 +14,16 @@ --svg-layouts: url('data:image/svg+xml;utf8,'); --vuu-light-text-primary: #15171b; + --menu-level-2-width: 0px; + box-shadow: 3px 4px 4px 0px rgba(0, 0, 0, 0.15); + display: flex; margin-bottom: 4px; + overflow: hidden; position: relative; - transition: width .3s ease-in-out; + transition: width .2s ease-out; z-index: 0; + width: calc(var(--menu-width) + var(--menu-level-2-width)); } @@ -32,10 +37,12 @@ .vuuLeftNav-menu-icons-content { --menu-width: var(--nav-menu-collapsed-width); + --menu-level-2-width: var(--nav-menu-content-width); } .vuuLeftNav-menu-full-content { --menu-width: var(--nav-menu-expanded-width); + --menu-level-2-width: var(--nav-menu-content-width); } .vuuLeftNav-menu-icons-content .vuuLeftNav-menu-secondary, @@ -50,18 +57,22 @@ flex-direction: column; height: 100%; padding: 32px 16px; - transition: width ease .3s; - width: var(--menu-width, 100%); + transition: flex-basis ease-out .2s; + flex-grow:0; + flex-shrink:0; + flex-basis: var(--menu-width); } .vuuLeftNav-menu-secondary { flex: 1 1 auto; height: 100%; display: none; - position: absolute; + /* position: absolute; */ top:0; right: 0; - width: var(--nav-menu-content-width, 240px); + flex-grow:0; + flex-shrink:0; + flex-basis: var(--nav-menu-content-width, 240px); z-index: -1; } @@ -98,9 +109,6 @@ padding: 0; } -/* .vuuLeftNav [data-icon]:after { - transition: left ease .3s; -} */ .vuuLeftNav [data-icon='demo'] { --vuu-icon-svg: var(--svg-demo); diff --git a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx index 722287349..f13540818 100644 --- a/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx +++ b/vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx @@ -13,30 +13,39 @@ import "./LeftNav.css"; const classBase = "vuuLeftNav"; +export type NavDisplayStatus = + | "menu-full" + | "menu-icons" + | "menu-full-content" + | "menu-icons-content"; + +export type NavDisplayStatusHandler = ( + navDisplayStatus: NavDisplayStatus +) => void; interface LeftNavProps extends HTMLAttributes { "data-path"?: string; + defaultActiveTabIndex?: number; + defaultDisplayStatus?: NavDisplayStatus; features: FeatureProps[]; tableFeatures: FeatureProps[]; + onChangeDisplayStatus?: NavDisplayStatusHandler; onResize?: (size: number) => void; sizeCollapsed?: number; sizeContent?: number; sizeExpanded?: number; } -type NavStatus = - | "menu-full" - | "menu-icons" - | "menu-full-content" - | "menu-icons-content"; - type NavState = { activeTabIndex: number; - navStatus: NavStatus; + navStatus: NavDisplayStatus; }; export const LeftNav = ({ "data-path": path, + defaultDisplayStatus = "menu-full", + defaultActiveTabIndex = 0, features, + onChangeDisplayStatus, onResize, sizeCollapsed = 80, sizeContent = 300, @@ -47,13 +56,14 @@ export const LeftNav = ({ }: LeftNavProps) => { const dispatch = useLayoutProviderDispatch(); const [navState, setNavState] = useState({ - activeTabIndex: 0, - navStatus: "menu-full", + activeTabIndex: defaultActiveTabIndex, + navStatus: defaultDisplayStatus, }); const [themeClass] = useThemeAttributes(); + console.log(`navState ${navState}`); const toggleNavWidth = useCallback( - (navStatus: NavStatus) => { + (navStatus: NavDisplayStatus) => { switch (navStatus) { case "menu-icons": return sizeExpanded; @@ -68,7 +78,7 @@ export const LeftNav = ({ [sizeCollapsed, sizeContent, sizeExpanded] ); - const toggleNavStatus = (navStatus: NavStatus) => { + const toggleNavStatus = (navStatus: NavDisplayStatus) => { switch (navStatus) { case "menu-icons": return "menu-full"; @@ -82,7 +92,10 @@ export const LeftNav = ({ }; const getWidthAndStatus = useCallback( - (navState: NavStatus, tabIndex: number): [number, NavStatus] => { + ( + navState: NavDisplayStatus, + tabIndex: number + ): [number, NavDisplayStatus] => { if (tabIndex === 0) { const newNavState = navState === "menu-full-content" @@ -127,16 +140,18 @@ export const LeftNav = ({ const toggleSize = useCallback(() => { const { activeTabIndex, navStatus: currentNavStatus } = navState; + const newNavStatus = toggleNavStatus(currentNavStatus); setNavState({ activeTabIndex, - navStatus: toggleNavStatus(currentNavStatus), + navStatus: newNavStatus, }); dispatch({ type: Action.LAYOUT_RESIZE, path, size: toggleNavWidth(currentNavStatus), } as LayoutResizeAction); - }, [dispatch, navState, path, toggleNavWidth]); + onChangeDisplayStatus?.(newNavStatus); + }, [dispatch, navState, onChangeDisplayStatus, path, toggleNavWidth]); const style = { ...styleProp, diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts index 8c6db9865..9c7d25131 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/index.ts @@ -1,2 +1,2 @@ export * from "./buildContextMenuDescriptors"; -export * from "./useTableContextMenu"; +export * from "./useHandleTableContextMenu"; diff --git a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts similarity index 99% rename from vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts rename to vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts index fc3813a85..70b84b051 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/context-menu/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/context-menu/useHandleTableContextMenu.ts @@ -49,7 +49,7 @@ const removeFilterColumn = ( const { Average, Count, Distinct, High, Low, Sum } = AggregationType; -export const useTableContextMenu = ({ +export const useHandleTableContextMenu = ({ dataSource, onPersistentColumnOperation, }: ContextMenuHookProps) => { diff --git a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css index 8522a0901..e9a9a58cb 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css +++ b/vuu-ui/packages/vuu-table/src/table-next/header-cell/HeaderCell.css @@ -34,6 +34,7 @@ line-height: calc(var(--header-height) - 1px); overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } .vuuTableNextHeaderCell-right .vuuTableNextHeaderCell-label { diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts index 339539b61..945e98938 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableContextMenu.ts @@ -45,7 +45,7 @@ export const useTableContextMenu = ({ }); } }, - [columns, data, dataSource, showContextMenu] + [columns, data, dataSource, getSelectedRows, showContextMenu] ); return onContextMenu; diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts index 4b963d86b..2c0211a87 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableModel.ts @@ -21,7 +21,7 @@ import { isTypeDescriptor, logger, metadataKeys, - moveItem, + replaceColumn, sortPinnedColumns, stripFilterFromColumns, subscribedOnly, @@ -107,7 +107,6 @@ export interface ColumnActionMove { type: "moveColumn"; column: KeyedColumnDescriptor; moveBy?: 1 | -1; - moveTo?: number; } export interface ColumnActionPin { @@ -331,7 +330,8 @@ const columnDescriptorToKeyedColumDescriptor = function moveColumn( state: InternalTableModel, - { column, moveBy, moveTo }: ColumnActionMove + // TODO do we ever use this ? + { column, moveBy }: ColumnActionMove ) { const { columns } = state; if (typeof moveBy === "number") { @@ -343,12 +343,6 @@ function moveColumn( ...state, columns: newColumns, }; - } else if (typeof moveTo === "number") { - const index = columns.indexOf(column); - return { - ...state, - columns: moveItem(columns, index, moveTo), - }; } return state; } @@ -513,10 +507,3 @@ function updateTableConfig( return result; } - -function replaceColumn( - state: KeyedColumnDescriptor[], - column: KeyedColumnDescriptor -) { - return state.map((col) => (col.name === column.name ? column : col)); -} diff --git a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts index 9e8760d92..dafbe4099 100644 --- a/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts +++ b/vuu-ui/packages/vuu-table/src/table-next/useTableNext.ts @@ -24,6 +24,7 @@ import { isJsonGroup, isValidNumber, metadataKeys, + moveColumnTo, updateColumn, visibleColumnAtIndex, } from "@finos/vuu-utils"; @@ -38,17 +39,19 @@ import { } from "react"; import { buildContextMenuDescriptors, + ColumnActionHide, + ColumnActionPin, MeasuredProps, RowClickHandler, TableProps, useSelection, - useTableContextMenu, } from "../table"; import { TableColumnResizeHandler } from "./column-resizing"; import { updateTableConfig } from "./table-config"; import { useDataSource } from "./useDataSource"; import { useInitialValue } from "./useInitialValue"; -import { useTableContextMenu as useTableContextMenuNext } from "./useTableContextMenu"; +import { useTableContextMenu } from "./useTableContextMenu"; +import { useHandleTableContextMenu } from "./context-menu"; import { useCellEditing } from "./useCellEditing"; import { isShowColumnSettings, @@ -144,6 +147,18 @@ export const useTable = ({ }); }, [tableConfig, dataSource.config, dispatchColumnAction]); + const applyTableConfigChange = useCallback( + (config: TableConfig) => { + dispatchColumnAction({ + type: "init", + tableConfig: config, + dataSourceConfig: dataSource.config, + }); + onConfigChange?.(config); + }, + [dataSource.config, dispatchColumnAction, onConfigChange] + ); + const [stateColumns, setStateColumns] = useState(); const [columns, setColumnSize] = useMemo(() => { const setSize = (columnName: string, width: number) => { @@ -202,6 +217,11 @@ export const useTable = ({ const handleConfigChanged = useCallback( (tableConfig: TableConfig) => { + console.log( + `useTableNext handleConfigChanged`, + JSON.stringify(tableConfig, null, 2) + ); + dispatchColumnAction({ type: "init", tableConfig, @@ -222,23 +242,6 @@ export const useTable = ({ [dataSource] ); - const handleCreateCalculatedColumn = useCallback( - (column: ColumnDescriptor) => { - dataSource.columns = dataSource.columns.concat(column.name); - const newTableConfig = addColumn(tableConfig, column); - dispatchColumnAction({ - type: "init", - tableConfig: newTableConfig, - dataSourceConfig: dataSource.config, - }); - console.log(`dispatch onConfigChange`, { - newTableConfig, - }); - onConfigChange?.(newTableConfig); - }, - [dataSource, dispatchColumnAction, onConfigChange, tableConfig] - ); - useEffect(() => { dataSource.on("config", (config, confirmed) => { dispatchColumnAction({ @@ -249,6 +252,42 @@ export const useTable = ({ }); }, [dataSource, dispatchColumnAction]); + const handleCreateCalculatedColumn = useCallback( + (column: ColumnDescriptor) => { + dataSource.columns = dataSource.columns.concat(column.name); + applyTableConfigChange(addColumn(tableConfig, column)); + }, + [dataSource, tableConfig, applyTableConfigChange] + ); + + const hideColumns = useCallback( + (action: ColumnActionHide) => { + const { columns } = action; + const hiddenColumns = columns.map((c) => c.name); + const newTableConfig = { + ...tableConfig, + columns: tableConfig.columns.map((col) => + hiddenColumns.includes(col.name) ? { ...col, hidden: true } : col + ), + }; + applyTableConfigChange(newTableConfig); + }, + [tableConfig, applyTableConfigChange] + ); + + const pinColumn = useCallback( + (action: ColumnActionPin) => { + applyTableConfigChange({ + ...tableConfig, + columns: updateColumn(tableConfig.columns, { + ...action.column, + pin: action.pin, + }), + }); + }, + [tableConfig, applyTableConfigChange] + ); + const { showColumnSettingsPanel, showTableSettingsPanel } = useTableAndColumnSettings({ availableColumns: @@ -271,14 +310,26 @@ export const useTable = ({ } else if (isShowTableSettings(action)) { showTableSettingsPanel(); } else { - // expectConfigChangeRef.current = true; - dispatchColumnAction(action); + switch (action.type) { + case "hideColumns": + return hideColumns(action); + case "pinColumn": + return pinColumn(action); + default: + dispatchColumnAction(action); + } } }, - [dispatchColumnAction, showColumnSettingsPanel, showTableSettingsPanel] + [ + dispatchColumnAction, + hideColumns, + pinColumn, + showColumnSettingsPanel, + showTableSettingsPanel, + ] ); - const handleContextMenuAction = useTableContextMenu({ + const handleContextMenuAction = useHandleTableContextMenu({ dataSource, onPersistentColumnOperation, }); @@ -429,7 +480,7 @@ export const useTable = ({ [navigationKeyDown, editingKeyDown] ); - const onContextMenu = useTableContextMenuNext({ + const onContextMenu = useTableContextMenu({ columns, data, dataSource, @@ -500,16 +551,21 @@ export const useTable = ({ const handleDrop = useCallback( (moveFrom: number, moveTo: number) => { - // onMoveColumn?.(fromIndex, toIndex); - const column = columns[moveFrom]; + const column = tableConfig.columns[moveFrom]; + + const newTableConfig = { + ...tableConfig, + columns: moveColumnTo(tableConfig.columns, column, moveTo), + }; dispatchColumnAction({ - type: "moveColumn", - column, - moveTo, + type: "init", + tableConfig: newTableConfig, + dataSourceConfig: dataSource.config, }); + onConfigChange?.(newTableConfig); }, - [columns, dispatchColumnAction] + [dataSource.config, dispatchColumnAction, onConfigChange, tableConfig] ); const handleDataEdited = useCallback( diff --git a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts index db4ec0320..fc3813a85 100644 --- a/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts +++ b/vuu-ui/packages/vuu-table/src/table/context-menu/useTableContextMenu.ts @@ -21,6 +21,11 @@ export interface ContextMenuOptions { } export interface ContextMenuHookProps { dataSource?: DataSource; + /** + * A persistent Column Operation is any manipulation of a table column that should be + * persisted across user sessions. e.g. if user pins a column, column should still be + * pinned next time user opens app. + */ onPersistentColumnOperation: (action: PersistentColumnAction) => void; } diff --git a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts index beefb4049..03161598e 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/tabstrip/useAnimatedSelectionThumb.ts @@ -40,7 +40,6 @@ export const useAnimatedSelectionThumb = ( isValidNumber(newPosition) && isValidNumber(oldSize) ) { - console.log({ orientation, positionProp, oldPosition, newPosition }); offset = oldPosition - newPosition; size = oldSize; const speed = orientation === "horizontal" ? 1100 : 700; diff --git a/vuu-ui/packages/vuu-utils/src/column-utils.ts b/vuu-ui/packages/vuu-utils/src/column-utils.ts index 3bb469015..17bc6f4b2 100644 --- a/vuu-ui/packages/vuu-utils/src/column-utils.ts +++ b/vuu-ui/packages/vuu-utils/src/column-utils.ts @@ -28,6 +28,7 @@ import type { SchemaColumn } from "@finos/vuu-data"; import type { CSSProperties } from "react"; import type { CellRendererDescriptor } from "./component-registry"; import { isFilterClause, isMultiClauseFilter } from "./filter-utils"; +import { moveItem } from "./array-utils"; /** * ColumnMap provides a lookup of the index position of a data item within a row @@ -614,10 +615,10 @@ export const findColumn = ( } }; -export function updateColumn( - columns: KeyedColumnDescriptor[], - column: KeyedColumnDescriptor -): KeyedColumnDescriptor[]; +export function updateColumn( + columns: T[], + column: T +): T[]; export function updateColumn( columns: KeyedColumnDescriptor[], column: string, @@ -936,3 +937,19 @@ export const setCalculatedColumnExpression = ( name: `${name}:${type}:=${expression}`, }; }; + +export const moveColumnTo = ( + columns: ColumnDescriptor[], + column: ColumnDescriptor, + newIndex: number +) => { + const index = columns.indexOf(column); + return moveItem(columns, index, newIndex); +}; + +export function replaceColumn( + state: KeyedColumnDescriptor[], + column: KeyedColumnDescriptor +) { + return state.map((col) => (col.name === column.name ? column : col)); +} diff --git a/vuu-ui/packages/vuu-utils/src/json-utils.ts b/vuu-ui/packages/vuu-utils/src/json-utils.ts index 71e505675..0ddc5f1ac 100644 --- a/vuu-ui/packages/vuu-utils/src/json-utils.ts +++ b/vuu-ui/packages/vuu-utils/src/json-utils.ts @@ -41,6 +41,12 @@ const getCellValue = ( ): CellValue => { if (isJsonData(attributeValue)) { return { attribute: `${attribute}+`, attributeValue: "", type: "json" }; + } else if (attributeValue === undefined) { + return { + attribute, + attributeValue: "undefined", + type: "string", + }; } else if (isVuuRowDataItem(attributeValue)) { return { attribute, diff --git a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx index e1b011728..073ee41b8 100644 --- a/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx +++ b/vuu-ui/sample-apps/feature-filter-table/src/useFilterTable.tsx @@ -100,7 +100,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => { const handleAvailableColumnsChange = useCallback( (columns: SchemaColumn[]) => { - console.log("save new available columns"); save?.(columns, "available-columns"); }, [save] @@ -108,7 +107,6 @@ export const useFilterTable = ({ tableSchema }: FilterTableFeatureProps) => { const handleTableConfigChange = useCallback( (config: TableConfig) => { - console.log(`table config changed`); save?.(config, "table-config"); }, [save] diff --git a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx index aa00a8c25..0790df5b9 100644 --- a/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx +++ b/vuu-ui/showcase/src/examples/Shell/LeftNav.examples.tsx @@ -6,3 +6,38 @@ export const VerticalTabstrip = () => { return ; }; VerticalTabstrip.displaySequence = displaySequence++; + +export const VerticalTabstripCollapsed = () => { + return ( + + ); +}; +VerticalTabstripCollapsed.displaySequence = displaySequence++; + +export const VerticalTabstripCollapsedContent = () => { + return ( + + ); +}; +VerticalTabstripCollapsedContent.displaySequence = displaySequence++; + +export const VerticalTabstripContent = () => { + return ( + + ); +}; +VerticalTabstripContent.displaySequence = displaySequence++; diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx index 551ddf9c3..3d5d6a432 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx @@ -6,9 +6,11 @@ import { View, } from "@finos/vuu-layout"; import { Feature, FeatureProps, useLayoutManager } from "@finos/vuu-shell"; -import { useCallback, useEffect } from "react"; +import { useCallback, useState } from "react"; import { FilterTableFeature } from "../../features/FilterTable.feature"; import { VuuBlotterHeader } from "./VuuBlotterHeader"; +import { JsonTable } from "@finos/vuu-datatable"; +import { JsonData } from "packages/vuu-utils/src"; registerComponent("FilterTableFeature", FilterTableFeature, "view"); @@ -24,31 +26,47 @@ export const DefaultFilterTableFeature = () => { //----------------------------------------------------------------------------------- const { applicationLayout, saveApplicationLayout } = useLayoutManager(); + // Save layout into state so we can display in JsonTable + const [savedLayoutJson, setSavedLayoutJson] = useState(applicationLayout); + const handleLayoutChange = useCallback( (layout) => { saveApplicationLayout(layout); + setSavedLayoutJson(layout); }, [saveApplicationLayout] ); // ---------------------------------------------------------------------------------- return ( - - + - - - + + + + +
+ +
+ ); }; DefaultFilterTableFeature.displaySequence = displaySequence++;