mirror of
https://github.com/pds-nest/nest.git
synced 2025-02-16 12:43:58 +00:00
✨ Add component BoxConditionHashtag
This commit is contained in:
parent
418a06f939
commit
b707b1fc88
11 changed files with 115 additions and 6 deletions
59
code/frontend/src/components/BoxConditionHashtag.js
Normal file
59
code/frontend/src/components/BoxConditionHashtag.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import React, { useContext, useState } from "react"
|
||||||
|
import BoxFull from "./BoxFull"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
import { faHashtag, faPlus } from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import InputWithIcon from "./InputWithIcon"
|
||||||
|
import FormInline from "./FormInline"
|
||||||
|
import Style from "./BoxConditionHashtag.module.css"
|
||||||
|
import ButtonIconOnly from "./ButtonIconOnly"
|
||||||
|
import ContextRepositoryEditor from "../contexts/ContextRepositoryEditor"
|
||||||
|
|
||||||
|
// Official hashtag regex from https://stackoverflow.com/a/22490853/4334568
|
||||||
|
// noinspection RegExpAnonymousGroup,LongLine
|
||||||
|
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 BoxConditionHashtag({ ...props }) {
|
||||||
|
const [hashtag, setHashtag] = useState("")
|
||||||
|
const {conditions, appendCondition} = useContext(ContextRepositoryEditor)
|
||||||
|
|
||||||
|
const onInputChange = event => {
|
||||||
|
let text = event.target.value
|
||||||
|
text = text.replace(INVALID_HASHTAG_CHARACTERS, "")
|
||||||
|
return setHashtag(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onButtonClick = () => {
|
||||||
|
const newCond = {
|
||||||
|
"id": null,
|
||||||
|
"type": 0,
|
||||||
|
"content": hashtag
|
||||||
|
}
|
||||||
|
let duplicate = null;
|
||||||
|
for(const oldCond of conditions) {
|
||||||
|
if(newCond.type === oldCond.type && newCond.content === oldCond.content) {
|
||||||
|
duplicate = oldCond;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(duplicate) {
|
||||||
|
console.debug("Refusing to append ", newCond, " to the Conditions list, as ", duplicate, " already exists.")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.debug("Appending ", newCond, " to the Conditions list")
|
||||||
|
appendCondition(newCond)
|
||||||
|
}
|
||||||
|
|
||||||
|
setHashtag("")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BoxFull header={<span>Search by <FontAwesomeIcon icon={faHashtag}/> hashtag</span>} {...props}>
|
||||||
|
<FormInline>
|
||||||
|
<InputWithIcon className={Style.Input} id={"condition-hashtag"} icon={faHashtag} value={hashtag} onChange={onInputChange} placeholder={"hashtag"}/>
|
||||||
|
<ButtonIconOnly className={Style.Button} icon={faPlus} color={"Green"} onClick={onButtonClick}/>
|
||||||
|
</FormInline>
|
||||||
|
</BoxFull>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.Input {
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Button {
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import ConditionBadge from "./ConditionBadge"
|
||||||
export default function BoxConditions({ ...props }) {
|
export default function BoxConditions({ ...props }) {
|
||||||
const {conditions} = useContext(ContextRepositoryEditor)
|
const {conditions} = useContext(ContextRepositoryEditor)
|
||||||
|
|
||||||
const badges = conditions.map((cond) => <ConditionBadge key={cond.id} {...cond}/>)
|
const badges = conditions.map((cond, pos) => <ConditionBadge key={pos} {...cond}/>)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxFull header={"Conditions"} {...props}>
|
<BoxFull header={"Conditions"} {...props}>
|
||||||
|
|
11
code/frontend/src/components/ButtonIconOnly.js
Normal file
11
code/frontend/src/components/ButtonIconOnly.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./ButtonIconOnly.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
import Button from "./Button"
|
||||||
|
|
||||||
|
|
||||||
|
export default function ButtonIconOnly({ children, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<Button className={classNames(Style.ButtonIconOnly, className)} {...props}/>
|
||||||
|
)
|
||||||
|
}
|
5
code/frontend/src/components/ButtonIconOnly.module.css
Normal file
5
code/frontend/src/components/ButtonIconOnly.module.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.ButtonIconOnly {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ const CONDITION_ICONS = {
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default function ConditionBadge(condition) {
|
export default function ConditionBadge({ ...condition }) {
|
||||||
const { id, type, content } = condition
|
const { id, type, content } = condition
|
||||||
const color = CONDITION_COLORS[type]
|
const color = CONDITION_COLORS[type]
|
||||||
const icon = CONDITION_ICONS[type]
|
const icon = CONDITION_ICONS[type]
|
||||||
|
@ -48,7 +48,10 @@ export default function ConditionBadge(condition) {
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ButtonSmallX onClick={() => removeCondition(condition)}/>
|
<ButtonSmallX onClick={() => {
|
||||||
|
console.debug(`Removing Condition: `, condition)
|
||||||
|
removeCondition(condition)
|
||||||
|
}}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
12
code/frontend/src/components/FormInline.js
Normal file
12
code/frontend/src/components/FormInline.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import React from "react"
|
||||||
|
import Style from "./FormInline.module.css"
|
||||||
|
import classNames from "classnames"
|
||||||
|
|
||||||
|
|
||||||
|
export default function FormInline({ children, className, ...props }) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(Style.FormInline, className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
9
code/frontend/src/components/FormInline.module.css
Normal file
9
code/frontend/src/components/FormInline.module.css
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.FormInline {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
font-family: var(--font-regular);
|
font-family: var(--font-regular);
|
||||||
|
|
||||||
width: 300px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Input:focus {
|
.Input:focus {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
color: var(--fg-field-off);
|
color: var(--fg-field-off);
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
|
||||||
width: 300px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.InputWithIcon.Focused {
|
.InputWithIcon.Focused {
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default function useArrayState(def) {
|
||||||
|
|
||||||
const appendValue = useCallback(
|
const appendValue = useCallback(
|
||||||
newSingle => {
|
newSingle => {
|
||||||
|
console.debug("Appending ", newSingle, " to ArrayState")
|
||||||
setValue(
|
setValue(
|
||||||
oldArray => [...oldArray, newSingle]
|
oldArray => [...oldArray, newSingle]
|
||||||
)
|
)
|
||||||
|
@ -15,6 +16,7 @@ export default function useArrayState(def) {
|
||||||
|
|
||||||
const spliceValue = useCallback(
|
const spliceValue = useCallback(
|
||||||
position => {
|
position => {
|
||||||
|
console.debug("Splicing ", position, " from ArrayState")
|
||||||
setValue(
|
setValue(
|
||||||
oldArray => {
|
oldArray => {
|
||||||
// TODO: Hope this doesn't break anything...
|
// TODO: Hope this doesn't break anything...
|
||||||
|
@ -28,8 +30,9 @@ export default function useArrayState(def) {
|
||||||
|
|
||||||
const removeValue = useCallback(
|
const removeValue = useCallback(
|
||||||
remValue => {
|
remValue => {
|
||||||
|
console.debug("Removing ", remValue, " from ArrayState")
|
||||||
setValue(
|
setValue(
|
||||||
oldArray => oldArray.filter(item => item !== remValue)
|
oldArray => oldArray.filter(item => JSON.stringify(item) !== JSON.stringify(remValue))
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
|
|
Loading…
Add table
Reference in a new issue