Merge branch 'master' into users/languye/improve-filter-view
This commit is contained in:
commit
1f4d0f21f0
|
@ -76,6 +76,10 @@ module.exports = {
|
|||
"^dnd-core$": "dnd-core/dist/cjs",
|
||||
"^react-dnd$": "react-dnd/dist/cjs",
|
||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
|
||||
"d3-force": "<rootDir>/node_modules/d3-force/dist/d3-force.min.js",
|
||||
"d3-quadtree": "<rootDir>/node_modules/d3-quadtree/dist/d3-quadtree.min.js",
|
||||
"d3-scale-chromatic": "<rootDir>/node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js",
|
||||
"d3-zoom": "<rootDir>/node_modules/d3-zoom/dist/d3-zoom.min.js",
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
|
@ -130,7 +134,6 @@ module.exports = {
|
|||
|
||||
// The test environment that will be used for testing
|
||||
// testEnvironment: "jest-environment-jsdom",
|
||||
|
||||
modulePaths: ["node_modules", "<rootDir>/src"],
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
"copy-webpack-plugin": "11.0.0",
|
||||
"crossroads": "0.12.2",
|
||||
"css-element-queries": "1.1.1",
|
||||
"d3": "6.1.1",
|
||||
"d3": "7.8.5",
|
||||
"datatables.net-colreorder-dt": "1.7.0",
|
||||
"datatables.net-dt": "1.13.8",
|
||||
"date-fns": "1.29.0",
|
||||
|
@ -194,14 +194,14 @@
|
|||
"sinon": "3.2.1",
|
||||
"style-loader": "0.23.0",
|
||||
"ts-loader": "9.2.4",
|
||||
"typedoc": "0.21.5",
|
||||
"typedoc": "0.22.15",
|
||||
"typescript": "4.3.5",
|
||||
"url-loader": "4.1.1",
|
||||
"wait-on": "4.0.2",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-bundle-analyzer": "4.9.1",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1"
|
||||
"webpack-dev-server": "4.15.2"
|
||||
}
|
||||
},
|
||||
"canvas": {
|
||||
|
@ -17283,40 +17283,43 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-6.1.1.tgz",
|
||||
"integrity": "sha512-bJYW9wlS2uvP2EoMkcPptrUzLMHQKCbiSW+/la8iGSLZgs4KbI/f3Fch4RtnUA9PA+/nPlwyFYzTwDjX80Of8w==",
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
|
||||
"integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
|
||||
"dependencies": {
|
||||
"d3-array": "2",
|
||||
"d3-axis": "2",
|
||||
"d3-brush": "2",
|
||||
"d3-chord": "2",
|
||||
"d3-color": "2",
|
||||
"d3-contour": "2",
|
||||
"d3-delaunay": "5",
|
||||
"d3-dispatch": "2",
|
||||
"d3-drag": "2",
|
||||
"d3-dsv": "2",
|
||||
"d3-ease": "2",
|
||||
"d3-fetch": "2",
|
||||
"d3-force": "2",
|
||||
"d3-format": "2",
|
||||
"d3-geo": "2",
|
||||
"d3-hierarchy": "2",
|
||||
"d3-interpolate": "2",
|
||||
"d3-path": "2",
|
||||
"d3-polygon": "2",
|
||||
"d3-quadtree": "2",
|
||||
"d3-random": "2",
|
||||
"d3-scale": "3",
|
||||
"d3-scale-chromatic": "2",
|
||||
"d3-selection": "2",
|
||||
"d3-shape": "2",
|
||||
"d3-time": "2",
|
||||
"d3-time-format": "3",
|
||||
"d3-timer": "2",
|
||||
"d3-transition": "2",
|
||||
"d3-zoom": "2"
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "4",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
|
@ -17328,9 +17331,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3-axis": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-2.1.0.tgz",
|
||||
"integrity": "sha512-z/G2TQMyuf0X3qP+Mh+2PimoJD41VOCjViJzT0BHeL/+JQAofkiWZbWxlwFGb1N8EN+Cl/CW+MUKbVzr1689Cw=="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-brush": {
|
||||
"version": "2.1.0",
|
||||
|
@ -17345,11 +17351,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3-chord": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-2.0.0.tgz",
|
||||
"integrity": "sha512-D5PZb7EDsRNdGU4SsjQyKhja8Zgu+SHZfUSO5Ls8Wsn+jsAKUUGkcshLxMg9HDFxG3KqavGWaWkJ8EpU8ojuig==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 2"
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-collection": {
|
||||
|
@ -17363,19 +17372,36 @@
|
|||
"integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
|
||||
},
|
||||
"node_modules/d3-contour": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-2.0.0.tgz",
|
||||
"integrity": "sha512-9unAtvIaNk06UwqBmvsdHX7CZ+NPDZnn8TtNH1myW93pWJkhsV25JcgnYAu0Ck5Veb1DHiCv++Ic5uvJ+h50JA==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
|
||||
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
||||
"dependencies": {
|
||||
"d3-array": "2"
|
||||
"d3-array": "^3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-contour/node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-5.3.0.tgz",
|
||||
"integrity": "sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||
"dependencies": {
|
||||
"delaunator": "4"
|
||||
"delaunator": "5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
|
@ -17393,24 +17419,46 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-2.0.0.tgz",
|
||||
"integrity": "sha512-E+Pn8UJYx9mViuIUkoc93gJGGYut6mSDKy2+XaPwccwkRGlR+LO97L2VCCRjQivTwLHkSnAJG7yo00BWY6QM+w==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"dependencies": {
|
||||
"commander": "2",
|
||||
"iconv-lite": "0.4",
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"bin": {
|
||||
"csv2json": "bin/dsv2json",
|
||||
"csv2tsv": "bin/dsv2dsv",
|
||||
"dsv2dsv": "bin/dsv2dsv",
|
||||
"dsv2json": "bin/dsv2json",
|
||||
"json2csv": "bin/json2dsv",
|
||||
"json2dsv": "bin/json2dsv",
|
||||
"json2tsv": "bin/json2dsv",
|
||||
"tsv2csv": "bin/dsv2dsv",
|
||||
"tsv2json": "bin/dsv2json"
|
||||
"csv2json": "bin/dsv2json.js",
|
||||
"csv2tsv": "bin/dsv2dsv.js",
|
||||
"dsv2dsv": "bin/dsv2dsv.js",
|
||||
"dsv2json": "bin/dsv2json.js",
|
||||
"json2csv": "bin/json2dsv.js",
|
||||
"json2dsv": "bin/json2dsv.js",
|
||||
"json2tsv": "bin/json2dsv.js",
|
||||
"tsv2csv": "bin/dsv2dsv.js",
|
||||
"tsv2json": "bin/dsv2json.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
|
@ -17419,21 +17467,27 @@
|
|||
"integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
|
||||
},
|
||||
"node_modules/d3-fetch": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-2.0.0.tgz",
|
||||
"integrity": "sha512-TkYv/hjXgCryBeNKiclrwqZH7Nb+GaOwo3Neg24ZVWA3MKB+Rd+BY84Nh6tmNEMcjUik1CSUWjXYndmeO6F7sw==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"dependencies": {
|
||||
"d3-dsv": "1 - 2"
|
||||
"d3-dsv": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-2.1.1.tgz",
|
||||
"integrity": "sha512-nAuHEzBqMvpFVMf9OX75d00OxvOXdxY+xECIXjW6Gv8BRrXu6gAWbv/9XKrvfJ5i5DCokDW7RYE50LRoK092ew==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 2",
|
||||
"d3-quadtree": "1 - 2",
|
||||
"d3-timer": "1 - 2"
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
|
@ -17442,11 +17496,14 @@
|
|||
"integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-2.0.2.tgz",
|
||||
"integrity": "sha512-8pM1WGMLGFuhq9S+FpPURxic+gKzjluCD/CHTuUF3mXMeiCo0i6R0tO1s4+GArRFde96SLcW/kOFRjoAosPsFA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "^2.5.0"
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-glyphedge": {
|
||||
|
@ -17460,9 +17517,12 @@
|
|||
"integrity": "sha512-KS3fUT2ReD4RlGCjvCEm1RgMtp2NFZumdMu4DBzQK8AZv3fXRM6Xm8I4fSU07UXvH4xxg03NwWKWdvxfS/yc4w=="
|
||||
},
|
||||
"node_modules/d3-hierarchy": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz",
|
||||
"integrity": "sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw=="
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "2.0.1",
|
||||
|
@ -17473,9 +17533,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
|
||||
"integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path-arrows": {
|
||||
"version": "0.4.0",
|
||||
|
@ -17497,19 +17560,28 @@
|
|||
"integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg=="
|
||||
},
|
||||
"node_modules/d3-polygon": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-2.0.0.tgz",
|
||||
"integrity": "sha512-MsexrCK38cTGermELs0cO1d79DcTsQRN7IWMJKczD/2kBjzNXxLUWP33qRF6VDpiLV/4EI4r6Gs0DAWQkE8pSQ=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-2.0.0.tgz",
|
||||
"integrity": "sha512-b0Ed2t1UUalJpc3qXzKi+cPGxeXRr4KU9YSlocN74aTzp6R/Ud43t79yLLqxHRWZfsvWXmbDWPpoENK1K539xw=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-random": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz",
|
||||
"integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-sankey-circular": {
|
||||
"version": "0.25.0",
|
||||
|
@ -17539,12 +17611,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz",
|
||||
"integrity": "sha512-LLqy7dJSL8yDy7NRmf6xSlsFZ6zYvJ4BcWFE4zBrOPnQERv9zj24ohnXKRbyi9YHnYV+HN1oEO3iFK971/gkzA==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 2",
|
||||
"d3-interpolate": "1 - 2"
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
|
@ -17607,23 +17682,181 @@
|
|||
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz",
|
||||
"integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 2",
|
||||
"d3-drag": "2",
|
||||
"d3-interpolate": "1 - 2",
|
||||
"d3-selection": "2",
|
||||
"d3-transition": "2"
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-shape": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz",
|
||||
"integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 2"
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3/node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
|
@ -18045,9 +18278,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz",
|
||||
"integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag=="
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
|
@ -21476,36 +21712,6 @@
|
|||
"integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5",
|
||||
"neo-async": "^2.6.2",
|
||||
"source-map": "^0.6.1",
|
||||
"wordwrap": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"handlebars": "bin/handlebars"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.7"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"uglify-js": "^3.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/handlebars/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
|
@ -28961,9 +29167,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
|
||||
"integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
|
@ -29764,15 +29970,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
|
||||
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
|
||||
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"marked": "bin/marked"
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/martinez-polygon-clipping": {
|
||||
|
@ -35940,6 +36146,11 @@
|
|||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
|
||||
},
|
||||
"node_modules/rst-selector-parser": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
||||
|
@ -36685,9 +36896,9 @@
|
|||
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz",
|
||||
"integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==",
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
|
||||
"integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^3.0.0",
|
||||
|
@ -38612,19 +38823,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.5.tgz",
|
||||
"integrity": "sha512-uRDRmYheE5Iju9Zz0X50pTASTpBorIHFt02F5Y8Dt4eBt55h3mwk1CBSY2+EfwBxY16N4Xm7f8KXhnfFZ0AmBw==",
|
||||
"version": "0.22.15",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz",
|
||||
"integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.7",
|
||||
"handlebars": "^4.7.7",
|
||||
"glob": "^7.2.0",
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^2.1.1",
|
||||
"minimatch": "^3.0.0",
|
||||
"progress": "^2.0.3",
|
||||
"shiki": "^0.9.3",
|
||||
"typedoc-default-themes": "^0.12.10"
|
||||
"marked": "^4.0.12",
|
||||
"minimatch": "^5.0.1",
|
||||
"shiki": "^0.10.1"
|
||||
},
|
||||
"bin": {
|
||||
"typedoc": "bin/typedoc"
|
||||
|
@ -38633,16 +38841,28 @@
|
|||
"node": ">= 12.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x"
|
||||
"typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-default-themes": {
|
||||
"version": "0.12.10",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz",
|
||||
"integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==",
|
||||
"node_modules/typedoc/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc/node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
|
@ -39655,9 +39875,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
|
||||
"integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
|
@ -39731,9 +39951,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
||||
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
||||
"version": "4.15.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
|
||||
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.9",
|
||||
|
@ -39764,7 +39984,7 @@
|
|||
"serve-index": "^1.9.1",
|
||||
"sockjs": "^0.3.24",
|
||||
"spdy": "^4.0.2",
|
||||
"webpack-dev-middleware": "^5.3.1",
|
||||
"webpack-dev-middleware": "^5.3.4",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -40241,12 +40461,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"copy-webpack-plugin": "11.0.0",
|
||||
"crossroads": "0.12.2",
|
||||
"css-element-queries": "1.1.1",
|
||||
"d3": "6.1.1",
|
||||
"d3": "7.8.5",
|
||||
"datatables.net-colreorder-dt": "1.7.0",
|
||||
"datatables.net-dt": "1.13.8",
|
||||
"date-fns": "1.29.0",
|
||||
|
@ -189,14 +189,14 @@
|
|||
"sinon": "3.2.1",
|
||||
"style-loader": "0.23.0",
|
||||
"ts-loader": "9.2.4",
|
||||
"typedoc": "0.21.5",
|
||||
"typedoc": "0.22.15",
|
||||
"typescript": "4.3.5",
|
||||
"url-loader": "4.1.1",
|
||||
"wait-on": "4.0.2",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-bundle-analyzer": "4.9.1",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1"
|
||||
"webpack-dev-server": "4.15.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
|
|
|
@ -677,6 +677,7 @@ export function useMongoProxyEndpoint(api: string): boolean {
|
|||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
];
|
||||
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
|
||||
if (
|
||||
|
|
|
@ -142,7 +142,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
|||
<Image
|
||||
{...imageProps}
|
||||
src={EditIcon}
|
||||
alt="editEntity"
|
||||
alt={`Edit ${entityProperty} entity`}
|
||||
onClick={onEditEntity}
|
||||
tabIndex={0}
|
||||
onKeyPress={handleKeyPress}
|
||||
|
@ -156,7 +156,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
|||
<Image
|
||||
{...imageProps}
|
||||
src={DeleteIcon}
|
||||
alt="delete entity"
|
||||
alt={`Delete ${entityProperty} entity`}
|
||||
id="deleteEntity"
|
||||
onClick={onDeleteEntity}
|
||||
tabIndex={0}
|
||||
|
|
|
@ -68,10 +68,6 @@ export interface OpenPane extends DataExplorerAction {
|
|||
paneKind: PaneKind | string;
|
||||
}
|
||||
|
||||
export interface OpenSampleNotebook extends DataExplorerAction {
|
||||
path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of actions that the DataExplorer supports performing upon opening.
|
||||
*/
|
||||
|
@ -80,5 +76,4 @@ export enum ActionType {
|
|||
OpenCollectionTab,
|
||||
OpenPane,
|
||||
TransmitCachedData,
|
||||
OpenSampleNotebook,
|
||||
}
|
||||
|
|
|
@ -29,8 +29,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"computedProperties": [Function],
|
||||
"conflictResolutionPolicy": [Function],
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -47,10 +45,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
},
|
||||
|
@ -107,8 +103,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"computedProperties": [Function],
|
||||
"conflictResolutionPolicy": [Function],
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -125,10 +119,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
},
|
||||
|
@ -224,8 +216,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"computedProperties": [Function],
|
||||
"conflictResolutionPolicy": [Function],
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -242,10 +232,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
},
|
||||
|
@ -271,8 +259,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||
}
|
||||
explorer={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -289,10 +275,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Link } from "@fluentui/react/lib/Link";
|
||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
|
@ -16,7 +15,7 @@ import shallow from "zustand/shallow";
|
|||
import { AuthType } from "../AuthType";
|
||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook, PoolIdType } from "../Common/Constants";
|
||||
import { Areas, ConnectionStatusType, HttpStatusCodes, PoolIdType } from "../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { QueriesClient } from "../Common/QueriesClient";
|
||||
|
@ -32,34 +31,23 @@ import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants"
|
|||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||
import { isAccountNewerThanThresholdInMs, userContext } from "../UserContext";
|
||||
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
||||
import { stringToBlob } from "../Utils/BlobUtils";
|
||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
|
||||
import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||
import { listByDatabaseAccount } from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
import { useTabs } from "../hooks/useTabs";
|
||||
import "./ComponentRegisterer";
|
||||
import { DialogProps, useDialog } from "./Controls/Dialog";
|
||||
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
||||
import { SnapshotRequest } from "./Notebook/NotebookComponent/types";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
||||
import { NotebookContentItem } from "./Notebook/NotebookContentItem";
|
||||
import type NotebookManager from "./Notebook/NotebookManager";
|
||||
import { NotebookPaneContent } from "./Notebook/NotebookManager";
|
||||
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
||||
import { useNotebook } from "./Notebook/useNotebook";
|
||||
import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
|
||||
import { CassandraAddCollectionPane } from "./Panes/CassandraAddCollectionPane/CassandraAddCollectionPane";
|
||||
import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane";
|
||||
import { StringInputPane } from "./Panes/StringInputPane/StringInputPane";
|
||||
import { UploadFilePane } from "./Panes/UploadFilePane/UploadFilePane";
|
||||
import { UploadItemsPane } from "./Panes/UploadItemsPane/UploadItemsPane";
|
||||
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
|
||||
import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab";
|
||||
import TabsBase from "./Tabs/TabsBase";
|
||||
import TerminalTab from "./Tabs/TerminalTab";
|
||||
import Database from "./Tree/Database";
|
||||
|
@ -87,7 +75,6 @@ export default class Explorer {
|
|||
// Notebooks
|
||||
public notebookManager?: NotebookManager;
|
||||
|
||||
private _isInitializingNotebooks: boolean;
|
||||
private notebookToImport: {
|
||||
name: string;
|
||||
content: string;
|
||||
|
@ -99,7 +86,6 @@ export default class Explorer {
|
|||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
});
|
||||
this._isInitializingNotebooks = false;
|
||||
|
||||
this.phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
|
||||
useNotebook.subscribe(
|
||||
|
@ -205,12 +191,10 @@ export default class Explorer {
|
|||
container: this,
|
||||
resourceTree: this.resourceTree,
|
||||
refreshCommandBarButtons: () => this.refreshCommandBarButtons(),
|
||||
refreshNotebookList: () => this.refreshNotebookList(),
|
||||
});
|
||||
}
|
||||
|
||||
this.refreshCommandBarButtons();
|
||||
this.refreshNotebookList();
|
||||
}
|
||||
|
||||
public openEnableSynapseLinkDialog(): void {
|
||||
|
@ -373,7 +357,6 @@ export default class Explorer {
|
|||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
this.refreshNotebookList();
|
||||
};
|
||||
|
||||
// Facade
|
||||
|
@ -381,19 +364,6 @@ export default class Explorer {
|
|||
window.open(Constants.Urls.feedbackEmail, "_blank");
|
||||
};
|
||||
|
||||
public async initNotebooks(databaseAccount: DataModels.DatabaseAccount): Promise<void> {
|
||||
if (!databaseAccount) {
|
||||
throw new Error("No database account specified");
|
||||
}
|
||||
|
||||
if (this._isInitializingNotebooks) {
|
||||
return;
|
||||
}
|
||||
this._isInitializingNotebooks = true;
|
||||
this.refreshNotebookList();
|
||||
this._isInitializingNotebooks = false;
|
||||
}
|
||||
|
||||
public async allocateContainer(poolId: PoolIdType, mode?: string): Promise<void> {
|
||||
const shouldUseNotebookStates = poolId === PoolIdType.DefaultPoolId ? true : false;
|
||||
const notebookServerInfo = shouldUseNotebookStates
|
||||
|
@ -472,8 +442,6 @@ export default class Explorer {
|
|||
? useNotebook.getState().setIsAllocating(false)
|
||||
: useQueryCopilot.getState().setIsAllocatingContainer(false);
|
||||
this.refreshCommandBarButtons();
|
||||
this.refreshNotebookList();
|
||||
this._isInitializingNotebooks = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -510,104 +478,6 @@ export default class Explorer {
|
|||
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
|
||||
}
|
||||
|
||||
public resetNotebookWorkspace(): void {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookClient) {
|
||||
handleError(
|
||||
"Attempt to reset notebook workspace, but notebook is not enabled",
|
||||
"Explorer/resetNotebookWorkspace",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const dialogContent = useNotebook.getState().isPhoenixNotebooks
|
||||
? "Notebooks saved in the temporary workspace will be deleted. Do you want to proceed?"
|
||||
: "This lets you keep your notebook files and the workspace will be restored to default. Proceed anyway?";
|
||||
|
||||
const resetConfirmationDialogProps: DialogProps = {
|
||||
isModal: true,
|
||||
title: "Reset Workspace",
|
||||
subText: dialogContent,
|
||||
primaryButtonText: "OK",
|
||||
secondaryButtonText: "Cancel",
|
||||
onPrimaryButtonClick: this._resetNotebookWorkspace,
|
||||
onSecondaryButtonClick: () => useDialog.getState().closeDialog(),
|
||||
};
|
||||
useDialog.getState().openDialog(resetConfirmationDialogProps);
|
||||
}
|
||||
|
||||
private async _containsDefaultNotebookWorkspace(databaseAccount: DataModels.DatabaseAccount): Promise<boolean> {
|
||||
if (!databaseAccount) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const { value: workspaces } = await listByDatabaseAccount(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
userContext.databaseAccount.name,
|
||||
);
|
||||
return workspaces && workspaces.length > 0 && workspaces.some((workspace) => workspace.name === "default");
|
||||
} catch (error) {
|
||||
Logger.logError(getErrorMessage(error), "Explorer/_containsDefaultNotebookWorkspace");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private _resetNotebookWorkspace = async () => {
|
||||
useDialog.getState().closeDialog();
|
||||
const clearInProgressMessage = logConsoleProgress("Resetting notebook workspace");
|
||||
let connectionStatus: ContainerConnectionInfo;
|
||||
try {
|
||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||
if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
|
||||
const error = "No server endpoint detected";
|
||||
Logger.logError(error, "NotebookContainerClient/resetWorkspace");
|
||||
logConsoleError(error);
|
||||
return;
|
||||
}
|
||||
TelemetryProcessor.traceStart(Action.PhoenixResetWorkspace, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
useTabs.getState().closeAllNotebookTabs(true);
|
||||
connectionStatus = {
|
||||
status: ConnectionStatusType.Connecting,
|
||||
};
|
||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||
}
|
||||
const connectionInfo = await this.notebookManager?.notebookClient.resetWorkspace();
|
||||
if (connectionInfo?.status !== HttpStatusCodes.OK) {
|
||||
throw new Error(`Reset Workspace: Received status code- ${connectionInfo?.status}`);
|
||||
}
|
||||
if (!connectionInfo?.data?.phoenixServiceUrl) {
|
||||
throw new Error(`Reset Workspace: PhoenixServiceUrl is invalid!`);
|
||||
}
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.setNotebookInfo(true, connectionInfo, connectionStatus);
|
||||
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
|
||||
}
|
||||
logConsoleInfo("Successfully reset notebook workspace");
|
||||
TelemetryProcessor.traceSuccess(Action.PhoenixResetWorkspace, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
} catch (error) {
|
||||
logConsoleError(`Failed to reset notebook workspace: ${error}`);
|
||||
TelemetryProcessor.traceFailure(Action.PhoenixResetWorkspace, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
});
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
connectionStatus = {
|
||||
status: ConnectionStatusType.Failed,
|
||||
};
|
||||
useNotebook.getState().resetContainerConnection(connectionStatus);
|
||||
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearInProgressMessage();
|
||||
}
|
||||
};
|
||||
|
||||
private getDeltaDatabases(
|
||||
updatedDatabaseList: DataModels.Database[],
|
||||
databases: ViewModels.Database[],
|
||||
|
@ -696,406 +566,6 @@ export default class Explorer {
|
|||
}
|
||||
}
|
||||
|
||||
public uploadFile(
|
||||
name: string,
|
||||
content: string,
|
||||
parent: NotebookContentItem,
|
||||
isGithubTree?: boolean,
|
||||
): Promise<NotebookContentItem> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to upload notebook, but notebook is not enabled";
|
||||
handleError(error, "Explorer/uploadFile");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent, isGithubTree);
|
||||
promise
|
||||
.then(() => this.resourceTree.triggerRender())
|
||||
.catch((reason) => useDialog.getState().showOkModalDialog("Unable to upload file", getErrorMessage(reason)));
|
||||
return promise;
|
||||
}
|
||||
|
||||
public async importAndOpen(path: string): Promise<boolean> {
|
||||
const name = NotebookUtil.getName(path);
|
||||
const item = NotebookUtil.createNotebookContentItem(name, path, "file");
|
||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||
|
||||
if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) {
|
||||
const existingItem = _.find(parent.children, (node) => node.name === name);
|
||||
if (existingItem) {
|
||||
return this.openNotebook(existingItem);
|
||||
}
|
||||
|
||||
const content = await this.readFile(item);
|
||||
const uploadedItem = await this.uploadFile(name, content, parent);
|
||||
return this.openNotebook(uploadedItem);
|
||||
}
|
||||
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||
|
||||
if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) {
|
||||
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
||||
this.notebookToImport = undefined; // we don't want to try opening this notebook again
|
||||
}
|
||||
|
||||
const existingItem = _.find(parent.children, (node) => node.name === name);
|
||||
if (existingItem) {
|
||||
return this.openNotebook(existingItem);
|
||||
}
|
||||
|
||||
const uploadedItem = await this.uploadFile(name, content, parent);
|
||||
return this.openNotebook(uploadedItem);
|
||||
}
|
||||
|
||||
this.notebookToImport = { name, content }; // we'll try opening this notebook later on
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
public async publishNotebook(
|
||||
name: string,
|
||||
content: NotebookPaneContent,
|
||||
notebookContentRef?: string,
|
||||
onTakeSnapshot?: (request: SnapshotRequest) => void,
|
||||
onClosePanel?: () => void,
|
||||
): Promise<void> {
|
||||
if (this.notebookManager) {
|
||||
await this.notebookManager.openPublishNotebookPane(
|
||||
name,
|
||||
content,
|
||||
notebookContentRef,
|
||||
onTakeSnapshot,
|
||||
onClosePanel,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public copyNotebook(name: string, content: string): void {
|
||||
this.notebookManager?.openCopyNotebookPane(name, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: To keep it simple, this creates a disconnected NotebookContentItem that is not connected to the resource tree.
|
||||
* Connecting it to a tree possibly requires the intermediate missing folders if the item is nested in a subfolder.
|
||||
* Manually creating the missing folders between the root and its parent dir would break the UX: expanding a folder
|
||||
* will not fetch its content if the children array exists (and has only one child which was manually created).
|
||||
* Fetching the intermediate folders possibly involves a few chained async calls which isn't ideal.
|
||||
*
|
||||
* @param name
|
||||
* @param path
|
||||
*/
|
||||
public createNotebookContentItemFile(name: string, path: string): NotebookContentItem {
|
||||
return NotebookUtil.createNotebookContentItem(name, path, "file");
|
||||
}
|
||||
|
||||
public async openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean> {
|
||||
if (!notebookContentItem || !notebookContentItem.path) {
|
||||
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
||||
}
|
||||
if (notebookContentItem.type === NotebookContentItemType.Notebook && useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
}
|
||||
|
||||
const notebookTabs = useTabs
|
||||
.getState()
|
||||
.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab) =>
|
||||
(tab as NotebookV2Tab).notebookPath &&
|
||||
FileSystemUtil.isPathEqual((tab as NotebookV2Tab).notebookPath(), notebookContentItem.path),
|
||||
) as NotebookV2Tab[];
|
||||
let notebookTab = notebookTabs && notebookTabs[0];
|
||||
|
||||
if (notebookTab) {
|
||||
useTabs.getState().activateTab(notebookTab);
|
||||
} else {
|
||||
const options: NotebookTabOptions = {
|
||||
account: userContext.databaseAccount,
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookV2,
|
||||
node: undefined,
|
||||
title: notebookContentItem.name,
|
||||
tabPath: notebookContentItem.path,
|
||||
collection: undefined,
|
||||
masterKey: userContext.masterKey || "",
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: undefined,
|
||||
container: this,
|
||||
notebookContentItem,
|
||||
};
|
||||
|
||||
try {
|
||||
const NotebookTabV2 = await import(/* webpackChunkName: "NotebookV2Tab" */ "./Tabs/NotebookV2Tab");
|
||||
notebookTab = new NotebookTabV2.default(options);
|
||||
useTabs.getState().activateNewTab(notebookTab);
|
||||
} catch (reason) {
|
||||
console.error("Import NotebookV2Tab failed!", reason);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public renameNotebook(notebookFile: NotebookContentItem, isGithubTree?: boolean): void {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to rename notebook, but notebook is not enabled";
|
||||
handleError(error, "Explorer/renameNotebook");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
// Don't delete if tab is open to avoid accidental deletion
|
||||
const openedNotebookTabs = useTabs
|
||||
.getState()
|
||||
.getTabs(ViewModels.CollectionTabKind.NotebookV2, (tab: NotebookV2Tab) => {
|
||||
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), notebookFile.path);
|
||||
});
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
|
||||
} else {
|
||||
useSidePanel.getState().openSidePanel(
|
||||
"Rename Notebook",
|
||||
<StringInputPane
|
||||
closePanel={() => {
|
||||
useSidePanel.getState().closeSidePanel();
|
||||
this.resourceTree.triggerRender();
|
||||
}}
|
||||
inputLabel="Enter new notebook name"
|
||||
submitButtonLabel="Rename"
|
||||
errorMessage="Could not rename notebook"
|
||||
inProgressMessage="Renaming notebook to"
|
||||
successMessage="Renamed notebook to"
|
||||
paneTitle="Rename Notebook"
|
||||
defaultInput={FileSystemUtil.stripExtension(notebookFile.name, "ipynb")}
|
||||
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
|
||||
this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input, isGithubTree)
|
||||
}
|
||||
notebookFile={notebookFile}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public onCreateDirectory(parent: NotebookContentItem, isGithubTree?: boolean): void {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
||||
handleError(error, "Explorer/onCreateDirectory");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
useSidePanel.getState().openSidePanel(
|
||||
"Create new directory",
|
||||
<StringInputPane
|
||||
closePanel={() => {
|
||||
useSidePanel.getState().closeSidePanel();
|
||||
this.resourceTree.triggerRender();
|
||||
}}
|
||||
errorMessage="Could not create directory "
|
||||
inProgressMessage="Creating directory "
|
||||
successMessage="Created directory "
|
||||
inputLabel="Enter new directory name"
|
||||
paneTitle="Create new directory"
|
||||
submitButtonLabel="Create"
|
||||
defaultInput=""
|
||||
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
|
||||
this.notebookManager?.notebookContentClient.createDirectory(notebookFile, input, isGithubTree)
|
||||
}
|
||||
notebookFile={parent}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to read file, but notebook is not enabled";
|
||||
handleError(error, "Explorer/downloadFile");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path);
|
||||
}
|
||||
|
||||
public downloadFile(notebookFile: NotebookContentItem): Promise<void> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to download file, but notebook is not enabled";
|
||||
handleError(error, "Explorer/downloadFile");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Downloading ${notebookFile.path}`);
|
||||
|
||||
return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then(
|
||||
(content: string) => {
|
||||
const blob = stringToBlob(content, "text/plain");
|
||||
if (navigator.msSaveBlob) {
|
||||
// for IE and Edge
|
||||
navigator.msSaveBlob(blob, notebookFile.name);
|
||||
} else {
|
||||
const downloadLink: HTMLAnchorElement = document.createElement("a");
|
||||
const url = URL.createObjectURL(blob);
|
||||
downloadLink.href = url;
|
||||
downloadLink.target = "_self";
|
||||
downloadLink.download = notebookFile.name;
|
||||
|
||||
// for some reason, FF displays the download prompt only when
|
||||
// the link is added to the dom so we add and remove it
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
downloadLink.remove();
|
||||
}
|
||||
|
||||
clearMessage();
|
||||
},
|
||||
(error) => {
|
||||
logConsoleError(`Could not download notebook ${getErrorMessage(error)}`);
|
||||
clearMessage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private refreshNotebookList = async (): Promise<void> => {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.resourceTree.initialize();
|
||||
await useNotebook.getState().initializeNotebooksTree(this.notebookManager);
|
||||
|
||||
this.notebookManager?.refreshPinnedRepos();
|
||||
if (this.notebookToImport) {
|
||||
this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content);
|
||||
}
|
||||
};
|
||||
|
||||
public deleteNotebookFile(item: NotebookContentItem, isGithubTree?: boolean): Promise<void> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to delete notebook file, but notebook is not enabled";
|
||||
handleError(error, "Explorer/deleteNotebookFile");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
// Don't delete if tab is open to avoid accidental deletion
|
||||
const openedNotebookTabs = useTabs
|
||||
.getState()
|
||||
.getTabs(ViewModels.CollectionTabKind.NotebookV2, (tab: NotebookV2Tab) => {
|
||||
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), item.path);
|
||||
});
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog("Unable to delete file", "This file is being edited. Please close the tab and try again.");
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
if (item.type === NotebookContentItemType.Directory && item.children && item.children.length > 0) {
|
||||
useDialog.getState().openDialog({
|
||||
isModal: true,
|
||||
title: "Unable to delete file",
|
||||
subText: "Directory is not empty.",
|
||||
primaryButtonText: "Close",
|
||||
secondaryButtonText: undefined,
|
||||
onPrimaryButtonClick: () => useDialog.getState().closeDialog(),
|
||||
onSecondaryButtonClick: undefined,
|
||||
});
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return this.notebookManager?.notebookContentClient.deleteContentItem(item, isGithubTree).then(
|
||||
() => logConsoleInfo(`Successfully deleted: ${item.path}`),
|
||||
(reason) => logConsoleError(`Failed to delete "${item.path}": ${JSON.stringify(reason)}`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a new notebook file, then opens the notebook
|
||||
*/
|
||||
public async onNewNotebookClicked(parent?: NotebookContentItem, isGithubTree?: boolean): Promise<void> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to create new notebook, but notebook is not enabled";
|
||||
handleError(error, "Explorer/onNewNotebookClicked");
|
||||
throw new Error(error);
|
||||
}
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
if (isGithubTree) {
|
||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
} else {
|
||||
useDialog.getState().showOkCancelModalDialog(
|
||||
Notebook.newNotebookModalTitle,
|
||||
undefined,
|
||||
"Create",
|
||||
async () => {
|
||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
this.getNewNoteWarningText(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.createNewNoteBook(parent, isGithubTree);
|
||||
}
|
||||
}
|
||||
|
||||
private getNewNoteWarningText(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<p>{Notebook.newNotebookModalContent1}</p>
|
||||
<br />
|
||||
<p>
|
||||
{Notebook.newNotebookModalContent2}
|
||||
<Link href={Notebook.cosmosNotebookHomePageUrl} target="_blank">
|
||||
{Notebook.learnMore}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private createNewNoteBook(parent?: NotebookContentItem, isGithubTree?: boolean): void {
|
||||
const clearInProgressMessage = logConsoleProgress(`Creating new notebook in ${parent.path}`);
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateNewNotebook, {
|
||||
dataExplorerArea: Constants.Areas.Notebook,
|
||||
});
|
||||
|
||||
this.notebookManager?.notebookContentClient
|
||||
.createNewNotebookFile(parent, isGithubTree)
|
||||
.then((newFile: NotebookContentItem) => {
|
||||
logConsoleInfo(`Successfully created: ${newFile.name}`);
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.CreateNewNotebook,
|
||||
{
|
||||
dataExplorerArea: Constants.Areas.Notebook,
|
||||
},
|
||||
startKey,
|
||||
);
|
||||
return this.openNotebook(newFile);
|
||||
})
|
||||
.then(() => this.resourceTree.triggerRender())
|
||||
.catch((error) => {
|
||||
const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
|
||||
logConsoleError(errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateNewNotebook,
|
||||
{
|
||||
dataExplorerArea: Constants.Areas.Notebook,
|
||||
error: errorMessage,
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey,
|
||||
);
|
||||
})
|
||||
.finally(clearInProgressMessage);
|
||||
}
|
||||
|
||||
// TODO: Delete this function when ResourceTreeAdapter is removed.
|
||||
public async refreshContentItem(item: NotebookContentItem): Promise<void> {
|
||||
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
|
||||
|
@ -1252,32 +722,6 @@ export default class Explorer {
|
|||
}
|
||||
}
|
||||
|
||||
public async handleOpenFileAction(path: string): Promise<void> {
|
||||
if (useNotebook.getState().isPhoenixNotebooks === undefined) {
|
||||
await useNotebook.getState().getPhoenixStatus();
|
||||
}
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
}
|
||||
|
||||
// We still use github urls like https://github.com/Azure-Samples/cosmos-notebooks/blob/master/CSharp_quickstarts/GettingStarted_CSharp.ipynb
|
||||
// when launching a notebook quickstart from Portal. In future we should just use gallery id and use Juno to fetch instead of directly
|
||||
// calling GitHub. For now convert this url to a raw url and download content.
|
||||
const gitHubInfo = fromContentUri(path);
|
||||
if (gitHubInfo) {
|
||||
const rawUrl = toRawContentUri(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.branch, gitHubInfo.path);
|
||||
const response = await fetch(rawUrl);
|
||||
if (response.status === Constants.HttpStatusCodes.OK) {
|
||||
this.notebookToImport = {
|
||||
name: NotebookUtil.getName(path),
|
||||
content: await response.text(),
|
||||
};
|
||||
|
||||
this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public openUploadItemsPanePane(): void {
|
||||
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
|
||||
}
|
||||
|
@ -1287,54 +731,6 @@ export default class Explorer {
|
|||
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
|
||||
}
|
||||
|
||||
public openUploadFilePanel(parent?: NotebookContentItem): void {
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
useDialog.getState().showOkCancelModalDialog(
|
||||
Notebook.newNotebookUploadModalTitle,
|
||||
undefined,
|
||||
"Upload",
|
||||
async () => {
|
||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.uploadFilePanel(parent);
|
||||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
this.getNewNoteWarningText(),
|
||||
);
|
||||
} else {
|
||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
||||
this.uploadFilePanel(parent);
|
||||
}
|
||||
}
|
||||
|
||||
private uploadFilePanel(parent?: NotebookContentItem): void {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Upload file to notebook server",
|
||||
<UploadFilePane uploadFile={(name: string, content: string) => this.uploadFile(name, content, parent)} />,
|
||||
);
|
||||
}
|
||||
|
||||
public getDownloadModalConent(fileName: string): JSX.Element {
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
return (
|
||||
<>
|
||||
<p>{Notebook.galleryNotebookDownloadContent1}</p>
|
||||
<br />
|
||||
<p>
|
||||
{Notebook.galleryNotebookDownloadContent2}
|
||||
<Link href={Notebook.cosmosNotebookGitDocumentationUrl} target="_blank">
|
||||
{Notebook.learnMore}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <p> Download {fileName} from gallery as a copy to your notebooks to run and/or edit the notebook. </p>;
|
||||
}
|
||||
|
||||
public async refreshExplorer(): Promise<void> {
|
||||
if (userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo") {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
|
@ -1359,10 +755,6 @@ export default class Explorer {
|
|||
dataExplorerArea: Constants.Areas.Notebook,
|
||||
});
|
||||
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.initNotebooks(userContext.databaseAccount);
|
||||
}
|
||||
|
||||
await this.refreshSampleData();
|
||||
}
|
||||
|
||||
|
|
|
@ -349,7 +349,7 @@ export class NodePropertiesComponent extends React.Component<
|
|||
onActivated={this.setIsDeleteConfirm.bind(this, true)}
|
||||
aria-label="Delete this vertex"
|
||||
>
|
||||
<img src={DeleteIcon} alt="Delete" />
|
||||
<img src={DeleteIcon} alt="Delete" role="button" />
|
||||
</AccessibleElement>
|
||||
);
|
||||
} else {
|
||||
|
@ -406,7 +406,7 @@ export class NodePropertiesComponent extends React.Component<
|
|||
aria-label="Edit properties"
|
||||
onActivated={expandClickHandler}
|
||||
>
|
||||
<img src={EditIcon} alt="Edit" />
|
||||
<img src={EditIcon} alt="Edit" role="button" />
|
||||
</AccessibleElement>
|
||||
)}
|
||||
|
||||
|
|
|
@ -184,12 +184,18 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
|
|||
className="rightPaneTrashIcon rightPaneBtns"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={`Delete ${data.key}`}
|
||||
onClick={(event: React.MouseEvent<HTMLDivElement>) => removeNewVertexProperty(event, index)}
|
||||
onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) =>
|
||||
removeNewVertexPropertyKeyPress(event, index)
|
||||
}
|
||||
>
|
||||
<img className="refreshcol rightPaneTrashIconImg" src={DeleteIcon} alt="Remove property" />
|
||||
<img
|
||||
aria-label="hidden"
|
||||
className="refreshcol rightPaneTrashIconImg"
|
||||
src={DeleteIcon}
|
||||
alt="Remove property"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,15 +12,13 @@ import * as Logger from "../../Common/Logger";
|
|||
import { GitHubClient } from "../../GitHub/GitHubClient";
|
||||
import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider";
|
||||
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { JunoClient } from "../../Juno/JunoClient";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getFullName } from "../../Utils/UserUtils";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { useDialog } from "../Controls/Dialog";
|
||||
import Explorer from "../Explorer";
|
||||
import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane";
|
||||
import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||
import { PublishNotebookPane } from "../Panes/PublishNotebookPane/PublishNotebookPane";
|
||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||
|
@ -40,7 +38,6 @@ export interface NotebookManagerOptions {
|
|||
container: Explorer;
|
||||
resourceTree: ResourceTreeAdapter;
|
||||
refreshCommandBarButtons: () => void;
|
||||
refreshNotebookList: () => void;
|
||||
}
|
||||
|
||||
export default class NotebookManager {
|
||||
|
@ -81,10 +78,6 @@ export default class NotebookManager {
|
|||
contents.JupyterContentProvider,
|
||||
);
|
||||
|
||||
this.notebookClient = new NotebookContainerClient(() =>
|
||||
this.params.container.initNotebooks(userContext?.databaseAccount),
|
||||
);
|
||||
|
||||
this.notebookContentClient = new NotebookContentClient(this.notebookContentProvider);
|
||||
|
||||
this.gitHubOAuthService.getTokenObservable().subscribe((token) => {
|
||||
|
@ -106,11 +99,9 @@ export default class NotebookManager {
|
|||
}
|
||||
|
||||
this.params.refreshCommandBarButtons();
|
||||
this.params.refreshNotebookList();
|
||||
});
|
||||
|
||||
this.junoClient.subscribeToPinnedRepos((pinnedRepos) => {
|
||||
this.params.resourceTree.initializeGitHubRepos(pinnedRepos);
|
||||
this.params.resourceTree.triggerRender();
|
||||
useNotebook.getState().initializeGitHubRepos(pinnedRepos);
|
||||
});
|
||||
|
@ -149,22 +140,6 @@ export default class NotebookManager {
|
|||
);
|
||||
}
|
||||
|
||||
public openCopyNotebookPane(name: string, content: string): void {
|
||||
const { container } = this.params;
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Copy Notebook",
|
||||
<CopyNotebookPane
|
||||
container={container}
|
||||
junoClient={this.junoClient}
|
||||
gitHubOAuthService={this.gitHubOAuthService}
|
||||
name={name}
|
||||
content={content}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
// Octokit's error handler uses any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private onGitHubClientError = (error: any): void => {
|
||||
|
|
|
@ -195,17 +195,5 @@ export function handleOpenAction(
|
|||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
action.actionType === ActionContracts.ActionType.OpenSampleNotebook ||
|
||||
action.actionType === ActionContracts.ActionType[ActionContracts.ActionType.OpenSampleNotebook]
|
||||
) {
|
||||
openFile(action as ActionContracts.OpenSampleNotebook, explorer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function openFile(action: ActionContracts.OpenSampleNotebook, explorer: Explorer) {
|
||||
explorer.handleOpenFileAction(decodeURIComponent(action.path));
|
||||
}
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
import { IDropdownOption } from "@fluentui/react";
|
||||
import React, { FormEvent, FunctionComponent, useEffect, useState } from "react";
|
||||
import { HttpStatusCodes, PoolIdType } from "../../../Common/Constants";
|
||||
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "../../Notebook/NotebookContentItem";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent";
|
||||
|
||||
interface Location {
|
||||
type: "MyNotebooks" | "GitHub";
|
||||
|
||||
// GitHub
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
}
|
||||
export interface CopyNotebookPanelProps {
|
||||
name: string;
|
||||
content: string;
|
||||
container: Explorer;
|
||||
junoClient: JunoClient;
|
||||
gitHubOAuthService: GitHubOAuthService;
|
||||
}
|
||||
|
||||
export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||
name,
|
||||
content,
|
||||
container,
|
||||
junoClient,
|
||||
gitHubOAuthService,
|
||||
}: CopyNotebookPanelProps) => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const [isExecuting, setIsExecuting] = useState<boolean>();
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [pinnedRepos, setPinnedRepos] = useState<IPinnedRepo[]>();
|
||||
const [selectedLocation, setSelectedLocation] = useState<Location>();
|
||||
|
||||
useEffect(() => {
|
||||
open();
|
||||
}, []);
|
||||
|
||||
const open = async (): Promise<void> => {
|
||||
if (gitHubOAuthService.isLoggedIn()) {
|
||||
const response = await junoClient.getPinnedRepos(gitHubOAuthService.getTokenObservable()()?.scope);
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit");
|
||||
}
|
||||
|
||||
if (response.data?.length > 0) {
|
||||
setPinnedRepos(response.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
let destination: string = selectedLocation?.type;
|
||||
let clearMessage: () => void;
|
||||
setIsExecuting(true);
|
||||
|
||||
try {
|
||||
if (!selectedLocation) {
|
||||
throw new Error(`No location selected`);
|
||||
}
|
||||
|
||||
if (selectedLocation.type === "GitHub") {
|
||||
destination = `${destination} - ${GitHubUtils.toRepoFullName(
|
||||
selectedLocation.owner,
|
||||
selectedLocation.repo,
|
||||
)} - ${selectedLocation.branch}`;
|
||||
} else if (selectedLocation.type === "MyNotebooks" && useNotebook.getState().isPhoenixNotebooks) {
|
||||
destination = useNotebook.getState().notebookFolderName;
|
||||
}
|
||||
|
||||
clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${name} to ${destination}`);
|
||||
|
||||
const notebookContentItem = await copyNotebook(selectedLocation);
|
||||
if (!notebookContentItem) {
|
||||
throw new Error(`Failed to upload ${name}`);
|
||||
}
|
||||
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`);
|
||||
closeSidePanel();
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
setFormError(`Failed to copy ${name} to ${destination}`);
|
||||
handleError(errorMessage, "CopyNotebookPaneAdapter/submit", formError);
|
||||
} finally {
|
||||
clearMessage && clearMessage();
|
||||
setIsExecuting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const copyNotebook = async (location: Location): Promise<NotebookContentItem> => {
|
||||
let parent: NotebookContentItem;
|
||||
let isGithubTree: boolean;
|
||||
switch (location.type) {
|
||||
case "MyNotebooks":
|
||||
parent = {
|
||||
name: useNotebook.getState().notebookFolderName,
|
||||
path: useNotebook.getState().notebookBasePath,
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
isGithubTree = false;
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await container.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
}
|
||||
break;
|
||||
|
||||
case "GitHub":
|
||||
parent = {
|
||||
name: selectedLocation.branch,
|
||||
path: GitHubUtils.toContentUri(selectedLocation.owner, selectedLocation.repo, selectedLocation.branch, ""),
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
isGithubTree = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported location type ${location.type}`);
|
||||
}
|
||||
|
||||
return container.uploadFile(name, content, parent, isGithubTree);
|
||||
};
|
||||
|
||||
const onDropDownChange = (_: FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
|
||||
setSelectedLocation(option?.data);
|
||||
};
|
||||
|
||||
const props: RightPaneFormProps = {
|
||||
formError,
|
||||
isExecuting: isExecuting,
|
||||
submitButtonText: "OK",
|
||||
onSubmit: () => submit(),
|
||||
};
|
||||
|
||||
const copyNotebookPaneProps: CopyNotebookPaneProps = {
|
||||
name,
|
||||
pinnedRepos,
|
||||
onDropDownChange: onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<RightPaneForm {...props}>
|
||||
<CopyNotebookPaneComponent {...copyNotebookPaneProps} />
|
||||
</RightPaneForm>
|
||||
);
|
||||
};
|
|
@ -1,120 +0,0 @@
|
|||
import {
|
||||
Dropdown,
|
||||
IDropdownOption,
|
||||
IDropdownProps,
|
||||
IRenderFunction,
|
||||
ISelectableOption,
|
||||
Label,
|
||||
SelectableOptionMenuItemType,
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import { GitHubReposTitle } from "Explorer/Tree/ResourceTree";
|
||||
import React, { FormEvent, FunctionComponent } from "react";
|
||||
import { IPinnedRepo } from "../../../Juno/JunoClient";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
|
||||
interface Location {
|
||||
type: "MyNotebooks" | "GitHub";
|
||||
|
||||
// GitHub
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
}
|
||||
|
||||
export interface CopyNotebookPaneProps {
|
||||
name: string;
|
||||
pinnedRepos: IPinnedRepo[];
|
||||
onDropDownChange: (_: FormEvent<HTMLDivElement>, option?: IDropdownOption) => void;
|
||||
}
|
||||
|
||||
export const CopyNotebookPaneComponent: FunctionComponent<CopyNotebookPaneProps> = ({
|
||||
name,
|
||||
pinnedRepos,
|
||||
onDropDownChange,
|
||||
}: CopyNotebookPaneProps) => {
|
||||
const BranchNameWhiteSpace = " ";
|
||||
|
||||
const onRenderDropDownTitle: IRenderFunction<IDropdownOption[]> = (options: IDropdownOption[]): JSX.Element => {
|
||||
return <span>{options.length && options[0].title}</span>;
|
||||
};
|
||||
|
||||
const onRenderDropDownOption: IRenderFunction<ISelectableOption> = (option: ISelectableOption): JSX.Element => {
|
||||
return <span style={{ whiteSpace: "pre-wrap" }}>{option.text}</span>;
|
||||
};
|
||||
|
||||
const getDropDownOptions = (): IDropdownOption[] => {
|
||||
const options: IDropdownOption[] = [];
|
||||
options.push({
|
||||
key: "MyNotebooks-Item",
|
||||
text: useNotebook.getState().notebookFolderName,
|
||||
title: useNotebook.getState().notebookFolderName,
|
||||
data: {
|
||||
type: "MyNotebooks",
|
||||
} as Location,
|
||||
});
|
||||
|
||||
if (pinnedRepos && pinnedRepos.length > 0) {
|
||||
options.push({
|
||||
key: "GitHub-Header-Divider",
|
||||
text: undefined,
|
||||
itemType: SelectableOptionMenuItemType.Divider,
|
||||
});
|
||||
|
||||
options.push({
|
||||
key: "GitHub-Header",
|
||||
text: GitHubReposTitle,
|
||||
itemType: SelectableOptionMenuItemType.Header,
|
||||
});
|
||||
|
||||
pinnedRepos.forEach((pinnedRepo) => {
|
||||
const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name);
|
||||
options.push({
|
||||
key: `GitHub-Repo-${repoFullName}`,
|
||||
text: repoFullName,
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
pinnedRepo.branches.forEach((branch) =>
|
||||
options.push({
|
||||
key: `GitHub-Repo-${repoFullName}-${branch.name}`,
|
||||
text: `${BranchNameWhiteSpace}${branch.name}`,
|
||||
title: `${repoFullName} - ${branch.name}`,
|
||||
data: {
|
||||
type: "GitHub",
|
||||
owner: pinnedRepo.owner,
|
||||
repo: pinnedRepo.name,
|
||||
branch: branch.name,
|
||||
} as Location,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
const dropDownProps: IDropdownProps = {
|
||||
label: "Location",
|
||||
ariaLabel: "Location",
|
||||
placeholder: "Select an option",
|
||||
onRenderTitle: onRenderDropDownTitle,
|
||||
onRenderOption: onRenderDropDownOption,
|
||||
options: getDropDownOptions(),
|
||||
onChange: onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="paneMainContent">
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Stack.Item>
|
||||
<Label htmlFor="notebookName">Name</Label>
|
||||
<Text id="notebookName">{name}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Dropdown {...dropDownProps} />
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -17,8 +17,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||
addRepoProps={
|
||||
Object {
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -35,10 +33,8 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -630,7 +630,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||
Enable sample database
|
||||
<InfoTooltip>
|
||||
This is a sample database and collection with synthetic product data you can use to explore using
|
||||
NoSQL queries and Copilot. This will appear as another database in the Data Explorer UI, and is
|
||||
NoSQL queries and Query Advisor. This will appear as another database in the Data Explorer UI, and is
|
||||
created by, and maintained by Microsoft at no cost to you.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
|
@ -640,7 +640,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||
label: { padding: 0 },
|
||||
}}
|
||||
className="padding"
|
||||
ariaLabel="Enable sample db for Copilot"
|
||||
ariaLabel="Enable sample db for Query Advisor"
|
||||
checked={copilotSampleDBEnabled}
|
||||
onChange={handleSampleDatabaseChange}
|
||||
/>
|
||||
|
|
|
@ -7,8 +7,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||
errorMessage="Could not create directory "
|
||||
explorer={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -25,10 +23,8 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -261,6 +261,7 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
|
|||
<TextField
|
||||
multiline
|
||||
rows={5}
|
||||
ariaLabel={entityAttributeProperty}
|
||||
value={entityAttributeValue}
|
||||
onChange={(event, newInput?: string) => {
|
||||
entityChange(newInput, selectedRow, "value");
|
||||
|
|
|
@ -385,7 +385,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||
hasSmallHeadline={true}
|
||||
headline="Write a prompt"
|
||||
>
|
||||
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
||||
Write a prompt here and Query Advisor will generate the query for you. You can also choose from our{" "}
|
||||
<Link
|
||||
onClick={() => {
|
||||
setShowSamplePrompts(true);
|
||||
|
|
|
@ -57,12 +57,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
|||
|
||||
const toggleCopilotButton = {
|
||||
iconSrc: QueryCommandIcon,
|
||||
iconAlt: "Copilot",
|
||||
iconAlt: "Query Advisor",
|
||||
onCommandClick: () => {
|
||||
toggleCopilot(true);
|
||||
},
|
||||
commandButtonLabel: "Copilot",
|
||||
ariaLabel: "Copilot",
|
||||
commandButtonLabel: "Query Advisor",
|
||||
ariaLabel: "Query Advisor",
|
||||
hasPopup: false,
|
||||
disabled: copilotActive,
|
||||
};
|
||||
|
|
|
@ -22,8 +22,6 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||
databaseId="CopilotSampleDb"
|
||||
explorer={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
@ -40,10 +38,8 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"parameters": [Function],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,11 +25,9 @@ import * as React from "react";
|
|||
import ConnectIcon from "../../../images/Connect_color.svg";
|
||||
import ContainersIcon from "../../../images/Containers.svg";
|
||||
import LinkIcon from "../../../images/Link_blue.svg";
|
||||
import NotebookColorIcon from "../../../images/Notebooks.svg";
|
||||
import PowerShellIcon from "../../../images/PowerShell.svg";
|
||||
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||
import QuickStartIcon from "../../../images/Quickstart_Lightning.svg";
|
||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
@ -151,9 +149,9 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
{useQueryCopilot.getState().copilotEnabled && (
|
||||
<SplashScreenButton
|
||||
imgSrc={CopilotIcon}
|
||||
title={"Query faster with Copilot"}
|
||||
title={"Query faster with Query Advisor"}
|
||||
description={
|
||||
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
||||
"Query Advisor is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
||||
}
|
||||
onClick={() => {
|
||||
const copilotVersion = userContext.features.copilotVersion;
|
||||
|
@ -410,14 +408,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
},
|
||||
};
|
||||
heroes.push(launchQuickstartBtn);
|
||||
} else if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
const newNotebookBtn = {
|
||||
iconSrc: NotebookColorIcon,
|
||||
title: "New notebook",
|
||||
description: "Visualize your data stored in Azure Cosmos DB",
|
||||
onClick: () => this.container.onNewNotebookClicked(),
|
||||
};
|
||||
heroes.push(newNotebookBtn);
|
||||
}
|
||||
|
||||
heroes.push(this.getShellCard());
|
||||
|
@ -493,28 +483,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||
};
|
||||
}
|
||||
|
||||
private decorateOpenNotebookActivity({ name, path }: MostRecentActivity.OpenNotebookItem) {
|
||||
return {
|
||||
info: path,
|
||||
iconSrc: NotebookIcon,
|
||||
title: name,
|
||||
description: "Notebook",
|
||||
onClick: () => {
|
||||
const notebookItem = this.container.createNotebookContentItemFile(name, path);
|
||||
notebookItem && this.container.openNotebook(notebookItem);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private createRecentItems(): SplashScreenItem[] {
|
||||
return MostRecentActivity.mostRecentActivity.getItems(userContext.databaseAccount?.id).map((activity) => {
|
||||
switch (activity.type) {
|
||||
default: {
|
||||
const unknownActivity: never = activity;
|
||||
throw new Error(`Unknown activity: ${unknownActivity}`);
|
||||
throw new Error(`Unknown activity: ${activity}`);
|
||||
}
|
||||
case MostRecentActivity.Type.OpenNotebook:
|
||||
return this.decorateOpenNotebookActivity(activity);
|
||||
|
||||
case MostRecentActivity.Type.OpenCollection:
|
||||
return this.decorateOpenCollectionActivity(activity);
|
||||
|
|
|
@ -80,7 +80,8 @@
|
|||
placeholder:isPreferredApiMongoDB?'Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents.':'Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents.'
|
||||
},
|
||||
css: { placeholderVisible: filterContent().length === 0 },
|
||||
textInput: filterContent"
|
||||
textInput: filterContent,
|
||||
event: { keydown: onFilterKeyDown }"
|
||||
/>
|
||||
|
||||
<datalist
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ItemDefinition, PartitionKey, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { KeyboardAction, KeyboardActionGroup, KeyboardHandlerSetter, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import * as ko from "knockout";
|
||||
|
@ -86,9 +86,11 @@ export default class DocumentsTab extends TabsBase {
|
|||
private _isQueryCopilotSampleContainer: boolean;
|
||||
private queryAbortController: AbortController;
|
||||
private cancelQueryTimeoutID: NodeJS.Timeout;
|
||||
private setKeyboardActions: KeyboardHandlerSetter;
|
||||
|
||||
constructor(options: ViewModels.DocumentsTabOptions) {
|
||||
super(options);
|
||||
this.setKeyboardActions = useKeyboardActionGroup(KeyboardActionGroup.ACTIVE_TAB);
|
||||
this.isPreferredApiMongoDB = userContext.apiType === "Mongo" || options.isPreferredApiMongoDB;
|
||||
|
||||
this.idHeader = this.isPreferredApiMongoDB ? "_id" : "id";
|
||||
|
@ -463,7 +465,22 @@ export default class DocumentsTab extends TabsBase {
|
|||
|
||||
private initializeNewDocument = (): void => {
|
||||
this.selectedDocumentId(null);
|
||||
const defaultDocument: string = this.renderObjectForEditor({ id: "replace_with_new_document_id" }, null, 4);
|
||||
const newDocument: any = {
|
||||
id: "replace_with_new_document_id",
|
||||
};
|
||||
this.partitionKeyProperties.forEach((partitionKeyProperty) => {
|
||||
let target = newDocument;
|
||||
const keySegments = partitionKeyProperty.split(".");
|
||||
const finalSegment = keySegments.pop();
|
||||
|
||||
// Initialize nested objects as needed
|
||||
keySegments.forEach((segment) => {
|
||||
target = target[segment] = target[segment] || {};
|
||||
});
|
||||
|
||||
target[finalSegment] = "replace_with_new_partition_key_value";
|
||||
});
|
||||
const defaultDocument: string = this.renderObjectForEditor(newDocument, null, 4);
|
||||
this.initialDocumentContent(defaultDocument);
|
||||
this.selectedDocumentContent.setBaseline(defaultDocument);
|
||||
this.editorState(ViewModels.DocumentExplorerState.newDocumentValid);
|
||||
|
@ -650,9 +667,38 @@ export default class DocumentsTab extends TabsBase {
|
|||
this.collection && this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
}
|
||||
|
||||
public onFilterKeyDown(model: unknown, e: KeyboardEvent): boolean {
|
||||
if (e.key === "Enter") {
|
||||
this.refreshDocumentsGrid(true);
|
||||
|
||||
// Suppress the default behavior of the key
|
||||
return false;
|
||||
} else if (e.key === "Escape") {
|
||||
this.onHideFilterClick();
|
||||
|
||||
// Suppress the default behavior of the key
|
||||
return false;
|
||||
} else {
|
||||
// Allow the default behavior of the key
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public async onActivate(): Promise<void> {
|
||||
super.onActivate();
|
||||
|
||||
this.setKeyboardActions({
|
||||
[KeyboardAction.SEARCH]: () => {
|
||||
this.onShowFilterClick();
|
||||
return true;
|
||||
},
|
||||
[KeyboardAction.CLEAR_SEARCH]: () => {
|
||||
this.filterContent("");
|
||||
this.refreshDocumentsGrid(true);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
if (!this._documentsIterator) {
|
||||
try {
|
||||
await this.autoPopulateContent();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useMongoProxyEndpoint } from "Common/MongoProxyClient";
|
||||
import React, { Component } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
|
@ -34,7 +35,7 @@ export interface IMongoShellTabAccessor {
|
|||
}
|
||||
|
||||
export interface IMongoShellTabComponentStates {
|
||||
url: URL;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface IMongoShellTabComponentProps {
|
||||
|
@ -54,8 +55,7 @@ export default class MongoShellTabComponent extends Component<
|
|||
constructor(props: IMongoShellTabComponentProps) {
|
||||
super(props);
|
||||
this._logTraces = new Map();
|
||||
this._useMongoProxyEndpoint = userContext.features.enableLegacyMongoShell;
|
||||
// this._useMongoProxyEndpoint = useMongoProxyEndpoint("legacyMongoShell");
|
||||
this._useMongoProxyEndpoint = useMongoProxyEndpoint("legacyMongoShell");
|
||||
|
||||
this.state = {
|
||||
url: getMongoShellUrl(this._useMongoProxyEndpoint),
|
||||
|
@ -221,7 +221,7 @@ export default class MongoShellTabComponent extends Component<
|
|||
name="explorer"
|
||||
className="iframe"
|
||||
style={{ width: "100%", height: "100%", border: 0, padding: 0, margin: 0, overflow: "hidden" }}
|
||||
src={this.state.url.toString()}
|
||||
src={this.state.url}
|
||||
id={this.props.tabsBaseInstance.tabId}
|
||||
onLoad={(event) => this.setContentFocus(event)}
|
||||
title="Mongo Shell"
|
||||
|
|
|
@ -3,7 +3,6 @@ import { updateUserContext, userContext } from "../../../UserContext";
|
|||
import { getMongoShellUrl } from "./getMongoShellUrl";
|
||||
|
||||
const mongoBackendEndpoint = "https://localhost:1234";
|
||||
const hostedExplorerURL = "https://cosmos.azure.com/";
|
||||
|
||||
describe("getMongoShellUrl", () => {
|
||||
let queryString = "";
|
||||
|
@ -13,7 +12,6 @@ describe("getMongoShellUrl", () => {
|
|||
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: mongoBackendEndpoint,
|
||||
hostedExplorerURL: hostedExplorerURL,
|
||||
platform: Platform.Hosted,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
import { configContext } from "ConfigContext";
|
||||
import { userContext } from "../../../UserContext";
|
||||
|
||||
export function getMongoShellUrl(useMongoProxyEndpoint?: boolean): URL {
|
||||
export function getMongoShellUrl(useMongoProxyEndpoint?: boolean): string {
|
||||
const { databaseAccount: account } = userContext;
|
||||
const resourceId = account?.id;
|
||||
const accountName = account?.name;
|
||||
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
|
||||
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
|
||||
const path: string = useMongoProxyEndpoint
|
||||
? `/mongoshell/index.html?${queryString}`
|
||||
: `/mongoshell/indexv2.html?${queryString}`;
|
||||
|
||||
return new URL(path, configContext.hostedExplorerURL);
|
||||
return useMongoProxyEndpoint ? `/mongoshell/index.html?${queryString}` : `/mongoshell/indexv2.html?${queryString}`;
|
||||
}
|
||||
|
|
|
@ -1,31 +1,11 @@
|
|||
import { stringifyNotebook, toJS } from "@nteract/commutable";
|
||||
import * as ko from "knockout";
|
||||
import * as Q from "q";
|
||||
import { userContext } from "UserContext";
|
||||
import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg";
|
||||
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
||||
import CutIcon from "../../../images/notebook/Notebook-cut.svg";
|
||||
import NewCellIcon from "../../../images/notebook/Notebook-insert-cell.svg";
|
||||
import PasteIcon from "../../../images/notebook/Notebook-paste.svg";
|
||||
import RestartIcon from "../../../images/notebook/Notebook-restart.svg";
|
||||
import RunAllIcon from "../../../images/notebook/Notebook-run-all.svg";
|
||||
import RunIcon from "../../../images/notebook/Notebook-run.svg";
|
||||
import { default as InterruptKernelIcon, default as KillKernelIcon } from "../../../images/notebook/Notebook-stop.svg";
|
||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||
import { useNotebookSnapshotStore } from "../../hooks/useNotebookSnapshotStore";
|
||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import * as NotebookConfigurationUtils from "../../Utils/NotebookConfigurationUtils";
|
||||
import { logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
import { useDialog } from "../Controls/Dialog";
|
||||
import * as CommandBarComponentButtonFactory from "../Menus/CommandBar/CommandBarComponentButtonFactory";
|
||||
import { KernelSpecsDisplay } from "../Notebook/NotebookClientV2";
|
||||
import * as CdbActions from "../Notebook/NotebookComponent/actions";
|
||||
import { NotebookComponentAdapter } from "../Notebook/NotebookComponent/NotebookComponentAdapter";
|
||||
import { CdbAppState, SnapshotRequest } from "../Notebook/NotebookComponent/types";
|
||||
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
import NotebookTabBase, { NotebookTabBaseOptions } from "./NotebookTabBase";
|
||||
|
||||
|
@ -90,275 +70,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
|||
}
|
||||
|
||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||
const availableKernels = NotebookTabV2.clientManager.getAvailableKernelSpecs();
|
||||
const isNotebookUntrusted = this.notebookComponentAdapter.isNotebookUntrusted();
|
||||
|
||||
const runBtnTooltip = isNotebookUntrusted ? NotebookUtil.UntrustedNotebookRunHint : undefined;
|
||||
|
||||
const saveLabel = "Save";
|
||||
const copyToLabel = "Copy to ...";
|
||||
const publishLabel = "Publish to gallery";
|
||||
const kernelLabel = "No Kernel";
|
||||
const runLabel = "Run";
|
||||
const runActiveCellLabel = "Run Active Cell";
|
||||
const runAllLabel = "Run All";
|
||||
const interruptKernelLabel = "Interrupt Kernel";
|
||||
const killKernelLabel = "Halt Kernel";
|
||||
const restartKernelLabel = "Restart Kernel";
|
||||
const clearLabel = "Clear outputs";
|
||||
const newCellLabel = "New Cell";
|
||||
const cellTypeLabel = "Cell Type";
|
||||
const codeLabel = "Code";
|
||||
const markdownLabel = "Markdown";
|
||||
const rawLabel = "Raw";
|
||||
const copyLabel = "Copy";
|
||||
const cutLabel = "Cut";
|
||||
const pasteLabel = "Paste";
|
||||
const cellCodeType = "code";
|
||||
const cellMarkdownType = "markdown";
|
||||
const cellRawType = "raw";
|
||||
|
||||
const saveButtonChildren = [];
|
||||
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||
saveButtonChildren.push({
|
||||
iconName: copyToLabel,
|
||||
onCommandClick: () => this.copyNotebook(),
|
||||
commandButtonLabel: copyToLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: copyToLabel,
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.features.publicGallery) {
|
||||
saveButtonChildren.push({
|
||||
iconName: "PublishContent",
|
||||
onCommandClick: async () => await this.publishToGallery(),
|
||||
commandButtonLabel: publishLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: publishLabel,
|
||||
});
|
||||
}
|
||||
|
||||
let buttons: CommandButtonComponentProps[] = [
|
||||
{
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: saveLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookSave(),
|
||||
commandButtonLabel: saveLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: saveLabel,
|
||||
children: saveButtonChildren.length && [
|
||||
{
|
||||
iconName: "Save",
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookSave(),
|
||||
commandButtonLabel: saveLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: saveLabel,
|
||||
},
|
||||
...saveButtonChildren,
|
||||
],
|
||||
},
|
||||
{
|
||||
iconSrc: null,
|
||||
iconAlt: kernelLabel,
|
||||
onCommandClick: () => {},
|
||||
commandButtonLabel: null,
|
||||
hasPopup: false,
|
||||
disabled: availableKernels.length < 1,
|
||||
isDropdown: true,
|
||||
dropdownPlaceholder: kernelLabel,
|
||||
dropdownSelectedKey: this.notebookComponentAdapter.getSelectedKernelName(), //this.currentKernelName,
|
||||
dropdownWidth: 100,
|
||||
children: availableKernels.map(
|
||||
(kernel: KernelSpecsDisplay) =>
|
||||
({
|
||||
iconSrc: null,
|
||||
iconAlt: kernel.displayName,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookChangeKernel(kernel.name),
|
||||
commandButtonLabel: kernel.displayName,
|
||||
dropdownItemKey: kernel.name,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: kernel.displayName,
|
||||
}) as CommandButtonComponentProps,
|
||||
),
|
||||
ariaLabel: kernelLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: RunIcon,
|
||||
iconAlt: runLabel,
|
||||
onCommandClick: () => {
|
||||
this.notebookComponentAdapter.notebookRunAndAdvance();
|
||||
this.traceTelemetry(Action.ExecuteCell);
|
||||
},
|
||||
commandButtonLabel: runLabel,
|
||||
tooltipText: runBtnTooltip,
|
||||
ariaLabel: runLabel,
|
||||
hasPopup: false,
|
||||
disabled: isNotebookUntrusted,
|
||||
children: [
|
||||
{
|
||||
iconSrc: RunIcon,
|
||||
iconAlt: runActiveCellLabel,
|
||||
onCommandClick: () => {
|
||||
this.notebookComponentAdapter.notebookRunAndAdvance();
|
||||
this.traceTelemetry(Action.ExecuteCell);
|
||||
},
|
||||
commandButtonLabel: runActiveCellLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: runActiveCellLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: RunAllIcon,
|
||||
iconAlt: runAllLabel,
|
||||
onCommandClick: () => {
|
||||
this.notebookComponentAdapter.notebookRunAll();
|
||||
this.traceTelemetry(Action.ExecuteAllCells);
|
||||
},
|
||||
commandButtonLabel: runAllLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: runAllLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: InterruptKernelIcon,
|
||||
iconAlt: interruptKernelLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookInterruptKernel(),
|
||||
commandButtonLabel: interruptKernelLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: interruptKernelLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: KillKernelIcon,
|
||||
iconAlt: killKernelLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookKillKernel(),
|
||||
commandButtonLabel: killKernelLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: killKernelLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: RestartIcon,
|
||||
iconAlt: restartKernelLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookRestartKernel(),
|
||||
commandButtonLabel: restartKernelLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: restartKernelLabel,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
iconSrc: ClearAllOutputsIcon,
|
||||
iconAlt: clearLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookClearAllOutputs(),
|
||||
commandButtonLabel: clearLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: clearLabel,
|
||||
},
|
||||
{
|
||||
iconSrc: NewCellIcon,
|
||||
iconAlt: newCellLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookInsertBelow(),
|
||||
commandButtonLabel: newCellLabel,
|
||||
ariaLabel: newCellLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
CommandBarComponentButtonFactory.createDivider(),
|
||||
{
|
||||
iconSrc: null,
|
||||
iconAlt: null,
|
||||
onCommandClick: () => {},
|
||||
commandButtonLabel: null,
|
||||
ariaLabel: cellTypeLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
isDropdown: true,
|
||||
dropdownPlaceholder: cellTypeLabel,
|
||||
dropdownSelectedKey: this.notebookComponentAdapter.getActiveCellTypeStr(),
|
||||
dropdownWidth: 110,
|
||||
children: [
|
||||
{
|
||||
iconSrc: null,
|
||||
iconAlt: null,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookChangeCellType(cellCodeType),
|
||||
commandButtonLabel: codeLabel,
|
||||
ariaLabel: codeLabel,
|
||||
dropdownItemKey: cellCodeType,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
iconSrc: null,
|
||||
iconAlt: null,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookChangeCellType(cellMarkdownType),
|
||||
commandButtonLabel: markdownLabel,
|
||||
ariaLabel: markdownLabel,
|
||||
dropdownItemKey: cellMarkdownType,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
iconSrc: null,
|
||||
iconAlt: null,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookChangeCellType(cellRawType),
|
||||
commandButtonLabel: rawLabel,
|
||||
ariaLabel: rawLabel,
|
||||
dropdownItemKey: cellRawType,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
iconSrc: CopyIcon,
|
||||
iconAlt: copyLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebokCopy(),
|
||||
commandButtonLabel: copyLabel,
|
||||
ariaLabel: copyLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
children: [
|
||||
{
|
||||
iconSrc: CopyIcon,
|
||||
iconAlt: copyLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebokCopy(),
|
||||
commandButtonLabel: copyLabel,
|
||||
ariaLabel: copyLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
iconSrc: CutIcon,
|
||||
iconAlt: cutLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookCut(),
|
||||
commandButtonLabel: cutLabel,
|
||||
ariaLabel: cutLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
iconSrc: PasteIcon,
|
||||
iconAlt: pasteLabel,
|
||||
onCommandClick: () => this.notebookComponentAdapter.notebookPaste(),
|
||||
commandButtonLabel: pasteLabel,
|
||||
ariaLabel: pasteLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
// TODO: Uncomment when undo/redo is reimplemented in nteract
|
||||
];
|
||||
return buttons;
|
||||
return [];
|
||||
}
|
||||
|
||||
protected buildCommandBarOptions(): void {
|
||||
|
@ -382,50 +94,4 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
|||
sparkClusterConnectionInfo,
|
||||
);
|
||||
}
|
||||
|
||||
private publishToGallery = async () => {
|
||||
TelemetryProcessor.trace(Action.NotebooksGalleryClickPublishToGallery, ActionModifiers.Mark, {
|
||||
source: Source.CommandBarMenu,
|
||||
});
|
||||
|
||||
const notebookReduxStore = NotebookTabV2.clientManager.getStore();
|
||||
const unsubscribe = notebookReduxStore.subscribe(() => {
|
||||
const cdbState = (notebookReduxStore.getState() as CdbAppState).cdb;
|
||||
useNotebookSnapshotStore.setState({
|
||||
snapshot: cdbState.notebookSnapshot?.imageSrc,
|
||||
error: cdbState.notebookSnapshotError,
|
||||
});
|
||||
});
|
||||
|
||||
const notebookContent = this.notebookComponentAdapter.getContent();
|
||||
const notebookContentRef = this.notebookComponentAdapter.contentRef;
|
||||
const onPanelClose = (): void => {
|
||||
unsubscribe();
|
||||
useNotebookSnapshotStore.setState({
|
||||
snapshot: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
notebookReduxStore.dispatch(CdbActions.takeNotebookSnapshot(undefined));
|
||||
};
|
||||
|
||||
await this.container.publishNotebook(
|
||||
notebookContent.name,
|
||||
notebookContent.content,
|
||||
notebookContentRef,
|
||||
(request: SnapshotRequest) => notebookReduxStore.dispatch(CdbActions.takeNotebookSnapshot(request)),
|
||||
onPanelClose,
|
||||
);
|
||||
};
|
||||
|
||||
private copyNotebook = () => {
|
||||
const notebookContent = this.notebookComponentAdapter.getContent();
|
||||
let content: string;
|
||||
if (typeof notebookContent.content === "string") {
|
||||
content = notebookContent.content;
|
||||
} else {
|
||||
content = stringifyNotebook(toJS(notebookContent.content));
|
||||
}
|
||||
|
||||
this.container.copyNotebook(notebookContent.name, content);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -381,9 +381,13 @@ export const QueryResultSection: React.FC<QueryResultProps> = ({
|
|||
<img className="paneErrorIcon" src={InfoColor} alt="Error" />
|
||||
</span>
|
||||
<span className="warningErrorDetailsLinkContainer">
|
||||
We have detected you may be using a subquery. Non-correlated subqueries are not currently supported.
|
||||
<a href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-subquery">
|
||||
Please see Cosmos sub query documentation for further information
|
||||
We detected you may be using a subquery. To learn more about subqueries effectively,{" "}
|
||||
<a
|
||||
href="https://learn.microsoft.com/azure/cosmos-db/nosql/query/subquery"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
visit the documentation
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,7 @@ import "react-splitter-layout/lib/index.css";
|
|||
import { format } from "react-string-format";
|
||||
import QueryCommandIcon from "../../../../images/CopilotCommand.svg";
|
||||
import LaunchCopilot from "../../../../images/CopilotTabIcon.svg";
|
||||
import DownloadQueryIcon from "../../../../images/DownloadQuery.svg";
|
||||
import CancelQueryIcon from "../../../../images/Entity_cancel.svg";
|
||||
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||
import SaveQueryIcon from "../../../../images/save-cosmos.svg";
|
||||
|
@ -225,6 +226,20 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||
}
|
||||
};
|
||||
|
||||
public onDownloadQueryClick = (): void => {
|
||||
const text = this.getCurrentEditorQuery();
|
||||
const queryFile = new File([text], `SavedQuery.txt`, { type: "text/plain" });
|
||||
|
||||
// It appears the most consistent to download a file from a blob is to create an anchor element and simulate clicking it
|
||||
const blobUrl = URL.createObjectURL(queryFile);
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = blobUrl;
|
||||
anchor.download = queryFile.name;
|
||||
document.body.appendChild(anchor); // Must put the anchor in the document.
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor); // Clean up the anchor.
|
||||
};
|
||||
|
||||
public onSaveQueryClick = (): void => {
|
||||
useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this.props.collection.container} />);
|
||||
};
|
||||
|
@ -405,15 +420,28 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||
});
|
||||
}
|
||||
|
||||
if (this.saveQueryButton.visible && configContext.platform !== Platform.Fabric) {
|
||||
const label = "Save Query";
|
||||
if (this.saveQueryButton.visible) {
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
const label = "Save Query";
|
||||
buttons.push({
|
||||
iconSrc: SaveQueryIcon,
|
||||
iconAlt: label,
|
||||
keyboardAction: KeyboardAction.SAVE_ITEM,
|
||||
onCommandClick: this.onSaveQueryClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: !this.saveQueryButton.enabled,
|
||||
});
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
iconSrc: SaveQueryIcon,
|
||||
iconAlt: label,
|
||||
keyboardAction: KeyboardAction.SAVE_ITEM,
|
||||
onCommandClick: this.onSaveQueryClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
iconSrc: DownloadQueryIcon,
|
||||
iconAlt: "Download Query",
|
||||
keyboardAction: KeyboardAction.DOWNLOAD_ITEM,
|
||||
onCommandClick: this.onDownloadQueryClick,
|
||||
commandButtonLabel: "Download Query",
|
||||
ariaLabel: "Download Query",
|
||||
hasPopup: false,
|
||||
disabled: !this.saveQueryButton.enabled,
|
||||
});
|
||||
|
@ -455,13 +483,13 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||
if (this.props.copilotEnabled) {
|
||||
const toggleCopilotButton: CommandButtonComponentProps = {
|
||||
iconSrc: QueryCommandIcon,
|
||||
iconAlt: "Copilot",
|
||||
iconAlt: "Query Advisor",
|
||||
keyboardAction: KeyboardAction.TOGGLE_COPILOT,
|
||||
onCommandClick: () => {
|
||||
this._toggleCopilot(!this.state.copilotActive);
|
||||
},
|
||||
commandButtonLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot",
|
||||
ariaLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot",
|
||||
commandButtonLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
|
||||
ariaLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
|
||||
hasPopup: false,
|
||||
};
|
||||
buttons.push(toggleCopilotButton);
|
||||
|
@ -525,6 +553,8 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||
}
|
||||
}
|
||||
|
||||
this.saveQueryButton.enabled = newContent.length > 0;
|
||||
|
||||
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { KeyboardActionGroup, clearKeyboardActionGroup } from "KeyboardShortcuts";
|
||||
import * as ko from "knockout";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import * as ThemeUtility from "../../Common/ThemeUtility";
|
||||
|
@ -107,6 +108,7 @@ export default class TabsBase extends WaitsForTemplateViewModel {
|
|||
}
|
||||
|
||||
public onActivate(): void {
|
||||
clearKeyboardActionGroup(KeyboardActionGroup.ACTIVE_TAB);
|
||||
this.updateSelectedNode();
|
||||
this.collection?.selectedSubnodeKind(this.tabKind);
|
||||
this.database?.selectedSubnodeKind(this.tabKind);
|
||||
|
|
|
@ -1,42 +1,23 @@
|
|||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||
import { SampleDataTree } from "Explorer/Tree/SampleDataTree";
|
||||
import { getItemName } from "Utils/APITypeUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import * as React from "react";
|
||||
import shallow from "zustand/shallow";
|
||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||
import DeleteIcon from "../../../images/delete.svg";
|
||||
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
||||
import NewNotebookIcon from "../../../images/notebook/Notebook-new.svg";
|
||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||
import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
||||
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
||||
import RefreshIcon from "../../../images/refresh-cosmos.svg";
|
||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||
import { Areas, ConnectionStatusType, Notebook } from "../../Common/Constants";
|
||||
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { isServerlessAccount } from "../../Utils/CapabilityUtils";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFactory";
|
||||
import { AccordionComponent, AccordionItemComponent } from "../Controls/Accordion/AccordionComponent";
|
||||
import { useDialog } from "../Controls/Dialog";
|
||||
import { TreeComponent, TreeNode, TreeNodeMenuItem } from "../Controls/TreeComponent/TreeComponent";
|
||||
import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "../Explorer";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
@ -45,391 +26,21 @@ import StoredProcedure from "./StoredProcedure";
|
|||
import Trigger from "./Trigger";
|
||||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
|
||||
export const MyNotebooksTitle = "My Notebooks";
|
||||
export const GitHubReposTitle = "GitHub repos";
|
||||
|
||||
interface ResourceTreeProps {
|
||||
container: Explorer;
|
||||
}
|
||||
|
||||
export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: ResourceTreeProps): JSX.Element => {
|
||||
const databases = useDatabases((state) => state.databases);
|
||||
const {
|
||||
isNotebookEnabled,
|
||||
myNotebooksContentRoot,
|
||||
galleryContentRoot,
|
||||
gitHubNotebooksContentRoot,
|
||||
updateNotebookItem,
|
||||
} = useNotebook(
|
||||
const { isNotebookEnabled } = useNotebook(
|
||||
(state) => ({
|
||||
isNotebookEnabled: state.isNotebookEnabled,
|
||||
myNotebooksContentRoot: state.myNotebooksContentRoot,
|
||||
galleryContentRoot: state.galleryContentRoot,
|
||||
gitHubNotebooksContentRoot: state.gitHubNotebooksContentRoot,
|
||||
updateNotebookItem: state.updateNotebookItem,
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
const { activeTab, refreshActiveTab } = useTabs();
|
||||
const { refreshActiveTab } = useTabs();
|
||||
const showScriptNodes =
|
||||
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||
const pseudoDirPath = "PsuedoDir";
|
||||
|
||||
const buildGalleryCallout = (): JSX.Element => {
|
||||
if (
|
||||
LocalStorageUtility.hasItem(StorageKey.GalleryCalloutDismissed) &&
|
||||
LocalStorageUtility.getEntryBoolean(StorageKey.GalleryCalloutDismissed)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const calloutProps: ICalloutProps = {
|
||||
calloutMaxWidth: 350,
|
||||
ariaLabel: "New gallery",
|
||||
role: "alertdialog",
|
||||
gapSpace: 0,
|
||||
target: ".galleryHeader",
|
||||
directionalHint: DirectionalHint.leftTopEdge,
|
||||
onDismiss: () => {
|
||||
LocalStorageUtility.setEntryBoolean(StorageKey.GalleryCalloutDismissed, true);
|
||||
},
|
||||
setInitialFocus: true,
|
||||
};
|
||||
|
||||
const openGalleryProps: ILinkProps = {
|
||||
onClick: () => {
|
||||
LocalStorageUtility.setEntryBoolean(StorageKey.GalleryCalloutDismissed, true);
|
||||
container.openGallery();
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Callout {...calloutProps}>
|
||||
<Stack tokens={{ childrenGap: 10, padding: 20 }}>
|
||||
<Text variant="xLarge" block>
|
||||
New gallery
|
||||
</Text>
|
||||
<Text block>
|
||||
Sample notebooks are now combined in gallery. View and try out samples provided by Microsoft and other
|
||||
contributors.
|
||||
</Text>
|
||||
<Link {...openGalleryProps}>Open gallery</Link>
|
||||
</Stack>
|
||||
</Callout>
|
||||
);
|
||||
};
|
||||
|
||||
const buildNotebooksTree = (): TreeNode => {
|
||||
const notebooksTree: TreeNode = {
|
||||
label: undefined,
|
||||
isExpanded: true,
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (!useNotebook.getState().isPhoenixNotebooks) {
|
||||
notebooksTree.children.push(buildNotebooksTemporarilyDownTree());
|
||||
} else {
|
||||
if (galleryContentRoot) {
|
||||
notebooksTree.children.push(buildGalleryNotebooksTree());
|
||||
}
|
||||
|
||||
if (
|
||||
myNotebooksContentRoot &&
|
||||
useNotebook.getState().isPhoenixNotebooks &&
|
||||
useNotebook.getState().connectionInfo.status === ConnectionStatusType.Connected
|
||||
) {
|
||||
notebooksTree.children.push(buildMyNotebooksTree());
|
||||
}
|
||||
if (container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||
// collapse all other notebook nodes
|
||||
notebooksTree.children.forEach((node) => (node.isExpanded = false));
|
||||
notebooksTree.children.push(buildGitHubNotebooksTree(true));
|
||||
}
|
||||
}
|
||||
return notebooksTree;
|
||||
};
|
||||
|
||||
const buildNotebooksTemporarilyDownTree = (): TreeNode => {
|
||||
return {
|
||||
label: Notebook.temporarilyDownMsg,
|
||||
className: "clickDisabled",
|
||||
};
|
||||
};
|
||||
|
||||
const buildGalleryNotebooksTree = (): TreeNode => {
|
||||
return {
|
||||
label: "Gallery",
|
||||
iconSrc: GalleryIcon,
|
||||
className: "notebookHeader galleryHeader",
|
||||
onClick: () => container.openGallery(),
|
||||
isSelected: () => activeTab?.tabKind === ViewModels.CollectionTabKind.Gallery,
|
||||
};
|
||||
};
|
||||
|
||||
const buildMyNotebooksTree = (): TreeNode => {
|
||||
const myNotebooksTree: TreeNode = buildNotebookDirectoryNode(
|
||||
myNotebooksContentRoot,
|
||||
(item: NotebookContentItem) => {
|
||||
container.openNotebook(item);
|
||||
},
|
||||
);
|
||||
|
||||
myNotebooksTree.isExpanded = true;
|
||||
myNotebooksTree.isAlphaSorted = true;
|
||||
// Remove "Delete" menu item from context menu
|
||||
myNotebooksTree.contextMenu = myNotebooksTree.contextMenu.filter((menuItem) => menuItem.label !== "Delete");
|
||||
return myNotebooksTree;
|
||||
};
|
||||
|
||||
const buildGitHubNotebooksTree = (isConnected: boolean): TreeNode => {
|
||||
const gitHubNotebooksTree: TreeNode = buildNotebookDirectoryNode(
|
||||
gitHubNotebooksContentRoot,
|
||||
(item: NotebookContentItem) => {
|
||||
container.openNotebook(item);
|
||||
},
|
||||
true,
|
||||
);
|
||||
const manageGitContextMenu: TreeNodeMenuItem[] = [
|
||||
{
|
||||
label: "Manage GitHub settings",
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Manage GitHub settings",
|
||||
<GitHubReposPanel
|
||||
explorer={container}
|
||||
gitHubClientProp={container.notebookManager.gitHubClient}
|
||||
junoClientProp={container.notebookManager.junoClient}
|
||||
/>,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Disconnect from GitHub",
|
||||
onClick: () => {
|
||||
TelemetryProcessor.trace(Action.NotebooksGitHubDisconnect, ActionModifiers.Mark, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
container.notebookManager?.gitHubOAuthService.logout();
|
||||
},
|
||||
},
|
||||
];
|
||||
gitHubNotebooksTree.contextMenu = manageGitContextMenu;
|
||||
gitHubNotebooksTree.isExpanded = true;
|
||||
gitHubNotebooksTree.isAlphaSorted = true;
|
||||
|
||||
return gitHubNotebooksTree;
|
||||
};
|
||||
|
||||
const buildChildNodes = (
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
isGithubTree?: boolean,
|
||||
): TreeNode[] => {
|
||||
if (!item || !item.children) {
|
||||
return [];
|
||||
} else {
|
||||
return item.children.map((item) => {
|
||||
const result =
|
||||
item.type === NotebookContentItemType.Directory
|
||||
? buildNotebookDirectoryNode(item, onFileClick, isGithubTree)
|
||||
: buildNotebookFileNode(item, onFileClick, isGithubTree);
|
||||
result.timestamp = item.timestamp;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const buildNotebookFileNode = (
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
isGithubTree?: boolean,
|
||||
): TreeNode => {
|
||||
return {
|
||||
label: item.name,
|
||||
iconSrc: NotebookUtil.isNotebookFile(item.path) ? NotebookIcon : FileIcon,
|
||||
className: "notebookHeader",
|
||||
onClick: () => onFileClick(item),
|
||||
isSelected: () => {
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
/* TODO Redesign Tab interface so that resource tree doesn't need to know about NotebookV2Tab.
|
||||
NotebookV2Tab could be dynamically imported, but not worth it to just get this type right.
|
||||
*/
|
||||
(activeTab as any).notebookPath() === item.path
|
||||
);
|
||||
},
|
||||
contextMenu: createFileContextMenu(container, item, isGithubTree),
|
||||
data: item,
|
||||
};
|
||||
};
|
||||
|
||||
const createFileContextMenu = (
|
||||
container: Explorer,
|
||||
item: NotebookContentItem,
|
||||
isGithubTree?: boolean,
|
||||
): TreeNodeMenuItem[] => {
|
||||
let items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
label: "Rename",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => container.renameNotebook(item, isGithubTree),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
iconSrc: DeleteIcon,
|
||||
onClick: () => {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Confirm delete",
|
||||
`Are you sure you want to delete "${item.name}"`,
|
||||
"Delete",
|
||||
() => container.deleteNotebookFile(item, isGithubTree),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Copy to ...",
|
||||
iconSrc: CopyIcon,
|
||||
onClick: () => copyNotebook(container, item),
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => container.downloadFile(item),
|
||||
},
|
||||
];
|
||||
|
||||
if (item.type === NotebookContentItemType.Notebook && userContext.features.publicGallery) {
|
||||
items.push({
|
||||
label: "Publish to gallery",
|
||||
iconSrc: PublishIcon,
|
||||
onClick: async () => {
|
||||
TelemetryProcessor.trace(Action.NotebooksGalleryClickPublishToGallery, ActionModifiers.Mark, {
|
||||
source: Source.ResourceTreeMenu,
|
||||
});
|
||||
|
||||
const content = await container.readFile(item);
|
||||
if (content) {
|
||||
await container.publishNotebook(item.name, content);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// "Copy to ..." isn't needed if github locations are not available
|
||||
if (!container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||
items = items.filter((item) => item.label !== "Copy to ...");
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
const copyNotebook = async (container: Explorer, item: NotebookContentItem) => {
|
||||
const content = await container.readFile(item);
|
||||
if (content) {
|
||||
container.copyNotebook(item.name, content);
|
||||
}
|
||||
};
|
||||
|
||||
const createDirectoryContextMenu = (
|
||||
container: Explorer,
|
||||
item: NotebookContentItem,
|
||||
isGithubTree?: boolean,
|
||||
): TreeNodeMenuItem[] => {
|
||||
let items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
label: "Refresh",
|
||||
iconSrc: RefreshIcon,
|
||||
onClick: () => loadSubitems(item, isGithubTree),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
iconSrc: DeleteIcon,
|
||||
onClick: () => {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Confirm delete",
|
||||
`Are you sure you want to delete "${item.name}?"`,
|
||||
"Delete",
|
||||
() => container.deleteNotebookFile(item, isGithubTree),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Rename",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => container.renameNotebook(item, isGithubTree),
|
||||
},
|
||||
{
|
||||
label: "New Directory",
|
||||
iconSrc: NewNotebookIcon,
|
||||
onClick: () => container.onCreateDirectory(item, isGithubTree),
|
||||
},
|
||||
{
|
||||
label: "Upload File",
|
||||
iconSrc: NewNotebookIcon,
|
||||
onClick: () => container.openUploadFilePanel(item),
|
||||
},
|
||||
];
|
||||
|
||||
//disallow renaming of temporary notebook workspace
|
||||
if (item?.path === useNotebook.getState().notebookBasePath) {
|
||||
items = items.filter((item) => item.label !== "Rename");
|
||||
}
|
||||
|
||||
// For GitHub paths remove "Delete", "Rename", "New Directory", "Upload File"
|
||||
if (GitHubUtils.fromContentUri(item.path)) {
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
item.label !== "Delete" &&
|
||||
item.label !== "Rename" &&
|
||||
item.label !== "New Directory" &&
|
||||
item.label !== "Upload File",
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
const buildNotebookDirectoryNode = (
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
isGithubTree?: boolean,
|
||||
): TreeNode => {
|
||||
return {
|
||||
label: item.name,
|
||||
iconSrc: undefined,
|
||||
className: "notebookHeader",
|
||||
isAlphaSorted: true,
|
||||
isLeavesParentsSeparate: true,
|
||||
onClick: () => {
|
||||
if (!item.children) {
|
||||
loadSubitems(item, isGithubTree);
|
||||
}
|
||||
},
|
||||
isSelected: () => {
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
/* TODO Redesign Tab interface so that resource tree doesn't need to know about NotebookV2Tab.
|
||||
NotebookV2Tab could be dynamically imported, but not worth it to just get this type right.
|
||||
*/
|
||||
(activeTab as any).notebookPath() === item.path
|
||||
);
|
||||
},
|
||||
contextMenu: item.path !== pseudoDirPath ? createDirectoryContextMenu(container, item, isGithubTree) : undefined,
|
||||
data: item,
|
||||
children: buildChildNodes(item, onFileClick, isGithubTree),
|
||||
};
|
||||
};
|
||||
|
||||
const buildDataTree = (): TreeNode => {
|
||||
const databaseTreeNodes: TreeNode[] = databases.map((database: ViewModels.Database) => {
|
||||
|
@ -757,11 +368,6 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||
return traverse(schema);
|
||||
};
|
||||
|
||||
const loadSubitems = async (item: NotebookContentItem, isGithubTree?: boolean): Promise<void> => {
|
||||
const updatedItem = await container.notebookManager?.notebookContentClient?.updateItemChildren(item);
|
||||
updateNotebookItem(updatedItem, isGithubTree);
|
||||
};
|
||||
|
||||
const dataRootNode = buildDataTree();
|
||||
const isSampleDataEnabled =
|
||||
useQueryCopilot().copilotEnabled &&
|
||||
|
@ -775,46 +381,16 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||
{!isNotebookEnabled && !isSampleDataEnabled && (
|
||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||
)}
|
||||
{isNotebookEnabled && !isSampleDataEnabled && (
|
||||
<>
|
||||
<AccordionComponent>
|
||||
<AccordionItemComponent title={"DATA"} isExpanded={!gitHubNotebooksContentRoot}>
|
||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||
</AccordionItemComponent>
|
||||
</AccordionComponent>
|
||||
|
||||
{/* {buildGalleryCallout()} */}
|
||||
</>
|
||||
)}
|
||||
{!isNotebookEnabled && isSampleDataEnabled && (
|
||||
<>
|
||||
<AccordionComponent>
|
||||
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}>
|
||||
<AccordionItemComponent title={"MY DATA"} isExpanded={true}>
|
||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||
</AccordionItemComponent>
|
||||
<AccordionItemComponent title={"SAMPLE DATA"} containerStyles={{ display: "table" }}>
|
||||
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
||||
</AccordionItemComponent>
|
||||
</AccordionComponent>
|
||||
|
||||
{/* {buildGalleryCallout()} */}
|
||||
</>
|
||||
)}
|
||||
{isNotebookEnabled && isSampleDataEnabled && (
|
||||
<>
|
||||
<AccordionComponent>
|
||||
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}>
|
||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||
</AccordionItemComponent>
|
||||
<AccordionItemComponent title={"SAMPLE DATA"} containerStyles={{ display: "table" }}>
|
||||
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
||||
</AccordionItemComponent>
|
||||
<AccordionItemComponent title={"NOTEBOOKS"}>
|
||||
<TreeComponent className="notebookResourceTree" rootNode={buildNotebooksTree()} />
|
||||
</AccordionItemComponent>
|
||||
</AccordionComponent>
|
||||
|
||||
{/* {buildGalleryCallout()} */}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -1,42 +1,21 @@
|
|||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||
import { getItemName } from "Utils/APITypeUtils";
|
||||
import * as ko from "knockout";
|
||||
import * as React from "react";
|
||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||
import DeleteIcon from "../../../images/delete.svg";
|
||||
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
||||
import NewNotebookIcon from "../../../images/notebook/Notebook-new.svg";
|
||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||
import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
||||
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
||||
import RefreshIcon from "../../../images/refresh-cosmos.svg";
|
||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||
import { Areas } from "../../Common/Constants";
|
||||
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { isServerlessAccount } from "../../Utils/CapabilityUtils";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFactory";
|
||||
import { AccordionComponent, AccordionItemComponent } from "../Controls/Accordion/AccordionComponent";
|
||||
import { useDialog } from "../Controls/Dialog";
|
||||
import { TreeComponent, TreeNode, TreeNodeMenuItem } from "../Controls/TreeComponent/TreeComponent";
|
||||
import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "../Explorer";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
@ -46,19 +25,8 @@ import Trigger from "./Trigger";
|
|||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
|
||||
export class ResourceTreeAdapter implements ReactAdapter {
|
||||
public static readonly MyNotebooksTitle = "My Notebooks";
|
||||
public static readonly GitHubReposTitle = "GitHub repos";
|
||||
|
||||
private static readonly DataTitle = "DATA";
|
||||
private static readonly NotebooksTitle = "NOTEBOOKS";
|
||||
private static readonly PseudoDirPath = "PsuedoDir";
|
||||
|
||||
public parameters: ko.Observable<number>;
|
||||
|
||||
public galleryContentRoot: NotebookContentItem;
|
||||
public myNotebooksContentRoot: NotebookContentItem;
|
||||
public gitHubNotebooksContentRoot: NotebookContentItem;
|
||||
|
||||
public constructor(private container: Explorer) {
|
||||
this.parameters = ko.observable(Date.now());
|
||||
|
||||
|
@ -76,111 +44,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
this.triggerRender();
|
||||
}
|
||||
|
||||
private traceMyNotebookTreeInfo() {
|
||||
const myNotebooksTree = this.myNotebooksContentRoot;
|
||||
if (myNotebooksTree.children) {
|
||||
// Count 1st generation children (tree is lazy-loaded)
|
||||
const nodeCounts = { files: 0, notebooks: 0, directories: 0 };
|
||||
myNotebooksTree.children.forEach((treeNode) => {
|
||||
switch ((treeNode as NotebookContentItem).type) {
|
||||
case NotebookContentItemType.File:
|
||||
nodeCounts.files++;
|
||||
break;
|
||||
case NotebookContentItemType.Directory:
|
||||
nodeCounts.directories++;
|
||||
break;
|
||||
case NotebookContentItemType.Notebook:
|
||||
nodeCounts.notebooks++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
TelemetryProcessor.trace(Action.RefreshResourceTreeMyNotebooks, ActionModifiers.Mark, { ...nodeCounts });
|
||||
}
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
const dataRootNode = this.buildDataTree();
|
||||
const notebooksRootNode = this.buildNotebooksTrees();
|
||||
|
||||
if (useNotebook.getState().isNotebookEnabled) {
|
||||
return (
|
||||
<>
|
||||
<AccordionComponent>
|
||||
<AccordionItemComponent title={ResourceTreeAdapter.DataTitle} isExpanded={!this.gitHubNotebooksContentRoot}>
|
||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||
</AccordionItemComponent>
|
||||
<AccordionItemComponent title={ResourceTreeAdapter.NotebooksTitle}>
|
||||
<TreeComponent className="notebookResourceTree" rootNode={notebooksRootNode} />
|
||||
</AccordionItemComponent>
|
||||
</AccordionComponent>
|
||||
|
||||
{/* {this.galleryContentRoot && this.buildGalleryCallout()} */}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <TreeComponent className="dataResourceTree" rootNode={dataRootNode} />;
|
||||
}
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void[]> {
|
||||
const refreshTasks: Promise<void>[] = [];
|
||||
|
||||
this.galleryContentRoot = {
|
||||
name: "Gallery",
|
||||
path: "Gallery",
|
||||
type: NotebookContentItemType.File,
|
||||
};
|
||||
this.myNotebooksContentRoot = {
|
||||
name: useNotebook.getState().notebookFolderName,
|
||||
path: useNotebook.getState().notebookBasePath,
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
|
||||
// Only if notebook server is available we can refresh
|
||||
if (useNotebook.getState().notebookServerInfo?.notebookServerEndpoint) {
|
||||
refreshTasks.push(
|
||||
this.container.refreshContentItem(this.myNotebooksContentRoot).then(() => {
|
||||
this.triggerRender();
|
||||
this.traceMyNotebookTreeInfo();
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.gitHubNotebooksContentRoot = {
|
||||
name: ResourceTreeAdapter.GitHubReposTitle,
|
||||
path: ResourceTreeAdapter.PseudoDirPath,
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
|
||||
return Promise.all(refreshTasks);
|
||||
}
|
||||
|
||||
public initializeGitHubRepos(pinnedRepos: IPinnedRepo[]): void {
|
||||
if (this.gitHubNotebooksContentRoot) {
|
||||
this.gitHubNotebooksContentRoot.children = [];
|
||||
pinnedRepos?.forEach((pinnedRepo) => {
|
||||
const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name);
|
||||
const repoTreeItem: NotebookContentItem = {
|
||||
name: repoFullName,
|
||||
path: ResourceTreeAdapter.PseudoDirPath,
|
||||
type: NotebookContentItemType.Directory,
|
||||
children: [],
|
||||
};
|
||||
|
||||
pinnedRepo.branches.forEach((branch) => {
|
||||
repoTreeItem.children.push({
|
||||
name: branch.name,
|
||||
path: GitHubUtils.toContentUri(pinnedRepo.owner, pinnedRepo.name, branch.name, ""),
|
||||
type: NotebookContentItemType.Directory,
|
||||
});
|
||||
});
|
||||
|
||||
this.gitHubNotebooksContentRoot.children.push(repoTreeItem);
|
||||
});
|
||||
|
||||
this.triggerRender();
|
||||
}
|
||||
return <TreeComponent className="dataResourceTree" rootNode={dataRootNode} />;
|
||||
}
|
||||
|
||||
private buildDataTree(): TreeNode {
|
||||
|
@ -504,365 +370,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
return traverse(schema);
|
||||
}
|
||||
|
||||
private buildNotebooksTrees(): TreeNode {
|
||||
let notebooksTree: TreeNode = {
|
||||
label: undefined,
|
||||
isExpanded: true,
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (this.galleryContentRoot) {
|
||||
notebooksTree.children.push(this.buildGalleryNotebooksTree());
|
||||
}
|
||||
|
||||
if (this.myNotebooksContentRoot) {
|
||||
notebooksTree.children.push(this.buildMyNotebooksTree());
|
||||
}
|
||||
|
||||
if (this.gitHubNotebooksContentRoot) {
|
||||
// collapse all other notebook nodes
|
||||
notebooksTree.children.forEach((node) => (node.isExpanded = false));
|
||||
notebooksTree.children.push(this.buildGitHubNotebooksTree());
|
||||
}
|
||||
|
||||
return notebooksTree;
|
||||
}
|
||||
|
||||
private buildGalleryCallout(): JSX.Element {
|
||||
if (
|
||||
LocalStorageUtility.hasItem(StorageKey.GalleryCalloutDismissed) &&
|
||||
LocalStorageUtility.getEntryBoolean(StorageKey.GalleryCalloutDismissed)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const calloutProps: ICalloutProps = {
|
||||
calloutMaxWidth: 350,
|
||||
ariaLabel: "New gallery",
|
||||
role: "alertdialog",
|
||||
gapSpace: 0,
|
||||
target: ".galleryHeader",
|
||||
directionalHint: DirectionalHint.leftTopEdge,
|
||||
onDismiss: () => {
|
||||
LocalStorageUtility.setEntryBoolean(StorageKey.GalleryCalloutDismissed, true);
|
||||
this.triggerRender();
|
||||
},
|
||||
setInitialFocus: true,
|
||||
};
|
||||
|
||||
const openGalleryProps: ILinkProps = {
|
||||
onClick: () => {
|
||||
LocalStorageUtility.setEntryBoolean(StorageKey.GalleryCalloutDismissed, true);
|
||||
this.container.openGallery();
|
||||
this.triggerRender();
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Callout {...calloutProps}>
|
||||
<Stack tokens={{ childrenGap: 10, padding: 20 }}>
|
||||
<Text variant="xLarge" block>
|
||||
New gallery
|
||||
</Text>
|
||||
<Text block>
|
||||
Sample notebooks are now combined in gallery. View and try out samples provided by Microsoft and other
|
||||
contributors.
|
||||
</Text>
|
||||
<Link {...openGalleryProps}>Open gallery</Link>
|
||||
</Stack>
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
private buildGalleryNotebooksTree(): TreeNode {
|
||||
return {
|
||||
label: "Gallery",
|
||||
iconSrc: GalleryIcon,
|
||||
className: "notebookHeader galleryHeader",
|
||||
onClick: () => this.container.openGallery(),
|
||||
isSelected: () => {
|
||||
const activeTab = useTabs.getState().activeTab;
|
||||
return activeTab && activeTab.tabKind === ViewModels.CollectionTabKind.Gallery;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private buildMyNotebooksTree(): TreeNode {
|
||||
const myNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||
this.myNotebooksContentRoot,
|
||||
(item: NotebookContentItem) => {
|
||||
this.container.openNotebook(item).then((hasOpened) => {
|
||||
if (hasOpened) {
|
||||
mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item);
|
||||
}
|
||||
});
|
||||
},
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
myNotebooksTree.isExpanded = true;
|
||||
myNotebooksTree.isAlphaSorted = true;
|
||||
// Remove "Delete" menu item from context menu
|
||||
myNotebooksTree.contextMenu = myNotebooksTree.contextMenu.filter((menuItem) => menuItem.label !== "Delete");
|
||||
return myNotebooksTree;
|
||||
}
|
||||
|
||||
private buildGitHubNotebooksTree(): TreeNode {
|
||||
const gitHubNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||
this.gitHubNotebooksContentRoot,
|
||||
(item: NotebookContentItem) => {
|
||||
this.container.openNotebook(item).then((hasOpened) => {
|
||||
if (hasOpened) {
|
||||
mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item);
|
||||
}
|
||||
});
|
||||
},
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
gitHubNotebooksTree.contextMenu = [
|
||||
{
|
||||
label: "Manage GitHub settings",
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Manage GitHub settings",
|
||||
<GitHubReposPanel
|
||||
explorer={this.container}
|
||||
gitHubClientProp={this.container.notebookManager.gitHubClient}
|
||||
junoClientProp={this.container.notebookManager.junoClient}
|
||||
/>,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Disconnect from GitHub",
|
||||
onClick: () => {
|
||||
TelemetryProcessor.trace(Action.NotebooksGitHubDisconnect, ActionModifiers.Mark, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
this.container.notebookManager?.gitHubOAuthService.logout();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
gitHubNotebooksTree.isExpanded = true;
|
||||
gitHubNotebooksTree.isAlphaSorted = true;
|
||||
|
||||
return gitHubNotebooksTree;
|
||||
}
|
||||
|
||||
private buildChildNodes(
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
createDirectoryContextMenu: boolean,
|
||||
createFileContextMenu: boolean,
|
||||
): TreeNode[] {
|
||||
if (!item || !item.children) {
|
||||
return [];
|
||||
} else {
|
||||
return item.children.map((item) => {
|
||||
const result =
|
||||
item.type === NotebookContentItemType.Directory
|
||||
? this.buildNotebookDirectoryNode(item, onFileClick, createDirectoryContextMenu, createFileContextMenu)
|
||||
: this.buildNotebookFileNode(item, onFileClick, createFileContextMenu);
|
||||
result.timestamp = item.timestamp;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private buildNotebookFileNode(
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
createFileContextMenu: boolean,
|
||||
): TreeNode {
|
||||
return {
|
||||
label: item.name,
|
||||
iconSrc: NotebookUtil.isNotebookFile(item.path) ? NotebookIcon : FileIcon,
|
||||
className: "notebookHeader",
|
||||
onClick: () => onFileClick(item),
|
||||
isSelected: () => {
|
||||
const activeTab = useTabs.getState().activeTab;
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
/* TODO Redesign Tab interface so that resource tree doesn't need to know about NotebookV2Tab.
|
||||
NotebookV2Tab could be dynamically imported, but not worth it to just get this type right.
|
||||
*/
|
||||
(activeTab as any).notebookPath() === item.path
|
||||
);
|
||||
},
|
||||
contextMenu: createFileContextMenu && this.createFileContextMenu(item),
|
||||
data: item,
|
||||
};
|
||||
}
|
||||
|
||||
private createFileContextMenu(item: NotebookContentItem): TreeNodeMenuItem[] {
|
||||
let items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
label: "Rename",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => this.container.renameNotebook(item),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
iconSrc: DeleteIcon,
|
||||
onClick: () => {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Confirm delete",
|
||||
`Are you sure you want to delete "${item.name}"`,
|
||||
"Delete",
|
||||
() => this.container.deleteNotebookFile(item).then(() => this.triggerRender()),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Copy to ...",
|
||||
iconSrc: CopyIcon,
|
||||
onClick: () => this.copyNotebook(item),
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => this.container.downloadFile(item),
|
||||
},
|
||||
];
|
||||
|
||||
if (item.type === NotebookContentItemType.Notebook) {
|
||||
items.push({
|
||||
label: "Publish to gallery",
|
||||
iconSrc: PublishIcon,
|
||||
onClick: async () => {
|
||||
TelemetryProcessor.trace(Action.NotebooksGalleryClickPublishToGallery, ActionModifiers.Mark, {
|
||||
source: Source.ResourceTreeMenu,
|
||||
});
|
||||
|
||||
const content = await this.container.readFile(item);
|
||||
if (content) {
|
||||
await this.container.publishNotebook(item.name, content);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// "Copy to ..." isn't needed if github locations are not available
|
||||
if (!this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||
items = items.filter((item) => item.label !== "Copy to ...");
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private copyNotebook = async (item: NotebookContentItem) => {
|
||||
const content = await this.container.readFile(item);
|
||||
if (content) {
|
||||
this.container.copyNotebook(item.name, content);
|
||||
}
|
||||
};
|
||||
|
||||
private createDirectoryContextMenu(item: NotebookContentItem): TreeNodeMenuItem[] {
|
||||
let items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
label: "Refresh",
|
||||
iconSrc: RefreshIcon,
|
||||
onClick: () => this.container.refreshContentItem(item).then(() => this.triggerRender()),
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
iconSrc: DeleteIcon,
|
||||
onClick: () => {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Confirm delete",
|
||||
`Are you sure you want to delete "${item.name}?"`,
|
||||
"Delete",
|
||||
() => this.container.deleteNotebookFile(item).then(() => this.triggerRender()),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Rename",
|
||||
iconSrc: NotebookIcon,
|
||||
onClick: () => this.container.renameNotebook(item),
|
||||
},
|
||||
{
|
||||
label: "New Directory",
|
||||
iconSrc: NewNotebookIcon,
|
||||
onClick: () => this.container.onCreateDirectory(item),
|
||||
},
|
||||
{
|
||||
label: "Upload File",
|
||||
iconSrc: NewNotebookIcon,
|
||||
onClick: () => this.container.openUploadFilePanel(item),
|
||||
},
|
||||
];
|
||||
|
||||
//disallow renaming of temporary notebook workspace
|
||||
if (item?.path === useNotebook.getState().notebookBasePath) {
|
||||
items = items.filter((item) => item.label !== "Rename");
|
||||
}
|
||||
|
||||
// For GitHub paths remove "Delete", "Rename", "New Directory", "Upload File"
|
||||
if (GitHubUtils.fromContentUri(item.path)) {
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
item.label !== "Delete" &&
|
||||
item.label !== "Rename" &&
|
||||
item.label !== "New Directory" &&
|
||||
item.label !== "Upload File",
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private buildNotebookDirectoryNode(
|
||||
item: NotebookContentItem,
|
||||
onFileClick: (item: NotebookContentItem) => void,
|
||||
createDirectoryContextMenu: boolean,
|
||||
createFileContextMenu: boolean,
|
||||
): TreeNode {
|
||||
return {
|
||||
label: item.name,
|
||||
iconSrc: undefined,
|
||||
className: "notebookHeader",
|
||||
isAlphaSorted: true,
|
||||
isLeavesParentsSeparate: true,
|
||||
onClick: () => {
|
||||
if (!item.children) {
|
||||
this.container.refreshContentItem(item).then(() => this.triggerRender());
|
||||
}
|
||||
},
|
||||
isSelected: () => {
|
||||
const activeTab = useTabs.getState().activeTab;
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
/* TODO Redesign Tab interface so that resource tree doesn't need to know about NotebookV2Tab.
|
||||
NotebookV2Tab could be dynamically imported, but not worth it to just get this type right.
|
||||
*/
|
||||
(activeTab as any).notebookPath() === item.path
|
||||
);
|
||||
},
|
||||
contextMenu:
|
||||
createDirectoryContextMenu && item.path !== ResourceTreeAdapter.PseudoDirPath
|
||||
? this.createDirectoryContextMenu(item)
|
||||
: undefined,
|
||||
data: item,
|
||||
children: this.buildChildNodes(item, onFileClick, createDirectoryContextMenu, createFileContextMenu),
|
||||
};
|
||||
}
|
||||
|
||||
public triggerRender() {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
|
|
@ -17,8 +17,17 @@ export type KeyboardHandlerMap = Partial<Record<KeyboardAction, KeyboardActionHa
|
|||
* Each group can be updated separately, but, when updated, must be completely replaced.
|
||||
*/
|
||||
export enum KeyboardActionGroup {
|
||||
/** Keyboard actions related to tab navigation. */
|
||||
TABS = "TABS",
|
||||
|
||||
/** Keyboard actions managed by the global command bar. */
|
||||
COMMAND_BAR = "COMMAND_BAR",
|
||||
|
||||
/**
|
||||
* Keyboard actions specific to the active tab.
|
||||
* This group is automatically cleared when the active tab changes.
|
||||
*/
|
||||
ACTIVE_TAB = "ACTIVE_TAB",
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,6 +38,7 @@ export enum KeyboardAction {
|
|||
EXECUTE_ITEM = "EXECUTE_ITEM",
|
||||
CANCEL_OR_DISCARD = "CANCEL_OR_DISCARD",
|
||||
SAVE_ITEM = "SAVE_ITEM",
|
||||
DOWNLOAD_ITEM = "DOWNLOAD_ITEM",
|
||||
OPEN_QUERY = "OPEN_QUERY",
|
||||
OPEN_QUERY_FROM_DISK = "OPEN_QUERY_FROM_DISK",
|
||||
NEW_SPROC = "NEW_SPROC",
|
||||
|
@ -42,6 +52,8 @@ export enum KeyboardAction {
|
|||
SELECT_LEFT_TAB = "SELECT_LEFT_TAB",
|
||||
SELECT_RIGHT_TAB = "SELECT_RIGHT_TAB",
|
||||
CLOSE_TAB = "CLOSE_TAB",
|
||||
SEARCH = "SEARCH",
|
||||
CLEAR_SEARCH = "CLEAR_SEARCH",
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,9 +66,10 @@ const bindings: Record<KeyboardAction, string[]> = {
|
|||
// See https://www.npmjs.com/package/tinykeys#commonly-used-keys-and-codes for more information on the expected values for keyboard shortcuts.
|
||||
|
||||
[KeyboardAction.NEW_QUERY]: ["$mod+J", "Alt+N Q"],
|
||||
[KeyboardAction.EXECUTE_ITEM]: ["Shift+Enter"],
|
||||
[KeyboardAction.EXECUTE_ITEM]: ["Shift+Enter", "F5"],
|
||||
[KeyboardAction.CANCEL_OR_DISCARD]: ["Escape"],
|
||||
[KeyboardAction.SAVE_ITEM]: ["$mod+S"],
|
||||
[KeyboardAction.DOWNLOAD_ITEM]: ["$mod+Shift+S"],
|
||||
[KeyboardAction.OPEN_QUERY]: ["$mod+O"],
|
||||
[KeyboardAction.OPEN_QUERY_FROM_DISK]: ["$mod+Shift+O"],
|
||||
[KeyboardAction.NEW_SPROC]: ["Alt+N P"],
|
||||
|
@ -67,9 +80,11 @@ const bindings: Record<KeyboardAction, string[]> = {
|
|||
[KeyboardAction.NEW_ITEM]: ["Alt+N I"],
|
||||
[KeyboardAction.DELETE_ITEM]: ["Alt+D"],
|
||||
[KeyboardAction.TOGGLE_COPILOT]: ["$mod+P"],
|
||||
[KeyboardAction.SELECT_LEFT_TAB]: ["$mod+Alt+["],
|
||||
[KeyboardAction.SELECT_RIGHT_TAB]: ["$mod+Alt+]"],
|
||||
[KeyboardAction.SELECT_LEFT_TAB]: ["$mod+Alt+[", "$mod+Shift+F6"],
|
||||
[KeyboardAction.SELECT_RIGHT_TAB]: ["$mod+Alt+]", "$mod+F6"],
|
||||
[KeyboardAction.CLOSE_TAB]: ["$mod+Alt+W"],
|
||||
[KeyboardAction.SEARCH]: ["$mod+Shift+F"],
|
||||
[KeyboardAction.CLEAR_SEARCH]: ["$mod+Shift+C"],
|
||||
};
|
||||
|
||||
interface KeyboardShortcutState {
|
||||
|
@ -89,13 +104,24 @@ interface KeyboardShortcutState {
|
|||
setHandlers: (group: KeyboardActionGroup, handlers: KeyboardHandlerMap) => void;
|
||||
}
|
||||
|
||||
export type KeyboardHandlerSetter = (handlers: KeyboardHandlerMap) => void;
|
||||
|
||||
/**
|
||||
* Defines the calling component as the manager of the keyboard actions for the given group.
|
||||
* @param group The group of keyboard actions to manage.
|
||||
* @returns A function that can be used to set the keyboard action handlers for the given group.
|
||||
*/
|
||||
export const useKeyboardActionGroup = (group: KeyboardActionGroup) => (handlers: KeyboardHandlerMap) =>
|
||||
useKeyboardActionHandlers.getState().setHandlers(group, handlers);
|
||||
export const useKeyboardActionGroup: (group: KeyboardActionGroup) => KeyboardHandlerSetter =
|
||||
(group: KeyboardActionGroup) => (handlers: KeyboardHandlerMap) =>
|
||||
useKeyboardActionHandlers.getState().setHandlers(group, handlers);
|
||||
|
||||
/**
|
||||
* Clears the keyboard action handlers for the given group.
|
||||
* @param group The group of keyboard actions to clear.
|
||||
*/
|
||||
export const clearKeyboardActionGroup = (group: KeyboardActionGroup) => {
|
||||
useKeyboardActionHandlers.getState().setHandlers(group, {});
|
||||
};
|
||||
|
||||
const useKeyboardActionHandlers: UseStore<KeyboardShortcutState> = create((set, get) => ({
|
||||
allHandlers: {},
|
||||
|
|
|
@ -38,7 +38,6 @@ export type Features = {
|
|||
readonly copilotChatFixedMonacoEditorHeight: boolean;
|
||||
readonly enablePriorityBasedExecution: boolean;
|
||||
readonly disableConnectionStringLogin: boolean;
|
||||
readonly enableLegacyMongoShell: boolean;
|
||||
|
||||
// can be set via both flight and feature flag
|
||||
autoscaleDefault: boolean;
|
||||
|
@ -109,7 +108,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
||||
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
||||
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
||||
enableLegacyMongoShell: "true" === get("enablelegacymongoshell"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -154,8 +154,16 @@ export const allowedNotebookServerUrls: ReadonlyArray<string> = [];
|
|||
export function useNewPortalBackendEndpoint(backendApi: string): boolean {
|
||||
// This maps backend APIs to the environments supported by the new backend.
|
||||
const newBackendApiEnvironmentMap: { [key: string]: string[] } = {
|
||||
[BackendApi.GenerateToken]: [PortalBackendEndpoints.Development],
|
||||
[BackendApi.PortalSettings]: [PortalBackendEndpoints.Development, PortalBackendEndpoints.Mpac],
|
||||
[BackendApi.GenerateToken]: [
|
||||
PortalBackendEndpoints.Development,
|
||||
PortalBackendEndpoints.Mpac,
|
||||
PortalBackendEndpoints.Prod,
|
||||
],
|
||||
[BackendApi.PortalSettings]: [
|
||||
PortalBackendEndpoints.Development,
|
||||
PortalBackendEndpoints.Mpac,
|
||||
PortalBackendEndpoints.Prod,
|
||||
],
|
||||
};
|
||||
|
||||
if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) {
|
||||
|
|
|
@ -245,7 +245,6 @@ export function downloadItem(
|
|||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
container.getDownloadModalConent(name),
|
||||
);
|
||||
}
|
||||
export async function downloadNotebookItem(
|
||||
|
@ -278,7 +277,6 @@ export async function downloadNotebookItem(
|
|||
metadata.untrusted = true;
|
||||
}
|
||||
|
||||
await container.importAndOpenContent(data.name, JSON.stringify(notebook));
|
||||
logConsoleInfo(`Successfully downloaded ${data.name} to ${useNotebook.getState().notebookFolderName}`);
|
||||
|
||||
const increaseDownloadResponse = await junoClient.increaseNotebookDownloadCount(data.id);
|
||||
|
|
Loading…
Reference in New Issue