You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by sh...@apache.org on 2024/01/26 02:46:08 UTC

(incubator-answer) branch test updated: fix: admin theme primary-color add reset btn

This is an automated email from the ASF dual-hosted git repository.

shuai pushed a commit to branch test
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git


The following commit(s) were added to refs/heads/test by this push:
     new 0bac1c14 fix: admin theme primary-color  add reset btn
0bac1c14 is described below

commit 0bac1c1436882239713018d15045c920ec4bcc4f
Author: shuai <li...@sifou.com>
AuthorDate: Fri Jan 26 10:45:57 2024 +0800

    fix: admin theme primary-color  add reset btn
---
 i18n/en_US.yaml                                    |  1 +
 ui/src/components/SchemaForm/components/Button.tsx | 50 +++++++++++-
 ui/src/components/SchemaForm/components/Input.tsx  |  2 +-
 .../SchemaForm/components/InputGroup.tsx           | 90 ++++++++++++++++++++++
 ui/src/components/SchemaForm/components/index.ts   |  2 +
 ui/src/components/SchemaForm/index.tsx             | 28 +++++++
 ui/src/components/SchemaForm/types.ts              | 18 ++++-
 ui/src/pages/Admin/Themes/index.tsx                | 18 +++++
 8 files changed, 203 insertions(+), 6 deletions(-)

diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 2ea52dcb..e155edff 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1157,6 +1157,7 @@ ui:
     dark: Dark
     system_setting: System setting
     default: Default
+    reset: Reset
   search:
     title: Search Results
     keywords: Keywords
diff --git a/ui/src/components/SchemaForm/components/Button.tsx b/ui/src/components/SchemaForm/components/Button.tsx
index 38bad913..0371992f 100644
--- a/ui/src/components/SchemaForm/components/Button.tsx
+++ b/ui/src/components/SchemaForm/components/Button.tsx
@@ -17,31 +17,43 @@
  * under the License.
  */
 
-import React, { FC, useLayoutEffect, useState } from 'react';
+import { FC, useEffect, useState } from 'react';
 import { Button, ButtonProps, Spinner } from 'react-bootstrap';
 
 import { request } from '@/utils';
 import type { UIAction, FormKit } from '../types';
 import { useToast } from '@/hooks';
