@logilab/sparqlexplorer 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +73 -0
  3. package/build/src/lib/App.d.ts +6 -0
  4. package/build/src/lib/components/ClassList.d.ts +9 -0
  5. package/build/src/lib/components/EndpointForm.d.ts +1 -0
  6. package/build/src/lib/components/GraphSelector.d.ts +1 -0
  7. package/build/src/lib/components/HomePage.d.ts +1 -0
  8. package/build/src/lib/components/SearchPage.d.ts +1 -0
  9. package/build/src/lib/components/UriPage.d.ts +1 -0
  10. package/build/src/lib/components/UtilsComponents.d.ts +11 -0
  11. package/build/src/lib/components/ViewSelector.d.ts +2 -0
  12. package/build/src/lib/components/Yasgui.d.ts +6 -0
  13. package/build/src/lib/components/YasrTableResults.d.ts +55 -0
  14. package/build/src/lib/components/layout/DrawerContent.d.ts +4 -0
  15. package/build/src/lib/components/layout/Footer.d.ts +1 -0
  16. package/build/src/lib/components/layout/Layout.d.ts +2 -0
  17. package/build/src/lib/components/layout/Navbar.d.ts +4 -0
  18. package/build/src/lib/components/uri/URIDefaultView.d.ts +6 -0
  19. package/build/src/lib/components/uri/URIWithSelectedView.d.ts +7 -0
  20. package/build/src/lib/context/AuthContext.d.ts +11 -0
  21. package/build/src/lib/context/ConfigContext.d.ts +15 -0
  22. package/build/src/lib/context/ViewsContext.d.ts +14 -0
  23. package/build/src/lib/hooks/useClasses.d.ts +6 -0
  24. package/build/src/lib/hooks/useGraphs.d.ts +8 -0
  25. package/build/src/lib/hooks/useNavigateWithParams.d.ts +5 -0
  26. package/build/src/lib/hooks/useParams.d.ts +1 -0
  27. package/build/src/lib/hooks/useURIData.d.ts +21 -0
  28. package/build/src/lib/hooks/useURILink.d.ts +1 -0
  29. package/build/src/lib/index.d.ts +8 -0
  30. package/build/src/lib/public-path.d.ts +1 -0
  31. package/build/src/lib/routes/Home.d.ts +1 -0
  32. package/build/src/lib/routes/Search.d.ts +1 -0
  33. package/build/src/lib/routes/Uri.d.ts +1 -0
  34. package/build/src/lib/routes/Yasgui.d.ts +1 -0
  35. package/build/src/lib/setupTests.d.ts +1 -0
  36. package/build/src/lib/utils/getIconFromURI.d.ts +3 -0
  37. package/build/src/lib/utils/utils.d.ts +24 -0
  38. package/build/src/lib/yasgui-utils/Storage.d.ts +16 -0
  39. package/build/src/lib/yasgui-utils/index.d.ts +16 -0
  40. package/build/static/js/lib.js +285 -0
  41. package/build/static/js/lib.js.LICENSE.txt +71 -0
  42. package/build/static/js/lib.js.map +1 -0
  43. package/package.json +73 -0
  44. package/src/app/index.css +23 -0
  45. package/src/app/index.tsx +28 -0
  46. package/src/app/templates/constants.js +1 -0
  47. package/src/app/templates/index.hbs +18 -0
  48. package/src/lib/App.css +83 -0
  49. package/src/lib/App.tsx +31 -0
  50. package/src/lib/components/ClassList.tsx +173 -0
  51. package/src/lib/components/EndpointForm.tsx +126 -0
  52. package/src/lib/components/GraphSelector.tsx +114 -0
  53. package/src/lib/components/HomePage.tsx +51 -0
  54. package/src/lib/components/SearchPage.tsx +211 -0
  55. package/src/lib/components/UriPage.tsx +158 -0
  56. package/src/lib/components/UtilsComponents.tsx +54 -0
  57. package/src/lib/components/ViewSelector.css +22 -0
  58. package/src/lib/components/ViewSelector.tsx +78 -0
  59. package/src/lib/components/Yasgui.tsx +127 -0
  60. package/src/lib/components/YasrTableResults.ts +529 -0
  61. package/src/lib/components/layout/DrawerContent.tsx +55 -0
  62. package/src/lib/components/layout/Footer.tsx +32 -0
  63. package/src/lib/components/layout/Layout.tsx +103 -0
  64. package/src/lib/components/layout/Navbar.tsx +231 -0
  65. package/src/lib/components/uri/URIDefaultView.tsx +392 -0
  66. package/src/lib/components/uri/URIWithSelectedView.tsx +31 -0
  67. package/src/lib/context/AuthContext.tsx +32 -0
  68. package/src/lib/context/ConfigContext.tsx +50 -0
  69. package/src/lib/context/ViewsContext.tsx +53 -0
  70. package/src/lib/hooks/useClasses.ts +48 -0
  71. package/src/lib/hooks/useGraphs.ts +67 -0
  72. package/src/lib/hooks/useNavigateWithParams.tsx +97 -0
  73. package/src/lib/hooks/useParams.tsx +8 -0
  74. package/src/lib/hooks/useURIData.ts +180 -0
  75. package/src/lib/hooks/useURILink.ts +7 -0
  76. package/src/lib/index.tsx +9 -0
  77. package/src/lib/public-path.ts +3 -0
  78. package/src/lib/routes/Home.tsx +13 -0
  79. package/src/lib/routes/Search.tsx +13 -0
  80. package/src/lib/routes/Uri.tsx +10 -0
  81. package/src/lib/routes/Yasgui.tsx +13 -0
  82. package/src/lib/setupTests.ts +5 -0
  83. package/src/lib/types.d.ts +6 -0
  84. package/src/lib/utils/getIconFromURI.ts +32 -0
  85. package/src/lib/utils/prefixInverted.json +2445 -0
  86. package/src/lib/utils/utils.ts +131 -0
  87. package/src/lib/yasgui-utils/Storage.ts +117 -0
  88. package/src/lib/yasgui-utils/index.ts +66 -0
