@@ -6,26 +6,26 @@ import React, {
66 useMemo ,
77} from "react" ;
88import { DiscourseNode } from "~/utils/getDiscourseNodes" ;
9+ import SelectPanel from "roamjs-components/components/ConfigPanels/SelectPanel" ;
910import BlocksPanel from "roamjs-components/components/ConfigPanels/BlocksPanel" ;
1011import { getSubTree } from "roamjs-components/util" ;
1112import Description from "roamjs-components/components/Description" ;
1213import { Label , Tabs , Tab , TabId , InputGroup } from "@blueprintjs/core" ;
13- import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid" ;
1414import DiscourseNodeSpecification from "./DiscourseNodeSpecification" ;
1515import DiscourseNodeAttributes from "./DiscourseNodeAttributes" ;
16- import SelectPanel from "roamjs-components/components/ConfigPanels/SelectPanel" ;
1716import DiscourseNodeCanvasSettings from "./DiscourseNodeCanvasSettings" ;
1817import DiscourseNodeIndex from "./DiscourseNodeIndex" ;
1918import { OnloadArgs } from "roamjs-components/types" ;
19+ import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid" ;
20+ import createBlock from "roamjs-components/writes/createBlock" ;
21+ import updateBlock from "roamjs-components/writes/updateBlock" ;
2022import DiscourseNodeSuggestiveRules from "./DiscourseNodeSuggestiveRules" ;
2123import { getFormattedConfigTree } from "~/utils/discourseConfigRef" ;
2224import refreshConfigTree from "~/utils/refreshConfigTree" ;
2325import {
2426 DiscourseNodeTextPanel ,
2527 DiscourseNodeFlagPanel ,
2628} from "./components/BlockPropSettingPanels" ;
27- import createBlock from "roamjs-components/writes/createBlock" ;
28- import updateBlock from "roamjs-components/writes/updateBlock" ;
2929
3030export const getCleanTagText = ( tag : string ) : string => {
3131 return tag . replace ( / ^ # + / , "" ) . trim ( ) . toUpperCase ( ) ;
@@ -65,7 +65,9 @@ const ValidatedInputPanel = ({
6565 </ div >
6666) ;
6767
68- const useDebouncedRoamUpdater = (
68+ const useDebouncedRoamUpdater = <
69+ T extends HTMLInputElement | HTMLTextAreaElement ,
70+ > (
6971 uid : string ,
7072 initialValue : string ,
7173 isValid : boolean ,
@@ -99,7 +101,7 @@ const useDebouncedRoamUpdater = (
99101 ) ;
100102
101103 const handleChange = useCallback (
102- ( e : React . ChangeEvent < HTMLInputElement > ) => {
104+ ( e : React . ChangeEvent < T > ) => {
103105 const newValue = e . target . value ;
104106 setValue ( newValue ) ;
105107 saveToRoam ( newValue , true ) ;
@@ -160,34 +162,21 @@ const NodeConfig = ({
160162 } ) ;
161163
162164 const [ selectedTabId , setSelectedTabId ] = useState < TabId > ( "general" ) ;
165+ const [ tagError , setTagError ] = useState ( "" ) ;
163166 const [ formatError , setFormatError ] = useState ( "" ) ;
164- const [ tagValue , setTagValue ] = useState ( node . tag || "" ) ;
167+ const isConfigurationValid = ! tagError && ! formatError ;
165168
169+ const [ tagValue , setTagValue ] = useState ( node . tag || "" ) ;
166170 const {
167171 value : formatValue ,
168172 handleChange : handleFormatChange ,
169173 handleBlur : handleFormatBlurFromHook ,
170- } = useDebouncedRoamUpdater ( formatUid , node . format , ! formatError ) ;
171-
172- const validateTag = useCallback (
173- ( tag : string ) : string | undefined => {
174- const cleanTag = getCleanTagText ( tag ) ;
175- if ( ! cleanTag ) return undefined ;
176- const roamTagRegex = / # ? \[ \[ ( .* ?) \] \] | # ( \S + ) / g;
177- const formatTags : string [ ] = [ ] ;
178- for ( const match of formatValue . matchAll ( roamTagRegex ) ) {
179- const tagName = match [ 1 ] || match [ 2 ] ;
180- if ( tagName ) formatTags . push ( tagName . toUpperCase ( ) ) ;
181- }
182- if ( formatTags . includes ( cleanTag ) ) {
183- return `The tag "${ tag } " is referenced in the format. Please use a different tag or format.` ;
184- }
185- return undefined ;
186- } ,
187- [ formatValue ] ,
174+ } = useDebouncedRoamUpdater < HTMLInputElement > (
175+ formatUid ,
176+ node . format ,
177+ isConfigurationValid ,
188178 ) ;
189-
190- const validateFormat = useCallback (
179+ const validate = useCallback (
191180 ( {
192181 tag,
193182 format,
@@ -203,12 +192,14 @@ const NodeConfig = ({
203192 key : "enabled" ,
204193 } ) ?. uid ?. length ;
205194 if ( format . trim ( ) . length === 0 && ! isSpecificationEnabled ) {
195+ setTagError ( "" ) ;
206196 setFormatError ( "Error: you must set either a format or specification" ) ;
207197 return ;
208198 }
209199 const cleanTag = getCleanTagText ( tag ) ;
210200
211201 if ( ! cleanTag ) {
202+ setTagError ( "" ) ;
212203 setFormatError ( "" ) ;
213204 return ;
214205 }
@@ -229,21 +220,25 @@ const NodeConfig = ({
229220 setFormatError (
230221 `The format references the node's tag "${ tag } ". Please use a different format or tag.` ,
231222 ) ;
223+ setTagError (
224+ `The tag "${ tag } " is referenced in the format. Please use a different tag or format.` ,
225+ ) ;
232226 } else {
227+ setTagError ( "" ) ;
233228 setFormatError ( "" ) ;
234229 }
235230 } ,
236231 [ specificationUid ] ,
237232 ) ;
238233
239234 useEffect ( ( ) => {
240- validateFormat ( { tag : tagValue , format : formatValue } ) ;
241- } , [ tagValue , formatValue , validateFormat ] ) ;
235+ validate ( { tag : tagValue , format : formatValue } ) ;
236+ } , [ tagValue , formatValue , validate ] ) ;
242237
243238 const handleFormatBlur = useCallback ( ( ) => {
244239 handleFormatBlurFromHook ( ) ;
245- validateFormat ( { tag : tagValue , format : formatValue } ) ;
246- } , [ handleFormatBlurFromHook , tagValue , formatValue , validateFormat ] ) ;
240+ validate ( { tag : tagValue , format : formatValue } ) ;
241+ } , [ handleFormatBlurFromHook , tagValue , formatValue , validate ] ) ;
247242
248243 return (
249244 < >
@@ -263,6 +258,7 @@ const NodeConfig = ({
263258 description = { `Describing what the ${ node . text } node represents in your graph.` }
264259 settingKeys = { [ "description" ] }
265260 initialValue = { node . description }
261+ multiline
266262 order = { 1 }
267263 parentUid = { node . type }
268264 uid = { descriptionUid }
@@ -284,7 +280,7 @@ const NodeConfig = ({
284280 settingKeys = { [ "tag" ] }
285281 initialValue = { node . tag }
286282 placeholder = { generateTagPlaceholder ( node ) }
287- getValidationError = { validateTag }
283+ error = { tagError }
288284 onChange = { setTagValue }
289285 order = { 2 }
290286 parentUid = { node . type }
@@ -330,7 +326,7 @@ const NodeConfig = ({
330326 node = { node }
331327 parentUid = { specificationUid }
332328 parentSetEnabled = { ( isSpecificationEnabled ) => {
333- validateFormat ( {
329+ validate ( {
334330 tag : tagValue ,
335331 format : formatValue ,
336332 isSpecificationEnabled,
@@ -388,7 +384,7 @@ const NodeConfig = ({
388384 < DiscourseNodeFlagPanel
389385 nodeType = { node . type }
390386 title = "Graph Overview"
391- description = "Whether to color the node in the graph overview based on canvas color. This is based on the node's plain title as described by a `has title` condition in its specification."
387+ description = "Whether to color the node in the graph overview based on canvas color. This is based on the node's plain title as described by a \ `has title\ ` condition in its specification."
392388 settingKeys = { [ "graphOverview" ] }
393389 initialValue = { node . graphOverview }
394390 order = { 0 }
0 commit comments