diff --git a/nest_frontend/components/base/BoxWordcloud.js b/nest_frontend/components/base/BoxWordcloud.js index a74cf3d..8bad4f1 100644 --- a/nest_frontend/components/base/BoxWordcloud.js +++ b/nest_frontend/components/base/BoxWordcloud.js @@ -1,33 +1,46 @@ -import React from "react" +import React, { useMemo } from "react" import BoxFull from "../base/BoxFull" import ReactWordcloud from "@steffo/nest-react-wordcloud" import Style from "./BoxWordcloud.module.css" /** - * A Box which displays a wordcloud. + * A {@link BoxFull} which displays a wordcloud. * * @param words - A list of word objects, made of a string "text" and a number "value" + * @param options - Additional options to pass to {@link ReactWordcloud}. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxWordcloud({ words, ...props }) { +export default function BoxWordcloud({ words, callbacks = {}, ...props }) { + const wordcloud = useMemo( + () => ( + + ), + [words] + ) + return (
- + {wordcloud}
) diff --git a/nest_frontend/components/interactive/BadgeFilter.js b/nest_frontend/components/interactive/BadgeFilter.js index dbfaf90..d4123f7 100644 --- a/nest_frontend/components/interactive/BadgeFilter.js +++ b/nest_frontend/components/interactive/BadgeFilter.js @@ -19,7 +19,7 @@ export default function BadgeFilter({ filter }) { removeFilter(filter)} > {filter.text()} diff --git a/nest_frontend/components/interactive/BoxFilterContains.js b/nest_frontend/components/interactive/BoxFilterContains.js new file mode 100644 index 0000000..b36f4ee --- /dev/null +++ b/nest_frontend/components/interactive/BoxFilterContains.js @@ -0,0 +1,28 @@ +import React, { useContext, useState } from "react" +import BoxFull from "../base/BoxFull" +import useRepositoryViewer from "../../hooks/useRepositoryViewer" +import useStrings from "../../hooks/useStrings" +import { ContainsFilter } from "../../utils/Filter" +import FormInlineText from "./FormInlineText" +import { faFont } from "@fortawesome/free-solid-svg-icons" + + +export default function BoxFilterContains({ ...props }) { + const strings = useStrings() + const { appendFilter } = useRepositoryViewer() + + const submit = value => { + appendFilter(new ContainsFilter(false, value)) + } + + // TODO: add this string + return ( + + + + ) +} diff --git a/nest_frontend/components/interactive/BoxFilterHashtag.js b/nest_frontend/components/interactive/BoxFilterHashtag.js new file mode 100644 index 0000000..4096492 --- /dev/null +++ b/nest_frontend/components/interactive/BoxFilterHashtag.js @@ -0,0 +1,43 @@ +import React, { useContext, useState } from "react" +import BoxFull from "../base/BoxFull" +import FormInline from "../base/FormInline" +import InputWithIcon from "../base/InputWithIcon" +import Style from "./BoxConditionUser.module.css" +import { faAt, faFilter, faFont, faHashtag } from "@fortawesome/free-solid-svg-icons" +import ButtonIconOnly from "../base/ButtonIconOnly" +import useRepositoryViewer from "../../hooks/useRepositoryViewer" +import useStrings from "../../hooks/useStrings" +import { ContainsFilter, HashtagFilter, UserFilter } from "../../utils/Filter" +import Condition from "../../utils/Condition" +import FormInlineText from "./FormInlineText" + + +const INVALID_HASHTAG_CHARACTERS = /([^a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73\u2b740-\u2b81\u2f800-\u2fa1])/g + + +export default function BoxFilterHashtag({ ...props }) { + // TODO: Translate this + // TODO: and also use a better string maybe + const strings = useStrings() + + const { appendFilter } = useRepositoryViewer() + + const validate = value => { + return value.replace(INVALID_HASHTAG_CHARACTERS, "") + } + + const submit = value => { + appendFilter(new HashtagFilter(false, value)) + } + + return ( + + + + ) +} diff --git a/nest_frontend/components/interactive/BoxFilterUser.js b/nest_frontend/components/interactive/BoxFilterUser.js new file mode 100644 index 0000000..37d24ea --- /dev/null +++ b/nest_frontend/components/interactive/BoxFilterUser.js @@ -0,0 +1,40 @@ +import React, { useContext, useState } from "react" +import BoxFull from "../base/BoxFull" +import FormInline from "../base/FormInline" +import InputWithIcon from "../base/InputWithIcon" +import Style from "./BoxConditionUser.module.css" +import { faAt, faFilter, faFont } from "@fortawesome/free-solid-svg-icons" +import ButtonIconOnly from "../base/ButtonIconOnly" +import useRepositoryViewer from "../../hooks/useRepositoryViewer" +import useStrings from "../../hooks/useStrings" +import { ContainsFilter, UserFilter } from "../../utils/Filter" +import Condition from "../../utils/Condition" +import FormInlineText from "./FormInlineText" + + +export default function BoxFilterUser({ ...props }) { + // TODO: Translate this + // TODO: and also use a better string maybe + const strings = useStrings() + + const { appendFilter } = useRepositoryViewer() + + const validate = value => { + return value.replace(/[^a-zA-Z0-9]/g, "") + } + + const submit = value => { + appendFilter(new UserFilter(false, value)) + } + + return ( + + + + ) +} diff --git a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js index 0c44d2d..543274e 100644 --- a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js +++ b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js @@ -4,11 +4,12 @@ import ContextLanguage from "../../contexts/ContextLanguage" import BoxFull from "../base/BoxFull" import Empty from "./Empty" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" +import { ContainsFilter } from "../../utils/Filter" export default function BoxVisualizationWordcloud({ ...props }) { const { strings } = useContext(ContextLanguage) - const {words} = useContext(ContextRepositoryViewer) + const {words, appendFilter} = useContext(ContextRepositoryViewer) if(words.length === 0) { return ( @@ -18,7 +19,16 @@ export default function BoxVisualizationWordcloud({ ...props }) { ) } + const onWordClick = word => { + appendFilter(new ContainsFilter(false, word.text)) + } + return ( - + ) } diff --git a/nest_frontend/components/interactive/FormInlineText.js b/nest_frontend/components/interactive/FormInlineText.js new file mode 100644 index 0000000..660e4aa --- /dev/null +++ b/nest_frontend/components/interactive/FormInlineText.js @@ -0,0 +1,39 @@ +import React, { useState } from "react" +import FormInline from "../base/FormInline" +import InputWithIcon from "../base/InputWithIcon" +import { faPlus } from "@fortawesome/free-solid-svg-icons" +import ButtonIconOnly from "../base/ButtonIconOnly" +import Style from "./FormInlineText.module.css" + + +export default function FormInlineText({ textIcon, placeholder, buttonIcon = faPlus, buttonColor = "Green", validate = value => value, submit, ...props }) { + const [value, setValue] = useState("") + + const _onSubmit = event => { + event.preventDefault() + submit(value) + setValue("") + } + + const _onChange = event => { + setValue(validate(event.target.value)) + } + + return ( + + + + + ) +} diff --git a/nest_frontend/components/interactive/FormInlineText.module.css b/nest_frontend/components/interactive/FormInlineText.module.css new file mode 100644 index 0000000..f8b3933 --- /dev/null +++ b/nest_frontend/components/interactive/FormInlineText.module.css @@ -0,0 +1,7 @@ +.Input { + flex-shrink: 1; +} + +.Button { + +} \ No newline at end of file diff --git a/nest_frontend/components/interactive/PickerFilter.js b/nest_frontend/components/interactive/PickerFilter.js index 899166c..4416abf 100644 --- a/nest_frontend/components/interactive/PickerFilter.js +++ b/nest_frontend/components/interactive/PickerFilter.js @@ -16,6 +16,12 @@ export default function PickerFilter({ ...props }) { name={"contains"} icon={faFont} /> + - {filterTab === "contains" ? "Contains" : null} - {filterTab === "user" ? "User" : null} + {filterTab === "contains" ? : null} + {filterTab === "hashtag" ? : null} + {filterTab === "user" ? : null} {filterTab === "time" ? "Time" : null} {filterTab === "location" ? "Location" : null} diff --git a/nest_frontend/components/providers/RepositoryViewer.module.css b/nest_frontend/components/providers/RepositoryViewer.module.css index 47ebd4b..c678bc5 100644 --- a/nest_frontend/components/providers/RepositoryViewer.module.css +++ b/nest_frontend/components/providers/RepositoryViewer.module.css @@ -10,7 +10,7 @@ grid-gap: 10px; grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto 3fr auto 1fr; + grid-template-rows: auto auto 1fr auto auto; width: 100%; height: 100%; diff --git a/nest_frontend/contexts/ContextLanguage.js b/nest_frontend/contexts/ContextLanguage.js index 8831fff..a48fb29 100644 --- a/nest_frontend/contexts/ContextLanguage.js +++ b/nest_frontend/contexts/ContextLanguage.js @@ -1,4 +1,5 @@ import { createContext } from "react" +import LocalizationStrings from "../LocalizationStrings" /** @@ -6,9 +7,11 @@ import { createContext } from "react" * - `lang`: a string corresponding to the ISO 639-1 code of the current language * - `setLang`: a function to change the current language * - `strings`: an object containing all strings of the current language + * + * Defaults to Italian. */ export default createContext({ - lang: null, + lang: "it", setLang: () => console.error("Trying to setLang while outside a ContextServer.Provider!"), - strings: null, + strings: LocalizationStrings.it, }) diff --git a/nest_frontend/hooks/useRepositoryViewer.js b/nest_frontend/hooks/useRepositoryViewer.js index 4ab4914..20e84f5 100644 --- a/nest_frontend/hooks/useRepositoryViewer.js +++ b/nest_frontend/hooks/useRepositoryViewer.js @@ -6,7 +6,7 @@ import ContextRepositoryViewer from "../contexts/ContextRepositoryViewer" /** * Hook to quickly use {@link ContextRepositoryEditor}. */ -export default function useRepositoryEditor() { +export default function useRepositoryViewer() { const context = useContext(ContextRepositoryViewer) if(!context) { throw new Error("This component must be placed inside a RepositoryViewer.") diff --git a/nest_frontend/hooks/useStrings.js b/nest_frontend/hooks/useStrings.js new file mode 100644 index 0000000..4a02c4a --- /dev/null +++ b/nest_frontend/hooks/useStrings.js @@ -0,0 +1,10 @@ +import { useContext } from "react" +import ContextLanguage from "../contexts/ContextLanguage" + + +/** + * Hook to quickly use the strings of {@link ContextLanguage}. + */ +export default function useStrings() { + return useContext(ContextLanguage).strings +} \ No newline at end of file diff --git a/nest_frontend/utils/Filter.js b/nest_frontend/utils/Filter.js index f9525ce..8e88463 100644 --- a/nest_frontend/utils/Filter.js +++ b/nest_frontend/utils/Filter.js @@ -1,8 +1,8 @@ -import { Location } from "../components/interactive/location" +import {Location} from "./location" import { faAt, faFilter, - faFont, + faFont, faHashtag, faLocationArrow, faMap, faMapMarkerAlt, @@ -64,6 +64,24 @@ export class ContainsFilter extends Filter { } +export class HashtagFilter extends ContainsFilter { + hashtag + + constructor(negate, hashtag) { + super(negate, `#${hashtag}`); + this.hashtag = hashtag + } + + icon() { + return faHashtag + } + + text() { + return this.hashtag + } +} + + export class UserFilter extends Filter { user