Kaynağa Gözat

fix: api extension selector (#1486)

zxhlyh 1 yıl önce
ebeveyn
işleme
146e95d88f

+ 4 - 3
web/app/components/app/configuration/toolbox/moderation/form-generation.tsx

@@ -2,7 +2,7 @@ import type { FC } from 'react'
 import { useContext } from 'use-context-selector'
 import type { CodeBasedExtensionForm } from '@/models/common'
 import I18n from '@/context/i18n'
-import { SimpleSelect } from '@/app/components/base/select'
+import { PortalSelect } from '@/app/components/base/select'
 import type { ModerationConfig } from '@/models/debug'
 
 type FormGenerationProps = {
@@ -56,8 +56,8 @@ const FormGeneration: FC<FormGenerationProps> = ({
             }
             {
               form.type === 'select' && (
-                <SimpleSelect
-                  defaultValue={value?.[form.variable]}
+                <PortalSelect
+                  value={value?.[form.variable]}
                   items={form.options.map((option) => {
                     return {
                       name: option.label[locale === 'zh-Hans' ? 'zh-Hans' : 'en-US'],
@@ -65,6 +65,7 @@ const FormGeneration: FC<FormGenerationProps> = ({
                     }
                   })}
                   onSelect={item => handleFormChange(form.variable, item.value as string)}
+                  popupClassName='w-[576px]'
                 />
               )
             }

+ 84 - 1
web/app/components/base/select/index.tsx

@@ -5,6 +5,11 @@ import { Combobox, Listbox, Transition } from '@headlessui/react'
 import classNames from 'classnames'
 import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/20/solid'
 import { useTranslation } from 'react-i18next'
+import {
+  PortalToFollowElem,
+  PortalToFollowElemContent,
+  PortalToFollowElemTrigger,
+} from '@/app/components/base/portal-to-follow-elem'
 
 const defaultItems = [
   { value: 1, name: 'option1' },
@@ -222,5 +227,83 @@ const SimpleSelect: FC<ISelectProps> = ({
     </Listbox>
   )
 }
-export { SimpleSelect }
+
+type PortalSelectProps = {
+  value: string | number
+  onSelect: (value: Item) => void
+  items: Item[]
+  placeholder?: string
+  popupClassName?: string
+}
+const PortalSelect: FC<PortalSelectProps> = ({
+  value,
+  onSelect,
+  items,
+  placeholder,
+  popupClassName,
+}) => {
+  const { t } = useTranslation()
+  const [open, setOpen] = useState(false)
+  const localPlaceholder = placeholder || t('common.placeholder.select')
+  const selectedItem = items.find(item => item.value === value)
+
+  return (
+    <PortalToFollowElem
+      open={open}
+      onOpenChange={setOpen}
+      placement='bottom-start'
+      offset={4}
+    >
+      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} className='w-full'>
+        <div
+          className={`
+            flex items-center justify-between px-2.5 h-9 rounded-lg border-0 bg-gray-100 text-sm cursor-pointer 
+          `}
+          title={selectedItem?.name}
+        >
+          <span
+            className={`
+              grow truncate
+              ${!selectedItem?.name && 'text-gray-400'}
+            `}
+          >
+            {selectedItem?.name ?? localPlaceholder}
+          </span>
+          <ChevronDownIcon className='shrink-0 h-4 w-4 text-gray-400' />
+        </div>
+      </PortalToFollowElemTrigger>
+      <PortalToFollowElemContent className={`z-20 ${popupClassName}`}>
+        <div
+          className='px-1 py-1 max-h-60 overflow-auto rounded-md bg-white text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm'
+        >
+          {items.map((item: Item) => (
+            <div
+              key={item.value}
+              className={`
+                flex items-center justify-between px-2.5 h-9 cursor-pointer rounded-lg hover:bg-gray-100 text-gray-700
+                ${item.value === value && 'bg-gray-100'}
+              `}
+              title={item.name}
+              onClick={() => {
+                onSelect(item)
+                setOpen(false)
+              }}
+            >
+              <span
+                className='grow truncate'
+                title={item.name}
+              >
+                {item.name}
+              </span>
+              {item.value === value && (
+                <CheckIcon className='shrink-0 h-4 w-4' />
+              )}
+            </div>
+          ))}
+        </div>
+      </PortalToFollowElemContent>
+    </PortalToFollowElem>
+  )
+}
+export { SimpleSelect, PortalSelect }
 export default React.memo(Select)