@@ -0,0 +1,55 @@
1
+ import ArrowLeftIcon from "@mui/icons-material/ChevronLeft";
2
+ import Box from "@mui/material/Box";
3
+ import Divider from "@mui/material/Divider";
4
+ import IconButton from "@mui/material/IconButton";
5
+ import Stack from "@mui/material/Stack";
6
+ import Typography from "@mui/material/Typography";
7
+ import { Link } from "react-router-dom";
8
+ import { useEndpoint } from "../../context/ConfigContext";
9
+ import { GraphSelector } from "../GraphSelector";
10
+ import { ViewSelector } from "../ViewSelector";
11
+
12
+ export const DRAWER_WIDTH = 340;
13
+
14
+ export function DrawerContent({ onClose }: { onClose: () => void }) {
15
+ const endpoint = useEndpoint();
16
+ return (
17
+ <Box
18
+ height={"100%"}
19
+ display={"flex"}
20
+ flexDirection={"column"}
21
+ width={340}
22
+ >
23
+ <Stack direction={"row"}>
24
+ <Box
25
+ padding={1}
26
+ display={"flex"}
27
+ alignItems={"center"}
28
+ justifyContent={"center"}
29
+ >
30
+ <IconButton size="large" color="inherit" onClick={onClose}>
31
+ <ArrowLeftIcon />
32
+ </IconButton>
33
+ </Box>
34
+ <Box padding={1} flex={1}>
35
+ <Typography variant="h5">SparqlExplorer</Typography>
36
+ {endpoint ? (
37
+ <Link to={endpoint} target="_blank">
38
+ <Typography variant="caption" color={"GrayText"}>
39
+ {endpoint}
40
+ </Typography>
41
+ </Link>
42
+ ) : null}
43
+ </Box>
44
+ </Stack>
45
+ <Divider />
46
+ <Box paddingTop={2}>
47
+ <Stack spacing={1}>
48
+ <ViewSelector />
49
+ <Divider />
50
+ <GraphSelector />
51
+ </Stack>
52
+ </Box>
53
+ </Box>
54
+ );
55
+ }
@@ -0,0 +1,32 @@
1
+ import packageJson from "../../../../package.json";
2
+
3
+ export function Footer() {
4
+ return (
5
+ <footer
6
+ style={{
7
+ display: "flex",
8
+ alignItems: "center",
9
+ justifyContent: "center",
10
+ borderTop: 1,
11
+ borderTopStyle: "solid",
12
+ borderTopColor: "rgba(140, 130, 115, 0.1)",
13
+ padding: 5,
14
+ }}
15
+ >
16
+ <p style={{ margin: 0 }}>
17
+ SparqlExplorer v{packageJson.version} made with ✨ by{" "}
18
+ <a
19
+ href="https://logilab.fr/"
20
+ style={{
21
+ textDecoration: "none",
22
+ }}
23
+ target="_blank"
24
+ rel="noopener"
25
+ >
26
+ Logilab
27
+ </a>
28
+ .
29
+ </p>
30
+ </footer>
31
+ );
32
+ }
@@ -0,0 +1,103 @@
1
+ import Box from "@mui/material/Box";
2
+ import Drawer from "@mui/material/Drawer";
3
+ import Stack from "@mui/material/Stack";
4
+ import { styled } from "@mui/material/styles";
5
+ import { type PropsWithChildren, useState } from "react";
6
+ import { ConfigProvider } from "../../context/ConfigContext";
7
+ import { useParam } from "../../hooks/useParams";
8
+ import { DRAWER_WIDTH, DrawerContent } from "./DrawerContent";
9
+ import { Footer } from "./Footer";
10
+ import { NavBar } from "./Navbar";
11
+
12
+ const Main = styled("div", { shouldForwardProp: (prop) => prop !== "open" })<{
13
+ open?: boolean;
14
+ }>(({ theme, open }) => ({
15
+ flexGrow: 1,
16
+ transition: theme.transitions.create("margin", {
17
+ easing: theme.transitions.easing.sharp,
18
+ duration: theme.transitions.duration.leavingScreen,
19
+ }),
20
+ marginLeft: 0,
21
+ ...(open && {
22
+ width: `calc(100% - ${DRAWER_WIDTH}px)`,
23
+ marginLeft: `${DRAWER_WIDTH}px`,
24
+ transition: theme.transitions.create("margin", {
25
+ easing: theme.transitions.easing.easeOut,
26
+ duration: theme.transitions.duration.enteringScreen,
27
+ }),
28
+ }),
29
+ }));
30
+
31
+ const OPEN_DRAWER_KEY = "sparqlexplorer_drawer open";
32
+
33
+ function shouldOpenDrawer() {
34
+ return window.localStorage.getItem(OPEN_DRAWER_KEY) === "1";
35
+ }
36
+
37
+ function saveDrawerState(open: boolean) {
38
+ return window.localStorage.setItem(OPEN_DRAWER_KEY, open ? "1" : "0");
39
+ }
40
+
41
+ function useGraphsParam(): string[] {
42
+ const selectedGraphsParam = useParam("graphs");
43
+ const parsedGraphs = JSON.parse(selectedGraphsParam ?? "[]");
44
+ if (
45
+ Array.isArray(parsedGraphs) &&
46
+ parsedGraphs.every((v) => typeof v === "string")
47
+ ) {
48
+ return parsedGraphs;
49
+ }
50
+ return [];
51
+ }
52
+
53
+ export function Layout({ children }: PropsWithChildren) {
54
+ const endpointParam = useParam("endpoint");
55
+ const endpointFixed = window.SPARQL_ENDPOINT;
56
+ const isEndpointFixed = endpointFixed !== null && endpointFixed !== "";
57
+ const endpoint = isEndpointFixed ? endpointFixed : endpointParam;
58
+ const view = useParam("view");
59
+ const selectedGraphs = useGraphsParam();
60
+ const [drawerOpen, setDrawerOpen] = useState(shouldOpenDrawer());
61
+
62
+ function setDrawerState(open: boolean) {
63
+ saveDrawerState(open);
64
+ setDrawerOpen(open);
65
+ }
66
+
67
+ return (
68
+ <ConfigProvider
69
+ config={{
70
+ endpoint,
71
+ canEditEndpoint: !isEndpointFixed,
72
+ view,
73
+ selectedGraphs,
74
+ }}
75
+ >
76
+ <div
77
+ style={{
78
+ minHeight: "100vh",
79
+ }}
80
+ >
81
+ <Stack spacing={2} sx={{ minHeight: "100vh" }}>
82
+ <Box>
83
+ <NavBar
84
+ onBurgerPressed={() => setDrawerState(!drawerOpen)}
85
+ drawerOpen={drawerOpen}
86
+ />
87
+ </Box>
88
+ <Box display={"flex"} flex={1}>
89
+ <Main open={drawerOpen}>{children}</Main>
90
+ </Box>
91
+ <Footer />
92
+ </Stack>
93
+ <Drawer
94
+ open={drawerOpen}
95
+ onClose={() => setDrawerState(false)}
96
+ variant="persistent"
97
+ >
98
+ <DrawerContent onClose={() => setDrawerState(false)} />
99
+ </Drawer>
100
+ </div>
101
+ </ConfigProvider>
102
+ );
103
+ }
@@ -0,0 +1,231 @@
1
+ import HomeIcon from "@mui/icons-material/HomeOutlined";
2
+ import LinkIcon from "@mui/icons-material/Link";
3
+ import MenuIcon from "@mui/icons-material/Menu";
4
+ import OpenInNewIcon from "@mui/icons-material/OpenInNew";
5
+ import SearchIcon from "@mui/icons-material/Search";
6
+ import TerminalIcon from "@mui/icons-material/Terminal";
7
+ import AppBar, { type AppBarProps } from "@mui/material/AppBar";
8
+ import Box from "@mui/material/Box";
9
+ import Button from "@mui/material/Button";
10
+ import IconButton from "@mui/material/IconButton";
11
+ import InputBase from "@mui/material/InputBase";
12
+ import Stack from "@mui/material/Stack";
13
+ import { alpha, styled } from "@mui/material/styles";
14
+ import Toolbar from "@mui/material/Toolbar";
15
+ import Tooltip from "@mui/material/Tooltip";
16
+ import Typography from "@mui/material/Typography";
17
+ import { useEffect, useState } from "react";
18
+ import { useMatch, useParams } from "react-router-dom";
19
+ import { useEndpoint } from "../../context/ConfigContext";
20
+ import { useNavigateWithParams } from "../../hooks/useNavigateWithParams";
21
+ import { useParam } from "../../hooks/useParams";
22
+ import { DRAWER_WIDTH } from "./DrawerContent";
23
+
24
+ interface CustomAppBarProps extends AppBarProps {
25
+ open?: boolean;
26
+ }
27
+
28
+ const CustomAppBar = styled(AppBar, {
29
+ shouldForwardProp: (prop) => prop !== "open",
30
+ })<CustomAppBarProps>(({ theme, open }) => ({
31
+ backgroundColor: theme.palette.background.paper,
32
+ color: theme.palette.text.primary,
33
+ transition: theme.transitions.create(["margin", "width"], {
34
+ easing: theme.transitions.easing.sharp,
35
+ duration: theme.transitions.duration.leavingScreen,
36
+ }),
37
+ ...(open && {
38
+ width: `calc(100% - ${DRAWER_WIDTH}px)`,
39
+ marginLeft: `${DRAWER_WIDTH}px`,
40
+ transition: theme.transitions.create(["margin", "width"], {
41
+ easing: theme.transitions.easing.easeOut,
42
+ duration: theme.transitions.duration.enteringScreen,
43
+ }),
44
+ }),
45
+ }));
46
+
47
+ const Search = styled("div")(({ theme }) => ({
48
+ position: "relative",
49
+ borderRadius: theme.shape.borderRadius,
50
+ backgroundColor: alpha(theme.palette.common.black, 0.05),
51
+ "&:hover": {
52
+ backgroundColor: alpha(theme.palette.common.black, 0.1),
53
+ },
54
+ flex: 1,
55
+ height: 40,
56
+ }));
57
+
58
+ export function NavBar({
59
+ onBurgerPressed,
60
+ drawerOpen,
61
+ }: {
62
+ onBurgerPressed: () => void;
63
+ drawerOpen: boolean;
64
+ }) {
65
+ const searchInput = useParam("search");
66
+ const params = useParams();
67
+ const currentUri = params["*"];
68
+
69
+ const [currentSearch, setCurrentSearch] = useState(
70
+ currentUri ?? searchInput ?? "",
71
+ );
72
+ const navigate = useNavigateWithParams();
73
+ const endpoint = useEndpoint();
74
+
75
+ useEffect(() => {
76
+ if (currentUri && currentUri !== currentSearch) {
77
+ setCurrentSearch(currentUri);
78
+ }
79
+ }, [currentUri, currentSearch]);
80
+
81
+ useEffect(() => {
82
+ if (searchInput && searchInput !== currentSearch) {
83
+ setCurrentSearch(searchInput);
84
+ }
85
+ }, [searchInput, currentSearch]);
86
+
87
+ function startSearch() {
88
+ if (!currentSearch) {
89
+ return;
90
+ }
91
+
92
+ const encodedSearch = encodeURIComponent(currentSearch);
93
+ if (
94
+ currentSearch.startsWith("http://") ||
95
+ currentSearch.startsWith("https://") ||
96
+ currentSearch.startsWith("urn:")
97
+ ) {
98
+ navigate(`/browse/${encodedSearch}`);
99
+ } else {
100
+ navigate("/search", {
101
+ search: encodedSearch,
102
+ });
103
+ }
104
+ }
105
+
106
+ // We show the home screen on all routes not matching those patterns
107
+ // See App.tsx
108
+ const onYasgui = useMatch("/yasgui");
109
+ const onSearch = useMatch("/search");
110
+ const onUri = useMatch("/browse/:uri");
111
+
112
+ const showHomeButton =
113
+ onYasgui !== null || onSearch !== null || onUri !== null;
114
+
115
+ return (
116
+ <CustomAppBar position="static" open={drawerOpen}>
117
+ <Toolbar>
118
+ {!drawerOpen ? (
119
+ <IconButton
120
+ size="large"
121
+ edge="start"
122
+ color="inherit"
123
+ aria-label="open drawer"
124
+ sx={{ mr: 2 }}
125
+ onClick={onBurgerPressed}
126
+ >
127
+ <MenuIcon />
128
+ </IconButton>
129
+ ) : null}
130
+ <Stack
131
+ direction={"row"}
132
+ spacing={1}
133
+ alignItems={"center"}
134
+ flexGrow={1}
135
+ >
136
+ {!drawerOpen ? (
137
+ <Typography
138
+ variant="h6"
139
+ noWrap
140
+ component="a"
141
+ sx={{
142
+ display: { xs: "none", sm: "block" },
143
+ cursor: "pointer",
144
+ textDecoration: "none",
145
+ color: "inherit",
146
+ }}
147
+ onClick={() => navigate("/")}
148
+ >
149
+ SparqlExplorer
150
+ </Typography>
151
+ ) : null}
152
+ {showHomeButton ? (
153
+ <IconButton
154
+ size="large"
155
+ edge="start"
156
+ color="inherit"
157
+ sx={{ mr: 2 }}
158
+ onClick={() => navigate("/")}
159
+ >
160
+ <HomeIcon />
161
+ </IconButton>
162
+ ) : null}
163
+ {endpoint ? (
164
+ <Search>
165
+ <Stack
166
+ paddingLeft={1}
167
+ paddingRight={1}
168
+ direction={"row"}
169
+ spacing={1}
170
+ alignItems={"center"}
171
+ flexGrow={1}
172
+ height={"100%"}
173
+ >
174
+ <Box>
175
+ {!currentUri ||
176
+ currentUri !== currentSearch ? (
177
+ <SearchIcon />
178
+ ) : (
179
+ <LinkIcon />
180
+ )}
181
+ </Box>
182
+ <form
183
+ style={{ flex: 1, display: "flex" }}
184
+ onSubmit={(e) => {
185
+ e.preventDefault();
186
+ startSearch();
187
+ }}
188
+ >
189
+ <InputBase
190
+ sx={{
191
+ flex: 1,
192
+ }}
193
+ name="search"
194
+ placeholder="Search…"
195
+ inputProps={{
196
+ "aria-label": "search",
197
+ }}
198
+ value={currentSearch}
199
+ onChange={(event) =>
200
+ setCurrentSearch(event.target.value)
201
+ }
202
+ />
203
+ </form>
204
+ {currentUri && currentUri === currentSearch ? (
205
+ <Tooltip title="Open URI">
206
+ <IconButton
207
+ href={currentUri}
208
+ target="_blank"
209
+ color="inherit"
210
+ >
211
+ <OpenInNewIcon />
212
+ </IconButton>
213
+ </Tooltip>
214
+ ) : null}
215
+ </Stack>
216
+ </Search>
217
+ ) : null}
218
+ {endpoint ? (
219
+ <Button
220
+ sx={{ color: "inherit" }}
221
+ onClick={() => navigate("/yasgui")}
222
+ startIcon={<TerminalIcon />}
223
+ >
224
+ Query
225
+ </Button>
226
+ ) : null}
227
+ </Stack>
228
+ </Toolbar>
229
+ </CustomAppBar>
230
+ );
231
+ }