+import { Icon } from '@/components';
 
 interface Props {
   fieldName: string;
   text: string;
   action: UIAction | undefined;
+  actionType?: 'submit' | 'click';
+  clickCallback?: () => void;
   formKit: FormKit;
   readOnly: boolean;
   variant?: ButtonProps['variant'];
   size?: ButtonProps['size'];
+  iconName?: string;
+  nowrap?: boolean;
+  title?: string;
 }
 const Index: FC<Props> = ({
   fieldName,
   action,
+  actionType = 'submit',
   formKit,
   text = '',
   readOnly = false,
   variant = 'primary',
   size,
+  iconName = '',
+  nowrap = false,
+  clickCallback,
+  title,
 }) => {
+  console.log('Button.tsx: action:', title);
   const Toast = useToast();
   const [isLoading, setLoading] = useState(false);
   const handleToast = (msg, type: 'success' | 'danger' = 'success') => {
@@ -62,6 +74,12 @@ const Index: FC<Props> = ({
     }
   };
   const handleAction = () => {
+    if (actionType === 'click') {
+      if (typeof clickCallback === 'function') {
+        clickCallback();
+      }
+      return;
+    }
     if (!action) {
       return;
     }
@@ -87,13 +105,39 @@ const Index: FC<Props> = ({
         setLoading(false);
       });
   };
-  useLayoutEffect(() => {
+  useEffect(() => {
     if (action?.loading?.state === 'pending') {
       setLoading(true);
     }
   }, []);
   const loadingText = action?.loading?.text || text;
   const disabled = isLoading || readOnly;
+  if (nowrap) {
+    return (
+      <Button
+        name={fieldName}
+        onClick={handleAction}
+        disabled={disabled}
+        size={size}
+        title={title}
+        variant={variant}>
+        {isLoading ? (
+          <>
+            <Spinner
+              className="align-middle me-2"
+              animation="border"
+              size="sm"
+              variant={variant}
+            />
+            {loadingText}
+          </>
+        ) : (
+          text
+        )}
+        {iconName && <Icon name={iconName} />}
+      </Button>
+    );
+  }
 
   return (
     <div className="d-flex">
@@ -102,6 +146,7 @@ const Index: FC<Props> = ({
         onClick={handleAction}
         disabled={disabled}
         size={size}
+        title={title}
         variant={variant}>
         {isLoading ? (
           <>
@@ -116,6 +161,7 @@ const Index: FC<Props> = ({
         ) : (
           text
         )}
+        {iconName && <Icon name={iconName} />}
       </Button>
     </div>
   );
diff --git a/ui/src/components/SchemaForm/components/Input.tsx b/ui/src/components/SchemaForm/components/Input.tsx
index 97229d13..1a3fe319 100644
--- a/ui/src/components/SchemaForm/components/Input.tsx
+++ b/ui/src/components/SchemaForm/components/Input.tsx
@@ -63,7 +63,7 @@ const Index: FC<Props> = ({
       onChange={handleChange}
       disabled={readOnly}
       isInvalid={fieldObject?.isInvalid}
-      style={type === 'color' ? { width: '6rem' } : {}}
+      style={type === 'color' ? { width: '100px', flex: 'none' } : {}}
     />
   );
 };
diff --git a/ui/src/components/SchemaForm/components/InputGroup.tsx b/ui/src/components/SchemaForm/components/InputGroup.tsx
new file mode 100644
index 00000000..88cb0388
--- /dev/null
+++ b/ui/src/components/SchemaForm/components/InputGroup.tsx
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { FC } from 'react';
+import { InputGroup } from 'react-bootstrap';
+
+import type { FormKit, InputGroupOptions } from '../types';
+
+import Button from './Button';
+
+interface Props {
+  formKitWithContext: FormKit;
+  uiOpt: InputGroupOptions;
+  prefixText?: string;
+  suffixText?: string;
+  children: React.ReactNode;
+}
+
+const InputGroupBtn = ({
+  formKitWithContext,
+  uiOpt,
+}: {
+  formKitWithContext: FormKit;
+  uiOpt:
+    | InputGroupOptions['prefixBtnOptions']
+    | InputGroupOptions['suffixBtnOptions'];
+}) => {
+  return (
+    <Button
+      fieldName="1"
+      text={String(uiOpt?.text)}
+      iconName={uiOpt?.iconName ? uiOpt?.iconName : ''}
+      action={uiOpt?.action ? uiOpt?.action : undefined}
+      actionType="click"
+      clickCallback={uiOpt?.clickCallback ? uiOpt?.clickCallback : undefined}
+      formKit={formKitWithContext}
+      variant={uiOpt?.variant ? uiOpt.variant : undefined}
+      size={uiOpt?.size ? uiOpt?.size : undefined}
+      title={uiOpt?.title ? uiOpt?.title : ''}
+      nowrap
+      readOnly={false}
+    />
+  );
+};
+
+const Index: FC<Props> = ({
+  formKitWithContext,
+  uiOpt,
+  prefixText = null,
+  suffixText = null,
+  children,
+}) => {
+  return (
+    <InputGroup>
+      {prefixText && <InputGroup.Text>{prefixText}</InputGroup.Text>}
+      {uiOpt && 'prefixBtnOptions' in uiOpt && (
+        <InputGroupBtn
+          uiOpt={uiOpt.prefixBtnOptions}
+          formKitWithContext={formKitWithContext}
+        />
+      )}
+      {children}
+      {uiOpt && 'suffixBtnOptions' in uiOpt && (
+        <InputGroupBtn
+          uiOpt={uiOpt.suffixBtnOptions}
+          formKitWithContext={formKitWithContext}
+        />
+      )}
+      {suffixText ? <InputGroup.Text>{suffixText}</InputGroup.Text> : null}
+    </InputGroup>
+  );
+};
+
+export default Index;
diff --git a/ui/src/components/SchemaForm/components/index.ts b/ui/src/components/SchemaForm/components/index.ts
index 4e74be30..df28f4ac 100644
--- a/ui/src/components/SchemaForm/components/index.ts
+++ b/ui/src/components/SchemaForm/components/index.ts
@@ -26,6 +26,7 @@ import Upload from './Upload';
 import Textarea from './Textarea';
 import Input from './Input';
 import Button from './Button';
+import InputGroup from './InputGroup';
 
 export {
   Legend,
@@ -37,4 +38,5 @@ export {
   Textarea,
   Input,
   Button,
+  InputGroup,
 };
diff --git a/ui/src/components/SchemaForm/index.tsx b/ui/src/components/SchemaForm/index.tsx
index e3464bc9..c90abee7 100644
--- a/ui/src/components/SchemaForm/index.tsx
+++ b/ui/src/components/SchemaForm/index.tsx
@@ -37,6 +37,7 @@ import type {
   FormRef,
   BaseUIOptions,
   FormKit,
+  InputGroupOptions,
 } from './types';
 import {
   Legend,
@@ -48,6 +49,7 @@ import {
   Textarea,
   Input,
   Button as SfButton,
+  InputGroup,
 } from './components';
 
 export * from './types';
@@ -377,8 +379,33 @@ const SchemaForm: ForwardRefRenderFunction<FormRef, FormProps> = (
                   uiOpt && 'variant' in uiOpt ? uiOpt.variant : undefined
                 }
                 size={uiOpt && 'size' in uiOpt ? uiOpt.size : undefined}
+                title={uiOpt && 'title' in uiOpt ? uiOpt?.title : ''}
               />
             ) : null}
+            {widget === 'input_group' ? (
+              <InputGroup
+                formKitWithContext={formKitWithContext}
+                uiOpt={uiOpt as InputGroupOptions}
+                prefixText={
+                  (uiOpt && 'prefixText' in uiOpt && uiOpt.prefixText) || ''
+                }
+                suffixText={
+                  (uiOpt && 'suffixText' in uiOpt && uiOpt.suffixText) || ''
+                }>
+                <Input
+                  type={
+                    uiOpt && 'inputType' in uiOpt ? uiOpt.inputType : 'text'
+                  }
+                  placeholder={
+                    uiOpt && 'placeholder' in uiOpt ? uiOpt.placeholder : ''
+                  }
+                  fieldName={key}
+                  onChange={onChange}
+                  formData={formData}
+                  readOnly={readOnly}
+                />
+              </InputGroup>
+            ) : null}
             {/* Unified handling of `Feedback` and `Text` */}
             <Form.Control.Feedback type="invalid">
               {fieldState?.errorMsg}
@@ -397,6 +424,7 @@ const SchemaForm: ForwardRefRenderFunction<FormRef, FormProps> = (
     </Form>
   );
 };
+
 export const initFormData = (schema: JSONSchema): Type.FormDataType => {
   const formData: Type.FormDataType = {};
   const props: JSONSchema['properties'] = schema?.properties || {};
diff --git a/ui/src/components/SchemaForm/types.ts b/ui/src/components/SchemaForm/types.ts
index 69cbe617..794761a0 100644
--- a/ui/src/components/SchemaForm/types.ts
+++ b/ui/src/components/SchemaForm/types.ts
@@ -112,10 +112,20 @@ export interface TextareaOptions extends BaseUIOptions {
 
 export interface ButtonOptions extends BaseUIOptions {
   text: string;
-  icon?: string;
+  iconName?: string;
   action?: UIAction;
+  actionType: 'click' | 'submit';
   variant?: ButtonProps['variant'];
   size?: ButtonProps['size'];
+  title?: string;
+  clickCallback?: () => void;
+}
+
+export interface InputGroupOptions extends InputOptions {
+  prefixText?: string;
+  suffixText?: string;
+  prefixBtnOptions?: ButtonOptions;
+  suffixBtnOptions?: ButtonOptions;
 }
 
 export type UIOptions =
@@ -127,7 +137,8 @@ export type UIOptions =
   | CheckboxOptions
   | RadioOptions
   | TextareaOptions
-  | ButtonOptions;
+  | ButtonOptions
+  | InputGroupOptions;
 
 export type UIWidget =
   | 'textarea'
@@ -139,7 +150,8 @@ export type UIWidget =
   | 'timezone'
   | 'switch'
   | 'legend'
-  | 'button';
+  | 'button'
+  | 'input_group';
 export interface UISchema {
   [key: string]: {
     'ui:widget'?: UIWidget;
diff --git a/ui/src/pages/Admin/Themes/index.tsx b/ui/src/pages/Admin/Themes/index.tsx
index 990f5e54..346e2e05 100644
--- a/ui/src/pages/Admin/Themes/index.tsx
+++ b/ui/src/pages/Admin/Themes/index.tsx
@@ -82,13 +82,31 @@ const Index: FC = () => {
       'ui:widget': 'select',
     },
     primary_color: {
+      'ui:widget': 'input_group',
       'ui:options': {
         inputType: 'color',
+        suffixBtnOptions: {
+          text: '',
+          variant: 'outline-secondary',
+          iconName: 'arrow-counterclockwise',
+          actionType: 'click',
+          title: t('reset', { keyPrefix: 'btns' }),
+          // eslint-disable-next-line @typescript-eslint/no-use-before-define
+          clickCallback: () => resetPrimaryScheme(),
+        },
       },
     },
   };
+
   const [formData, setFormData] = useState(initFormData(schema));
   const { update: updateThemeSetting } = themeSettingStore((_) => _);
+
+  const resetPrimaryScheme = () => {
+    const formMeta = { ...formData };
+    formMeta.primary_color.value = '#0033FF';
+    setFormData({ ...formMeta });
+  };
+
   const onSubmit = (evt) => {
     evt.preventDefault();
     evt.stopPropagation();