index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import type {
  4. DefaultModel,
  5. Model,
  6. ModelItem,
  7. } from '../declarations'
  8. import { useCurrentProviderAndModel } from '../hooks'
  9. import ModelTrigger from './model-trigger'
  10. import EmptyTrigger from './empty-trigger'
  11. import DeprecatedModelTrigger from './deprecated-model-trigger'
  12. import Popup from './popup'
  13. import {
  14. PortalToFollowElem,
  15. PortalToFollowElemContent,
  16. PortalToFollowElemTrigger,
  17. } from '@/app/components/base/portal-to-follow-elem'
  18. import classNames from '@/utils/classnames'
  19. type ModelSelectorProps = {
  20. defaultModel?: DefaultModel
  21. modelList: Model[]
  22. triggerClassName?: string
  23. popupClassName?: string
  24. onSelect?: (model: DefaultModel) => void
  25. readonly?: boolean
  26. scopeFeatures?: string[]
  27. deprecatedClassName?: string
  28. showDeprecatedWarnIcon?: boolean
  29. }
  30. const ModelSelector: FC<ModelSelectorProps> = ({
  31. defaultModel,
  32. modelList,
  33. triggerClassName,
  34. popupClassName,
  35. onSelect,
  36. readonly,
  37. scopeFeatures = [],
  38. deprecatedClassName,
  39. showDeprecatedWarnIcon = false,
  40. }) => {
  41. const [open, setOpen] = useState(false)
  42. const {
  43. currentProvider,
  44. currentModel,
  45. } = useCurrentProviderAndModel(
  46. modelList,
  47. defaultModel,
  48. )
  49. const handleSelect = (provider: string, model: ModelItem) => {
  50. setOpen(false)
  51. if (onSelect)
  52. onSelect({ provider, model: model.model })
  53. }
  54. const handleToggle = () => {
  55. if (readonly)
  56. return
  57. setOpen(v => !v)
  58. }
  59. return (
  60. <PortalToFollowElem
  61. open={open}
  62. onOpenChange={setOpen}
  63. placement='bottom-start'
  64. offset={4}
  65. >
  66. <div className={classNames('relative')}>
  67. <PortalToFollowElemTrigger
  68. onClick={handleToggle}
  69. className='block'
  70. >
  71. {
  72. currentModel && currentProvider && (
  73. <ModelTrigger
  74. open={open}
  75. provider={currentProvider}
  76. model={currentModel}
  77. className={triggerClassName}
  78. readonly={readonly}
  79. />
  80. )
  81. }
  82. {
  83. !currentModel && defaultModel && (
  84. <DeprecatedModelTrigger
  85. modelName={defaultModel?.model || ''}
  86. providerName={defaultModel?.provider || ''}
  87. className={triggerClassName}
  88. showWarnIcon={showDeprecatedWarnIcon}
  89. contentClassName={deprecatedClassName}
  90. />
  91. )
  92. }
  93. {
  94. !defaultModel && (
  95. <EmptyTrigger
  96. open={open}
  97. className={triggerClassName}
  98. />
  99. )
  100. }
  101. </PortalToFollowElemTrigger>
  102. <PortalToFollowElemContent className={`z-[1002] ${popupClassName}`}>
  103. <Popup
  104. defaultModel={defaultModel}
  105. modelList={modelList}
  106. onSelect={handleSelect}
  107. scopeFeatures={scopeFeatures}
  108. onHide={() => setOpen(false)}
  109. />
  110. </PortalToFollowElemContent>
  111. </div>
  112. </PortalToFollowElem>
  113. )
  114. }
  115. export default ModelSelector