@blocklet/discuss-kit 1.0.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 (45) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +15 -0
  3. package/lib/cjs/api.js +45 -0
  4. package/lib/cjs/comment-list.js +117 -0
  5. package/lib/cjs/components/comment.js +335 -0
  6. package/lib/cjs/components/error-fallback.js +26 -0
  7. package/lib/cjs/components/menu.js +84 -0
  8. package/lib/cjs/components/rating/binary-thumb.js +199 -0
  9. package/lib/cjs/components/rating/emoji-based.js +39 -0
  10. package/lib/cjs/components/rating/index.js +9 -0
  11. package/lib/cjs/components/rating/rating.js +93 -0
  12. package/lib/cjs/context.js +77 -0
  13. package/lib/cjs/did-comment-with-session.js +25 -0
  14. package/lib/cjs/did-comment.js +389 -0
  15. package/lib/cjs/hooks.js +202 -0
  16. package/lib/cjs/index.js +21 -0
  17. package/lib/cjs/lib/utils.js +17 -0
  18. package/lib/cjs/locales/en.js +27 -0
  19. package/lib/cjs/locales/index.js +10 -0
  20. package/lib/cjs/locales/zh.js +27 -0
  21. package/lib/cjs/session.js +14 -0
  22. package/lib/cjs/theme-provider.js +48 -0
  23. package/lib/cjs/ws.js +39 -0
  24. package/lib/es/api.js +43 -0
  25. package/lib/es/comment-list.js +112 -0
  26. package/lib/es/components/comment.js +309 -0
  27. package/lib/es/components/error-fallback.js +23 -0
  28. package/lib/es/components/menu.js +77 -0
  29. package/lib/es/components/rating/binary-thumb.js +176 -0
  30. package/lib/es/components/rating/emoji-based.js +37 -0
  31. package/lib/es/components/rating/index.js +4 -0
  32. package/lib/es/components/rating/rating.js +91 -0
  33. package/lib/es/context.js +74 -0
  34. package/lib/es/did-comment-with-session.js +24 -0
  35. package/lib/es/did-comment.js +377 -0
  36. package/lib/es/hooks.js +197 -0
  37. package/lib/es/index.js +8 -0
  38. package/lib/es/lib/utils.js +17 -0
  39. package/lib/es/locales/en.js +26 -0
  40. package/lib/es/locales/index.js +7 -0
  41. package/lib/es/locales/zh.js +26 -0
  42. package/lib/es/session.js +14 -0
  43. package/lib/es/theme-provider.js +46 -0
  44. package/lib/es/ws.js +37 -0
  45. package/package.json +79 -0
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const react = require("react");
4
+ const Session = require("@arcblock/did-connect/lib/Session");
5
+ const { SessionProvider, SessionContext, SessionConsumer, withSession } = Session.createAuthServiceSessionContext();
6
+ function useSessionContext() {
7
+ const data = react.useContext(SessionContext);
8
+ return data;
9
+ }
10
+ exports.SessionConsumer = SessionConsumer;
11
+ exports.SessionContext = SessionContext;
12
+ exports.SessionProvider = SessionProvider;
13
+ exports.useSessionContext = useSessionContext;
14
+ exports.withSession = withSession;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const react = require("react");
4
+ const PropTypes = require("prop-types");
5
+ const react$1 = require("@emotion/react");
6
+ const styles = require("@mui/material/styles");
7
+ const jsxRuntime = require("react/jsx-runtime");
8
+ const _interopDefaultLegacy = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
9
+ const PropTypes__default = /* @__PURE__ */ _interopDefaultLegacy(PropTypes);
10
+ const defaultTheme = styles.createTheme({});
11
+ const ThemeContext = react.createContext({});
12
+ function ThemeProvider({
13
+ children,
14
+ theme: _theme
15
+ } = {}) {
16
+ const [theme, setTheme] = react.useState(defaultTheme);
17
+ react.useEffect(() => {
18
+ if (_theme && typeof _theme === "object")
19
+ setTheme(_theme);
20
+ }, [_theme]);
21
+ if (theme) {
22
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, {
23
+ value: theme,
24
+ children: /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Consumer, {
25
+ children: (themeValue) => {
26
+ return /* @__PURE__ */ jsxRuntime.jsx(styles.ThemeProvider, {
27
+ theme: themeValue,
28
+ children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ThemeProvider, {
29
+ theme: themeValue,
30
+ children
31
+ })
32
+ });
33
+ }
34
+ })
35
+ });
36
+ }
37
+ return children;
38
+ }
39
+ ThemeProvider.propTypes = {
40
+ children: PropTypes__default.default.any.isRequired,
41
+ theme: PropTypes__default.default.object.isRequired
42
+ };
43
+ function useThemeContext() {
44
+ return react.useContext(ThemeContext);
45
+ }
46
+ exports.ThemeProvider = ThemeProvider;
47
+ exports.default = ThemeProvider;
48
+ exports.useThemeContext = useThemeContext;
package/lib/cjs/ws.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const react = require("react");
4
+ const Cookie = require("js-cookie");
5
+ const ws = require("@arcblock/ws");
6
+ const _interopDefaultLegacy = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
7
+ const Cookie__default = /* @__PURE__ */ _interopDefaultLegacy(Cookie);
8
+ let client;
9
+ function create(prefix) {
10
+ const pathPrefix = prefix;
11
+ const url = `//${window.location.host}${pathPrefix.replace(/\/$/, "")}`;
12
+ return new ws.WsClient(url, {
13
+ heartbeatIntervalMs: 10 * 1e3,
14
+ params: () => ({
15
+ token: Cookie__default.default.get("login_token")
16
+ })
17
+ });
18
+ }
19
+ function getWsClient(prefix) {
20
+ if (!client) {
21
+ client = create(prefix);
22
+ }
23
+ return client;
24
+ }
25
+ const useSubscription = (event, cb = () => {
26
+ }, deps = [], prefix = "") => {
27
+ if (!client) {
28
+ client = getWsClient(prefix);
29
+ }
30
+ react.useEffect(() => {
31
+ client.on(event, cb);
32
+ return () => {
33
+ client.off(event, cb);
34
+ };
35
+ }, deps);
36
+ };
37
+ exports.create = create;
38
+ exports.default = getWsClient;
39
+ exports.useSubscription = useSubscription;
package/lib/es/api.js ADDED
@@ -0,0 +1,43 @@
1
+ import axios from "axios";
2
+ const request = axios.create({ baseURL: "/", timeout: 5e3 });
3
+ request.interceptors.request.use(
4
+ (config) => {
5
+ return config;
6
+ },
7
+ (error) => Promise.reject(error)
8
+ );
9
+ const fetchRatingStats = async (id) => {
10
+ const { data } = await request.get(`/ratings/${id}`);
11
+ return data;
12
+ };
13
+ const fetchComments = async (params) => {
14
+ try {
15
+ const { data } = await request.get("/comments", { params });
16
+ return data;
17
+ } catch (e) {
18
+ console.error(e);
19
+ throw e;
20
+ }
21
+ };
22
+ const reply = async ({ commentId, parentId, content }) => {
23
+ const { data } = await request.post(`/comments/${commentId}/replies`, { content, parentId });
24
+ return data;
25
+ };
26
+ const fetchMoreReplies = async ({ commentId, cursor, limit = 10 }) => {
27
+ try {
28
+ const { data } = await request.get(`/comments/${commentId}/replies`, {
29
+ params: { cursor, limit, embed: "rating" }
30
+ });
31
+ return data;
32
+ } catch (e) {
33
+ console.error(e);
34
+ throw e;
35
+ }
36
+ };
37
+ export {
38
+ request as default,
39
+ fetchComments,
40
+ fetchMoreReplies,
41
+ fetchRatingStats,
42
+ reply
43
+ };
@@ -0,0 +1,112 @@
1
+ import { useContext, useState } from "react";
2
+ import styled from "@emotion/styled";
3
+ import Button from "@arcblock/ux/lib/Button";
4
+ import CircularProgress from "@mui/material/CircularProgress";
5
+ import { LocaleContext } from "@arcblock/ux/lib/Locale/context";
6
+ import { Confirm } from "@arcblock/ux/lib/Dialog";
7
+ import Comment from "./components/comment";
8
+ import { useCommentsContext } from "./context";
9
+ import api from "./api";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ function CommentList() {
12
+ const {
13
+ comments,
14
+ loadMore,
15
+ hasMore,
16
+ loading,
17
+ removeComment
18
+ } = useCommentsContext();
19
+ const {
20
+ t
21
+ } = useContext(LocaleContext);
22
+ const [waitDelRow, setWaitDelRow] = useState({});
23
+ const handleConfirmDelete = async (comment) => {
24
+ try {
25
+ setWaitDelRow({});
26
+ await api.delete(`/comments/${comment.id}`);
27
+ removeComment(comment);
28
+ } catch (e) {
29
+ console.error(e);
30
+ }
31
+ };
32
+ if (loading) {
33
+ return /* @__PURE__ */ jsx(Center, {
34
+ style: {
35
+ width: "100%"
36
+ },
37
+ children: /* @__PURE__ */ jsx(CircularProgress, {})
38
+ });
39
+ }
40
+ return /* @__PURE__ */ jsxs(Container, {
41
+ children: [comments.length === 0 && /* @__PURE__ */ jsx("div", {
42
+ className: "empty",
43
+ children: t("empty")
44
+ }), comments.length > 0 && comments.map((comment, index) => {
45
+ return /* @__PURE__ */ jsx(Comment, {
46
+ comment,
47
+ index: index + 1,
48
+ onDelete: setWaitDelRow
49
+ }, comment.id);
50
+ }), hasMore && /* @__PURE__ */ jsx("div", {
51
+ className: "load-more",
52
+ children: /* @__PURE__ */ jsx(Button, {
53
+ onClick: loadMore,
54
+ disabled: loading,
55
+ variant: "outlined",
56
+ color: "primary",
57
+ size: "small",
58
+ children: t("loadMore")
59
+ })
60
+ }), /* @__PURE__ */ jsx(Confirm, {
61
+ open: !!waitDelRow.id,
62
+ title: t("tip"),
63
+ onConfirm: () => {
64
+ handleConfirmDelete(waitDelRow);
65
+ },
66
+ confirmButton: {
67
+ text: t("confirm"),
68
+ props: {
69
+ variant: "contained",
70
+ color: "error"
71
+ }
72
+ },
73
+ cancelButton: {
74
+ text: t("cancel"),
75
+ props: {
76
+ color: "inherit"
77
+ }
78
+ },
79
+ onCancel: () => {
80
+ setWaitDelRow({});
81
+ },
82
+ children: /* @__PURE__ */ jsx("span", {
83
+ children: t("deleteCommentDesc")
84
+ })
85
+ })]
86
+ });
87
+ }
88
+ CommentList.propTypes = {};
89
+ CommentList.defaultProps = {};
90
+ const Center = styled.div`
91
+ width: 100%;
92
+ padding: 120px 0 0 0;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ `;
97
+ const Container = styled.div`
98
+ width: 100%;
99
+ .empty {
100
+ text-align: center;
101
+ color: #9397a1;
102
+ font-size: 16px;
103
+ margin-top: 44px;
104
+ }
105
+ .load-more {
106
+ text-align: center;
107
+ margin: 24px 0;
108
+ }
109
+ `;
110
+ export {
111
+ CommentList as default
112
+ };
@@ -0,0 +1,309 @@
1
+ import * as React from "react";
2
+ import { useContext, useState } from "react";
3
+ import styled from "@emotion/styled";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import PropTypes from "prop-types";
7
+ import NavigateNextIcon from "@mui/icons-material/NavigateNext";
8
+ import DIDAddress from "@arcblock/did-connect/lib/Address";
9
+ import { format } from "timeago.js";
10
+ import { LocaleContext } from "@arcblock/ux/lib/Locale/context";
11
+ import { Avatar, PostContent, CommentInput } from "@blocklet/discuss-kit-ux";
12
+ import { BinaryThumb } from "./rating";
13
+ import Menu from "./menu";
14
+ import api, { reply } from "../api";
15
+ import { protectLogin, minDelay } from "../lib/utils";
16
+ import { useCommentsContext } from "../context";
17
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
18
+ const SvgMessage = (props) => /* @__PURE__ */ React.createElement("svg", {
19
+ width: 32,
20
+ height: 32,
21
+ viewBox: "0 0 24 24",
22
+ ...props
23
+ }, /* @__PURE__ */ React.createElement("path", {
24
+ fill: "none",
25
+ stroke: "#888888",
26
+ strokeLinecap: "round",
27
+ strokeLinejoin: "round",
28
+ strokeWidth: 2,
29
+ d: "m3 20l1.3-3.9A9 8 0 1 1 7.7 19L3 20"
30
+ }));
31
+ function CommentReplies({
32
+ comment,
33
+ onDelete,
34
+ ...rest
35
+ }) {
36
+ const {
37
+ t
38
+ } = useContext(LocaleContext);
39
+ const {
40
+ loadMoreReplies
41
+ } = useCommentsContext();
42
+ const {
43
+ replies,
44
+ totalReplies
45
+ } = comment;
46
+ if (!(replies == null ? void 0 : replies.length)) {
47
+ return null;
48
+ }
49
+ return /* @__PURE__ */ jsxs(Box, {
50
+ mt: 1,
51
+ ...rest,
52
+ children: [replies.map((item) => {
53
+ return /* @__PURE__ */ jsx("div", {
54
+ children: /* @__PURE__ */ jsx(Comment, {
55
+ comment: item,
56
+ onDelete,
57
+ rootComment: comment
58
+ })
59
+ }, item.id);
60
+ }), totalReplies > replies.length && /* @__PURE__ */ jsx(Button, {
61
+ onClick: () => loadMoreReplies(comment.id),
62
+ variant: "outlined",
63
+ color: "inherit",
64
+ size: "small",
65
+ endIcon: /* @__PURE__ */ jsx(NavigateNextIcon, {}),
66
+ sx: {
67
+ mt: 2,
68
+ ml: 6,
69
+ color: "grey.600",
70
+ borderColor: "grey.400",
71
+ fontSize: 12,
72
+ textTransform: "none"
73
+ },
74
+ children: t("showMoreReplies")
75
+ })]
76
+ });
77
+ }
78
+ CommentReplies.propTypes = {
79
+ comment: PropTypes.object.isRequired,
80
+ onDelete: PropTypes.func.isRequired
81
+ };
82
+ function Comment({
83
+ comment,
84
+ onDelete,
85
+ rootComment,
86
+ ...rest
87
+ }) {
88
+ var _a, _b, _c, _d;
89
+ const {
90
+ session,
91
+ addComment,
92
+ updateComment,
93
+ manageMode,
94
+ flatMode
95
+ } = useCommentsContext();
96
+ const {
97
+ t,
98
+ locale
99
+ } = useContext(LocaleContext);
100
+ const [inputBoxVisible, setInputBoxVisible] = useState(false);
101
+ const [editMode, setEditMode] = useState(false);
102
+ const isCommenter = ((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.did) === comment.commenter.did;
103
+ const canEditOrDelete = !comment.deletedAt && (isCommenter || manageMode);
104
+ const menuItems = [...canEditOrDelete ? [{
105
+ text: /* @__PURE__ */ jsx(Box, {
106
+ component: "span",
107
+ sx: {
108
+ color: "error.main"
109
+ },
110
+ children: t("delete")
111
+ }),
112
+ onClick: () => onDelete(comment)
113
+ }, {
114
+ text: /* @__PURE__ */ jsx(Box, {
115
+ component: "span",
116
+ children: t("edit")
117
+ }),
118
+ onClick: () => setEditMode(true)
119
+ }] : []];
120
+ const sendReply = async (content) => {
121
+ const commentId = (rootComment == null ? void 0 : rootComment.id) || comment.commentId || comment.id;
122
+ const data = await minDelay(reply({
123
+ commentId,
124
+ parentId: comment.id,
125
+ content
126
+ }), 800);
127
+ setInputBoxVisible(false);
128
+ addComment(data);
129
+ };
130
+ const handleOnRate = async ({
131
+ ratingType,
132
+ value
133
+ }) => {
134
+ if (!session.user) {
135
+ session.login();
136
+ throw new Error("Unauthenticated.");
137
+ }
138
+ await api.post(`/objects/${comment.objectId}/comments/${comment.id}/ratings`, {
139
+ ratingType,
140
+ value
141
+ });
142
+ };
143
+ const handleOnUnrate = async () => {
144
+ if (!session.user) {
145
+ session.login();
146
+ throw new Error("Unauthenticated.");
147
+ }
148
+ await api.delete(`/objects/${comment.objectId}/comments/${comment.id}/ratings`);
149
+ };
150
+ const handleUpdateComment = async (content) => {
151
+ await api.put(`/comments/${comment.id}`, {
152
+ content
153
+ });
154
+ updateComment(comment.id, (current) => ({
155
+ ...current,
156
+ content
157
+ }));
158
+ };
159
+ return /* @__PURE__ */ jsxs(Root, {
160
+ ...rest,
161
+ children: [/* @__PURE__ */ jsx(Avatar, {
162
+ did: comment.commenter.did,
163
+ src: comment.commenter.avatar,
164
+ size: 36,
165
+ shape: "circle",
166
+ variant: "circle"
167
+ }), /* @__PURE__ */ jsxs(Box, {
168
+ flex: "1",
169
+ pl: 2,
170
+ overflow: "hidden",
171
+ children: [/* @__PURE__ */ jsxs(Box, {
172
+ display: "flex",
173
+ justifyContent: "space-between",
174
+ alignItems: "start",
175
+ children: [/* @__PURE__ */ jsxs(Box, {
176
+ children: [/* @__PURE__ */ jsxs(Box, {
177
+ display: "flex",
178
+ alignItems: "center",
179
+ gap: 1,
180
+ flexWrap: "wrap",
181
+ color: "#333",
182
+ lineHeight: 1,
183
+ children: [/* @__PURE__ */ jsx("span", {
184
+ children: comment.commenter.fullName || "Unknown"
185
+ }), /* @__PURE__ */ jsxs("div", {
186
+ children: ["(", /* @__PURE__ */ jsx(DIDAddress, {
187
+ copyable: false,
188
+ responsive: false,
189
+ compact: true,
190
+ inline: true,
191
+ children: comment.commenter.did
192
+ }), ")"]
193
+ }), /* @__PURE__ */ jsx(Box, {
194
+ width: {
195
+ xs: "100%",
196
+ md: "auto"
197
+ },
198
+ fontSize: 13,
199
+ color: "grey.500",
200
+ children: /* @__PURE__ */ jsx("span", {
201
+ children: format(comment.createdAt, locale === "zh" ? "zh_CN" : "en_US")
202
+ })
203
+ })]
204
+ }), ((_b = comment.replyTo) == null ? void 0 : _b.did) && (flatMode || comment.parentId !== comment.commentId) && /* @__PURE__ */ jsxs(Box, {
205
+ mt: 0.5,
206
+ fontSize: 13,
207
+ lineHeight: 1,
208
+ color: "primary.light",
209
+ sx: {
210
+ ".did-address-text": {
211
+ color: "primary.light"
212
+ }
213
+ },
214
+ children: ["@", /* @__PURE__ */ jsx(Box, {
215
+ component: "span",
216
+ mr: 1,
217
+ children: comment.replyTo.fullName
218
+ }), "(", /* @__PURE__ */ jsx(DIDAddress, {
219
+ copyable: false,
220
+ responsive: false,
221
+ compact: true,
222
+ inline: true,
223
+ children: comment.replyTo.did
224
+ }), ")"]
225
+ })]
226
+ }), !!(menuItems == null ? void 0 : menuItems.length) && /* @__PURE__ */ jsx(Menu, {
227
+ items: menuItems,
228
+ style: {
229
+ position: "absolute",
230
+ right: 0,
231
+ top: 16
232
+ }
233
+ })]
234
+ }), /* @__PURE__ */ jsxs(Box, {
235
+ mt: 1,
236
+ children: [!comment.deletedAt && /* @__PURE__ */ jsx(PostContent, {
237
+ content: comment.content,
238
+ editing: editMode,
239
+ onExitEditing: () => setEditMode(false),
240
+ onSubmit: handleUpdateComment
241
+ }), comment.deletedAt && /* @__PURE__ */ jsx(Box, {
242
+ display: "inline-block",
243
+ px: 2,
244
+ py: 1,
245
+ mt: 1,
246
+ fontSize: 14,
247
+ bgcolor: "grey.100",
248
+ color: "grey.600",
249
+ borderRadius: 1,
250
+ children: t("deleted")
251
+ })]
252
+ }), !manageMode && /* @__PURE__ */ jsxs(Fragment, {
253
+ children: [/* @__PURE__ */ jsxs(Box, {
254
+ display: "flex",
255
+ alignItems: "center",
256
+ mt: 1.5,
257
+ children: [/* @__PURE__ */ jsx(BinaryThumb, {
258
+ selected: (_c = comment.rating) == null ? void 0 : _c.myRating,
259
+ countPerValue: (_d = comment.rating) == null ? void 0 : _d.counts,
260
+ onRate: handleOnRate,
261
+ onUnrate: handleOnUnrate
262
+ }), /* @__PURE__ */ jsx(Box, {
263
+ onClick: () => protectLogin(session, () => setInputBoxVisible(!inputBoxVisible)),
264
+ sx: {
265
+ lineHeight: 1,
266
+ "&:hover": {
267
+ cursor: "pointer"
268
+ }
269
+ },
270
+ children: /* @__PURE__ */ jsx(SvgMessage, {
271
+ style: {
272
+ width: "auto",
273
+ height: 16
274
+ }
275
+ })
276
+ })]
277
+ }), inputBoxVisible && /* @__PURE__ */ jsx(Box, {
278
+ my: 2,
279
+ children: /* @__PURE__ */ jsx(CommentInput, {
280
+ send: sendReply
281
+ })
282
+ })]
283
+ }), !comment.parentId && /* @__PURE__ */ jsx(CommentReplies, {
284
+ comment,
285
+ onDelete
286
+ })]
287
+ })]
288
+ });
289
+ }
290
+ Comment.propTypes = {
291
+ comment: PropTypes.object.isRequired,
292
+ onDelete: PropTypes.func,
293
+ rootComment: PropTypes.object
294
+ };
295
+ Comment.defaultProps = {
296
+ onDelete: void 0,
297
+ rootComment: void 0
298
+ };
299
+ const Root = styled(Box)`
300
+ display: flex;
301
+ position: relative;
302
+ padding: 24px 0 0 0;
303
+ &:first-of-type {
304
+ flex-shrink: 0;
305
+ }
306
+ `;
307
+ export {
308
+ Comment as default
309
+ };
@@ -0,0 +1,23 @@
1
+ import PropTypes from "prop-types";
2
+ import Alert from "@mui/material/Alert";
3
+ import AlertTitle from "@mui/material/AlertTitle";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function ErrorFallback({
6
+ error
7
+ }) {
8
+ const errorMessageElement = process.env.NODE_ENV === "production" ? null : /* @__PURE__ */ jsx("pre", {
9
+ children: error.message
10
+ });
11
+ return /* @__PURE__ */ jsxs(Alert, {
12
+ severity: "warning",
13
+ children: [/* @__PURE__ */ jsx(AlertTitle, {
14
+ children: "Oops!"
15
+ }), "DID Comments is not working properly.", errorMessageElement]
16
+ });
17
+ }
18
+ ErrorFallback.propTypes = {
19
+ error: PropTypes.instanceOf(Error).isRequired
20
+ };
21
+ export {
22
+ ErrorFallback as default
23
+ };
@@ -0,0 +1,77 @@
1
+ import { useState } from "react";
2
+ import PropTypes from "prop-types";
3
+ import styled from "@emotion/styled";
4
+ import IconButton from "@mui/material/IconButton";
5
+ import MoreVertIcon from "@mui/icons-material/MoreVert";
6
+ import Box from "@mui/material/Box";
7
+ import MuiMenu from "@mui/material/Menu";
8
+ import MuiMenuItem from "@mui/material/MenuItem";
9
+ import { jsxs, jsx } from "react/jsx-runtime";
10
+ function Menu({
11
+ items,
12
+ ...rest
13
+ }) {
14
+ const [anchorEl, setAnchorEl] = useState(null);
15
+ const open = Boolean(anchorEl);
16
+ const handleClick = (event) => {
17
+ setAnchorEl(event.currentTarget);
18
+ };
19
+ const handleClose = () => {
20
+ setAnchorEl(null);
21
+ };
22
+ return /* @__PURE__ */ jsxs(Root, {
23
+ ...rest,
24
+ children: [/* @__PURE__ */ jsx(IconButton, {
25
+ size: "medium",
26
+ onClick: handleClick,
27
+ children: /* @__PURE__ */ jsx(MoreVertIcon, {
28
+ sx: {
29
+ fontSize: 18
30
+ }
31
+ })
32
+ }), /* @__PURE__ */ jsx(MuiMenu, {
33
+ anchorEl,
34
+ anchorOrigin: {
35
+ vertical: "bottom",
36
+ horizontal: "right"
37
+ },
38
+ transformOrigin: {
39
+ vertical: "top",
40
+ horizontal: "right"
41
+ },
42
+ open,
43
+ onClose: handleClose,
44
+ children: items.map(({
45
+ onClick,
46
+ text,
47
+ ...itemProps
48
+ }, index) => {
49
+ const _onClick = () => {
50
+ onClick();
51
+ handleClose();
52
+ };
53
+ return /* @__PURE__ */ jsx(StyledMenuItem, {
54
+ onClick: _onClick,
55
+ ...itemProps,
56
+ children: /* @__PURE__ */ jsx(Box, {
57
+ minWidth: 100,
58
+ children: text
59
+ })
60
+ }, index);
61
+ })
62
+ })]
63
+ });
64
+ }
65
+ Menu.propTypes = {
66
+ items: PropTypes.array.isRequired
67
+ };
68
+ Menu.defaultProps = {};
69
+ const Root = styled("div")`
70
+ display: inline-block;
71
+ `;
72
+ const StyledMenuItem = styled(MuiMenuItem)`
73
+ font-size: 14px;
74
+ `;
75
+ export {
76
+ Menu as default
77
+ };