1
1
import { z } from "zod" ;
2
2
import { nanoid } from "nanoid" ;
3
+ import { computed } from "nanostores" ;
3
4
import { useStore } from "@nanostores/react" ;
4
5
import {
5
6
type ReactNode ,
@@ -12,6 +13,7 @@ import {
12
13
useRef ,
13
14
createContext ,
14
15
useEffect ,
16
+ useCallback ,
15
17
} from "react" ;
16
18
import { CopyIcon , RefreshIcon , UpgradeIcon } from "@webstudio-is/icons" ;
17
19
import {
@@ -55,7 +57,7 @@ import {
55
57
$userPlanFeatures ,
56
58
} from "~/shared/nano-states" ;
57
59
import { serverSyncStore } from "~/shared/sync" ;
58
-
60
+ import { $selectedInstance } from "~/shared/awareness" ;
59
61
import { BindingPopoverProvider } from "~/builder/shared/binding-popover" ;
60
62
import {
61
63
EditorDialog ,
@@ -68,15 +70,43 @@ import {
68
70
SystemResourceForm ,
69
71
} from "./resource-panel" ;
70
72
import { generateCurl } from "./curl" ;
71
- import { $selectedInstance } from "~/shared/awareness" ;
72
73
73
- const validateName = ( value : string ) =>
74
- value . trim ( ) . length === 0 ? "Name is required" : "" ;
74
+ const $variablesByName = computed (
75
+ [ $selectedInstance , $dataSources ] ,
76
+ ( instance , dataSources ) => {
77
+ const variablesByName = new Map < DataSource [ "name" ] , DataSource [ "id" ] > ( ) ;
78
+ for ( const dataSource of dataSources . values ( ) ) {
79
+ if ( dataSource . scopeInstanceId === instance ?. id ) {
80
+ variablesByName . set ( dataSource . name , dataSource . id ) ;
81
+ }
82
+ }
83
+ return variablesByName ;
84
+ }
85
+ ) ;
75
86
76
- const NameField = ( { defaultValue } : { defaultValue : string } ) => {
87
+ const NameField = ( {
88
+ variableId,
89
+ defaultValue,
90
+ } : {
91
+ variableId : undefined | DataSource [ "id" ] ;
92
+ defaultValue : string ;
93
+ } ) => {
77
94
const ref = useRef < HTMLInputElement > ( null ) ;
78
95
const [ error , setError ] = useState ( "" ) ;
79
96
const nameId = useId ( ) ;
97
+ const variablesByName = useStore ( $variablesByName ) ;
98
+ const validateName = useCallback (
99
+ ( value : string ) => {
100
+ if ( variablesByName . get ( value ) !== variableId ) {
101
+ return "Name is already used by another variable on this instance" ;
102
+ }
103
+ if ( value . trim ( ) . length === 0 ) {
104
+ return "Name is required" ;
105
+ }
106
+ return "" ;
107
+ } ,
108
+ [ variablesByName , variableId ]
109
+ ) ;
80
110
useEffect ( ( ) => {
81
111
ref . current ?. setCustomValidity ( validateName ( defaultValue ) ) ;
82
112
} , [ defaultValue ] ) ;
@@ -510,15 +540,21 @@ const VariablePanel = forwardRef<
510
540
if ( variableType === "parameter" ) {
511
541
return (
512
542
< >
513
- < NameField defaultValue = { variable ?. name ?? "" } />
543
+ < NameField
544
+ variableId = { variable ?. id }
545
+ defaultValue = { variable ?. name ?? "" }
546
+ />
514
547
< ParameterForm ref = { ref } variable = { variable } />
515
548
</ >
516
549
) ;
517
550
}
518
551
if ( variableType === "string" ) {
519
552
return (
520
553
< >
521
- < NameField defaultValue = { variable ?. name ?? "" } />
554
+ < NameField
555
+ variableId = { variable ?. id }
556
+ defaultValue = { variable ?. name ?? "" }
557
+ />
522
558
< TypeField value = { variableType } onChange = { setVariableType } />
523
559
< StringForm ref = { ref } variable = { variable } />
524
560
</ >
@@ -527,7 +563,10 @@ const VariablePanel = forwardRef<
527
563
if ( variableType === "number" ) {
528
564
return (
529
565
< >
530
- < NameField defaultValue = { variable ?. name ?? "" } />
566
+ < NameField
567
+ variableId = { variable ?. id }
568
+ defaultValue = { variable ?. name ?? "" }
569
+ />
531
570
< TypeField value = { variableType } onChange = { setVariableType } />
532
571
< NumberForm ref = { ref } variable = { variable } />
533
572
</ >
@@ -536,7 +575,10 @@ const VariablePanel = forwardRef<
536
575
if ( variableType === "boolean" ) {
537
576
return (
538
577
< >
539
- < NameField defaultValue = { variable ?. name ?? "" } />
578
+ < NameField
579
+ variableId = { variable ?. id }
580
+ defaultValue = { variable ?. name ?? "" }
581
+ />
540
582
< TypeField value = { variableType } onChange = { setVariableType } />
541
583
< BooleanForm ref = { ref } variable = { variable } />
542
584
</ >
@@ -545,7 +587,10 @@ const VariablePanel = forwardRef<
545
587
if ( variableType === "json" ) {
546
588
return (
547
589
< >
548
- < NameField defaultValue = { variable ?. name ?? "" } />
590
+ < NameField
591
+ variableId = { variable ?. id }
592
+ defaultValue = { variable ?. name ?? "" }
593
+ />
549
594
< TypeField value = { variableType } onChange = { setVariableType } />
550
595
< JsonForm ref = { ref } variable = { variable } />
551
596
</ >
@@ -555,7 +600,10 @@ const VariablePanel = forwardRef<
555
600
if ( variableType === "resource" ) {
556
601
return (
557
602
< >
558
- < NameField defaultValue = { variable ?. name ?? "" } />
603
+ < NameField
604
+ variableId = { variable ?. id }
605
+ defaultValue = { variable ?. name ?? "" }
606
+ />
559
607
< TypeField value = { variableType } onChange = { setVariableType } />
560
608
< ResourceForm ref = { ref } variable = { variable } />
561
609
</ >
@@ -565,7 +613,10 @@ const VariablePanel = forwardRef<
565
613
if ( variableType === "graphql-resource" ) {
566
614
return (
567
615
< >
568
- < NameField defaultValue = { variable ?. name ?? "" } />
616
+ < NameField
617
+ variableId = { variable ?. id }
618
+ defaultValue = { variable ?. name ?? "" }
619
+ />
569
620
< TypeField value = { variableType } onChange = { setVariableType } />
570
621
< GraphqlResourceForm ref = { ref } variable = { variable } />
571
622
</ >
@@ -575,7 +626,10 @@ const VariablePanel = forwardRef<
575
626
if ( variableType === "system-resource" ) {
576
627
return (
577
628
< >
578
- < NameField defaultValue = { variable ?. name ?? "" } />
629
+ < NameField
630
+ variableId = { variable ?. id }
631
+ defaultValue = { variable ?. name ?? "" }
632
+ />
579
633
< TypeField value = { variableType } onChange = { setVariableType } />
580
634
< SystemResourceForm ref = { ref } variable = { variable } />
581
635
</ >
0 commit comments